@cef-ai/testing 3.5.4 → 4.0.0

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,90 +1,163 @@
1
1
  /**
2
- * A memory bank you can run a workflow against.
2
+ * THE FAKE MEMORY BANK — vault-api's memory door, in memory (contract C2 §5).
3
3
  *
4
- * WHY THIS HAD TO EXIST. `ctx.memory` was a stub that threw — nine tools, one
5
- * message saying to assert on it through a fetch mock. That was defensible
6
- * while memory was something an agent occasionally reached for. It stopped
7
- * being defensible when `remember`, `relate` and `recall` became NODES: the
8
- * runner now executes them itself, inline, and a workflow's durable output goes
9
- * through this path. A runner feature with no simulator is a runner feature
10
- * whose tests can only assert that it was ATTEMPTED.
4
+ * A JS model of the bank's own columns (the parity test diffs them against a
5
+ * SHA-pinned copy of ddc-node's DDL, `fixtures/vault-memory-ddl.sql`), served
6
+ * behind a fetch-level copy of the REST door: `POST <base>/<tool>` with the
7
+ * real request bodies, the real `{columns, rows}` projections, 204 writes, and
8
+ * vault-api's `{"error":{"code","message"}}` refusals. The agent-sdk's own
9
+ * `ctx.memory` shim runs against it unmodified — the fake is no longer a
10
+ * parallel object with method signatures of its own.
11
11
  *
12
- * That gap has a bill attached. WF1 reported "done · 6 of 8 steps ran" with
13
- * nothing persisted, twice, for two different reasons — a runner that walked
14
- * past unknown kinds in silence, and inline steps that left no trace — and
15
- * neither was caught by a unit test, because no unit test could reach the
16
- * write.
12
+ * FAITHFUL ON PURPOSE, where the old fake was not:
13
+ * - Reads are gated: scope ∈ the connection's scopes, privacy ≤ its ceiling.
14
+ * A hidden record and an absent one look the same.
15
+ * - Statuses: a new row with no status lands `accepted`; reads return
16
+ * accepted rows unless `statuses` says otherwise; `statuses` with `status`
17
+ * or `include_unaccepted` is a 400.
18
+ * - The write policy. The default is `curators` (the D1 target, and devnet):
19
+ * an ordinary connection's write is a 403; a harvester's upsert is forced
20
+ * to `proposed` and never rewrites a decided row; an analyzer writes only
21
+ * `metric:`/`obs:` ids and metric keys the rule admits.
22
+ * - A write is acked alike whether or not it landed: a hidden target, an edge
23
+ * to an invisible end, a metric on a decided record all answer 204 and
24
+ * change nothing.
25
+ * - Per platform, never a module singleton.
17
26
  *
18
- * FAITHFUL WHERE IT MATTERS, AND SMALL EVERYWHERE ELSE. Three behaviours are
19
- * real because workflows depend on them:
27
+ * APPROXIMATED, and said so: search is case-insensitive term matching over
28
+ * id, type, title and body (all terms, a trailing `*` makes a prefix), not
29
+ * FTS5 with stemming and bm25. `rank` is the negated hit count.
20
30
  *
21
- * UPSERT ON ID. `record_upsert` is idempotent on the record id, which is the
22
- * whole safety property of the store: events redeliver, and a bank where one
23
- * candidate appears four times makes every report over it quietly wrong. A
24
- * fake that appended would let a double-write pass.
25
- *
26
- * PRIVACY IS REFUSED AT THE DOOR, not defaulted. The real bank rejects a
27
- * write that does not classify itself, and these records are about real
28
- * people. A fake that accepted one would let a workflow ship that the store
29
- * then rejects in production.
30
- *
31
- * SEARCH NEEDS A TERM. The index is FTS5, so an empty match matches NOTHING
32
- * rather than everything. A fake that returned everything for "" would make
33
- * a node that silently reads nothing look like it works.
34
- *
35
- * Matching is substring rather than FTS5, with a trailing `*` tolerated, which
36
- * is close enough to test a graph and not close enough to test a query.
37
- *
38
- * PROPS, CREATED_AT AND DOMAIN follow the bank's rules too, because a view
39
- * that draws records reads them: props must be one object, a date may not be
40
- * in the future, and a write that leaves either out keeps what the record
41
- * already has (a new record is stamped now). Domain is the exception: the bank
42
- * overwrites it from every write, so leaving it out clears it. A read returns props as JSON
43
- * text, as the bank's own row does, so a workflow that parses them in
44
- * production parses them here; `bank.records` holds the object, for
45
- * assertions.
31
+ * `c3` (default true) models vault-api with contract C3: `id_prefix`,
32
+ * `domains` on `record_list`, the review stamp, the analyzer key rule, the
33
+ * `involves` harvester edge, and a 403 for a write above the writer's ceiling.
34
+ * `c3: false` models the vault-api deployed before it: those facets are
35
+ * IGNORED (as Go's JSON decoder ignores an unknown field), analyzer keys are
36
+ * the nine of the old allow-list, and an insert above the ceiling lands where
37
+ * its writer cannot see it.
38
+ */
39
+ import { type CefBuildInfo } from "@cef-ai/agent-sdk/runtime";
40
+ import type { MemoryHandle, MemoryPrivacy } from "@cef-ai/agent-sdk";
41
+ /** `record`'s columns (ddc-node memory migrations 0001, 0002, 0007; C3 §1.4). */
42
+ export declare const RECORD_COLUMNS: readonly ["id", "type", "title", "body", "scope", "privacy", "created_at", "updated_at", "domain", "status", "version", "props", "reviewed_by", "reviewed_at"];
43
+ /** `relation`'s columns (0001, 0002). */
44
+ export declare const RELATION_COLUMNS: readonly ["id", "in_id", "out_id", "type", "scope", "privacy", "created_at", "updated_at", "domain"];
45
+ /** `record_metric`'s columns (0002). */
46
+ export declare const RECORD_METRIC_COLUMNS: readonly ["record_id", "key", "value"];
47
+ /**
48
+ * Columns the model carries ahead of the pinned DDL because an approved
49
+ * contract adds them and the migration is not merged yet. The parity test
50
+ * fails when one of these appears in the DDL, so the list is emptied the day
51
+ * the fixture is refreshed past that migration.
46
52
  */
