aismemory 0.5.4 → 0.6.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.
package/README.md CHANGED
@@ -59,6 +59,57 @@ Authentication uses the OAuth device flow (RFC 8628). Credentials are saved to `
59
59
  |----------|---------|-------------|
60
60
  | `AIS_URL` | `https://ais.agentsandswarms.ai` | AIS server URL |
61
61
 
62
+ ## Unattended machines (agent API keys)
63
+
64
+ For CI runners, servers, and other machines that can't complete an interactive
65
+ browser bond, skip the device flow entirely with a scoped agent API key:
66
+
67
+ ```json
68
+ {
69
+ "mcpServers": {
70
+ "aismemory": {
71
+ "command": "npx",
72
+ "args": ["aismemory"],
73
+ "env": {
74
+ "AIS_AGENT_API_KEY": "ais_ak_...",
75
+ "AIS_AGENT_ID": "<the agent id this key was minted for>",
76
+ "AIS_URL": "https://ais.agentsandswarms.ai"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ When `AIS_AGENT_API_KEY` is set, `AIS_AGENT_ID` is required — the server
84
+ mints keys scoped to exactly one agent, so the client needs to be told which
85
+ agent that is up front. If `AIS_AGENT_ID` is missing, the process prints an
86
+ actionable error and exits immediately; it never falls back to the device
87
+ flow or creates a new agent on your behalf.
88
+
89
+ This credential is intentionally scoped: it can only read/write that one
90
+ agent's `memory`, `goals`, `dream`, and `full_context` data. Tools that need
91
+ a broader route — `agent_load` (switching agents, which needs tenant-wide
92
+ agent listing) and `handoff` (not on the key's allowed route list) — return
93
+ an explicit "not available with agent API key auth" error rather than
94
+ failing silently or faking success. `whoami` answers from `AIS_AGENT_ID`
95
+ directly instead of calling the (unscoped) identity route.
96
+
97
+ ## Troubleshooting
98
+
99
+ If aismemory "doesn't work," run the built-in self-diagnosis before filing a
100
+ support ticket:
101
+
102
+ ```
103
+ npx aismemory doctor
104
+ ```
105
+
106
+ It checks your Node version, DNS/TLS reachability of the AIS host, REST API
107
+ and MCP endpoint health, local credential state (not bonded / expired /
108
+ rejected / accepted), proxy environment variables, and clock skew — each
109
+ with a PASS/WARN/FAIL line and a remediation hint. Nothing is written to the
110
+ API. Pass `--json` for machine-readable output. Exits `0` unless a check
111
+ FAILs.
112
+
62
113
  ## License
63
114
 
64
115
  MIT
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,189 @@
1
+ /**
2
+ * E2E wiring tests for agent-API-key auth mode (CORBOT-B1D3244C).
3
+ *
4
+ * Spawns the built binary (dist/index.js) exactly like the other MCP e2e
5
+ * tests in this package (see memory-write-failure.test.ts, mcp-init-no-auth
6
+ * .test.ts) so we exercise real process startup, not just the dispatch
7
+ * function in isolation.
8
+ *
9
+ * Covers:
10
+ * (a) AIS_AGENT_API_KEY + AIS_AGENT_ID set → tool calls succeed, every
11
+ * request to AIS carries x-agent-api-key and no Authorization header,
12
+ * and the device-flow endpoint is never hit.
13
+ * (b) AIS_AGENT_API_KEY set without AIS_AGENT_ID → process exits nonzero
14
+ * with an actionable stderr message, and AIS never receives a single
15
+ * request (no device flow, no provisional agent creation).
16
+ */
17
+ import { createServer } from 'node:http';
18
+ import { spawn } from 'node:child_process';
19
+ import { mkdtempSync, rmSync, existsSync } from 'node:fs';
20
+ import { tmpdir } from 'node:os';
21
+ import { join, resolve } from 'path';
22
+ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
23
+ function waitFor(predicate, ms, label) {
24
+ return new Promise((resolveOuter, reject) => {
25
+ const start = Date.now();
26
+ const tick = () => {
27
+ if (predicate()) {
28
+ resolveOuter();
29
+ return;
30
+ }
31
+ if (Date.now() - start > ms) {
32
+ reject(new Error(`timeout waiting for ${label}`));
33
+ return;
34
+ }
35
+ setTimeout(tick, 50);
36
+ };
37
+ tick();
38
+ });
39
+ }
40
+ function extractResult(buf, id) {
41
+ for (const line of buf.split('\n')) {
42
+ if (!line.includes(`"id":${id}`))
43
+ continue;
44
+ try {
45
+ const parsed = JSON.parse(line);
46
+ if (parsed.result) {
47
+ return {
48
+ isError: parsed.result.isError === true,
49
+ text: parsed.result.content?.[0]?.text ?? '',
50
+ };
51
+ }
52
+ }
53
+ catch { /* malformed line; skip */ }
54
+ }
55
+ return { isError: false, text: '' };
56
+ }
57
+ function initRequest() {
58
+ return JSON.stringify({
59
+ jsonrpc: '2.0',
60
+ id: 1,
61
+ method: 'initialize',
62
+ params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'agent-key-mode-e2e', version: '0.0.0' } },
63
+ }) + '\n';
64
+ }
65
+ describe('agent API key auth mode (e2e)', () => {
66
+ let fakeAis;
67
+ let port = 0;
68
+ let recorded = [];
69
+ beforeAll(async () => {
70
+ fakeAis = createServer((req, res) => {
71
+ req.on('data', () => { });
72
+ req.on('end', () => {
73
+ recorded.push({ method: req.method ?? '', url: req.url ?? '', headers: req.headers });
74
+ if (req.url?.startsWith('/v1/agents/agent-scoped-1/memory') && req.method === 'GET') {
75
+ res.writeHead(200, { 'content-type': 'application/json' });
76
+ res.end(JSON.stringify({ success: true, data: { memories: [] } }));
77
+ return;
78
+ }
79
+ if (req.url === '/v1/agents/agent-scoped-1/memory' && req.method === 'POST') {
80
+ res.writeHead(200, { 'content-type': 'application/json' });
81
+ res.end(JSON.stringify({ success: true, data: { id: 'mem-1' } }));
82
+ return;
83
+ }
84
+ // Anything else (device flow, agent listing, handoff, ...) — if this
85
+ // ever gets hit in the missing-agentId test, the assertion on
86
+ // `recorded` length catches it regardless of what we answer here.
87
+ res.writeHead(200, { 'content-type': 'application/json' });
88
+ res.end(JSON.stringify({ success: true, data: {} }));
89
+ });
90
+ });
91
+ await new Promise((r) => fakeAis.listen(0, '127.0.0.1', r));
92
+ const addr = fakeAis.address();
93
+ if (addr === null || typeof addr === 'string')
94
+ throw new Error('no port');
95
+ port = addr.port;
96
+ });
97
+ afterAll(async () => {
98
+ if (fakeAis)
99
+ await new Promise((r) => fakeAis.close(() => r()));
100
+ });
101
+ beforeEach(() => {
102
+ recorded = [];
103
+ });
104
+ it('with AIS_AGENT_API_KEY + AIS_AGENT_ID: sends x-agent-api-key (no Authorization) and never hits the device-flow endpoint', async () => {
105
+ const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
106
+ if (!existsSync(distPath)) {
107
+ throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
108
+ }
109
+ const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-keymode-'));
110
+ let proc = null;
111
+ try {
112
+ proc = spawn('node', [distPath], {
113
+ env: {
114
+ ...process.env,
115
+ HOME: fakeHome,
116
+ AIS_URL: `http://127.0.0.1:${port}`,
117
+ AIS_AGENT_API_KEY: 'ais_ak_test_scoped',
118
+ AIS_AGENT_ID: 'agent-scoped-1',
119
+ },
120
+ stdio: ['pipe', 'pipe', 'pipe'],
121
+ });
122
+ let stdoutBuf = '';
123
+ let stderrBuf = '';
124
+ proc.stdout.on('data', (d) => { stdoutBuf += d.toString(); });
125
+ proc.stderr.on('data', (d) => { stderrBuf += d.toString(); });
126
+ proc.stdin.write(initRequest());
127
+ await waitFor(() => stdoutBuf.includes('"id":1'), 5000, 'initialize response');
128
+ proc.stdin.write(JSON.stringify({
129
+ jsonrpc: '2.0', id: 2, method: 'tools/call',
130
+ params: { name: 'remember', arguments: { content: 'a scoped fact', type: 'fact' } },
131
+ }) + '\n');
132
+ await waitFor(() => stdoutBuf.includes('"id":2'), 10000, 'remember response');
133
+ const result = extractResult(stdoutBuf, 2);
134
+ expect(result.isError).toBe(false);
135
+ // No device-flow / OAuth authorize call was ever made.
136
+ expect(recorded.some((r) => r.url.includes('/v1/oauth/'))).toBe(false);
137
+ expect(recorded.some((r) => r.url.includes('/v1/auth/'))).toBe(false);
138
+ const memWrite = recorded.find((r) => r.url === '/v1/agents/agent-scoped-1/memory' && r.method === 'POST');
139
+ expect(memWrite).toBeDefined();
140
+ expect(memWrite?.headers['x-agent-api-key']).toBe('ais_ak_test_scoped');
141
+ expect(memWrite?.headers['authorization']).toBeUndefined();
142
+ expect(stderrBuf).toMatch(/agent API key auth mode/i);
143
+ expect(stderrBuf).not.toMatch(/bond required/i);
144
+ }
145
+ finally {
146
+ proc?.kill();
147
+ rmSync(fakeHome, { recursive: true, force: true });
148
+ }
149
+ }, 30000);
150
+ it('with AIS_AGENT_API_KEY but no AIS_AGENT_ID: exits nonzero with an actionable message and never contacts AIS', async () => {
151
+ const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
152
+ if (!existsSync(distPath)) {
153
+ throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
154
+ }
155
+ const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-keymode-noagent-'));
156
+ let proc = null;
157
+ try {
158
+ proc = spawn('node', [distPath], {
159
+ env: {
160
+ ...process.env,
161
+ HOME: fakeHome,
162
+ AIS_URL: `http://127.0.0.1:${port}`,
163
+ AIS_AGENT_API_KEY: 'ais_ak_test_no_agent',
164
+ },
165
+ stdio: ['pipe', 'pipe', 'pipe'],
166
+ });
167
+ let stderrBuf = '';
168
+ proc.stderr.on('data', (d) => { stderrBuf += d.toString(); });
169
+ const exitCode = await new Promise((resolveOuter, reject) => {
170
+ const timeout = setTimeout(() => reject(new Error(`process did not exit. stderr:\n${stderrBuf}`)), 5000);
171
+ proc.on('exit', (code) => {
172
+ clearTimeout(timeout);
173
+ resolveOuter(code);
174
+ });
175
+ });
176
+ expect(exitCode).not.toBe(0);
177
+ expect(stderrBuf).toMatch(/AIS_AGENT_ID is missing/);
178
+ expect(stderrBuf).toMatch(/Refusing to start/);
179
+ // No fallback to device flow, no provisional agent creation attempt.
180
+ expect(recorded).toHaveLength(0);
181
+ }
182
+ finally {
183
+ if (proc && !proc.killed)
184
+ proc.kill('SIGKILL');
185
+ rmSync(fakeHome, { recursive: true, force: true });
186
+ }
187
+ }, 10000);
188
+ });
189
+ //# sourceMappingURL=agent-key-mode-e2e.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-key-mode-e2e.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-key-mode-e2e.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Unit tests for the agent-API-key auth mode module (CORBOT-B1D3244C).
3
+ *
4
+ * Covers:
5
+ * - loadAgentApiKeyConfig(): env parsing, and the fatal-error case when
6
+ * AIS_AGENT_API_KEY is set without AIS_AGENT_ID.
7
+ * - dispatchToolKeyMode(): every HTTP request sends x-agent-api-key and no
8
+ * Authorization header; in-scope tools call the expected route;
9
+ * out-of-scope tools (handoff, agent_load) fail honestly without ever
10
+ * hitting the network; a 403 from the server is surfaced, not swallowed.
11
+ */
12
+ import { describe, expect, it, vi } from 'vitest';
13
+ import { AgentApiKeyConfigError, dispatchToolKeyMode, loadAgentApiKeyConfig, OutOfScopeToolError, UNKNOWN_TOOL_KEY_MODE, } from '../agent-key-mode.js';
14
+ const AIS_URL = 'https://ais.example.test';
15
+ function jsonResponse(status, body) {
16
+ return new Response(JSON.stringify(body), {
17
+ status,
18
+ headers: { 'content-type': 'application/json' },
19
+ });
20
+ }
21
+ describe('loadAgentApiKeyConfig', () => {
22
+ it('returns null when AIS_AGENT_API_KEY is unset', () => {
23
+ expect(loadAgentApiKeyConfig({})).toBeNull();
24
+ });
25
+ it('returns the config when both AIS_AGENT_API_KEY and AIS_AGENT_ID are set', () => {
26
+ const config = loadAgentApiKeyConfig({
27
+ AIS_AGENT_API_KEY: 'ais_ak_test123',
28
+ AIS_AGENT_ID: 'agent-1',
29
+ });
30
+ expect(config).toEqual({ apiKey: 'ais_ak_test123', agentId: 'agent-1' });
31
+ });
32
+ it('throws AgentApiKeyConfigError when AIS_AGENT_API_KEY is set without AIS_AGENT_ID', () => {
33
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'ais_ak_test123' })).toThrow(AgentApiKeyConfigError);
34
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'ais_ak_test123' })).toThrow(/AIS_AGENT_ID is missing/);
35
+ });
36
+ it('does not throw or reference agent creation when unset entirely', () => {
37
+ // Regression guard: this must be a clean no-op, never a fallback path.
38
+ expect(() => loadAgentApiKeyConfig({ AIS_URL: 'https://x' })).not.toThrow();
39
+ });
40
+ it('throws (never falls through to device flow) when AIS_AGENT_API_KEY is set but empty', () => {
41
+ // An empty/whitespace value is a misconfiguration (e.g. unresolved CI
42
+ // secret). Falling through to the device flow here would mint a duplicate
43
+ // provisional agent — the exact bug this mode exists to kill.
44
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: '', AIS_AGENT_ID: 'agent-1' })).toThrow(AgentApiKeyConfigError);
45
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: ' ', AIS_AGENT_ID: 'agent-1' })).toThrow(/empty \(or whitespace-only\)/);
46
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: '\n' })).toThrow(/Unset the variable to use the device flow/);
47
+ });
48
+ it('trims a trailing newline from the key (CI secret injection)', () => {
49
+ const config = loadAgentApiKeyConfig({
50
+ AIS_AGENT_API_KEY: 'ais_ak_test123\n',
51
+ AIS_AGENT_ID: 'agent-1',
52
+ });
53
+ expect(config).toEqual({ apiKey: 'ais_ak_test123', agentId: 'agent-1' });
54
+ });
55
+ it('trims AIS_AGENT_ID', () => {
56
+ const config = loadAgentApiKeyConfig({
57
+ AIS_AGENT_API_KEY: 'ais_ak_test123',
58
+ AIS_AGENT_ID: ' agent-1\n',
59
+ });
60
+ expect(config).toEqual({ apiKey: 'ais_ak_test123', agentId: 'agent-1' });
61
+ });
62
+ it('treats a whitespace-only AIS_AGENT_ID as missing', () => {
63
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'ais_ak_test123', AIS_AGENT_ID: ' ' })).toThrow(/AIS_AGENT_ID is missing/);
64
+ });
65
+ it('rejects keys without the ais_ak_ prefix (service keys / JWTs do not work here)', () => {
66
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'sk_service_key_123', AIS_AGENT_ID: 'agent-1' })).toThrow(AgentApiKeyConfigError);
67
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'eyJhbGciOiJIUzI1NiJ9.x.y', AIS_AGENT_ID: 'agent-1' })).toThrow(/expected the ais_ak_ prefix/);
68
+ expect(() => loadAgentApiKeyConfig({ AIS_AGENT_API_KEY: 'not-a-key', AIS_AGENT_ID: 'agent-1' })).toThrow(/Service keys and JWTs do not work here/);
69
+ });
70
+ });
71
+ describe('dispatchToolKeyMode — in-scope tools send the scoped header only', () => {
72
+ const config = { apiKey: 'ais_ak_scoped', agentId: 'agent-42' };
73
+ it('remember: POSTs to /v1/agents/:id/memory with x-agent-api-key and no Authorization', async () => {
74
+ const fetchMock = vi.fn(async (_url, init) => {
75
+ const headers = init?.headers;
76
+ expect(headers['x-agent-api-key']).toBe('ais_ak_scoped');
77
+ expect(headers['Authorization']).toBeUndefined();
78
+ expect(headers['authorization']).toBeUndefined();
79
+ return jsonResponse(200, { success: true, data: { id: 'mem-1' } });
80
+ });
81
+ const result = await dispatchToolKeyMode(AIS_URL, config, 'remember', { content: 'a fact', type: 'fact' }, fetchMock);
82
+ expect(fetchMock).toHaveBeenCalledTimes(1);
83
+ const [url] = fetchMock.mock.calls[0];
84
+ expect(url).toBe(`${AIS_URL}/v1/agents/agent-42/memory`);
85
+ expect(result).toEqual({ success: true, data: { id: 'mem-1' } });
86
+ });
87
+ it('recall: GETs /v1/agents/:id/memory with the scoped header', async () => {
88
+ const fetchMock = vi.fn(async (_url, init) => {
89
+ const headers = init?.headers;
90
+ expect(headers['x-agent-api-key']).toBe('ais_ak_scoped');
91
+ expect(headers['Authorization']).toBeUndefined();
92
+ return jsonResponse(200, { success: true, data: { memories: [] } });
93
+ });
94
+ await dispatchToolKeyMode(AIS_URL, config, 'recall', { query: 'test' }, fetchMock);
95
+ const [url] = fetchMock.mock.calls[0];
96
+ expect(String(url)).toContain('/v1/agents/agent-42/memory?query=test');
97
+ });
98
+ it('full_context: GETs /v1/agents/:id/full-context', async () => {
99
+ const fetchMock = vi.fn(async (_url, _init) => jsonResponse(200, { success: true, data: {} }));
100
+ await dispatchToolKeyMode(AIS_URL, config, 'full_context', {}, fetchMock);
101
+ const [url] = fetchMock.mock.calls[0];
102
+ expect(url).toBe(`${AIS_URL}/v1/agents/agent-42/full-context`);
103
+ });
104
+ it('dream: POSTs /v1/agents/:id/dream', async () => {
105
+ const fetchMock = vi.fn(async (_url, _init) => jsonResponse(200, { success: true, data: {} }));
106
+ await dispatchToolKeyMode(AIS_URL, config, 'dream', {}, fetchMock);
107
+ const [url, init] = fetchMock.mock.calls[0];
108
+ expect(url).toBe(`${AIS_URL}/v1/agents/agent-42/dream`);
109
+ expect(init.method).toBe('POST');
110
+ });
111
+ it('a key loaded from env with a trailing newline reaches the header trimmed', async () => {
112
+ const loaded = loadAgentApiKeyConfig({
113
+ AIS_AGENT_API_KEY: 'ais_ak_from_ci\n',
114
+ AIS_AGENT_ID: 'agent-42\n',
115
+ });
116
+ expect(loaded).not.toBeNull();
117
+ const fetchMock = vi.fn(async (_url, init) => {
118
+ const headers = init?.headers;
119
+ expect(headers['x-agent-api-key']).toBe('ais_ak_from_ci');
120
+ return jsonResponse(200, { success: true, data: { id: 'mem-1' } });
121
+ });
122
+ await dispatchToolKeyMode(AIS_URL, loaded, 'remember', { content: 'x' }, fetchMock);
123
+ expect(fetchMock).toHaveBeenCalledTimes(1);
124
+ const [url] = fetchMock.mock.calls[0];
125
+ expect(url).toBe(`${AIS_URL}/v1/agents/agent-42/memory`);
126
+ });
127
+ it('whoami: answers from AIS_AGENT_ID and probes an allowed route (no identity route call)', async () => {
128
+ const calledPaths = [];
129
+ const fetchMock = vi.fn(async (url) => {
130
+ calledPaths.push(String(url));
131
+ return jsonResponse(200, { success: true, data: { memories: [] } });
132
+ });
133
+ const result = await dispatchToolKeyMode(AIS_URL, config, 'whoami', {}, fetchMock);
134
+ expect(calledPaths).toHaveLength(1);
135
+ expect(calledPaths[0]).toContain('/v1/agents/agent-42/memory?limit=1');
136
+ // Must never call the bare identity route — it has no allowlisted subresource.
137
+ expect(calledPaths.some((p) => /\/v1\/agents\/agent-42$/.test(p))).toBe(false);
138
+ expect(result).toMatchObject({
139
+ success: true,
140
+ data: { agentId: 'agent-42', authMode: 'agent-api-key', keyValid: true },
141
+ });
142
+ });
143
+ });
144
+ describe('dispatchToolKeyMode — out-of-scope tools fail honestly, no request made', () => {
145
+ const config = { apiKey: 'ais_ak_scoped', agentId: 'agent-42' };
146
+ it('handoff throws OutOfScopeToolError without calling fetch', async () => {
147
+ const fetchMock = vi.fn();
148
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'handoff', {}, fetchMock)).rejects.toThrow(OutOfScopeToolError);
149
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'handoff', {}, fetchMock)).rejects.toThrow(/not available with agent API key auth/);
150
+ expect(fetchMock).not.toHaveBeenCalled();
151
+ });
152
+ it('agent_load throws OutOfScopeToolError without calling fetch (no tenant-wide listing)', async () => {
153
+ const fetchMock = vi.fn();
154
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'agent_load', { query: 'Corbot' }, fetchMock)).rejects.toThrow(OutOfScopeToolError);
155
+ expect(fetchMock).not.toHaveBeenCalled();
156
+ });
157
+ it('unknown tool name returns the UNKNOWN_TOOL_KEY_MODE sentinel', async () => {
158
+ const fetchMock = vi.fn();
159
+ const result = await dispatchToolKeyMode(AIS_URL, config, 'not_a_real_tool', {}, fetchMock);
160
+ expect(result).toBe(UNKNOWN_TOOL_KEY_MODE);
161
+ expect(fetchMock).not.toHaveBeenCalled();
162
+ });
163
+ });
164
+ describe('dispatchToolKeyMode — server rejections surface honestly', () => {
165
+ const config = { apiKey: 'ais_ak_revoked', agentId: 'agent-42' };
166
+ it('403 (wrong agent / revoked key) is surfaced as a thrown error, not swallowed', async () => {
167
+ const fetchMock = vi.fn(async () => jsonResponse(403, {
168
+ success: false,
169
+ error: { code: 'FORBIDDEN', message: 'Agent API key does not authorize access to this agent' },
170
+ }));
171
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'recall', { query: 'x' }, fetchMock)).rejects.toThrow(/403/);
172
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'recall', { query: 'x' }, fetchMock)).rejects.toThrow(/does not authorize access to this agent/);
173
+ });
174
+ it('401 (invalid or expired key) is surfaced as a thrown error', async () => {
175
+ const fetchMock = vi.fn(async () => jsonResponse(401, { success: false, error: { code: 'UNAUTHORIZED', message: 'Invalid or expired agent API key' } }));
176
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'remember', { content: 'x' }, fetchMock)).rejects.toThrow(/401/);
177
+ await expect(dispatchToolKeyMode(AIS_URL, config, 'remember', { content: 'x' }, fetchMock)).rejects.toThrow(/Invalid or expired agent API key/);
178
+ });
179
+ });
180
+ //# sourceMappingURL=agent-key-mode.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-key-mode.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-key-mode.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Regression test for CORBOT-497C7FA3:
3
+ * agent_load must persist the switched agentId to disk so that process
4
+ * restarts (silent MCP reconnects via StdioServerTransport) restore the
5
+ * correct agent instead of silently reverting to the default (Corbot).
6
+ *
7
+ * Root cause: agent_load updated `creds` in memory but did not call
8
+ * saveCredentials(), so the credentials.json file still held the original
9
+ * agentId after a reconnect.
10
+ *
11
+ * Fix: saveCredentials() is now called immediately after the in-memory
12
+ * update inside the agent_load handler.
13
+ */
14
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
15
+ import { mkdtempSync, rmSync } from 'node:fs';
16
+ import { tmpdir } from 'node:os';
17
+ import { join } from 'node:path';
18
+ import { saveOAuthCredentials, loadOAuthCredentials, } from '../oauth-credentials.js';
19
+ const FUTURE_EXPIRY = new Date(Date.now() + 3_600_000).toISOString();
20
+ function makeCreds(agentId) {
21
+ return {
22
+ token: 'tok-test',
23
+ agentId,
24
+ tenantId: 'tenant-1',
25
+ expiresAt: FUTURE_EXPIRY,
26
+ };
27
+ }
28
+ describe('agent_load credential persistence (CORBOT-497C7FA3)', () => {
29
+ let tmpHome;
30
+ beforeEach(() => {
31
+ tmpHome = mkdtempSync(join(tmpdir(), 'aismem-agent-load-'));
32
+ });
33
+ afterEach(() => {
34
+ rmSync(tmpHome, { recursive: true, force: true });
35
+ });
36
+ it('credentials file reflects the switched agentId after saveOAuthCredentials', () => {
37
+ // Start with the default (Corbot) agent on disk, as it would be after initial auth.
38
+ saveOAuthCredentials(makeCreds('corbot-id'), tmpHome);
39
+ // Simulate what agent_load now does: update agentId AND persist to disk.
40
+ const switched = { ...makeCreds('corbot-id'), agentId: 'ender-id' };
41
+ saveOAuthCredentials(switched, tmpHome); // the fix ensures this is called
42
+ // A new process (reconnect) loads credentials from disk — must get 'ender-id'.
43
+ const restored = loadOAuthCredentials(tmpHome);
44
+ expect(restored?.agentId).toBe('ender-id');
45
+ });
46
+ it('without persisting, a reload returns the original agentId — documents the pre-fix bug', () => {
47
+ // Start with 'corbot-id' on disk.
48
+ saveOAuthCredentials(makeCreds('corbot-id'), tmpHome);
49
+ // Pre-fix: agent_load only updated the in-memory variable, never called save.
50
+ // Demonstrate what that means: disk still has the old agentId.
51
+ const _inMemoryOnly = { ...makeCreds('corbot-id'), agentId: 'ender-id' };
52
+ // (intentionally NOT calling saveOAuthCredentials here)
53
+ // Reconnect scenario: fresh process reads from disk → wrong agent.
54
+ const restored = loadOAuthCredentials(tmpHome);
55
+ expect(restored?.agentId).toBe('corbot-id'); // bug: silent revert to default
56
+ });
57
+ it('token and tenantId are preserved when only agentId is updated', () => {
58
+ const initial = makeCreds('corbot-id');
59
+ saveOAuthCredentials(initial, tmpHome);
60
+ const switched = { ...initial, agentId: 'ender-id' };
61
+ saveOAuthCredentials(switched, tmpHome);
62
+ const restored = loadOAuthCredentials(tmpHome);
63
+ expect(restored?.agentId).toBe('ender-id');
64
+ expect(restored?.token).toBe(initial.token);
65
+ expect(restored?.tenantId).toBe(initial.tenantId);
66
+ expect(restored?.expiresAt).toBe(initial.expiresAt);
67
+ });
68
+ });
69
+ //# sourceMappingURL=agent-load-persistence.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-load-persistence.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-load-persistence.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};