@poncho-ai/harness 0.34.1 → 0.35.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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.35.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`83d3c5f`](https://github.com/cesr/poncho-ai/commit/83d3c5f841fe84965d1f9fec6dfc5d8832e4489a) Thanks [@cesr](https://github.com/cesr)! - feat: add multi-tenancy with JWT-based tenant scoping
8
+
9
+ Deploy one agent, serve many tenants with fully isolated conversations, memory, reminders, and secrets. Tenancy activates automatically when a valid JWT is received — no config changes needed.
10
+ - **Auth**: `createTenantToken()` in client SDK, `poncho auth create-token` CLI, or any HS256 JWT library.
11
+ - **Isolation**: conversations, memory, reminders, and todos scoped per tenant.
12
+ - **Per-tenant secrets**: encrypted secret overrides for MCP auth tokens, manageable via CLI (`poncho secrets`), API, and web UI settings panel.
13
+ - **MCP**: per-tenant token resolution with deferred discovery for servers without a default env var.
14
+ - **Web UI**: `?token=` tenant access, settings cog for secret management, dark mode support.
15
+ - **Backward compatible**: existing single-user deployments work unchanged.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [[`83d3c5f`](https://github.com/cesr/poncho-ai/commit/83d3c5f841fe84965d1f9fec6dfc5d8832e4489a)]:
20
+ - @poncho-ai/sdk@1.8.0
21
+
3
22
  ## 0.34.1
4
23
 
5
24
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
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, RunResult, AgentFailure, RunInput, AgentEvent, JsonSchema } from '@poncho-ai/sdk';
4
4
  export { ToolDefinition, defineTool } from '@poncho-ai/sdk';
5
5
  import { z } from 'zod';
6
6
 
@@ -206,10 +206,16 @@ interface Conversation {
206
206
  updatedAt: number;
207
207
  }
208
208
  interface ConversationStore {
209
- list(ownerId?: string): Promise<Conversation[]>;
210
- listSummaries(ownerId?: string): Promise<ConversationSummary[]>;
209
+ /**
210
+ * List conversations. tenantId semantics:
211
+ * undefined = no filter (builder/admin sees everything)
212
+ * null = legacy single-user only
213
+ * string = tenant-scoped
214
+ */
215
+ list(ownerId?: string, tenantId?: string | null): Promise<Conversation[]>;
216
+ listSummaries(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
211
217
  get(conversationId: string): Promise<Conversation | undefined>;
212
- create(ownerId?: string, title?: string): Promise<Conversation>;
218
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
213
219
  update(conversation: Conversation): Promise<void>;
214
220
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
215
221
  delete(conversationId: string): Promise<boolean>;
@@ -245,10 +251,10 @@ declare class InMemoryConversationStore implements ConversationStore {
245
251
  constructor(ttlSeconds?: number);
246
252
  private isExpired;
247
253
  private purgeExpired;
248
- list(ownerId?: string): Promise<Conversation[]>;
249
- listSummaries(ownerId?: string): Promise<ConversationSummary[]>;
254
+ list(ownerId?: string, tenantId?: string | null): Promise<Conversation[]>;
255
+ listSummaries(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
250
256
  get(conversationId: string): Promise<Conversation | undefined>;
251
- create(ownerId?: string, title?: string): Promise<Conversation>;
257
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
252
258
  update(conversation: Conversation): Promise<void>;
253
259
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
254
260
  delete(conversationId: string): Promise<boolean>;
@@ -261,6 +267,7 @@ type ConversationSummary = {
261
267
  updatedAt: number;
262
268
  createdAt?: number;
263
269
  ownerId: string;
270
+ tenantId?: string | null;
264
271
  parentConversationId?: string;
265
272
  messageCount?: number;
266
273
  hasPendingApprovals?: boolean;
@@ -301,8 +308,9 @@ interface MemoryStore {
301
308
  }
302
309
  declare const createMemoryStore: (agentId: string, config?: MemoryConfig, options?: {
303
310
  workingDir?: string;
311
+ tenantId?: string;
304
312
  }) => MemoryStore;
305
- declare const createMemoryTools: (store: MemoryStore, options?: {
313
+ declare const createMemoryTools: (store: MemoryStore | ((context: ToolContext) => MemoryStore), options?: {
306
314
  maxRecallConversations?: number;
307
315
  }) => ToolDefinition[];
308
316
 
@@ -328,10 +336,24 @@ declare class LocalMcpBridge {
328
336
  private readonly toolCatalog;
329
337
  private readonly unavailableServers;
330
338
  private readonly authFailedServers;
339
+ private envResolver?;
340
+ /**
341
+ * Set a resolver for per-tenant env vars (e.g. MCP auth tokens).
342
+ * Called by the harness after creating the secrets store.
343
+ */
344
+ setEnvResolver(resolver: (tenantId: string | undefined, envName: string) => Promise<string | undefined>): void;
331
345
  constructor(config: McpConfig | undefined);
332
346
  private getServerName;
333
347
  private log;
348
+ /** Set of servers where discovery was deferred (no default token, has env resolver). */
349
+ private readonly deferredDiscoveryServers;
334
350
  discoverTools(): Promise<void>;
351
+ /**
352
+ * Run deferred discovery and return ToolDefinitions for all newly discovered tools.
353
+ * Call this during run() so the tools are available to the model immediately.
354
+ */
355
+ discoverAndLoadDeferred(tenantId: string): Promise<ToolDefinition[]>;
356
+ private tryDeferredDiscovery;
335
357
  startLocalServers(): Promise<void>;
336
358
  stopLocalServers(): Promise<void>;
337
359
  listServers(): RemoteMcpServerConfig[];
@@ -344,6 +366,11 @@ declare class LocalMcpBridge {
344
366
  toSerializableConfig(): McpConfig;
345
367
  getLocalServers(): never[];
346
368
  listDiscoveredTools(serverName?: string): string[];
369
+ hasDeferredServers(): boolean;
370
+ /**
371
+ * Return ToolDefinitions for catalog tools not already registered in the dispatcher.
372
+ */
373
+ getUnregisteredTools(registeredNames: Set<string>): ToolDefinition[];
347
374
  loadTools(requestedPatterns: string[]): Promise<ToolDefinition[]>;
348
375
  private toToolDefinitions;
349
376
  }
@@ -467,6 +494,12 @@ interface PonchoConfig extends McpConfig {
467
494
  /** Cron expression controlling how often the reminder poll runs (local and serverless). Default: every 10 minutes. */
468
495
  pollSchedule?: string;
469
496
  };
497
+ /**
498
+ * Declare env var names that tenants can self-manage via the web UI or API.
499
+ * Key = env var name, value = human-readable label shown in the settings panel.
500
+ * Example: { LINEAR_API_KEY: "Linear API Key", STRIPE_KEY: "Stripe Secret Key" }
501
+ */
502
+ tenantSecrets?: Record<string, string>;
470
503
  /** Set to `false` to disable the built-in web UI (headless / API-only mode). */
471
504
  webUi?: false;
472
505
  /** Enable browser automation tools. Set `true` for defaults, or provide config. */
@@ -567,6 +600,7 @@ interface Reminder {
567
600
  createdAt: number;
568
601
  conversationId: string;
569
602
  ownerId?: string;
603
+ tenantId?: string | null;
570
604
  }
571
605
  interface ReminderStore {
572
606
  list(): Promise<Reminder[]>;
@@ -576,6 +610,7 @@ interface ReminderStore {
576
610
  timezone?: string;
577
611
  conversationId: string;
578
612
  ownerId?: string;
613
+ tenantId?: string | null;
579
614
  }): Promise<Reminder>;
580
615
  cancel(id: string): Promise<Reminder>;
581
616
  delete(id: string): Promise<void>;
@@ -584,6 +619,20 @@ declare const createReminderStore: (agentId: string, config?: StateConfig, optio
584
619
  workingDir?: string;
585
620
  }) => ReminderStore;
586
621
 
622
+ interface SecretsStore {
623
+ get(tenantId: string): Promise<Record<string, string>>;
624
+ set(tenantId: string, key: string, value: string): Promise<void>;
625
+ delete(tenantId: string, key: string): Promise<void>;
626
+ list(tenantId: string): Promise<string[]>;
627
+ }
628
+ declare const createSecretsStore: (agentId: string, signingKey: string, config?: StateConfig, options?: {
629
+ workingDir?: string;
630
+ }) => SecretsStore;
631
+ /**
632
+ * Resolve an env var name: check tenant secrets first, then process.env.
633
+ */
634
+ declare function resolveEnv(secretsStore: SecretsStore | undefined, tenantId: string | null | undefined, envName: string): Promise<string | undefined>;
635
+
587
636
  declare const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
588
637
  interface OpenAICodexAuthConfig {
589
638
  refreshTokenEnv?: string;
@@ -660,6 +709,7 @@ interface SubagentManager {
660
709
  task: string;
661
710
  parentConversationId: string;
662
711
  ownerId: string;
712
+ tenantId?: string | null;
663
713
  }): Promise<SubagentSpawnResult>;
664
714
  sendMessage(subagentId: string, message: string): Promise<SubagentSpawnResult>;
665
715
  stop(subagentId: string): Promise<void>;
@@ -720,8 +770,11 @@ declare class AgentHarness {
720
770
  readonly uploadStore?: UploadStore;
721
771
  private skillContextWindow;
722
772
  private memoryStore?;
773
+ private readonly tenantMemoryStores;
774
+ private memoryConfig?;
723
775
  private todoStore?;
724
776
  reminderStore?: ReminderStore;
777
+ secretsStore?: SecretsStore;
725
778
  private loadedConfig?;
726
779
  private loadedSkills;
727
780
  private skillFingerprint;
@@ -759,6 +812,11 @@ declare class AgentHarness {
759
812
  private truncateHistoricalToolResults;
760
813
  private shouldPreserveSkillToolResult;
761
814
  getTodos(conversationId: string): Promise<TodoItem[]>;
815
+ /**
816
+ * Get a memory store, optionally scoped to a tenant.
817
+ * Returns the default (agent-wide) store when tenantId is null/undefined.
818
+ */
819
+ private getMemoryStore;
762
820
  private listActiveSkills;
763
821
  private getAgentMcpIntent;
764
822
  private getAgentScriptIntent;
@@ -921,6 +979,16 @@ declare class TelemetryEmitter {
921
979
  private sendOtlp;
922
980
  }
923
981
 
982
+ interface TenantTokenPayload {
983
+ tenantId: string;
984
+ metadata?: Record<string, unknown>;
985
+ }
986
+ /**
987
+ * Verify a tenant JWT (HS256) signed with the given key.
988
+ * Returns the decoded payload on success, or undefined on any failure.
989
+ */
990
+ declare function verifyTenantToken(signingKey: string, token: string): Promise<TenantTokenPayload | undefined>;
991
+
924
992
  declare const createSubagentTools: (manager: SubagentManager) => ToolDefinition[];
925
993
 
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 };
994
+ 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 SecretsStore, type SkillContextEntry, type SkillMetadata, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type SubagentManager, type SubagentResult, type SubagentSpawnResult, type SubagentSummary, type TelemetryConfig, TelemetryEmitter, type TenantTokenPayload, 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, createSecretsStore, 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, resolveEnv, resolveMemoryConfig, resolveSkillDirs, resolveStateConfig, slugifyStorageComponent, startOpenAICodexDeviceAuth, verifyTenantToken, writeOpenAICodexSession };