@poncho-ai/harness 0.34.0 → 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,33 @@
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
+
22
+ ## 0.34.1
23
+
24
+ ### Patch Changes
25
+
26
+ - [`59a88cc`](https://github.com/cesr/poncho-ai/commit/59a88cc52b5c3aa7432b820424bb8067174233e5) Thanks [@cesr](https://github.com/cesr)! - fix: improve token estimation accuracy and handle missing attachments
27
+ - Use a JSON-specific token ratio for tool definitions to avoid inflating counts with many MCP tools.
28
+ - Track actual context size from model responses for compaction triggers instead of cumulative input tokens.
29
+ - Gracefully degrade when file attachments are missing or expired instead of crashing.
30
+
3
31
  ## 0.34.0
4
32
 
5
33
  ### Minor 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
 
@@ -83,6 +83,11 @@ declare const resolveCompactionConfig: (explicit?: Partial<CompactionConfig>) =>
83
83
  declare const estimateTokens: (text: string) => number;
84
84
  /**
85
85
  * Estimate the total token count of a system prompt + messages + tool defs.
86
+ *
87
+ * Tool definitions are structured JSON (property names, braces, enum values)
88
+ * which tokenizes more efficiently than natural language — roughly 5-6
89
+ * chars/token vs ~4 chars/token for prose. We estimate them separately to
90
+ * avoid inflating the count when there are many MCP tools (100+).
86
91
  */
87
92
  declare const estimateTotalTokens: (systemPrompt: string, messages: Message[], toolDefinitionsJson?: string) => number;
88
93
  /**
@@ -201,10 +206,16 @@ interface Conversation {
201
206
  updatedAt: number;
202
207
  }
203
208
  interface ConversationStore {
204
- list(ownerId?: string): Promise<Conversation[]>;
205
- 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[]>;
206
217
  get(conversationId: string): Promise<Conversation | undefined>;
207
- create(ownerId?: string, title?: string): Promise<Conversation>;
218
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
208
219
  update(conversation: Conversation): Promise<void>;
209
220
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
210
221
  delete(conversationId: string): Promise<boolean>;
@@ -240,10 +251,10 @@ declare class InMemoryConversationStore implements ConversationStore {
240
251
  constructor(ttlSeconds?: number);
241
252
  private isExpired;
242
253
  private purgeExpired;
243
- list(ownerId?: string): Promise<Conversation[]>;
244
- listSummaries(ownerId?: string): Promise<ConversationSummary[]>;
254
+ list(ownerId?: string, tenantId?: string | null): Promise<Conversation[]>;
255
+ listSummaries(ownerId?: string, tenantId?: string | null): Promise<ConversationSummary[]>;
245
256
  get(conversationId: string): Promise<Conversation | undefined>;
246
- create(ownerId?: string, title?: string): Promise<Conversation>;
257
+ create(ownerId?: string, title?: string, tenantId?: string | null): Promise<Conversation>;
247
258
  update(conversation: Conversation): Promise<void>;
248
259
  rename(conversationId: string, title: string): Promise<Conversation | undefined>;
249
260
  delete(conversationId: string): Promise<boolean>;
@@ -256,6 +267,7 @@ type ConversationSummary = {
256
267
  updatedAt: number;
257
268
  createdAt?: number;
258
269
  ownerId: string;
270
+ tenantId?: string | null;
259
271
  parentConversationId?: string;
260
272
  messageCount?: number;
261
273
  hasPendingApprovals?: boolean;
@@ -296,8 +308,9 @@ interface MemoryStore {
296
308
  }
297
309
  declare const createMemoryStore: (agentId: string, config?: MemoryConfig, options?: {
298
310
  workingDir?: string;
311
+ tenantId?: string;
299
312
  }) => MemoryStore;
300
- declare const createMemoryTools: (store: MemoryStore, options?: {
313
+ declare const createMemoryTools: (store: MemoryStore | ((context: ToolContext) => MemoryStore), options?: {
301
314
  maxRecallConversations?: number;
302
315
  }) => ToolDefinition[];
303
316
 
@@ -323,10 +336,24 @@ declare class LocalMcpBridge {
323
336
  private readonly toolCatalog;
324
337
  private readonly unavailableServers;
325
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;
326
345
  constructor(config: McpConfig | undefined);
327
346
  private getServerName;
328
347
  private log;
348
+ /** Set of servers where discovery was deferred (no default token, has env resolver). */
349
+ private readonly deferredDiscoveryServers;
329
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;
330
357
  startLocalServers(): Promise<void>;
331
358
  stopLocalServers(): Promise<void>;
332
359
  listServers(): RemoteMcpServerConfig[];
@@ -339,6 +366,11 @@ declare class LocalMcpBridge {
339
366
  toSerializableConfig(): McpConfig;
340
367
  getLocalServers(): never[];
341
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[];
342
374
  loadTools(requestedPatterns: string[]): Promise<ToolDefinition[]>;
343
375
  private toToolDefinitions;
344
376
  }
@@ -462,6 +494,12 @@ interface PonchoConfig extends McpConfig {
462
494
  /** Cron expression controlling how often the reminder poll runs (local and serverless). Default: every 10 minutes. */
463
495
  pollSchedule?: string;
464
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>;
465
503
  /** Set to `false` to disable the built-in web UI (headless / API-only mode). */
466
504
  webUi?: false;
467
505
  /** Enable browser automation tools. Set `true` for defaults, or provide config. */
@@ -562,6 +600,7 @@ interface Reminder {
562
600
  createdAt: number;
563
601
  conversationId: string;
564
602
  ownerId?: string;
603
+ tenantId?: string | null;
565
604
  }
566
605
  interface ReminderStore {
567
606
  list(): Promise<Reminder[]>;
@@ -571,6 +610,7 @@ interface ReminderStore {
571
610
  timezone?: string;
572
611
  conversationId: string;
573
612
  ownerId?: string;
613
+ tenantId?: string | null;
574
614
  }): Promise<Reminder>;
575
615
  cancel(id: string): Promise<Reminder>;
576
616
  delete(id: string): Promise<void>;
@@ -579,6 +619,20 @@ declare const createReminderStore: (agentId: string, config?: StateConfig, optio
579
619
  workingDir?: string;
580
620
  }) => ReminderStore;
581
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
+
582
636
  declare const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
583
637
  interface OpenAICodexAuthConfig {
584
638
  refreshTokenEnv?: string;
@@ -655,6 +709,7 @@ interface SubagentManager {
655
709
  task: string;
656
710
  parentConversationId: string;
657
711
  ownerId: string;
712
+ tenantId?: string | null;
658
713
  }): Promise<SubagentSpawnResult>;
659
714
  sendMessage(subagentId: string, message: string): Promise<SubagentSpawnResult>;
660
715
  stop(subagentId: string): Promise<void>;
@@ -715,8 +770,11 @@ declare class AgentHarness {
715
770
  readonly uploadStore?: UploadStore;
716
771
  private skillContextWindow;
717
772
  private memoryStore?;
773
+ private readonly tenantMemoryStores;
774
+ private memoryConfig?;
718
775
  private todoStore?;
719
776
  reminderStore?: ReminderStore;
777
+ secretsStore?: SecretsStore;
720
778
  private loadedConfig?;
721
779
  private loadedSkills;
722
780
  private skillFingerprint;
@@ -754,6 +812,11 @@ declare class AgentHarness {
754
812
  private truncateHistoricalToolResults;
755
813
  private shouldPreserveSkillToolResult;
756
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;
757
820
  private listActiveSkills;
758
821
  private getAgentMcpIntent;
759
822
  private getAgentScriptIntent;
@@ -916,6 +979,16 @@ declare class TelemetryEmitter {
916
979
  private sendOtlp;
917
980
  }
918
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
+
919
992
  declare const createSubagentTools: (manager: SubagentManager) => ToolDefinition[];
920
993
 
921
- 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 };