aismemory 0.7.4 → 0.8.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.
Files changed (101) hide show
  1. package/README.md +211 -11
  2. package/dist/__tests__/agent-archive.test.d.ts +1 -0
  3. package/dist/__tests__/agent-archive.test.js +153 -0
  4. package/dist/__tests__/agent-archive.test.js.map +1 -0
  5. package/dist/__tests__/agent-key-mode.test.js +88 -7
  6. package/dist/__tests__/agent-key-mode.test.js.map +1 -1
  7. package/dist/__tests__/ais-http.test.js +32 -0
  8. package/dist/__tests__/ais-http.test.js.map +1 -1
  9. package/dist/__tests__/credential-isolation.test.js +117 -3
  10. package/dist/__tests__/credential-isolation.test.js.map +1 -1
  11. package/dist/__tests__/credential-resolution.test.js +5 -5
  12. package/dist/__tests__/credential-resolution.test.js.map +1 -1
  13. package/dist/__tests__/device-flow-gate-e2e.test.js +5 -0
  14. package/dist/__tests__/device-flow-gate-e2e.test.js.map +1 -1
  15. package/dist/__tests__/doctor.test.js +144 -79
  16. package/dist/__tests__/doctor.test.js.map +1 -1
  17. package/dist/__tests__/mcp-tools.test.d.ts +1 -0
  18. package/dist/__tests__/mcp-tools.test.js +189 -0
  19. package/dist/__tests__/mcp-tools.test.js.map +1 -0
  20. package/dist/__tests__/memory-pull.test.d.ts +1 -0
  21. package/dist/__tests__/memory-pull.test.js +380 -0
  22. package/dist/__tests__/memory-pull.test.js.map +1 -0
  23. package/dist/__tests__/memory-write-failure.test.js +4 -8
  24. package/dist/__tests__/memory-write-failure.test.js.map +1 -1
  25. package/dist/__tests__/no-registered-keys-disk-bearer-fallback.test.js +4 -7
  26. package/dist/__tests__/no-registered-keys-disk-bearer-fallback.test.js.map +1 -1
  27. package/dist/__tests__/pending-device-flow-e2e.test.d.ts +19 -0
  28. package/dist/__tests__/pending-device-flow-e2e.test.js +159 -0
  29. package/dist/__tests__/pending-device-flow-e2e.test.js.map +1 -0
  30. package/dist/__tests__/pending-device-flow.test.d.ts +15 -0
  31. package/dist/__tests__/pending-device-flow.test.js +108 -0
  32. package/dist/__tests__/pending-device-flow.test.js.map +1 -0
  33. package/dist/__tests__/provisional-register-mcp-remember.test.js +4 -8
  34. package/dist/__tests__/provisional-register-mcp-remember.test.js.map +1 -1
  35. package/dist/__tests__/readme-quickstart-default-ais-url.test.d.ts +1 -0
  36. package/dist/__tests__/readme-quickstart-default-ais-url.test.js +80 -0
  37. package/dist/__tests__/readme-quickstart-default-ais-url.test.js.map +1 -0
  38. package/dist/__tests__/reconnect-survival-e2e.test.js +3 -4
  39. package/dist/__tests__/reconnect-survival-e2e.test.js.map +1 -1
  40. package/dist/__tests__/remember-duplicate-truth.test.d.ts +13 -0
  41. package/dist/__tests__/remember-duplicate-truth.test.js +106 -0
  42. package/dist/__tests__/remember-duplicate-truth.test.js.map +1 -0
  43. package/dist/__tests__/retry-after.test.d.ts +4 -0
  44. package/dist/__tests__/retry-after.test.js +128 -0
  45. package/dist/__tests__/retry-after.test.js.map +1 -0
  46. package/dist/__tests__/startup-credential-warning-e2e.test.d.ts +1 -0
  47. package/dist/__tests__/startup-credential-warning-e2e.test.js +102 -0
  48. package/dist/__tests__/startup-credential-warning-e2e.test.js.map +1 -0
  49. package/dist/__tests__/tool-args.test.js +10 -0
  50. package/dist/__tests__/tool-args.test.js.map +1 -1
  51. package/dist/__tests__/version-check.test.d.ts +1 -0
  52. package/dist/__tests__/version-check.test.js +349 -0
  53. package/dist/__tests__/version-check.test.js.map +1 -0
  54. package/dist/agent-archive.d.ts +36 -0
  55. package/dist/agent-archive.js +69 -0
  56. package/dist/agent-archive.js.map +1 -0
  57. package/dist/agent-key-mode.d.ts +1 -1
  58. package/dist/agent-key-mode.js +56 -19
  59. package/dist/agent-key-mode.js.map +1 -1
  60. package/dist/ais-http-error.d.ts +26 -2
  61. package/dist/ais-http-error.js +52 -4
  62. package/dist/ais-http-error.js.map +1 -1
  63. package/dist/ais-http.js +46 -16
  64. package/dist/ais-http.js.map +1 -1
  65. package/dist/cli/doctor.d.ts +17 -2
  66. package/dist/cli/doctor.js +93 -14
  67. package/dist/cli/doctor.js.map +1 -1
  68. package/dist/cli/pull-memory.d.ts +17 -0
  69. package/dist/cli/pull-memory.js +141 -0
  70. package/dist/cli/pull-memory.js.map +1 -0
  71. package/dist/index.js +135 -398
  72. package/dist/index.js.map +1 -1
  73. package/dist/mcp/tools.d.ts +82 -0
  74. package/dist/mcp/tools.js +453 -0
  75. package/dist/mcp/tools.js.map +1 -0
  76. package/dist/memory-pull.d.ts +114 -0
  77. package/dist/memory-pull.js +171 -0
  78. package/dist/memory-pull.js.map +1 -0
  79. package/dist/pending-device-flow.d.ts +49 -0
  80. package/dist/pending-device-flow.js +96 -0
  81. package/dist/pending-device-flow.js.map +1 -0
  82. package/dist/retry-after.d.ts +26 -0
  83. package/dist/retry-after.js +42 -0
  84. package/dist/retry-after.js.map +1 -0
  85. package/dist/session-identity.d.ts +26 -0
  86. package/dist/session-identity.js +89 -0
  87. package/dist/session-identity.js.map +1 -0
  88. package/dist/telemetry.d.ts +1 -1
  89. package/dist/telemetry.js.map +1 -1
  90. package/dist/tool-args.d.ts +4 -0
  91. package/dist/tool-args.js +16 -0
  92. package/dist/tool-args.js.map +1 -1
  93. package/dist/version-check.d.ts +72 -0
  94. package/dist/version-check.js +237 -0
  95. package/dist/version-check.js.map +1 -0
  96. package/dist/workspace-key-mode.js +22 -6
  97. package/dist/workspace-key-mode.js.map +1 -1
  98. package/package.json +4 -3
  99. package/dist/harness-identity.d.ts +0 -17
  100. package/dist/harness-identity.js +0 -105
  101. package/dist/harness-identity.js.map +0 -1
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Direct tests for the extracted model context protocol tool dispatch
3
+ * (CORBOT-B8D6C9BA). These import `src/mcp/tools.ts` rather than spawning
4
+ * dist/index.js, so the dispatch, the unknown-tool sentinel, and the
5
+ * invalidate-and-retry-once path are covered without a live AIS.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
8
+ import { createOAuthCallToolHandler } from '../mcp/tools.js';
9
+ const aisGet = vi.hoisted(() => vi.fn());
10
+ const aisPost = vi.hoisted(() => vi.fn());
11
+ const aisPatch = vi.hoisted(() => vi.fn());
12
+ const hydrateIdentity = vi.hoisted(() => vi.fn());
13
+ vi.mock('../ais-http.js', () => ({ aisGet, aisPost, aisPatch }));
14
+ vi.mock('../hydration.js', () => ({ hydrateIdentity }));
15
+ const CREDS = {
16
+ token: 'token-1',
17
+ agentId: 'agent-1',
18
+ tenantId: 'tenant-1',
19
+ expiresAt: new Date(Date.now() + 3_600_000).toISOString(),
20
+ };
21
+ function call(name, args = {}) {
22
+ return { method: 'tools/call', params: { name, arguments: args } };
23
+ }
24
+ function makeHarness(overrides = {}) {
25
+ let creds = { ...CREDS };
26
+ let invalidateCalls = 0;
27
+ let ensureCalls = 0;
28
+ const stderrLines = [];
29
+ // The handler only ever calls notification() on the server; a stub keeps
30
+ // the test off a real stdio transport.
31
+ const serverStub = {
32
+ notification: async () => undefined,
33
+ };
34
+ const ctx = {
35
+ aisUrl: 'https://ais.test',
36
+ server: serverStub,
37
+ activityTracker: { recordToolUse: () => undefined, recordMemoryStored: () => undefined },
38
+ getSessionIdentity: () => null,
39
+ setSessionIdentity: () => { },
40
+ getCredentials: () => creds,
41
+ setCredentials: (next) => { creds = next; },
42
+ setSessionAgentOverride: () => undefined,
43
+ getAgentContext: () => null,
44
+ setAgentContext: () => undefined,
45
+ ensureCredentials: async () => { ensureCalls += 1; creds = creds ?? { ...CREDS }; return creds; },
46
+ invalidateToken: () => { invalidateCalls += 1; },
47
+ applyMemoryRefresh: () => undefined,
48
+ resolveAgentForLoad: async () => ({ id: 'agent-2', name: 'Two', status: 'active' }),
49
+ persistClaudeSessionAgentName: () => undefined,
50
+ isBondPendingError: () => false,
51
+ stderr: (msg) => { stderrLines.push(msg); },
52
+ ...overrides,
53
+ };
54
+ return {
55
+ handler: createOAuthCallToolHandler(ctx),
56
+ invalidateCalls: () => invalidateCalls,
57
+ ensureCalls: () => ensureCalls,
58
+ creds: () => creds,
59
+ stderrLines: () => stderrLines,
60
+ };
61
+ }
62
+ describe('createOAuthCallToolHandler', () => {
63
+ beforeEach(() => {
64
+ aisGet.mockReset();
65
+ aisPost.mockReset();
66
+ aisPatch.mockReset();
67
+ hydrateIdentity.mockReset();
68
+ hydrateIdentity.mockResolvedValue(null);
69
+ });
70
+ it('routes whoami to GET /v1/agents/<id> and returns the payload as JSON text', async () => {
71
+ aisGet.mockResolvedValue({ success: true, data: { id: 'agent-1' } });
72
+ const h = makeHarness();
73
+ const res = await h.handler(call('whoami'));
74
+ expect(aisGet).toHaveBeenCalledWith('/v1/agents/agent-1', 'token-1');
75
+ expect(res.isError).toBeUndefined();
76
+ expect(JSON.parse(res.content[0].text)).toEqual({ success: true, data: { id: 'agent-1' } });
77
+ });
78
+ it('routes remember to POST /v1/agents/<id>/memory with the parsed defaults', async () => {
79
+ aisPost.mockResolvedValue({ success: true });
80
+ const h = makeHarness();
81
+ await h.handler(call('remember', { content: 'a fact' }));
82
+ expect(aisPost).toHaveBeenCalledWith('/v1/agents/agent-1/memory', { content: 'a fact', type: 'context', importance: 0.5 }, 'token-1');
83
+ });
84
+ // CORBOT-E3541A7F: the handoff tool schema declares docPath and docPrUrl, so
85
+ // this path forwards them when present and sends neither when absent.
86
+ it('routes handoff with docPath and docPrUrl when the caller supplies them', async () => {
87
+ aisPost.mockResolvedValue({ success: true });
88
+ const h = makeHarness();
89
+ await h.handler(call('handoff', {
90
+ summary: 'session summary',
91
+ docPath: 'docs/handoffs/2026-09-15.md',
92
+ docPrUrl: 'https://github.com/rudedoggg/Corbot/pull/5806',
93
+ }));
94
+ expect(aisPost).toHaveBeenCalledWith('/v1/agents/agent-1/handoff', {
95
+ summary: 'session summary',
96
+ keyLearnings: [],
97
+ docPath: 'docs/handoffs/2026-09-15.md',
98
+ docPrUrl: 'https://github.com/rudedoggg/Corbot/pull/5806',
99
+ }, 'token-1');
100
+ });
101
+ it('routes handoff without the document fields when the caller omits them', async () => {
102
+ aisPost.mockResolvedValue({ success: true });
103
+ const h = makeHarness();
104
+ await h.handler(call('handoff', { summary: 'no document this session' }));
105
+ expect(aisPost).toHaveBeenCalledWith('/v1/agents/agent-1/handoff', { summary: 'no document this session', keyLearnings: [] }, 'token-1');
106
+ });
107
+ it('returns an isError result naming an unknown tool', async () => {
108
+ const h = makeHarness();
109
+ const res = await h.handler(call('not_a_tool'));
110
+ expect(res.isError).toBe(true);
111
+ expect(JSON.parse(res.content[0].text)).toEqual({ error: 'Unknown tool: not_a_tool' });
112
+ });
113
+ it('returns an isError result carrying the thrown message', async () => {
114
+ aisGet.mockRejectedValue(new Error('AIS said no'));
115
+ const h = makeHarness();
116
+ const res = await h.handler(call('whoami'));
117
+ expect(res.isError).toBe(true);
118
+ expect(JSON.parse(res.content[0].text)).toEqual({ error: 'AIS said no' });
119
+ });
120
+ it('returns a bond-pending message verbatim instead of JSON', async () => {
121
+ aisGet.mockRejectedValue(new Error('Memory bond required — ask your human.'));
122
+ const h = makeHarness({ isBondPendingError: () => true });
123
+ const res = await h.handler(call('whoami'));
124
+ expect(res.isError).toBe(true);
125
+ expect(res.content[0].text).toBe('Memory bond required — ask your human.');
126
+ });
127
+ it('clears creds, invalidates the token, and retries exactly once on a rejected token', async () => {
128
+ aisGet
129
+ .mockResolvedValueOnce({ success: false, error: { code: 'INVALID_TOKEN' } })
130
+ .mockResolvedValueOnce({ success: true, data: { id: 'agent-1' } });
131
+ const h = makeHarness();
132
+ const res = await h.handler(call('whoami'));
133
+ expect(h.invalidateCalls()).toBe(1);
134
+ expect(h.ensureCalls()).toBe(2);
135
+ expect(aisGet).toHaveBeenCalledTimes(2);
136
+ expect(res.isError).toBeUndefined();
137
+ expect(JSON.parse(res.content[0].text)).toEqual({ success: true, data: { id: 'agent-1' } });
138
+ expect(h.stderrLines()).toContain('AIS rejected the cached token; re-authenticating and retrying once...');
139
+ });
140
+ it('does not retry a second time when the retry is also rejected', async () => {
141
+ aisGet.mockResolvedValue({ success: false, error: { code: 'TOKEN_EXPIRED' } });
142
+ const h = makeHarness();
143
+ const res = await h.handler(call('whoami'));
144
+ expect(aisGet).toHaveBeenCalledTimes(2);
145
+ expect(h.invalidateCalls()).toBe(1);
146
+ expect(JSON.parse(res.content[0].text)).toEqual({ success: false, error: { code: 'TOKEN_EXPIRED' } });
147
+ });
148
+ it('agent_load switches the agent id used by the next call', async () => {
149
+ aisGet.mockResolvedValue({ success: true, data: { id: 'agent-2' } });
150
+ const h = makeHarness();
151
+ const res = await h.handler(call('agent_load', { query: 'Two' }));
152
+ expect(JSON.parse(res.content[0].text)).toMatchObject({ success: true, agentId: 'agent-2', name: 'Two' });
153
+ expect(h.creds()?.agentId).toBe('agent-2');
154
+ });
155
+ // CORBOT-95B9ABB6: a session may switch its own identity through agent_load,
156
+ // but only to an agent AIS resolves AND authorizes for this session's token.
157
+ // An unconfirmed target must leave the session exactly where it was.
158
+ it('does not switch the session identity when AIS does not confirm the agent_load target', async () => {
159
+ let identity = { version: 1, source: 'env', agentId: 'agent-1' };
160
+ aisGet.mockImplementation(async (path) => (path === '/v1/agents/agent-2'
161
+ ? { success: false, error: { code: 'FORBIDDEN' } }
162
+ : { success: true, data: { id: 'agent-1' } }));
163
+ const h = makeHarness({
164
+ getSessionIdentity: () => identity,
165
+ setSessionIdentity: (next) => { identity = next; },
166
+ });
167
+ const refused = await h.handler(call('agent_load', { query: 'Two' }));
168
+ expect(refused.isError).toBe(true);
169
+ expect(JSON.parse(refused.content[0].text).error).toContain('whoami did not confirm agent-2');
170
+ expect(identity?.agentId).toBe('agent-1');
171
+ expect(h.creds()?.agentId).toBe('agent-1');
172
+ // The next write still goes out as the agent this session actually holds.
173
+ aisPost.mockResolvedValue({ success: true, data: { id: 'memory-1' } });
174
+ const write = await h.handler(call('remember', { content: 'written after the refused switch' }));
175
+ expect(write.isError).toBeUndefined();
176
+ expect(aisPost).toHaveBeenCalledWith('/v1/agents/agent-1/memory', expect.anything(), 'token-1');
177
+ });
178
+ it('refuses a write when the live agent differs from the session identity', async () => {
179
+ const identity = { version: 1, source: 'agent_load', agentId: 'agent-2' };
180
+ // The token now answers as someone else: the gate must stop the write.
181
+ aisGet.mockResolvedValue({ success: true, data: { id: 'agent-9' } });
182
+ const h = makeHarness({ getSessionIdentity: () => identity });
183
+ const write = await h.handler(call('remember', { content: 'must not be stored' }));
184
+ expect(write.isError).toBe(true);
185
+ expect(JSON.parse(write.content[0].text).error).toContain('whoami did not confirm agent-2');
186
+ expect(aisPost).not.toHaveBeenCalled();
187
+ });
188
+ });
189
+ //# sourceMappingURL=mcp-tools.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-tools.test.js","sourceRoot":"","sources":["../../src/__tests__/mcp-tools.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Tests for the memory pull capability (CORBOT-31C43CBD).
3
+ *
4
+ * Covers the three things the backup has to get right:
5
+ * - pagination walks the whole corpus and terminates,
6
+ * - the JSON Lines file is written correctly and atomically,
7
+ * - the walk can only ever read the calling agent's own memories.
8
+ */
9
+ import { describe, it, expect, vi, afterEach } from 'vitest';
10
+ import { mkdtempSync, rmSync, readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { pullMemories, parseBrowsePage, clampPageSize, toJsonLines, describePullFailure, MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE, } from '../memory-pull.js';
14
+ import { parseArgs, defaultBackupPath, writeBackupFile } from '../cli/pull-memory.js';
15
+ import { AisHttpError } from '../ais-http-error.js';
16
+ const AIS_URL = 'https://ais.test';
17
+ const AGENT = 'agent-self';
18
+ const KEY = 'ais_ak_test';
19
+ /** A corpus of `n` memories, newest first, as the browse path orders them. */
20
+ function makeCorpus(n) {
21
+ return Array.from({ length: n }, (_, i) => ({
22
+ id: `mem-${String(i).padStart(3, '0')}`,
23
+ content: `memory ${i}`,
24
+ type: 'fact',
25
+ createdAt: new Date(Date.UTC(2026, 0, 1) - i * 1000).toISOString(),
26
+ }));
27
+ }
28
+ /**
29
+ * A fake service that serves `corpus` over limit/offset, recording every
30
+ * request so the tests can assert on the URLs and headers actually sent.
31
+ */
32
+ function makeFetch(corpus, recorded, options = {}) {
33
+ return (async (input, init) => {
34
+ const url = typeof input === 'string' ? input : input.toString();
35
+ recorded.push({ url, headers: (init?.headers ?? {}) });
36
+ const parsed = new URL(url);
37
+ const limit = Number(parsed.searchParams.get('limit'));
38
+ const offset = Number(parsed.searchParams.get('offset') ?? '0');
39
+ const page = corpus.slice(offset, offset + limit);
40
+ return new Response(JSON.stringify({
41
+ success: true,
42
+ data: {
43
+ memories: page,
44
+ totalCount: options.totalCount ?? corpus.length,
45
+ },
46
+ }), { status: 200, headers: { 'content-type': 'application/json' } });
47
+ });
48
+ }
49
+ function tempDir() {
50
+ return mkdtempSync(join(tmpdir(), 'aismemory-pull-test-'));
51
+ }
52
+ afterEach(() => {
53
+ vi.unstubAllGlobals();
54
+ });
55
+ // ---------------------------------------------------------------------------
56
+ // Pagination
57
+ // ---------------------------------------------------------------------------
58
+ describe('pullMemories pagination', () => {
59
+ it('walks every page of a corpus larger than one page', async () => {
60
+ const corpus = makeCorpus(250);
61
+ const recorded = [];
62
+ const result = await pullMemories({
63
+ aisUrl: AIS_URL,
64
+ agentId: AGENT,
65
+ apiKey: KEY,
66
+ pageSize: 100,
67
+ fetchImpl: makeFetch(corpus, recorded),
68
+ });
69
+ expect(result.memories).toHaveLength(250);
70
+ expect(result.memories.map((m) => m.id)).toEqual(corpus.map((m) => m.id));
71
+ // 100 + 100 + 50: the short third page ends the walk, no wasted request.
72
+ expect(result.pagesFetched).toBe(3);
73
+ expect(result.truncated).toBe(false);
74
+ expect(result.totalCount).toBe(250);
75
+ });
76
+ it('advances the offset by a full page each time', async () => {
77
+ const recorded = [];
78
+ await pullMemories({
79
+ aisUrl: AIS_URL,
80
+ agentId: AGENT,
81
+ apiKey: KEY,
82
+ pageSize: 10,
83
+ fetchImpl: makeFetch(makeCorpus(25), recorded),
84
+ });
85
+ const offsets = recorded.map((r) => new URL(r.url).searchParams.get('offset'));
86
+ expect(offsets).toEqual(['0', '10', '20']);
87
+ });
88
+ it('stops after a single request when the corpus fits on one page', async () => {
89
+ const recorded = [];
90
+ const result = await pullMemories({
91
+ aisUrl: AIS_URL,
92
+ agentId: AGENT,
93
+ apiKey: KEY,
94
+ pageSize: 100,
95
+ fetchImpl: makeFetch(makeCorpus(7), recorded),
96
+ });
97
+ expect(result.pagesFetched).toBe(1);
98
+ expect(result.memories).toHaveLength(7);
99
+ });
100
+ it('handles an empty corpus without writing anything odd', async () => {
101
+ const recorded = [];
102
+ const result = await pullMemories({
103
+ aisUrl: AIS_URL,
104
+ agentId: AGENT,
105
+ apiKey: KEY,
106
+ fetchImpl: makeFetch([], recorded),
107
+ });
108
+ expect(result.memories).toEqual([]);
109
+ expect(result.pagesFetched).toBe(1);
110
+ expect(toJsonLines(result.memories)).toBe('');
111
+ });
112
+ it('exactly-full final page costs one extra request and still terminates', async () => {
113
+ const recorded = [];
114
+ const result = await pullMemories({
115
+ aisUrl: AIS_URL,
116
+ agentId: AGENT,
117
+ apiKey: KEY,
118
+ pageSize: 10,
119
+ fetchImpl: makeFetch(makeCorpus(20), recorded),
120
+ });
121
+ // Pages of 10, 10, then an empty page proves exhaustion.
122
+ expect(result.pagesFetched).toBe(3);
123
+ expect(result.memories).toHaveLength(20);
124
+ });
125
+ it('deduplicates by id when a concurrent write shifts the window', async () => {
126
+ // The browse path orders newest first, so a memory written mid-walk lands
127
+ // at offset 0 and pushes every later row down one. Without dedupe the row
128
+ // at the page boundary would be captured twice.
129
+ const corpus = makeCorpus(20);
130
+ const recorded = [];
131
+ let served = 0;
132
+ const shiftingFetch = (async (input) => {
133
+ const url = typeof input === 'string' ? input : input.toString();
134
+ recorded.push({ url, headers: {} });
135
+ const parsed = new URL(url);
136
+ const limit = Number(parsed.searchParams.get('limit'));
137
+ const offset = Number(parsed.searchParams.get('offset') ?? '0');
138
+ // After the first page is served, a new memory appears at the head.
139
+ const live = served === 0 ? corpus : [{ id: 'mem-new', content: 'written mid-walk' }, ...corpus];
140
+ served += 1;
141
+ const page = live.slice(offset, offset + limit);
142
+ return new Response(JSON.stringify({ success: true, data: { memories: page, totalCount: live.length } }), { status: 200 });
143
+ });
144
+ const result = await pullMemories({
145
+ aisUrl: AIS_URL,
146
+ agentId: AGENT,
147
+ apiKey: KEY,
148
+ pageSize: 10,
149
+ fetchImpl: shiftingFetch,
150
+ });
151
+ const ids = result.memories.map((m) => m.id);
152
+ expect(new Set(ids).size).toBe(ids.length);
153
+ // Every original memory is still captured despite the shift.
154
+ for (const original of corpus) {
155
+ expect(ids).toContain(original.id);
156
+ }
157
+ });
158
+ it('reports progress per page', async () => {
159
+ const progress = [];
160
+ await pullMemories({
161
+ aisUrl: AIS_URL,
162
+ agentId: AGENT,
163
+ apiKey: KEY,
164
+ pageSize: 10,
165
+ fetchImpl: makeFetch(makeCorpus(25), []),
166
+ onProgress: (p) => progress.push(p.total),
167
+ });
168
+ expect(progress).toEqual([10, 20, 25]);
169
+ });
170
+ it('clamps the page size to what the server accepts', () => {
171
+ expect(clampPageSize(undefined)).toBe(DEFAULT_PAGE_SIZE);
172
+ expect(clampPageSize(5_000)).toBe(MAX_PAGE_SIZE);
173
+ expect(clampPageSize(0)).toBe(1);
174
+ expect(clampPageSize(-3)).toBe(1);
175
+ expect(clampPageSize(42)).toBe(42);
176
+ });
177
+ it('never asks the server for a limit above the cap', async () => {
178
+ const recorded = [];
179
+ await pullMemories({
180
+ aisUrl: AIS_URL,
181
+ agentId: AGENT,
182
+ apiKey: KEY,
183
+ pageSize: 10_000,
184
+ fetchImpl: makeFetch(makeCorpus(3), recorded),
185
+ });
186
+ const limit = new URL(recorded[0].url).searchParams.get('limit');
187
+ expect(limit).toBe(String(MAX_PAGE_SIZE));
188
+ });
189
+ });
190
+ // ---------------------------------------------------------------------------
191
+ // Own-agent-only
192
+ // ---------------------------------------------------------------------------
193
+ describe('pullMemories reads only its own agent', () => {
194
+ it('requests only the configured agent path and sends the key header', async () => {
195
+ const recorded = [];
196
+ await pullMemories({
197
+ aisUrl: AIS_URL,
198
+ agentId: AGENT,
199
+ apiKey: KEY,
200
+ pageSize: 10,
201
+ fetchImpl: makeFetch(makeCorpus(25), recorded),
202
+ });
203
+ expect(recorded).toHaveLength(3);
204
+ for (const request of recorded) {
205
+ expect(request.url.startsWith(`${AIS_URL}/v1/agents/${AGENT}/memory?`)).toBe(true);
206
+ expect(request.headers['x-agent-api-key']).toBe(KEY);
207
+ // No bearer token: a scoped key must never be paired with owner auth.
208
+ expect(request.headers['Authorization']).toBeUndefined();
209
+ }
210
+ });
211
+ it('never sends principalAgentId, the delegated cross-agent read parameter', async () => {
212
+ const recorded = [];
213
+ await pullMemories({
214
+ aisUrl: AIS_URL,
215
+ agentId: AGENT,
216
+ apiKey: KEY,
217
+ pageSize: 10,
218
+ fetchImpl: makeFetch(makeCorpus(25), recorded),
219
+ });
220
+ for (const request of recorded) {
221
+ expect(new URL(request.url).searchParams.has('principalAgentId')).toBe(false);
222
+ }
223
+ });
224
+ it('encodes the agent id into the path rather than letting it alter the route', async () => {
225
+ const recorded = [];
226
+ await pullMemories({
227
+ aisUrl: AIS_URL,
228
+ agentId: '../other-agent',
229
+ apiKey: KEY,
230
+ fetchImpl: makeFetch([], recorded),
231
+ });
232
+ const path = new URL(recorded[0].url).pathname;
233
+ expect(path).toBe('/v1/agents/..%2Fother-agent/memory');
234
+ expect(path).not.toContain('/other-agent/');
235
+ });
236
+ });
237
+ // ---------------------------------------------------------------------------
238
+ // Response handling
239
+ // ---------------------------------------------------------------------------
240
+ describe('parseBrowsePage', () => {
241
+ it('reads memories and totalCount out of the documented shape', () => {
242
+ const page = parseBrowsePage({ success: true, data: { memories: [{ id: 'a' }], totalCount: 9 } });
243
+ expect(page.memories).toEqual([{ id: 'a' }]);
244
+ expect(page.totalCount).toBe(9);
245
+ });
246
+ it('omits totalCount when the server does not send one', () => {
247
+ const page = parseBrowsePage({ data: { memories: [] } });
248
+ expect(page.totalCount).toBeUndefined();
249
+ });
250
+ it('throws rather than silently returning nothing when data is missing', () => {
251
+ // An empty parse here would be indistinguishable from "no memories" and
252
+ // would overwrite a good backup with an empty file.
253
+ expect(() => parseBrowsePage({ success: false, error: { code: 'FORBIDDEN' } })).toThrow(/no .data. object/);
254
+ expect(() => parseBrowsePage('<html>gateway error</html>')).toThrow(/no .data. object/);
255
+ });
256
+ it('throws when memories is not an array or a memory has no id', () => {
257
+ expect(() => parseBrowsePage({ data: { memories: 'nope' } })).toThrow(/not an array/);
258
+ expect(() => parseBrowsePage({ data: { memories: [{ content: 'no id' }] } })).toThrow(/string .id./);
259
+ });
260
+ it('propagates a rejected read instead of writing an empty backup', async () => {
261
+ const failing = (async () => new Response(JSON.stringify({ success: false, error: { code: 'FORBIDDEN', message: 'nope' } }), {
262
+ status: 403,
263
+ }));
264
+ await expect(pullMemories({ aisUrl: AIS_URL, agentId: AGENT, apiKey: KEY, fetchImpl: failing })).rejects.toBeInstanceOf(AisHttpError);
265
+ });
266
+ });
267
+ describe('describePullFailure', () => {
268
+ it('names the cause for the statuses an operator actually hits', () => {
269
+ expect(describePullFailure(new AisHttpError(401, '/p', null))).toMatch(/AIS_API_KEY/);
270
+ expect(describePullFailure(new AisHttpError(403, '/p', null))).toMatch(/own memories/);
271
+ expect(describePullFailure(new AisHttpError(404, '/p', null))).toMatch(/AIS_AGENT_ID/);
272
+ expect(describePullFailure(new AisHttpError(500, '/p', null))).toMatch(/HTTP 500/);
273
+ expect(describePullFailure(new Error('socket hang up'))).toBe('socket hang up');
274
+ });
275
+ });
276
+ // ---------------------------------------------------------------------------
277
+ // File output
278
+ // ---------------------------------------------------------------------------
279
+ describe('JSON Lines backup file', () => {
280
+ it('writes one complete JSON object per line, newest first', () => {
281
+ const memories = makeCorpus(3);
282
+ const text = toJsonLines(memories);
283
+ const lines = text.trimEnd().split('\n');
284
+ expect(lines).toHaveLength(3);
285
+ expect(text.endsWith('\n')).toBe(true);
286
+ for (const [i, line] of lines.entries()) {
287
+ expect(JSON.parse(line)).toEqual(memories[i]);
288
+ }
289
+ });
290
+ it('keeps content with newlines on a single line', () => {
291
+ const text = toJsonLines([{ id: 'a', content: 'line one\nline two' }]);
292
+ expect(text.trimEnd().split('\n')).toHaveLength(1);
293
+ expect(JSON.parse(text.trimEnd())).toEqual({ id: 'a', content: 'line one\nline two' });
294
+ });
295
+ it('writes the file and creates missing parent directories', () => {
296
+ const dir = tempDir();
297
+ try {
298
+ const target = join(dir, 'nested', 'deeper', 'backup.jsonl');
299
+ writeBackupFile(target, toJsonLines(makeCorpus(2)));
300
+ expect(existsSync(target)).toBe(true);
301
+ expect(readFileSync(target, 'utf8').trimEnd().split('\n')).toHaveLength(2);
302
+ }
303
+ finally {
304
+ rmSync(dir, { recursive: true, force: true });
305
+ }
306
+ });
307
+ it('replaces an existing backup and leaves no temporary file behind', () => {
308
+ const dir = tempDir();
309
+ try {
310
+ const target = join(dir, 'backup.jsonl');
311
+ writeFileSync(target, 'stale contents\n');
312
+ writeBackupFile(target, toJsonLines(makeCorpus(1)));
313
+ expect(readFileSync(target, 'utf8')).not.toContain('stale');
314
+ expect(readdirSync(dir)).toEqual(['backup.jsonl']);
315
+ }
316
+ finally {
317
+ rmSync(dir, { recursive: true, force: true });
318
+ }
319
+ });
320
+ it('round-trips a pull to a file that parses back to the same memories', async () => {
321
+ const dir = tempDir();
322
+ try {
323
+ const corpus = makeCorpus(25);
324
+ const result = await pullMemories({
325
+ aisUrl: AIS_URL,
326
+ agentId: AGENT,
327
+ apiKey: KEY,
328
+ pageSize: 10,
329
+ fetchImpl: makeFetch(corpus, []),
330
+ });
331
+ const target = join(dir, 'backup.jsonl');
332
+ writeBackupFile(target, toJsonLines(result.memories));
333
+ const parsed = readFileSync(target, 'utf8')
334
+ .trimEnd()
335
+ .split('\n')
336
+ .map((line) => JSON.parse(line));
337
+ expect(parsed).toEqual(corpus);
338
+ }
339
+ finally {
340
+ rmSync(dir, { recursive: true, force: true });
341
+ }
342
+ });
343
+ it('defaults to a per-agent path that is NOT the LocalMirror cache file', () => {
344
+ const path = defaultBackupPath(AGENT, '/home/someone');
345
+ expect(path).toBe(`/home/someone/.aismemory/backups/${AGENT}.jsonl`);
346
+ // LocalMirror owns ~/.aismemory/cache/<agentId>.jsonl, stores a different
347
+ // shape, and prunes it on a retention window — a backup written there
348
+ // would be silently deleted.
349
+ expect(path).not.toContain(`${'.aismemory'}/cache/`);
350
+ });
351
+ });
352
+ // ---------------------------------------------------------------------------
353
+ // Argument parsing
354
+ // ---------------------------------------------------------------------------
355
+ describe('parseArgs', () => {
356
+ it('defaults to a quiet-off, full-page pull', () => {
357
+ expect(parseArgs([])).toEqual({ quiet: false, help: false });
358
+ });
359
+ it('reads --out, --page-size and --quiet', () => {
360
+ expect(parseArgs(['--out', './b.jsonl', '--page-size', '25', '--quiet'])).toEqual({
361
+ out: './b.jsonl',
362
+ pageSize: 25,
363
+ quiet: true,
364
+ help: false,
365
+ });
366
+ });
367
+ it('accepts --help and -h', () => {
368
+ expect(parseArgs(['--help']).help).toBe(true);
369
+ expect(parseArgs(['-h']).help).toBe(true);
370
+ });
371
+ it('rejects a page size the server would reject', () => {
372
+ expect(() => parseArgs(['--page-size', '0'])).toThrow(/between 1 and/);
373
+ expect(() => parseArgs(['--page-size', '101'])).toThrow(/between 1 and/);
374
+ expect(() => parseArgs(['--page-size', 'lots'])).toThrow(/between 1 and/);
375
+ });
376
+ it('rejects an unknown option rather than ignoring it', () => {
377
+ expect(() => parseArgs(['--all-agents'])).toThrow(/Unknown option/);
378
+ });
379
+ });
380
+ //# sourceMappingURL=memory-pull.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-pull.test.js","sourceRoot":"","sources":["../../src/__tests__/memory-pull.test.ts"],"names":[],"mappings":"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"}