oh-my-opencode-kikokikok 2.15.0 → 2.15.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/cli/index.js +17 -2
- package/dist/config/schema.d.ts +28 -3
- package/dist/features/letta-memory/adapter.d.ts +24 -0
- package/dist/features/letta-memory/adapter.test.d.ts +1 -0
- package/dist/features/letta-memory/index.d.ts +2 -0
- package/dist/features/letta-memory/types.d.ts +140 -0
- package/dist/hooks/memory-rehydration/index.d.ts +2 -2
- package/dist/index.js +320 -17
- package/dist/tools/memory/index.d.ts +1 -1
- package/dist/tools/memory/tools.d.ts +2 -2
- package/dist/tools/memory/types.d.ts +41 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2253,7 +2253,7 @@ var require_picocolors = __commonJS((exports, module) => {
|
|
|
2253
2253
|
var require_package = __commonJS((exports, module) => {
|
|
2254
2254
|
module.exports = {
|
|
2255
2255
|
name: "oh-my-opencode-kikokikok",
|
|
2256
|
-
version: "2.15.
|
|
2256
|
+
version: "2.15.1",
|
|
2257
2257
|
description: "OpenCode plugin - custom agents (oracle, librarian) and enhanced features",
|
|
2258
2258
|
main: "dist/index.js",
|
|
2259
2259
|
types: "dist/index.d.ts",
|
|
@@ -22646,6 +22646,21 @@ var Mem0ConfigSchema = exports_external.object({
|
|
|
22646
22646
|
autoRehydrate: exports_external.boolean().default(true),
|
|
22647
22647
|
rehydrateLayers: exports_external.array(MemoryLayerSchema).default(["user", "project", "team"])
|
|
22648
22648
|
});
|
|
22649
|
+
var LettaConfigSchema = exports_external.object({
|
|
22650
|
+
enabled: exports_external.boolean().default(false),
|
|
22651
|
+
endpoint: exports_external.string().url().optional().describe("Letta server endpoint (default: http://localhost:8283)"),
|
|
22652
|
+
apiKey: exports_external.string().optional().describe("Optional API key for cloud Letta (not needed for self-hosted)"),
|
|
22653
|
+
userId: exports_external.string().optional(),
|
|
22654
|
+
projectId: exports_external.string().optional(),
|
|
22655
|
+
teamId: exports_external.string().optional(),
|
|
22656
|
+
orgId: exports_external.string().optional(),
|
|
22657
|
+
companyId: exports_external.string().optional(),
|
|
22658
|
+
agentPrefix: exports_external.string().optional().describe("Agent name prefix (default: opencode)"),
|
|
22659
|
+
llmModel: exports_external.string().optional().describe("LLM model for agent (e.g., openai/gpt-4.1)"),
|
|
22660
|
+
embeddingModel: exports_external.string().optional().describe("Embedding model for semantic search"),
|
|
22661
|
+
autoRehydrate: exports_external.boolean().default(true),
|
|
22662
|
+
rehydrateLayers: exports_external.array(MemoryLayerSchema).default(["user", "project", "team"])
|
|
22663
|
+
});
|
|
22649
22664
|
var CentralHubConfigSchema = exports_external.object({
|
|
22650
22665
|
url: exports_external.string().url(),
|
|
22651
22666
|
branch: exports_external.string().default("main"),
|
|
@@ -22749,7 +22764,7 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
22749
22764
|
skills: SkillsConfigSchema.optional(),
|
|
22750
22765
|
ralph_loop: RalphLoopConfigSchema.optional(),
|
|
22751
22766
|
mem0: Mem0ConfigSchema.optional(),
|
|
22752
|
-
letta:
|
|
22767
|
+
letta: LettaConfigSchema.optional(),
|
|
22753
22768
|
knowledge_repo: KnowledgeRepoConfigSchema.optional(),
|
|
22754
22769
|
knowledge_provider: KnowledgeProviderConfigSchema.optional(),
|
|
22755
22770
|
openspec: OpenSpecConfigSchema.optional()
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -1455,6 +1455,29 @@ export declare const Mem0ConfigSchema: z.ZodObject<{
|
|
|
1455
1455
|
company: "company";
|
|
1456
1456
|
}>>>;
|
|
1457
1457
|
}, z.core.$strip>;
|
|
1458
|
+
export declare const LettaConfigSchema: z.ZodObject<{
|
|
1459
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1460
|
+
endpoint: z.ZodOptional<z.ZodString>;
|
|
1461
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
1462
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
1463
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
1464
|
+
teamId: z.ZodOptional<z.ZodString>;
|
|
1465
|
+
orgId: z.ZodOptional<z.ZodString>;
|
|
1466
|
+
companyId: z.ZodOptional<z.ZodString>;
|
|
1467
|
+
agentPrefix: z.ZodOptional<z.ZodString>;
|
|
1468
|
+
llmModel: z.ZodOptional<z.ZodString>;
|
|
1469
|
+
embeddingModel: z.ZodOptional<z.ZodString>;
|
|
1470
|
+
autoRehydrate: z.ZodDefault<z.ZodBoolean>;
|
|
1471
|
+
rehydrateLayers: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
1472
|
+
user: "user";
|
|
1473
|
+
agent: "agent";
|
|
1474
|
+
session: "session";
|
|
1475
|
+
project: "project";
|
|
1476
|
+
team: "team";
|
|
1477
|
+
org: "org";
|
|
1478
|
+
company: "company";
|
|
1479
|
+
}>>>;
|
|
1480
|
+
}, z.core.$strip>;
|
|
1458
1481
|
export declare const CentralHubConfigSchema: z.ZodObject<{
|
|
1459
1482
|
url: z.ZodString;
|
|
1460
1483
|
branch: z.ZodDefault<z.ZodString>;
|
|
@@ -2900,15 +2923,16 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
|
|
|
2900
2923
|
}, z.core.$strip>>;
|
|
2901
2924
|
letta: z.ZodOptional<z.ZodObject<{
|
|
2902
2925
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
2903
|
-
apiKey: z.ZodOptional<z.ZodString>;
|
|
2904
2926
|
endpoint: z.ZodOptional<z.ZodString>;
|
|
2927
|
+
apiKey: z.ZodOptional<z.ZodString>;
|
|
2905
2928
|
userId: z.ZodOptional<z.ZodString>;
|
|
2906
|
-
sessionId: z.ZodOptional<z.ZodString>;
|
|
2907
2929
|
projectId: z.ZodOptional<z.ZodString>;
|
|
2908
2930
|
teamId: z.ZodOptional<z.ZodString>;
|
|
2909
2931
|
orgId: z.ZodOptional<z.ZodString>;
|
|
2910
2932
|
companyId: z.ZodOptional<z.ZodString>;
|
|
2911
|
-
|
|
2933
|
+
agentPrefix: z.ZodOptional<z.ZodString>;
|
|
2934
|
+
llmModel: z.ZodOptional<z.ZodString>;
|
|
2935
|
+
embeddingModel: z.ZodOptional<z.ZodString>;
|
|
2912
2936
|
autoRehydrate: z.ZodDefault<z.ZodBoolean>;
|
|
2913
2937
|
rehydrateLayers: z.ZodDefault<z.ZodArray<z.ZodEnum<{
|
|
2914
2938
|
user: "user";
|
|
@@ -3027,6 +3051,7 @@ export type SkillsConfig = z.infer<typeof SkillsConfigSchema>;
|
|
|
3027
3051
|
export type SkillDefinition = z.infer<typeof SkillDefinitionSchema>;
|
|
3028
3052
|
export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>;
|
|
3029
3053
|
export type Mem0Config = z.infer<typeof Mem0ConfigSchema>;
|
|
3054
|
+
export type LettaConfig = z.infer<typeof LettaConfigSchema>;
|
|
3030
3055
|
export type CentralHubConfig = z.infer<typeof CentralHubConfigSchema>;
|
|
3031
3056
|
export type KnowledgeRepoConfig = z.infer<typeof KnowledgeRepoConfigSchema>;
|
|
3032
3057
|
export type OpenSpecConfig = z.infer<typeof OpenSpecConfigSchema>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { LettaConfig, Memory, MemorySearchResult, AddMemoryInput, SearchMemoryInput, UpdateMemoryInput, MemoryStats, MemoryLayer } from "./types";
|
|
2
|
+
export declare class LettaAdapter {
|
|
3
|
+
private config;
|
|
4
|
+
private endpoint;
|
|
5
|
+
private agentCache;
|
|
6
|
+
constructor(config: LettaConfig);
|
|
7
|
+
add(input: AddMemoryInput): Promise<Memory>;
|
|
8
|
+
search(input: SearchMemoryInput): Promise<MemorySearchResult[]>;
|
|
9
|
+
get(id: string): Promise<Memory | null>;
|
|
10
|
+
update(input: UpdateMemoryInput): Promise<Memory>;
|
|
11
|
+
delete(id: string): Promise<void>;
|
|
12
|
+
getAll(layer?: MemoryLayer): Promise<Memory[]>;
|
|
13
|
+
getStats(): Promise<MemoryStats>;
|
|
14
|
+
isAvailable(): Promise<boolean>;
|
|
15
|
+
private getOrCreateAgent;
|
|
16
|
+
private getAgent;
|
|
17
|
+
private listAgents;
|
|
18
|
+
private request;
|
|
19
|
+
private getAgentName;
|
|
20
|
+
private getUserId;
|
|
21
|
+
private normalizeLayers;
|
|
22
|
+
private getAllLayers;
|
|
23
|
+
private passageToMemory;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Letta (formerly MemGPT) Memory Types
|
|
3
|
+
*
|
|
4
|
+
* Letta organizes memory around Agents with:
|
|
5
|
+
* - Core Memory (Blocks): Always in-context, editable by agent
|
|
6
|
+
* - Archival Memory (Passages): Out-of-context, searchable via embeddings
|
|
7
|
+
*/
|
|
8
|
+
export type MemoryLayer = "user" | "session" | "project" | "team" | "org" | "company" | "agent";
|
|
9
|
+
export interface LettaConfig {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
/** Letta server endpoint (default: http://localhost:8283) */
|
|
12
|
+
endpoint?: string;
|
|
13
|
+
/** Optional API key for cloud Letta (not needed for self-hosted) */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** User identifier for scoping agents */
|
|
16
|
+
userId?: string;
|
|
17
|
+
/** Project identifier */
|
|
18
|
+
projectId?: string;
|
|
19
|
+
/** Team identifier */
|
|
20
|
+
teamId?: string;
|
|
21
|
+
/** Organization identifier */
|
|
22
|
+
orgId?: string;
|
|
23
|
+
/** Company identifier */
|
|
24
|
+
companyId?: string;
|
|
25
|
+
/** Default agent name prefix */
|
|
26
|
+
agentPrefix?: string;
|
|
27
|
+
/** LLM model for agent (e.g., "openai/gpt-4.1") */
|
|
28
|
+
llmModel?: string;
|
|
29
|
+
/** Embedding model for semantic search */
|
|
30
|
+
embeddingModel?: string;
|
|
31
|
+
/** Auto-rehydrate memories on session start */
|
|
32
|
+
autoRehydrate?: boolean;
|
|
33
|
+
/** Layers to rehydrate */
|
|
34
|
+
rehydrateLayers?: MemoryLayer[];
|
|
35
|
+
}
|
|
36
|
+
/** Letta Agent representation */
|
|
37
|
+
export interface LettaAgent {
|
|
38
|
+
id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
created_at: string;
|
|
42
|
+
user_id?: string;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
memory_blocks?: LettaBlock[];
|
|
45
|
+
tools?: string[];
|
|
46
|
+
}
|
|
47
|
+
/** Letta Memory Block (Core Memory - always in-context) */
|
|
48
|
+
export interface LettaBlock {
|
|
49
|
+
id: string;
|
|
50
|
+
label: string;
|
|
51
|
+
value: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
limit?: number;
|
|
54
|
+
metadata?: Record<string, unknown>;
|
|
55
|
+
created_at?: string;
|
|
56
|
+
updated_at?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Letta Passage (Archival Memory - searchable) */
|
|
59
|
+
export interface LettaPassage {
|
|
60
|
+
id: string;
|
|
61
|
+
text: string;
|
|
62
|
+
agent_id?: string;
|
|
63
|
+
tags?: string[];
|
|
64
|
+
metadata?: Record<string, unknown>;
|
|
65
|
+
created_at: string;
|
|
66
|
+
embedding?: number[];
|
|
67
|
+
}
|
|
68
|
+
/** Letta Archive container for passages */
|
|
69
|
+
export interface LettaArchive {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
agent_id?: string;
|
|
74
|
+
created_at: string;
|
|
75
|
+
}
|
|
76
|
+
/** Unified memory representation (compatible with existing tools) */
|
|
77
|
+
export interface Memory {
|
|
78
|
+
id: string;
|
|
79
|
+
content: string;
|
|
80
|
+
layer: MemoryLayer;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
createdAt: string;
|
|
83
|
+
updatedAt?: string;
|
|
84
|
+
/** Letta-specific: source type */
|
|
85
|
+
source?: "block" | "passage";
|
|
86
|
+
/** Letta-specific: agent ID */
|
|
87
|
+
agentId?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface MemorySearchResult {
|
|
90
|
+
memory: Memory;
|
|
91
|
+
score: number;
|
|
92
|
+
}
|
|
93
|
+
export interface AddMemoryInput {
|
|
94
|
+
content: string;
|
|
95
|
+
layer: MemoryLayer;
|
|
96
|
+
metadata?: Record<string, unknown>;
|
|
97
|
+
/** Store as block (core memory) or passage (archival). Default: passage */
|
|
98
|
+
type?: "block" | "passage";
|
|
99
|
+
/** Block label if storing as block */
|
|
100
|
+
blockLabel?: string;
|
|
101
|
+
/** Tags for passage search */
|
|
102
|
+
tags?: string[];
|
|
103
|
+
}
|
|
104
|
+
export interface SearchMemoryInput {
|
|
105
|
+
query: string;
|
|
106
|
+
layer?: MemoryLayer | MemoryLayer[];
|
|
107
|
+
limit?: number;
|
|
108
|
+
threshold?: number;
|
|
109
|
+
/** Filter by tags */
|
|
110
|
+
tags?: string[];
|
|
111
|
+
/** Tag match mode */
|
|
112
|
+
tagMatchMode?: "any" | "all";
|
|
113
|
+
}
|
|
114
|
+
export interface UpdateMemoryInput {
|
|
115
|
+
id: string;
|
|
116
|
+
content?: string;
|
|
117
|
+
metadata?: Record<string, unknown>;
|
|
118
|
+
tags?: string[];
|
|
119
|
+
}
|
|
120
|
+
export interface MemoryStats {
|
|
121
|
+
totalMemories: number;
|
|
122
|
+
byLayer: Record<MemoryLayer, number>;
|
|
123
|
+
/** Letta-specific stats */
|
|
124
|
+
totalAgents?: number;
|
|
125
|
+
totalBlocks?: number;
|
|
126
|
+
totalPassages?: number;
|
|
127
|
+
}
|
|
128
|
+
/** Letta API response types */
|
|
129
|
+
export interface LettaApiResponse<T> {
|
|
130
|
+
data?: T;
|
|
131
|
+
error?: string;
|
|
132
|
+
message?: string;
|
|
133
|
+
}
|
|
134
|
+
export interface LettaSearchResponse {
|
|
135
|
+
passages: LettaPassage[];
|
|
136
|
+
total?: number;
|
|
137
|
+
}
|
|
138
|
+
export interface LettaAgentListResponse {
|
|
139
|
+
agents: LettaAgent[];
|
|
140
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MemoryAdapter } from "../../tools/memory/types";
|
|
2
2
|
import type { MemoryRehydrationConfig, RehydrationResult } from "./types";
|
|
3
3
|
import { HOOK_NAME } from "./constants";
|
|
4
4
|
export interface MemoryRehydrationHook {
|
|
@@ -12,6 +12,6 @@ export interface MemoryRehydrationHook {
|
|
|
12
12
|
forceRehydrate: (sessionID: string) => Promise<RehydrationResult>;
|
|
13
13
|
clearSession: (sessionID: string) => void;
|
|
14
14
|
}
|
|
15
|
-
export declare function createMemoryRehydrationHook(adapter:
|
|
15
|
+
export declare function createMemoryRehydrationHook(adapter: MemoryAdapter, config?: MemoryRehydrationConfig): MemoryRehydrationHook;
|
|
16
16
|
export { HOOK_NAME };
|
|
17
17
|
export type { MemoryRehydrationConfig } from "./types";
|
package/dist/index.js
CHANGED
|
@@ -43777,6 +43777,290 @@ class Mem0Adapter {
|
|
|
43777
43777
|
});
|
|
43778
43778
|
}
|
|
43779
43779
|
}
|
|
43780
|
+
|
|
43781
|
+
// src/features/letta-memory/adapter.ts
|
|
43782
|
+
var DEFAULT_ENDPOINT2 = "http://localhost:8283";
|
|
43783
|
+
var DEFAULT_AGENT_PREFIX = "opencode";
|
|
43784
|
+
var DEFAULT_LLM_MODEL = "openai/gpt-4.1";
|
|
43785
|
+
var DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
|
|
43786
|
+
|
|
43787
|
+
class LettaAdapter {
|
|
43788
|
+
config;
|
|
43789
|
+
endpoint;
|
|
43790
|
+
agentCache = new Map;
|
|
43791
|
+
constructor(config3) {
|
|
43792
|
+
this.config = config3;
|
|
43793
|
+
this.endpoint = config3.endpoint ?? DEFAULT_ENDPOINT2;
|
|
43794
|
+
}
|
|
43795
|
+
async add(input) {
|
|
43796
|
+
if (!this.config.enabled) {
|
|
43797
|
+
throw new Error("Letta is not enabled");
|
|
43798
|
+
}
|
|
43799
|
+
const agent = await this.getOrCreateAgent(input.layer);
|
|
43800
|
+
const tags = [
|
|
43801
|
+
`layer:${input.layer}`,
|
|
43802
|
+
...input.tags ?? [],
|
|
43803
|
+
...input.metadata ? Object.entries(input.metadata).map(([k, v]) => `${k}:${v}`) : []
|
|
43804
|
+
];
|
|
43805
|
+
const response2 = await this.request(`/v1/agents/${agent.id}/archival-memory`, {
|
|
43806
|
+
method: "POST",
|
|
43807
|
+
body: JSON.stringify({
|
|
43808
|
+
text: input.content,
|
|
43809
|
+
tags
|
|
43810
|
+
})
|
|
43811
|
+
});
|
|
43812
|
+
const passage = await response2.json();
|
|
43813
|
+
return this.passageToMemory(passage, input.layer, agent.id);
|
|
43814
|
+
}
|
|
43815
|
+
async search(input) {
|
|
43816
|
+
if (!this.config.enabled) {
|
|
43817
|
+
throw new Error("Letta is not enabled");
|
|
43818
|
+
}
|
|
43819
|
+
const layers = this.normalizeLayers(input.layer);
|
|
43820
|
+
const results = [];
|
|
43821
|
+
for (const layer of layers) {
|
|
43822
|
+
try {
|
|
43823
|
+
const agent = await this.getAgent(layer);
|
|
43824
|
+
if (!agent)
|
|
43825
|
+
continue;
|
|
43826
|
+
const params = new URLSearchParams;
|
|
43827
|
+
params.set("query", input.query);
|
|
43828
|
+
if (input.limit)
|
|
43829
|
+
params.set("limit", String(input.limit));
|
|
43830
|
+
if (input.tags?.length) {
|
|
43831
|
+
params.set("tags", input.tags.join(","));
|
|
43832
|
+
params.set("tag_match_mode", input.tagMatchMode ?? "any");
|
|
43833
|
+
}
|
|
43834
|
+
const response2 = await this.request(`/v1/agents/${agent.id}/archival-memory/search?${params.toString()}`, { method: "GET" });
|
|
43835
|
+
const data = await response2.json();
|
|
43836
|
+
const layerResults = data.map((passage, index) => ({
|
|
43837
|
+
memory: this.passageToMemory(passage, layer, agent.id),
|
|
43838
|
+
score: 1 - index * 0.05
|
|
43839
|
+
}));
|
|
43840
|
+
results.push(...layerResults);
|
|
43841
|
+
} catch {
|
|
43842
|
+
continue;
|
|
43843
|
+
}
|
|
43844
|
+
}
|
|
43845
|
+
return results.filter((r) => !input.threshold || r.score >= input.threshold).sort((a, b) => b.score - a.score).slice(0, input.limit ?? 10);
|
|
43846
|
+
}
|
|
43847
|
+
async get(id) {
|
|
43848
|
+
if (!this.config.enabled) {
|
|
43849
|
+
throw new Error("Letta is not enabled");
|
|
43850
|
+
}
|
|
43851
|
+
for (const layer of this.getAllLayers()) {
|
|
43852
|
+
try {
|
|
43853
|
+
const agent = await this.getAgent(layer);
|
|
43854
|
+
if (!agent)
|
|
43855
|
+
continue;
|
|
43856
|
+
const response2 = await this.request(`/v1/agents/${agent.id}/archival-memory`, { method: "GET" });
|
|
43857
|
+
const passages = await response2.json();
|
|
43858
|
+
const passage = passages.find((p) => p.id === id);
|
|
43859
|
+
if (passage) {
|
|
43860
|
+
return this.passageToMemory(passage, layer, agent.id);
|
|
43861
|
+
}
|
|
43862
|
+
} catch {
|
|
43863
|
+
continue;
|
|
43864
|
+
}
|
|
43865
|
+
}
|
|
43866
|
+
return null;
|
|
43867
|
+
}
|
|
43868
|
+
async update(input) {
|
|
43869
|
+
if (!this.config.enabled) {
|
|
43870
|
+
throw new Error("Letta is not enabled");
|
|
43871
|
+
}
|
|
43872
|
+
const existing = await this.get(input.id);
|
|
43873
|
+
if (!existing) {
|
|
43874
|
+
throw new Error(`Memory not found: ${input.id}`);
|
|
43875
|
+
}
|
|
43876
|
+
await this.delete(input.id);
|
|
43877
|
+
return this.add({
|
|
43878
|
+
content: input.content ?? existing.content,
|
|
43879
|
+
layer: existing.layer,
|
|
43880
|
+
metadata: input.metadata ? { ...existing.metadata, ...input.metadata } : existing.metadata,
|
|
43881
|
+
tags: input.tags
|
|
43882
|
+
});
|
|
43883
|
+
}
|
|
43884
|
+
async delete(id) {
|
|
43885
|
+
if (!this.config.enabled) {
|
|
43886
|
+
throw new Error("Letta is not enabled");
|
|
43887
|
+
}
|
|
43888
|
+
for (const layer of this.getAllLayers()) {
|
|
43889
|
+
try {
|
|
43890
|
+
const agent = await this.getAgent(layer);
|
|
43891
|
+
if (!agent)
|
|
43892
|
+
continue;
|
|
43893
|
+
await this.request(`/v1/agents/${agent.id}/archival-memory/${id}`, {
|
|
43894
|
+
method: "DELETE"
|
|
43895
|
+
});
|
|
43896
|
+
return;
|
|
43897
|
+
} catch {
|
|
43898
|
+
continue;
|
|
43899
|
+
}
|
|
43900
|
+
}
|
|
43901
|
+
throw new Error(`Memory not found: ${id}`);
|
|
43902
|
+
}
|
|
43903
|
+
async getAll(layer) {
|
|
43904
|
+
if (!this.config.enabled) {
|
|
43905
|
+
throw new Error("Letta is not enabled");
|
|
43906
|
+
}
|
|
43907
|
+
const layers = layer ? [layer] : this.getAllLayers();
|
|
43908
|
+
const memories = [];
|
|
43909
|
+
for (const l of layers) {
|
|
43910
|
+
try {
|
|
43911
|
+
const agent = await this.getAgent(l);
|
|
43912
|
+
if (!agent)
|
|
43913
|
+
continue;
|
|
43914
|
+
const response2 = await this.request(`/v1/agents/${agent.id}/archival-memory`, { method: "GET" });
|
|
43915
|
+
const passages = await response2.json();
|
|
43916
|
+
memories.push(...passages.map((p) => this.passageToMemory(p, l, agent.id)));
|
|
43917
|
+
} catch {
|
|
43918
|
+
continue;
|
|
43919
|
+
}
|
|
43920
|
+
}
|
|
43921
|
+
return memories;
|
|
43922
|
+
}
|
|
43923
|
+
async getStats() {
|
|
43924
|
+
const memories = await this.getAll();
|
|
43925
|
+
const byLayer = {
|
|
43926
|
+
user: 0,
|
|
43927
|
+
session: 0,
|
|
43928
|
+
project: 0,
|
|
43929
|
+
team: 0,
|
|
43930
|
+
org: 0,
|
|
43931
|
+
company: 0,
|
|
43932
|
+
agent: 0
|
|
43933
|
+
};
|
|
43934
|
+
for (const memory of memories) {
|
|
43935
|
+
byLayer[memory.layer]++;
|
|
43936
|
+
}
|
|
43937
|
+
const agents = await this.listAgents();
|
|
43938
|
+
return {
|
|
43939
|
+
totalMemories: memories.length,
|
|
43940
|
+
byLayer,
|
|
43941
|
+
totalAgents: agents.length,
|
|
43942
|
+
totalPassages: memories.length
|
|
43943
|
+
};
|
|
43944
|
+
}
|
|
43945
|
+
async isAvailable() {
|
|
43946
|
+
try {
|
|
43947
|
+
const response2 = await fetch(`${this.endpoint}/v1/health`, {
|
|
43948
|
+
method: "GET",
|
|
43949
|
+
signal: AbortSignal.timeout(5000)
|
|
43950
|
+
});
|
|
43951
|
+
return response2.ok;
|
|
43952
|
+
} catch {
|
|
43953
|
+
return false;
|
|
43954
|
+
}
|
|
43955
|
+
}
|
|
43956
|
+
async getOrCreateAgent(layer) {
|
|
43957
|
+
const existing = await this.getAgent(layer);
|
|
43958
|
+
if (existing)
|
|
43959
|
+
return existing;
|
|
43960
|
+
const agentName = this.getAgentName(layer);
|
|
43961
|
+
const response2 = await this.request("/v1/agents", {
|
|
43962
|
+
method: "POST",
|
|
43963
|
+
body: JSON.stringify({
|
|
43964
|
+
name: agentName,
|
|
43965
|
+
model: this.config.llmModel ?? DEFAULT_LLM_MODEL,
|
|
43966
|
+
embedding: this.config.embeddingModel ?? DEFAULT_EMBEDDING_MODEL,
|
|
43967
|
+
memory_blocks: [
|
|
43968
|
+
{ label: "persona", value: `OpenCode memory agent for ${layer} layer` },
|
|
43969
|
+
{ label: "human", value: this.getUserId(layer) }
|
|
43970
|
+
],
|
|
43971
|
+
metadata: {
|
|
43972
|
+
layer,
|
|
43973
|
+
user_id: this.getUserId(layer),
|
|
43974
|
+
created_by: "oh-my-opencode"
|
|
43975
|
+
}
|
|
43976
|
+
})
|
|
43977
|
+
});
|
|
43978
|
+
const agent = await response2.json();
|
|
43979
|
+
this.agentCache.set(layer, agent);
|
|
43980
|
+
return agent;
|
|
43981
|
+
}
|
|
43982
|
+
async getAgent(layer) {
|
|
43983
|
+
if (this.agentCache.has(layer)) {
|
|
43984
|
+
return this.agentCache.get(layer);
|
|
43985
|
+
}
|
|
43986
|
+
const agentName = this.getAgentName(layer);
|
|
43987
|
+
const agents = await this.listAgents();
|
|
43988
|
+
const agent = agents.find((a) => a.name === agentName);
|
|
43989
|
+
if (agent) {
|
|
43990
|
+
this.agentCache.set(layer, agent);
|
|
43991
|
+
}
|
|
43992
|
+
return agent ?? null;
|
|
43993
|
+
}
|
|
43994
|
+
async listAgents() {
|
|
43995
|
+
try {
|
|
43996
|
+
const response2 = await this.request("/v1/agents", { method: "GET" });
|
|
43997
|
+
const data = await response2.json();
|
|
43998
|
+
return Array.isArray(data) ? data : [];
|
|
43999
|
+
} catch {
|
|
44000
|
+
return [];
|
|
44001
|
+
}
|
|
44002
|
+
}
|
|
44003
|
+
async request(path7, options) {
|
|
44004
|
+
const headers = {
|
|
44005
|
+
"Content-Type": "application/json"
|
|
44006
|
+
};
|
|
44007
|
+
if (this.config.apiKey) {
|
|
44008
|
+
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
44009
|
+
}
|
|
44010
|
+
const response2 = await fetch(`${this.endpoint}${path7}`, {
|
|
44011
|
+
method: options.method,
|
|
44012
|
+
headers,
|
|
44013
|
+
body: options.body
|
|
44014
|
+
});
|
|
44015
|
+
if (!response2.ok) {
|
|
44016
|
+
const text = await response2.text().catch(() => "");
|
|
44017
|
+
throw new Error(`Letta API error: ${response2.status} ${response2.statusText} - ${text}`);
|
|
44018
|
+
}
|
|
44019
|
+
return response2;
|
|
44020
|
+
}
|
|
44021
|
+
getAgentName(layer) {
|
|
44022
|
+
const prefix = this.config.agentPrefix ?? DEFAULT_AGENT_PREFIX;
|
|
44023
|
+
const userId = this.getUserId(layer);
|
|
44024
|
+
return `${prefix}-${layer}-${userId}`;
|
|
44025
|
+
}
|
|
44026
|
+
getUserId(layer) {
|
|
44027
|
+
switch (layer) {
|
|
44028
|
+
case "user":
|
|
44029
|
+
return this.config.userId ?? "default-user";
|
|
44030
|
+
case "session":
|
|
44031
|
+
return `session-${this.config.userId ?? "default"}`;
|
|
44032
|
+
case "project":
|
|
44033
|
+
return this.config.projectId ?? "default-project";
|
|
44034
|
+
case "team":
|
|
44035
|
+
return this.config.teamId ?? "default-team";
|
|
44036
|
+
case "org":
|
|
44037
|
+
return this.config.orgId ?? "default-org";
|
|
44038
|
+
case "company":
|
|
44039
|
+
return this.config.companyId ?? "default-company";
|
|
44040
|
+
case "agent":
|
|
44041
|
+
return "default-agent";
|
|
44042
|
+
}
|
|
44043
|
+
}
|
|
44044
|
+
normalizeLayers(layer) {
|
|
44045
|
+
if (!layer)
|
|
44046
|
+
return this.getAllLayers();
|
|
44047
|
+
return Array.isArray(layer) ? layer : [layer];
|
|
44048
|
+
}
|
|
44049
|
+
getAllLayers() {
|
|
44050
|
+
return ["user", "session", "project", "team", "org", "company", "agent"];
|
|
44051
|
+
}
|
|
44052
|
+
passageToMemory(passage, layer, agentId) {
|
|
44053
|
+
return {
|
|
44054
|
+
id: passage.id,
|
|
44055
|
+
content: passage.text,
|
|
44056
|
+
layer,
|
|
44057
|
+
metadata: passage.metadata,
|
|
44058
|
+
createdAt: passage.created_at,
|
|
44059
|
+
source: "passage",
|
|
44060
|
+
agentId
|
|
44061
|
+
};
|
|
44062
|
+
}
|
|
44063
|
+
}
|
|
43780
44064
|
// src/features/knowledge-provider/registry.ts
|
|
43781
44065
|
var DEFAULT_CONFIG2 = {
|
|
43782
44066
|
defaultLimit: 10,
|
|
@@ -49637,6 +49921,21 @@ var Mem0ConfigSchema = exports_external.object({
|
|
|
49637
49921
|
autoRehydrate: exports_external.boolean().default(true),
|
|
49638
49922
|
rehydrateLayers: exports_external.array(MemoryLayerSchema).default(["user", "project", "team"])
|
|
49639
49923
|
});
|
|
49924
|
+
var LettaConfigSchema = exports_external.object({
|
|
49925
|
+
enabled: exports_external.boolean().default(false),
|
|
49926
|
+
endpoint: exports_external.string().url().optional().describe("Letta server endpoint (default: http://localhost:8283)"),
|
|
49927
|
+
apiKey: exports_external.string().optional().describe("Optional API key for cloud Letta (not needed for self-hosted)"),
|
|
49928
|
+
userId: exports_external.string().optional(),
|
|
49929
|
+
projectId: exports_external.string().optional(),
|
|
49930
|
+
teamId: exports_external.string().optional(),
|
|
49931
|
+
orgId: exports_external.string().optional(),
|
|
49932
|
+
companyId: exports_external.string().optional(),
|
|
49933
|
+
agentPrefix: exports_external.string().optional().describe("Agent name prefix (default: opencode)"),
|
|
49934
|
+
llmModel: exports_external.string().optional().describe("LLM model for agent (e.g., openai/gpt-4.1)"),
|
|
49935
|
+
embeddingModel: exports_external.string().optional().describe("Embedding model for semantic search"),
|
|
49936
|
+
autoRehydrate: exports_external.boolean().default(true),
|
|
49937
|
+
rehydrateLayers: exports_external.array(MemoryLayerSchema).default(["user", "project", "team"])
|
|
49938
|
+
});
|
|
49640
49939
|
var CentralHubConfigSchema = exports_external.object({
|
|
49641
49940
|
url: exports_external.string().url(),
|
|
49642
49941
|
branch: exports_external.string().default("main"),
|
|
@@ -49740,22 +50039,12 @@ var OhMyOpenCodeConfigSchema = exports_external.object({
|
|
|
49740
50039
|
skills: SkillsConfigSchema.optional(),
|
|
49741
50040
|
ralph_loop: RalphLoopConfigSchema.optional(),
|
|
49742
50041
|
mem0: Mem0ConfigSchema.optional(),
|
|
49743
|
-
letta:
|
|
50042
|
+
letta: LettaConfigSchema.optional(),
|
|
49744
50043
|
knowledge_repo: KnowledgeRepoConfigSchema.optional(),
|
|
49745
50044
|
knowledge_provider: KnowledgeProviderConfigSchema.optional(),
|
|
49746
50045
|
openspec: OpenSpecConfigSchema.optional()
|
|
49747
50046
|
});
|
|
49748
50047
|
// src/plugin-config.ts
|
|
49749
|
-
function normalizeLettaToMem0(config3) {
|
|
49750
|
-
if (!config3.letta) {
|
|
49751
|
-
return config3;
|
|
49752
|
-
}
|
|
49753
|
-
const { letta, ...rest } = config3;
|
|
49754
|
-
if (config3.mem0) {
|
|
49755
|
-
return { ...rest, mem0: deepMerge(letta, config3.mem0) };
|
|
49756
|
-
}
|
|
49757
|
-
return { ...rest, mem0: letta };
|
|
49758
|
-
}
|
|
49759
50048
|
function loadConfigFromPath2(configPath, ctx) {
|
|
49760
50049
|
try {
|
|
49761
50050
|
if (fs9.existsSync(configPath)) {
|
|
@@ -49772,9 +50061,8 @@ function loadConfigFromPath2(configPath, ctx) {
|
|
|
49772
50061
|
});
|
|
49773
50062
|
return null;
|
|
49774
50063
|
}
|
|
49775
|
-
|
|
49776
|
-
|
|
49777
|
-
return normalized;
|
|
50064
|
+
log(`Config loaded from ${configPath}`, { agents: result.data.agents });
|
|
50065
|
+
return result.data;
|
|
49778
50066
|
}
|
|
49779
50067
|
} catch (err) {
|
|
49780
50068
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -49789,6 +50077,7 @@ function mergeConfigs(base, override) {
|
|
|
49789
50077
|
...override,
|
|
49790
50078
|
agents: deepMerge(base.agents, override.agents),
|
|
49791
50079
|
mem0: deepMerge(base.mem0, override.mem0),
|
|
50080
|
+
letta: deepMerge(base.letta, override.letta),
|
|
49792
50081
|
disabled_agents: [
|
|
49793
50082
|
...new Set([
|
|
49794
50083
|
...base.disabled_agents ?? [],
|
|
@@ -55444,16 +55733,30 @@ var OhMyOpenCodePlugin = async (ctx) => {
|
|
|
55444
55733
|
companyId: pluginConfig.mem0.companyId,
|
|
55445
55734
|
agentId: pluginConfig.mem0.agentId
|
|
55446
55735
|
}) : null;
|
|
55447
|
-
const
|
|
55736
|
+
const lettaAdapter = pluginConfig.letta?.enabled ? new LettaAdapter({
|
|
55737
|
+
enabled: true,
|
|
55738
|
+
endpoint: pluginConfig.letta.endpoint,
|
|
55739
|
+
apiKey: pluginConfig.letta.apiKey,
|
|
55740
|
+
userId: pluginConfig.letta.userId,
|
|
55741
|
+
projectId: pluginConfig.letta.projectId,
|
|
55742
|
+
teamId: pluginConfig.letta.teamId,
|
|
55743
|
+
orgId: pluginConfig.letta.orgId,
|
|
55744
|
+
companyId: pluginConfig.letta.companyId,
|
|
55745
|
+
agentPrefix: pluginConfig.letta.agentPrefix,
|
|
55746
|
+
llmModel: pluginConfig.letta.llmModel,
|
|
55747
|
+
embeddingModel: pluginConfig.letta.embeddingModel
|
|
55748
|
+
}) : null;
|
|
55749
|
+
const memoryAdapter = lettaAdapter ?? mem0Adapter;
|
|
55750
|
+
const memoryTools = memoryAdapter ? createMemoryTools(memoryAdapter) : {};
|
|
55448
55751
|
const knowledgeProviderRegistry = pluginConfig.knowledge_provider?.enabled ? await initializeKnowledgeProviderRegistry(pluginConfig, mem0Adapter) : null;
|
|
55449
55752
|
const knowledgeMonitor = isHookEnabled("knowledge-monitor") && pluginConfig.knowledge_repo?.enabled ? createKnowledgeMonitorHook(ctx.directory, {
|
|
55450
55753
|
enabled: true,
|
|
55451
55754
|
checkPreTool: true,
|
|
55452
55755
|
checkPostTool: false
|
|
55453
55756
|
}) : null;
|
|
55454
|
-
const memoryRehydration = isHookEnabled("memory-rehydration") &&
|
|
55757
|
+
const memoryRehydration = isHookEnabled("memory-rehydration") && memoryAdapter && (pluginConfig.letta?.autoRehydrate !== false || pluginConfig.mem0?.autoRehydrate !== false) ? createMemoryRehydrationHook(memoryAdapter, {
|
|
55455
55758
|
enabled: true,
|
|
55456
|
-
layers: pluginConfig.mem0?.rehydrateLayers
|
|
55759
|
+
layers: pluginConfig.letta?.rehydrateLayers ?? pluginConfig.mem0?.rehydrateLayers
|
|
55457
55760
|
}) : null;
|
|
55458
55761
|
const contextWindowMonitor = isHookEnabled("context-window-monitor") ? createContextWindowMonitorHook(ctx) : null;
|
|
55459
55762
|
const sessionRecovery = isHookEnabled("session-recovery") ? createSessionRecoveryHook(ctx, { experimental: pluginConfig.experimental }) : null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { createMemoryTools } from "./tools";
|
|
2
|
-
export type {
|
|
2
|
+
export type { MemoryAdapter, MemoryLayer, Memory, MemorySearchResult, MemoryStats } from "./types";
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type ToolDefinition } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function createMemoryTools(adapter:
|
|
2
|
+
import type { MemoryAdapter } from "./types";
|
|
3
|
+
export declare function createMemoryTools(adapter: MemoryAdapter): Record<string, ToolDefinition>;
|
|
@@ -1,6 +1,44 @@
|
|
|
1
|
-
import type { Mem0Adapter } from "../../features/mem0-memory/adapter";
|
|
2
1
|
import type { MemoryLayer } from "../../features/mem0-memory/types";
|
|
3
|
-
export interface
|
|
4
|
-
|
|
2
|
+
export interface Memory {
|
|
3
|
+
id: string;
|
|
4
|
+
content: string;
|
|
5
|
+
layer: MemoryLayer;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
createdAt: string;
|
|
8
|
+
updatedAt?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface MemorySearchResult {
|
|
11
|
+
memory: Memory;
|
|
12
|
+
score: number;
|
|
13
|
+
}
|
|
14
|
+
export interface AddMemoryInput {
|
|
15
|
+
content: string;
|
|
16
|
+
layer: MemoryLayer;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
export interface SearchMemoryInput {
|
|
20
|
+
query: string;
|
|
21
|
+
layer?: MemoryLayer | MemoryLayer[];
|
|
22
|
+
limit?: number;
|
|
23
|
+
threshold?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface UpdateMemoryInput {
|
|
26
|
+
id: string;
|
|
27
|
+
content?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface MemoryStats {
|
|
31
|
+
totalMemories: number;
|
|
32
|
+
byLayer: Record<MemoryLayer, number>;
|
|
33
|
+
}
|
|
34
|
+
export interface MemoryAdapter {
|
|
35
|
+
add(input: AddMemoryInput): Promise<Memory>;
|
|
36
|
+
search(input: SearchMemoryInput): Promise<MemorySearchResult[]>;
|
|
37
|
+
get(id: string): Promise<Memory | null>;
|
|
38
|
+
update(input: UpdateMemoryInput): Promise<Memory>;
|
|
39
|
+
delete(id: string): Promise<void>;
|
|
40
|
+
getAll(layer?: MemoryLayer): Promise<Memory[]>;
|
|
41
|
+
getStats(): Promise<MemoryStats>;
|
|
42
|
+
isAvailable?(): Promise<boolean>;
|
|
5
43
|
}
|
|
6
44
|
export type { MemoryLayer };
|