47
- /** A record as the bank stores it. */
53
+ export declare const PENDING_COLUMNS: Record<string, readonly string[]>;
54
+ /** A record as the bank stores it. `props` is JSON text, as in the bank. */
48
55
  export interface BankRecord {
49
56
  id: string;
50
57
  type: string;
51
58
  title: string;
52
59
  body: string;
53
60
  scope: string;
54
- privacy: string;
55
- /** The record's structured facts. Absent until a write sends some. */
56
- props?: Record<string, unknown>;
57
- /** When what the record describes happened (RFC 3339), named as the bank's column is. */
58
- created_at?: string;
59
- /** The subject facet. */
60
- domain?: string;
61
+ privacy: MemoryPrivacy;
62
+ created_at: string;
63
+ updated_at: string;
64
+ domain: string;
65
+ status: BankStatus;
66
+ version: number;
67
+ props: string;
68
+ reviewed_by: string | null;
69
+ reviewed_at: string | null;
61
70
  }
62
- /** A typed edge between two records. */
63
- export interface BankEdge {
64
- in: string;
65
- out: string;
71
+ export interface BankRelation {
72
+ id: string;
73
+ in_id: string;
74
+ out_id: string;
66
75
  type: string;
67
76
  scope: string;
68
- privacy: string;
69
- /** The subject facet. */
70
- domain?: string;
77
+ privacy: MemoryPrivacy;
78
+ created_at: string;
79
+ updated_at: string;
80
+ domain: string;
81
+ }
82
+ export type BankStatus = "proposed" | "accepted" | "rejected" | "superseded";
83
+ export type BankPolicy = "allow" | "curators";
84
+ export type BankRole = "harvester" | "analyzer" | "embedder" | null;
85
+ /** One connection's gate, as vault-api derives it for an agent. */
86
+ export interface BankConnection {
87
+ /** Scopes it reads and writes. `"*"` = every scope (convenient; not a real grant). */
88
+ scopes: readonly string[] | "*";
89
+ /** Its run initiator's privacy ceiling. */
90
+ ceiling: MemoryPrivacy;
91
+ role?: BankRole;
92
+ /** False = a run with no initiator: reads empty, writes 403. Default true. */
93
+ initiator?: boolean;
94
+ }
95
+ export interface FakeBankOptions {
96
+ /** Default `"curators"` (D1; what devnet runs). */
97
+ policy?: BankPolicy;
98
+ /** The connection every caller without its own entry gets. Default: every scope, `restricted`, no role. */
99
+ connection?: Partial<BankConnection>;
100
+ /** Per agent id. */
101
+ connections?: Record<string, Partial<BankConnection>>;
102
+ /** Default true: vault-api with contract C3 (see the header). */
103
+ c3?: boolean;
71
104
  }
72
105
  export interface FakeBank {
106
+ /** Records by id, as stored. */
73
107
  records: Map<string, BankRecord>;
74
- edges: BankEdge[];
75
- /** Everything filed, oldest first — for assertions that read like a story. */
108
+ /** Every relation, oldest first. */
109
+ relations: BankRelation[];
110
+ /** record id → key → value */
111
+ metrics: Map<string, Map<string, number>>;
112
+ /** Every record, oldest first — for assertions that read like a story. */
76
113
  all(): BankRecord[];
77
- refuseWrite?: (record: BankRecord) => boolean;
114
+ /** A record's props, parsed. */
115
+ props(id: string): Record<string, unknown> | undefined;
116
+ /** The options in force; mutable between calls. */
117
+ options: Required<Pick<FakeBankOptions, "policy" | "c3">> & Pick<FakeBankOptions, "connection" | "connections">;
118
+ /**
119
+ * Decide which writes the bank swallows: return true and the write is acked
120
+ * (204) and kept nowhere, as a scope the gate does not admit does.
121
+ */
122
+ refuseWrite?: (record: {
123
+ id: string;
124
+ type: string;
125
+ scope: string;
126
+ privacy: string;
127
+ }) => boolean;
128
+ /** A person files a record directly, as sent (status defaults to accepted). */
129
+ seed(record: Partial<BankRecord> & {
130
+ id: string;
131
+ type: string;
132
+ scope: string;
133
+ privacy: MemoryPrivacy;
134
+ props?: unknown;
135
+ }): BankRecord;
136
+ /** A person accepts a record: status `accepted`, review stamp set (C3 §1.4). */
137
+ accept(id: string, opts?: {
138
+ by?: string;
139
+ }): void;
140
+ /** A person rejects a record: status `rejected`, review stamp set. */
141
+ reject(id: string, opts?: {
142
+ by?: string;
143
+ }): void;
144
+ /** Status `superseded`; the stamp is kept. */
145
+ supersede(id: string): void;
146
+ /** The door: `POST <base>/<tool>`. The caller is named by the `x-agent-id` header. */
147
+ fetch: typeof fetch;
148
+ /** `…/vaults/<vaultId>/memory` */
149
+ base: string;
150
+ /** The agent-sdk `ctx.memory` handle, over this door, as one agent. */
151
+ handle(self?: {
152
+ agentId?: string;
153
+ jobId?: string;
154
+ }, build?: CefBuildInfo): MemoryHandle;
155
+ /** Every request the door served, oldest first: tool, body, and the agent that sent it. */
156
+ requests: {
157
+ tool: string;
158
+ body: Record<string, unknown>;
159
+ agentId: string;
160
+ }[];
78
161
  }
79
- /**
80
- * The nine tools `ctx.memory` wraps, over a Map.
81
- *
82
- * Returns the bank alongside so a test can assert on what is IN it, which is
83
- * the question worth asking: "the step did not throw" is not the same claim as
84
- * "the record is there, with that classification".
85
- */
86
- export declare function fakeMemoryBank(): {
87
- memory: Record<string, unknown>;
88
- bank: FakeBank;
89
- };
162
+ export declare function fakeMemoryBank(opts?: FakeBankOptions): FakeBank;
90
163
  //# sourceMappingURL=memory-bank.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-bank.d.ts","sourceRoot":"","sources":["../../src/harness/memory-bank.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wCAAwC;AACxC,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACjC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,GAAG,IAAI,UAAU,EAAE,CAAC;IAcpB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC;CAC/C;AAwBD;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI;IAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAqHpF"}
1
+ {"version":3,"file":"memory-bank.d.ts","sourceRoot":"","sources":["../../src/harness/memory-bank.ts"],"names":[],"mappings":"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"}