aismemory 0.6.2 → 0.7.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.
Files changed (42) hide show
  1. package/dist/__tests__/agent-load-persistence.test.js +194 -56
  2. package/dist/__tests__/agent-load-persistence.test.js.map +1 -1
  3. package/dist/__tests__/agent-load-session-override-e2e.test.d.ts +1 -0
  4. package/dist/__tests__/agent-load-session-override-e2e.test.js +214 -0
  5. package/dist/__tests__/agent-load-session-override-e2e.test.js.map +1 -0
  6. package/dist/__tests__/agent-preference.test.d.ts +1 -0
  7. package/dist/__tests__/agent-preference.test.js +173 -0
  8. package/dist/__tests__/agent-preference.test.js.map +1 -0
  9. package/dist/__tests__/device-flow-gate-e2e.test.d.ts +1 -0
  10. package/dist/__tests__/device-flow-gate-e2e.test.js +244 -0
  11. package/dist/__tests__/device-flow-gate-e2e.test.js.map +1 -0
  12. package/dist/__tests__/memory-write-failure.test.js +30 -0
  13. package/dist/__tests__/memory-write-failure.test.js.map +1 -1
  14. package/dist/__tests__/reconnect-survival-e2e.test.d.ts +1 -0
  15. package/dist/__tests__/reconnect-survival-e2e.test.js +248 -0
  16. package/dist/__tests__/reconnect-survival-e2e.test.js.map +1 -0
  17. package/dist/__tests__/silent-renewal-e2e.test.d.ts +1 -0
  18. package/dist/__tests__/silent-renewal-e2e.test.js +249 -0
  19. package/dist/__tests__/silent-renewal-e2e.test.js.map +1 -0
  20. package/dist/__tests__/split-brain-upgrade-e2e.test.d.ts +1 -0
  21. package/dist/__tests__/split-brain-upgrade-e2e.test.js +206 -0
  22. package/dist/__tests__/split-brain-upgrade-e2e.test.js.map +1 -0
  23. package/dist/__tests__/token-manager.test.d.ts +1 -0
  24. package/dist/__tests__/token-manager.test.js +403 -0
  25. package/dist/__tests__/token-manager.test.js.map +1 -0
  26. package/dist/__tests__/token-rejection-reauth-e2e.test.d.ts +1 -0
  27. package/dist/__tests__/token-rejection-reauth-e2e.test.js +219 -0
  28. package/dist/__tests__/token-rejection-reauth-e2e.test.js.map +1 -0
  29. package/dist/agent-preference.d.ts +59 -0
  30. package/dist/agent-preference.js +127 -0
  31. package/dist/agent-preference.js.map +1 -0
  32. package/dist/index.js +129 -51
  33. package/dist/index.js.map +1 -1
  34. package/dist/key-auth.d.ts +8 -0
  35. package/dist/key-auth.js +13 -0
  36. package/dist/key-auth.js.map +1 -1
  37. package/dist/telemetry.d.ts +1 -1
  38. package/dist/telemetry.js.map +1 -1
  39. package/dist/token-manager.d.ts +83 -0
  40. package/dist/token-manager.js +237 -0
  41. package/dist/token-manager.js.map +1 -0
  42. package/package.json +1 -1
