agentfootprint 9.36.0 → 9.37.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.
- package/dist/adapters/hosting/firestoreSessions.js +28 -6
- package/dist/adapters/hosting/firestoreSessions.js.map +1 -1
- package/dist/adapters/hosting/googleAgentEngine.js +64 -0
- package/dist/adapters/hosting/googleAgentEngine.js.map +1 -1
- package/dist/adapters/memory/agentcore.js +7 -1
- package/dist/adapters/memory/agentcore.js.map +1 -1
- package/dist/adapters/memory/memoryBank.js +29 -8
- package/dist/adapters/memory/memoryBank.js.map +1 -1
- package/dist/adapters/memory/redis.js +28 -1
- package/dist/adapters/memory/redis.js.map +1 -1
- package/dist/artifacts/scopePath.js +13 -2
- package/dist/artifacts/scopePath.js.map +1 -1
- package/dist/esm/adapters/hosting/firestoreSessions.js +29 -7
- package/dist/esm/adapters/hosting/firestoreSessions.js.map +1 -1
- package/dist/esm/adapters/hosting/googleAgentEngine.js +64 -0
- package/dist/esm/adapters/hosting/googleAgentEngine.js.map +1 -1
- package/dist/esm/adapters/memory/agentcore.js +7 -1
- package/dist/esm/adapters/memory/agentcore.js.map +1 -1
- package/dist/esm/adapters/memory/memoryBank.d.ts +13 -3
- package/dist/esm/adapters/memory/memoryBank.js +29 -8
- package/dist/esm/adapters/memory/memoryBank.js.map +1 -1
- package/dist/esm/adapters/memory/redis.d.ts +25 -0
- package/dist/esm/adapters/memory/redis.js +28 -1
- package/dist/esm/adapters/memory/redis.js.map +1 -1
- package/dist/esm/artifacts/scopePath.d.ts +9 -1
- package/dist/esm/artifacts/scopePath.js +13 -2
- package/dist/esm/artifacts/scopePath.js.map +1 -1
- package/dist/esm/hosting/conformance/cases.d.ts +15 -0
- package/dist/esm/hosting/conformance/cases.js +395 -0
- package/dist/esm/hosting/conformance/cases.js.map +1 -0
- package/dist/esm/hosting/conformance/index.d.ts +11 -0
- package/dist/esm/hosting/conformance/index.js +11 -0
- package/dist/esm/hosting/conformance/index.js.map +1 -0
- package/dist/esm/hosting/conformance/run.d.ts +46 -0
- package/dist/esm/hosting/conformance/run.js +182 -0
- package/dist/esm/hosting/conformance/run.js.map +1 -0
- package/dist/esm/hosting/conformance/types.d.ts +140 -0
- package/dist/esm/hosting/conformance/types.js +6 -0
- package/dist/esm/hosting/conformance/types.js.map +1 -0
- package/dist/esm/hosting/errors.d.ts +34 -0
- package/dist/esm/hosting/errors.js +47 -0
- package/dist/esm/hosting/errors.js.map +1 -1
- package/dist/esm/hosting/index.d.ts +4 -1
- package/dist/esm/hosting/index.js +11 -1
- package/dist/esm/hosting/index.js.map +1 -1
- package/dist/esm/hosting/memorySessions.d.ts +5 -0
- package/dist/esm/hosting/memorySessions.js +25 -17
- package/dist/esm/hosting/memorySessions.js.map +1 -1
- package/dist/esm/hosting/sessionOwnership.d.ts +86 -0
- package/dist/esm/hosting/sessionOwnership.js +106 -0
- package/dist/esm/hosting/sessionOwnership.js.map +1 -0
- package/dist/esm/hosting/sqliteSessions.js +41 -10
- package/dist/esm/hosting/sqliteSessions.js.map +1 -1
- package/dist/esm/hosting/types.d.ts +68 -4
- package/dist/esm/hosting/types.js.map +1 -1
- package/dist/esm/memory/identity/encode.d.ts +67 -0
- package/dist/esm/memory/identity/encode.js +91 -0
- package/dist/esm/memory/identity/encode.js.map +1 -0
- package/dist/esm/memory/identity/index.d.ts +1 -0
- package/dist/esm/memory/identity/index.js +1 -0
- package/dist/esm/memory/identity/index.js.map +1 -1
- package/dist/esm/memory/identity/types.d.ts +21 -4
- package/dist/esm/memory/identity/types.js +27 -7
- package/dist/esm/memory/identity/types.js.map +1 -1
- package/dist/hosting/conformance/cases.js +398 -0
- package/dist/hosting/conformance/cases.js.map +1 -0
- package/dist/hosting/conformance/index.js +18 -0
- package/dist/hosting/conformance/index.js.map +1 -0
- package/dist/hosting/conformance/run.js +188 -0
- package/dist/hosting/conformance/run.js.map +1 -0
- package/dist/hosting/conformance/types.js +7 -0
- package/dist/hosting/conformance/types.js.map +1 -0
- package/dist/hosting/errors.js +49 -1
- package/dist/hosting/errors.js.map +1 -1
- package/dist/hosting/index.js +18 -2
- package/dist/hosting/index.js.map +1 -1
- package/dist/hosting/memorySessions.js +25 -17
- package/dist/hosting/memorySessions.js.map +1 -1
- package/dist/hosting/sessionOwnership.js +110 -0
- package/dist/hosting/sessionOwnership.js.map +1 -0
- package/dist/hosting/sqliteSessions.js +41 -10
- package/dist/hosting/sqliteSessions.js.map +1 -1
- package/dist/hosting/types.js.map +1 -1
- package/dist/memory/identity/encode.js +96 -0
- package/dist/memory/identity/encode.js.map +1 -0
- package/dist/memory/identity/index.js +5 -1
- package/dist/memory/identity/index.js.map +1 -1
- package/dist/memory/identity/types.js +27 -7
- package/dist/memory/identity/types.js.map +1 -1
- package/dist/types/adapters/hosting/firestoreSessions.d.ts.map +1 -1
- package/dist/types/adapters/hosting/googleAgentEngine.d.ts.map +1 -1
- package/dist/types/adapters/memory/agentcore.d.ts.map +1 -1
- package/dist/types/adapters/memory/memoryBank.d.ts +13 -3
- package/dist/types/adapters/memory/memoryBank.d.ts.map +1 -1
- package/dist/types/adapters/memory/redis.d.ts +25 -0
- package/dist/types/adapters/memory/redis.d.ts.map +1 -1
- package/dist/types/artifacts/scopePath.d.ts +9 -1
- package/dist/types/artifacts/scopePath.d.ts.map +1 -1
- package/dist/types/hosting/conformance/cases.d.ts +16 -0
- package/dist/types/hosting/conformance/cases.d.ts.map +1 -0
- package/dist/types/hosting/conformance/index.d.ts +12 -0
- package/dist/types/hosting/conformance/index.d.ts.map +1 -0
- package/dist/types/hosting/conformance/run.d.ts +47 -0
- package/dist/types/hosting/conformance/run.d.ts.map +1 -0
- package/dist/types/hosting/conformance/types.d.ts +141 -0
- package/dist/types/hosting/conformance/types.d.ts.map +1 -0
- package/dist/types/hosting/errors.d.ts +34 -0
- package/dist/types/hosting/errors.d.ts.map +1 -1
- package/dist/types/hosting/index.d.ts +4 -1
- package/dist/types/hosting/index.d.ts.map +1 -1
- package/dist/types/hosting/memorySessions.d.ts +5 -0
- package/dist/types/hosting/memorySessions.d.ts.map +1 -1
- package/dist/types/hosting/sessionOwnership.d.ts +87 -0
- package/dist/types/hosting/sessionOwnership.d.ts.map +1 -0
- package/dist/types/hosting/sqliteSessions.d.ts.map +1 -1
- package/dist/types/hosting/types.d.ts +68 -4
- package/dist/types/hosting/types.d.ts.map +1 -1
- package/dist/types/memory/identity/encode.d.ts +68 -0
- package/dist/types/memory/identity/encode.d.ts.map +1 -0
- package/dist/types/memory/identity/index.d.ts +1 -0
- package/dist/types/memory/identity/index.d.ts.map +1 -1
- package/dist/types/memory/identity/types.d.ts +21 -4
- package/dist/types/memory/identity/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/conformance/cases — the battery itself.
|
|
3
|
+
*
|
|
4
|
+
* Each case holds ONE law of the port and says which one in its `law` field,
|
|
5
|
+
* so a failure reads as a broken promise rather than as a broken assertion.
|
|
6
|
+
* Nothing here imports a test framework: a case throws to fail, which is the
|
|
7
|
+
* one convention every runner in every language already agrees on.
|
|
8
|
+
*/
|
|
9
|
+
import { envelopeOwner } from '../envelope.js';
|
|
10
|
+
// ─── The little assertion kit ────────────────────────────────────────
|
|
11
|
+
/** Fail this case, naming what was expected. */
|
|
12
|
+
function check(condition, message) {
|
|
13
|
+
if (!condition)
|
|
14
|
+
throw new Error(message);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The error a call failed with, or `undefined` when it succeeded.
|
|
18
|
+
*
|
|
19
|
+
* Takes a THUNK, so a store that throws synchronously where the port promises
|
|
20
|
+
* a promise is still measured on its semantics rather than blowing the case up
|
|
21
|
+
* on its calling convention. That style is its own small defect — a caller
|
|
22
|
+
* then needs two error handlers, and the one they forget is the one that fires
|
|
23
|
+
* at 3am — but it is not the law any of these cases is about.
|
|
24
|
+
*/
|
|
25
|
+
async function attempt(work) {
|
|
26
|
+
try {
|
|
27
|
+
await work();
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
return err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** The text of the conversation inside an envelope, for round-trip checks. */
|
|
35
|
+
function textOf(envelope) {
|
|
36
|
+
const data = envelope?.data;
|
|
37
|
+
const history = envelope?.format === 'flowchart-v1' ? data.conversation?.history : data.history;
|
|
38
|
+
if (!Array.isArray(history))
|
|
39
|
+
return undefined;
|
|
40
|
+
const first = history[0];
|
|
41
|
+
return typeof first?.content === 'string' ? first.content : undefined;
|
|
42
|
+
}
|
|
43
|
+
/** Read every page of a listing, so a paging bug shows up as a wrong SET. */
|
|
44
|
+
async function allPages(store, userId, limit) {
|
|
45
|
+
const ids = [];
|
|
46
|
+
const pages = [];
|
|
47
|
+
let cursor;
|
|
48
|
+
// A bound, not a `while (true)`: a store whose cursor never terminates
|
|
49
|
+
// should fail this case, not hang the suite that was checking it.
|
|
50
|
+
for (let page = 0; page < 50; page++) {
|
|
51
|
+
const result = await store.listByUser(userId, { limit, ...(cursor && { cursor }) });
|
|
52
|
+
pages.push(result);
|
|
53
|
+
for (const row of result.sessions)
|
|
54
|
+
ids.push(row.sessionId);
|
|
55
|
+
cursor = result.cursor;
|
|
56
|
+
if (cursor === undefined)
|
|
57
|
+
return { ids, pages };
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`listByUser never stopped: 50 pages of ${limit} and the cursor is still present. ` +
|
|
60
|
+
`A cursor is promised only when MORE rows exist, so a listing that keeps handing one ` +
|
|
61
|
+
`back is a sidebar that scrolls forever.`);
|
|
62
|
+
}
|
|
63
|
+
// ─── The battery ─────────────────────────────────────────────────────
|
|
64
|
+
const absentHydratesUndefined = {
|
|
65
|
+
name: 'absent-session-hydrates-undefined',
|
|
66
|
+
law: 'Only a session that was never written hydrates as `undefined`.',
|
|
67
|
+
async run(store, kit) {
|
|
68
|
+
const stored = await store.hydrate(kit.id('never-written'));
|
|
69
|
+
check(stored === undefined, `hydrate() of a session that was never written answered ${JSON.stringify(stored)}. ` +
|
|
70
|
+
`An empty store has to say "nothing here" — anything else and the composer starts a ` +
|
|
71
|
+
`conversation on top of a value it did not understand.`);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
const roundTrip = {
|
|
75
|
+
name: 'persist-hydrate-round-trip',
|
|
76
|
+
law: 'What persist stored is what hydrate answers, and the last write wins.',
|
|
77
|
+
async run(store, kit) {
|
|
78
|
+
const id = kit.id('round-trip');
|
|
79
|
+
const first = kit.envelope('turn one', 'alice');
|
|
80
|
+
await store.persist(id, first);
|
|
81
|
+
const back = await store.hydrate(id);
|
|
82
|
+
check(back !== undefined, 'hydrate() answered undefined for a session that was just written.');
|
|
83
|
+
check(back.format === first.format && back.savedAt === first.savedAt, `hydrate() answered format='${String(back.format)}' savedAt=${String(back.savedAt)}, ` +
|
|
84
|
+
`and '${first.format}'/${first.savedAt} went in. A store that rewrites either one ` +
|
|
85
|
+
`breaks the reader that branches on format and the listing that sorts on savedAt.`);
|
|
86
|
+
check(textOf(back) === 'turn one', `the stored conversation came back as ${JSON.stringify(textOf(back))}. ` +
|
|
87
|
+
`A store may re-encode an envelope; it may not change what was said.`);
|
|
88
|
+
check(envelopeOwner(back) === 'alice', 'the identity the conversation was signed with did not survive the round trip — ' +
|
|
89
|
+
'which is the fact every ownership check in this package is derived from.');
|
|
90
|
+
// Last write wins for the PAYLOAD. This is the half the ownership rule
|
|
91
|
+
// deliberately does not touch: turn two replaces turn one.
|
|
92
|
+
await store.persist(id, kit.envelope('turn two', 'alice'));
|
|
93
|
+
check(textOf(await store.hydrate(id)) === 'turn two', 'a second persist did not replace the stored conversation. Every turn after the ' +
|
|
94
|
+
'first is a second persist, so a store that keeps the first one stops the ' +
|
|
95
|
+
'conversation at one exchange.');
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
const unreadableIsNotAbsent = {
|
|
99
|
+
name: 'unreadable-is-not-absent',
|
|
100
|
+
law: 'A stored conversation this runtime cannot read is never answered as absent.',
|
|
101
|
+
harnessNeeds: ['corrupt'],
|
|
102
|
+
async run(store, kit) {
|
|
103
|
+
const id = kit.id('corrupted');
|
|
104
|
+
await store.persist(id, kit.envelope('something real', 'alice'));
|
|
105
|
+
await kit.harness.corrupt(store, id);
|
|
106
|
+
// Either answer holds the law. A store that decodes (a file, a document)
|
|
107
|
+
// refuses BY NAME, so the message points at the store that produced the
|
|
108
|
+
// bytes; a store that hands its value straight back leaves the refusal to
|
|
109
|
+
// the composer's readers. What NEITHER may do is answer `undefined` — that
|
|
110
|
+
// is a fresh start on top of a conversation that exists, and it is
|
|
111
|
+
// invisible from the outside until somebody's history is gone.
|
|
112
|
+
let answered;
|
|
113
|
+
const err = await attempt(async () => {
|
|
114
|
+
answered = await store.hydrate(id);
|
|
115
|
+
});
|
|
116
|
+
check(err !== undefined || answered !== undefined, `hydrate() answered \`undefined\` for a session whose stored bytes are not a readable ` +
|
|
117
|
+
`envelope. Absent and unreadable are different facts, and only one of them is safe ` +
|
|
118
|
+
`to answer with a fresh start.`);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
const forgetRemoves = {
|
|
122
|
+
name: 'forget-removes-the-conversation',
|
|
123
|
+
law: 'A store that can forget a session forgets it completely, and forgetting twice is not an error.',
|
|
124
|
+
members: ['forget'],
|
|
125
|
+
async run(store, kit) {
|
|
126
|
+
const forget = store.forget.bind(store);
|
|
127
|
+
const id = kit.id('forgettable');
|
|
128
|
+
await store.persist(id, kit.envelope('delete me', 'alice'));
|
|
129
|
+
await forget(id);
|
|
130
|
+
check((await store.hydrate(id)) === undefined, 'forget() left the conversation readable. A deletion that does not delete is the ' +
|
|
131
|
+
'kind of thing a person finds out about from a regulator.');
|
|
132
|
+
if (store.ownerOf !== undefined) {
|
|
133
|
+
check((await store.ownerOf(id)) === undefined, 'forget() removed the conversation and left the owner index naming somebody. ' +
|
|
134
|
+
'A listing built on that index then points at a session nobody can open.');
|
|
135
|
+
}
|
|
136
|
+
// Idempotent: a retry, a double-click and a cleanup job all do this.
|
|
137
|
+
await forget(id);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
const ownershipDerived = {
|
|
141
|
+
name: 'ownership-is-derived-from-the-envelope',
|
|
142
|
+
law: 'The owner is DERIVED from the stored conversation, never declared by whoever called persist.',
|
|
143
|
+
members: ['ownerOf'],
|
|
144
|
+
async run(store, kit) {
|
|
145
|
+
const signed = kit.id('signed');
|
|
146
|
+
await store.persist(signed, kit.envelope('mine', 'alice'));
|
|
147
|
+
check((await store.ownerOf(signed)) === 'alice', 'a conversation signed by somebody came back owned by nobody. The owner is the ' +
|
|
148
|
+
'`principal` on the stored conversation — the composer wrote it there.');
|
|
149
|
+
// A caller cannot state an owner, because there is nowhere to state one:
|
|
150
|
+
// the port takes a session id and an envelope. So the only way to try is
|
|
151
|
+
// to plant the word somewhere and see whether a store reads it. A store
|
|
152
|
+
// that does has built its permission system out of a request field.
|
|
153
|
+
const decoy = kit.id('decoy');
|
|
154
|
+
const envelope = kit.envelope('not mine', 'alice');
|
|
155
|
+
await store.persist(decoy, {
|
|
156
|
+
...envelope,
|
|
157
|
+
// Two shapes a hopeful caller might try, neither of which is where the
|
|
158
|
+
// identity lives. `unknown` first because the envelope union has no such
|
|
159
|
+
// fields — which is the point: they are not part of the contract, and a
|
|
160
|
+
// store that reads one has invented an API nobody offered.
|
|
161
|
+
owner: 'mallory',
|
|
162
|
+
userId: 'mallory',
|
|
163
|
+
});
|
|
164
|
+
const owner = await store.ownerOf(decoy);
|
|
165
|
+
check(owner === 'alice', `a planted 'owner' field on the envelope was read as the owner (got ` +
|
|
166
|
+
`${JSON.stringify(owner)}). Owning somebody's session must not be a matter of ` +
|
|
167
|
+
`asking for it.`);
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
const ownershipFillsIn = {
|
|
171
|
+
name: 'ownership-fills-in-on-a-later-signed-turn',
|
|
172
|
+
law: 'A conversation that ran anonymously and is then signed for gains that owner.',
|
|
173
|
+
members: ['ownerOf'],
|
|
174
|
+
async run(store, kit) {
|
|
175
|
+
const id = kit.id('gains-an-owner');
|
|
176
|
+
await store.persist(id, kit.envelope('who am i'));
|
|
177
|
+
check((await store.ownerOf(id)) === undefined, 'an anonymous conversation came back owned by somebody. Inventing an owner is ' +
|
|
178
|
+
'inventing an entitlement.');
|
|
179
|
+
await store.persist(id, kit.envelope('now signed in', 'alice'));
|
|
180
|
+
check((await store.ownerOf(id)) === 'alice', 'the FIRST turn that signs for a conversation owns it, and this one did not. ' +
|
|
181
|
+
'A session that gained an identity on turn two must appear in that person’s list.');
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const leanerTurnKeepsOwner = {
|
|
185
|
+
name: 'ownership-survives-a-leaner-turn',
|
|
186
|
+
law: 'A later turn carrying a LEANER identity is stored, and does not erase the owner.',
|
|
187
|
+
members: ['ownerOf'],
|
|
188
|
+
async run(store, kit) {
|
|
189
|
+
const id = kit.id('leaner-turn');
|
|
190
|
+
await store.persist(id, kit.envelope('turn one', 'alice'));
|
|
191
|
+
// The blessed flow, and the reason this suite refuses an absence rather
|
|
192
|
+
// than treating it as a conflict: a turn with no identity claims NOBODY,
|
|
193
|
+
// so it contradicts nobody. Refusing it would break a flow the contract
|
|
194
|
+
// describes as intended, to protect against a claim that was never made.
|
|
195
|
+
const err = await attempt(() => store.persist(id, kit.envelope('turn two')));
|
|
196
|
+
check(err === undefined, `persisting a turn with no identity onto an owned session was REFUSED (${String(err)}). ` +
|
|
197
|
+
`An absent identity is not a contradiction, and the port blesses this write in as ` +
|
|
198
|
+
`many words. A store that refuses it fails turns that have always worked.`);
|
|
199
|
+
check((await store.ownerOf(id)) === 'alice', 'a leaner turn erased the owner. The session would silently drop out of its ' +
|
|
200
|
+
"owner's list, which looks to them exactly like losing the conversation.");
|
|
201
|
+
check(textOf(await store.hydrate(id)) === 'turn two', 'the leaner turn was accepted and then not stored. Keeping the owner is not a ' +
|
|
202
|
+
'reason to drop the conversation.');
|
|
203
|
+
if (store.listByUser !== undefined) {
|
|
204
|
+
const { ids } = await allPages(store, 'alice', 10);
|
|
205
|
+
check(ids.includes(id), 'the owner kept the session and the listing lost it. An index and a listing that ' +
|
|
206
|
+
'disagree is a sidebar missing a conversation nobody can explain.');
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
const differentSignerRefused = {
|
|
211
|
+
name: 'ownership-is-not-taken-by-a-different-signer',
|
|
212
|
+
law: 'A turn signed by somebody else is refused WHOLE — the owner index and the stored conversation both.',
|
|
213
|
+
members: ['ownerOf'],
|
|
214
|
+
async run(store, kit) {
|
|
215
|
+
const id = kit.id('contested');
|
|
216
|
+
await store.persist(id, kit.envelope('alice speaking', 'alice'));
|
|
217
|
+
const err = await attempt(() => store.persist(id, kit.envelope('bob speaking', 'bob')));
|
|
218
|
+
check(err !== undefined, `persisting a conversation signed by a DIFFERENT person onto an owned session was ` +
|
|
219
|
+
`accepted. This is the defect the suite was written for: the write-once rule keeps ` +
|
|
220
|
+
`the first writer in the owner index and stores the second writer's entire ` +
|
|
221
|
+
`conversation, so the owner lists the session, opens it, and reads somebody else's.`);
|
|
222
|
+
// A refusal is read by whoever provoked it. It must teach, and it must
|
|
223
|
+
// not be a way to find out who is signed in or what they said.
|
|
224
|
+
const text = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
225
|
+
for (const leak of ['alice', 'bob', 'alice speaking', 'bob speaking']) {
|
|
226
|
+
check(!text.toLowerCase().includes(leak), `the refusal names '${leak}'. An ownership refusal must carry no principal, no ` +
|
|
227
|
+
`owner and no conversation text — otherwise provoking one is how you find out ` +
|
|
228
|
+
`who owns a session id.`);
|
|
229
|
+
}
|
|
230
|
+
// And the half that matters: refused means NOTHING moved.
|
|
231
|
+
check((await store.ownerOf(id)) === 'alice', 'the refusal fired and the owner changed anyway.');
|
|
232
|
+
const back = await store.hydrate(id);
|
|
233
|
+
check(textOf(back) === 'alice speaking' && envelopeOwner(back) === 'alice', `the refusal fired and the CONVERSATION was replaced anyway (stored: ` +
|
|
234
|
+
`${JSON.stringify(textOf(back))} signed by ${JSON.stringify(envelopeOwner(back))}). ` +
|
|
235
|
+
`Refusing only the index column is the original defect wearing a thrown error.`);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
const contestedWrite = {
|
|
239
|
+
name: 'contested-write-leaves-no-split-brain',
|
|
240
|
+
law: 'However two concurrent writers interleave, the owner index and the stored conversation never name different people.',
|
|
241
|
+
members: ['ownerOf'],
|
|
242
|
+
async run(store, kit) {
|
|
243
|
+
const id = kit.id('race');
|
|
244
|
+
// Both writers, on one FRESH session, started without awaiting the first —
|
|
245
|
+
// the shape a live trial reproduced against a real backend, where both
|
|
246
|
+
// calls fulfilled and the second was genuinely retried.
|
|
247
|
+
const settled = await Promise.all([
|
|
248
|
+
attempt(() => store.persist(id, kit.envelope('alice speaking', 'alice'))),
|
|
249
|
+
attempt(() => store.persist(id, kit.envelope('bob speaking', 'bob'))),
|
|
250
|
+
]);
|
|
251
|
+
check(settled.some((err) => err === undefined), `both writers were refused, so nothing was stored at all. One of them was first and ` +
|
|
252
|
+
`is entitled to its conversation; refusing both turns a contested write into an ` +
|
|
253
|
+
`outage.`);
|
|
254
|
+
const owner = await store.ownerOf(id);
|
|
255
|
+
const signer = envelopeOwner(await store.hydrate(id));
|
|
256
|
+
// The invariant, one-directional on purpose. An index that names somebody
|
|
257
|
+
// over a conversation that names nobody is the leaner-turn state, and it
|
|
258
|
+
// only ever refuses. An index and a conversation naming DIFFERENT people
|
|
259
|
+
// is the split brain: the index decides who may list and read it, the
|
|
260
|
+
// conversation decides what they read, and they disagree.
|
|
261
|
+
check(owner === undefined || signer === undefined || owner === signer, `SPLIT BRAIN: ownerOf() and the stored conversation name different people. ` +
|
|
262
|
+
`The person the index names lists this session, opens it, and reads the other ` +
|
|
263
|
+
`one's conversation.`);
|
|
264
|
+
// And the conversation that survived is a WHOLE one. A store that let both
|
|
265
|
+
// writes land field by field would satisfy the check above by accident
|
|
266
|
+
// while holding a document nobody wrote.
|
|
267
|
+
check(['alice speaking', 'bob speaking'].includes(textOf(await store.hydrate(id)) ?? ''), 'the stored conversation is neither writer’s. A contested write may lose one turn; ' +
|
|
268
|
+
'it may not invent a third.');
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
const ownerOfAmbiguity = {
|
|
272
|
+
name: 'owner-of-is-undefined-for-missing-and-unowned',
|
|
273
|
+
law: '`ownerOf` answers `undefined` for BOTH a session that does not exist and one nobody signed for.',
|
|
274
|
+
members: ['ownerOf'],
|
|
275
|
+
async run(store, kit) {
|
|
276
|
+
const missing = await store.ownerOf(kit.id('no-such-session'));
|
|
277
|
+
check(missing === undefined, `ownerOf() of a session that does not exist answered ${JSON.stringify(missing)}.`);
|
|
278
|
+
const unowned = kit.id('unowned');
|
|
279
|
+
await store.persist(unowned, kit.envelope('nobody signed this'));
|
|
280
|
+
const owner = await store.ownerOf(unowned);
|
|
281
|
+
check(owner === undefined, `ownerOf() of a session nobody signed for answered ${JSON.stringify(owner)}. ` +
|
|
282
|
+
`The two answers must be indistinguishable, or a caller has an oracle for which ` +
|
|
283
|
+
`session ids are real.`);
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
const listPages = {
|
|
287
|
+
name: 'list-by-user-pages-with-a-stable-tie-break',
|
|
288
|
+
law: 'Paging a listing shows every owned session exactly once, nobody else’s ever, and hands back a cursor only when more rows exist.',
|
|
289
|
+
members: ['listByUser'],
|
|
290
|
+
async run(store, kit) {
|
|
291
|
+
// Five for Alice sharing ONE savedAt, so the tie-break is what carries the
|
|
292
|
+
// paging rather than the timestamp. A cursor that only orders by time
|
|
293
|
+
// repeats or skips a row exactly here, and only here.
|
|
294
|
+
const tied = 1_700_000_000_000;
|
|
295
|
+
const alices = [];
|
|
296
|
+
for (let n = 0; n < 5; n++) {
|
|
297
|
+
const id = kit.id(`tied-${n}`);
|
|
298
|
+
alices.push(id);
|
|
299
|
+
await store.persist(id, kit.envelope(`alice ${n}`, 'alice', tied));
|
|
300
|
+
}
|
|
301
|
+
const bob = kit.id('bobs');
|
|
302
|
+
await store.persist(bob, kit.envelope('bob', 'bob', tied));
|
|
303
|
+
const { ids, pages } = await allPages(store, 'alice', 2);
|
|
304
|
+
check(!ids.includes(bob), "another user's session appeared in this listing. A listing IS the permission " +
|
|
305
|
+
'check for everything downstream of it.');
|
|
306
|
+
check(new Set(ids).size === ids.length, `paging repeated a session (${ids.join(', ')}). With equal timestamps a cursor needs ` +
|
|
307
|
+
`a second, total ordering — usually the session id — or a row lands on both sides ` +
|
|
308
|
+
`of a page boundary.`);
|
|
309
|
+
check(alices.every((id) => ids.includes(id)), `paging LOST a session. Seen: ${ids.join(', ')}. Written: ${alices.join(', ')}.`);
|
|
310
|
+
const last = pages[pages.length - 1];
|
|
311
|
+
check(last?.cursor === undefined, 'the final page still carried a cursor. A cursor promises MORE, so one on the last ' +
|
|
312
|
+
'page is a sidebar with a "load more" that loads nothing.');
|
|
313
|
+
for (const page of pages.slice(0, -1)) {
|
|
314
|
+
check(page.cursor !== undefined, 'a non-final page carried no cursor, so the rows after it are unreachable.');
|
|
315
|
+
}
|
|
316
|
+
// Newest first, when the timestamps actually differ.
|
|
317
|
+
const older = kit.id('older');
|
|
318
|
+
const newer = kit.id('newer');
|
|
319
|
+
await store.persist(older, kit.envelope('older', 'carol', tied - 60_000));
|
|
320
|
+
await store.persist(newer, kit.envelope('newer', 'carol', tied + 60_000));
|
|
321
|
+
const carol = await store.listByUser('carol', { limit: 10 });
|
|
322
|
+
check(carol.sessions[0]?.sessionId === newer, `the listing did not put the newest conversation first (got ` +
|
|
323
|
+
`${JSON.stringify(carol.sessions.map((s) => s.sessionId))}). Newest-first is the ` +
|
|
324
|
+
`order a sidebar draws in and the one every listing in this package promises.`);
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
const awkwardIds = {
|
|
328
|
+
name: 'awkward-session-ids-round-trip',
|
|
329
|
+
law: 'A session id is OPAQUE: whatever a caller chose comes back as its own conversation, and two different ids never collide.',
|
|
330
|
+
async run(store, kit) {
|
|
331
|
+
const awkward = [
|
|
332
|
+
'has/slashes/inside',
|
|
333
|
+
'has spaces',
|
|
334
|
+
'..',
|
|
335
|
+
'./..',
|
|
336
|
+
'ünïcødé-😀-id',
|
|
337
|
+
`quote'and"double`,
|
|
338
|
+
"'; DROP TABLE agent_sessions; --",
|
|
339
|
+
'-ish-but-not-really',
|
|
340
|
+
'a'.repeat(1000),
|
|
341
|
+
];
|
|
342
|
+
for (const [index, raw] of awkward.entries()) {
|
|
343
|
+
const id = `${kit.id('awkward')}${raw}`;
|
|
344
|
+
await store.persist(id, kit.envelope(`conversation ${index}`, 'alice'));
|
|
345
|
+
const back = await store.hydrate(id);
|
|
346
|
+
check(textOf(back) === `conversation ${index}`, `a session id the caller chose did not round-trip: ${JSON.stringify(raw)} came back ` +
|
|
347
|
+
`as ${JSON.stringify(textOf(back))}. An id is somebody's opaque string, and a ` +
|
|
348
|
+
`store that mangles it hands them a different conversation.`);
|
|
349
|
+
}
|
|
350
|
+
// Two ids that a naive normalisation would fold together.
|
|
351
|
+
const left = `${kit.id('fold')}a/b`;
|
|
352
|
+
const right = `${kit.id('fold')}a%2Fb`;
|
|
353
|
+
await store.persist(left, kit.envelope('left', 'alice'));
|
|
354
|
+
await store.persist(right, kit.envelope('right', 'alice'));
|
|
355
|
+
check(textOf(await store.hydrate(left)) === 'left' &&
|
|
356
|
+
textOf(await store.hydrate(right)) === 'right', 'two different session ids collided onto one conversation. Whatever mapping a store ' +
|
|
357
|
+
'uses to make an id legal for its backend has to be injective, or one person’s ' +
|
|
358
|
+
'turn lands in another’s conversation.');
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
const featureDetected = {
|
|
362
|
+
name: 'optional-members-are-feature-detected',
|
|
363
|
+
law: 'The optional members are present as functions or absent — never something a caller has to guess about.',
|
|
364
|
+
async run(store) {
|
|
365
|
+
for (const member of ['onWake', 'listByUser', 'ownerOf']) {
|
|
366
|
+
const value = store[member];
|
|
367
|
+
check(value === undefined || typeof value === 'function', `'${member}' is present as ${typeof value}. Feature detection reads ` +
|
|
368
|
+
`\`typeof store.${member} === 'function'\`, so a member that is present-but-not-a-` +
|
|
369
|
+
`function is detected as available and then fails at the door.`);
|
|
370
|
+
}
|
|
371
|
+
check(store.listByUser === undefined || store.ownerOf !== undefined, 'this store can LIST a user’s sessions and cannot check ownership of one. ' +
|
|
372
|
+
'A door built on it hands somebody a list and then refuses to open any of it — ' +
|
|
373
|
+
'implement `ownerOf` beside `listByUser`.');
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* The battery, in the order a store fails it most usefully: reading before
|
|
378
|
+
* writing, writing before ownership, ownership before the listing built on it.
|
|
379
|
+
*/
|
|
380
|
+
export const sessionLifecycleConformance = [
|
|
381
|
+
absentHydratesUndefined,
|
|
382
|
+
roundTrip,
|
|
383
|
+
unreadableIsNotAbsent,
|
|
384
|
+
forgetRemoves,
|
|
385
|
+
ownershipDerived,
|
|
386
|
+
ownershipFillsIn,
|
|
387
|
+
leanerTurnKeepsOwner,
|
|
388
|
+
differentSignerRefused,
|
|
389
|
+
contestedWrite,
|
|
390
|
+
ownerOfAmbiguity,
|
|
391
|
+
listPages,
|
|
392
|
+
awkwardIds,
|
|
393
|
+
featureDetected,
|
|
394
|
+
];
|
|
395
|
+
//# sourceMappingURL=cases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cases.js","sourceRoot":"","sources":["../../../../src/hosting/conformance/cases.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/conformance — the battery a store must pass to claim
|
|
3
|
+
* {@link SessionLifecycle}.
|
|
4
|
+
*
|
|
5
|
+
* Re-exported from `agentfootprint/hosting`, so an out-of-tree store imports
|
|
6
|
+
* it exactly the way it imports the port it is implementing. See the README
|
|
7
|
+
* beside this file for why it exists and how to run it.
|
|
8
|
+
*/
|
|
9
|
+
export { sessionLifecycleConformance } from './cases.js';
|
|
10
|
+
export { formatConformanceReport, runSessionLifecycleCase, runSessionLifecycleConformance, } from './run.js';
|
|
11
|
+
export type { ConformanceKit, SessionLifecycleCase, SessionLifecycleCaseName, SessionLifecycleOutcome, SessionLifecycleReport, SessionStoreHarness, SessionStoreMember, } from './types.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/conformance — the battery a store must pass to claim
|
|
3
|
+
* {@link SessionLifecycle}.
|
|
4
|
+
*
|
|
5
|
+
* Re-exported from `agentfootprint/hosting`, so an out-of-tree store imports
|
|
6
|
+
* it exactly the way it imports the port it is implementing. See the README
|
|
7
|
+
* beside this file for why it exists and how to run it.
|
|
8
|
+
*/
|
|
9
|
+
export { sessionLifecycleConformance } from './cases.js';
|
|
10
|
+
export { formatConformanceReport, runSessionLifecycleCase, runSessionLifecycleConformance, } from './run.js';
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/hosting/conformance/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hosting/conformance/run — running the battery, and saying honestly how it
|
|
3
|
+
* went.
|
|
4
|
+
*
|
|
5
|
+
* The runner owns exactly three decisions, and all three are about the ways a
|
|
6
|
+
* case can NOT run:
|
|
7
|
+
*
|
|
8
|
+
* 1. the case is about an OPTIONAL port member this store does not implement
|
|
9
|
+
* → `'not-applicable'`, naming the member. Feature detection, which is the
|
|
10
|
+
* port's own rule applied to its own battery.
|
|
11
|
+
* 2. the store implements it and cannot satisfy the case, and the harness said
|
|
12
|
+
* so by name → `'declared'`, carrying the reason. It is run ANYWAY, so a
|
|
13
|
+
* declaration that has quietly become untrue is visible rather than
|
|
14
|
+
* permanent.
|
|
15
|
+
* 3. the case needs a harness hook nobody supplied and nobody declared →
|
|
16
|
+
* `'failed'`. This is the one that matters: an undeclared skip is a pass
|
|
17
|
+
* with the evidence removed, which is the same shape as every defect this
|
|
18
|
+
* suite exists to catch.
|
|
19
|
+
*/
|
|
20
|
+
import type { SessionLifecycleCase, SessionLifecycleOutcome, SessionLifecycleReport, SessionStoreHarness } from './types.js';
|
|
21
|
+
/**
|
|
22
|
+
* Run ONE case against one store, building and disposing the store around it.
|
|
23
|
+
*
|
|
24
|
+
* Exported because a test framework wants one assertion per case: iterate
|
|
25
|
+
* {@link sessionLifecycleConformance}, call this, and turn the outcome into a
|
|
26
|
+
* `it()`. That gives per-case granularity in any framework without this module
|
|
27
|
+
* knowing what a framework is.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runSessionLifecycleCase(testCase: SessionLifecycleCase, harness: SessionStoreHarness): Promise<SessionLifecycleOutcome>;
|
|
30
|
+
/**
|
|
31
|
+
* Run the whole battery against one store and report.
|
|
32
|
+
*
|
|
33
|
+
* @example Claiming the port for a store of your own
|
|
34
|
+
* const report = await runSessionLifecycleConformance({
|
|
35
|
+
* name: 'ourOwnSessions',
|
|
36
|
+
* createStore: () => ourOwnSessions({ pool }),
|
|
37
|
+
* disposeStore: (store) => store.close(),
|
|
38
|
+
* });
|
|
39
|
+
* if (!report.ok) throw new Error(formatConformanceReport(report));
|
|
40
|
+
*/
|
|
41
|
+
export declare function runSessionLifecycleConformance(harness: SessionStoreHarness): Promise<SessionLifecycleReport>;
|
|
42
|
+
/**
|
|
43
|
+
* The report as something to put in a failure message or a log — one line per
|
|
44
|
+
* case, with the reason a case did not simply pass.
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatConformanceReport(report: SessionLifecycleReport): string;
|