@poncho-ai/harness 0.25.0 → 0.26.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
package/src/config.ts CHANGED
@@ -42,6 +42,10 @@ export type BuiltInToolToggles = {
42
42
  edit_file?: boolean;
43
43
  delete_file?: boolean;
44
44
  delete_directory?: boolean;
45
+ todo_list?: boolean;
46
+ todo_add?: boolean;
47
+ todo_update?: boolean;
48
+ todo_remove?: boolean;
45
49
  };
46
50
 
47
51
  export interface MessagingChannelConfig {
package/src/harness.ts CHANGED
@@ -16,13 +16,14 @@ import { getTextContent } from "@poncho-ai/sdk";
16
16
  import type { UploadStore } from "./upload-store.js";
17
17
  import { PONCHO_UPLOAD_SCHEME, deriveUploadKey } from "./upload-store.js";
18
18
  import { parseAgentFile, parseAgentMarkdown, renderAgentPrompt, type ParsedAgent, type AgentFrontmatter } from "./agent-parser.js";
19
- import { loadPonchoConfig, resolveMemoryConfig, type PonchoConfig, type ToolAccess, type BuiltInToolToggles } from "./config.js";
19
+ import { loadPonchoConfig, resolveMemoryConfig, resolveStateConfig, type PonchoConfig, type ToolAccess, type BuiltInToolToggles } from "./config.js";
20
20
  import { createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createWriteTool, ponchoDocsTool } from "./default-tools.js";
21
21
  import {
22
22
  createMemoryStore,
23
23
  createMemoryTools,
24
24
  type MemoryStore,
25
25
  } from "./memory.js";
26
+ import { createTodoStore, createTodoTools, type TodoItem, type TodoStore } from "./todo-tools.js";
26
27
  import { LocalMcpBridge } from "./mcp.js";
27
28
  import { createModelProvider, getModelContextWindow, type ModelProviderFactory, type ProviderConfig } from "./model-factory.js";
28
29
  import { buildSkillContextWindow, loadSkillMetadata } from "./skill-context.js";
@@ -550,6 +551,7 @@ export class AgentHarness {
550
551
  readonly uploadStore?: UploadStore;
551
552
  private skillContextWindow = "";
552
553
  private memoryStore?: MemoryStore;
554
+ private todoStore?: TodoStore;
553
555
  private loadedConfig?: PonchoConfig;
554
556
  private loadedSkills: SkillMetadata[] = [];
555
557
  private skillFingerprint = "";
@@ -678,6 +680,11 @@ export class AgentHarness {
678
680
  return this.parsedAgent?.frontmatter;
679
681
  }
680
682
 
683
+ async getTodos(conversationId: string): Promise<TodoItem[]> {
684
+ if (!this.todoStore) return [];
685
+ return this.todoStore.get(conversationId);
686
+ }
687
+
681
688
  private listActiveSkills(): string[] {
682
689
  return [...this.activeSkillNames].sort();
683
690
  }
@@ -1008,8 +1015,9 @@ export class AgentHarness {
1008
1015
  this.skillContextWindow = buildSkillContextWindow(skillMetadata);
1009
1016
  this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
1010
1017
  this.registerSkillTools(skillMetadata);
1018
+ const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
1019
+
1011
1020
  if (memoryConfig?.enabled) {
1012
- const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
1013
1021
  this.memoryStore = createMemoryStore(
1014
1022
  agentId,
1015
1023
  memoryConfig,
@@ -1022,6 +1030,14 @@ export class AgentHarness {
1022
1030
  );
1023
1031
  }
1024
1032
 
1033
+ const stateConfig = resolveStateConfig(config);
1034
+ this.todoStore = createTodoStore(agentId, stateConfig, { workingDir: this.workingDir });
1035
+ for (const tool of createTodoTools(this.todoStore)) {
1036
+ if (this.isToolEnabled(tool.name)) {
1037
+ this.registerIfMissing(tool);
1038
+ }
1039
+ }
1040
+
1025
1041
  if (config?.browser) {
1026
1042
  await this.initBrowserTools(config)
1027
1043
  .catch((e) => {
@@ -0,0 +1,206 @@
1
+ import type { StateConfig } from "./state.js";
2
+
3
+ /**
4
+ * Minimal raw key-value interface shared by MemoryStore, TodoStore, and any
5
+ * future stores that sit on top of the same user-configured backend.
6
+ */
7
+ export interface RawKVStore {
8
+ get(key: string): Promise<string | undefined>;
9
+ set(key: string, value: string): Promise<void>;
10
+ setWithTtl(key: string, value: string, ttlSeconds: number): Promise<void>;
11
+ }
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Upstash
15
+ // ---------------------------------------------------------------------------
16
+
17
+ class UpstashKVStore implements RawKVStore {
18
+ private readonly baseUrl: string;
19
+ private readonly token: string;
20
+
21
+ constructor(baseUrl: string, token: string) {
22
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
23
+ this.token = token;
24
+ }
25
+
26
+ private headers(): HeadersInit {
27
+ return { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" };
28
+ }
29
+
30
+ async get(key: string): Promise<string | undefined> {
31
+ const response = await fetch(`${this.baseUrl}/get/${encodeURIComponent(key)}`, {
32
+ method: "POST",
33
+ headers: this.headers(),
34
+ });
35
+ if (!response.ok) return undefined;
36
+ const payload = (await response.json()) as { result?: string | null };
37
+ return payload.result ?? undefined;
38
+ }
39
+
40
+ async set(key: string, value: string): Promise<void> {
41
+ await fetch(
42
+ `${this.baseUrl}/set/${encodeURIComponent(key)}/${encodeURIComponent(value)}`,
43
+ { method: "POST", headers: this.headers() },
44
+ );
45
+ }
46
+
47
+ async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
48
+ await fetch(
49
+ `${this.baseUrl}/setex/${encodeURIComponent(key)}/${Math.max(1, ttl)}/${encodeURIComponent(value)}`,
50
+ { method: "POST", headers: this.headers() },
51
+ );
52
+ }
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Redis
57
+ // ---------------------------------------------------------------------------
58
+
59
+ class RedisKVStore implements RawKVStore {
60
+ private readonly clientPromise: Promise<
61
+ | {
62
+ get: (key: string) => Promise<string | null>;
63
+ set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
64
+ }
65
+ | undefined
66
+ >;
67
+
68
+ constructor(url: string) {
69
+ this.clientPromise = (async () => {
70
+ try {
71
+ const redisModule = (await import("redis")) as unknown as {
72
+ createClient: (args: { url: string }) => {
73
+ connect: () => Promise<unknown>;
74
+ get: (key: string) => Promise<string | null>;
75
+ set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
76
+ };
77
+ };
78
+ const client = redisModule.createClient({ url });
79
+ await client.connect();
80
+ return client;
81
+ } catch {
82
+ return undefined;
83
+ }
84
+ })();
85
+ }
86
+
87
+ async get(key: string): Promise<string | undefined> {
88
+ const client = await this.clientPromise;
89
+ if (!client) throw new Error("Redis unavailable");
90
+ const value = await client.get(key);
91
+ return value ?? undefined;
92
+ }
93
+
94
+ async set(key: string, value: string): Promise<void> {
95
+ const client = await this.clientPromise;
96
+ if (!client) throw new Error("Redis unavailable");
97
+ await client.set(key, value);
98
+ }
99
+
100
+ async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
101
+ const client = await this.clientPromise;
102
+ if (!client) throw new Error("Redis unavailable");
103
+ await client.set(key, value, { EX: Math.max(1, ttl) });
104
+ }
105
+ }
106
+
107
+ // ---------------------------------------------------------------------------
108
+ // DynamoDB
109
+ // ---------------------------------------------------------------------------
110
+
111
+ class DynamoDbKVStore implements RawKVStore {
112
+ private readonly table: string;
113
+ private readonly clientPromise: Promise<
114
+ | {
115
+ send: (command: unknown) => Promise<unknown>;
116
+ GetItemCommand: new (input: unknown) => unknown;
117
+ PutItemCommand: new (input: unknown) => unknown;
118
+ }
119
+ | undefined
120
+ >;
121
+
122
+ constructor(table: string, region?: string) {
123
+ this.table = table;
124
+ this.clientPromise = (async () => {
125
+ try {
126
+ const module = (await import("@aws-sdk/client-dynamodb")) as {
127
+ DynamoDBClient: new (input: { region?: string }) => {
128
+ send: (command: unknown) => Promise<unknown>;
129
+ };
130
+ GetItemCommand: new (input: unknown) => unknown;
131
+ PutItemCommand: new (input: unknown) => unknown;
132
+ };
133
+ const client = new module.DynamoDBClient({ region });
134
+ return {
135
+ send: client.send.bind(client),
136
+ GetItemCommand: module.GetItemCommand,
137
+ PutItemCommand: module.PutItemCommand,
138
+ };
139
+ } catch {
140
+ return undefined;
141
+ }
142
+ })();
143
+ }
144
+
145
+ async get(key: string): Promise<string | undefined> {
146
+ const client = await this.clientPromise;
147
+ if (!client) throw new Error("DynamoDB unavailable");
148
+ const result = (await client.send(
149
+ new client.GetItemCommand({ TableName: this.table, Key: { runId: { S: key } } }),
150
+ )) as { Item?: { value?: { S?: string } } };
151
+ return result.Item?.value?.S;
152
+ }
153
+
154
+ async set(key: string, value: string): Promise<void> {
155
+ const client = await this.clientPromise;
156
+ if (!client) throw new Error("DynamoDB unavailable");
157
+ await client.send(
158
+ new client.PutItemCommand({
159
+ TableName: this.table,
160
+ Item: { runId: { S: key }, value: { S: value } },
161
+ }),
162
+ );
163
+ }
164
+
165
+ async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
166
+ const client = await this.clientPromise;
167
+ if (!client) throw new Error("DynamoDB unavailable");
168
+ const ttlEpoch = Math.floor(Date.now() / 1000) + Math.max(1, ttl);
169
+ await client.send(
170
+ new client.PutItemCommand({
171
+ TableName: this.table,
172
+ Item: { runId: { S: key }, value: { S: value }, ttl: { N: String(ttlEpoch) } },
173
+ }),
174
+ );
175
+ }
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Factory — resolves the user's storage config into a RawKVStore, or
180
+ // undefined when the provider is "local" or "memory" (handled by callers).
181
+ // ---------------------------------------------------------------------------
182
+
183
+ export const createRawKVStore = (config?: StateConfig): RawKVStore | undefined => {
184
+ const provider = config?.provider ?? "local";
185
+
186
+ if (provider === "upstash") {
187
+ const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
188
+ const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
189
+ const url = process.env[urlEnv] ?? "";
190
+ const token = process.env[tokenEnv] ?? "";
191
+ if (url && token) return new UpstashKVStore(url, token);
192
+ }
193
+
194
+ if (provider === "redis") {
195
+ const urlEnv = config?.urlEnv ?? "REDIS_URL";
196
+ const url = process.env[urlEnv] ?? "";
197
+ if (url) return new RedisKVStore(url);
198
+ }
199
+
200
+ if (provider === "dynamodb") {
201
+ const table = config?.table ?? process.env.PONCHO_DYNAMODB_TABLE ?? "";
202
+ if (table) return new DynamoDbKVStore(table, config?.region);
203
+ }
204
+
205
+ return undefined;
206
+ };
package/src/memory.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  slugifyStorageComponent,
9
9
  STORAGE_SCHEMA_VERSION,
10
10
  } from "./agent-identity.js";
11
+ import { createRawKVStore, type RawKVStore } from "./kv-store.js";
11
12
 
12
13
  export interface MainMemory {
13
14
  content: string;
@@ -168,25 +169,23 @@ class FileMainMemoryStore implements MemoryStore {
168
169
  }
169
170
  }
170
171
 
171
- abstract class KeyValueMainMemoryStoreBase implements MemoryStore {
172
- protected readonly ttl?: number;
173
- protected readonly memoryFallback: InMemoryMemoryStore;
172
+ class KVBackedMemoryStore implements MemoryStore {
173
+ private readonly kv: RawKVStore;
174
+ private readonly storageKey: string;
175
+ private readonly ttl?: number;
176
+ private readonly memoryFallback: InMemoryMemoryStore;
174
177
 
175
- constructor(ttl?: number) {
178
+ constructor(kv: RawKVStore, storageKey: string, ttl?: number) {
179
+ this.kv = kv;
180
+ this.storageKey = storageKey;
176
181
  this.ttl = ttl;
177
182
  this.memoryFallback = new InMemoryMemoryStore(ttl);
178
183
  }
179
184
 
180
- protected abstract getRaw(key: string): Promise<string | undefined>;
181
- protected abstract setRaw(key: string, value: string): Promise<void>;
182
- protected abstract setRawWithTtl(key: string, value: string, ttl: number): Promise<void>;
183
-
184
- protected async readPayload(key: string): Promise<MainMemoryPayload> {
185
+ private async readPayload(): Promise<MainMemoryPayload> {
185
186
  try {
186
- const raw = await this.getRaw(key);
187
- if (!raw) {
188
- return { main: { ...DEFAULT_MAIN_MEMORY } };
189
- }
187
+ const raw = await this.kv.get(this.storageKey);
188
+ if (!raw) return { main: { ...DEFAULT_MAIN_MEMORY } };
190
189
  const parsed = JSON.parse(raw) as MainMemoryPayload;
191
190
  const content = typeof parsed.main?.content === "string" ? parsed.main.content : "";
192
191
  const updatedAt = typeof parsed.main?.updatedAt === "number" ? parsed.main.updatedAt : 0;
@@ -197,259 +196,30 @@ abstract class KeyValueMainMemoryStoreBase implements MemoryStore {
197
196
  }
198
197
  }
199
198
 
200
- protected async writePayload(key: string, payload: MainMemoryPayload): Promise<void> {
199
+ private async writePayload(payload: MainMemoryPayload): Promise<void> {
201
200
  try {
202
201
  const serialized = JSON.stringify(payload);
203
202
  if (typeof this.ttl === "number") {
204
- await this.setRawWithTtl(key, serialized, Math.max(1, this.ttl));
203
+ await this.kv.setWithTtl(this.storageKey, serialized, Math.max(1, this.ttl));
205
204
  } else {
206
- await this.setRaw(key, serialized);
205
+ await this.kv.set(this.storageKey, serialized);
207
206
  }
208
207
  } catch {
209
- await this.memoryFallback.updateMainMemory({
210
- content: payload.main.content,
211
- });
208
+ await this.memoryFallback.updateMainMemory({ content: payload.main.content });
212
209
  }
213
210
  }
214
211
 
215
212
  async getMainMemory(): Promise<MainMemory> {
216
- const payload = await this.readPayload(this.key());
213
+ const payload = await this.readPayload();
217
214
  return payload.main;
218
215
  }
219
216
 
220
217
  async updateMainMemory(input: { content: string }): Promise<MainMemory> {
221
- const key = this.key();
222
- const payload = await this.readPayload(key);
223
- payload.main = {
224
- content: input.content.trim(),
225
- updatedAt: Date.now(),
226
- };
227
- await this.writePayload(key, payload);
218
+ const payload = await this.readPayload();
219
+ payload.main = { content: input.content.trim(), updatedAt: Date.now() };
220
+ await this.writePayload(payload);
228
221
  return payload.main;
229
222
  }
230
-
231
- protected abstract key(): string;
232
- }
233
-
234
- class UpstashMemoryStore extends KeyValueMainMemoryStoreBase {
235
- private readonly baseUrl: string;
236
- private readonly token: string;
237
- private readonly storageKey: string;
238
-
239
- constructor(options: {
240
- baseUrl: string;
241
- token: string;
242
- storageKey: string;
243
- ttl?: number;
244
- }) {
245
- super(options.ttl);
246
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
247
- this.token = options.token;
248
- this.storageKey = options.storageKey;
249
- }
250
-
251
- protected key(): string {
252
- return this.storageKey;
253
- }
254
-
255
- private headers(): HeadersInit {
256
- return {
257
- Authorization: `Bearer ${this.token}`,
258
- "Content-Type": "application/json",
259
- };
260
- }
261
-
262
- protected async getRaw(key: string): Promise<string | undefined> {
263
- const response = await fetch(`${this.baseUrl}/get/${encodeURIComponent(key)}`, {
264
- method: "POST",
265
- headers: this.headers(),
266
- });
267
- if (!response.ok) {
268
- return undefined;
269
- }
270
- const payload = (await response.json()) as { result?: string | null };
271
- return payload.result ?? undefined;
272
- }
273
-
274
- protected async setRaw(key: string, value: string): Promise<void> {
275
- await fetch(
276
- `${this.baseUrl}/set/${encodeURIComponent(key)}/${encodeURIComponent(value)}`,
277
- { method: "POST", headers: this.headers() },
278
- );
279
- }
280
-
281
- protected async setRawWithTtl(key: string, value: string, ttl: number): Promise<void> {
282
- await fetch(
283
- `${this.baseUrl}/setex/${encodeURIComponent(key)}/${Math.max(1, ttl)}/${encodeURIComponent(
284
- value,
285
- )}`,
286
- { method: "POST", headers: this.headers() },
287
- );
288
- }
289
- }
290
-
291
- class RedisMemoryStore extends KeyValueMainMemoryStoreBase {
292
- private readonly storageKey: string;
293
- private readonly clientPromise: Promise<
294
- | {
295
- get: (key: string) => Promise<string | null>;
296
- set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
297
- }
298
- | undefined
299
- >;
300
-
301
- constructor(options: {
302
- url: string;
303
- storageKey: string;
304
- ttl?: number;
305
- }) {
306
- super(options.ttl);
307
- this.storageKey = options.storageKey;
308
- this.clientPromise = (async () => {
309
- try {
310
- const redisModule = (await import("redis")) as unknown as {
311
- createClient: (args: { url: string }) => {
312
- connect: () => Promise<unknown>;
313
- get: (key: string) => Promise<string | null>;
314
- set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
315
- };
316
- };
317
- const client = redisModule.createClient({ url: options.url });
318
- await client.connect();
319
- return client;
320
- } catch {
321
- return undefined;
322
- }
323
- })();
324
- }
325
-
326
- protected key(): string {
327
- return this.storageKey;
328
- }
329
-
330
- protected async getRaw(key: string): Promise<string | undefined> {
331
- const client = await this.clientPromise;
332
- if (!client) {
333
- throw new Error("Redis unavailable");
334
- }
335
- const value = await client.get(key);
336
- return value ?? undefined;
337
- }
338
-
339
- protected async setRaw(key: string, value: string): Promise<void> {
340
- const client = await this.clientPromise;
341
- if (!client) {
342
- throw new Error("Redis unavailable");
343
- }
344
- await client.set(key, value);
345
- }
346
-
347
- protected async setRawWithTtl(key: string, value: string, ttl: number): Promise<void> {
348
- const client = await this.clientPromise;
349
- if (!client) {
350
- throw new Error("Redis unavailable");
351
- }
352
- await client.set(key, value, { EX: Math.max(1, ttl) });
353
- }
354
- }
355
-
356
- class DynamoDbMemoryStore extends KeyValueMainMemoryStoreBase {
357
- private readonly storageKey: string;
358
- private readonly table: string;
359
- private readonly clientPromise: Promise<
360
- | {
361
- send: (command: unknown) => Promise<unknown>;
362
- GetItemCommand: new (input: unknown) => unknown;
363
- PutItemCommand: new (input: unknown) => unknown;
364
- }
365
- | undefined
366
- >;
367
-
368
- constructor(options: {
369
- table: string;
370
- storageKey: string;
371
- region?: string;
372
- ttl?: number;
373
- }) {
374
- super(options.ttl);
375
- this.storageKey = options.storageKey;
376
- this.table = options.table;
377
- this.clientPromise = (async () => {
378
- try {
379
- const module = (await import("@aws-sdk/client-dynamodb")) as {
380
- DynamoDBClient: new (input: { region?: string }) => {
381
- send: (command: unknown) => Promise<unknown>;
382
- };
383
- GetItemCommand: new (input: unknown) => unknown;
384
- PutItemCommand: new (input: unknown) => unknown;
385
- };
386
- const client = new module.DynamoDBClient({ region: options.region });
387
- return {
388
- send: client.send.bind(client),
389
- GetItemCommand: module.GetItemCommand,
390
- PutItemCommand: module.PutItemCommand,
391
- };
392
- } catch {
393
- return undefined;
394
- }
395
- })();
396
- }
397
-
398
- protected key(): string {
399
- return this.storageKey;
400
- }
401
-
402
- protected async getRaw(key: string): Promise<string | undefined> {
403
- const client = await this.clientPromise;
404
- if (!client) {
405
- throw new Error("DynamoDB unavailable");
406
- }
407
- const result = (await client.send(
408
- new client.GetItemCommand({
409
- TableName: this.table,
410
- Key: { runId: { S: key } },
411
- }),
412
- )) as {
413
- Item?: {
414
- value?: { S?: string };
415
- };
416
- };
417
- return result.Item?.value?.S;
418
- }
419
-
420
- protected async setRaw(key: string, value: string): Promise<void> {
421
- const client = await this.clientPromise;
422
- if (!client) {
423
- throw new Error("DynamoDB unavailable");
424
- }
425
- await client.send(
426
- new client.PutItemCommand({
427
- TableName: this.table,
428
- Item: {
429
- runId: { S: key },
430
- value: { S: value },
431
- },
432
- }),
433
- );
434
- }
435
-
436
- protected async setRawWithTtl(key: string, value: string, ttl: number): Promise<void> {
437
- const client = await this.clientPromise;
438
- if (!client) {
439
- throw new Error("DynamoDB unavailable");
440
- }
441
- const ttlEpoch = Math.floor(Date.now() / 1000) + Math.max(1, ttl);
442
- await client.send(
443
- new client.PutItemCommand({
444
- TableName: this.table,
445
- Item: {
446
- runId: { S: key },
447
- value: { S: value },
448
- ttl: { N: String(ttlEpoch) },
449
- },
450
- }),
451
- );
452
- }
453
223
  }
454
224
 
455
225
  export const createMemoryStore = (
@@ -459,54 +229,19 @@ export const createMemoryStore = (
459
229
  ): MemoryStore => {
460
230
  const provider = config?.provider ?? "local";
461
231
  const ttl = config?.ttl;
462
- const storageKey = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(
463
- agentId,
464
- )}:memory:main`;
465
232
  const workingDir = options?.workingDir ?? process.cwd();
233
+
466
234
  if (provider === "local") {
467
235
  return new FileMainMemoryStore(workingDir, ttl);
468
236
  }
469
237
  if (provider === "memory") {
470
238
  return new InMemoryMemoryStore(ttl);
471
239
  }
472
- if (provider === "upstash") {
473
- const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
474
- const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
475
- const url = process.env[urlEnv] ?? "";
476
- const token = process.env[tokenEnv] ?? "";
477
- if (url && token) {
478
- return new UpstashMemoryStore({
479
- baseUrl: url,
480
- token,
481
- storageKey,
482
- ttl,
483
- });
484
- }
485
- return new InMemoryMemoryStore(ttl);
486
- }
487
- if (provider === "redis") {
488
- const urlEnv = config?.urlEnv ?? "REDIS_URL";
489
- const url = process.env[urlEnv] ?? "";
490
- if (url) {
491
- return new RedisMemoryStore({
492
- url,
493
- storageKey,
494
- ttl,
495
- });
496
- }
497
- return new InMemoryMemoryStore(ttl);
498
- }
499
- if (provider === "dynamodb") {
500
- const table = config?.table ?? process.env.PONCHO_DYNAMODB_TABLE ?? "";
501
- if (table) {
502
- return new DynamoDbMemoryStore({
503
- table,
504
- storageKey,
505
- region: config?.region,
506
- ttl,
507
- });
508
- }
509
- return new InMemoryMemoryStore(ttl);
240
+
241
+ const kv = createRawKVStore(config);
242
+ if (kv) {
243
+ const storageKey = `poncho:${STORAGE_SCHEMA_VERSION}:${slugifyStorageComponent(agentId)}:memory:main`;
244
+ return new KVBackedMemoryStore(kv, storageKey, ttl);
510
245
  }
511
246
  return new InMemoryMemoryStore(ttl);
512
247
  };