@@ -0,0 +1,248 @@
1
+ /**
2
+ * CORBOT-48DD2E25 (F5b): no-enrolled-key reconnect-survival regression lock.
3
+ *
4
+ * Reproduces the exact regression the adjudication traced: a fresh device
5
+ * flow completes for a user whose device-key auto-enroll FAILS
6
+ * (`register-failed` — e.g. a network blip, or an AIS that rejects the
7
+ * registration). In 0.7.0, the device-flow completion path
8
+ * (`startDeviceFlow`, index.ts) only ever called `saveAgentPreference()`
9
+ * (ids only, no token) — so on the very next silent stdio respawn,
10
+ * `loadCredentials()` finds no usable token and the user is forced to
11
+ * re-bond from scratch. 0.6.2 never had this problem: it persisted the
12
+ * real token and reconnects were silent.
13
+ *
14
+ * The fix (F5b, agent-preference.ts `migrateCredentialsFileIfNeeded` +
15
+ * index.ts device-flow completion): for the no-enrolled-key cohort, the v1
16
+ * token IS persisted and is NOT migrated away until a key gets enrolled or
17
+ * a fresh device flow overwrites it. This test drives a full device flow
18
+ * to completion (against a fake AIS that always fails key registration),
19
+ * kills the process, and respawns it — asserting the second process
20
+ * authenticates silently, with zero additional `/v1/oauth/device/authorize`
21
+ * calls.
22
+ */
23
+ import { createServer } from 'node:http';
24
+ import { spawn } from 'node:child_process';
25
+ import { mkdtempSync, rmSync, existsSync, readFileSync } from 'node:fs';
26
+ import { tmpdir } from 'node:os';
27
+ import { join, resolve } from 'path';
28
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
29
+ function mintJwt() {
30
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
31
+ const payload = Buffer.from(JSON.stringify({ sub: 'user-1', user_id: 'user-1', tenant_id: 'tenant-1' })).toString('base64url');
32
+ return `${header}.${payload}.fakesig`;
33
+ }
34
+ describe('no-enrolled-key cohort: a stdio respawn after a completed bond never re-demands a bond (0.6.2 parity)', () => {
35
+ let fakeAis;
36
+ let port = 0;
37
+ let deviceAuthorizeCalls = 0;
38
+ let keyRegistrationAttempts = 0;
39
+ const bondToken = mintJwt();
40
+ beforeAll(async () => {
41
+ fakeAis = createServer((req, res) => {
42
+ req.on('data', () => { });
43
+ req.on('end', () => {
44
+ const url = req.url ?? '';
45
+ if (url === '/v1/oauth/device/authorize' && req.method === 'POST') {
46
+ deviceAuthorizeCalls += 1;
47
+ const userCode = `RECON-${String(deviceAuthorizeCalls).padStart(4, '0')}`;
48
+ res.writeHead(200, { 'content-type': 'application/json' });
49
+ res.end(JSON.stringify({
50
+ success: true,
51
+ data: {
52
+ device_code: `device-${deviceAuthorizeCalls}`,
53
+ user_code: userCode,
54
+ verification_uri: `http://127.0.0.1:${port}/activate`,
55
+ verification_uri_complete: `http://127.0.0.1:${port}/activate?user_code=${userCode}`,
56
+ expires_in: 1800,
57
+ interval: 5,
58
+ agent_id: `agent-provisional-${deviceAuthorizeCalls}`,
59
+ provisional_tenant_id: `tenant-provisional-${deviceAuthorizeCalls}`,
60
+ provisional_token: `provisional-token-${deviceAuthorizeCalls}`,
61
+ },
62
+ }));
63
+ return;
64
+ }
65
+ if (url === '/v1/oauth/token' && req.method === 'POST') {
66
+ // Approves on the very first poll — no pending state needed.
67
+ res.writeHead(200, { 'content-type': 'application/json' });
68
+ res.end(JSON.stringify({
69
+ access_token: bondToken,
70
+ expires_in: 7 * 24 * 3600,
71
+ }));
72
+ return;
73
+ }
74
+ if (url === '/v1/users/me/keys' && req.method === 'POST') {
75
+ // Simulate the "register-failed" cohort: auto-enroll never
76
+ // succeeds, so hasEnrolledKey() stays false across respawns.
77
+ keyRegistrationAttempts += 1;
78
+ res.writeHead(500, { 'content-type': 'application/json' });
79
+ res.end(JSON.stringify({ success: false, error: { message: 'simulated registration failure' } }));
80
+ return;
81
+ }
82
+ if (url.startsWith('/v1/agents') && !url.includes('/v1/agents/agent-') && req.method === 'GET') {
83
+ res.writeHead(200, { 'content-type': 'application/json' });
84
+ res.end(JSON.stringify({
85
+ success: true,
86
+ data: {
87
+ agents: [{ id: 'agent-1', name: 'Agent1', status: 'active' }],
88
+ pagination: { total: 1, page: 1, limit: 100, hasMore: false },
89
+ },
90
+ }));
91
+ return;
92
+ }
93
+ if (url === '/v1/agents/agent-1' && req.method === 'GET') {
94
+ res.writeHead(200, { 'content-type': 'application/json' });
95
+ res.end(JSON.stringify({ success: true, data: { id: 'agent-1', name: 'Agent1', status: 'active' } }));
96
+ return;
97
+ }
98
+ // hydration / memory-refresh / handoff / resolve — generic success.
99
+ res.writeHead(200, { 'content-type': 'application/json' });
100
+ res.end(JSON.stringify({ success: true, data: {} }));
101
+ });
102
+ });
103
+ await new Promise((resolveListen) => {
104
+ fakeAis.listen(0, '127.0.0.1', () => {
105
+ const addr = fakeAis.address();
106
+ if (addr && typeof addr === 'object')
107
+ port = addr.port;
108
+ resolveListen();
109
+ });
110
+ });
111
+ });
112
+ afterAll(async () => {
113
+ await new Promise((r) => fakeAis?.close(() => r()));
114
+ });
115
+ beforeEach(() => {
116
+ deviceAuthorizeCalls = 0;
117
+ keyRegistrationAttempts = 0;
118
+ });
119
+ it('bonds once, respawns, and reconnects silently using the persisted token', async () => {
120
+ const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
121
+ if (!existsSync(distPath)) {
122
+ throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
123
+ }
124
+ // Fresh home — no key, no credentials.json. Exactly first-ever launch.
125
+ const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-reconnect-'));
126
+ let proc1 = null;
127
+ let proc2 = null;
128
+ try {
129
+ // ── Process 1: drive the device flow to completion ──────────────
130
+ proc1 = spawn('node', [distPath], {
131
+ env: { ...process.env, HOME: fakeHome, AIS_URL: `http://127.0.0.1:${port}` },
132
+ stdio: ['pipe', 'pipe', 'pipe'],
133
+ });
134
+ let stdoutBuf1 = '';
135
+ proc1.stdout.on('data', (d) => { stdoutBuf1 += d.toString(); });
136
+ proc1.stderr.on('data', () => { });
137
+ proc1.stdin.write(JSON.stringify({
138
+ jsonrpc: '2.0', id: 0, method: 'initialize',
139
+ params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'reconnect-test-1', version: '0.0.0' } },
140
+ }) + '\n');
141
+ await waitFor(() => stdoutBuf1.includes('"id":0'), 5000, 'proc1 initialize response');
142
+ // Retry whoami until the device flow (5s poll interval) completes —
143
+ // mirrors how a real MCP client keeps calling tools until bonded.
144
+ let nextId = 1;
145
+ let agentBonded = false;
146
+ const deadline = Date.now() + 25000;
147
+ while (Date.now() < deadline && !agentBonded) {
148
+ const id = nextId++;
149
+ stdoutBuf1 = ''; // only inspect this call's response
150
+ proc1.stdin.write(JSON.stringify({
151
+ jsonrpc: '2.0', id, method: 'tools/call', params: { name: 'whoami', arguments: {} },
152
+ }) + '\n');
153
+ try {
154
+ await waitFor(() => stdoutBuf1.includes(`"id":${id}`), 8000, `proc1 whoami #${id} response`);
155
+ }
156
+ catch {
157
+ continue;
158
+ }
159
+ const text = extractResponseText(stdoutBuf1, id);
160
+ if (text.includes('agent-1') || text.includes('agent-provisional')) {
161
+ agentBonded = true;
162
+ }
163
+ }
164
+ expect(agentBonded).toBe(true);
165
+ expect(deviceAuthorizeCalls).toBe(1);
166
+ // Give the async saveAgentPreference/saveOAuthCredentials write (and
167
+ // the failed key-enroll attempt) a moment to land on disk.
168
+ await new Promise((r) => setTimeout(r, 500));
169
+ // Confirms this genuinely exercises the "enroll failed" cohort — the
170
+ // key auto-enroll was attempted (and rejected by the fake AIS) at
171
+ // least once, so hasEnrolledKey() stays false for process 1.
172
+ expect(keyRegistrationAttempts).toBeGreaterThan(0);
173
+ // F5b core assertion: the no-key cohort's token IS on disk after a
174
+ // completed bond — never stranded with only ids.
175
+ const credsPath = join(fakeHome, '.aismemory', 'credentials.json');
176
+ const raw = readFileSync(credsPath, 'utf-8');
177
+ const parsed = JSON.parse(raw);
178
+ expect(typeof parsed['token']).toBe('string');
179
+ expect(parsed['token']).toBe(bondToken);
180
+ proc1.kill();
181
+ proc1 = null;
182
+ // ── Process 2: silent stdio respawn — must reconnect with NO new
183
+ // device-authorize call and NO bond prompt. ──────────────────────
184
+ proc2 = spawn('node', [distPath], {
185
+ env: { ...process.env, HOME: fakeHome, AIS_URL: `http://127.0.0.1:${port}` },
186
+ stdio: ['pipe', 'pipe', 'pipe'],
187
+ });
188
+ let stdoutBuf2 = '';
189
+ proc2.stdout.on('data', (d) => { stdoutBuf2 += d.toString(); });
190
+ proc2.stderr.on('data', () => { });
191
+ proc2.stdin.write(JSON.stringify({
192
+ jsonrpc: '2.0', id: 0, method: 'initialize',
193
+ params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'reconnect-test-2', version: '0.0.0' } },
194
+ }) + '\n');
195
+ await waitFor(() => stdoutBuf2.includes('"id":0'), 5000, 'proc2 initialize response');
196
+ const id2 = 100;
197
+ proc2.stdin.write(JSON.stringify({
198
+ jsonrpc: '2.0', id: id2, method: 'tools/call', params: { name: 'whoami', arguments: {} },
199
+ }) + '\n');
200
+ await waitFor(() => stdoutBuf2.includes(`"id":${id2}`), 10000, 'proc2 whoami response');
201
+ const text2 = extractResponseText(stdoutBuf2, id2);
202
+ expect(text2).not.toContain('Memory bond required');
203
+ expect(text2).toContain('agent-1');
204
+ // No new device flow was started — the respawn used the persisted token.
205
+ expect(deviceAuthorizeCalls).toBe(1);
206
+ }
207
+ finally {
208
+ proc1?.kill();
209
+ proc2?.kill();
210
+ try {
211
+ rmSync(fakeHome, { recursive: true, force: true });
212
+ }
213
+ catch { /* ignore */ }
214
+ }
215
+ }, 60000);
216
+ });
217
+ function waitFor(predicate, ms, label) {
218
+ return new Promise((resolveOuter, reject) => {
219
+ const start = Date.now();
220
+ const tick = () => {
221
+ if (predicate()) {
222
+ resolveOuter();
223
+ return;
224
+ }
225
+ if (Date.now() - start > ms) {
226
+ reject(new Error(`timeout waiting for ${label}`));
227
+ return;
228
+ }
229
+ setTimeout(tick, 50);
230
+ };
231
+ tick();
232
+ });
233
+ }
234
+ function extractResponseText(buf, id) {
235
+ for (const line of buf.split('\n')) {
236
+ if (!line.includes(`"id":${id}`))
237
+ continue;
238
+ try {
239
+ const parsed = JSON.parse(line);
240
+ const text = parsed.result?.content?.[0]?.text;
241
+ if (typeof text === 'string')
242
+ return text;
243
+ }
244
+ catch { /* malformed line; skip */ }
245
+ }
246
+ return '';
247
+ }
248
+ //# sourceMappingURL=reconnect-survival-e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconnect-survival-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/reconnect-survival-e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Task 8 (token-lifecycle redesign): proves the release's headline
3
+ * client-side invariants against the REAL built binary and a real
4
+ * TokenManager (see `../token-manager.ts`):
5
+ *
6
+ * 1. Renewal is silent — it happens in the background, driven purely by
7
+ * the manager's internal timer (75% of TTL), never by a tool call.
8
+ * 2. Nothing token-shaped ever touches disk — `~/.aismemory/credentials.json`
9
+ * holds only `{schemaVersion, agentId, tenantId}` (see
10
+ * `agent-preference.ts`), never a `token` key or the raw token text.
11
+ * 3. A sustained AIS outage degrades to a structured, self-healing error
12
+ * (`AisUnreachableError`) once the current token hard-expires — never a
13
+ * crash, never a device-flow prompt (this is the key-enrolled path).
14
+ * 4. Recovery needs no restart: once AIS comes back, the next backed-off
15
+ * renewal attempt (or an on-demand attempt made after the backoff
16
+ * window elapses) succeeds and `whoami` works again.
17
+ *
18
+ * The fake AIS issues short-lived tokens (`SHORT_TTL_MS`) so a 75% renewal
19
+ * and a hard expiry both land inside the test's real wall-clock budget — no
20
+ * injected clock, this is the actual spawned process on a real timer.
21
+ * `SHORT_TTL_MS` must clear `scheduleRenewal`'s 30s floor (CORBOT-4F7CAA8E /
22
+ * F7) with margin so 75% of it is used as-is rather than being clamped up to
23
+ * the floor — this pushes the real-time budget well past what an 8s TTL
24
+ * needed, hence the longer `it()` timeout below.
25
+ */
26
+ import { createServer } from 'node:http';
27
+ import { spawn } from 'node:child_process';
28
+ import { mkdtempSync, mkdirSync, rmSync, existsSync, readFileSync } from 'node:fs';
29
+ import { tmpdir } from 'node:os';
30
+ import { join, resolve } from 'path';
31
+ import { describe, it, expect, beforeEach } from 'vitest';
32
+ import { generateAndSaveKeypair } from '../key-auth.js';
33
+ /** 75% renewal (~31.5s) and hard expiry (~42s) both land inside the test's (extended) wall-clock budget. */
34
+ const SHORT_TTL_MS = 42_000;
35
+ function mintJwt(callNumber) {
36
+ const header = Buffer.from(JSON.stringify({ alg: 'HS256', typ: 'JWT' })).toString('base64url');
37
+ const payload = Buffer.from(JSON.stringify({ sub: 'user-1', user_id: 'user-1', tenant_id: 'tenant-1', call: callNumber, r: Math.random() })).toString('base64url');
38
+ return `${header}.${payload}.fakesig-${callNumber}`;
39
+ }
40
+ /** Route table shared by the initial fake AIS server and its post-outage restart on the same port. */
41
+ function createFakeAis(state) {
42
+ return createServer((req, res) => {
43
+ req.on('data', () => { });
44
+ req.on('end', () => {
45
+ const url = req.url ?? '';
46
+ if (url === '/v1/auth/challenge' && req.method === 'POST') {
47
+ state.challengeCalls += 1;
48
+ res.writeHead(200, { 'content-type': 'application/json' });
49
+ res.end(JSON.stringify({
50
+ success: true,
51
+ data: { nonce: 'test-nonce', exp: Math.floor(Date.now() / 1000) + 60, hmac: 'test-hmac' },
52
+ }));
53
+ return;
54
+ }
55
+ if (url === '/v1/auth/did-prove' && req.method === 'POST') {
56
+ state.proveCalls += 1;
57
+ const token = mintJwt(state.proveCalls);
58
+ state.issuedTokens.push(token);
59
+ const expiresAtMs = Date.now() + SHORT_TTL_MS;
60
+ state.lastExpiresAtMs = expiresAtMs;
61
+ res.writeHead(200, { 'content-type': 'application/json' });
62
+ res.end(JSON.stringify({
63
+ success: true,
64
+ data: { bearerToken: token, expiresAt: new Date(expiresAtMs).toISOString() },
65
+ }));
66
+ return;
67
+ }
68
+ if (url.startsWith('/v1/agents') && !url.includes('/v1/agents/agent-') && req.method === 'GET') {
69
+ res.writeHead(200, { 'content-type': 'application/json' });
70
+ res.end(JSON.stringify({
71
+ success: true,
72
+ data: {
73
+ agents: [{ id: 'agent-1', name: 'Agent1', status: 'active' }],
74
+ pagination: { total: 1, page: 1, limit: 100, hasMore: false },
75
+ },
76
+ }));
77
+ return;
78
+ }
79
+ if (url === '/v1/agents/agent-1' && req.method === 'GET') {
80
+ res.writeHead(200, { 'content-type': 'application/json' });
81
+ res.end(JSON.stringify({ success: true, data: { id: 'agent-1', name: 'Agent1', status: 'active' } }));
82
+ return;
83
+ }
84
+ // Hydration / memory refresh / handoff / resolve — generic success.
85
+ res.writeHead(200, { 'content-type': 'application/json' });
86
+ res.end(JSON.stringify({ success: true, data: {} }));
87
+ });
88
+ });
89
+ }
90
+ function listen(server, port) {
91
+ return new Promise((resolveListen) => {
92
+ server.listen(port, '127.0.0.1', () => {
93
+ const addr = server.address();
94
+ resolveListen(addr && typeof addr === 'object' ? addr.port : port);
95
+ });
96
+ });
97
+ }
98
+ /** Force-close a server even if the client is holding a keep-alive socket open. */
99
+ function closeServer(server) {
100
+ return new Promise((resolveClose) => {
101
+ server.closeAllConnections();
102
+ server.close(() => resolveClose());
103
+ });
104
+ }
105
+ describe('silent renewal, disk hygiene, and outage self-heal (key-enrolled path)', () => {
106
+ let state;
107
+ beforeEach(() => {
108
+ state = { challengeCalls: 0, proveCalls: 0, issuedTokens: [], lastExpiresAtMs: 0 };
109
+ });
110
+ it('renews silently in the background, keeps no token at rest, and self-heals a sustained outage', async () => {
111
+ const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
112
+ if (!existsSync(distPath)) {
113
+ throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
114
+ }
115
+ let fakeAis = createFakeAis(state);
116
+ const port = await listen(fakeAis, 0);
117
+ const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-silent-renewal-'));
118
+ const aismemDir = join(fakeHome, '.aismemory');
119
+ mkdirSync(aismemDir, { recursive: true });
120
+ await generateAndSaveKeypair({
121
+ userId: 'user-1',
122
+ userDid: 'did:web:test:users:user-1',
123
+ keysDir: join(aismemDir, 'keys'),
124
+ });
125
+ let proc = null;
126
+ const sentToolCallIds = [];
127
+ let nextId = 1;
128
+ function sendToolCall(procRef, name, args = {}) {
129
+ const id = nextId++;
130
+ sentToolCallIds.push(id);
131
+ procRef.stdin.write(JSON.stringify({
132
+ jsonrpc: '2.0', id, method: 'tools/call', params: { name, arguments: args },
133
+ }) + '\n');
134
+ return id;
135
+ }
136
+ try {
137
+ proc = spawn('node', [distPath], {
138
+ env: { ...process.env, HOME: fakeHome, AIS_URL: `http://127.0.0.1:${port}` },
139
+ stdio: ['pipe', 'pipe', 'pipe'],
140
+ });
141
+ let stdoutBuf = '';
142
+ let stderrBuf = '';
143
+ proc.stdout.on('data', (d) => { stdoutBuf += d.toString(); });
144
+ proc.stderr.on('data', (d) => { stderrBuf += d.toString(); });
145
+ proc.stdin.write(JSON.stringify({
146
+ jsonrpc: '2.0', id: 0, method: 'initialize',
147
+ params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'silent-renewal-test', version: '0.0.0' } },
148
+ }) + '\n');
149
+ await waitFor(() => stdoutBuf.includes('"id":0'), 5000, 'initialize response');
150
+ // ── (a) first whoami: ok, establishes the manager's first acquire ──
151
+ const id1 = sendToolCall(proc, 'whoami');
152
+ await waitFor(() => stdoutBuf.includes(`"id":${id1}`), 15000, 'first whoami response');
153
+ expect(extractResponseText(stdoutBuf, id1)).toContain('agent-1');
154
+ expect(state.proveCalls).toBe(1);
155
+ // ── (b) wait for a background renewal — driven purely by the manager's
156
+ // internal timer, never by a tool call. We send nothing while waiting,
157
+ // then assert explicitly that no tool call was dispatched in between. ──
158
+ const toolCallCountBeforeWait = sentToolCallIds.length;
159
+ await waitFor(() => state.proveCalls >= 2, 40000, 'background renewal (prove call #2)');
160
+ expect(state.proveCalls).toBeGreaterThanOrEqual(2);
161
+ expect(sentToolCallIds.length).toBe(toolCallCountBeforeWait); // zero tool calls between (a) and the renewal
162
+ // ── (c) whoami again: ok, and the bond prompt never fired on this
163
+ // key-enrolled path (no device flow should ever have started). ──
164
+ const id2 = sendToolCall(proc, 'whoami');
165
+ await waitFor(() => stdoutBuf.includes(`"id":${id2}`), 15000, 'whoami after renewal');
166
+ expect(extractResponseText(stdoutBuf, id2)).toContain('agent-1');
167
+ // "No saved credentials. Bond URL will be surfaced on first tool
168
+ // call." is a benign generic startup notice printed regardless of
169
+ // auth mode (loadCredentials() never resolves the v2 agent-preference
170
+ // shape); the actual bond PROMPT never fired on this key-enrolled
171
+ // path, so check for that specifically rather than the substring
172
+ // "bond" (which the benign notice also contains).
173
+ expect(stderrBuf.toLowerCase()).not.toContain('memory bond required');
174
+ expect(stderrBuf).not.toContain('Starting authentication');
175
+ // ── (d) disk hygiene: credentials.json has no token key, and its raw
176
+ // bytes contain none of the token strings the fake server issued. ──
177
+ const credsPath = join(aismemDir, 'credentials.json');
178
+ const raw = readFileSync(credsPath, 'utf-8');
179
+ const savedCreds = JSON.parse(raw);
180
+ expect(savedCreds.schemaVersion).toBe(2);
181
+ expect(savedCreds.agentId).toBe('agent-1');
182
+ expect(Object.prototype.hasOwnProperty.call(savedCreds, 'token')).toBe(false);
183
+ expect(state.issuedTokens.length).toBeGreaterThan(0);
184
+ for (const issuedToken of state.issuedTokens) {
185
+ expect(raw).not.toContain(issuedToken);
186
+ }
187
+ // ── (e) kill AIS, wait past the current token's hard expiry, call
188
+ // whoami → structured, self-healing "AIS unreachable" error. ──
189
+ const expiryToOutlive = state.lastExpiresAtMs;
190
+ await closeServer(fakeAis);
191
+ await waitFor(() => Date.now() > expiryToOutlive + 300, SHORT_TTL_MS + 20000, 'current token hard expiry');
192
+ const id3 = sendToolCall(proc, 'whoami');
193
+ await waitFor(() => stdoutBuf.includes(`"id":${id3}`), 10000, 'whoami during outage');
194
+ const outageText = extractResponseText(stdoutBuf, id3);
195
+ expect(outageText).toContain('AIS unreachable');
196
+ expect(outageText).toContain('no action needed');
197
+ // ── (f) restart AIS on the SAME port, wait one backoff window, call
198
+ // whoami → ok again, no restart of the MCP process required. ──
199
+ fakeAis = createFakeAis(state);
200
+ await listen(fakeAis, port);
201
+ await new Promise((r) => setTimeout(r, 7000)); // one backoff window (base 5s, ±20% jitter)
202
+ const id4 = sendToolCall(proc, 'whoami');
203
+ await waitFor(() => stdoutBuf.includes(`"id":${id4}`), 10000, 'whoami after recovery');
204
+ const recoveredText = extractResponseText(stdoutBuf, id4);
205
+ expect(recoveredText).toContain('agent-1');
206
+ expect(recoveredText).not.toContain('AIS unreachable');
207
+ }
208
+ finally {
209
+ proc?.kill();
210
+ await closeServer(fakeAis).catch(() => { });
211
+ try {
212
+ rmSync(fakeHome, { recursive: true, force: true });
213
+ }
214
+ catch { /* ignore */ }
215
+ }
216
+ }, 150000);
217
+ });
218
+ function waitFor(predicate, ms, label) {
219
+ return new Promise((resolveOuter, reject) => {
220
+ const start = Date.now();
221
+ const tick = () => {
222
+ if (predicate()) {
223
+ resolveOuter();
224
+ return;
225
+ }
226
+ if (Date.now() - start > ms) {
227
+ reject(new Error(`timeout waiting for ${label}`));
228
+ return;
229
+ }
230
+ setTimeout(tick, 50);
231
+ };
232
+ tick();
233
+ });
234
+ }
235
+ function extractResponseText(buf, id) {
236
+ for (const line of buf.split('\n')) {
237
+ if (!line.includes(`"id":${id}`))
238
+ continue;
239
+ try {
240
+ const parsed = JSON.parse(line);
241
+ const text = parsed.result?.content?.[0]?.text;
242
+ if (typeof text === 'string')
243
+ return text;
244
+ }
245
+ catch { /* malformed line; skip */ }
246
+ }
247
+ return '';
248
+ }
249
+ //# sourceMappingURL=silent-renewal-e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silent-renewal-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/silent-renewal-e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};