wyrm-mcp 7.2.0 → 7.2.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.
Files changed (156) hide show
  1. package/LICENSE +26 -667
  2. package/NOTICE +14 -33
  3. package/dist/activation.d.ts.map +1 -1
  4. package/dist/activation.js +1 -44
  5. package/dist/activation.js.map +1 -1
  6. package/dist/agent-daemon.js +4 -281
  7. package/dist/agent-loop.js +7 -332
  8. package/dist/analytics.js +13 -236
  9. package/dist/attribution.js +1 -49
  10. package/dist/audit.js +2 -457
  11. package/dist/auto-capture.js +3 -138
  12. package/dist/auto-orchestrator.js +1 -325
  13. package/dist/autoconfig.js +39 -840
  14. package/dist/buddy-runner.js +1 -109
  15. package/dist/buddy.js +14 -564
  16. package/dist/build-flags.js +1 -17
  17. package/dist/capabilities.js +3 -183
  18. package/dist/capture.js +1 -56
  19. package/dist/causality.js +6 -107
  20. package/dist/cli.js +20 -281
  21. package/dist/cloud/cli.js +5 -541
  22. package/dist/cloud/client.js +1 -221
  23. package/dist/cloud/crypto.js +1 -85
  24. package/dist/cloud/machine-id.js +2 -113
  25. package/dist/cloud/recovery.js +1 -60
  26. package/dist/cloud/sync-engine.js +7 -543
  27. package/dist/cloud-backup.js +5 -579
  28. package/dist/cloud-profile.js +1 -138
  29. package/dist/cloud-sync-entrypoint.js +1 -47
  30. package/dist/cloud-sync.js +2 -309
  31. package/dist/constellation.js +12 -168
  32. package/dist/context-build-budgeted.js +4 -144
  33. package/dist/context-ranking.js +1 -69
  34. package/dist/crypto.js +1 -179
  35. package/dist/daemon-write-endpoint.js +1 -290
  36. package/dist/daemon-writer.js +2 -406
  37. package/dist/database.js +43 -1110
  38. package/dist/deprecations.js +2 -162
  39. package/dist/design.js +13 -141
  40. package/dist/event-replication.js +1 -112
  41. package/dist/events-sse.js +7 -43
  42. package/dist/events.js +6 -238
  43. package/dist/failure-patterns.js +42 -659
  44. package/dist/federation.js +12 -236
  45. package/dist/goals.js +13 -101
  46. package/dist/golden.js +3 -355
  47. package/dist/handlers/agent.js +4 -165
  48. package/dist/handlers/alias-adapters.js +1 -129
  49. package/dist/handlers/aliases.js +1 -171
  50. package/dist/handlers/audit.js +1 -87
  51. package/dist/handlers/boundary.js +1 -221
  52. package/dist/handlers/capture.js +73 -1109
  53. package/dist/handlers/causality.js +7 -114
  54. package/dist/handlers/cloud.js +85 -382
  55. package/dist/handlers/companion.js +28 -459
  56. package/dist/handlers/datalake.js +7 -187
  57. package/dist/handlers/dispatch-context.js +0 -22
  58. package/dist/handlers/entity.js +25 -256
  59. package/dist/handlers/events.js +16 -335
  60. package/dist/handlers/failure.js +13 -340
  61. package/dist/handlers/goals.js +4 -296
  62. package/dist/handlers/intelligence.js +126 -674
  63. package/dist/handlers/invoicing.js +1 -70
  64. package/dist/handlers/mcpclient.js +6 -137
  65. package/dist/handlers/orchestration.js +40 -125
  66. package/dist/handlers/output-schemas.js +1 -24
  67. package/dist/handlers/presence.js +3 -99
  68. package/dist/handlers/project.js +28 -182
  69. package/dist/handlers/prompts.js +6 -157
  70. package/dist/handlers/quest.js +4 -224
  71. package/dist/handlers/recall.js +11 -218
  72. package/dist/handlers/registry.js +1 -167
  73. package/dist/handlers/resources.js +1 -288
  74. package/dist/handlers/review.js +11 -74
  75. package/dist/handlers/run.js +17 -487
  76. package/dist/handlers/search.js +15 -326
  77. package/dist/handlers/session.js +28 -615
  78. package/dist/handlers/share.js +8 -184
  79. package/dist/handlers/shims.js +1 -464
  80. package/dist/handlers/skill.js +67 -449
  81. package/dist/handlers/survivors.js +1 -120
  82. package/dist/handlers/symbols.js +8 -109
  83. package/dist/handlers/syncops.js +4 -302
  84. package/dist/handlers/types.js +1 -27
  85. package/dist/harvest.js +5 -191
  86. package/dist/hours.js +7 -156
  87. package/dist/http-auth.js +3 -321
  88. package/dist/http-fast.js +21 -1137
  89. package/dist/icons.js +1 -47
  90. package/dist/index.js +2 -924
  91. package/dist/indexer.js +4 -145
  92. package/dist/intelligence.js +31 -261
  93. package/dist/internal-dispatch.js +3 -212
  94. package/dist/keyset.js +1 -110
  95. package/dist/knowledge-graph.js +12 -176
  96. package/dist/license.d.ts +11 -0
  97. package/dist/license.d.ts.map +1 -1
  98. package/dist/license.js +2 -414
  99. package/dist/license.js.map +1 -1
  100. package/dist/logger.js +2 -199
  101. package/dist/maintenance.js +2 -148
  102. package/dist/mcp-client.js +6 -262
  103. package/dist/memory-artifacts.js +30 -449
  104. package/dist/migrate-prompt.js +2 -124
  105. package/dist/migrations.js +40 -655
  106. package/dist/performance.js +1 -228
  107. package/dist/presence.js +11 -140
  108. package/dist/priority-embed.js +5 -164
  109. package/dist/providers/embedding-provider.js +1 -196
  110. package/dist/readonly-gate.js +1 -29
  111. package/dist/rehydration.js +9 -157
  112. package/dist/reindex.js +1 -88
  113. package/dist/render-target.js +21 -514
  114. package/dist/render.js +4 -280
  115. package/dist/repl-guard.js +1 -173
  116. package/dist/replication-daemon-entrypoint.js +1 -31
  117. package/dist/replication-daemon.js +2 -262
  118. package/dist/resilience.js +1 -591
  119. package/dist/reverse-bridge.js +5 -360
  120. package/dist/security.js +1 -244
  121. package/dist/session-seen.js +3 -51
  122. package/dist/setup.js +1 -260
  123. package/dist/skill-author.js +5 -168
  124. package/dist/spec-kit.js +1 -191
  125. package/dist/sqlite-busy.js +1 -154
  126. package/dist/statusline.js +11 -315
  127. package/dist/sub-agent.js +13 -262
  128. package/dist/summarizer.js +13 -139
  129. package/dist/symbols.js +7 -283
  130. package/dist/sync.js +5 -359
  131. package/dist/tasks-dispatch.js +1 -84
  132. package/dist/tasks.js +1 -282
  133. package/dist/token-budget.js +1 -143
  134. package/dist/tool-analytics.js +7 -129
  135. package/dist/tool-annotations.js +1 -365
  136. package/dist/tool-manifest-v2.json +1 -1
  137. package/dist/tool-manifest.json +1 -1
  138. package/dist/tool-profiles.js +1 -75
  139. package/dist/trace-harvest.js +6 -244
  140. package/dist/types.js +1 -30
  141. package/dist/ui-dashboard.js +41 -50
  142. package/dist/ulid.js +1 -81
  143. package/dist/validate.js +1 -129
  144. package/dist/vault.js +1 -534
  145. package/dist/vectors.js +3 -184
  146. package/dist/version-check.js +4 -136
  147. package/dist/visibility.js +19 -155
  148. package/dist/wyrm-cli.js +98 -2451
  149. package/dist/wyrm-cli.js.map +1 -1
  150. package/dist/wyrm-guard.js +14 -424
  151. package/dist/wyrm-loop.js +3 -150
  152. package/dist/wyrm-manifest.json +1 -1
  153. package/dist/wyrm-statusline-daemon.js +1 -11
  154. package/dist/wyrm-statusline.js +4 -56
  155. package/dist/wyrm-ui.js +9 -77
  156. package/package.json +4 -2
