aismemory 0.5.0 → 0.5.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/dist/__tests__/auth-staleness.test.d.ts +9 -0
- package/dist/__tests__/auth-staleness.test.js +46 -0
- package/dist/__tests__/auth-staleness.test.js.map +1 -0
- package/dist/__tests__/auto-handoff.test.js +108 -2
- package/dist/__tests__/auto-handoff.test.js.map +1 -1
- package/dist/__tests__/config.test.js +6 -3
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/env-agent.test.d.ts +1 -0
- package/dist/__tests__/env-agent.test.js +19 -0
- package/dist/__tests__/env-agent.test.js.map +1 -0
- package/dist/__tests__/existing-hashes.test.d.ts +1 -0
- package/dist/__tests__/existing-hashes.test.js +122 -0
- package/dist/__tests__/existing-hashes.test.js.map +1 -0
- package/dist/__tests__/hydration.test.js +38 -0
- package/dist/__tests__/hydration.test.js.map +1 -1
- package/dist/__tests__/local-mirror.test.js +4 -0
- package/dist/__tests__/local-mirror.test.js.map +1 -1
- package/dist/__tests__/oauth-credentials.test.d.ts +1 -0
- package/dist/__tests__/oauth-credentials.test.js +29 -0
- package/dist/__tests__/oauth-credentials.test.js.map +1 -0
- package/dist/__tests__/pipeline-ingestion.test.js +112 -1
- package/dist/__tests__/pipeline-ingestion.test.js.map +1 -1
- package/dist/__tests__/refresh.test.js +24 -0
- package/dist/__tests__/refresh.test.js.map +1 -1
- package/dist/__tests__/sync-memory-cli.test.d.ts +1 -0
- package/dist/__tests__/sync-memory-cli.test.js +200 -0
- package/dist/__tests__/sync-memory-cli.test.js.map +1 -0
- package/dist/__tests__/telemetry.test.d.ts +1 -0
- package/dist/__tests__/telemetry.test.js +67 -0
- package/dist/__tests__/telemetry.test.js.map +1 -0
- package/dist/__tests__/token-expiry-reauth.test.d.ts +1 -0
- package/dist/__tests__/token-expiry-reauth.test.js +201 -0
- package/dist/__tests__/token-expiry-reauth.test.js.map +1 -0
- package/dist/__tests__/tool-args.test.d.ts +1 -0
- package/dist/__tests__/tool-args.test.js +78 -0
- package/dist/__tests__/tool-args.test.js.map +1 -0
- package/dist/auth-staleness.d.ts +27 -0
- package/dist/auth-staleness.js +41 -0
- package/dist/auth-staleness.js.map +1 -0
- package/dist/auto-handoff.d.ts +2 -0
- package/dist/auto-handoff.js +40 -16
- package/dist/auto-handoff.js.map +1 -1
- package/dist/cli/sync-memory.js +31 -36
- package/dist/cli/sync-memory.js.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/env-agent.d.ts +10 -0
- package/dist/env-agent.js +14 -0
- package/dist/env-agent.js.map +1 -0
- package/dist/hydration.js +54 -3
- package/dist/hydration.js.map +1 -1
- package/dist/index.js +229 -113
- package/dist/index.js.map +1 -1
- package/dist/local-mirror.d.ts +5 -0
- package/dist/local-mirror.js +72 -14
- package/dist/local-mirror.js.map +1 -1
- package/dist/oauth-credentials.d.ts +14 -0
- package/dist/oauth-credentials.js +35 -0
- package/dist/oauth-credentials.js.map +1 -0
- package/dist/pipeline/bulk-store.d.ts +46 -0
- package/dist/pipeline/bulk-store.js +165 -0
- package/dist/pipeline/bulk-store.js.map +1 -0
- package/dist/pipeline/existing-hashes.d.ts +20 -0
- package/dist/pipeline/existing-hashes.js +111 -0
- package/dist/pipeline/existing-hashes.js.map +1 -0
- package/dist/pipeline/ingestion.d.ts +8 -4
- package/dist/pipeline/ingestion.js +36 -8
- package/dist/pipeline/ingestion.js.map +1 -1
- package/dist/telemetry.d.ts +22 -0
- package/dist/telemetry.js +28 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/tool-args.d.ts +52 -0
- package/dist/tool-args.js +78 -0
- package/dist/tool-args.js.map +1 -0
- package/dist/trust-ledger.js +2 -2
- package/dist/trust-ledger.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import { mkdtempSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join, resolve } from 'path';
|
|
6
|
+
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
7
|
+
import { generateAndSaveKeypair } from '../key-auth.js';
|
|
8
|
+
/**
|
|
9
|
+
* Regression: mid-session TOKEN_EXPIRED failures (CORBOT-A5BBD580).
|
|
10
|
+
*
|
|
11
|
+
* The MCP checked credential expiry only when loading credentials.json at
|
|
12
|
+
* startup; ensureCredentials() returned the cached creds forever after, and
|
|
13
|
+
* no TOKEN_EXPIRED response ever cleared them. A session starting inside the
|
|
14
|
+
* final hours of the 7-day token life expired mid-session and every memory
|
|
15
|
+
* write failed until the process was restarted — observed 2026-05-24,
|
|
16
|
+
* 2026-05-31, 2026-06-09.
|
|
17
|
+
*
|
|
18
|
+
* After the fix:
|
|
19
|
+
* 1. ensureCredentials() treats creds expiring within a safety margin as
|
|
20
|
+
* stale and silently re-runs key auth (challenge → did-prove).
|
|
21
|
+
* 2. A TOKEN_EXPIRED / INVALID_TOKEN response from any tool call clears
|
|
22
|
+
* the cached creds, re-auths, and retries the call once.
|
|
23
|
+
*
|
|
24
|
+
* Both scenarios drive the real binary against a fake AIS server, with a
|
|
25
|
+
* key file present in the fake HOME so re-auth is silent (no device flow).
|
|
26
|
+
*/
|
|
27
|
+
const OLD_TOKEN = mintJwt('old-session-token');
|
|
28
|
+
const NEW_TOKEN = mintJwt('fresh-after-reauth');
|
|
29
|
+
function mintJwt(marker) {
|
|
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', marker })).toString('base64url');
|
|
32
|
+
return `${header}.${payload}.fakesig-${marker}`;
|
|
33
|
+
}
|
|
34
|
+
describe('aismemory MCP token-expiry re-auth', () => {
|
|
35
|
+
let fakeAis;
|
|
36
|
+
let port = 0;
|
|
37
|
+
let didProveCalls = 0;
|
|
38
|
+
const agentTokensSeen = [];
|
|
39
|
+
beforeAll(async () => {
|
|
40
|
+
fakeAis = createServer((req, res) => {
|
|
41
|
+
req.on('data', () => { });
|
|
42
|
+
req.on('end', () => {
|
|
43
|
+
const auth = (req.headers['authorization'] ?? '').replace('Bearer ', '');
|
|
44
|
+
if (req.url === '/v1/auth/challenge' && req.method === 'POST') {
|
|
45
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
46
|
+
res.end(JSON.stringify({
|
|
47
|
+
success: true,
|
|
48
|
+
data: { nonce: 'test-nonce', exp: Math.floor(Date.now() / 1000) + 60, hmac: 'test-hmac' },
|
|
49
|
+
}));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (req.url === '/v1/auth/did-prove' && req.method === 'POST') {
|
|
53
|
+
didProveCalls += 1;
|
|
54
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
55
|
+
res.end(JSON.stringify({
|
|
56
|
+
success: true,
|
|
57
|
+
data: {
|
|
58
|
+
bearerToken: NEW_TOKEN,
|
|
59
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 3600 * 1000).toISOString(),
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (req.url?.startsWith('/v1/agents') && req.method === 'GET') {
|
|
65
|
+
agentTokensSeen.push(auth);
|
|
66
|
+
if (auth !== NEW_TOKEN) {
|
|
67
|
+
res.writeHead(401, { 'content-type': 'application/json' });
|
|
68
|
+
res.end(JSON.stringify({ success: false, error: { code: 'TOKEN_EXPIRED', message: 'Token expired' } }));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (req.url === '/v1/agents') {
|
|
72
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
73
|
+
res.end(JSON.stringify({
|
|
74
|
+
success: true,
|
|
75
|
+
data: { agents: [{ id: 'agent-1', name: 'TestAgent', status: 'active' }] },
|
|
76
|
+
}));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (req.url === '/v1/agents/agent-1') {
|
|
80
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
81
|
+
res.end(JSON.stringify({
|
|
82
|
+
success: true,
|
|
83
|
+
data: { id: 'agent-1', name: 'TestAgent', status: 'active' },
|
|
84
|
+
}));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Hydration / memory refresh / telemetry — irrelevant here.
|
|
89
|
+
res.writeHead(404, { 'content-type': 'application/json' });
|
|
90
|
+
res.end(JSON.stringify({ success: false, error: { code: 'NOT_FOUND' } }));
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
await new Promise((r) => {
|
|
94
|
+
fakeAis.listen(0, '127.0.0.1', () => {
|
|
95
|
+
const addr = fakeAis.address();
|
|
96
|
+
if (addr && typeof addr === 'object')
|
|
97
|
+
port = addr.port;
|
|
98
|
+
r();
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
afterAll(async () => {
|
|
103
|
+
await new Promise((r) => fakeAis?.close(() => r()));
|
|
104
|
+
});
|
|
105
|
+
beforeEach(() => {
|
|
106
|
+
didProveCalls = 0;
|
|
107
|
+
agentTokensSeen.length = 0;
|
|
108
|
+
});
|
|
109
|
+
async function runWhoami(credsExpiresAt) {
|
|
110
|
+
const distPath = resolve(__dirname, '..', '..', 'dist', 'index.js');
|
|
111
|
+
if (!existsSync(distPath)) {
|
|
112
|
+
throw new Error(`dist/index.js missing — run \`pnpm build\` first (looked at ${distPath})`);
|
|
113
|
+
}
|
|
114
|
+
const fakeHome = mkdtempSync(join(tmpdir(), 'aismem-reauth-'));
|
|
115
|
+
const aismemDir = join(fakeHome, '.aismemory');
|
|
116
|
+
mkdirSync(aismemDir, { recursive: true });
|
|
117
|
+
writeFileSync(join(aismemDir, 'credentials.json'), JSON.stringify({
|
|
118
|
+
token: OLD_TOKEN,
|
|
119
|
+
agentId: 'agent-1',
|
|
120
|
+
tenantId: 'tenant-1',
|
|
121
|
+
expiresAt: credsExpiresAt,
|
|
122
|
+
}));
|
|
123
|
+
await generateAndSaveKeypair({
|
|
124
|
+
userId: 'user-1',
|
|
125
|
+
userDid: 'did:web:test:users:user-1',
|
|
126
|
+
keysDir: join(aismemDir, 'keys'),
|
|
127
|
+
});
|
|
128
|
+
let proc = null;
|
|
129
|
+
try {
|
|
130
|
+
proc = 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 stdoutBuf = '';
|
|
135
|
+
proc.stdout.on('data', (d) => { stdoutBuf += d.toString(); });
|
|
136
|
+
proc.stderr.on('data', () => { });
|
|
137
|
+
proc.stdin.write(JSON.stringify({
|
|
138
|
+
jsonrpc: '2.0', id: 1, method: 'initialize',
|
|
139
|
+
params: { protocolVersion: '2024-11-05', capabilities: {}, clientInfo: { name: 'reauth-test', version: '0.0.0' } },
|
|
140
|
+
}) + '\n');
|
|
141
|
+
await waitFor(() => stdoutBuf.includes('"id":1'), 5000, 'initialize response');
|
|
142
|
+
proc.stdin.write(JSON.stringify({
|
|
143
|
+
jsonrpc: '2.0', id: 2, method: 'tools/call',
|
|
144
|
+
params: { name: 'whoami', arguments: {} },
|
|
145
|
+
}) + '\n');
|
|
146
|
+
await waitFor(() => stdoutBuf.includes('"id":2'), 15000, 'whoami response');
|
|
147
|
+
return extractResponseText(stdoutBuf, 2);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
proc?.kill();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
it('re-auths via key file and retries once when AIS rejects the stored token', async () => {
|
|
154
|
+
// Creds look fresh by clock (30 days out) but the server rejects them —
|
|
155
|
+
// covers server-side rotation/expiry discovered only on use.
|
|
156
|
+
const response = await runWhoami(new Date(Date.now() + 30 * 24 * 3600 * 1000).toISOString());
|
|
157
|
+
expect(didProveCalls).toBe(1);
|
|
158
|
+
expect(response).toContain('TestAgent');
|
|
159
|
+
expect(response).not.toContain('TOKEN_EXPIRED');
|
|
160
|
+
}, 30000);
|
|
161
|
+
it('refuses near-expiry cached creds and re-auths before using them', async () => {
|
|
162
|
+
// Creds expire in 60s — inside the safety margin. The old token must
|
|
163
|
+
// never reach the API.
|
|
164
|
+
const response = await runWhoami(new Date(Date.now() + 60 * 1000).toISOString());
|
|
165
|
+
expect(didProveCalls).toBe(1);
|
|
166
|
+
expect(agentTokensSeen).not.toContain(OLD_TOKEN);
|
|
167
|
+
expect(response).toContain('TestAgent');
|
|
168
|
+
}, 30000);
|
|
169
|
+
});
|
|
170
|
+
function waitFor(predicate, ms, label) {
|
|
171
|
+
return new Promise((resolveOuter, reject) => {
|
|
172
|
+
const start = Date.now();
|
|
173
|
+
const tick = () => {
|
|
174
|
+
if (predicate()) {
|
|
175
|
+
resolveOuter();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (Date.now() - start > ms) {
|
|
179
|
+
reject(new Error(`timeout waiting for ${label}`));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
setTimeout(tick, 50);
|
|
183
|
+
};
|
|
184
|
+
tick();
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function extractResponseText(buf, id) {
|
|
188
|
+
for (const line of buf.split('\n')) {
|
|
189
|
+
if (!line.includes(`"id":${id}`))
|
|
190
|
+
continue;
|
|
191
|
+
try {
|
|
192
|
+
const parsed = JSON.parse(line);
|
|
193
|
+
const text = parsed.result?.content?.[0]?.text;
|
|
194
|
+
if (typeof text === 'string')
|
|
195
|
+
return text;
|
|
196
|
+
}
|
|
197
|
+
catch { /* malformed line; skip */ }
|
|
198
|
+
}
|
|
199
|
+
return '';
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=token-expiry-reauth.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-expiry-reauth.test.js","sourceRoot":"","sources":["../../src/__tests__/token-expiry-reauth.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { MAX_MEMORY_CONTENT_LENGTH, parseAgentLoadArgs, parseEmptyToolArgs, parseHandoffArgs, parseRecallArgs, parseRememberArgs, } from '../tool-args.js';
|
|
3
|
+
describe('parseRememberArgs', () => {
|
|
4
|
+
it('accepts valid remember payload', () => {
|
|
5
|
+
const parsed = parseRememberArgs({
|
|
6
|
+
content: 'hello',
|
|
7
|
+
type: 'fact',
|
|
8
|
+
importance: 0.8,
|
|
9
|
+
});
|
|
10
|
+
expect(parsed).toEqual({ content: 'hello', type: 'fact', importance: 0.8 });
|
|
11
|
+
});
|
|
12
|
+
it('applies defaults for optional fields', () => {
|
|
13
|
+
expect(parseRememberArgs({ content: 'x' })).toEqual({
|
|
14
|
+
content: 'x',
|
|
15
|
+
type: 'context',
|
|
16
|
+
importance: 0.5,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('rejects missing content', () => {
|
|
20
|
+
expect(() => parseRememberArgs({})).toThrow(/content is required/);
|
|
21
|
+
});
|
|
22
|
+
it('rejects invalid memory type', () => {
|
|
23
|
+
expect(() => parseRememberArgs({ content: 'x', type: 'invalid' })).toThrow();
|
|
24
|
+
});
|
|
25
|
+
it('rejects importance outside 0-1', () => {
|
|
26
|
+
expect(() => parseRememberArgs({ content: 'x', importance: 2 })).toThrow();
|
|
27
|
+
});
|
|
28
|
+
it('rejects oversized content', () => {
|
|
29
|
+
expect(() => parseRememberArgs({ content: 'x'.repeat(MAX_MEMORY_CONTENT_LENGTH + 1) })).toThrow(/exceeds maximum length/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('parseRecallArgs', () => {
|
|
33
|
+
it('requires query', () => {
|
|
34
|
+
expect(() => parseRecallArgs({})).toThrow(/query is required/);
|
|
35
|
+
});
|
|
36
|
+
it('accepts query with optional limit and type', () => {
|
|
37
|
+
expect(parseRecallArgs({ query: 'cats', limit: 5, type: 'fact' })).toEqual({
|
|
38
|
+
query: 'cats',
|
|
39
|
+
limit: 5,
|
|
40
|
+
type: 'fact',
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
it('rejects limit above MAX_QUERY_LIMIT', () => {
|
|
44
|
+
expect(() => parseRecallArgs({ query: 'x', limit: 101 })).toThrow();
|
|
45
|
+
});
|
|
46
|
+
it('rejects oversized query', () => {
|
|
47
|
+
expect(() => parseRecallArgs({ query: 'q'.repeat(MAX_MEMORY_CONTENT_LENGTH + 1) })).toThrow(/exceeds maximum length/);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('parseHandoffArgs', () => {
|
|
51
|
+
it('accepts empty handoff args', () => {
|
|
52
|
+
expect(parseHandoffArgs({})).toEqual({});
|
|
53
|
+
});
|
|
54
|
+
it('accepts summary and keyLearnings', () => {
|
|
55
|
+
expect(parseHandoffArgs({ summary: 'done', keyLearnings: ['a', 'b'] })).toEqual({ summary: 'done', keyLearnings: ['a', 'b'] });
|
|
56
|
+
});
|
|
57
|
+
it('rejects too many keyLearnings', () => {
|
|
58
|
+
expect(() => parseHandoffArgs({ keyLearnings: Array.from({ length: 101 }, () => 'x') })).toThrow(/maximum of 100/);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('parseAgentLoadArgs', () => {
|
|
62
|
+
it('requires non-empty query', () => {
|
|
63
|
+
expect(() => parseAgentLoadArgs({})).toThrow(/query is required/);
|
|
64
|
+
expect(() => parseAgentLoadArgs({ query: ' ' })).toThrow(/query is required/);
|
|
65
|
+
});
|
|
66
|
+
it('trims whitespace from query', () => {
|
|
67
|
+
expect(parseAgentLoadArgs({ query: ' Corbot ' })).toEqual({ query: 'Corbot' });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('parseEmptyToolArgs', () => {
|
|
71
|
+
it('accepts empty object for no-arg tools', () => {
|
|
72
|
+
expect(parseEmptyToolArgs({})).toEqual({});
|
|
73
|
+
});
|
|
74
|
+
it('rejects unexpected keys', () => {
|
|
75
|
+
expect(() => parseEmptyToolArgs({ extra: true })).toThrow();
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=tool-args.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-args.test.js","sourceRoot":"","sources":["../../src/__tests__/tool-args.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential staleness + auth-error detection for the MCP re-auth loop.
|
|
3
|
+
*
|
|
4
|
+
* key-auth.ts promises: "When the JWT expires (or AIS rotates JWT_SECRET)
|
|
5
|
+
* the client silently re-runs challenge/prove. The user notices nothing."
|
|
6
|
+
* These helpers are the detection half of that promise (CORBOT-A5BBD580):
|
|
7
|
+
* ensureCredentials() uses isCredsStale() to refuse near-expiry cached
|
|
8
|
+
* creds, and the tool dispatcher uses isAuthErrorResult() to clear creds
|
|
9
|
+
* and retry once when AIS rejects a token mid-session.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Safety margin before the recorded expiry at which cached credentials are
|
|
13
|
+
* treated as stale. Five minutes comfortably covers clock skew between the
|
|
14
|
+
* client and AIS plus the duration of any single tool call.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CREDS_STALENESS_MARGIN_MS: number;
|
|
17
|
+
/**
|
|
18
|
+
* True when credentials expiring at `expiresAt` should no longer be used at
|
|
19
|
+
* time `nowMs`. Unparseable expiry timestamps are treated as stale — a
|
|
20
|
+
* re-auth is cheap, silently using a token of unknown validity is not.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isCredsStale(expiresAt: string, nowMs: number, marginMs?: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* True when an AIS response body is a token-rejection error
|
|
25
|
+
* (TOKEN_EXPIRED or INVALID_TOKEN) — the signal to re-auth and retry.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isAuthErrorResult(result: unknown): boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential staleness + auth-error detection for the MCP re-auth loop.
|
|
3
|
+
*
|
|
4
|
+
* key-auth.ts promises: "When the JWT expires (or AIS rotates JWT_SECRET)
|
|
5
|
+
* the client silently re-runs challenge/prove. The user notices nothing."
|
|
6
|
+
* These helpers are the detection half of that promise (CORBOT-A5BBD580):
|
|
7
|
+
* ensureCredentials() uses isCredsStale() to refuse near-expiry cached
|
|
8
|
+
* creds, and the tool dispatcher uses isAuthErrorResult() to clear creds
|
|
9
|
+
* and retry once when AIS rejects a token mid-session.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Safety margin before the recorded expiry at which cached credentials are
|
|
13
|
+
* treated as stale. Five minutes comfortably covers clock skew between the
|
|
14
|
+
* client and AIS plus the duration of any single tool call.
|
|
15
|
+
*/
|
|
16
|
+
export const CREDS_STALENESS_MARGIN_MS = 5 * 60 * 1000;
|
|
17
|
+
/**
|
|
18
|
+
* True when credentials expiring at `expiresAt` should no longer be used at
|
|
19
|
+
* time `nowMs`. Unparseable expiry timestamps are treated as stale — a
|
|
20
|
+
* re-auth is cheap, silently using a token of unknown validity is not.
|
|
21
|
+
*/
|
|
22
|
+
export function isCredsStale(expiresAt, nowMs, marginMs = CREDS_STALENESS_MARGIN_MS) {
|
|
23
|
+
const expiresMs = Date.parse(expiresAt);
|
|
24
|
+
if (Number.isNaN(expiresMs))
|
|
25
|
+
return true;
|
|
26
|
+
return expiresMs - nowMs <= marginMs;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* True when an AIS response body is a token-rejection error
|
|
30
|
+
* (TOKEN_EXPIRED or INVALID_TOKEN) — the signal to re-auth and retry.
|
|
31
|
+
*/
|
|
32
|
+
export function isAuthErrorResult(result) {
|
|
33
|
+
if (typeof result !== 'object' || result === null)
|
|
34
|
+
return false;
|
|
35
|
+
const r = result;
|
|
36
|
+
if (r.success !== false)
|
|
37
|
+
return false;
|
|
38
|
+
const code = r.error?.code;
|
|
39
|
+
return code === 'TOKEN_EXPIRED' || code === 'INVALID_TOKEN';
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=auth-staleness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-staleness.js","sourceRoot":"","sources":["../src/auth-staleness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiB,EACjB,KAAa,EACb,WAAmB,yBAAyB;IAE5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,OAAO,SAAS,GAAG,KAAK,IAAI,QAAQ,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAChE,MAAM,CAAC,GAAG,MAA0D,CAAC;IACrE,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK;QAAE,OAAO,KAAK,CAAC;IACtC,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC;IAC3B,OAAO,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,eAAe,CAAC;AAC9D,CAAC"}
|
package/dist/auto-handoff.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export declare class ActivityTracker {
|
|
2
2
|
private toolCounts;
|
|
3
3
|
private storedMemories;
|
|
4
|
+
private storedMemoryCount;
|
|
4
5
|
recordToolUse(toolName: string): void;
|
|
5
6
|
recordMemoryStored(content: string): void;
|
|
6
7
|
getToolCounts(): Record<string, number>;
|
|
8
|
+
getStoredMemoryCount(): number;
|
|
7
9
|
getStoredMemories(): string[];
|
|
8
10
|
hasActivity(): boolean;
|
|
9
11
|
}
|
package/dist/auto-handoff.js
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
|
+
/** Max verbatim snippets kept for handoff keyLearnings (FIFO). */
|
|
2
|
+
const MAX_STORED_MEMORY_SNIPPETS = 10;
|
|
3
|
+
/** Max chars per snippet to bound handoff payload size. */
|
|
4
|
+
const MAX_STORED_MEMORY_SNIPPET_CHARS = 512;
|
|
1
5
|
export class ActivityTracker {
|
|
2
6
|
toolCounts = {};
|
|
3
7
|
storedMemories = [];
|
|
8
|
+
storedMemoryCount = 0;
|
|
4
9
|
recordToolUse(toolName) {
|
|
5
10
|
this.toolCounts[toolName] = (this.toolCounts[toolName] ?? 0) + 1;
|
|
6
11
|
}
|
|
7
12
|
recordMemoryStored(content) {
|
|
8
|
-
this.
|
|
13
|
+
this.storedMemoryCount++;
|
|
14
|
+
const snippet = content.length > MAX_STORED_MEMORY_SNIPPET_CHARS
|
|
15
|
+
? `${content.slice(0, MAX_STORED_MEMORY_SNIPPET_CHARS)}…`
|
|
16
|
+
: content;
|
|
17
|
+
this.storedMemories.push(snippet);
|
|
18
|
+
if (this.storedMemories.length > MAX_STORED_MEMORY_SNIPPETS) {
|
|
19
|
+
this.storedMemories.shift();
|
|
20
|
+
}
|
|
9
21
|
}
|
|
10
22
|
getToolCounts() {
|
|
11
23
|
return { ...this.toolCounts };
|
|
12
24
|
}
|
|
25
|
+
getStoredMemoryCount() {
|
|
26
|
+
return this.storedMemoryCount;
|
|
27
|
+
}
|
|
13
28
|
getStoredMemories() {
|
|
14
29
|
return [...this.storedMemories];
|
|
15
30
|
}
|
|
@@ -24,30 +39,39 @@ export function buildHandoffPayload(tracker) {
|
|
|
24
39
|
const toolParts = Object.entries(tracker.getToolCounts())
|
|
25
40
|
.map(([name, count]) => `${name} (${count})`)
|
|
26
41
|
.join(', ');
|
|
42
|
+
const memoryCount = tracker.getStoredMemoryCount();
|
|
27
43
|
const memories = tracker.getStoredMemories();
|
|
28
|
-
const summary = `Used tools: ${toolParts}.${
|
|
44
|
+
const summary = `Used tools: ${toolParts}.${memoryCount > 0 ? ` Stored ${memoryCount} memories.` : ''}`;
|
|
29
45
|
return { summary, keyLearnings: memories };
|
|
30
46
|
}
|
|
47
|
+
const HANDOFF_FETCH_TIMEOUT_MS = 5_000;
|
|
31
48
|
export function setupAutoHandoff(aisUrl, agentId, token, tracker, onBeforeExit) {
|
|
32
|
-
|
|
49
|
+
let exiting = false;
|
|
50
|
+
const doHandoff = async () => {
|
|
51
|
+
if (exiting)
|
|
52
|
+
return;
|
|
53
|
+
exiting = true;
|
|
33
54
|
if (!tracker.hasActivity())
|
|
34
55
|
return;
|
|
35
56
|
const payload = buildHandoffPayload(tracker);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
try {
|
|
58
|
+
await fetch(`${aisUrl}/v1/agents/${agentId}/handoff`, {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
|
|
61
|
+
body: JSON.stringify(payload),
|
|
62
|
+
signal: AbortSignal.timeout(HANDOFF_FETCH_TIMEOUT_MS),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Best-effort — still exit if POST fails or times out
|
|
67
|
+
}
|
|
41
68
|
if (onBeforeExit)
|
|
42
69
|
onBeforeExit();
|
|
43
70
|
};
|
|
44
|
-
|
|
45
|
-
doHandoff();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
process.on('SIGINT',
|
|
49
|
-
doHandoff();
|
|
50
|
-
process.exit(0);
|
|
51
|
-
});
|
|
71
|
+
const onSignal = () => {
|
|
72
|
+
void doHandoff().finally(() => process.exit(0));
|
|
73
|
+
};
|
|
74
|
+
process.on('SIGTERM', onSignal);
|
|
75
|
+
process.on('SIGINT', onSignal);
|
|
52
76
|
}
|
|
53
77
|
//# sourceMappingURL=auto-handoff.js.map
|
package/dist/auto-handoff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-handoff.js","sourceRoot":"","sources":["../src/auto-handoff.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,eAAe;IAClB,UAAU,GAA2B,EAAE,CAAC;IACxC,cAAc,GAAa,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"auto-handoff.js","sourceRoot":"","sources":["../src/auto-handoff.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/sync-memory.js
CHANGED
|
@@ -3,6 +3,8 @@ import { join, resolve } from 'node:path';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { createInterface } from 'node:readline/promises';
|
|
5
5
|
import { runIngestion } from '../pipeline/ingestion.js';
|
|
6
|
+
import { loadExistingHashes } from '../pipeline/existing-hashes.js';
|
|
7
|
+
import { bulkStoreViaAis, singleWriteAllViaAis } from '../pipeline/bulk-store.js';
|
|
6
8
|
import { LocalClaudeSource } from '../sources/local-claude.js';
|
|
7
9
|
import { SyncConfig } from '../config.js';
|
|
8
10
|
import { TrustLedger } from '../trust-ledger.js';
|
|
@@ -11,7 +13,13 @@ import { interactiveReview, bulkSelectAll } from '../review/cli-review.js';
|
|
|
11
13
|
import { resolveScope } from '../pipeline/scope-resolver.js';
|
|
12
14
|
import { readClaudeMemoryTree } from './read-claude-memory-tree.js';
|
|
13
15
|
function parseArgs(argv) {
|
|
14
|
-
const args = {
|
|
16
|
+
const args = {
|
|
17
|
+
source: 'claude-local',
|
|
18
|
+
dryRun: false,
|
|
19
|
+
saveScope: false,
|
|
20
|
+
acceptAll: false,
|
|
21
|
+
singleWrite: false,
|
|
22
|
+
};
|
|
15
23
|
for (let i = 0; i < argv.length; i++) {
|
|
16
24
|
if (argv[i] === '--source' && argv[i + 1]) {
|
|
17
25
|
args.source = argv[++i];
|
|
@@ -25,6 +33,12 @@ function parseArgs(argv) {
|
|
|
25
33
|
else if (argv[i] === '--save-scope') {
|
|
26
34
|
args.saveScope = true;
|
|
27
35
|
}
|
|
36
|
+
else if (argv[i] === '--accept-all') {
|
|
37
|
+
args.acceptAll = true;
|
|
38
|
+
}
|
|
39
|
+
else if (argv[i] === '--single-write') {
|
|
40
|
+
args.singleWrite = true;
|
|
41
|
+
}
|
|
28
42
|
}
|
|
29
43
|
return args;
|
|
30
44
|
}
|
|
@@ -33,38 +47,6 @@ function getAdapter(id) {
|
|
|
33
47
|
return new LocalClaudeSource();
|
|
34
48
|
throw new Error(`Unknown source: ${id}. Phase 1 supports only 'claude-local'.`);
|
|
35
49
|
}
|
|
36
|
-
async function bulkStoreViaAis(agentId, drafts) {
|
|
37
|
-
const apiBase = process.env['AIS_DOMAIN']
|
|
38
|
-
? `https://${process.env['AIS_DOMAIN']}`
|
|
39
|
-
: 'https://ais.agentsandswarms.ai';
|
|
40
|
-
const apiKey = process.env['AIS_SERVICE_KEY'] ?? process.env['AIS_API_KEY'];
|
|
41
|
-
const tenantId = process.env['AIS_TENANT_ID'];
|
|
42
|
-
if (!apiKey || !tenantId)
|
|
43
|
-
throw new Error('AIS_API_KEY and AIS_TENANT_ID must be set');
|
|
44
|
-
const body = {
|
|
45
|
-
memories: drafts.map((d) => ({
|
|
46
|
-
content: d.content,
|
|
47
|
-
type: d.type,
|
|
48
|
-
importance: d.importance,
|
|
49
|
-
metadata: { provenance: d.provenance, trustScoreHint: d.trustScore },
|
|
50
|
-
})),
|
|
51
|
-
};
|
|
52
|
-
const res = await fetch(`${apiBase}/v1/agents/${agentId}/memory/bulk`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: {
|
|
55
|
-
'Content-Type': 'application/json',
|
|
56
|
-
'x-api-key': apiKey,
|
|
57
|
-
'x-tenant-id': tenantId,
|
|
58
|
-
},
|
|
59
|
-
body: JSON.stringify(body),
|
|
60
|
-
});
|
|
61
|
-
if (!res.ok) {
|
|
62
|
-
const bodyText = await res.text().catch(() => '');
|
|
63
|
-
throw new Error(`AIS bulk store failed: HTTP ${res.status} ${res.statusText}${bodyText ? ` — ${bodyText.slice(0, 500)}` : ''}`);
|
|
64
|
-
}
|
|
65
|
-
const json = (await res.json());
|
|
66
|
-
return { created: json.data?.created ?? [], failed: json.data?.failed ?? [] };
|
|
67
|
-
}
|
|
68
50
|
async function main() {
|
|
69
51
|
const args = parseArgs(process.argv.slice(2));
|
|
70
52
|
const agentId = process.env['AIS_AGENT_ID'];
|
|
@@ -112,19 +94,32 @@ async function main() {
|
|
|
112
94
|
scope.label,
|
|
113
95
|
].join('|');
|
|
114
96
|
const firstSyncForScope = !ledger.isTrusted(agentId, adapter.id, scopeKey);
|
|
115
|
-
const review = async (drafts) => args.dryRun
|
|
97
|
+
const review = async (drafts) => args.dryRun || args.acceptAll
|
|
116
98
|
? bulkSelectAll(drafts)
|
|
117
99
|
: interactiveReview(drafts, {
|
|
118
100
|
sourceId: adapter.id,
|
|
119
101
|
scopeLabel: scope.label,
|
|
120
102
|
firstSyncForScope,
|
|
121
103
|
});
|
|
104
|
+
const apiKey = process.env['AIS_SERVICE_KEY'] ?? process.env['AIS_API_KEY'];
|
|
105
|
+
const tenantId = process.env['AIS_TENANT_ID'];
|
|
106
|
+
const apiBase = process.env['AIS_DOMAIN']
|
|
107
|
+
? `https://${process.env['AIS_DOMAIN']}`
|
|
108
|
+
: 'https://ais.agentsandswarms.ai';
|
|
122
109
|
const bulkStore = args.dryRun
|
|
123
110
|
? async (drafts) => ({
|
|
124
111
|
created: drafts.map((_, i) => ({ index: i, id: `dry-run-${i}` })),
|
|
125
112
|
failed: [],
|
|
126
113
|
})
|
|
127
|
-
: (
|
|
114
|
+
: (() => {
|
|
115
|
+
if (!apiKey || !tenantId)
|
|
116
|
+
throw new Error('AIS_API_KEY and AIS_TENANT_ID must be set');
|
|
117
|
+
const creds = { apiBase, apiKey, tenantId };
|
|
118
|
+
return args.singleWrite
|
|
119
|
+
? (drafts) => singleWriteAllViaAis(agentId, drafts, creds)
|
|
120
|
+
: (drafts) => bulkStoreViaAis(agentId, drafts, creds);
|
|
121
|
+
})();
|
|
122
|
+
const existingHashes = await loadExistingHashes(agentId, mirror, apiKey && tenantId ? { apiBase, apiKey, tenantId } : undefined);
|
|
128
123
|
const result = await runIngestion({
|
|
129
124
|
adapter,
|
|
130
125
|
rawSourceData,
|
|
@@ -133,7 +128,7 @@ async function main() {
|
|
|
133
128
|
config,
|
|
134
129
|
ledger,
|
|
135
130
|
mirror,
|
|
136
|
-
existingHashes
|
|
131
|
+
existingHashes,
|
|
137
132
|
bulkStore,
|
|
138
133
|
review,
|
|
139
134
|
});
|