wyrm-mcp 6.18.0 → 6.18.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "contract": "wyrm-cloud-memory-v1",
3
- "version": "6.18.0",
3
+ "version": "6.18.1",
4
4
  "tools": [
5
5
  {
6
6
  "name": "wyrm_recall",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "wyrm-mcp",
3
- "version": "6.18.0",
4
- "description": "Wyrm the memory layer that makes AI assistants remember, learn from failures, execute long-running goals, and federate across every session. MCP server that surfaces a curated working set of tools by default (the full surface is opt-in via WYRM_PROFILE=full), spanning persistent memory, counter-pattern blocking, OODA agent loop, knowledge graph, and cross-project memory. Works with Claude / Copilot / Cursor / Windsurf / Codex.",
3
+ "version": "6.18.1",
4
+ "description": "Local-first persistent memory for AI agents over MCP. Ground truths, negative learning (recorded failures block repeats), decision causality, hybrid recall, live memory streams a structured SQLite memory on your machine, no cloud or LLM required. Claude / Copilot / Cursor / Windsurf / Codex.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
@@ -66,7 +66,13 @@
66
66
  "federation",
67
67
  "buddy-protocol",
68
68
  "skills",
69
- "ghost-protocol"
69
+ "ghost-protocol",
70
+ "local-first",
71
+ "ai-memory",
72
+ "agent-memory",
73
+ "negative-learning",
74
+ "multi-agent",
75
+ "agent-fleet"
70
76
  ],
71
77
  "author": "Ghost Protocol (Pvt) Ltd <ryan@ghosts.lk>",
72
78
  "license": "AGPL-3.0-or-later",
@@ -1,51 +0,0 @@
1
- /**
2
- * Wyrm attribution read helpers (v7 F2, T008).
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- *
7
- * Migration 20 adds nullable `agent_id`/`run_id` attribution columns to 9
8
- * tables. Every row written before v7 — and any v7 row written outside a fleet
9
- * run — carries NULL attribution. Article VI: a v6.x DB must open under v7
10
- * with 100% of its rows, and that history must stay legibly attributed, so at
11
- * every READ SITE that surfaces attribution, NULL reads as actor='legacy'.
12
- *
13
- * Precedence when rendering "who did this" (the same order is documented in
14
- * migration 20 next to the events.agent_id column):
15
- * 1. agent_id — machine identity (v7 fleet writes)
16
- * 2. actor — display identity (the existing 6.x column on
17
- * events/audit_log; it is EXTENDED, never replaced)
18
- * 3. LEGACY_ACTOR — 'legacy' (neither set: a pre-v7 row)
19
- *
20
- * Wire paths (eventsForPush, ingestRemoteEvent, replication) intentionally do
21
- * NOT coalesce: NULL stays NULL in storage and on the wire so replication
22
- * round-trips are byte-faithful. 'legacy' is a READ-TIME presentation value
23
- * only — it is never written back to the database.
24
- */
25
- /** What NULL attribution reads as at attribution-surfacing read sites. */
26
- export declare const LEGACY_ACTOR = "legacy";
27
- /**
28
- * Read a display actor from a nullable actor column.
29
- * NULL/undefined/blank -> 'legacy'; anything else passes through trimmed.
30
- */
31
- export declare function readActor(actor?: string | null): string;
32
- /** The attribution fields migration 20 adds (all nullable). */
33
- export interface AttributedRow {
34
- agent_id?: string | null;
35
- run_id?: string | null;
36
- /** Existing display-identity column on events/audit_log (6.x). */
37
- actor?: string | null;
38
- }
39
- export interface ResolvedAttribution {
40
- /** Display identity, never null: agent_id > actor > 'legacy'. */
41
- actor: string;
42
- agent_id: string | null;
43
- run_id: string | null;
44
- }
45
- /**
46
- * Resolve the full attribution for one row using the documented precedence.
47
- * Use this at any read site that surfaces who/which-run produced a row
48
- * (failure_check verdicts, wyrm_stats view=failures, event displays).
49
- */
50
- export declare function resolveAttribution(row: AttributedRow): ResolvedAttribution;
51
- //# sourceMappingURL=attribution.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,YAAY,WAAW,CAAC;AAErC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGvD;AAED,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,mBAAmB,CAQ1E"}
@@ -1,49 +0,0 @@
1
- /**
2
- * Wyrm attribution read helpers (v7 F2, T008).
3
- *
4
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
5
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
6
- *
7
- * Migration 20 adds nullable `agent_id`/`run_id` attribution columns to 9
8
- * tables. Every row written before v7 — and any v7 row written outside a fleet
9
- * run — carries NULL attribution. Article VI: a v6.x DB must open under v7
10
- * with 100% of its rows, and that history must stay legibly attributed, so at
11
- * every READ SITE that surfaces attribution, NULL reads as actor='legacy'.
12
- *
13
- * Precedence when rendering "who did this" (the same order is documented in
14
- * migration 20 next to the events.agent_id column):
15
- * 1. agent_id — machine identity (v7 fleet writes)
16
- * 2. actor — display identity (the existing 6.x column on
17
- * events/audit_log; it is EXTENDED, never replaced)
18
- * 3. LEGACY_ACTOR — 'legacy' (neither set: a pre-v7 row)
19
- *
20
- * Wire paths (eventsForPush, ingestRemoteEvent, replication) intentionally do
21
- * NOT coalesce: NULL stays NULL in storage and on the wire so replication
22
- * round-trips are byte-faithful. 'legacy' is a READ-TIME presentation value
23
- * only — it is never written back to the database.
24
- */
25
- /** What NULL attribution reads as at attribution-surfacing read sites. */
26
- export const LEGACY_ACTOR = 'legacy';
27
- /**
28
- * Read a display actor from a nullable actor column.
29
- * NULL/undefined/blank -> 'legacy'; anything else passes through trimmed.
30
- */
31
- export function readActor(actor) {
32
- const a = typeof actor === 'string' ? actor.trim() : '';
33
- return a !== '' ? a : LEGACY_ACTOR;
34
- }
35
- /**
36
- * Resolve the full attribution for one row using the documented precedence.
37
- * Use this at any read site that surfaces who/which-run produced a row
38
- * (failure_check verdicts, wyrm_stats view=failures, event displays).
39
- */
40
- export function resolveAttribution(row) {
41
- const agentId = row.agent_id ?? null;
42
- const runId = row.run_id ?? null;
43
- return {
44
- actor: agentId ?? readActor(row.actor),
45
- agent_id: agentId,
46
- run_id: runId,
47
- };
48
- }
49
- //# sourceMappingURL=attribution.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"attribution.js","sourceRoot":"","sources":["../src/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,MAAM,CAAC,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;AACrC,CAAC;AAiBD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAkB;IACnD,MAAM,OAAO,GAAG,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC;IACjC,OAAO;QACL,KAAK,EAAE,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC;QACtC,QAAQ,EAAE,OAAO;QACjB,MAAM,EAAE,KAAK;KACd,CAAC;AACJ,CAAC"}
@@ -1,112 +0,0 @@
1
- /**
2
- * Daemon-as-writer endpoint logic (v7 F2, T012) — the `POST /write` op surface.
3
- *
4
- * When a client process runs with WYRM_DAEMON_WRITES=1 (src/daemon-writer.ts),
5
- * its CANONICAL WRITES are routed over loopback HTTP to the running daemon
6
- * (http-fast), which executes them here against ITS database connection —
7
- * funneling a fleet's writes through one process instead of N contending ones.
8
- * Reads always stay local to the client.
9
- *
10
- * Extracted from http-fast.ts in the readonly-gate.ts style: DB-parameterized
11
- * and side-effect-free at module load, so the op surface is unit-testable
12
- * without booting the HTTP server.
13
- *
14
- * Article VII — minimal request surface, validated at the boundary:
15
- * - ONE endpoint, a CLOSED op allowlist (DAEMON_WRITE_OPS). Unknown ops are
16
- * rejected; there is deliberately no generic "run this SQL/method" escape.
17
- * - Each op calls the SAME domain method the client's direct path would call
18
- * (db.insertData / failures.record / truths.set / artifacts.add /
19
- * db.addQuest), so row shapes are identical in both modes.
20
- * - `project_id` is a plain integer id: client and daemon share the same
21
- * database file, so ids are stable across the loopback hop. Project-path
22
- * resolution (a read) happens client-side.
23
- * - Enum-ish fields (scope/severity/kind/...) are NOT re-validated here beyond
24
- * type checks: the domain methods + the schema CHECK constraints are the
25
- * single source of truth for value validation in BOTH modes — re-implementing
26
- * the lists here would let the two paths drift. A CHECK/FK violation comes
27
- * back to the client as the rejected `{e}` shape (see handleDaemonWrite's
28
- * error mapping), never a generic 500 the client would have to treat as
29
- * commit-state-unknown.
30
- *
31
- * Attribution (v7 F2 review fix): the caller's FULL envelope rides in the
32
- * request body as `actor: {agent_id, run_id}` — the `Wyrm-Actor` header
33
- * grammar (`agent_id[;run_id]`) cannot express a run-only envelope, so a
34
- * header-only hop silently dropped run attribution and let the DAEMON's own
35
- * env (http-fast's env-level resolution) stamp the caller's row — flipping
36
- * the T014 quarantine tier (a run-quarantined failure could land
37
- * project-wide, or an unattributed write could be quarantined to an
38
- * unrelated run). When the body carries `actor`, it is AUTHORITATIVE: the
39
- * write runs inside that exact envelope ('param' level), and both-null is an
40
- * EXPLICIT legacy envelope (columns stay NULL) — the daemon's env can never
41
- * bleed in. Bodies without `actor` (pre-fix clients) keep the ambient
42
- * header/env semantics.
43
- *
44
- * Idempotency (v7 F2 review fix): the client mints a ULID `write_id` per
45
- * logical write. Committed write_ids are remembered in an in-memory ledger
46
- * with TIME-BASED retention (DEDUP_TTL_MS — sized to the realistic caller
47
- * retry horizon: the instructed retry round-trips through an LLM agent whose
48
- * next turn is seconds to minutes away, NOT a protocol-bounded window); a
49
- * duplicate write_id inside the horizon REPLAYS the committed result instead
50
- * of re-executing — so the client's timeout re-send / instructed retry cannot
51
- * land a second row (the at-least-once duplicate the adversarial review
52
- * confirmed). The count cap (DEDUP_CAP) is a memory backstop only — under a
53
- * sustained fleet burst it, not time, may evict first. Documented residual
54
- * at-least-once windows: a daemon RESTART loses the ledger, and a retry
55
- * arriving past the retention horizon (TTL or backstop eviction) re-executes
56
- * — a VISIBLE fresh execution, never a silent stale replay.
57
- *
58
- * Privacy: ops write through the normal domain methods, so the Live Memory
59
- * surface is unchanged — failure events stay reference-only (is_shared=0).
60
- *
61
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
62
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
63
- */
64
- import type { WyrmDB } from './database.js';
65
- import type { MemoryArtifacts } from './memory-artifacts.js';
66
- import type { GroundTruths } from './intelligence.js';
67
- import type { FailurePatterns } from './failure-patterns.js';
68
- /**
69
- * The closed op allowlist — the four canonical write paths of spec FR-1
70
- * (data-lake insert, failure_record, truth_set, capture), where `capture`
71
- * decomposes into the three writes wyrm_capture can perform (quest_add /
72
- * truth_set / artifact_add). Anything else stays on the client's direct path.
73
- */
74
- export declare const DAEMON_WRITE_OPS: readonly ["data_insert", "failure_record", "truth_set", "artifact_add", "quest_add"];
75
- export type DaemonWriteOp = (typeof DAEMON_WRITE_OPS)[number];
76
- export declare function isDaemonWriteOp(value: unknown): value is DaemonWriteOp;
77
- export interface DaemonWriteDeps {
78
- db: WyrmDB;
79
- artifacts: MemoryArtifacts;
80
- truths: GroundTruths;
81
- failures: FailurePatterns;
82
- }
83
- export type DaemonWriteResult = {
84
- ok: true;
85
- op: DaemonWriteOp;
86
- row: unknown;
87
- } | {
88
- e: string;
89
- };
90
- export declare const DEDUP_TTL_MS: number;
91
- /** Test hook: clear the idempotency ledger between cases. */
92
- export declare function _resetDaemonWriteDedupForTests(): void;
93
- /** Test hook: age every ledger entry by `ms` (drives TTL eviction without a clock). */
94
- export declare function _ageDaemonWriteDedupForTests(ms: number): void;
95
- /**
96
- * Execute one allowlisted write op. Returns the written row on success or a
97
- * compact `{ e }` error (the house wire format for http-fast routes).
98
- *
99
- * Error mapping by what the throw PROVES about the commit:
100
- * - SQLITE_CONSTRAINT family (CHECK/FK/UNIQUE/NOT NULL) → the rejected `{e}`
101
- * shape carrying the real constraint message. Every op is a single
102
- * statement or one IMMEDIATE transaction, so a constraint throw proves the
103
- * rollback — the client safely fails direct and the local path surfaces
104
- * the same deterministic error to the caller. Without this, a generic 500
105
- * reads as commit-state-unknown and the same inputs re-violate forever.
106
- * - Everything else (SQLITE_BUSY → http-fast's structured 503; SQLITE_FULL,
107
- * a post-commit throw, ...) propagates — the dispatcher catch owns
108
- * transport mapping, and a non-constraint throw does NOT prove no commit,
109
- * so it must never claim the rejected shape.
110
- */
111
- export declare function handleDaemonWrite(deps: DaemonWriteDeps, args: Record<string, unknown>): DaemonWriteResult;
112
- //# sourceMappingURL=daemon-write-endpoint.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-write-endpoint.d.ts","sourceRoot":"","sources":["../src/daemon-write-endpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,eAAe,EAAsB,MAAM,uBAAuB,CAAC;AACjF,OAAO,KAAK,EAAE,YAAY,EAAmB,MAAM,mBAAmB,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAiC,MAAM,uBAAuB,CAAC;AAS5F;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,sFAMnB,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,eAAe,CAAC;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,aAAa,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAsB9F,eAAO,MAAM,YAAY,QAAc,CAAC;AAIxC,6DAA6D;AAC7D,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AAED,uFAAuF;AACvF,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7D;AAyBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,CAqDzG"}
@@ -1,290 +0,0 @@
1
- /**
2
- * Daemon-as-writer endpoint logic (v7 F2, T012) — the `POST /write` op surface.
3
- *
4
- * When a client process runs with WYRM_DAEMON_WRITES=1 (src/daemon-writer.ts),
5
- * its CANONICAL WRITES are routed over loopback HTTP to the running daemon
6
- * (http-fast), which executes them here against ITS database connection —
7
- * funneling a fleet's writes through one process instead of N contending ones.
8
- * Reads always stay local to the client.
9
- *
10
- * Extracted from http-fast.ts in the readonly-gate.ts style: DB-parameterized
11
- * and side-effect-free at module load, so the op surface is unit-testable
12
- * without booting the HTTP server.
13
- *
14
- * Article VII — minimal request surface, validated at the boundary:
15
- * - ONE endpoint, a CLOSED op allowlist (DAEMON_WRITE_OPS). Unknown ops are
16
- * rejected; there is deliberately no generic "run this SQL/method" escape.
17
- * - Each op calls the SAME domain method the client's direct path would call
18
- * (db.insertData / failures.record / truths.set / artifacts.add /
19
- * db.addQuest), so row shapes are identical in both modes.
20
- * - `project_id` is a plain integer id: client and daemon share the same
21
- * database file, so ids are stable across the loopback hop. Project-path
22
- * resolution (a read) happens client-side.
23
- * - Enum-ish fields (scope/severity/kind/...) are NOT re-validated here beyond
24
- * type checks: the domain methods + the schema CHECK constraints are the
25
- * single source of truth for value validation in BOTH modes — re-implementing
26
- * the lists here would let the two paths drift. A CHECK/FK violation comes
27
- * back to the client as the rejected `{e}` shape (see handleDaemonWrite's
28
- * error mapping), never a generic 500 the client would have to treat as
29
- * commit-state-unknown.
30
- *
31
- * Attribution (v7 F2 review fix): the caller's FULL envelope rides in the
32
- * request body as `actor: {agent_id, run_id}` — the `Wyrm-Actor` header
33
- * grammar (`agent_id[;run_id]`) cannot express a run-only envelope, so a
34
- * header-only hop silently dropped run attribution and let the DAEMON's own
35
- * env (http-fast's env-level resolution) stamp the caller's row — flipping
36
- * the T014 quarantine tier (a run-quarantined failure could land
37
- * project-wide, or an unattributed write could be quarantined to an
38
- * unrelated run). When the body carries `actor`, it is AUTHORITATIVE: the
39
- * write runs inside that exact envelope ('param' level), and both-null is an
40
- * EXPLICIT legacy envelope (columns stay NULL) — the daemon's env can never
41
- * bleed in. Bodies without `actor` (pre-fix clients) keep the ambient
42
- * header/env semantics.
43
- *
44
- * Idempotency (v7 F2 review fix): the client mints a ULID `write_id` per
45
- * logical write. Committed write_ids are remembered in an in-memory ledger
46
- * with TIME-BASED retention (DEDUP_TTL_MS — sized to the realistic caller
47
- * retry horizon: the instructed retry round-trips through an LLM agent whose
48
- * next turn is seconds to minutes away, NOT a protocol-bounded window); a
49
- * duplicate write_id inside the horizon REPLAYS the committed result instead
50
- * of re-executing — so the client's timeout re-send / instructed retry cannot
51
- * land a second row (the at-least-once duplicate the adversarial review
52
- * confirmed). The count cap (DEDUP_CAP) is a memory backstop only — under a
53
- * sustained fleet burst it, not time, may evict first. Documented residual
54
- * at-least-once windows: a daemon RESTART loses the ledger, and a retry
55
- * arriving past the retention horizon (TTL or backstop eviction) re-executes
56
- * — a VISIBLE fresh execution, never a silent stale replay.
57
- *
58
- * Privacy: ops write through the normal domain methods, so the Live Memory
59
- * surface is unchanged — failure events stay reference-only (is_shared=0).
60
- *
61
- * @copyright 2026 Ghost Protocol (Pvt) Ltd.
62
- * @license AGPL-3.0-or-later — dual-licensed; commercial terms: ghosts.lk@proton.me. See LICENSE.
63
- */
64
- import { isSqliteConstraintError } from './sqlite-busy.js';
65
- import { LEGACY_ENVELOPE, runWithActor, sanitizeActorId, } from './handlers/boundary.js';
66
- /**
67
- * The closed op allowlist — the four canonical write paths of spec FR-1
68
- * (data-lake insert, failure_record, truth_set, capture), where `capture`
69
- * decomposes into the three writes wyrm_capture can perform (quest_add /
70
- * truth_set / artifact_add). Anything else stays on the client's direct path.
71
- */
72
- export const DAEMON_WRITE_OPS = [
73
- 'data_insert',
74
- 'failure_record',
75
- 'truth_set',
76
- 'artifact_add',
77
- 'quest_add',
78
- ];
79
- export function isDaemonWriteOp(value) {
80
- return typeof value === 'string' && DAEMON_WRITE_OPS.includes(value);
81
- }
82
- /** Boundary check: a finite positive integer id (project ids are rowids). */
83
- function asProjectId(value) {
84
- return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : null;
85
- }
86
- function asString(value) {
87
- return typeof value === 'string' && value.length > 0 ? value : null;
88
- }
89
- // ── idempotency ledger (v7 F2 review fix) ───────────────────────────────────
90
- // write_id → the committed result, replayed on duplicate delivery. Retention
91
- // is TIME-based: the caller-level instructed retry round-trips through an LLM
92
- // agent (seconds to minutes — retry_after_ms is advice, not a bound), so a
93
- // count-FIFO sized to "seconds-scale" gets evicted from under a live retry by
94
- // any >cap fleet burst in that gap — re-executing a committed write_id, a
95
- // duplicate with no daemon crash. Entries live DEDUP_TTL_MS; expired entries
96
- // are swept on insert (insertion order is commit order, so they cluster at
97
- // the front). DEDUP_CAP remains only as a memory backstop ({ulid, small row}
98
- // entries are tiny). Only SUCCESSFUL (committed) results are remembered — a
99
- // rejected/thrown write left nothing behind, so re-executing it is correct.
100
- export const DEDUP_TTL_MS = 10 * 60_000;
101
- const DEDUP_CAP = 4096;
102
- const committedWrites = new Map();
103
- /** Test hook: clear the idempotency ledger between cases. */
104
- export function _resetDaemonWriteDedupForTests() {
105
- committedWrites.clear();
106
- }
107
- /** Test hook: age every ledger entry by `ms` (drives TTL eviction without a clock). */
108
- export function _ageDaemonWriteDedupForTests(ms) {
109
- for (const entry of committedWrites.values())
110
- entry.committedAt -= ms;
111
- }
112
- function rememberCommitted(writeId, result) {
113
- const now = Date.now();
114
- // Age sweep first: retention is the TTL; the count cap below is only the
115
- // memory backstop. committedAt is non-decreasing in insertion order, so the
116
- // sweep stops at the first fresh entry.
117
- for (const [id, entry] of committedWrites) {
118
- if (now - entry.committedAt < DEDUP_TTL_MS)
119
- break;
120
- committedWrites.delete(id);
121
- }
122
- if (!committedWrites.has(writeId) && committedWrites.size >= DEDUP_CAP) {
123
- const oldest = committedWrites.keys().next().value;
124
- if (oldest !== undefined)
125
- committedWrites.delete(oldest);
126
- }
127
- committedWrites.set(writeId, { result, committedAt: now });
128
- }
129
- /** Boundary check for the client-minted idempotency key (a 26-char ULID; the
130
- * bound mirrors sanitizeActorId's 64-char ceiling). Absent/invalid → no dedup
131
- * (the pre-fix client shape). */
132
- function asWriteId(value) {
133
- return typeof value === 'string' && value.length > 0 && value.length <= 64 ? value : null;
134
- }
135
- /**
136
- * Execute one allowlisted write op. Returns the written row on success or a
137
- * compact `{ e }` error (the house wire format for http-fast routes).
138
- *
139
- * Error mapping by what the throw PROVES about the commit:
140
- * - SQLITE_CONSTRAINT family (CHECK/FK/UNIQUE/NOT NULL) → the rejected `{e}`
141
- * shape carrying the real constraint message. Every op is a single
142
- * statement or one IMMEDIATE transaction, so a constraint throw proves the
143
- * rollback — the client safely fails direct and the local path surfaces
144
- * the same deterministic error to the caller. Without this, a generic 500
145
- * reads as commit-state-unknown and the same inputs re-violate forever.
146
- * - Everything else (SQLITE_BUSY → http-fast's structured 503; SQLITE_FULL,
147
- * a post-commit throw, ...) propagates — the dispatcher catch owns
148
- * transport mapping, and a non-constraint throw does NOT prove no commit,
149
- * so it must never claim the rejected shape.
150
- */
151
- export function handleDaemonWrite(deps, args) {
152
- const op = args.op;
153
- if (!isDaemonWriteOp(op))
154
- return { e: 'unknown op' };
155
- const input = (args.input ?? {});
156
- const projectId = asProjectId(args.project_id);
157
- // Idempotent replay: this write_id already committed — the response was
158
- // lost in flight. Hand back the SAME row; never execute twice. No age check
159
- // on read: an old committed result is still the truth (the TTL governs
160
- // memory retention, not correctness).
161
- const writeId = asWriteId(args.write_id);
162
- if (writeId !== null) {
163
- const replay = committedWrites.get(writeId);
164
- if (replay !== undefined)
165
- return replay.result;
166
- }
167
- // Body-supplied attribution is authoritative for the forwarded write (see
168
- // the module header): sanitize at the boundary, run the op inside that
169
- // exact envelope. Both-null → the EXPLICIT legacy envelope (NULL columns) —
170
- // never the daemon's own env. No `actor` field at all → pre-fix client:
171
- // keep the ambient (header/env) semantics unchanged.
172
- let result;
173
- try {
174
- const actorRaw = args.actor;
175
- if (actorRaw !== undefined && actorRaw !== null && typeof actorRaw === 'object') {
176
- const a = actorRaw;
177
- const agent = sanitizeActorId(a.agent_id);
178
- const run = sanitizeActorId(a.run_id);
179
- const envelope = agent === null && run === null
180
- ? LEGACY_ENVELOPE
181
- : { agent_id: agent, run_id: run, source: 'param' };
182
- result = runWithActor(envelope, () => executeDaemonOp(deps, op, projectId, args.project_id, input));
183
- }
184
- else {
185
- result = executeDaemonOp(deps, op, projectId, args.project_id, input);
186
- }
187
- }
188
- catch (err) {
189
- // Deterministic domain rejection — provably rolled back (see the function
190
- // doc). Carry the real message so the caller's direct-path retry of the
191
- // same inputs reproduces a NAMED error, not a mystery 500.
192
- if (isSqliteConstraintError(err)) {
193
- return { e: err instanceof Error ? err.message : String(err) };
194
- }
195
- // BUSY (the structured 503 contract) and anything that might have
196
- // committed stay on the throw path — never claim "no commit" for them.
197
- throw err;
198
- }
199
- // Remember only COMMITTED outcomes (post-commit by construction: the domain
200
- // method returned synchronously above).
201
- if (writeId !== null && 'ok' in result && result.ok === true) {
202
- rememberCommitted(writeId, result);
203
- }
204
- return result;
205
- }
206
- function executeDaemonOp(deps, op, projectId, rawProjectId, input) {
207
- switch (op) {
208
- case 'data_insert': {
209
- if (projectId === null)
210
- return { e: 'project_id required' };
211
- const category = asString(input.category);
212
- const key = asString(input.key);
213
- const value = asString(input.value);
214
- if (!category || !key || !value)
215
- return { e: 'category, key, value required' };
216
- // value arrives client-prepared (sanitized + maybeEncrypt'ed): the
217
- // encryption key stays in the CLIENT process (Article VII — the daemon
218
- // never needs WYRM_ENCRYPTION_KEY to serve writes).
219
- const row = deps.db.insertData(projectId, category, key, value, input.metadata);
220
- return { ok: true, op, row };
221
- }
222
- case 'failure_record': {
223
- // project_id may be null here: global (cross-project) failures are legal.
224
- if (rawProjectId != null && projectId === null)
225
- return { e: 'project_id must be an integer' };
226
- const target = asString(input.target);
227
- const description = asString(input.description);
228
- const scope = asString(input.scope);
229
- if (!scope || !target || !description)
230
- return { e: 'scope, target, description required' };
231
- const row = deps.failures.record({
232
- project_id: projectId,
233
- scope: scope,
234
- target,
235
- description,
236
- why_failed: typeof input.why_failed === 'string' ? input.why_failed : undefined,
237
- severity: typeof input.severity === 'string' ? input.severity : undefined,
238
- });
239
- return { ok: true, op, row };
240
- }
241
- case 'truth_set': {
242
- if (projectId === null)
243
- return { e: 'project_id required' };
244
- const category = asString(input.category);
245
- const key = asString(input.key);
246
- const value = asString(input.value);
247
- if (!category || !key || !value)
248
- return { e: 'category, key, value required' };
249
- const row = deps.truths.set(projectId, {
250
- category, key, value,
251
- rationale: typeof input.rationale === 'string' ? input.rationale : undefined,
252
- source: typeof input.source === 'string' ? input.source : undefined,
253
- confidence: typeof input.confidence === 'number' ? input.confidence : undefined,
254
- ttl_days: typeof input.ttl_days === 'number' ? input.ttl_days : undefined,
255
- });
256
- return { ok: true, op, row };
257
- }
258
- case 'artifact_add': {
259
- if (projectId === null)
260
- return { e: 'project_id required' };
261
- const kind = asString(input.kind);
262
- const problem = asString(input.problem);
263
- if (!kind || !problem)
264
- return { e: 'kind, problem required' };
265
- const row = deps.artifacts.add(projectId, {
266
- kind, problem,
267
- constraints: typeof input.constraints === 'string' ? input.constraints : undefined,
268
- validatedFix: typeof input.validatedFix === 'string' ? input.validatedFix : undefined,
269
- whyItWorked: typeof input.whyItWorked === 'string' ? input.whyItWorked : undefined,
270
- outcome: typeof input.outcome === 'string' ? input.outcome : undefined,
271
- sourceSessionId: typeof input.sourceSessionId === 'number' ? input.sourceSessionId : undefined,
272
- tags: Array.isArray(input.tags) ? input.tags.filter((t) => typeof t === 'string') : undefined,
273
- confidence: typeof input.confidence === 'number' ? input.confidence : undefined,
274
- createdBy: typeof input.createdBy === 'string' ? input.createdBy : undefined,
275
- needsReview: typeof input.needsReview === 'number' ? input.needsReview : undefined,
276
- });
277
- return { ok: true, op, row };
278
- }
279
- case 'quest_add': {
280
- if (projectId === null)
281
- return { e: 'project_id required' };
282
- const title = asString(input.title);
283
- if (!title)
284
- return { e: 'title required' };
285
- const row = deps.db.addQuest(projectId, title, typeof input.description === 'string' ? input.description : undefined, (typeof input.priority === 'string' ? input.priority : 'medium'), typeof input.tags === 'string' ? input.tags : undefined);
286
- return { ok: true, op, row };
287
- }
288
- }
289
- }
290
- //# sourceMappingURL=daemon-write-endpoint.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"daemon-write-endpoint.js","sourceRoot":"","sources":["../src/daemon-write-endpoint.ts"],"names":[],"mappings":"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"}