@poncho-ai/harness 0.34.1 → 0.36.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.
Files changed (64) hide show
  1. package/.turbo/turbo-build.log +12 -11
  2. package/.turbo/turbo-lint.log +6 -0
  3. package/.turbo/turbo-test.log +27100 -0
  4. package/CHANGELOG.md +37 -0
  5. package/dist/chunk-MCKGQKYU.js +15 -0
  6. package/dist/dist-3KMQR4IO.js +27092 -0
  7. package/dist/index.d.ts +553 -29
  8. package/dist/index.js +3132 -1902
  9. package/dist/isolate-5MISBSUK.js +733 -0
  10. package/dist/isolate-5R6762YA.js +605 -0
  11. package/dist/isolate-KUZ5NOPG.js +727 -0
  12. package/dist/isolate-LOL3T7RA.js +729 -0
  13. package/dist/isolate-N22X4TCE.js +740 -0
  14. package/dist/isolate-T7WXM7IL.js +1490 -0
  15. package/dist/isolate-TCWTUVG4.js +1532 -0
  16. package/dist/isolate-WFOLANOB.js +768 -0
  17. package/package.json +24 -4
  18. package/scripts/migrate-to-engine.mjs +556 -0
  19. package/src/config.ts +112 -1
  20. package/src/harness.ts +282 -91
  21. package/src/index.ts +7 -0
  22. package/src/isolate/bindings.ts +206 -0
  23. package/src/isolate/bundler.ts +179 -0
  24. package/src/isolate/index.ts +10 -0
  25. package/src/isolate/polyfills.ts +796 -0
  26. package/src/isolate/run-code-tool.ts +220 -0
  27. package/src/isolate/runtime.ts +286 -0
  28. package/src/isolate/type-stubs.ts +196 -0
  29. package/src/mcp.ts +140 -9
  30. package/src/memory.ts +142 -191
  31. package/src/reminder-store.ts +7 -235
  32. package/src/reminder-tools.ts +15 -2
  33. package/src/secrets-store.ts +163 -0
  34. package/src/state.ts +22 -1291
  35. package/src/storage/engine.ts +106 -0
  36. package/src/storage/index.ts +59 -0
  37. package/src/storage/memory-engine.ts +588 -0
  38. package/src/storage/postgres-engine.ts +139 -0
  39. package/src/storage/schema.ts +145 -0
  40. package/src/storage/sql-dialect.ts +963 -0
  41. package/src/storage/sqlite-engine.ts +99 -0
  42. package/src/storage/store-adapters.ts +100 -0
  43. package/src/subagent-manager.ts +1 -0
  44. package/src/subagent-tools.ts +1 -0
  45. package/src/telemetry.ts +5 -1
  46. package/src/tenant-token.ts +42 -0
  47. package/src/todo-tools.ts +1 -136
  48. package/src/upload-store.ts +1 -0
  49. package/src/vfs/bash-manager.ts +120 -0
  50. package/src/vfs/bash-tool.ts +59 -0
  51. package/src/vfs/create-bash-fs.ts +32 -0
  52. package/src/vfs/edit-file-tool.ts +72 -0
  53. package/src/vfs/index.ts +5 -0
  54. package/src/vfs/poncho-fs-adapter.ts +267 -0
  55. package/src/vfs/protected-fs.ts +177 -0
  56. package/src/vfs/read-file-tool.ts +103 -0
  57. package/src/vfs/write-file-tool.ts +49 -0
  58. package/test/harness.test.ts +30 -36
  59. package/test/isolate-vfs.test.ts +453 -0
  60. package/test/isolate.test.ts +252 -0
  61. package/test/state.test.ts +4 -27
  62. package/test/storage-engine.test.ts +250 -0
  63. package/test/vfs.test.ts +242 -0
  64. package/src/kv-store.ts +0 -216
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { LanguageModel } from 'ai';
2
2
  import * as _poncho_ai_sdk from '@poncho-ai/sdk';
3
- import { Message, ToolDefinition, RunResult, AgentFailure, ToolContext, RunInput, AgentEvent, JsonSchema } from '@poncho-ai/sdk';
3
+ import { Message, ToolContext, ToolDefinition, JsonSchema, RunResult, AgentFailure, RunInput, AgentEvent } from '@poncho-ai/sdk';
4
4
  export { ToolDefinition, defineTool } from '@poncho-ai/sdk';
5
5
  import { z } from 'zod';
6
+ import { IFileSystem, BufferEncoding, FsStat, FileContent, MkdirOptions, RmOptions, CpOptions, Bash } from 'just-bash';
6
7
 
