aismemory 0.7.5 → 0.8.2
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 +247 -7
- package/dist/__tests__/agent-archive.test.d.ts +1 -0
- package/dist/__tests__/agent-archive.test.js +153 -0
- package/dist/__tests__/agent-archive.test.js.map +1 -0
- package/dist/__tests__/agent-key-mode.test.js +121 -6
- package/dist/__tests__/agent-key-mode.test.js.map +1 -1
- package/dist/__tests__/agent-load-persistence.test.js +22 -6
- package/dist/__tests__/agent-load-persistence.test.js.map +1 -1
- package/dist/__tests__/ais-http.test.js +23 -3
- package/dist/__tests__/ais-http.test.js.map +1 -1
- package/dist/__tests__/auto-handoff.test.js +56 -0
- package/dist/__tests__/auto-handoff.test.js.map +1 -1
- package/dist/__tests__/bond-link-survives-exit.test.d.ts +1 -0
- package/dist/__tests__/bond-link-survives-exit.test.js +177 -0
- package/dist/__tests__/bond-link-survives-exit.test.js.map +1 -0
- package/dist/__tests__/credential-resolution.test.js +56 -8
- package/dist/__tests__/credential-resolution.test.js.map +1 -1
- package/dist/__tests__/credential-whoami.test.d.ts +8 -0
- package/dist/__tests__/credential-whoami.test.js +93 -0
- package/dist/__tests__/credential-whoami.test.js.map +1 -0
- package/dist/__tests__/device-flow-gate-e2e.test.js +6 -4
- package/dist/__tests__/device-flow-gate-e2e.test.js.map +1 -1
- package/dist/__tests__/doctor.test.js +176 -85
- package/dist/__tests__/doctor.test.js.map +1 -1
- package/dist/__tests__/full-context-view-forwarding.test.d.ts +1 -0
- package/dist/__tests__/full-context-view-forwarding.test.js +80 -0
- package/dist/__tests__/full-context-view-forwarding.test.js.map +1 -0
- package/dist/__tests__/hydration.test.js +56 -1
- package/dist/__tests__/hydration.test.js.map +1 -1
- package/dist/__tests__/install.test.d.ts +1 -0
- package/dist/__tests__/install.test.js +126 -0
- package/dist/__tests__/install.test.js.map +1 -0
- package/dist/__tests__/integration.test.js +1 -0
- package/dist/__tests__/integration.test.js.map +1 -1
- package/dist/__tests__/lost-agent.test.js +51 -0
- package/dist/__tests__/lost-agent.test.js.map +1 -1
- package/dist/__tests__/mcp-tools.test.js +210 -3
- package/dist/__tests__/mcp-tools.test.js.map +1 -1
- package/dist/__tests__/memory-write-failure.test.js +4 -8
- package/dist/__tests__/memory-write-failure.test.js.map +1 -1
- package/dist/__tests__/no-registered-keys-disk-bearer-fallback.test.js +4 -7
- package/dist/__tests__/no-registered-keys-disk-bearer-fallback.test.js.map +1 -1
- package/dist/__tests__/pending-device-flow-e2e.test.js +22 -1
- package/dist/__tests__/pending-device-flow-e2e.test.js.map +1 -1
- package/dist/__tests__/pending-device-flow-resume.test.d.ts +1 -0
- package/dist/__tests__/pending-device-flow-resume.test.js +104 -0
- package/dist/__tests__/pending-device-flow-resume.test.js.map +1 -0
- package/dist/__tests__/prompt-resource.test.js +1 -0
- package/dist/__tests__/prompt-resource.test.js.map +1 -1
- package/dist/__tests__/provisional-register-mcp-remember.test.js +4 -8
- package/dist/__tests__/provisional-register-mcp-remember.test.js.map +1 -1
- package/dist/__tests__/reconnect-survival-e2e.test.js +3 -4
- package/dist/__tests__/reconnect-survival-e2e.test.js.map +1 -1
- package/dist/__tests__/refresh.test.js +24 -2
- package/dist/__tests__/refresh.test.js.map +1 -1
- package/dist/__tests__/request-timeout.test.d.ts +1 -0
- package/dist/__tests__/request-timeout.test.js +40 -0
- package/dist/__tests__/request-timeout.test.js.map +1 -0
- package/dist/__tests__/resolve-agent-preference-e2e.test.js +15 -3
- package/dist/__tests__/resolve-agent-preference-e2e.test.js.map +1 -1
- package/dist/__tests__/retry-after.test.d.ts +4 -0
- package/dist/__tests__/retry-after.test.js +134 -0
- package/dist/__tests__/retry-after.test.js.map +1 -0
- package/dist/__tests__/run-pass-mode.test.d.ts +1 -0
- package/dist/__tests__/run-pass-mode.test.js +130 -0
- package/dist/__tests__/run-pass-mode.test.js.map +1 -0
- package/dist/__tests__/session-identity.test.d.ts +1 -0
- package/dist/__tests__/session-identity.test.js +213 -0
- package/dist/__tests__/session-identity.test.js.map +1 -0
- package/dist/__tests__/version-check.test.js +1 -1
- package/dist/__tests__/workspace-key-mode.test.js +194 -16
- package/dist/__tests__/workspace-key-mode.test.js.map +1 -1
- package/dist/agent-archive.d.ts +36 -0
- package/dist/agent-archive.js +69 -0
- package/dist/agent-archive.js.map +1 -0
- package/dist/agent-key-mode.js +86 -29
- package/dist/agent-key-mode.js.map +1 -1
- package/dist/ais-http-error.d.ts +7 -2
- package/dist/ais-http-error.js +18 -5
- package/dist/ais-http-error.js.map +1 -1
- package/dist/ais-http.d.ts +2 -0
- package/dist/ais-http.js +41 -19
- package/dist/ais-http.js.map +1 -1
- package/dist/auto-handoff.js +33 -1
- package/dist/auto-handoff.js.map +1 -1
- package/dist/cli/doctor.d.ts +5 -2
- package/dist/cli/doctor.js +72 -15
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/install.d.ts +6 -0
- package/dist/cli/install.js +52 -0
- package/dist/cli/install.js.map +1 -0
- package/dist/cli/orientation.d.ts +2 -0
- package/dist/cli/orientation.js +31 -1
- package/dist/cli/orientation.js.map +1 -1
- package/dist/credential-whoami.d.ts +50 -0
- package/dist/credential-whoami.js +91 -0
- package/dist/credential-whoami.js.map +1 -0
- package/dist/hydration.js +8 -0
- package/dist/hydration.js.map +1 -1
- package/dist/index.js +213 -50
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools.d.ts +18 -4
- package/dist/mcp/tools.js +107 -12
- package/dist/mcp/tools.js.map +1 -1
- package/dist/pending-device-flow.d.ts +31 -0
- package/dist/pending-device-flow.js +38 -0
- package/dist/pending-device-flow.js.map +1 -1
- package/dist/refresh.js +5 -1
- package/dist/refresh.js.map +1 -1
- package/dist/request-timeout.d.ts +26 -0
- package/dist/request-timeout.js +49 -0
- package/dist/request-timeout.js.map +1 -0
- package/dist/retry-after.d.ts +26 -0
- package/dist/retry-after.js +42 -0
- package/dist/retry-after.js.map +1 -0
- package/dist/run-pass-mode.d.ts +16 -0
- package/dist/run-pass-mode.js +274 -0
- package/dist/run-pass-mode.js.map +1 -0
- package/dist/session-identity.d.ts +40 -0
- package/dist/session-identity.js +179 -0
- package/dist/session-identity.js.map +1 -0
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.js.map +1 -1
- package/dist/tool-args.d.ts +13 -1
- package/dist/tool-args.js +27 -1
- package/dist/tool-args.js.map +1 -1
- package/dist/types.d.ts +32 -0
- package/dist/workspace-key-mode.js +100 -26
- package/dist/workspace-key-mode.js.map +1 -1
- package/package.json +4 -6
- package/dist/harness-identity.d.ts +0 -17
- package/dist/harness-identity.js +0 -105
- package/dist/harness-identity.js.map +0 -1
package/dist/hydration.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydration.js","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hydration.js","sourceRoot":"","sources":["../src/hydration.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { aisGet, aisPost, DEFAULT_AIS_URL } from './ais-http.js';
|
|
|
18
18
|
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
19
19
|
import { homedir } from 'node:os';
|
|
20
20
|
import { loadOAuthCredentials, loadNonStaleDiskBearer, saveOAuthCredentials, } from './oauth-credentials.js';
|
|
21
|
-
import { clearPendingDeviceFlow, loadPendingDeviceFlow, savePendingDeviceFlow, } from './pending-device-flow.js';
|
|
21
|
+
import { clearPendingDeviceFlow, isResumableDeviceFlow, loadPendingDeviceFlow, savePendingDeviceFlow, } from './pending-device-flow.js';
|
|
22
22
|
import { saveAgentPreference, loadAgentPreference, migrateCredentialsFileIfNeeded } from './agent-preference.js';
|
|
23
23
|
import { createTokenManager, isNoRegisteredKeyFailure } from './token-manager.js';
|
|
24
24
|
import { hydrateIdentity } from './hydration.js';
|
|
@@ -28,16 +28,19 @@ import { ActivityTracker, setupAutoHandoff } from './auto-handoff.js';
|
|
|
28
28
|
import { assertEnvAgentInTenant, selectAgentForSession } from './env-agent.js';
|
|
29
29
|
import { emitTelemetry, hashAgentId } from './telemetry.js';
|
|
30
30
|
import { INBOX_MAX_BODY_CHARS, INBOX_MAX_TASK_ID_CHARS, INBOX_MIN_TTL_SECONDS, INBOX_MAX_TTL_SECONDS, INBOX_DEFAULT_TTL_SECONDS, INBOX_MAX_PAGE_SIZE, INBOX_DEFAULT_PAGE_SIZE, INBOX_MAX_ACK_IDS, GOAL_MAX_TITLE_CHARS, GOAL_MAX_DESCRIPTION_CHARS, GOAL_MAX_CRITERIA_ITEMS, GOAL_DEFAULT_PAGE_SIZE, GOAL_MAX_PAGE_SIZE, } from './tool-args.js';
|
|
31
|
-
import {
|
|
31
|
+
import { identityCredentialFingerprint, loadSessionIdentity, verifyPinnedIdentity } from './session-identity.js';
|
|
32
32
|
import { runPollLoop, DeviceFlowRestartedError } from './device-poll.js';
|
|
33
33
|
import { loadAgentApiKeyConfig, AgentApiKeyConfigError, } from './agent-key-mode.js';
|
|
34
34
|
import { loadWorkspaceKeyConfig, WorkspaceKeyConfigError, } from './workspace-key-mode.js';
|
|
35
|
-
import {
|
|
35
|
+
import { loadRunPassConfig, RunPassConfigError, } from './run-pass-mode.js';
|
|
36
|
+
import { createAgentKeyCallToolHandler, createOAuthCallToolHandler, createRunPassCallToolHandler, createWorkspaceKeyCallToolHandler, } from './mcp/tools.js';
|
|
36
37
|
import { readPackageVersion, startVersionCheck } from './version-check.js';
|
|
38
|
+
import { AGENT_ARCHIVE_DESCRIPTION, AGENT_ARCHIVE_INPUT_SCHEMA, AGENT_DELETE_ALIAS_DESCRIPTION } from './agent-archive.js';
|
|
37
39
|
// ── Config ──────────────────────────────────────────────
|
|
38
40
|
const AIS_URL = process.env['AIS_URL']?.trim() || DEFAULT_AIS_URL;
|
|
39
41
|
const PACKAGE_VERSION = readPackageVersion();
|
|
40
|
-
|
|
42
|
+
const RUN_PASS_CONFLICTING_KEY_ENV_NAMES = ['AIS_WORKSPACE_KEY', 'AIS_API_KEY', 'AIS_AGENT_API_KEY'];
|
|
43
|
+
let sessionIdentity = null;
|
|
41
44
|
// ── Credential persistence ──────────────────────────────
|
|
42
45
|
function loadCredentials() {
|
|
43
46
|
// F5a (CORBOT-48DD2E25): migrate a legacy v1 file to v2 (ids only) BEFORE
|
|
@@ -61,6 +64,16 @@ function loadCredentials() {
|
|
|
61
64
|
function stderr(msg) {
|
|
62
65
|
process.stderr.write(`[aismemory] ${msg}\n`);
|
|
63
66
|
}
|
|
67
|
+
function rejectRunPassWithKeyEnv(env) {
|
|
68
|
+
if (env['AIS_RUN_PASS_FILE'] === undefined)
|
|
69
|
+
return;
|
|
70
|
+
const conflictingKeyName = RUN_PASS_CONFLICTING_KEY_ENV_NAMES.find((keyName) => env[keyName] !== undefined);
|
|
71
|
+
if (conflictingKeyName === undefined)
|
|
72
|
+
return;
|
|
73
|
+
stderr(`Refusing to start: AIS_RUN_PASS_FILE and ${conflictingKeyName} are both set. ` +
|
|
74
|
+
'Run-pass mode must not silently fall back to a long-lived key; unset one variable.');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
64
77
|
function isSafeClaudeSessionId(sessionId) {
|
|
65
78
|
return sessionId !== '.' && sessionId !== '..' && /^[A-Za-z0-9._-]+$/.test(sessionId);
|
|
66
79
|
}
|
|
@@ -131,8 +144,8 @@ async function resolveAgentForLoad(token, query) {
|
|
|
131
144
|
* User can override with AIS_AGENT_ID or AIS_AGENT_NAME env vars.
|
|
132
145
|
*/
|
|
133
146
|
async function resolveAgent(token, fallbackAgentId, fallbackTenantId, preferredAgentId) {
|
|
134
|
-
//
|
|
135
|
-
const envAgentId =
|
|
147
|
+
// This session's own identity overrides shared preferences and must belong to this tenant.
|
|
148
|
+
const envAgentId = sessionIdentity?.agentId;
|
|
136
149
|
if (envAgentId) {
|
|
137
150
|
const agents = await listAllAgents(token);
|
|
138
151
|
try {
|
|
@@ -207,14 +220,79 @@ class BondPendingError extends Error {
|
|
|
207
220
|
`${verificationUri}\n\n` +
|
|
208
221
|
`Send nothing else: not this message, not a code, not a key, not a command, not JSON. ` +
|
|
209
222
|
`Your human opens the link, signs in, and presses Bond. When they say it is done, retry this tool.\n` +
|
|
210
|
-
`The link expires in 30 minutes
|
|
223
|
+
`The link expires in 30 minutes and is only redeemed while this tool is running — if it stops, ` +
|
|
224
|
+
`start it again before your human presses Bond and the same link still works. ` +
|
|
225
|
+
`An agent nobody bonds is archived after 7 days, with its memories.`);
|
|
211
226
|
this.verificationUri = verificationUri;
|
|
212
227
|
this.userCode = userCode;
|
|
213
228
|
this.name = 'BondPendingError';
|
|
214
229
|
}
|
|
215
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Wire the poll loop onto a freshly built flow. Shared by the mint path and
|
|
233
|
+
* the resume path so a change to the poll/dead bookkeeping lands in one place.
|
|
234
|
+
*/
|
|
235
|
+
function attachDeviceFlowPoll(flow, interval) {
|
|
236
|
+
// flowRef points into the live flow object so auto-restart can update the
|
|
237
|
+
// surfaced URL without the caller needing to restart the MCP process.
|
|
238
|
+
const flowRef = flow;
|
|
239
|
+
const poll = pollForToken(flow.device_code, interval, flow.agent_id, flow.provisional_tenant_id, flowRef);
|
|
240
|
+
flow.poll = poll;
|
|
241
|
+
// Mark the flow dead as soon as its poll rejects so the next
|
|
242
|
+
// `ensureCredentials` call starts a fresh flow. The `.catch` here is purely
|
|
243
|
+
// for state-tracking — the original rejection is still observed by callers
|
|
244
|
+
// racing `flow.poll`. We swallow on this branch to suppress unhandled
|
|
245
|
+
// rejection warnings between rejection and the next ensureCredentials call.
|
|
246
|
+
poll.catch(() => {
|
|
247
|
+
flow.dead = true;
|
|
248
|
+
});
|
|
249
|
+
return flow;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Pick up the device code a previous process already minted, instead of
|
|
253
|
+
* asking the server for a new one (CORBOT-7F3EC71C).
|
|
254
|
+
*
|
|
255
|
+
* The bond link is redeemable for its full 30 minutes server-side, but
|
|
256
|
+
* `/v1/oauth/device/authorize` expires every unclaimed claim token for the
|
|
257
|
+
* agent before it issues the next one (OAuthDeviceService `tryReuseProvisional`).
|
|
258
|
+
* So the act of restarting — which an IDE does to its stdio MCP server
|
|
259
|
+
* constantly — used to kill the exact link the agent had just relayed to its
|
|
260
|
+
* human, and the human clicked a page that no longer meant anything.
|
|
261
|
+
* Resuming polls the same code and re-surfaces the same URL, so the link that
|
|
262
|
+
* left the building stays the link that works.
|
|
263
|
+
*/
|
|
264
|
+
function resumeDeviceFlow(saved) {
|
|
265
|
+
emitTelemetry({ event: 'auth.device_flow.resume' });
|
|
266
|
+
const remainingMin = Math.max(1, Math.round((Date.parse(saved.codeExpiresAt) - Date.now()) / 60_000));
|
|
267
|
+
stderr('');
|
|
268
|
+
stderr('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
269
|
+
stderr(' aismemory — still waiting on the bond link already sent');
|
|
270
|
+
stderr('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
271
|
+
stderr(` Bond URL : ${saved.verificationUriComplete}`);
|
|
272
|
+
stderr(` Code : ${saved.userCode}`);
|
|
273
|
+
stderr(` Same link as before — good for about ${remainingMin} more min.`);
|
|
274
|
+
stderr('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
275
|
+
return attachDeviceFlowPoll({
|
|
276
|
+
device_code: saved.deviceCode,
|
|
277
|
+
user_code: saved.userCode,
|
|
278
|
+
verification_uri_complete: saved.verificationUriComplete,
|
|
279
|
+
agent_id: saved.agentId,
|
|
280
|
+
provisional_tenant_id: saved.provisionalTenantId,
|
|
281
|
+
provisional_token: saved.provisionalToken,
|
|
282
|
+
dead: false,
|
|
283
|
+
}, saved.interval ?? DEFAULT_DEVICE_POLL_INTERVAL_S);
|
|
284
|
+
}
|
|
285
|
+
/** Fallback poll interval when a resumed record predates the stored field. */
|
|
286
|
+
const DEFAULT_DEVICE_POLL_INTERVAL_S = 5;
|
|
216
287
|
async function startDeviceFlow(priorProvisionalToken) {
|
|
217
288
|
emitTelemetry({ event: 'auth.device_flow.start' });
|
|
289
|
+
// A caller that passes a prior provisional token has just watched its flow
|
|
290
|
+
// die (expired, denied, timed out), so its code is spent — mint, never
|
|
291
|
+
// resume. Only a process with no flow of its own looks on disk.
|
|
292
|
+
const resumable = priorProvisionalToken ? null : loadPendingDeviceFlow();
|
|
293
|
+
if (isResumableDeviceFlow(resumable)) {
|
|
294
|
+
return resumeDeviceFlow(resumable);
|
|
295
|
+
}
|
|
218
296
|
stderr('Starting authentication...');
|
|
219
297
|
// CORBOT-8F96A8EF: when this process has no in-flight flow of its own,
|
|
220
298
|
// fall back to the provisional token left on disk by an earlier process.
|
|
@@ -246,32 +324,23 @@ async function startDeviceFlow(priorProvisionalToken) {
|
|
|
246
324
|
agentId: agent_id,
|
|
247
325
|
provisionalTenantId: provisional_tenant_id,
|
|
248
326
|
startedAt: new Date().toISOString(),
|
|
327
|
+
deviceCode: device_code,
|
|
328
|
+
userCode: user_code,
|
|
329
|
+
verificationUriComplete: verification_uri_complete,
|
|
330
|
+
interval,
|
|
331
|
+
codeExpiresAt: new Date(Date.now() + authRes.data.expires_in * 1000).toISOString(),
|
|
249
332
|
});
|
|
250
|
-
|
|
333
|
+
return attachDeviceFlowPoll({
|
|
251
334
|
device_code,
|
|
252
335
|
user_code,
|
|
253
336
|
verification_uri_complete,
|
|
254
337
|
agent_id,
|
|
255
338
|
provisional_tenant_id,
|
|
256
339
|
provisional_token,
|
|
257
|
-
// poll is assigned
|
|
258
|
-
//
|
|
340
|
+
// poll is assigned by attachDeviceFlowPoll, which captures the live flow
|
|
341
|
+
// object so pollForToken can update the surfaced URL on auto-restart.
|
|
259
342
|
dead: false,
|
|
260
|
-
};
|
|
261
|
-
// flowRef points into the live flow object so auto-restart can update the
|
|
262
|
-
// surfaced URL without the caller needing to restart the MCP process.
|
|
263
|
-
const flowRef = flow;
|
|
264
|
-
const poll = pollForToken(device_code, interval, agent_id, provisional_tenant_id, flowRef);
|
|
265
|
-
flow.poll = poll;
|
|
266
|
-
// Mark the flow dead as soon as its poll rejects so the next
|
|
267
|
-
// `ensureCredentials` call starts a fresh flow. The `.catch` here is purely
|
|
268
|
-
// for state-tracking — the original rejection is still observed by callers
|
|
269
|
-
// racing `flow.poll`. We swallow on this branch to suppress unhandled
|
|
270
|
-
// rejection warnings between rejection and the next ensureCredentials call.
|
|
271
|
-
poll.catch(() => {
|
|
272
|
-
flow.dead = true;
|
|
273
|
-
});
|
|
274
|
-
return flow;
|
|
343
|
+
}, interval);
|
|
275
344
|
}
|
|
276
345
|
async function pollForToken(device_code, interval, agent_id, provisional_tenant_id,
|
|
277
346
|
/** Mutable ref so auto-restart can update the surfaced URL and provisional token in ActiveDeviceFlow */
|
|
@@ -332,6 +401,11 @@ flowRef) {
|
|
|
332
401
|
agentId: authRes.data.agent_id,
|
|
333
402
|
provisionalTenantId: authRes.data.provisional_tenant_id,
|
|
334
403
|
startedAt: new Date().toISOString(),
|
|
404
|
+
deviceCode: authRes.data.device_code,
|
|
405
|
+
userCode: authRes.data.user_code,
|
|
406
|
+
verificationUriComplete: authRes.data.verification_uri_complete,
|
|
407
|
+
interval: authRes.data.interval,
|
|
408
|
+
codeExpiresAt: new Date(Date.now() + authRes.data.expires_in * 1000).toISOString(),
|
|
335
409
|
});
|
|
336
410
|
stderr('');
|
|
337
411
|
stderr('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
@@ -380,10 +454,10 @@ flowRef) {
|
|
|
380
454
|
// reconnected silently. migrateCredentialsFileIfNeeded() (agent-preference.ts)
|
|
381
455
|
// is what later removes this token from disk, once a key gets enrolled.
|
|
382
456
|
if (hasEnrolledKey()) {
|
|
383
|
-
if (!
|
|
457
|
+
if (!sessionIdentity)
|
|
384
458
|
saveAgentPreference({ agentId: creds.agentId, tenantId: creds.tenantId });
|
|
385
459
|
}
|
|
386
|
-
else if (!
|
|
460
|
+
else if (!sessionIdentity) {
|
|
387
461
|
saveOAuthCredentials(creds);
|
|
388
462
|
}
|
|
389
463
|
// The flow is finished: this agent is bonded and reachable by id from the
|
|
@@ -410,6 +484,7 @@ const tools = [
|
|
|
410
484
|
content: { type: 'string', description: 'The memory content to store' },
|
|
411
485
|
type: { type: 'string', enum: ['fact', 'event', 'lesson', 'context', 'goal', 'task'], description: 'Memory type' },
|
|
412
486
|
importance: { type: 'number', minimum: 0, maximum: 1, description: 'Importance (0-1, default 0.5)' },
|
|
487
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags to filter on later, e.g. ["work", "acme"]. At most 20; each 1-64 characters of a-z 0-9 . _ : - (stored lower-case). Tags are a filter, not a permission — use separate agents to wall off e.g. work from personal.' },
|
|
413
488
|
},
|
|
414
489
|
required: ['content'],
|
|
415
490
|
},
|
|
@@ -417,13 +492,17 @@ const tools = [
|
|
|
417
492
|
{
|
|
418
493
|
name: 'recall',
|
|
419
494
|
description: "Search memories by natural language query. Set principalAgentId to search a DIFFERENT " +
|
|
420
|
-
"agent's memories under a 'memory:read' grant from that agent's owner." +
|
|
495
|
+
"agent's memories under a 'memory:read' grant from that agent's owner. " +
|
|
496
|
+
"Each memory carries createdAt, updatedAt (its last edit), and tags. " +
|
|
497
|
+
"Memories superseded by a newer one are left out; to list or page through every memory, " +
|
|
498
|
+
"use the REST list route described in the aismemory README (cursor paging, sortBy=updatedAt, includeSuperseded=true)." + BOND_LINK_NOTE,
|
|
421
499
|
inputSchema: {
|
|
422
500
|
type: 'object',
|
|
423
501
|
properties: {
|
|
424
502
|
query: { type: 'string', description: 'Search query' },
|
|
425
503
|
limit: { type: 'number', description: 'Max results (default 10)' },
|
|
426
504
|
type: { type: 'string', enum: ['fact', 'event', 'lesson', 'context', 'goal', 'task'], description: 'Filter by type' },
|
|
505
|
+
tags: { type: 'array', items: { type: 'string' }, description: 'Only memories carrying ALL of these tags. A hard filter: results are never widened past it.' },
|
|
427
506
|
principalAgentId: { type: 'string', description: "Optional: search ANOTHER agent's memories instead of your own (its agent UUID). Requires that agent's owner to have granted you the 'memory:read' scope and that you share a tenant; otherwise the call is refused with 403. Read-only — your own identity and write access are unchanged." },
|
|
428
507
|
},
|
|
429
508
|
required: ['query'],
|
|
@@ -431,18 +510,49 @@ const tools = [
|
|
|
431
510
|
},
|
|
432
511
|
{
|
|
433
512
|
name: 'whoami',
|
|
434
|
-
description: 'Get the current agent identity — name, DID, status, description.
|
|
513
|
+
description: 'Get the current agent identity — name, DID, status, description — and, when running on an API key, what that credential can and cannot do: mode, tenant, the sub-resources it may reach, its restrictions and when it expires. `selfDescribed: false` means the server predates that answer and only liveness was confirmed.'
|
|
514
|
+
+ BOND_LINK_NOTE,
|
|
435
515
|
inputSchema: { type: 'object', properties: {} },
|
|
436
516
|
},
|
|
517
|
+
{
|
|
518
|
+
name: 'orientation_check',
|
|
519
|
+
description: 'Run the same expected-environment orientation check "aismemory doctor --orientation" runs from a local file, ' +
|
|
520
|
+
'reachable here for a filesystem-free surface. Pass an owner-approved manifest (host, account, required channels, ' +
|
|
521
|
+
'and the wider tool-server/connector surface this agent expects — names and purposes only, never a secret value) ' +
|
|
522
|
+
'plus this session\'s self-reported evidence. Returns PASS/FAIL per item; read-only, never self-healing, never grants anything.',
|
|
523
|
+
inputSchema: {
|
|
524
|
+
type: 'object',
|
|
525
|
+
properties: {
|
|
526
|
+
version: { type: 'number', enum: [1], description: 'Manifest format version. Always 1.' },
|
|
527
|
+
identity: { type: 'string', description: 'This agent\'s AIS identity (uuid).' },
|
|
528
|
+
requirements: {
|
|
529
|
+
type: 'object',
|
|
530
|
+
description: 'Owner-approved expectations: { host, account, channels: ["ais","mcp","inbox"], environment?: [{name, kind, purpose, required, permission?, dataEnvironment?}] }.',
|
|
531
|
+
},
|
|
532
|
+
current: { type: 'object', description: 'This session\'s observed { host, account, session } marker.' },
|
|
533
|
+
evidence: { type: 'object', description: 'This session\'s self-reported evidence: { session, host, account, credentials, credentialSource, channels: {ais,mcp,inbox}, environment?: {[name]: "pass"|"missing"|"unknown"} }.' },
|
|
534
|
+
relocation: { type: 'object', description: '{ verified, observedAction? } — whether relocation off the expected host/account is a verified, human-confirmed capability.' },
|
|
535
|
+
activationUrl: { type: 'string', description: 'Optional AIS device-flow activation door URL, echoed back as bondUrl.' },
|
|
536
|
+
},
|
|
537
|
+
required: ['version', 'identity', 'requirements', 'current', 'evidence', 'relocation'],
|
|
538
|
+
},
|
|
539
|
+
},
|
|
437
540
|
{
|
|
438
541
|
name: 'full_context',
|
|
439
|
-
description: 'Load the full agent context — identity, personality, goals, facts, skills, and
|
|
440
|
-
"Set principalAgentId to load ANOTHER agent's context under a 'memory:read' grant." +
|
|
542
|
+
description: 'Load the full agent context — identity, personality, goals, facts, skills, last handoff, and the operating layer: the constitution rules that govern this agent and the index of skills it has, under one sha256 version you can compare between two machines. ' +
|
|
543
|
+
"Set principalAgentId to load ANOTHER agent's context under a 'memory:read' grant. " +
|
|
544
|
+
'Every view is bounded: the default view budgets to 6000 tokens, and anything the ceiling cut is ' +
|
|
545
|
+
"named in the response's receipt.omitted rather than dropped silently — goal titles are cut to 200 " +
|
|
546
|
+
"characters with the goal's memory id in the payload, so the whole body is one memory-by-id fetch away. " +
|
|
547
|
+
"Pass view='lean' for continuity only (last handoff first, recent memories) or view='bootstrap' " +
|
|
548
|
+
'for a cross-harness session start (adds goals and inbox). maxTokens sets the ceiling on any view.' + BOND_LINK_NOTE,
|
|
441
549
|
inputSchema: {
|
|
442
550
|
type: 'object',
|
|
443
551
|
properties: {
|
|
444
552
|
principalAgentId: { type: 'string', description: "Optional: search ANOTHER agent's memories instead of your own (its agent UUID). Requires that agent's owner to have granted you the 'memory:read' scope and that you share a tenant; otherwise the call is refused with 403. Read-only — your own identity and write access are unchanged." },
|
|
445
|
-
memoriesLimit: { type: 'number', description: 'Number of memories to load (1-50, default 5). Lower values reduce context size.' },
|
|
553
|
+
memoriesLimit: { type: 'number', description: 'Number of memories to load (1-50, default 5). Lower values reduce context size. Governs recent memories only — on the full view that is a small share of the payload, so prefer view to bound the whole response.' },
|
|
554
|
+
view: { type: 'string', enum: ['full', 'lean', 'bootstrap'], description: "Which projection to load. 'full' (default) is everything the stores hold, bounded to 6000 tokens. 'lean' is continuity only — last handoff first, then recent memories. 'bootstrap' is lean plus goals and inbox, for starting a session in another harness. All three honour maxTokens." },
|
|
555
|
+
maxTokens: { type: 'number', description: 'Token ceiling (500-20000; default 6000 for full, 2000 for lean, 3000 for bootstrap). Content is evicted from the tail until the payload fits — assembled context sections, then facts, then goals, then recent memories on the full view — and each eviction is named in receipt.omitted. Identity, the operating layer and the last handoff are never dropped.' },
|
|
446
556
|
},
|
|
447
557
|
},
|
|
448
558
|
},
|
|
@@ -654,6 +764,18 @@ const tools = [
|
|
|
654
764
|
required: ['goalId', 'success'],
|
|
655
765
|
},
|
|
656
766
|
},
|
|
767
|
+
// Owner-only lifecycle (CORBOT-BA5AADB0): archive, plus the agent_delete
|
|
768
|
+
// alias, which archives too. Key modes refuse both (agent-key-mode.ts).
|
|
769
|
+
{
|
|
770
|
+
name: 'agent_archive',
|
|
771
|
+
description: AGENT_ARCHIVE_DESCRIPTION,
|
|
772
|
+
inputSchema: AGENT_ARCHIVE_INPUT_SCHEMA,
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
name: 'agent_delete',
|
|
776
|
+
description: AGENT_DELETE_ALIAS_DESCRIPTION,
|
|
777
|
+
inputSchema: AGENT_ARCHIVE_INPUT_SCHEMA,
|
|
778
|
+
},
|
|
657
779
|
];
|
|
658
780
|
// ── MCP Server ──────────────────────────────────────────
|
|
659
781
|
/**
|
|
@@ -693,7 +815,8 @@ async function runWorkspaceKeyModeServer(config) {
|
|
|
693
815
|
server.setRequestHandler(CallToolRequestSchema, createWorkspaceKeyCallToolHandler({
|
|
694
816
|
aisUrl: AIS_URL,
|
|
695
817
|
activityTracker,
|
|
696
|
-
|
|
818
|
+
getSessionIdentity: () => sessionIdentity,
|
|
819
|
+
setSessionIdentity: (next) => { sessionIdentity = next; },
|
|
697
820
|
getActiveConfig: () => activeConfig,
|
|
698
821
|
setActiveConfig: (next) => { activeConfig = next; },
|
|
699
822
|
stderr,
|
|
@@ -702,6 +825,30 @@ async function runWorkspaceKeyModeServer(config) {
|
|
|
702
825
|
await server.connect(transport);
|
|
703
826
|
stderr(`MCP server running (workspace key mode). Tools: ${tools.map((t) => t.name).join(', ')}`);
|
|
704
827
|
}
|
|
828
|
+
/**
|
|
829
|
+
* MCP server entrypoint for run-pass auth mode (CORBOT-662A4B3A).
|
|
830
|
+
*
|
|
831
|
+
* The pass itself is never stored in process state: each tool request re-reads
|
|
832
|
+
* AIS_RUN_PASS_FILE and sends the current content as x-run-pass only.
|
|
833
|
+
*/
|
|
834
|
+
async function runPassModeServer(config) {
|
|
835
|
+
stderr(`Run-pass auth mode — agent fixed to ${config.agentId}; pass file ${config.runPassFile}`);
|
|
836
|
+
const server = new Server({ name: 'aismemory', version: PACKAGE_VERSION }, { capabilities: { tools: {}, prompts: {} } });
|
|
837
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
|
|
838
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
|
839
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
840
|
+
throw new Error(`Unknown prompt: ${request.params.name}`);
|
|
841
|
+
});
|
|
842
|
+
server.setRequestHandler(CallToolRequestSchema, createRunPassCallToolHandler({
|
|
843
|
+
aisUrl: AIS_URL,
|
|
844
|
+
activityTracker,
|
|
845
|
+
config,
|
|
846
|
+
getSessionIdentity: () => sessionIdentity,
|
|
847
|
+
}));
|
|
848
|
+
const transport = new StdioServerTransport();
|
|
849
|
+
await server.connect(transport);
|
|
850
|
+
stderr(`MCP server running (run-pass mode). Tools: ${tools.map((t) => t.name).join(', ')}`);
|
|
851
|
+
}
|
|
705
852
|
/**
|
|
706
853
|
* MCP server entrypoint for agent-API-key auth mode (CORBOT-B1D3244C).
|
|
707
854
|
*
|
|
@@ -725,7 +872,7 @@ async function runAgentKeyModeServer(config) {
|
|
|
725
872
|
aisUrl: AIS_URL,
|
|
726
873
|
activityTracker,
|
|
727
874
|
config,
|
|
728
|
-
|
|
875
|
+
getSessionIdentity: () => sessionIdentity,
|
|
729
876
|
}));
|
|
730
877
|
const transport = new StdioServerTransport();
|
|
731
878
|
await server.connect(transport);
|
|
@@ -733,6 +880,10 @@ async function runAgentKeyModeServer(config) {
|
|
|
733
880
|
}
|
|
734
881
|
async function main() {
|
|
735
882
|
// Subcommand dispatch — CLI entrypoints exit before MCP server startup.
|
|
883
|
+
if (process.argv[2] === 'install') {
|
|
884
|
+
(await import('./cli/install.js')).main(process.argv.slice(3));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
736
887
|
if (process.argv[2] === 'enable-key-auth') {
|
|
737
888
|
await import('./cli/enable-key-auth.js');
|
|
738
889
|
return; // CLI calls process.exit() itself
|
|
@@ -749,23 +900,17 @@ async function main() {
|
|
|
749
900
|
await (await import('./cli/pull-memory.js')).main(process.argv.slice(3));
|
|
750
901
|
return;
|
|
751
902
|
}
|
|
752
|
-
if (process.argv[2] === 'install') {
|
|
753
|
-
const source = optionValue(process.argv.slice(3), '--manifest');
|
|
754
|
-
if (!source)
|
|
755
|
-
throw new Error('Usage: aismemory install --manifest <file>');
|
|
756
|
-
const pin = installHarnessIdentity(source);
|
|
757
|
-
process.stdout.write(`Installed identity pin for ${pin.harness}: ${pin.agentId}. Launch: aismemory --harness ${pin.harness}\nVerify: aismemory doctor --harness ${pin.harness}\n`);
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
903
|
// CORBOT-7B8A1D16: ask the npm registry, at most about once a day, whether a
|
|
761
904
|
// newer aismemory has been published. Deliberately not awaited — startup must
|
|
762
905
|
// not wait on a third-party registry, and the answer is only needed later,
|
|
763
906
|
// when whoami or full_context is called. Resolves to a skip when offline.
|
|
764
907
|
void startVersionCheck({ currentVersion: PACKAGE_VERSION, onUpdateAvailable: stderr });
|
|
765
|
-
|
|
766
|
-
const adapterEnv = { ...process.env, AIS_AGENT_ID:
|
|
767
|
-
|
|
768
|
-
//
|
|
908
|
+
sessionIdentity = loadSessionIdentity();
|
|
909
|
+
const adapterEnv = { ...process.env, AIS_AGENT_ID: sessionIdentity?.agentId };
|
|
910
|
+
rejectRunPassWithKeyEnv(adapterEnv);
|
|
911
|
+
// Explicit key auth modes keep their existing order when AIS_RUN_PASS_FILE is
|
|
912
|
+
// unset: workspace key -> agent API key -> device flow. A run pass combined
|
|
913
|
+
// with any key env var is refused above.
|
|
769
914
|
let workspaceKeyConfig;
|
|
770
915
|
try {
|
|
771
916
|
workspaceKeyConfig = loadWorkspaceKeyConfig(adapterEnv);
|
|
@@ -800,6 +945,23 @@ async function main() {
|
|
|
800
945
|
await runAgentKeyModeServer(agentKeyConfig);
|
|
801
946
|
return;
|
|
802
947
|
}
|
|
948
|
+
// Run-pass auth mode (CORBOT-662A4B3A) — short-lived per-run credential
|
|
949
|
+
// written by the launcher. It must not read or require any key env var.
|
|
950
|
+
let runPassConfig;
|
|
951
|
+
try {
|
|
952
|
+
runPassConfig = loadRunPassConfig(adapterEnv);
|
|
953
|
+
}
|
|
954
|
+
catch (err) {
|
|
955
|
+
if (err instanceof RunPassConfigError) {
|
|
956
|
+
stderr(err.message);
|
|
957
|
+
process.exit(1);
|
|
958
|
+
}
|
|
959
|
+
throw err;
|
|
960
|
+
}
|
|
961
|
+
if (runPassConfig) {
|
|
962
|
+
await runPassModeServer(runPassConfig);
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
803
965
|
// Lazy-auth state. We connect the MCP transport before the user has approved
|
|
804
966
|
// the device flow so Claude Code's 30s connection timeout is not blocked on
|
|
805
967
|
// human approval. The first tool call surfaces the bond URL via its result.
|
|
@@ -855,7 +1017,7 @@ async function main() {
|
|
|
855
1017
|
const claims = decodeJwtClaims(token);
|
|
856
1018
|
const tenantId = claims.tenantId ?? '';
|
|
857
1019
|
const resolved = await resolveAgent(token, '', tenantId, loadAgentPreference()?.agentId);
|
|
858
|
-
if (!
|
|
1020
|
+
if (!sessionIdentity)
|
|
859
1021
|
saveAgentPreference({ agentId: resolved.agentId, tenantId: resolved.tenantId || tenantId });
|
|
860
1022
|
return { agentId: resolved.agentId, tenantId: resolved.tenantId || tenantId };
|
|
861
1023
|
},
|
|
@@ -979,7 +1141,7 @@ async function main() {
|
|
|
979
1141
|
}
|
|
980
1142
|
/** Identity hydration, prompt refresh, and auto-handoff — all post-auth. */
|
|
981
1143
|
async function runPostAuth(credentials) {
|
|
982
|
-
const c = { ...credentials, agentId:
|
|
1144
|
+
const c = { ...credentials, agentId: sessionIdentity?.agentId ?? credentials.agentId };
|
|
983
1145
|
// Auto-enroll a DID key so re-auth stays silent after the 7-day bond
|
|
984
1146
|
// token expires. Without this, key enrollment was a manual opt-in step
|
|
985
1147
|
// (`enable-key-auth`) and every normal user was re-prompted to bond at
|
|
@@ -1007,7 +1169,7 @@ async function main() {
|
|
|
1007
1169
|
setupAutoHandoff(AIS_URL, c.agentId, c.token, activityTracker, () => {
|
|
1008
1170
|
stopRefreshTimer();
|
|
1009
1171
|
stderr('Session handoff saved');
|
|
1010
|
-
}, () => verifyPinnedIdentity(
|
|
1172
|
+
}, () => verifyPinnedIdentity(sessionIdentity, c.agentId, () => aisGet(`/v1/agents/${c.agentId}`, c.token), { credentialKey: identityCredentialFingerprint('oauth-bearer', c.token) }));
|
|
1011
1173
|
}
|
|
1012
1174
|
catch (e) {
|
|
1013
1175
|
stderr(`Post-auth setup failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -1035,7 +1197,8 @@ async function main() {
|
|
|
1035
1197
|
aisUrl: AIS_URL,
|
|
1036
1198
|
server,
|
|
1037
1199
|
activityTracker,
|
|
1038
|
-
|
|
1200
|
+
getSessionIdentity: () => sessionIdentity,
|
|
1201
|
+
setSessionIdentity: (next) => { sessionIdentity = next; },
|
|
1039
1202
|
getCredentials: () => creds,
|
|
1040
1203
|
setCredentials: (next) => { creds = next; },
|
|
1041
1204
|
setSessionAgentOverride: (next) => { sessionAgentOverride = next; },
|