myagentmemory 0.4.16 → 0.4.17
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/LICENSE +1 -0
- package/README.md +17 -4
- package/dist/cli-spec.d.ts +1 -1
- package/dist/cli-spec.js +14 -2
- package/dist/cli.js +107 -42
- package/dist/plugin-bootstrap.js +2 -2
- package/dist/plugin-host.d.ts +18 -0
- package/dist/plugin-runtime.d.ts +8 -1
- package/dist/plugin-runtime.js +39 -0
- package/dist/plugin-service.js +36 -39
- package/docs/official-plugin-bootstrap.md +29 -21
- package/package.json +1 -1
- package/src/cli-spec.ts +14 -2
- package/src/cli.ts +112 -51
- package/src/plugin-bootstrap.ts +2 -2
- package/src/plugin-host.ts +20 -0
- package/src/plugin-runtime.ts +64 -0
- package/src/plugin-service.ts +39 -40
package/src/plugin-host.ts
CHANGED
|
@@ -131,11 +131,31 @@ export interface PluginStructuredErrorV1 {
|
|
|
131
131
|
retryable?: boolean;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
export interface PluginContextSectionV1 {
|
|
135
|
+
id: string;
|
|
136
|
+
label: string;
|
|
137
|
+
content: string;
|
|
138
|
+
artifactPath?: string;
|
|
139
|
+
metadata?: Record<string, unknown>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface PluginContextProviderV1 {
|
|
143
|
+
name: string;
|
|
144
|
+
requiredCapability: string;
|
|
145
|
+
provide(context: {
|
|
146
|
+
host: string;
|
|
147
|
+
cwd?: string;
|
|
148
|
+
query?: string;
|
|
149
|
+
signal: AbortSignal;
|
|
150
|
+
}): Promise<PluginContextSectionV1[]>;
|
|
151
|
+
}
|
|
152
|
+
|
|
134
153
|
export interface AgentMemoryPluginHostV1 {
|
|
135
154
|
apiVersion: 1;
|
|
136
155
|
coreVersion: string;
|
|
137
156
|
registerCommand(command: PluginCommandV1): void;
|
|
138
157
|
registerSessionStartHook(hook: PluginSessionStartHookV1): void;
|
|
158
|
+
registerContextProvider?(provider: PluginContextProviderV1): void;
|
|
139
159
|
getStateDirectory(): string;
|
|
140
160
|
getMemoryDirectory(): string;
|
|
141
161
|
getEntitlement(): Promise<PluginEntitlementStatusV1>;
|
package/src/plugin-runtime.ts
CHANGED
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
type PluginCommandContextV1,
|
|
24
24
|
type PluginCommandResultV1,
|
|
25
25
|
type PluginCommandV1,
|
|
26
|
+
type PluginContextProviderV1,
|
|
27
|
+
type PluginContextSectionV1,
|
|
26
28
|
type PluginEntitlementStatusV1,
|
|
27
29
|
type PluginMemoryCorrectionV1,
|
|
28
30
|
type PluginMemoryWriteV1,
|
|
@@ -38,6 +40,11 @@ interface RegisteredCommand {
|
|
|
38
40
|
pluginId: string;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
interface RegisteredContextProvider {
|
|
44
|
+
provider: PluginContextProviderV1;
|
|
45
|
+
pluginId: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
41
48
|
export interface PluginRuntimeOptionsV1 {
|
|
42
49
|
coreVersion: string;
|
|
43
50
|
store?: PluginInstallStoreV1;
|
|
@@ -130,6 +137,7 @@ export class InstalledPluginRuntimeV1 {
|
|
|
130
137
|
private readonly backend: PluginBootstrapBackendV1;
|
|
131
138
|
private readonly commands = new Map<string, RegisteredCommand>();
|
|
132
139
|
private readonly hooks: PluginSessionStartHookV1[] = [];
|
|
140
|
+
private readonly contextProviders: RegisteredContextProvider[] = [];
|
|
133
141
|
private loaded = false;
|
|
134
142
|
|
|
135
143
|
constructor(private readonly options: PluginRuntimeOptionsV1) {
|
|
@@ -202,6 +210,49 @@ export class InstalledPluginRuntimeV1 {
|
|
|
202
210
|
}
|
|
203
211
|
}
|
|
204
212
|
|
|
213
|
+
async provideContext(context: {
|
|
214
|
+
host: string;
|
|
215
|
+
cwd?: string;
|
|
216
|
+
query?: string;
|
|
217
|
+
signal: AbortSignal;
|
|
218
|
+
}): Promise<PluginContextSectionV1[]> {
|
|
219
|
+
if (!(await this.load()) || this.contextProviders.length === 0) return [];
|
|
220
|
+
const entitlement = await this.refreshEntitlement();
|
|
221
|
+
const sections: PluginContextSectionV1[] = [];
|
|
222
|
+
for (const registered of this.contextProviders) {
|
|
223
|
+
if (!isPluginCapabilityEnabled(entitlement, registered.provider.requiredCapability)) continue;
|
|
224
|
+
const provided = await registered.provider.provide(context);
|
|
225
|
+
if (!Array.isArray(provided) || provided.length > 16)
|
|
226
|
+
throw new PluginBootstrapFailure(
|
|
227
|
+
"plugin_context_invalid",
|
|
228
|
+
`Plugin ${registered.pluginId} returned invalid context sections`,
|
|
229
|
+
);
|
|
230
|
+
for (const section of provided) {
|
|
231
|
+
if (
|
|
232
|
+
!section ||
|
|
233
|
+
typeof section.id !== "string" ||
|
|
234
|
+
section.id.length === 0 ||
|
|
235
|
+
section.id.length > 256 ||
|
|
236
|
+
typeof section.label !== "string" ||
|
|
237
|
+
section.label.length === 0 ||
|
|
238
|
+
section.label.length > 256 ||
|
|
239
|
+
typeof section.content !== "string" ||
|
|
240
|
+
Buffer.byteLength(section.content, "utf-8") > 64 * 1024 ||
|
|
241
|
+
(section.artifactPath !== undefined &&
|
|
242
|
+
(typeof section.artifactPath !== "string" || section.artifactPath.length > 4_096)) ||
|
|
243
|
+
(section.metadata !== undefined &&
|
|
244
|
+
(!section.metadata || typeof section.metadata !== "object" || Array.isArray(section.metadata)))
|
|
245
|
+
)
|
|
246
|
+
throw new PluginBootstrapFailure(
|
|
247
|
+
"plugin_context_invalid",
|
|
248
|
+
`Plugin ${registered.pluginId} returned an invalid context section`,
|
|
249
|
+
);
|
|
250
|
+
sections.push(structuredClone(section));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return sections;
|
|
254
|
+
}
|
|
255
|
+
|
|
205
256
|
private createHost(manifest: AgentMemoryPluginManifestV1): AgentMemoryPluginHostV1 {
|
|
206
257
|
const descriptors = new Map(manifest.commands.map((command) => [command.name, command]));
|
|
207
258
|
const stateRoot = path.join(this.store.root, "state");
|
|
@@ -242,6 +293,19 @@ export class InstalledPluginRuntimeV1 {
|
|
|
242
293
|
);
|
|
243
294
|
this.hooks.push(hook);
|
|
244
295
|
},
|
|
296
|
+
registerContextProvider: (provider) => {
|
|
297
|
+
if (!(manifest.capabilities ?? []).includes(provider.requiredCapability))
|
|
298
|
+
throw new PluginBootstrapFailure(
|
|
299
|
+
"plugin_context_invalid",
|
|
300
|
+
`Plugin ${manifest.id} registered a context provider with an undeclared capability`,
|
|
301
|
+
);
|
|
302
|
+
if (!provider.name || this.contextProviders.some((item) => item.provider.name === provider.name))
|
|
303
|
+
throw new PluginBootstrapFailure(
|
|
304
|
+
"plugin_context_invalid",
|
|
305
|
+
`Plugin context provider ${provider.name || "(unnamed)"} is invalid or already registered`,
|
|
306
|
+
);
|
|
307
|
+
this.contextProviders.push({ provider, pluginId: manifest.id });
|
|
308
|
+
},
|
|
245
309
|
getStateDirectory: () => stateDirectory,
|
|
246
310
|
getMemoryDirectory: () => {
|
|
247
311
|
assertPermission(manifest, "memory:read");
|
package/src/plugin-service.ts
CHANGED
|
@@ -29,12 +29,12 @@ const MISSING_ENTITLEMENT: PluginEntitlementStatusV1 = {
|
|
|
29
29
|
state: "missing",
|
|
30
30
|
features: [],
|
|
31
31
|
capabilities: {},
|
|
32
|
-
reason: "
|
|
32
|
+
reason: "Install the no-account Pro preview to activate local recall and learning",
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
interface TemporaryActivationV1 {
|
|
36
|
-
schemaVersion:
|
|
37
|
-
|
|
36
|
+
schemaVersion: 3;
|
|
37
|
+
installationId: string;
|
|
38
38
|
activatedAt: string;
|
|
39
39
|
usageCredential: string;
|
|
40
40
|
dailySessionLimit: number;
|
|
@@ -54,24 +54,23 @@ function cloneEntitlement(value: PluginEntitlementStatusV1): PluginEntitlementSt
|
|
|
54
54
|
return structuredClone(value);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
function freeEntitlement(
|
|
57
|
+
function freeEntitlement(): PluginEntitlementStatusV1 {
|
|
58
58
|
return {
|
|
59
59
|
plan: "free",
|
|
60
60
|
state: "active",
|
|
61
61
|
features: ["session-intelligence", "web-console"],
|
|
62
62
|
capabilities: {
|
|
63
63
|
"session-index": { enabled: true },
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
learning: { enabled: true },
|
|
64
|
+
recall: { enabled: true, quota: { limit: 10, window: "day", scope: "device" } },
|
|
65
|
+
"session-worker": { enabled: false },
|
|
66
|
+
learning: { enabled: true, quota: { limit: 1, window: "day", scope: "device" } },
|
|
69
67
|
"retrieval-evaluation": { enabled: true },
|
|
70
68
|
"operational-metrics": { enabled: true },
|
|
71
69
|
"web-console": { enabled: true },
|
|
72
70
|
"memory-explorer": { enabled: true },
|
|
73
71
|
},
|
|
74
|
-
reason:
|
|
72
|
+
reason:
|
|
73
|
+
"Free preview: 10 recalls and 1 learning scan per local day; indexing and dashboard access remain available",
|
|
75
74
|
};
|
|
76
75
|
}
|
|
77
76
|
|
|
@@ -388,12 +387,12 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
388
387
|
this.artifactOrigin = options.artifactOrigin ?? ARTIFACT_ORIGIN;
|
|
389
388
|
this.fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
390
389
|
this.openUrl = options.openUrl ?? openLoopbackUrl;
|
|
391
|
-
this.activate = options.activate ?? (() =>
|
|
390
|
+
this.activate = options.activate ?? (async () => `am_install_${randomBytes(24).toString("base64url")}`);
|
|
392
391
|
}
|
|
393
392
|
|
|
394
393
|
async getLocalEntitlement(): Promise<PluginEntitlementStatusV1> {
|
|
395
394
|
const activation = this.readActivation();
|
|
396
|
-
return activation ? freeEntitlement(
|
|
395
|
+
return activation ? freeEntitlement() : cloneEntitlement(MISSING_ENTITLEMENT);
|
|
397
396
|
}
|
|
398
397
|
|
|
399
398
|
async resolveAccess(request: {
|
|
@@ -403,33 +402,19 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
403
402
|
allowAuthentication: boolean;
|
|
404
403
|
}): Promise<PluginAccessDecisionV1> {
|
|
405
404
|
const activation = this.readActivation();
|
|
406
|
-
|
|
407
|
-
if (!email) {
|
|
408
|
-
if (!request.allowAuthentication)
|
|
409
|
-
return {
|
|
410
|
-
kind: "auth_required",
|
|
411
|
-
entitlement: cloneEntitlement(MISSING_ENTITLEMENT),
|
|
412
|
-
nextAction: {
|
|
413
|
-
kind: "authenticate",
|
|
414
|
-
url: "https://jayzeng.github.io/agentmemory/",
|
|
415
|
-
message: "Run plugin install in an interactive terminal to enter an email address",
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
email = await this.activate();
|
|
419
|
-
}
|
|
405
|
+
const installationId = activation?.installationId ?? (await this.activate());
|
|
420
406
|
const response = await this.request(`${this.apiOrigin}/v1/plugin/access`, {
|
|
421
407
|
method: "POST",
|
|
422
408
|
headers: { "Content-Type": "application/json" },
|
|
423
409
|
body: JSON.stringify({
|
|
424
|
-
schemaVersion:
|
|
425
|
-
|
|
410
|
+
schemaVersion: 2,
|
|
411
|
+
installationId,
|
|
426
412
|
bundleId: request.bundleId,
|
|
427
413
|
installedVersion: request.installedVersion ?? null,
|
|
428
414
|
coreVersion: this.coreVersion,
|
|
429
415
|
channel: request.channel,
|
|
430
416
|
platform: process.platform,
|
|
431
417
|
architecture: process.arch,
|
|
432
|
-
consentVersion: "activation-v2",
|
|
433
418
|
}),
|
|
434
419
|
});
|
|
435
420
|
const value = (await readJson(response)) as {
|
|
@@ -445,16 +430,22 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
445
430
|
"service_response_invalid",
|
|
446
431
|
"The access response omitted its usage credential",
|
|
447
432
|
);
|
|
448
|
-
const
|
|
433
|
+
const recallQuota = value.entitlement.capabilities.recall?.quota;
|
|
434
|
+
const learningQuota = value.entitlement.capabilities.learning?.quota;
|
|
449
435
|
if (
|
|
450
436
|
value.entitlement.plan !== "free" ||
|
|
451
437
|
value.entitlement.state !== "active" ||
|
|
452
|
-
!
|
|
453
|
-
|
|
454
|
-
|
|
438
|
+
!recallQuota ||
|
|
439
|
+
recallQuota.scope !== "device" ||
|
|
440
|
+
recallQuota.window !== "day" ||
|
|
441
|
+
!learningQuota ||
|
|
442
|
+
learningQuota.scope !== "device" ||
|
|
443
|
+
learningQuota.window !== "day" ||
|
|
444
|
+
value.entitlement.capabilities["session-index"]?.enabled !== true ||
|
|
445
|
+
value.entitlement.capabilities["web-console"]?.enabled !== true
|
|
455
446
|
)
|
|
456
|
-
throw new PluginBootstrapFailure("service_response_invalid", "The free
|
|
457
|
-
this.writeActivation(
|
|
447
|
+
throw new PluginBootstrapFailure("service_response_invalid", "The free preview policy is invalid");
|
|
448
|
+
this.writeActivation(installationId, value.usageCredential, 1);
|
|
458
449
|
return { kind: "granted", entitlement: value.entitlement, artifactGrant: value.artifactGrant };
|
|
459
450
|
}
|
|
460
451
|
|
|
@@ -523,8 +514,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
523
514
|
return null;
|
|
524
515
|
const value = JSON.parse(fs.readFileSync(activationPath, "utf-8")) as TemporaryActivationV1;
|
|
525
516
|
if (
|
|
526
|
-
value.schemaVersion !==
|
|
527
|
-
|
|
517
|
+
value.schemaVersion !== 3 ||
|
|
518
|
+
typeof value.installationId !== "string" ||
|
|
519
|
+
!/^am_install_[A-Za-z0-9_-]{32}$/.test(value.installationId) ||
|
|
528
520
|
!Number.isFinite(Date.parse(value.activatedAt)) ||
|
|
529
521
|
!ACTIVATION_CREDENTIAL.test(value.usageCredential) ||
|
|
530
522
|
!Number.isSafeInteger(value.dailySessionLimit) ||
|
|
@@ -538,8 +530,9 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
538
530
|
}
|
|
539
531
|
}
|
|
540
532
|
|
|
541
|
-
private writeActivation(
|
|
542
|
-
if (
|
|
533
|
+
private writeActivation(installationId: string, usageCredential: string, dailySessionLimit: number): void {
|
|
534
|
+
if (!/^am_install_[A-Za-z0-9_-]{32}$/.test(installationId))
|
|
535
|
+
throw new PluginBootstrapFailure("activation_failed", "The installation identifier is invalid");
|
|
543
536
|
if (!ACTIVATION_CREDENTIAL.test(usageCredential))
|
|
544
537
|
throw new PluginBootstrapFailure("activation_failed", "The activation credential is invalid");
|
|
545
538
|
if (!Number.isSafeInteger(dailySessionLimit) || dailySessionLimit <= 0 || dailySessionLimit > 10_000)
|
|
@@ -558,7 +551,13 @@ export class TemporaryPluginBackend implements PluginBootstrapBackendV1 {
|
|
|
558
551
|
fs.writeFileSync(
|
|
559
552
|
temporary,
|
|
560
553
|
`${JSON.stringify(
|
|
561
|
-
{
|
|
554
|
+
{
|
|
555
|
+
schemaVersion: 3,
|
|
556
|
+
installationId,
|
|
557
|
+
activatedAt: new Date().toISOString(),
|
|
558
|
+
usageCredential,
|
|
559
|
+
dailySessionLimit,
|
|
560
|
+
},
|
|
562
561
|
null,
|
|
563
562
|
2,
|
|
564
563
|
)}\n`,
|