@keyoku/memory 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +74 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +113 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +18 -0
- package/src/client.ts +174 -0
- package/src/index.ts +2 -0
- package/test/client.test.ts +233 -0
- package/tsconfig.json +1 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed HTTP client for the Keyoku memory engine API
|
|
3
|
+
*/
|
|
4
|
+
import type { Memory, SearchResult, RememberResult, HeartbeatResult, HeartbeatContextResult, MemoryStats } from '@keyoku/types';
|
|
5
|
+
export { type Memory, type SearchResult, type RememberResult, type HeartbeatResult, type HeartbeatContextResult, type MemoryStats } from '@keyoku/types';
|
|
6
|
+
export declare class KeyokuError extends Error {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly path: string;
|
|
9
|
+
constructor(status: number, message: string, path: string);
|
|
10
|
+
}
|
|
11
|
+
export declare class KeyokuClient {
|
|
12
|
+
private baseUrl;
|
|
13
|
+
private timeout;
|
|
14
|
+
constructor(options: {
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
});
|
|
18
|
+
private request;
|
|
19
|
+
remember(entityId: string, content: string, options?: {
|
|
20
|
+
session_id?: string;
|
|
21
|
+
agent_id?: string;
|
|
22
|
+
source?: string;
|
|
23
|
+
team_id?: string;
|
|
24
|
+
visibility?: string;
|
|
25
|
+
}): Promise<RememberResult>;
|
|
26
|
+
search(entityId: string, query: string, options?: {
|
|
27
|
+
limit?: number;
|
|
28
|
+
mode?: string;
|
|
29
|
+
agent_id?: string;
|
|
30
|
+
team_aware?: boolean;
|
|
31
|
+
min_score?: number;
|
|
32
|
+
}): Promise<SearchResult[]>;
|
|
33
|
+
listMemories(entityId: string, limit?: number): Promise<Memory[]>;
|
|
34
|
+
getMemory(id: string): Promise<Memory>;
|
|
35
|
+
deleteMemory(id: string): Promise<{
|
|
36
|
+
status: string;
|
|
37
|
+
}>;
|
|
38
|
+
deleteAllMemories(entityId: string): Promise<{
|
|
39
|
+
status: string;
|
|
40
|
+
}>;
|
|
41
|
+
getStats(entityId: string): Promise<MemoryStats>;
|
|
42
|
+
heartbeatCheck(entityId: string, options?: {
|
|
43
|
+
deadline_window?: string;
|
|
44
|
+
decay_threshold?: number;
|
|
45
|
+
importance_floor?: number;
|
|
46
|
+
max_results?: number;
|
|
47
|
+
agent_id?: string;
|
|
48
|
+
team_id?: string;
|
|
49
|
+
}): Promise<HeartbeatResult>;
|
|
50
|
+
/** Combined heartbeat + context search in a single call, with optional LLM analysis. */
|
|
51
|
+
heartbeatContext(entityId: string, options?: {
|
|
52
|
+
query?: string;
|
|
53
|
+
top_k?: number;
|
|
54
|
+
min_score?: number;
|
|
55
|
+
deadline_window?: string;
|
|
56
|
+
max_results?: number;
|
|
57
|
+
agent_id?: string;
|
|
58
|
+
team_id?: string;
|
|
59
|
+
analyze?: boolean;
|
|
60
|
+
activity_summary?: string;
|
|
61
|
+
autonomy?: 'observe' | 'suggest' | 'act';
|
|
62
|
+
}): Promise<HeartbeatContextResult>;
|
|
63
|
+
createSchedule(entityId: string, agentId: string, content: string, cronTag: string): Promise<Memory>;
|
|
64
|
+
listSchedules(entityId: string, agentId?: string): Promise<Memory[]>;
|
|
65
|
+
ackSchedule(memoryId: string): Promise<{
|
|
66
|
+
status: string;
|
|
67
|
+
memory_id: string;
|
|
68
|
+
}>;
|
|
69
|
+
cancelSchedule(id: string): Promise<{
|
|
70
|
+
status: string;
|
|
71
|
+
memory_id: string;
|
|
72
|
+
}>;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,WAAW,EACZ,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,KAAK,sBAAsB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEzJ,qBAAa,WAAY,SAAQ,KAAK;aAElB,MAAM,EAAE,MAAM;aAEd,IAAI,EAAE,MAAM;gBAFZ,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM;CAK/B;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;YAK7C,OAAO;IA6Bf,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC1D,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,OAAO,CAAC,cAAc,CAAC;IAQrB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACtD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAQrB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI9D,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAItC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAIrD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAIhE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAMhD,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,eAAe,CAAC;IAO5B,wFAAwF;IAClF,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACjD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC;KAC1C,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAS7B,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IASpG,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMpE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAM7E,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;CAGjF"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed HTTP client for the Keyoku memory engine API
|
|
3
|
+
*/
|
|
4
|
+
export class KeyokuError extends Error {
|
|
5
|
+
status;
|
|
6
|
+
path;
|
|
7
|
+
constructor(status, message, path) {
|
|
8
|
+
super(`Keyoku error (${status}) on ${path}: ${message}`);
|
|
9
|
+
this.status = status;
|
|
10
|
+
this.path = path;
|
|
11
|
+
this.name = 'KeyokuError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class KeyokuClient {
|
|
15
|
+
baseUrl;
|
|
16
|
+
timeout;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.baseUrl = (options.baseUrl ?? 'http://localhost:18900').replace(/\/$/, '');
|
|
19
|
+
this.timeout = options.timeout ?? 10000;
|
|
20
|
+
}
|
|
21
|
+
async request(method, path, body) {
|
|
22
|
+
const url = `${this.baseUrl}${path}`;
|
|
23
|
+
const controller = new AbortController();
|
|
24
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
25
|
+
try {
|
|
26
|
+
const headers = {};
|
|
27
|
+
if (body)
|
|
28
|
+
headers['Content-Type'] = 'application/json';
|
|
29
|
+
const res = await fetch(url, {
|
|
30
|
+
method,
|
|
31
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
32
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
});
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
const errBody = await res.json().catch(() => ({}));
|
|
37
|
+
throw new KeyokuError(res.status, errBody.error || res.statusText, path);
|
|
38
|
+
}
|
|
39
|
+
return await res.json();
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// === Memory ===
|
|
46
|
+
async remember(entityId, content, options) {
|
|
47
|
+
return this.request('POST', '/api/v1/remember', {
|
|
48
|
+
entity_id: entityId,
|
|
49
|
+
content,
|
|
50
|
+
...options,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async search(entityId, query, options) {
|
|
54
|
+
return this.request('POST', '/api/v1/search', {
|
|
55
|
+
entity_id: entityId,
|
|
56
|
+
query,
|
|
57
|
+
...options,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async listMemories(entityId, limit = 100) {
|
|
61
|
+
return this.request('GET', `/api/v1/memories?entity_id=${entityId}&limit=${limit}`);
|
|
62
|
+
}
|
|
63
|
+
async getMemory(id) {
|
|
64
|
+
return this.request('GET', `/api/v1/memories/${id}`);
|
|
65
|
+
}
|
|
66
|
+
async deleteMemory(id) {
|
|
67
|
+
return this.request('DELETE', `/api/v1/memories/${id}`);
|
|
68
|
+
}
|
|
69
|
+
async deleteAllMemories(entityId) {
|
|
70
|
+
return this.request('DELETE', '/api/v1/memories', { entity_id: entityId });
|
|
71
|
+
}
|
|
72
|
+
async getStats(entityId) {
|
|
73
|
+
return this.request('GET', `/api/v1/stats/${entityId}`);
|
|
74
|
+
}
|
|
75
|
+
// === Heartbeat ===
|
|
76
|
+
async heartbeatCheck(entityId, options) {
|
|
77
|
+
return this.request('POST', '/api/v1/heartbeat/check', {
|
|
78
|
+
entity_id: entityId,
|
|
79
|
+
...options,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/** Combined heartbeat + context search in a single call, with optional LLM analysis. */
|
|
83
|
+
async heartbeatContext(entityId, options) {
|
|
84
|
+
return this.request('POST', '/api/v1/heartbeat/context', {
|
|
85
|
+
entity_id: entityId,
|
|
86
|
+
...options,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// === Schedules ===
|
|
90
|
+
async createSchedule(entityId, agentId, content, cronTag) {
|
|
91
|
+
return this.request('POST', '/api/v1/schedule', {
|
|
92
|
+
entity_id: entityId,
|
|
93
|
+
agent_id: agentId,
|
|
94
|
+
content,
|
|
95
|
+
cron_tag: cronTag,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async listSchedules(entityId, agentId) {
|
|
99
|
+
const params = new URLSearchParams({ entity_id: entityId });
|
|
100
|
+
if (agentId)
|
|
101
|
+
params.set('agent_id', agentId);
|
|
102
|
+
return this.request('GET', `/api/v1/scheduled?${params}`);
|
|
103
|
+
}
|
|
104
|
+
async ackSchedule(memoryId) {
|
|
105
|
+
return this.request('POST', '/api/v1/schedule/ack', {
|
|
106
|
+
memory_id: memoryId,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async cancelSchedule(id) {
|
|
110
|
+
return this.request('DELETE', `/api/v1/schedule/${id}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@keyoku/memory",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Keyoku memory engine HTTP client",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@keyoku/types": "1.0.0"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed HTTP client for the Keyoku memory engine API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
Memory,
|
|
7
|
+
SearchResult,
|
|
8
|
+
RememberResult,
|
|
9
|
+
HeartbeatResult,
|
|
10
|
+
HeartbeatContextResult,
|
|
11
|
+
MemoryStats,
|
|
12
|
+
} from '@keyoku/types';
|
|
13
|
+
|
|
14
|
+
export { type Memory, type SearchResult, type RememberResult, type HeartbeatResult, type HeartbeatContextResult, type MemoryStats } from '@keyoku/types';
|
|
15
|
+
|
|
16
|
+
export class KeyokuError extends Error {
|
|
17
|
+
constructor(
|
|
18
|
+
public readonly status: number,
|
|
19
|
+
message: string,
|
|
20
|
+
public readonly path: string,
|
|
21
|
+
) {
|
|
22
|
+
super(`Keyoku error (${status}) on ${path}: ${message}`);
|
|
23
|
+
this.name = 'KeyokuError';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class KeyokuClient {
|
|
28
|
+
private baseUrl: string;
|
|
29
|
+
private timeout: number;
|
|
30
|
+
|
|
31
|
+
constructor(options: { baseUrl?: string; timeout?: number }) {
|
|
32
|
+
this.baseUrl = (options.baseUrl ?? 'http://localhost:18900').replace(/\/$/, '');
|
|
33
|
+
this.timeout = options.timeout ?? 10000;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
37
|
+
const url = `${this.baseUrl}${path}`;
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
const headers: Record<string, string> = {};
|
|
43
|
+
if (body) headers['Content-Type'] = 'application/json';
|
|
44
|
+
|
|
45
|
+
const res = await fetch(url, {
|
|
46
|
+
method,
|
|
47
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
48
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const errBody = await res.json().catch(() => ({}));
|
|
54
|
+
throw new KeyokuError(res.status, (errBody as Record<string, string>).error || res.statusText, path);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return await res.json() as T;
|
|
58
|
+
} finally {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// === Memory ===
|
|
64
|
+
|
|
65
|
+
async remember(entityId: string, content: string, options?: {
|
|
66
|
+
session_id?: string;
|
|
67
|
+
agent_id?: string;
|
|
68
|
+
source?: string;
|
|
69
|
+
team_id?: string;
|
|
70
|
+
visibility?: string;
|
|
71
|
+
}): Promise<RememberResult> {
|
|
72
|
+
return this.request<RememberResult>('POST', '/api/v1/remember', {
|
|
73
|
+
entity_id: entityId,
|
|
74
|
+
content,
|
|
75
|
+
...options,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async search(entityId: string, query: string, options?: {
|
|
80
|
+
limit?: number;
|
|
81
|
+
mode?: string;
|
|
82
|
+
agent_id?: string;
|
|
83
|
+
team_aware?: boolean;
|
|
84
|
+
min_score?: number;
|
|
85
|
+
}): Promise<SearchResult[]> {
|
|
86
|
+
return this.request<SearchResult[]>('POST', '/api/v1/search', {
|
|
87
|
+
entity_id: entityId,
|
|
88
|
+
query,
|
|
89
|
+
...options,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async listMemories(entityId: string, limit = 100): Promise<Memory[]> {
|
|
94
|
+
return this.request<Memory[]>('GET', `/api/v1/memories?entity_id=${entityId}&limit=${limit}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async getMemory(id: string): Promise<Memory> {
|
|
98
|
+
return this.request<Memory>('GET', `/api/v1/memories/${id}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async deleteMemory(id: string): Promise<{ status: string }> {
|
|
102
|
+
return this.request<{ status: string }>('DELETE', `/api/v1/memories/${id}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async deleteAllMemories(entityId: string): Promise<{ status: string }> {
|
|
106
|
+
return this.request<{ status: string }>('DELETE', '/api/v1/memories', { entity_id: entityId });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async getStats(entityId: string): Promise<MemoryStats> {
|
|
110
|
+
return this.request<MemoryStats>('GET', `/api/v1/stats/${entityId}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// === Heartbeat ===
|
|
114
|
+
|
|
115
|
+
async heartbeatCheck(entityId: string, options?: {
|
|
116
|
+
deadline_window?: string;
|
|
117
|
+
decay_threshold?: number;
|
|
118
|
+
importance_floor?: number;
|
|
119
|
+
max_results?: number;
|
|
120
|
+
agent_id?: string;
|
|
121
|
+
team_id?: string;
|
|
122
|
+
}): Promise<HeartbeatResult> {
|
|
123
|
+
return this.request<HeartbeatResult>('POST', '/api/v1/heartbeat/check', {
|
|
124
|
+
entity_id: entityId,
|
|
125
|
+
...options,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Combined heartbeat + context search in a single call, with optional LLM analysis. */
|
|
130
|
+
async heartbeatContext(entityId: string, options?: {
|
|
131
|
+
query?: string;
|
|
132
|
+
top_k?: number;
|
|
133
|
+
min_score?: number;
|
|
134
|
+
deadline_window?: string;
|
|
135
|
+
max_results?: number;
|
|
136
|
+
agent_id?: string;
|
|
137
|
+
team_id?: string;
|
|
138
|
+
analyze?: boolean;
|
|
139
|
+
activity_summary?: string;
|
|
140
|
+
autonomy?: 'observe' | 'suggest' | 'act';
|
|
141
|
+
}): Promise<HeartbeatContextResult> {
|
|
142
|
+
return this.request<HeartbeatContextResult>('POST', '/api/v1/heartbeat/context', {
|
|
143
|
+
entity_id: entityId,
|
|
144
|
+
...options,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// === Schedules ===
|
|
149
|
+
|
|
150
|
+
async createSchedule(entityId: string, agentId: string, content: string, cronTag: string): Promise<Memory> {
|
|
151
|
+
return this.request<Memory>('POST', '/api/v1/schedule', {
|
|
152
|
+
entity_id: entityId,
|
|
153
|
+
agent_id: agentId,
|
|
154
|
+
content,
|
|
155
|
+
cron_tag: cronTag,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async listSchedules(entityId: string, agentId?: string): Promise<Memory[]> {
|
|
160
|
+
const params = new URLSearchParams({ entity_id: entityId });
|
|
161
|
+
if (agentId) params.set('agent_id', agentId);
|
|
162
|
+
return this.request<Memory[]>('GET', `/api/v1/scheduled?${params}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async ackSchedule(memoryId: string): Promise<{ status: string; memory_id: string }> {
|
|
166
|
+
return this.request<{ status: string; memory_id: string }>('POST', '/api/v1/schedule/ack', {
|
|
167
|
+
memory_id: memoryId,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async cancelSchedule(id: string): Promise<{ status: string; memory_id: string }> {
|
|
172
|
+
return this.request<{ status: string; memory_id: string }>('DELETE', `/api/v1/schedule/${id}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { KeyokuClient, KeyokuError } from '../src/client.js';
|
|
3
|
+
|
|
4
|
+
const mockFetch = vi.fn();
|
|
5
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
6
|
+
|
|
7
|
+
function jsonResponse(data: unknown, status = 200) {
|
|
8
|
+
return {
|
|
9
|
+
ok: status >= 200 && status < 300,
|
|
10
|
+
status,
|
|
11
|
+
statusText: status === 200 ? 'OK' : 'Error',
|
|
12
|
+
json: () => Promise.resolve(data),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('KeyokuClient', () => {
|
|
17
|
+
let client: KeyokuClient;
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
client = new KeyokuClient({ baseUrl: 'http://localhost:18900' });
|
|
21
|
+
mockFetch.mockReset();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('constructor', () => {
|
|
25
|
+
it('defaults to localhost:18900', () => {
|
|
26
|
+
const c = new KeyokuClient({});
|
|
27
|
+
mockFetch.mockResolvedValue(jsonResponse([]));
|
|
28
|
+
c.listMemories('entity-1');
|
|
29
|
+
|
|
30
|
+
const url = mockFetch.mock.calls[0][0];
|
|
31
|
+
expect(url).toContain('http://localhost:18900');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('strips trailing slash', () => {
|
|
35
|
+
const c = new KeyokuClient({ baseUrl: 'http://example.com/' });
|
|
36
|
+
mockFetch.mockResolvedValue(jsonResponse([]));
|
|
37
|
+
c.listMemories('e1');
|
|
38
|
+
|
|
39
|
+
const url = mockFetch.mock.calls[0][0];
|
|
40
|
+
expect(url.startsWith('http://example.com/api')).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('remember', () => {
|
|
45
|
+
it('calls POST /api/v1/remember with content', async () => {
|
|
46
|
+
mockFetch.mockResolvedValue(jsonResponse({
|
|
47
|
+
memories_created: 1,
|
|
48
|
+
memories_updated: 0,
|
|
49
|
+
memories_deleted: 0,
|
|
50
|
+
skipped: 0,
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
const result = await client.remember('entity-1', 'Important fact', {
|
|
54
|
+
agent_id: 'agent-1',
|
|
55
|
+
team_id: 'team-1',
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(result.memories_created).toBe(1);
|
|
59
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
60
|
+
expect(body.entity_id).toBe('entity-1');
|
|
61
|
+
expect(body.content).toBe('Important fact');
|
|
62
|
+
expect(body.agent_id).toBe('agent-1');
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('search', () => {
|
|
67
|
+
it('calls POST /api/v1/search', async () => {
|
|
68
|
+
mockFetch.mockResolvedValue(jsonResponse([
|
|
69
|
+
{ memory: { id: 'm1', content: 'test' }, similarity: 0.9, score: 0.85 },
|
|
70
|
+
]));
|
|
71
|
+
|
|
72
|
+
const results = await client.search('entity-1', 'test query', { limit: 5 });
|
|
73
|
+
|
|
74
|
+
expect(results).toHaveLength(1);
|
|
75
|
+
expect(results[0].similarity).toBe(0.9);
|
|
76
|
+
|
|
77
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
78
|
+
expect(body.query).toBe('test query');
|
|
79
|
+
expect(body.limit).toBe(5);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('listMemories', () => {
|
|
84
|
+
it('calls GET with entity_id and limit', async () => {
|
|
85
|
+
mockFetch.mockResolvedValue(jsonResponse([]));
|
|
86
|
+
|
|
87
|
+
await client.listMemories('entity-1', 50);
|
|
88
|
+
|
|
89
|
+
const url = mockFetch.mock.calls[0][0];
|
|
90
|
+
expect(url).toContain('entity_id=entity-1');
|
|
91
|
+
expect(url).toContain('limit=50');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('defaults limit to 100', async () => {
|
|
95
|
+
mockFetch.mockResolvedValue(jsonResponse([]));
|
|
96
|
+
await client.listMemories('entity-1');
|
|
97
|
+
|
|
98
|
+
const url = mockFetch.mock.calls[0][0];
|
|
99
|
+
expect(url).toContain('limit=100');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('getMemory', () => {
|
|
104
|
+
it('calls GET /api/v1/memories/:id', async () => {
|
|
105
|
+
const memory = { id: 'm1', content: 'test', entity_id: 'e1' };
|
|
106
|
+
mockFetch.mockResolvedValue(jsonResponse(memory));
|
|
107
|
+
|
|
108
|
+
const result = await client.getMemory('m1');
|
|
109
|
+
expect(result.id).toBe('m1');
|
|
110
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
111
|
+
'http://localhost:18900/api/v1/memories/m1',
|
|
112
|
+
expect.anything(),
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe('deleteMemory', () => {
|
|
118
|
+
it('calls DELETE /api/v1/memories/:id', async () => {
|
|
119
|
+
mockFetch.mockResolvedValue(jsonResponse({ status: 'deleted' }));
|
|
120
|
+
|
|
121
|
+
const result = await client.deleteMemory('m1');
|
|
122
|
+
expect(result.status).toBe('deleted');
|
|
123
|
+
expect(mockFetch.mock.calls[0][1].method).toBe('DELETE');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('deleteAllMemories', () => {
|
|
128
|
+
it('calls DELETE with entity_id body', async () => {
|
|
129
|
+
mockFetch.mockResolvedValue(jsonResponse({ status: 'deleted' }));
|
|
130
|
+
|
|
131
|
+
await client.deleteAllMemories('entity-1');
|
|
132
|
+
|
|
133
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
134
|
+
expect(body.entity_id).toBe('entity-1');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('getStats', () => {
|
|
139
|
+
it('calls GET /api/v1/stats/:entityId', async () => {
|
|
140
|
+
const stats = { total_memories: 42, active_memories: 30, by_type: {}, by_state: {} };
|
|
141
|
+
mockFetch.mockResolvedValue(jsonResponse(stats));
|
|
142
|
+
|
|
143
|
+
const result = await client.getStats('entity-1');
|
|
144
|
+
expect(result.total_memories).toBe(42);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('heartbeatCheck', () => {
|
|
149
|
+
it('calls POST /api/v1/heartbeat/check', async () => {
|
|
150
|
+
mockFetch.mockResolvedValue(jsonResponse({
|
|
151
|
+
should_act: false,
|
|
152
|
+
pending_work: [],
|
|
153
|
+
deadlines: [],
|
|
154
|
+
scheduled: [],
|
|
155
|
+
decaying: [],
|
|
156
|
+
conflicts: [],
|
|
157
|
+
stale_monitors: [],
|
|
158
|
+
summary: 'All clear',
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
const result = await client.heartbeatCheck('entity-1', {
|
|
162
|
+
deadline_window: '1h',
|
|
163
|
+
max_results: 10,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(result.should_act).toBe(false);
|
|
167
|
+
expect(result.summary).toBe('All clear');
|
|
168
|
+
|
|
169
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
170
|
+
expect(body.entity_id).toBe('entity-1');
|
|
171
|
+
expect(body.deadline_window).toBe('1h');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('schedules', () => {
|
|
176
|
+
it('createSchedule sends correct body', async () => {
|
|
177
|
+
mockFetch.mockResolvedValue(jsonResponse({ id: 's1' }));
|
|
178
|
+
|
|
179
|
+
await client.createSchedule('entity-1', 'agent-1', 'Daily standup', 'daily');
|
|
180
|
+
|
|
181
|
+
const body = JSON.parse(mockFetch.mock.calls[0][1].body);
|
|
182
|
+
expect(body.entity_id).toBe('entity-1');
|
|
183
|
+
expect(body.agent_id).toBe('agent-1');
|
|
184
|
+
expect(body.content).toBe('Daily standup');
|
|
185
|
+
expect(body.cron_tag).toBe('daily');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('listSchedules filters by agent', async () => {
|
|
189
|
+
mockFetch.mockResolvedValue(jsonResponse([]));
|
|
190
|
+
|
|
191
|
+
await client.listSchedules('entity-1', 'agent-1');
|
|
192
|
+
|
|
193
|
+
const url = mockFetch.mock.calls[0][0];
|
|
194
|
+
expect(url).toContain('entity_id=entity-1');
|
|
195
|
+
expect(url).toContain('agent_id=agent-1');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('ackSchedule calls POST', async () => {
|
|
199
|
+
mockFetch.mockResolvedValue(jsonResponse({ status: 'acknowledged', memory_id: 'm1' }));
|
|
200
|
+
|
|
201
|
+
const result = await client.ackSchedule('m1');
|
|
202
|
+
expect(result.status).toBe('acknowledged');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('cancelSchedule calls DELETE', async () => {
|
|
206
|
+
mockFetch.mockResolvedValue(jsonResponse({ status: 'cancelled', memory_id: 's1' }));
|
|
207
|
+
|
|
208
|
+
const result = await client.cancelSchedule('s1');
|
|
209
|
+
expect(result.status).toBe('cancelled');
|
|
210
|
+
expect(mockFetch.mock.calls[0][1].method).toBe('DELETE');
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('error handling', () => {
|
|
215
|
+
it('throws KeyokuError on non-OK response', async () => {
|
|
216
|
+
mockFetch.mockResolvedValue(jsonResponse({ error: 'Not found' }, 404));
|
|
217
|
+
|
|
218
|
+
await expect(client.getMemory('bad-id')).rejects.toThrow(KeyokuError);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('includes status and path in error', async () => {
|
|
222
|
+
mockFetch.mockResolvedValue(jsonResponse({ error: 'Server error' }, 500));
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await client.getStats('e1');
|
|
226
|
+
} catch (err) {
|
|
227
|
+
expect(err).toBeInstanceOf(KeyokuError);
|
|
228
|
+
expect((err as KeyokuError).status).toBe(500);
|
|
229
|
+
expect((err as KeyokuError).path).toContain('/api/v1/stats/e1');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"extends":"../../tsconfig.json","compilerOptions":{"outDir":"dist","rootDir":"src"},"include":["src"]}
|