@@ -1,257 +1,33 @@
1
- /**
2
- * Federated team Wyrm (Tier 2.8).
3
- *
4
- * Each operator runs a local Wyrm; opt-in tagged data syncs to a shared
5
- * "team Wyrm" instance. PII never leaves the local box unless explicitly
6
- * shared (per-row `is_shared=1` flag — set when the operator marks a
7
- * truth/quest/artifact as team-visible).
8
- *
9
- * Sync surface:
10
- * POST /sync/push pushes is_shared=1 local rows changed since cursor
11
- * GET /sync/pull pulls remote rows changed since cursor
12
- *
13
- * Conflict policy: if a remote push lands on top of a local edit whose
14
- * `updated_at` is newer, we DO NOT silently overwrite. Instead we record
15
- * the conflict in `sync_conflicts` and mint a quest for the operator to
16
- * resolve. Last-write-wins is the failure mode of every team sync tool;
17
- * Wyrm refuses to silently lose work.
18
- *
19
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
20
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
21
- */
22
- const KIND_TO_TABLE = {
23
- session: { table: 'sessions', ts: 'created_at' },
24
- quest: { table: 'quests', ts: 'created_at' },
25
- truth: { table: 'ground_truths', ts: 'updated_at' },
26
- artifact: { table: 'memory_artifacts', ts: 'last_accessed_at' },
27
- edge: { table: 'decision_edges', ts: 'created_at' },
28
- };
29
- export class Federation {
30
- db;
31
- constructor(db) {
32
- this.db = db;
33
- }
34
- /** Mark a row as shared (will be picked up on next push). */
35
- share(kind, id) {
36
- const t = KIND_TO_TABLE[kind];
37
- if (!t)
38
- return false;
39
- // [grove isolation] Refuse to share a row that belongs to a PRIVATE grove.
40
- // A private grove (projects.sync_policy='private') must never federate to a
41
- // team Wyrm, so we block the is_shared flag at the source (defense in depth
42
- // alongside the collectForPush grove gate).
43
- if (this.isPrivateGroveRow(t.table, id))
44
- return false;
45
- const info = this.db.prepare(`UPDATE ${t.table} SET is_shared = 1 WHERE id = ?`).run(id);
46
- return info.changes > 0;
47
- }
48
- /** True if the row's project grove has sync_policy='private' (never federates). */
49
- isPrivateGroveRow(table, id) {
50
- try {
51
- const row = this.db.prepare(`SELECT (SELECT sync_policy FROM projects WHERE id = t.project_id) AS policy
52
- FROM ${table} t WHERE t.id = ?`).get(id);
53
- // No row, or a project-less / unknown-project row: not grove-private here
54
- // (governed by is_shared alone). An explicit 'private' policy blocks it.
55
- if (!row || row.policy == null)
56
- return false;
57
- return row.policy === 'private';
58
- }
59
- catch {
60
- // projects.sync_policy not present yet (pre-migration) → grove feature off.
61
- return false;
62
- }
63
- }
64
- /** Whether the grove-policy column exists (feature gate, pre-migration safe). */
65
- projectsHasSyncPolicy() {
66
- try {
67
- return this.db.prepare(`PRAGMA table_info(projects)`).all()
68
- .some((c) => c.name === 'sync_policy');
69
- }
70
- catch {
71
- return false;
72
- }
73
- }
74
- /** Unshare (revoke from team visibility). Local row stays. */
75
- unshare(kind, id) {
76
- const t = KIND_TO_TABLE[kind];
77
- if (!t)
78
- return false;
79
- const info = this.db.prepare(`UPDATE ${t.table} SET is_shared = 0 WHERE id = ?`).run(id);
80
- return info.changes > 0;
81
- }
82
- /** Collect rows to push: is_shared=1 AND ts > since_cursor. */
83
- collectForPush(since_cursor, limit = 500) {
84
- const cursor = since_cursor ?? '1970-01-01';
85
- const out = [];
86
- // [grove isolation] Only federate rows whose grove opts into team sharing
87
- // (projects.sync_policy='team'). Project-less rows fall back to is_shared.
88
- const groveGate = this.projectsHasSyncPolicy()
89
- ? `AND (project_id IS NULL OR project_id IN (SELECT id FROM projects WHERE sync_policy = 'team'))`
90
- : ``;
91
- for (const [kindStr, t] of Object.entries(KIND_TO_TABLE)) {
92
- if (out.length >= limit)
93
- break;
94
- const kind = kindStr;
95
- const rows = this.db.prepare(`SELECT * FROM ${t.table}
96
- WHERE is_shared = 1 ${groveGate} AND ${t.ts} > ?
97
- ORDER BY ${t.ts} ASC
98
- LIMIT ?`).all(cursor, Math.max(1, limit - out.length));
99
- for (const r of rows) {
100
- out.push({
101
- kind,
102
- id: r.id,
103
- project_id: (r.project_id ?? null),
104
- payload: r,
105
- updated_at: r[t.ts] || new Date().toISOString(),
106
- });
107
- }
108
- }
109
- return out;
110
- }
111
- /** Apply a remote row locally. Returns:
112
- * { kind: 'inserted' } — fresh row, no conflict
113
- * { kind: 'overwritten' } — local row was older, remote wins
114
- * { kind: 'conflict', conflict_id } — local row newer, conflict recorded
115
- * Idempotent — applying the same payload twice is a no-op.
116
- *
117
- * Conflict policy: if local updated_at > remote updated_at, the remote
118
- * is REJECTED into sync_conflicts; a quest is minted. The local row stays.
119
- */
120
- applyRemote(row, remoteOrigin) {
121
- const t = KIND_TO_TABLE[row.kind];
122
- if (!t)
123
- return { kind: 'skipped' };
124
- const localTs = this.db.prepare(`SELECT ${t.ts} as ts FROM ${t.table} WHERE id = ?`).get(row.id);
125
- if (!localTs) {
126
- // Fresh insert. Build columns from payload, skipping id (auto-incremented)
127
- // — but we want to preserve the same id so future syncs are stable.
128
- // SQLite allows explicit id insert.
129
- const cols = Object.keys(row.payload).filter(c => c !== 'rowid');
130
- const placeholders = cols.map(() => '?').join(',');
131
- const values = cols.map(c => row.payload[c]);
132
- try {
133
- this.db.prepare(`INSERT OR IGNORE INTO ${t.table} (${cols.join(',')}) VALUES (${placeholders})`).run(...values);
134
- return { kind: 'inserted' };
135
- }
136
- catch {
137
- return { kind: 'skipped' };
138
- }
139
- }
140
- // Existing row — compare timestamps.
141
- const localNewer = localTs.ts > row.updated_at;
142
- if (localNewer) {
143
- // Conflict — record it and mint a quest if this row has a project_id.
144
- const conflictInfo = this.db.prepare(`
1
+ const d={session:{table:"sessions",ts:"created_at"},quest:{table:"quests",ts:"created_at"},truth:{table:"ground_truths",ts:"updated_at"},artifact:{table:"memory_artifacts",ts:"last_accessed_at"},edge:{table:"decision_edges",ts:"created_at"}};class E{db;constructor(t){this.db=t}share(t,r){const e=d[t];return!e||this.isPrivateGroveRow(e.table,r)?!1:this.db.prepare(`UPDATE ${e.table} SET is_shared = 1 WHERE id = ?`).run(r).changes>0}isPrivateGroveRow(t,r){try{const e=this.db.prepare(`SELECT (SELECT sync_policy FROM projects WHERE id = t.project_id) AS policy
2
+ FROM ${t} t WHERE t.id = ?`).get(r);return!e||e.policy==null?!1:e.policy==="private"}catch{return!1}}projectsHasSyncPolicy(){try{return this.db.prepare("PRAGMA table_info(projects)").all().some(t=>t.name==="sync_policy")}catch{return!1}}unshare(t,r){const e=d[t];return e?this.db.prepare(`UPDATE ${e.table} SET is_shared = 0 WHERE id = ?`).run(r).changes>0:!1}collectForPush(t,r=500){const e=t??"1970-01-01",s=[],o=this.projectsHasSyncPolicy()?"AND (project_id IS NULL OR project_id IN (SELECT id FROM projects WHERE sync_policy = 'team'))":"";for(const[a,c]of Object.entries(d)){if(s.length>=r)break;const l=a,n=this.db.prepare(`SELECT * FROM ${c.table}
3
+ WHERE is_shared = 1 ${o} AND ${c.ts} > ?
4
+ ORDER BY ${c.ts} ASC
5
+ LIMIT ?`).all(e,Math.max(1,r-s.length));for(const i of n)s.push({kind:l,id:i.id,project_id:i.project_id??null,payload:i,updated_at:i[c.ts]||new Date().toISOString()})}return s}applyRemote(t,r){const e=d[t.kind];if(!e)return{kind:"skipped"};const s=this.db.prepare(`SELECT ${e.ts} as ts FROM ${e.table} WHERE id = ?`).get(t.id);if(!s){const n=Object.keys(t.payload).filter(p=>p!=="rowid"),i=n.map(()=>"?").join(","),u=n.map(p=>t.payload[p]);try{return this.db.prepare(`INSERT OR IGNORE INTO ${e.table} (${n.join(",")}) VALUES (${i})`).run(...u),{kind:"inserted"}}catch{return{kind:"skipped"}}}if(s.ts>t.updated_at){const i=this.db.prepare(`
145
6
  INSERT INTO sync_conflicts
146
7
  (local_kind, local_id, remote_payload, remote_origin)
147
8
  VALUES (?, ?, ?, ?)
148
- `).run(row.kind, row.id, JSON.stringify(row.payload), remoteOrigin);
149
- const conflict_id = conflictInfo.lastInsertRowid;
150
- // Best-effort: mint a quest so the operator sees it on next session
151
- if (row.project_id != null) {
152
- try {
153
- this.db.prepare(`
9
+ `).run(t.kind,t.id,JSON.stringify(t.payload),r).lastInsertRowid;if(t.project_id!=null)try{this.db.prepare(`
154
10
  INSERT INTO quests (project_id, title, description, priority, status, tags)
155
11
  VALUES (?, ?, ?, 'high', 'pending', 'sync-conflict')
156
- `).run(row.project_id, `[sync] Resolve conflict on ${row.kind} #${row.id}`, `A remote ${remoteOrigin} pushed a ${row.kind} that conflicts with a newer local edit. ` +
157
- `See sync_conflicts row #${conflict_id} for the remote payload. ` +
158
- `Resolve with wyrm_sync_resolve (kept_local | kept_remote | merged).`);
159
- }
160
- catch { /* best-effort */ }
161
- }
162
- return { kind: 'conflict', conflict_id };
163
- }
164
- // Local is older — accept remote overwrite.
165
- const cols = Object.keys(row.payload).filter(c => c !== 'id');
166
- const setClause = cols.map(c => `${c} = ?`).join(', ');
167
- const values = cols.map(c => row.payload[c]);
168
- try {
169
- this.db.prepare(`UPDATE ${t.table} SET ${setClause} WHERE id = ?`).run(...values, row.id);
170
- return { kind: 'overwritten' };
171
- }
172
- catch {
173
- return { kind: 'skipped' };
174
- }
175
- }
176
- /** Bulk push — record one sync_log row, return summary. Caller does HTTP. */
177
- recordPush(rows, remoteOrigin, duration_ms, since_cursor) {
178
- const newCursor = new Date().toISOString();
179
- this.db.prepare(`
12
+ `).run(t.project_id,`[sync] Resolve conflict on ${t.kind} #${t.id}`,`A remote ${r} pushed a ${t.kind} that conflicts with a newer local edit. See sync_conflicts row #${i} for the remote payload. Resolve with wyrm_sync_resolve (kept_local | kept_remote | merged).`)}catch{}return{kind:"conflict",conflict_id:i}}const a=Object.keys(t.payload).filter(n=>n!=="id"),c=a.map(n=>`${n} = ?`).join(", "),l=a.map(n=>t.payload[n]);try{return this.db.prepare(`UPDATE ${e.table} SET ${c} WHERE id = ?`).run(...l,t.id),{kind:"overwritten"}}catch{return{kind:"skipped"}}}recordPush(t,r,e,s){const o=new Date().toISOString();return this.db.prepare(`
180
13
  INSERT INTO sync_log
181
14
  (direction, remote_origin, kind, count, since_cursor, new_cursor, duration_ms)
182
15
  VALUES ('push', ?, 'mixed', ?, ?, ?, ?)
183
- `).run(remoteOrigin, rows.length, since_cursor ?? null, newCursor, duration_ms);
184
- return {
185
- pushed: rows.length,
186
- conflicts: 0,
187
- new_cursor: newCursor,
188
- conflict_ids: [],
189
- };
190
- }
191
- /** Bulk pull — apply each remote row, record sync_log, return summary. */
192
- applyPull(rows, remoteOrigin, duration_ms, since_cursor) {
193
- const byKind = {};
194
- let pulled = 0;
195
- for (const row of rows) {
196
- const result = this.applyRemote(row, remoteOrigin);
197
- if (result.kind === 'inserted' || result.kind === 'overwritten') {
198
- pulled++;
199
- byKind[row.kind] = (byKind[row.kind] ?? 0) + 1;
200
- }
201
- }
202
- const newCursor = new Date().toISOString();
203
- this.db.prepare(`
16
+ `).run(r,t.length,s??null,o,e),{pushed:t.length,conflicts:0,new_cursor:o,conflict_ids:[]}}applyPull(t,r,e,s){const o={};let a=0;for(const l of t){const n=this.applyRemote(l,r);(n.kind==="inserted"||n.kind==="overwritten")&&(a++,o[l.kind]=(o[l.kind]??0)+1)}const c=new Date().toISOString();return this.db.prepare(`
204
17
  INSERT INTO sync_log
205
18
  (direction, remote_origin, kind, count, since_cursor, new_cursor, duration_ms)
206
19
  VALUES ('pull', ?, 'mixed', ?, ?, ?, ?)
207
- `).run(remoteOrigin, pulled, since_cursor ?? null, newCursor, duration_ms);
208
- return { pulled, new_cursor: newCursor, by_kind: byKind };
209
- }
210
- /** Get the latest cursor from a previous sync. */
211
- latestCursor(direction, remoteOrigin) {
212
- const row = this.db.prepare(`
20
+ `).run(r,a,s??null,c,e),{pulled:a,new_cursor:c,by_kind:o}}latestCursor(t,r){return this.db.prepare(`
213
21
  SELECT new_cursor FROM sync_log
214
22
  WHERE direction = ? AND remote_origin = ?
215
23
  ORDER BY id DESC LIMIT 1
216
- `).get(direction, remoteOrigin);
217
- return row?.new_cursor ?? null;
218
- }
219
- /** List unresolved sync conflicts. */
220
- unresolvedConflicts(limit = 50) {
221
- return this.db.prepare(`
24
+ `).get(t,r)?.new_cursor??null}unresolvedConflicts(t=50){return this.db.prepare(`
222
25
  SELECT id, local_kind, local_id, remote_origin, detected_at, quest_id
223
26
  FROM sync_conflicts
224
27
  WHERE resolved_at IS NULL
225
28
  ORDER BY detected_at DESC LIMIT ?
226
- `).all(limit);
227
- }
228
- /** Resolve a conflict. mode: 'kept_local' | 'kept_remote' | 'merged'. */
229
- resolveConflict(conflict_id, mode, merged_payload) {
230
- const conflict = this.db.prepare('SELECT * FROM sync_conflicts WHERE id = ? AND resolved_at IS NULL').get(conflict_id);
231
- if (!conflict)
232
- return false;
233
- if (mode === 'kept_remote' || mode === 'merged') {
234
- const t = KIND_TO_TABLE[conflict.local_kind];
235
- if (t) {
236
- const payload = mode === 'merged' && merged_payload
237
- ? merged_payload
238
- : JSON.parse(conflict.remote_payload);
239
- const cols = Object.keys(payload).filter(c => c !== 'id');
240
- const setClause = cols.map(c => `${c} = ?`).join(', ');
241
- const values = cols.map(c => payload[c]);
242
- try {
243
- this.db.prepare(`UPDATE ${t.table} SET ${setClause} WHERE id = ?`).run(...values, conflict.local_id);
244
- }
245
- catch { /* best-effort */ }
246
- }
247
- }
248
- // 'kept_local' = no row mutation needed.
249
- this.db.prepare(`
29
+ `).all(t)}resolveConflict(t,r,e){const s=this.db.prepare("SELECT * FROM sync_conflicts WHERE id = ? AND resolved_at IS NULL").get(t);if(!s)return!1;if(r==="kept_remote"||r==="merged"){const o=d[s.local_kind];if(o){const a=r==="merged"&&e?e:JSON.parse(s.remote_payload),c=Object.keys(a).filter(i=>i!=="id"),l=c.map(i=>`${i} = ?`).join(", "),n=c.map(i=>a[i]);try{this.db.prepare(`UPDATE ${o.table} SET ${l} WHERE id = ?`).run(...n,s.local_id)}catch{}}}return this.db.prepare(`
250
30
  UPDATE sync_conflicts
251
31
  SET resolved_at = datetime('now'), resolution = ?
252
32
  WHERE id = ?
253
- `).run(mode, conflict_id);
254
- return true;
255
- }
256
- }
257
- //# sourceMappingURL=federation.js.map
33
+ `).run(r,t),!0}}export{E as Federation};
package/dist/goals.js CHANGED
@@ -1,97 +1,32 @@
1
- /**
2
- * Goals — persistent objectives Wyrm pursues across sessions.
3
- *
4
- * A goal is a long-lived row. Each `pursue` invocation runs one OODA
5
- * iteration (Observe → Orient → Decide → Act), appends a row to
6
- * `goal_iterations`, and updates the goal's `last_pursued_at` +
7
- * `iterations_count`. The agent loop (`wyrm-loop` daemon) picks the
8
- * highest-priority active goal each tick.
9
- *
10
- * Goals carry `max_iterations` to prevent infinite spinning on
11
- * unresolvable objectives. `success_criteria` is the operator-defined
12
- * "done when X" — the LLM checks against it each iteration.
13
- *
14
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
15
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
16
- */
17
- export class Goals {
18
- db;
19
- constructor(db) {
20
- this.db = db;
21
- }
22
- set(input) {
23
- const info = this.db.prepare(`
1
+ class n{db;constructor(t){this.db=t}set(t){const e=this.db.prepare(`
24
2
  INSERT INTO goals
25
3
  (project_id, title, description, success_criteria, deadline,
26
4
  priority, max_iterations)
27
5
  VALUES (?, ?, ?, ?, ?, ?, ?)
28
- `).run(input.project_id ?? null, input.title, input.description ?? null, input.success_criteria ?? null, input.deadline ?? null, input.priority ?? 'medium', Math.max(1, Math.min(input.max_iterations ?? 20, 200)));
29
- return this.get(info.lastInsertRowid);
30
- }
31
- get(id) {
32
- return this.db.prepare('SELECT * FROM goals WHERE id = ?').get(id) ?? null;
33
- }
34
- list(opts) {
35
- const where = [];
36
- const params = [];
37
- if (opts?.status) {
38
- where.push('status = ?');
39
- params.push(opts.status);
40
- }
41
- if (opts?.project_id != null) {
42
- where.push('project_id = ?');
43
- params.push(opts.project_id);
44
- }
45
- const sql = `
6
+ `).run(t.project_id??null,t.title,t.description??null,t.success_criteria??null,t.deadline??null,t.priority??"medium",Math.max(1,Math.min(t.max_iterations??20,200)));return this.get(e.lastInsertRowid)}get(t){return this.db.prepare("SELECT * FROM goals WHERE id = ?").get(t)??null}list(t){const e=[],a=[];t?.status&&(e.push("status = ?"),a.push(t.status)),t?.project_id!=null&&(e.push("project_id = ?"),a.push(t.project_id));const s=`
46
7
  SELECT * FROM goals
47
- ${where.length ? 'WHERE ' + where.join(' AND ') : ''}
8
+ ${e.length?"WHERE "+e.join(" AND "):""}
48
9
  ORDER BY
49
10
  CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1
50
11
  WHEN 'medium' THEN 2 ELSE 3 END,
51
12
  COALESCE(last_pursued_at, '1970-01-01') ASC,
52
13
  created_at ASC
53
14
  LIMIT ?
54
- `;
55
- params.push(opts?.limit ?? 50);
56
- return this.db.prepare(sql).all(...params);
57
- }
58
- /** Pick the next goal to pursue: highest priority, least-recently
59
- * pursued, only active goals that haven't hit iteration cap. Returns
60
- * null when there's nothing to do. */
61
- nextToPursue(projectId) {
62
- const params = [];
63
- let where = `status = 'active' AND iterations_count < max_iterations`;
64
- if (projectId != null) {
65
- where += ` AND project_id = ?`;
66
- params.push(projectId);
67
- }
68
- const sql = `
15
+ `;return a.push(t?.limit??50),this.db.prepare(s).all(...a)}nextToPursue(t){const e=[];let a="status = 'active' AND iterations_count < max_iterations";t!=null&&(a+=" AND project_id = ?",e.push(t));const s=`
69
16
  SELECT * FROM goals
70
- WHERE ${where}
17
+ WHERE ${a}
71
18
  ORDER BY
72
19
  CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1
73
20
  WHEN 'medium' THEN 2 ELSE 3 END,
74
21
  COALESCE(last_pursued_at, '1970-01-01') ASC
75
22
  LIMIT 1
76
- `;
77
- return this.db.prepare(sql).get(...params) ?? null;
78
- }
79
- /** Record one OODA iteration result and bump the goal's counters. */
80
- recordIteration(goal_id, input) {
81
- const goal = this.get(goal_id);
82
- if (!goal)
83
- throw new Error(`Goal ${goal_id} not found`);
84
- const iterationNum = goal.iterations_count + 1;
85
- const info = this.db.prepare(`
23
+ `;return this.db.prepare(s).get(...e)??null}recordIteration(t,e){const a=this.get(t);if(!a)throw new Error(`Goal ${t} not found`);const s=a.iterations_count+1,r=this.db.prepare(`
86
24
  INSERT INTO goal_iterations
87
25
  (goal_id, iteration_num, observe_summary, orient_summary,
88
26
  decided_action, action_result, outcome, notes,
89
27
  latency_ms, tokens_in, tokens_out, model)
90
28
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
91
- `).run(goal_id, iterationNum, input.observe_summary ?? null, input.orient_summary ?? null, input.decided_action ?? null, input.action_result ?? null, input.outcome, input.notes ?? null, Math.max(0, Math.round(input.latency_ms)), input.tokens_in ?? null, input.tokens_out ?? null, input.model ?? null);
92
- // Update goal counters + last_outcome. If outcome=='done', mark completed.
93
- const shouldComplete = input.outcome === 'done';
94
- this.db.prepare(`
29
+ `).run(t,s,e.observe_summary??null,e.orient_summary??null,e.decided_action??null,e.action_result??null,e.outcome,e.notes??null,Math.max(0,Math.round(e.latency_ms)),e.tokens_in??null,e.tokens_out??null,e.model??null),o=e.outcome==="done";return this.db.prepare(`
95
30
  UPDATE goals
96
31
  SET iterations_count = iterations_count + 1,
97
32
  last_pursued_at = datetime('now'),
@@ -100,47 +35,24 @@ export class Goals {
100
35
  status = CASE WHEN ? = 1 THEN 'completed' ELSE status END,
101
36
  completed_at = CASE WHEN ? = 1 THEN datetime('now') ELSE completed_at END
102
37
  WHERE id = ?
103
- `).run(input.outcome, shouldComplete ? 1 : 0, shouldComplete ? 1 : 0, goal_id);
104
- return (this.db.prepare('SELECT * FROM goal_iterations WHERE id = ?').get(info.lastInsertRowid));
105
- }
106
- iterationsFor(goal_id, limit = 50) {
107
- return this.db.prepare(`
38
+ `).run(e.outcome,o?1:0,o?1:0,t),this.db.prepare("SELECT * FROM goal_iterations WHERE id = ?").get(r.lastInsertRowid)}iterationsFor(t,e=50){return this.db.prepare(`
108
39
  SELECT * FROM goal_iterations
109
40
  WHERE goal_id = ? ORDER BY iteration_num DESC LIMIT ?
110
- `).all(goal_id, limit);
111
- }
112
- complete(id, note) {
113
- this.db.prepare(`
41
+ `).all(t,e)}complete(t,e){return this.db.prepare(`
114
42
  UPDATE goals
115
43
  SET status = 'completed',
116
44
  completed_at = datetime('now'),
117
45
  updated_at = datetime('now'),
118
46
  last_outcome = COALESCE(?, last_outcome)
119
47
  WHERE id = ?
120
- `).run(note ?? null, id);
121
- return this.get(id);
122
- }
123
- pause(id) {
124
- this.db.prepare(`
48
+ `).run(e??null,t),this.get(t)}pause(t){return this.db.prepare(`
125
49
  UPDATE goals SET status = 'paused', updated_at = datetime('now') WHERE id = ?
126
- `).run(id);
127
- return this.get(id);
128
- }
129
- resume(id) {
130
- this.db.prepare(`
50
+ `).run(t),this.get(t)}resume(t){return this.db.prepare(`
131
51
  UPDATE goals SET status = 'active', updated_at = datetime('now') WHERE id = ?
132
- `).run(id);
133
- return this.get(id);
134
- }
135
- abandon(id, reason) {
136
- this.db.prepare(`
52
+ `).run(t),this.get(t)}abandon(t,e){return this.db.prepare(`
137
53
  UPDATE goals
138
54
  SET status = 'abandoned',
139
55
  updated_at = datetime('now'),
140
56
  last_outcome = COALESCE(?, last_outcome)
141
57
  WHERE id = ?
142
- `).run(reason ?? null, id);
143
- return this.get(id);
144
- }
145
- }
146
- //# sourceMappingURL=goals.js.map
58
+ `).run(e??null,t),this.get(t)}}export{n as Goals};