7
8
  interface AgentModelConfig {
8
9
  provider: string;
@@ -188,40 +189,25 @@ interface Conversation {
188
189
  subagentCallbackCount?: number;
189
190
  runningCallbackSince?: number;
190
191
  lastActivityAt?: number;
191
- /** Harness-internal message chain preserved across continuation runs.
192
- * Cleared when a run completes without continuation. */
193
192
  _continuationMessages?: Message[];
194
- /** Number of continuation pickups for the current multi-step run.
195
- * Reset when a run completes without continuation. Used to enforce
196
- * a maximum continuation count across all entry points. */
197
193
  _continuationCount?: number;
198
- /** Full structured message chain from the last harness run, including
199
- * tool-call and tool-result messages the model needs for context.
200
- * Unlike `_continuationMessages`, this is always set after a run
201
- * and does NOT signal that a continuation is pending. */
202
194
  _harnessMessages?: Message[];
203
- /** Archived full-fidelity tool results keyed by toolResultId. */
204
195
  _toolResultArchive?: Record<string, ArchivedToolResult$1>;
205
196
  createdAt: number;
206
197
  updatedAt: number;
207
198
  }
208
199
  interface ConversationStore {
209
- list(ownerId?: string): Promise<Conversation[]>;
210
- listSummaries(ownerId?: string): Promise<ConversationSummary[]>;
200
+ list(ownerId?: string, tenantId?: string | null): Promise<Conversation[]>;
201
+ listSummaries(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
211
202
  get(conversationId: string): Promise<Conversation | undefined>;
212
- create(ownerId?: string, title?: string): Promise<Conversation>;
203
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
213
204
  update(conversation: Conversation): Promise<void>;
214
205
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
215
206
  delete(conversationId: string): Promise<boolean>;
216
207
  appendSubagentResult(conversationId: string, result: PendingSubagentResult): Promise<void>;
217
- /**
218
- * Atomically clear `runningCallbackSince` without clobbering other fields.
219
- * Returns the conversation as it exists after the clear (with current
220
- * `pendingSubagentResults`).
221
- */
222
208
  clearCallbackLock(conversationId: string): Promise<Conversation | undefined>;
223
209
  }
224
- type StateProviderName = "local" | "memory" | "redis" | "upstash" | "dynamodb";
210
+ type StateProviderName = "local" | "memory" | "sqlite" | "postgresql" | "redis" | "upstash" | "dynamodb";
225
211
  interface StateConfig {
226
212
  provider?: StateProviderName;
227
213
  ttl?: number;
@@ -245,10 +231,10 @@ declare class InMemoryConversationStore implements ConversationStore {
245
231
  constructor(ttlSeconds?: number);
246
232
  private isExpired;
247
233
  private purgeExpired;
248
- list(ownerId?: string): Promise<Conversation[]>;
249
- listSummaries(ownerId?: string): Promise<ConversationSummary[]>;
234
+ list(ownerId?: string, tenantId?: string | null): Promise<Conversation[]>;
235
+ listSummaries(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
250
236
  get(conversationId: string): Promise<Conversation | undefined>;
251
- create(ownerId?: string, title?: string): Promise<Conversation>;
237
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
252
238
  update(conversation: Conversation): Promise<void>;
253
239
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
254
240
  delete(conversationId: string): Promise<boolean>;
@@ -261,6 +247,7 @@ type ConversationSummary = {
261
247
  updatedAt: number;
262
248
  createdAt?: number;
263
249
  ownerId: string;
250
+ tenantId?: string | null;
264
251
  parentConversationId?: string;
265
252
  messageCount?: number;
266
253
  hasPendingApprovals?: boolean;
@@ -270,11 +257,11 @@ type ConversationSummary = {
270
257
  platformThreadId: string;
271
258
  };
272
259
  };
273
- declare const createStateStore: (config?: StateConfig, options?: {
260
+ declare const createStateStore: (config?: StateConfig, _options?: {
274
261
  workingDir?: string;
275
262
  agentId?: string;
276
263
  }) => StateStore;
277
- declare const createConversationStore: (config?: StateConfig, options?: {
264
+ declare const createConversationStore: (config?: StateConfig, _options?: {
278
265
  workingDir?: string;
279
266
  agentId?: string;
280
267
  }) => ConversationStore;
@@ -301,8 +288,9 @@ interface MemoryStore {
301
288
  }
302
289
  declare const createMemoryStore: (agentId: string, config?: MemoryConfig, options?: {
303
290
  workingDir?: string;
291
+ tenantId?: string;
304
292
  }) => MemoryStore;
305
- declare const createMemoryTools: (store: MemoryStore, options?: {
293
+ declare const createMemoryTools: (store: MemoryStore | ((context: ToolContext) => MemoryStore), options?: {
306
294
  maxRecallConversations?: number;
307
295
  }) => ToolDefinition[];
308
296
 
@@ -328,10 +316,24 @@ declare class LocalMcpBridge {
328
316
  private readonly toolCatalog;
329
317
  private readonly unavailableServers;
330
318
  private readonly authFailedServers;
319
+ private envResolver?;
320
+ /**
321
+ * Set a resolver for per-tenant env vars (e.g. MCP auth tokens).
322
+ * Called by the harness after creating the secrets store.
323
+ */
324
+ setEnvResolver(resolver: (tenantId: string | undefined, envName: string) => Promise<string | undefined>): void;
331
325
  constructor(config: McpConfig | undefined);
332
326
  private getServerName;
333
327
  private log;
328
+ /** Set of servers where discovery was deferred (no default token, has env resolver). */
329
+ private readonly deferredDiscoveryServers;
334
330
  discoverTools(): Promise<void>;
331
+ /**
332
+ * Run deferred discovery and return ToolDefinitions for all newly discovered tools.
333
+ * Call this during run() so the tools are available to the model immediately.
334
+ */
335
+ discoverAndLoadDeferred(tenantId: string): Promise<ToolDefinition[]>;
336
+ private tryDeferredDiscovery;
335
337
  startLocalServers(): Promise<void>;
336
338
  stopLocalServers(): Promise<void>;
337
339
  listServers(): RemoteMcpServerConfig[];
@@ -344,12 +346,17 @@ declare class LocalMcpBridge {
344
346
  toSerializableConfig(): McpConfig;
345
347
  getLocalServers(): never[];
346
348
  listDiscoveredTools(serverName?: string): string[];
349
+ hasDeferredServers(): boolean;
350
+ /**
351
+ * Return ToolDefinitions for catalog tools not already registered in the dispatcher.
352
+ */
353
+ getUnregisteredTools(registeredNames: Set<string>): ToolDefinition[];
347
354
  loadTools(requestedPatterns: string[]): Promise<ToolDefinition[]>;
348
355
  private toToolDefinitions;
349
356
  }
350
357
 
351
358
  interface StorageConfig {
352
- provider?: "local" | "memory" | "redis" | "upstash" | "dynamodb";
359
+ provider?: "local" | "memory" | "sqlite" | "postgresql" | "redis" | "upstash" | "dynamodb";
353
360
  urlEnv?: string;
354
361
  tokenEnv?: string;
355
362
  table?: string;
@@ -362,6 +369,10 @@ interface StorageConfig {
362
369
  enabled?: boolean;
363
370
  maxRecallConversations?: number;
364
371
  };
372
+ limits?: {
373
+ maxFileSize?: number;
374
+ maxTotalStorage?: number;
375
+ };
365
376
  }
366
377
  interface UploadsConfig {
367
378
  provider?: "local" | "vercel-blob" | "s3";
@@ -401,6 +412,99 @@ interface MessagingChannelConfig {
401
412
  maxSendsPerRun?: number;
402
413
  allowedUserIds?: number[];
403
414
  }
415
+ interface IsolateBinding {
416
+ description: string;
417
+ inputSchema: JsonSchema;
418
+ handler: (input: Record<string, unknown>) => Promise<unknown> | unknown;
419
+ }
420
+ /**
421
+ * Network access configuration for the bash sandbox (curl, wget).
422
+ * Network access is disabled by default — you must explicitly allow URLs.
423
+ */
424
+ interface NetworkConfig {
425
+ /**
426
+ * List of allowed URL prefixes. Each entry must be a full origin (scheme + host),
427
+ * optionally followed by a path prefix.
428
+ *
429
+ * Examples:
430
+ * - `"https://api.example.com"` — allows all paths on this origin
431
+ * - `"https://api.example.com/v1/"` — allows only paths starting with /v1/
432
+ *
433
+ * Entries can be plain strings or objects with header transforms for credentials brokering:
434
+ * ```
435
+ * { url: "https://api.example.com", transform: [{ headers: { "Authorization": "Bearer ..." } }] }
436
+ * ```
437
+ */
438
+ allowedUrls?: (string | {
439
+ url: string;
440
+ transform?: {
441
+ headers: Record<string, string>;
442
+ }[];
443
+ })[];
444
+ /** Allowed HTTP methods. Defaults to `["GET", "HEAD"]`. */
445
+ allowedMethods?: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS")[];
446
+ /** Bypass the allow-list and permit all URLs and methods. Only use in trusted environments. */
447
+ dangerouslyAllowAll?: boolean;
448
+ /** Maximum number of redirects to follow. Default: 20. */
449
+ maxRedirects?: number;
450
+ /** Request timeout in milliseconds. Default: 30000. */
451
+ timeoutMs?: number;
452
+ /** Maximum response body size in bytes. Default: 10MB. */
453
+ maxResponseSize?: number;
454
+ /** Reject URLs resolving to private/loopback IPs (SSRF protection). Default: false. */
455
+ denyPrivateRanges?: boolean;
456
+ }
457
+ interface BashExecutionLimits {
458
+ /** Maximum function call/recursion depth. Default: 100. */
459
+ maxCallDepth?: number;
460
+ /** Maximum number of commands to execute. Default: 10000. */
461
+ maxCommandCount?: number;
462
+ /** Maximum loop iterations for while/for/until. Default: 10000. */
463
+ maxLoopIterations?: number;
464
+ /** Maximum total output size (stdout + stderr) in bytes. Default: 10MB. */
465
+ maxOutputSize?: number;
466
+ /** Maximum string length in bytes. Default: 10MB. */
467
+ maxStringLength?: number;
468
+ /** Maximum array elements. Default: 100000. */
469
+ maxArrayElements?: number;
470
+ }
471
+ interface BashConfig {
472
+ /**
473
+ * Whitelist of allowed commands. When set, only these commands are available.
474
+ * Omit to allow all built-in commands.
475
+ *
476
+ * @example ["cat", "grep", "jq", "echo", "ls", "head", "tail", "wc", "sort"]
477
+ */
478
+ commands?: string[];
479
+ /** Execution limits to prevent runaway scripts. */
480
+ executionLimits?: BashExecutionLimits;
481
+ /** Enable python3/python commands in the sandbox. Default: false. */
482
+ python?: boolean;
483
+ /** Enable js-exec/node commands via QuickJS in the sandbox. Default: false. */
484
+ javascript?: boolean;
485
+ /** Environment variables injected into every bash session. */
486
+ env?: Record<string, string>;
487
+ }
488
+ interface IsolateConfig {
489
+ /** V8 isolate memory limit in MB. Default: 128 */
490
+ memoryLimit?: number;
491
+ /** Execution timeout in ms. Default: 10000 */
492
+ timeLimit?: number;
493
+ /** Max combined stdout+stderr in bytes. Default: 65536 */
494
+ outputLimit?: number;
495
+ /** Max code input size in bytes. Default: 102400 (100KB) */
496
+ codeLimit?: number;
497
+ /** npm packages to bundle and make available via require() */
498
+ libraries?: string[];
499
+ /** External API access */
500
+ apis?: {
501
+ fetch?: {
502
+ allowedDomains: string[];
503
+ };
504
+ };
505
+ /** Builder-defined custom bindings injected into the isolate */
506
+ bindings?: Record<string, IsolateBinding>;
507
+ }
404
508
  interface PonchoConfig extends McpConfig {
405
509
  harness?: string;
406
510
  messaging?: MessagingChannelConfig[];
@@ -467,8 +571,23 @@ interface PonchoConfig extends McpConfig {
467
571
  /** Cron expression controlling how often the reminder poll runs (local and serverless). Default: every 10 minutes. */
468
572
  pollSchedule?: string;
469
573
  };
574
+ /**
575
+ * Declare env var names that tenants can self-manage via the web UI or API.
576
+ * Key = env var name, value = human-readable label shown in the settings panel.
577
+ * Example: { LINEAR_API_KEY: "Linear API Key", STRIPE_KEY: "Stripe Secret Key" }
578
+ */
579
+ tenantSecrets?: Record<string, string>;
470
580
  /** Set to `false` to disable the built-in web UI (headless / API-only mode). */
471
581
  webUi?: false;
582
+ /** Enable sandboxed V8 isolate code execution. */
583
+ isolate?: IsolateConfig;
584
+ /**
585
+ * Network access for sandboxed tools (bash curl/wget, isolate fetch).
586
+ * Disabled by default — you must explicitly allow URLs.
587
+ */
588
+ network?: NetworkConfig;
589
+ /** Bash sandbox configuration. */
590
+ bash?: BashConfig;
472
591
  /** Enable browser automation tools. Set `true` for defaults, or provide config. */
473
592
  browser?: boolean | {
474
593
  viewport?: {
@@ -506,6 +625,7 @@ declare const createDeleteDirectoryTool: (workingDir: string) => ToolDefinition;
506
625
  declare const ponchoDocsTool: ToolDefinition;
507
626
 
508
627
  declare const PONCHO_UPLOAD_SCHEME = "poncho-upload://";
628
+ declare const VFS_SCHEME = "vfs://";
509
629
  interface UploadStore {
510
630
  put(key: string, data: Buffer, mediaType: string): Promise<string>;
511
631
  get(urlOrKey: string): Promise<Buffer>;
@@ -556,6 +676,10 @@ interface TodoItem {
556
676
  createdAt: number;
557
677
  updatedAt: number;
558
678
  }
679
+ interface TodoStore {
680
+ get(conversationId: string): Promise<TodoItem[]>;
681
+ set(conversationId: string, todos: TodoItem[]): Promise<void>;
682
+ }
559
683
 
560
684
  type ReminderStatus = "pending" | "fired" | "cancelled";
561
685
  interface Reminder {
@@ -567,6 +691,7 @@ interface Reminder {
567
691
  createdAt: number;
568
692
  conversationId: string;
569
693
  ownerId?: string;
694
+ tenantId?: string | null;
570
695
  }
571
696
  interface ReminderStore {
572
697
  list(): Promise<Reminder[]>;
@@ -576,14 +701,102 @@ interface ReminderStore {
576
701
  timezone?: string;
577
702
  conversationId: string;
578
703
  ownerId?: string;
704
+ tenantId?: string | null;
579
705
  }): Promise<Reminder>;
580
706
  cancel(id: string): Promise<Reminder>;
581
707
  delete(id: string): Promise<void>;
582
708
  }
583
- declare const createReminderStore: (agentId: string, config?: StateConfig, options?: {
709
+ declare const createReminderStore: (_agentId: string, _config?: StateConfig, _options?: {
584
710
  workingDir?: string;
585
711
  }) => ReminderStore;
586
712
 
713
+ interface VfsStat {
714
+ type: "file" | "directory" | "symlink";
715
+ size: number;
716
+ mode: number;
717
+ mimeType?: string;
718
+ symlinkTarget?: string;
719
+ createdAt: number;
720
+ updatedAt: number;
721
+ }
722
+ interface VfsDirEntry {
723
+ name: string;
724
+ type: "file" | "directory" | "symlink";
725
+ }
726
+ interface StorageEngine {
727
+ /** Run migrations and prepare the storage backend. */
728
+ initialize(): Promise<void>;
729
+ /** Gracefully release resources. */
730
+ close(): Promise<void>;
731
+ conversations: {
732
+ list(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
733
+ get(conversationId: string): Promise<Conversation | undefined>;
734
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
735
+ update(conversation: Conversation): Promise<void>;
736
+ rename(conversationId: string, title: string): Promise<Conversation | undefined>;
737
+ delete(conversationId: string): Promise<boolean>;
738
+ search(query: string, tenantId?: string | null): Promise<ConversationSummary[]>;
739
+ appendSubagentResult(conversationId: string, result: PendingSubagentResult): Promise<void>;
740
+ clearCallbackLock(conversationId: string): Promise<Conversation | undefined>;
741
+ };
742
+ memory: {
743
+ get(tenantId?: string | null): Promise<MainMemory>;
744
+ update(content: string, tenantId?: string | null): Promise<MainMemory>;
745
+ };
746
+ todos: {
747
+ get(conversationId: string): Promise<TodoItem[]>;
748
+ set(conversationId: string, todos: TodoItem[]): Promise<void>;
749
+ };
750
+ reminders: {
751
+ list(tenantId?: string | null): Promise<Reminder[]>;
752
+ create(input: {
753
+ task: string;
754
+ scheduledAt: number;
755
+ timezone?: string;
756
+ conversationId: string;
757
+ ownerId?: string;
758
+ tenantId?: string | null;
759
+ }): Promise<Reminder>;
760
+ cancel(id: string): Promise<Reminder>;
761
+ delete(id: string): Promise<void>;
762
+ };
763
+ vfs: {
764
+ readFile(tenantId: string, path: string): Promise<Uint8Array>;
765
+ writeFile(tenantId: string, path: string, content: Uint8Array, mimeType?: string): Promise<void>;
766
+ appendFile(tenantId: string, path: string, content: Uint8Array): Promise<void>;
767
+ deleteFile(tenantId: string, path: string): Promise<void>;
768
+ deleteDir(tenantId: string, path: string, recursive?: boolean): Promise<void>;
769
+ stat(tenantId: string, path: string): Promise<VfsStat | undefined>;
770
+ readdir(tenantId: string, path: string): Promise<VfsDirEntry[]>;
771
+ mkdir(tenantId: string, path: string, recursive?: boolean): Promise<void>;
772
+ rename(tenantId: string, oldPath: string, newPath: string): Promise<void>;
773
+ chmod(tenantId: string, path: string, mode: number): Promise<void>;
774
+ utimes(tenantId: string, path: string, mtime: Date): Promise<void>;
775
+ symlink(tenantId: string, target: string, linkPath: string): Promise<void>;
776
+ readlink(tenantId: string, path: string): Promise<string>;
777
+ lstat(tenantId: string, path: string): Promise<VfsStat | undefined>;
778
+ listAllPaths(tenantId: string): string[];
779
+ getUsage(tenantId: string): Promise<{
780
+ fileCount: number;
781
+ totalBytes: number;
782
+ }>;
783
+ };
784
+ }
785
+
786
+ interface SecretsStore {
787
+ get(tenantId: string): Promise<Record<string, string>>;
788
+ set(tenantId: string, key: string, value: string): Promise<void>;
789
+ delete(tenantId: string, key: string): Promise<void>;
790
+ list(tenantId: string): Promise<string[]>;
791
+ }
792
+ declare const createSecretsStore: (_agentId: string, signingKey: string, _config?: StateConfig, options?: {
793
+ workingDir?: string;
794
+ }) => SecretsStore;
795
+ /**
796
+ * Resolve an env var name: check tenant secrets first, then process.env.
797
+ */
798
+ declare function resolveEnv(secretsStore: SecretsStore | undefined, tenantId: string | null | undefined, envName: string): Promise<string | undefined>;
799
+
587
800
  declare const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
588
801
  interface OpenAICodexAuthConfig {
589
802
  refreshTokenEnv?: string;
@@ -660,6 +873,7 @@ interface SubagentManager {
660
873
  task: string;
661
874
  parentConversationId: string;
662
875
  ownerId: string;
876
+ tenantId?: string | null;
663
877
  }): Promise<SubagentSpawnResult>;
664
878
  sendMessage(subagentId: string, message: string): Promise<SubagentSpawnResult>;
665
879
  stop(subagentId: string): Promise<void>;
@@ -720,8 +934,11 @@ declare class AgentHarness {
720
934
  readonly uploadStore?: UploadStore;
721
935
  private skillContextWindow;
722
936
  private memoryStore?;
937
+ private readonly tenantMemoryStores;
938
+ private memoryConfig?;
723
939
  private todoStore?;
724
940
  reminderStore?: ReminderStore;
941
+ secretsStore?: SecretsStore;
725
942
  private loadedConfig?;
726
943
  private loadedSkills;
727
944
  private skillFingerprint;
@@ -738,6 +955,10 @@ declare class AgentHarness {
738
955
  private mcpBridge?;
739
956
  private subagentManager?;
740
957
  private readonly archivedToolResultsByConversation;
958
+ /** Unified storage engine (replaces individual KV-backed stores). */
959
+ storageEngine?: StorageEngine;
960
+ /** Bash environment manager (creates per-tenant bash instances). */
961
+ private bashManager?;
741
962
  private resolveToolAccess;
742
963
  private isToolEnabled;
743
964
  private registerIfMissing;
@@ -751,6 +972,7 @@ declare class AgentHarness {
751
972
  setSubagentManager(manager: SubagentManager): void;
752
973
  private registerConfiguredBuiltInTools;
753
974
  private createGetToolResultByIdTool;
975
+ private createVfsAccess;
754
976
  private shouldEnableWriteTool;
755
977
  constructor(options?: HarnessOptions);
756
978
  get frontmatter(): AgentFrontmatter | undefined;
@@ -759,6 +981,11 @@ declare class AgentHarness {
759
981
  private truncateHistoricalToolResults;
760
982
  private shouldPreserveSkillToolResult;
761
983
  getTodos(conversationId: string): Promise<TodoItem[]>;
984
+ /**
985
+ * Get a memory store, optionally scoped to a tenant.
986
+ * Returns the default (agent-wide) store when tenantId is null/undefined.
987
+ */
988
+ private getMemoryStore;
762
989
  private listActiveSkills;
763
990
  private getAgentMcpIntent;
764
991
  private getAgentScriptIntent;
@@ -806,6 +1033,10 @@ declare class AgentHarness {
806
1033
  get browserSession(): unknown;
807
1034
  shutdown(): Promise<void>;
808
1035
  listTools(): ToolDefinition[];
1036
+ listSkills(): Array<{
1037
+ name: string;
1038
+ description: string;
1039
+ }>;
809
1040
  /**
810
1041
  * Wraps the run() generator with an OTel root span (invoke_agent) so all
811
1042
  * child spans (LLM calls via AI SDK, tool execution) group under one trace.
@@ -921,6 +1152,299 @@ declare class TelemetryEmitter {
921
1152
  private sendOtlp;
922
1153
  }
923
1154
 
1155
+ declare class InMemoryEngine implements StorageEngine {
1156
+ private readonly agentId;
1157
+ private convs;
1158
+ private mem;
1159
+ private todoData;
1160
+ private reminderData;
1161
+ private vfsData;
1162
+ constructor(agentId: string);
1163
+ initialize(): Promise<void>;
1164
+ close(): Promise<void>;
1165
+ conversations: {
1166
+ list: (ownerId?: string, tenantId?: string | null) => Promise<ConversationSummary[]>;
1167
+ get: (conversationId: string) => Promise<Conversation | undefined>;
1168
+ create: (ownerId?: string, title?: string, tenantId?: string | null) => Promise<Conversation>;
1169
+ update: (conversation: Conversation) => Promise<void>;
1170
+ rename: (conversationId: string, title: string) => Promise<Conversation | undefined>;
1171
+ delete: (conversationId: string) => Promise<boolean>;
1172
+ search: (query: string, tenantId?: string | null) => Promise<ConversationSummary[]>;
1173
+ appendSubagentResult: (conversationId: string, result: PendingSubagentResult) => Promise<void>;
1174
+ clearCallbackLock: (conversationId: string) => Promise<Conversation | undefined>;
1175
+ };
1176
+ memory: {
1177
+ get: (tenantId?: string | null) => Promise<MainMemory>;
1178
+ update: (content: string, tenantId?: string | null) => Promise<MainMemory>;
1179
+ };
1180
+ todos: {
1181
+ get: (conversationId: string) => Promise<TodoItem[]>;
1182
+ set: (conversationId: string, todos: TodoItem[]) => Promise<void>;
1183
+ };
1184
+ reminders: {
1185
+ list: (tenantId?: string | null) => Promise<Reminder[]>;
1186
+ create: (input: {
1187
+ task: string;
1188
+ scheduledAt: number;
1189
+ timezone?: string;
1190
+ conversationId: string;
1191
+ ownerId?: string;
1192
+ tenantId?: string | null;
1193
+ }) => Promise<Reminder>;
1194
+ cancel: (id: string) => Promise<Reminder>;
1195
+ delete: (id: string) => Promise<void>;
1196
+ };
1197
+ vfs: {
1198
+ readFile: (tenantId: string, path: string) => Promise<Uint8Array>;
1199
+ writeFile: (tenantId: string, path: string, content: Uint8Array, mimeType?: string) => Promise<void>;
1200
+ appendFile: (tenantId: string, path: string, content: Uint8Array) => Promise<void>;
1201
+ deleteFile: (tenantId: string, path: string) => Promise<void>;
1202
+ deleteDir: (tenantId: string, path: string, recursive?: boolean) => Promise<void>;
1203
+ stat: (tenantId: string, path: string) => Promise<VfsStat | undefined>;
1204
+ readdir: (tenantId: string, path: string) => Promise<VfsDirEntry[]>;
1205
+ mkdir: (tenantId: string, path: string, recursive?: boolean) => Promise<void>;
1206
+ rename: (tenantId: string, oldPath: string, newPath: string) => Promise<void>;
1207
+ chmod: (tenantId: string, path: string, mode: number) => Promise<void>;
1208
+ utimes: (tenantId: string, path: string, mtime: Date) => Promise<void>;
1209
+ symlink: (tenantId: string, target: string, linkPath: string) => Promise<void>;
1210
+ readlink: (tenantId: string, path: string) => Promise<string>;
1211
+ lstat: (tenantId: string, path: string) => Promise<VfsStat | undefined>;
1212
+ listAllPaths: (tenantId: string) => string[];
1213
+ getUsage: (tenantId: string) => Promise<{
1214
+ fileCount: number;
1215
+ totalBytes: number;
1216
+ }>;
1217
+ };
1218
+ private toSummary;
1219
+ private ensureParentDirs;
1220
+ private mkdirSingle;
1221
+ private resolveSymlink;
1222
+ }
1223
+
1224
+ /** Tag used by migrations to branch on backend. */
1225
+ type DialectTag = "sqlite" | "postgresql";
1226
+
1227
+ interface Dialect {
1228
+ tag: DialectTag;
1229
+ /** Return a positional parameter placeholder. 1-indexed. */
1230
+ param(index: number): string;
1231
+ /** BLOB type name */
1232
+ blob: string;
1233
+ /** JSON type name */
1234
+ json: string;
1235
+ /** Current-timestamp expression */
1236
+ now(): string;
1237
+ /** UPSERT conflict clause for a given PK column list */
1238
+ upsert(pkCols: string[]): string;
1239
+ }
1240
+ interface QueryRow {
1241
+ [key: string]: unknown;
1242
+ }
1243
+ interface QueryExecutor {
1244
+ run(sql: string, params?: unknown[]): Promise<void>;
1245
+ get<T extends QueryRow = QueryRow>(sql: string, params?: unknown[]): Promise<T | undefined>;
1246
+ all<T extends QueryRow = QueryRow>(sql: string, params?: unknown[]): Promise<T[]>;
1247
+ /** Execute raw SQL (for migrations). */
1248
+ exec(sql: string): Promise<void>;
1249
+ /** Run multiple statements in a transaction. */
1250
+ transaction(fn: () => Promise<void>): Promise<void>;
1251
+ }
1252
+ declare abstract class SqlStorageEngine implements StorageEngine {
1253
+ protected readonly dialect: Dialect;
1254
+ protected readonly agentId: string;
1255
+ protected abstract readonly executor: QueryExecutor;
1256
+ constructor(dialect: Dialect, agentId: string);
1257
+ initialize(): Promise<void>;
1258
+ abstract close(): Promise<void>;
1259
+ /** Hook for subclass-specific setup (e.g. WAL mode). */
1260
+ protected onBeforeInit(): Promise<void>;
1261
+ private runMigrations;
1262
+ conversations: {
1263
+ list: (ownerId?: string, tenantId?: string | null) => Promise<ConversationSummary[]>;
1264
+ get: (conversationId: string) => Promise<Conversation | undefined>;
1265
+ create: (ownerId?: string, title?: string, tenantId?: string | null) => Promise<Conversation>;
1266
+ update: (conversation: Conversation) => Promise<void>;
1267
+ rename: (conversationId: string, title: string) => Promise<Conversation | undefined>;
1268
+ delete: (conversationId: string) => Promise<boolean>;
1269
+ search: (query: string, tenantId?: string | null) => Promise<ConversationSummary[]>;
1270
+ appendSubagentResult: (conversationId: string, result: PendingSubagentResult) => Promise<void>;
1271
+ clearCallbackLock: (conversationId: string) => Promise<Conversation | undefined>;
1272
+ };
1273
+ memory: {
1274
+ get: (tenantId?: string | null) => Promise<MainMemory>;
1275
+ update: (content: string, tenantId?: string | null) => Promise<MainMemory>;
1276
+ };
1277
+ todos: {
1278
+ get: (conversationId: string) => Promise<TodoItem[]>;
1279
+ set: (conversationId: string, todos: TodoItem[]) => Promise<void>;
1280
+ };
1281
+ reminders: {
1282
+ list: (tenantId?: string | null) => Promise<Reminder[]>;
1283
+ create: (input: {
1284
+ task: string;
1285
+ scheduledAt: number;
1286
+ timezone?: string;
1287
+ conversationId: string;
1288
+ ownerId?: string;
1289
+ tenantId?: string | null;
1290
+ }) => Promise<Reminder>;
1291
+ cancel: (id: string) => Promise<Reminder>;
1292
+ delete: (id: string) => Promise<void>;
1293
+ };
1294
+ vfs: {
1295
+ readFile: (tenantId: string, path: string) => Promise<Uint8Array>;
1296
+ writeFile: (tenantId: string, path: string, content: Uint8Array, mimeType?: string) => Promise<void>;
1297
+ appendFile: (tenantId: string, path: string, content: Uint8Array) => Promise<void>;
1298
+ deleteFile: (tenantId: string, path: string) => Promise<void>;
1299
+ deleteDir: (tenantId: string, path: string, recursive?: boolean) => Promise<void>;
1300
+ stat: (tenantId: string, path: string) => Promise<VfsStat | undefined>;
1301
+ readdir: (tenantId: string, path: string) => Promise<VfsDirEntry[]>;
1302
+ mkdir: (tenantId: string, path: string, recursive?: boolean) => Promise<void>;
1303
+ rename: (tenantId: string, oldPath: string, newPath: string) => Promise<void>;
1304
+ chmod: (tenantId: string, path: string, mode: number) => Promise<void>;
1305
+ utimes: (tenantId: string, path: string, mtime: Date) => Promise<void>;
1306
+ symlink: (tenantId: string, target: string, linkPath: string) => Promise<void>;
1307
+ readlink: (tenantId: string, path: string) => Promise<string>;
1308
+ lstat: (tenantId: string, path: string) => Promise<VfsStat | undefined>;
1309
+ listAllPaths: (_tenantId: string) => string[];
1310
+ getUsage: (tenantId: string) => Promise<{
1311
+ fileCount: number;
1312
+ totalBytes: number;
1313
+ }>;
1314
+ };
1315
+ private parseConversation;
1316
+ private rowToSummary;
1317
+ private rowToReminder;
1318
+ protected toUint8Array(value: unknown): Uint8Array;
1319
+ private ensureParentDirs;
1320
+ private mkdirSingle;
1321
+ private resolveSymlink;
1322
+ }
1323
+
1324
+ declare class SqliteEngine extends SqlStorageEngine {
1325
+ private db;
1326
+ private readonly dbPath;
1327
+ protected readonly executor: QueryExecutor;
1328
+ constructor(options: {
1329
+ workingDir: string;
1330
+ agentId: string;
1331
+ dbPath?: string;
1332
+ });
1333
+ protected onBeforeInit(): Promise<void>;
1334
+ initialize(): Promise<void>;
1335
+ close(): Promise<void>;
1336
+ }
1337
+
1338
+ declare class PostgresEngine extends SqlStorageEngine {
1339
+ private sql;
1340
+ private readonly urlEnv;
1341
+ protected readonly executor: QueryExecutor;
1342
+ /** In-memory path cache per tenant for sync getAllPaths(). */
1343
+ private pathCache;
1344
+ constructor(options: {
1345
+ agentId: string;
1346
+ urlEnv?: string;
1347
+ });
1348
+ protected onBeforeInit(): Promise<void>;
1349
+ initialize(): Promise<void>;
1350
+ close(): Promise<void>;
1351
+ /** Refresh the path cache for a tenant (call before bash.exec()). */
1352
+ refreshPathCache(tenantId: string): Promise<void>;
1353
+ private patchVfs;
1354
+ private query;
1355
+ private addToPathCache;
1356
+ private removeFromPathCache;
1357
+ }
1358
+
1359
+ type StorageProvider = "memory" | "sqlite" | "postgresql" | "local";
1360
+ interface StorageFactoryOptions {
1361
+ provider?: StorageProvider;
1362
+ workingDir: string;
1363
+ agentId: string;
1364
+ /** Env var name for the PostgreSQL connection URL (default: DATABASE_URL). */
1365
+ urlEnv?: string;
1366
+ /** Override the SQLite database file path. */
1367
+ dbPath?: string;
1368
+ }
1369
+ declare function createStorageEngine(options: StorageFactoryOptions): StorageEngine;
1370
+
1371
+ declare function createConversationStoreFromEngine(engine: StorageEngine): ConversationStore;
1372
+ declare function createMemoryStoreFromEngine(engine: StorageEngine, tenantId?: string | null): MemoryStore;
1373
+ declare function createTodoStoreFromEngine(engine: StorageEngine): TodoStore;
1374
+ declare function createReminderStoreFromEngine(engine: StorageEngine): ReminderStore;
1375
+
1376
+ declare class PonchoFsAdapter implements IFileSystem {
1377
+ private engine;
1378
+ private tenantId;
1379
+ private limits;
1380
+ constructor(engine: StorageEngine, tenantId: string, limits: {
1381
+ maxFileSize: number;
1382
+ maxTotalStorage: number;
1383
+ });
1384
+ readFile(path: string, _options?: {
1385
+ encoding?: BufferEncoding | null;
1386
+ } | BufferEncoding): Promise<string>;
1387
+ readFileBuffer(path: string): Promise<Uint8Array>;
1388
+ exists(path: string): Promise<boolean>;
1389
+ stat(path: string): Promise<FsStat>;
1390
+ readdir(path: string): Promise<string[]>;
1391
+ readdirWithFileTypes(path: string): Promise<Array<{
1392
+ name: string;
1393
+ isFile: boolean;
1394
+ isDirectory: boolean;
1395
+ isSymbolicLink: boolean;
1396
+ }>>;
1397
+ writeFile(path: string, content: FileContent, _options?: {
1398
+ encoding?: BufferEncoding;
1399
+ } | BufferEncoding): Promise<void>;
1400
+ appendFile(path: string, content: FileContent, _options?: {
1401
+ encoding?: BufferEncoding;
1402
+ } | BufferEncoding): Promise<void>;
1403
+ mkdir(path: string, options?: MkdirOptions): Promise<void>;
1404
+ rm(path: string, options?: RmOptions): Promise<void>;
1405
+ cp(src: string, dest: string, options?: CpOptions): Promise<void>;
1406
+ mv(src: string, dest: string): Promise<void>;
1407
+ resolvePath(base: string, path: string): string;
1408
+ realpath(path: string): Promise<string>;
1409
+ getAllPaths(): string[];
1410
+ chmod(path: string, mode: number): Promise<void>;
1411
+ utimes(path: string, _atime: Date, mtime: Date): Promise<void>;
1412
+ symlink(target: string, linkPath: string): Promise<void>;
1413
+ link(existingPath: string, newPath: string): Promise<void>;
1414
+ readlink(path: string): Promise<string>;
1415
+ lstat(path: string): Promise<FsStat>;
1416
+ }
1417
+
1418
+ declare class BashEnvironmentManager {
1419
+ private engine;
1420
+ private limits;
1421
+ private environments;
1422
+ private readonly workingDir;
1423
+ private readonly bashOptions;
1424
+ constructor(engine: StorageEngine, limits: {
1425
+ maxFileSize: number;
1426
+ maxTotalStorage: number;
1427
+ }, workingDir: string | null, bashConfig?: BashConfig, network?: NetworkConfig);
1428
+ getOrCreate(tenantId: string): Bash;
1429
+ getAdapter(tenantId: string): PonchoFsAdapter;
1430
+ /** Refresh the PostgreSQL path cache before a bash.exec() call. */
1431
+ refreshPathCache(tenantId: string): Promise<void>;
1432
+ destroy(tenantId: string): void;
1433
+ destroyAll(): void;
1434
+ }
1435
+
1436
+ declare const createBashTool: (bashManager: BashEnvironmentManager) => ToolDefinition;
1437
+
1438
+ interface TenantTokenPayload {
1439
+ tenantId: string;
1440
+ metadata?: Record<string, unknown>;
1441
+ }
1442
+ /**
1443
+ * Verify a tenant JWT (HS256) signed with the given key.
1444
+ * Returns the decoded payload on success, or undefined on any failure.
1445
+ */
1446
+ declare function verifyTenantToken(signingKey: string, token: string): Promise<TenantTokenPayload | undefined>;
1447
+
924
1448
  declare const createSubagentTools: (manager: SubagentManager) => ToolDefinition[];
925
1449
 
926
- export { type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, type ArchivedToolResult$1 as ArchivedToolResult, type BuiltInToolToggles, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type Conversation, type ConversationState, type ConversationStore, type ConversationSummary, type CronJobConfig, type HarnessOptions, type HarnessRunOutput, InMemoryConversationStore, InMemoryStateStore, LocalMcpBridge, LocalUploadStore, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, OPENAI_CODEX_CLIENT_ID, type OpenAICodexAuthConfig, type OpenAICodexDeviceAuthRequest, type OpenAICodexSession, type OtlpConfig, type OtlpOption, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PendingSubagentResult, type PonchoConfig, type ProviderConfig, type Reminder, type ReminderStatus, type ReminderStore, type RemoteMcpServerConfig, type RuntimeRenderContext, S3UploadStore, STORAGE_SCHEMA_VERSION, type SkillContextEntry, type SkillMetadata, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type SubagentManager, type SubagentResult, type SubagentSpawnResult, type SubagentSummary, type TelemetryConfig, TelemetryEmitter, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type UploadStore, type UploadsConfig, VercelBlobUploadStore, buildAgentDirectoryName, buildSkillContextWindow, compactMessages, completeOpenAICodexDeviceAuth, createConversationStore, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createMemoryStore, createMemoryTools, createModelProvider, createReminderStore, createReminderTools, createSearchTools, createSkillTools, createStateStore, createSubagentTools, createUploadStore, createWriteTool, deleteOpenAICodexSession, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, findSafeSplitPoint, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getOpenAICodexAccessToken, getOpenAICodexAuthFilePath, getOpenAICodexRequiredScopes, getPonchoStoreRoot, jsonSchemaToZod, loadPonchoConfig, loadSkillContext, loadSkillInstructions, loadSkillMetadata, normalizeOtlp, normalizeScriptPolicyPath, parseAgentFile, parseAgentMarkdown, ponchoDocsTool, readOpenAICodexSession, readSkillResource, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveMemoryConfig, resolveSkillDirs, resolveStateConfig, slugifyStorageComponent, startOpenAICodexDeviceAuth, writeOpenAICodexSession };
1450
+ export { type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, type ArchivedToolResult$1 as ArchivedToolResult, type BashConfig, BashEnvironmentManager, type BashExecutionLimits, type BuiltInToolToggles, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type Conversation, type ConversationState, type ConversationStore, type ConversationSummary, type CronJobConfig, type HarnessOptions, type HarnessRunOutput, InMemoryConversationStore, InMemoryEngine, InMemoryStateStore, type IsolateBinding, type IsolateConfig, LocalMcpBridge, LocalUploadStore, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, type NetworkConfig, OPENAI_CODEX_CLIENT_ID, type OpenAICodexAuthConfig, type OpenAICodexDeviceAuthRequest, type OpenAICodexSession, type OtlpConfig, type OtlpOption, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PendingSubagentResult, type PonchoConfig, PonchoFsAdapter, PostgresEngine, type ProviderConfig, type Reminder, type ReminderStatus, type ReminderStore, type RemoteMcpServerConfig, type RuntimeRenderContext, S3UploadStore, STORAGE_SCHEMA_VERSION, type SecretsStore, type SkillContextEntry, type SkillMetadata, SqliteEngine, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type StorageEngine, type StorageFactoryOptions, type StorageProvider, type SubagentManager, type SubagentResult, type SubagentSpawnResult, type SubagentSummary, type TelemetryConfig, TelemetryEmitter, type TenantTokenPayload, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type UploadStore, type UploadsConfig, VFS_SCHEME, VercelBlobUploadStore, type VfsDirEntry, type VfsStat, buildAgentDirectoryName, buildSkillContextWindow, compactMessages, completeOpenAICodexDeviceAuth, createBashTool, createConversationStore, createConversationStoreFromEngine, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createMemoryStore, createMemoryStoreFromEngine, createMemoryTools, createModelProvider, createReminderStore, createReminderStoreFromEngine, createReminderTools, createSearchTools, createSecretsStore, createSkillTools, createStateStore, createStorageEngine, createSubagentTools, createTodoStoreFromEngine, createUploadStore, createWriteTool, deleteOpenAICodexSession, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, findSafeSplitPoint, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getOpenAICodexAccessToken, getOpenAICodexAuthFilePath, getOpenAICodexRequiredScopes, getPonchoStoreRoot, jsonSchemaToZod, loadPonchoConfig, loadSkillContext, loadSkillInstructions, loadSkillMetadata, normalizeOtlp, normalizeScriptPolicyPath, parseAgentFile, parseAgentMarkdown, ponchoDocsTool, readOpenAICodexSession, readSkillResource, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveEnv, resolveMemoryConfig, resolveSkillDirs, resolveStateConfig, slugifyStorageComponent, startOpenAICodexDeviceAuth, verifyTenantToken, writeOpenAICodexSession };