Merge branch 'codex/work-folders-pi-refresh' into codex/work-folders-storybook-refresh

* codex/work-folders-pi-refresh:
  fix(runner): keep healthy native sessions alive (#13261)
  feat(connections): add AgentMail inboxes and email tasks (#13256)
  fix: stop completion reviews caused by policy upgrades (#13266)
  fix(ci): avoid empty pnpm caches from lockfile refresh (#13267)
  feat: accept a base64-encoded Cloud UI snippet (#13245)
  ci: use reserved AWS capacity for post-merge cloud verification (#13257)
  Fix Codex API key authentication in tests and runs (#13260)
  refactor: remove automatic productivity reviews (#13263)
  fix: continue conversations after confirmed remote runner stop (#13254)
  ci: cache Rust dependencies used by post-merge typecheck (#13259)
  fix(ui): restore task page archive shortcut (#13253)
  feat(ui): show one rolling runner activity per commentary group (#13255)
  fix(ui): hide retry countdown after execution starts (#13258)
  test: isolate native controller takeover fixture (#13212)
This commit is contained in:
Dotta committed 2026-09-11 17:15:49 -05:00
commit 24a7ab22f6
201 files changed
+62608 -3880

No files matched your search

@@ -1,67 +1,68 @@
import a0 from "./app-definitions/zapier.json" with { type: "json" };
import a1 from "./app-definitions/github.json" with { type: "json" };
import a2 from "./app-definitions/slack.json" with { type: "json" };
import a3 from "./app-definitions/microsoft-teams.json" with { type: "json" };
import a4 from "./app-definitions/telegram.json" with { type: "json" };
import a5 from "./app-definitions/discord.json" with { type: "json" };
import a6 from "./app-definitions/notion.json" with { type: "json" };
import a7 from "./app-definitions/posthog.json" with { type: "json" };
import a8 from "./app-definitions/linear.json" with { type: "json" };
import a9 from "./app-definitions/context7.json" with { type: "json" };
import a10 from "./app-definitions/shopify.json" with { type: "json" };
import a11 from "./app-definitions/composio.json" with { type: "json" };
import a12 from "./app-definitions/oauth-generic.json" with { type: "json" };
import a13 from "./app-definitions/api-key-generic.json" with { type: "json" };
import a14 from "./app-definitions/sentry.json" with { type: "json" };
import a15 from "./app-definitions/vercel.json" with { type: "json" };
import a16 from "./app-definitions/anthropic.json" with { type: "json" };
import a17 from "./app-definitions/jira.json" with { type: "json" };
import a18 from "./app-definitions/airtable.json" with { type: "json" };
import a19 from "./app-definitions/beehiiv.json" with { type: "json" };
import a20 from "./app-definitions/bitly.json" with { type: "json" };
import a21 from "./app-definitions/candid.json" with { type: "json" };
import a22 from "./app-definitions/cloudflare.json" with { type: "json" };
import a23 from "./app-definitions/cloudinary.json" with { type: "json" };
import a24 from "./app-definitions/coda.json" with { type: "json" };
import a25 from "./app-definitions/hugging-face.json" with { type: "json" };
import a26 from "./app-definitions/kernel.json" with { type: "json" };
import a27 from "./app-definitions/local-falcon.json" with { type: "json" };
import a28 from "./app-definitions/make.json" with { type: "json" };
import a29 from "./app-definitions/manufact.json" with { type: "json" };
import a30 from "./app-definitions/miro.json" with { type: "json" };
import a31 from "./app-definitions/netlify.json" with { type: "json" };
import a32 from "./app-definitions/oreilly.json" with { type: "json" };
import a33 from "./app-definitions/planetscale.json" with { type: "json" };
import a34 from "./app-definitions/resend.json" with { type: "json" };
import a35 from "./app-definitions/ticktick.json" with { type: "json" };
import a36 from "./app-definitions/todoist.json" with { type: "json" };
import a37 from "./app-definitions/webflow.json" with { type: "json" };
import a38 from "./app-definitions/wix.json" with { type: "json" };
import a39 from "./app-definitions/brex.json" with { type: "json" };
import a40 from "./app-definitions/clickhouse.json" with { type: "json" };
import a41 from "./app-definitions/egnyte.json" with { type: "json" };
import a42 from "./app-definitions/embat.json" with { type: "json" };
import a43 from "./app-definitions/mixpanel.json" with { type: "json" };
import a44 from "./app-definitions/postman.json" with { type: "json" };
import a45 from "./app-definitions/razorpay.json" with { type: "json" };
import a46 from "./app-definitions/sanity.json" with { type: "json" };
import a47 from "./app-definitions/stripe.json" with { type: "json" };
import a48 from "./app-definitions/supabase.json" with { type: "json" };
import a49 from "./app-definitions/ticket-tailor.json" with { type: "json" };
import a50 from "./app-definitions/asana.json" with { type: "json" };
import a51 from "./app-definitions/box.json" with { type: "json" };
import a52 from "./app-definitions/mem0.json" with { type: "json" };
import a53 from "./app-definitions/pagerduty.json" with { type: "json" };
import a54 from "./app-definitions/similarweb.json" with { type: "json" };
import a55 from "./app-definitions/xero.json" with { type: "json" };
import a56 from "./app-definitions/gmail.json" with { type: "json" };
import a57 from "./app-definitions/google-drive.json" with { type: "json" };
import a58 from "./app-definitions/google-docs.json" with { type: "json" };
import a59 from "./app-definitions/google-sheets.json" with { type: "json" };
import a60 from "./app-definitions/google-slides.json" with { type: "json" };
import a61 from "./app-definitions/google-calendar.json" with { type: "json" };
import a62 from "./app-definitions/google-chat.json" with { type: "json" };
import a63 from "./app-definitions/google-people.json" with { type: "json" };
import a64 from "./app-definitions/google-workspace-search.json" with { type: "json" };
import a0 from "./app-definitions/agentmail.json" with { type: "json" };
import a1 from "./app-definitions/zapier.json" with { type: "json" };
import a2 from "./app-definitions/github.json" with { type: "json" };
import a3 from "./app-definitions/slack.json" with { type: "json" };
import a4 from "./app-definitions/microsoft-teams.json" with { type: "json" };
import a5 from "./app-definitions/telegram.json" with { type: "json" };
import a6 from "./app-definitions/discord.json" with { type: "json" };
import a7 from "./app-definitions/notion.json" with { type: "json" };
import a8 from "./app-definitions/posthog.json" with { type: "json" };
import a9 from "./app-definitions/linear.json" with { type: "json" };
import a10 from "./app-definitions/context7.json" with { type: "json" };
import a11 from "./app-definitions/shopify.json" with { type: "json" };
import a12 from "./app-definitions/composio.json" with { type: "json" };
import a13 from "./app-definitions/oauth-generic.json" with { type: "json" };
import a14 from "./app-definitions/api-key-generic.json" with { type: "json" };
import a15 from "./app-definitions/sentry.json" with { type: "json" };
import a16 from "./app-definitions/vercel.json" with { type: "json" };
import a17 from "./app-definitions/anthropic.json" with { type: "json" };
import a18 from "./app-definitions/jira.json" with { type: "json" };
import a19 from "./app-definitions/airtable.json" with { type: "json" };
import a20 from "./app-definitions/beehiiv.json" with { type: "json" };
import a21 from "./app-definitions/bitly.json" with { type: "json" };
import a22 from "./app-definitions/candid.json" with { type: "json" };
import a23 from "./app-definitions/cloudflare.json" with { type: "json" };
import a24 from "./app-definitions/cloudinary.json" with { type: "json" };
import a25 from "./app-definitions/coda.json" with { type: "json" };
import a26 from "./app-definitions/hugging-face.json" with { type: "json" };
import a27 from "./app-definitions/kernel.json" with { type: "json" };
import a28 from "./app-definitions/local-falcon.json" with { type: "json" };
import a29 from "./app-definitions/make.json" with { type: "json" };
import a30 from "./app-definitions/manufact.json" with { type: "json" };
import a31 from "./app-definitions/miro.json" with { type: "json" };
import a32 from "./app-definitions/netlify.json" with { type: "json" };
import a33 from "./app-definitions/oreilly.json" with { type: "json" };
import a34 from "./app-definitions/planetscale.json" with { type: "json" };
import a35 from "./app-definitions/resend.json" with { type: "json" };
import a36 from "./app-definitions/ticktick.json" with { type: "json" };
import a37 from "./app-definitions/todoist.json" with { type: "json" };
import a38 from "./app-definitions/webflow.json" with { type: "json" };
import a39 from "./app-definitions/wix.json" with { type: "json" };
import a40 from "./app-definitions/brex.json" with { type: "json" };
import a41 from "./app-definitions/clickhouse.json" with { type: "json" };
import a42 from "./app-definitions/egnyte.json" with { type: "json" };
import a43 from "./app-definitions/embat.json" with { type: "json" };
import a44 from "./app-definitions/mixpanel.json" with { type: "json" };
import a45 from "./app-definitions/postman.json" with { type: "json" };
import a46 from "./app-definitions/razorpay.json" with { type: "json" };
import a47 from "./app-definitions/sanity.json" with { type: "json" };
import a48 from "./app-definitions/stripe.json" with { type: "json" };
import a49 from "./app-definitions/supabase.json" with { type: "json" };
import a50 from "./app-definitions/ticket-tailor.json" with { type: "json" };
import a51 from "./app-definitions/asana.json" with { type: "json" };
import a52 from "./app-definitions/box.json" with { type: "json" };
import a53 from "./app-definitions/mem0.json" with { type: "json" };
import a54 from "./app-definitions/pagerduty.json" with { type: "json" };
import a55 from "./app-definitions/similarweb.json" with { type: "json" };
import a56 from "./app-definitions/xero.json" with { type: "json" };
import a57 from "./app-definitions/gmail.json" with { type: "json" };
import a58 from "./app-definitions/google-drive.json" with { type: "json" };
import a59 from "./app-definitions/google-docs.json" with { type: "json" };
import a60 from "./app-definitions/google-sheets.json" with { type: "json" };
import a61 from "./app-definitions/google-slides.json" with { type: "json" };
import a62 from "./app-definitions/google-calendar.json" with { type: "json" };
import a63 from "./app-definitions/google-chat.json" with { type: "json" };
import a64 from "./app-definitions/google-people.json" with { type: "json" };
import a65 from "./app-definitions/google-workspace-search.json" with { type: "json" };
import type { AppDefinition } from "./types/app-definition.js";
export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,a61,a62,a63,a64] as AppDefinition[];
export const APP_DEFINITIONS=[a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31,a32,a33,a34,a35,a36,a37,a38,a39,a40,a41,a42,a43,a44,a45,a46,a47,a48,a49,a50,a51,a52,a53,a54,a55,a56,a57,a58,a59,a60,a61,a62,a63,a64,a65] as AppDefinition[];
+5 -5
View File
@@ -679,7 +679,7 @@ describe("AppDefinition catalog", () => {
"ticktick",
"xero",
]);
expect(APP_STORE_DEFINITIONS).toHaveLength(40);
expect(APP_STORE_DEFINITIONS).toHaveLength(41);
const connectableSlugs = new Set(
CONNECTABLE_APP_DEFINITIONS.map((entry) => entry.slug),
);
@@ -691,7 +691,7 @@ describe("AppDefinition catalog", () => {
expect(storeSlugs.has(slug), slug).toBe(false);
}
});
it("ships complete local branding provenance for all 40 store-visible providers", () => {
it("ships complete local branding provenance for all 41 store-visible providers", () => {
const uiPublic = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../../ui/public",
@@ -711,14 +711,14 @@ describe("AppDefinition catalog", () => {
}>;
};
const visible = manifest.providers.filter((entry) => entry.catalogVisible);
expect(visible).toHaveLength(40);
expect(visible).toHaveLength(41);
expect(new Set(visible.map((entry) => entry.slug))).toHaveProperty(
"size",
40,
41,
);
expect(new Set(visible.map((entry) => entry.localAsset))).toHaveProperty(
"size",
40,
41,
);
expect(new Set(APP_STORE_DEFINITIONS.map((entry) => entry.slug))).toEqual(
new Set(visible.map((entry) => entry.slug)),
+1
View File
@@ -4,6 +4,7 @@ import type { AppDefinition, ConnectionMethodDef, FieldDef } from "./types/app-d
import type { ToolConnectionOwnership } from "./types/tool-access.js";
export const CONNECTABLE_APP_SLUGS = new Set([
"agentmail",
...SELF_SERVE_MCP_CANDIDATES.map((entry) => entry.slug),
"zapier",
"slack",
@@ -0,0 +1,50 @@
{
"schemaVersion": 1,
"slug": "agentmail",
"name": "AgentMail",
"description": "Give agents email inboxes and handle each conversation as a task.",
"categories": [
"communication"
],
"featured": false,
"branding": {
"logoUrl": "/brands/apps/agentmail.svg",
"darkLogoUrl": "/brands/apps/agentmail-dark.svg"
},
"urlPatterns": [
"https://console.agentmail.to/*"
],
"methods": [
{
"key": "email-agent",
"label": "Email with an agent",
"purpose": "channel",
"provider": "agentmail",
"transport": "rest_api",
"auth": "api_key",
"ownershipModes": [
"customer"
],
"whenToUse": "Assign an inbox to an agent and manage email conversations in tasks.",
"credentialFields": [
{
"key": "apiKey",
"label": "AgentMail API key",
"type": "password",
"placeholder": "am_…",
"required": true,
"secret": true
}
],
"guidanceMd": "Connect an AgentMail API key, then create or select an inbox for your agent. WebSocket receiving works without a public URL.",
"consoleLinks": {
"keys": "https://console.agentmail.to",
"docs": "https://docs.agentmail.to/inboxes"
},
"riskTier": "S3",
"requiredResourceFilters": [
"inbox"
]
}
]
}
+1
View File
@@ -358,6 +358,7 @@ export const ISSUE_ORIGIN_KINDS = [
"routine_execution",
"stale_active_run_evaluation",
"harness_liveness_escalation",
// Historical origin only; automatic productivity reviews have been retired.
"issue_productivity_review",
"stranded_issue_recovery",
"task_watchdog",
+3 -2
View File
@@ -1122,8 +1122,6 @@ export type {
IssueBlockedInboxReason,
IssueBlockedInboxSeverity,
IssueBlockedInboxState,
IssueProductivityReview,
IssueProductivityReviewTrigger,
IssueRecoveryAction,
IssueWatchdog,
IssueWatchdogStatus,
@@ -2762,3 +2760,6 @@ export type { ExecutionContinuationEnvelope } from "./types/execution-continuati
export type { ExecutionProjection, ExecutionReconciliation, ExecutionBlocker } from "./types/execution-projection.js";
export { EXECUTION_RECONCILIATION_CAUSES, requiresExecutionReconciliation } from "./types/execution-projection.js";
export * from "./types/email.js";
export * from "./validators/email.js";
+1 -1
View File
@@ -2,7 +2,7 @@ import type { ConnectionGrantKind, ToolConnectionOwnership, ToolConnectionPurpos
export type AppCategory = "ai"|"analytics"|"commerce"|"communication"|"content"|"data"|"developer"|"productivity"|"other";
export type OAuthRedirectConstraints = "https-or-loopback-http";
export interface FieldDef { key:string; label:string; type:"text"|"password"|"textarea"|"datetime"|"select"|"checkbox"; required?:boolean; advanced?:boolean; hidden?:boolean; placeholder?:string; helperMd?:string; secret?:boolean; prefix?:string; defaultValue?:string|boolean; validation?:{pattern?:string;maxLength?:number}; options?:Array<{value:string;label:string}>; transport?:{location:"query"|"header";name:string;format?:"string"|"csv"|"boolean";omitFalse?:boolean} }
export interface ConnectionMethodDef { key:string; label?:string; purpose?:ToolConnectionPurpose; provider?:"slack"|"github"|"discord"|"microsoft-teams"|"telegram"; transport:ToolConnectionTransport; auth:"oauth"|"api_key"|"none"; oauthStrategy?:"paperclip_cloud_connector"|"paperclip_id_connector"; connectorProfile?:string; capabilityProfile?:{key:string;label:string;description?:string}; grantKinds?:ConnectionGrantKind[]; ownershipModes:ToolConnectionOwnership[]; whenToUse:string; defaults?:{serverUrl?:string;serverUrlTemplate?:string;discoveryUrl?:string|null;serviceHost?:string;templateKey?:string;authorizationEndpoint?:string;tokenEndpoint?:string;metadataUrl?:string;scopesHint?:string[];oauthAuthorizationParams?:{access_type?:"offline";prompt?:"consent"};toolArgumentDefaults?:Record<string,unknown>}; tenantFields?:FieldDef[]; extensionFields?:FieldDef[]; configRequirements?:{atLeastOneOf?:string[]}; credentialFields?:FieldDef[]; keyPlacement?:{location:"header"|"query"|"body_json"|"env";name:string;prefix?:string|null}; credentialSources?:{vercelConnect?:{services:string[];principalModes:VercelConnectPrincipalMode[];scopes:string[];header:{name:string;prefix?:string|null}}}; guidanceMd:string; consoleLinks?:{register?:string;keys?:string;settings?:string;docs?:string}; warnings?:string[]; variants?:Array<{key:string;label:string;whenToUse:string;tenantFields?:FieldDef[]}>; riskTier:"S1"|"S2"|"S3"|"S4"; requiredResourceFilters?:string[] }
export interface ConnectionMethodDef { key:string; label?:string; purpose?:ToolConnectionPurpose; provider?:"slack"|"github"|"discord"|"microsoft-teams"|"telegram" | "agentmail"; transport:ToolConnectionTransport; auth:"oauth"|"api_key"|"none"; oauthStrategy?:"paperclip_cloud_connector"|"paperclip_id_connector"; connectorProfile?:string; capabilityProfile?:{key:string;label:string;description?:string}; grantKinds?:ConnectionGrantKind[]; ownershipModes:ToolConnectionOwnership[]; whenToUse:string; defaults?:{serverUrl?:string;serverUrlTemplate?:string;discoveryUrl?:string|null;serviceHost?:string;templateKey?:string;authorizationEndpoint?:string;tokenEndpoint?:string;metadataUrl?:string;scopesHint?:string[];oauthAuthorizationParams?:{access_type?:"offline";prompt?:"consent"};toolArgumentDefaults?:Record<string,unknown>}; tenantFields?:FieldDef[]; extensionFields?:FieldDef[]; configRequirements?:{atLeastOneOf?:string[]}; credentialFields?:FieldDef[]; keyPlacement?:{location:"header"|"query"|"body_json"|"env";name:string;prefix?:string|null}; credentialSources?:{vercelConnect?:{services:string[];principalModes:VercelConnectPrincipalMode[];scopes:string[];header:{name:string;prefix?:string|null}}}; guidanceMd:string; consoleLinks?:{register?:string;keys?:string;settings?:string;docs?:string}; warnings?:string[]; variants?:Array<{key:string;label:string;whenToUse:string;tenantFields?:FieldDef[]}>; riskTier:"S1"|"S2"|"S3"|"S4"; requiredResourceFilters?:string[] }
export interface AppDefinition { schemaVersion:1; slug:string; name:string; description:string; categories:AppCategory[]; featured?:boolean; branding:{logoUrl:string;darkLogoUrl?:string;backgroundColor?:string;accentColor?:string}; urlPatterns:string[]; docsUrl?:string; setupPrerequisite?:{title:string;description:string;steps?:string[];actionLabel:string;actionUrl:string}; redirectConstraints?:OAuthRedirectConstraints; methods:ConnectionMethodDef[]; suggestable?:boolean; availability?:{available:boolean;reason?:string;robotEmail?:string}; ownershipAvailability?:Partial<Record<ToolConnectionOwnership,boolean>> }
export type SelfServeMcpAuthMode =
+1
View File
@@ -11,6 +11,7 @@ export const ATTENTION_SOURCE_KINDS = [
"issue_thread_interaction",
"join_request",
"recovery_action",
// Legacy persisted decision sources remain readable; no feed items are generated.
"productivity_review",
"blocker_attention",
"review",
@@ -5,6 +5,7 @@ export const CHAT_PROVIDERS = [
"discord",
"microsoft-teams",
"telegram",
"agentmail",
] as const;
export type ChatProvider = (typeof CHAT_PROVIDERS)[number];
@@ -202,10 +203,16 @@ export interface ChatEndpointSetupSecret {
webhookSecret: string;
}
export type ChannelPublicationMode = "automatic" | "explicit";
export type ExternalMessageExecutionPolicy = "restricted" | "agent";
export interface ChatEndpoint {
id: string;
companyId: string;
connectionId: string;
/** Older clients omit these fields; defaults are automatic/restricted. */
publicationMode?: ChannelPublicationMode;
externalExecutionPolicy?: ExternalMessageExecutionPolicy;
provider: ChatProvider;
publicId: string;
status: ChatEndpointStatus;
+54
View File
@@ -0,0 +1,54 @@
export interface EmailEnvelope {
from: string;
to: string[];
cc?: string[];
bcc?: string[];
replyTo?: string[];
subject: string;
}
export type EmailDeliveryOutcome =
| "queued"
| "sent"
| "delivered"
| "failed"
| "uncertain";
export interface EmailMessage extends EmailEnvelope {
id: string;
providerMessageId: string;
direction: "inbound" | "outbound";
text: string;
fullText: string;
commentId: string | null;
attachmentIds: string[];
timestamp: string;
automatic: boolean;
}
export interface EmailEndpointSummary {
id: string;
companyId: string;
connectionId: string;
assignedAgentId: string;
address: string | null;
status: string;
receiveMode: "websocket" | "webhook";
lastError: string | null;
lastSyncAt: string | null;
}
export interface EmailPublicationSummary {
request?: import("../validators/email.js").EmailSendInput;
createdAt?: string;
id: string;
issueId: string;
conversationId: string;
outcome: EmailDeliveryOutcome;
error: string | null;
providerMessageId: string | null;
}
export interface EmailThreadSummary {
conversationId: string;
issueId: string;
endpoint: EmailEndpointSummary;
subject: string;
messages: EmailMessage[];
publications: EmailPublicationSummary[];
}
+2 -2
View File
@@ -683,8 +683,6 @@ export type {
IssueBlockedInboxReason,
IssueBlockedInboxSeverity,
IssueBlockedInboxState,
IssueProductivityReview,
IssueProductivityReviewTrigger,
IssueRecoveryAction,
SuccessfulRunHandoffState,
SuccessfulRunHandoffStateKind,
@@ -1066,3 +1064,5 @@ export type {
} from "./plugin.js";
export * from "./app-definition.js";
export * from "./chat-channels.js";
export * from "./email.js";
-18
View File
@@ -537,22 +537,6 @@ export interface IssueUnblockDescriptor {
action: string;
}
export type IssueProductivityReviewTrigger =
| "no_comment_streak"
| "long_active_duration"
| "high_churn";
export interface IssueProductivityReview {
reviewIssueId: string;
reviewIdentifier: string | null;
status: IssueStatus;
priority: IssuePriority;
trigger: IssueProductivityReviewTrigger | null;
noCommentStreak: number | null;
createdAt: Date;
updatedAt: Date;
}
export interface IssueRecoveryAction {
id: string;
companyId: string;
@@ -846,7 +830,6 @@ export interface Issue {
unblockDescriptor?: IssueUnblockDescriptor | null;
blockedTransitionAt?: Date | null;
blockedOwnerNotifiedAt?: Date | null;
productivityReview?: IssueProductivityReview | null;
activeRecoveryAction?: IssueRecoveryAction | null;
successfulRunHandoff?: SuccessfulRunHandoffState | null;
executionBlocker?: ExecutionBlocker | null;
@@ -919,7 +902,6 @@ export type CompactIssue = Pick<
blockerAttention?: IssueBlockerAttention;
reviewAttention?: IssueReviewAttention;
blockedInboxAttention?: IssueBlockedInboxAttention | null;
productivityReview?: IssueProductivityReview | null;
scheduledRetry?: IssueScheduledRetry | null;
liveDescendantCount?: number;
myLastTouchAt?: Date | null;
+2
View File
@@ -242,6 +242,8 @@ export const resetAgentSessionSchema = z.object({
export type ResetAgentSession = z.infer<typeof resetAgentSessionSchema>;
export const testAdapterEnvironmentSchema = z.object({
/** Saved agent whose redacted environment entries are restored for this probe. */
agentId: z.string().guid().optional(),
/** One-shot provider keys for a probe. Never persist these in agent config. */
testCredentials: z.object({
ANTHROPIC_API_KEY: z.string().max(16384),
@@ -5,6 +5,6 @@ const appBrandAssetUrlSchema=z.string().refine((value)=>{
try{return new URL(value).protocol==="https:";}catch{return false;}
},{message:"Brand assets must be HTTPS URLs or local /brands/apps SVG/PNG paths"});
const field=z.object({key:z.string().min(1),label:z.string().min(1),type:z.enum(["text","password","textarea","datetime","select","checkbox"]),required:z.boolean().optional(),advanced:z.boolean().optional(),hidden:z.boolean().optional(),placeholder:z.string().optional(),helperMd:z.string().optional(),secret:z.boolean().optional(),prefix:z.string().optional(),defaultValue:z.union([z.string(),z.boolean()]).optional(),validation:z.object({pattern:z.string().optional(),maxLength:z.number().int().positive().optional()}).optional(),options:z.array(z.object({value:z.string(),label:z.string()})).optional(),transport:z.object({location:z.enum(["query","header"]),name:z.string().min(1),format:z.enum(["string","csv","boolean"]).optional(),omitFalse:z.boolean().optional()}).optional()}).superRefine((v,c)=>{if(v.required&&v.type!=="checkbox"&&!v.placeholder)c.addIssue({code:"custom",message:"Required fields need placeholders",path:["placeholder"]});if(v.type==="select"&&(!v.options||v.options.length===0))c.addIssue({code:"custom",message:"Select fields need options",path:["options"]});if(v.hidden&&v.defaultValue===undefined)c.addIssue({code:"custom",message:"Hidden fields need defaults",path:["defaultValue"]})});
export const connectionMethodDefSchema=z.object({key:z.string().min(1),label:z.string().min(1).optional(),purpose:toolConnectionPurposeSchema.optional(),provider:z.enum(["slack","github","discord","microsoft-teams","telegram"]).optional(),transport:toolConnectionTransportSchema,auth:z.enum(["oauth","api_key","none"]),oauthStrategy:z.enum(["paperclip_cloud_connector","paperclip_id_connector"]).optional(),connectorProfile:z.string().regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/).optional(),capabilityProfile:z.object({key:z.string().min(1),label:z.string().min(1),description:z.string().min(1).optional()}).optional(),grantKinds:z.array(connectionGrantKindSchema).min(1).optional(),ownershipModes:z.array(toolConnectionOwnershipSchema).min(1),whenToUse:z.string().min(1),defaults:z.object({serverUrl:z.string().url().optional(),serverUrlTemplate:z.string().regex(/^https:\/\//).optional(),discoveryUrl:z.string().url().nullable().optional(),serviceHost:z.string().optional(),templateKey:z.string().optional(),authorizationEndpoint:z.string().url().optional(),tokenEndpoint:z.string().url().optional(),metadataUrl:z.string().url().optional(),scopesHint:z.array(z.string()).optional(),oauthAuthorizationParams:z.object({access_type:z.literal("offline").optional(),prompt:z.literal("consent").optional()}).optional(),toolArgumentDefaults:z.record(z.string(),z.unknown()).optional()}).optional(),tenantFields:z.array(field).optional(),extensionFields:z.array(field).optional(),configRequirements:z.object({atLeastOneOf:z.array(z.string().min(1)).min(1).optional()}).optional(),credentialFields:z.array(field).optional(),keyPlacement:z.object({location:z.enum(["header","query","body_json","env"]),name:z.string().min(1),prefix:z.string().nullable().optional()}).optional(),credentialSources:z.object({vercelConnect:z.object({services:z.array(z.string().min(1)).min(1),principalModes:z.array(z.enum(["app","user"])).min(1),scopes:z.array(z.string().min(1)).min(1),header:z.object({name:z.string().min(1),prefix:z.string().nullable().optional()})}).optional()}).optional(),guidanceMd:z.string().min(1),consoleLinks:z.object({register:z.string().url().optional(),keys:z.string().url().optional(),settings:z.string().url().optional(),docs:z.string().url().optional()}).optional(),warnings:z.array(z.string()).optional(),variants:z.array(z.object({key:z.string(),label:z.string(),whenToUse:z.string(),tenantFields:z.array(field).optional()})).optional(),riskTier:z.enum(["S1","S2","S3","S4"]),requiredResourceFilters:z.array(z.string()).optional()}).superRefine((v,c)=>{const purpose=v.purpose??"tool";if(v.transport==="chat_sdk"&&purpose!=="channel")c.addIssue({code:"custom",message:"Chat SDK methods must be channel connections",path:["purpose"]});if(purpose==="channel"&&v.transport!=="chat_sdk")c.addIssue({code:"custom",message:"Channel connections must use the Chat SDK transport",path:["transport"]});if(purpose==="channel"&&!v.provider)c.addIssue({code:"custom",message:"Channel connections require a chat provider",path:["provider"]});if(v.auth==="api_key"&&!v.keyPlacement&&purpose!=="channel")c.addIssue({code:"custom",message:"API-key tool methods require keyPlacement",path:["keyPlacement"]});if(v.oauthStrategy&&v.auth!=="oauth")c.addIssue({code:"custom",message:"OAuth strategies require OAuth auth",path:["oauthStrategy"]});if(v.oauthStrategy&&!v.connectorProfile)c.addIssue({code:"custom",message:"Paperclip Cloud connector methods require connectorProfile",path:["connectorProfile"]});if(v.connectorProfile&&!v.oauthStrategy)c.addIssue({code:"custom",message:"connectorProfile requires a Paperclip Cloud OAuth strategy",path:["connectorProfile"]});if(v.credentialSources?.vercelConnect&&(v.transport!=="mcp_remote"||v.auth==="none"))c.addIssue({code:"custom",message:"Vercel Connect requires an authenticated remote MCP method",path:["credentialSources","vercelConnect"]});const keys=new Set([...(v.tenantFields??[]),...(v.extensionFields??[])].map((entry)=>entry.key));for(const key of v.configRequirements?.atLeastOneOf??[])if(!keys.has(key))c.addIssue({code:"custom",message:"Config requirement references an unknown field",path:["configRequirements","atLeastOneOf"]});if(v.defaults?.serverUrl&&v.defaults.serverUrlTemplate)c.addIssue({code:"custom",message:"Use either serverUrl or serverUrlTemplate",path:["defaults"]});for(const placeholder of v.defaults?.serverUrlTemplate?.matchAll(/\{([a-zA-Z0-9_-]+)\}/g)??[])if(!keys.has(placeholder[1]))c.addIssue({code:"custom",message:"Server URL template references an unknown field",path:["defaults","serverUrlTemplate"]})});
export const connectionMethodDefSchema=z.object({key:z.string().min(1),label:z.string().min(1).optional(),purpose:toolConnectionPurposeSchema.optional(),provider:z.enum(["slack","github","discord","microsoft-teams","telegram","agentmail"]).optional(),transport:toolConnectionTransportSchema,auth:z.enum(["oauth","api_key","none"]),oauthStrategy:z.enum(["paperclip_cloud_connector","paperclip_id_connector"]).optional(),connectorProfile:z.string().regex(/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/).optional(),capabilityProfile:z.object({key:z.string().min(1),label:z.string().min(1),description:z.string().min(1).optional()}).optional(),grantKinds:z.array(connectionGrantKindSchema).min(1).optional(),ownershipModes:z.array(toolConnectionOwnershipSchema).min(1),whenToUse:z.string().min(1),defaults:z.object({serverUrl:z.string().url().optional(),serverUrlTemplate:z.string().regex(/^https:\/\//).optional(),discoveryUrl:z.string().url().nullable().optional(),serviceHost:z.string().optional(),templateKey:z.string().optional(),authorizationEndpoint:z.string().url().optional(),tokenEndpoint:z.string().url().optional(),metadataUrl:z.string().url().optional(),scopesHint:z.array(z.string()).optional(),oauthAuthorizationParams:z.object({access_type:z.literal("offline").optional(),prompt:z.literal("consent").optional()}).optional(),toolArgumentDefaults:z.record(z.string(),z.unknown()).optional()}).optional(),tenantFields:z.array(field).optional(),extensionFields:z.array(field).optional(),configRequirements:z.object({atLeastOneOf:z.array(z.string().min(1)).min(1).optional()}).optional(),credentialFields:z.array(field).optional(),keyPlacement:z.object({location:z.enum(["header","query","body_json","env"]),name:z.string().min(1),prefix:z.string().nullable().optional()}).optional(),credentialSources:z.object({vercelConnect:z.object({services:z.array(z.string().min(1)).min(1),principalModes:z.array(z.enum(["app","user"])).min(1),scopes:z.array(z.string().min(1)).min(1),header:z.object({name:z.string().min(1),prefix:z.string().nullable().optional()})}).optional()}).optional(),guidanceMd:z.string().min(1),consoleLinks:z.object({register:z.string().url().optional(),keys:z.string().url().optional(),settings:z.string().url().optional(),docs:z.string().url().optional()}).optional(),warnings:z.array(z.string()).optional(),variants:z.array(z.object({key:z.string(),label:z.string(),whenToUse:z.string(),tenantFields:z.array(field).optional()})).optional(),riskTier:z.enum(["S1","S2","S3","S4"]),requiredResourceFilters:z.array(z.string()).optional()}).superRefine((v,c)=>{const purpose=v.purpose??"tool";if(v.transport==="chat_sdk"&&purpose!=="channel")c.addIssue({code:"custom",message:"Chat SDK methods must be channel connections",path:["purpose"]});if(purpose==="channel"&&v.transport!=="chat_sdk"&&!(v.provider==="agentmail"&&v.transport==="rest_api"))c.addIssue({code:"custom",message:"Channel connections must use the Chat SDK transport",path:["transport"]});if(purpose==="channel"&&!v.provider)c.addIssue({code:"custom",message:"Channel connections require a chat provider",path:["provider"]});if(v.auth==="api_key"&&!v.keyPlacement&&purpose!=="channel")c.addIssue({code:"custom",message:"API-key tool methods require keyPlacement",path:["keyPlacement"]});if(v.oauthStrategy&&v.auth!=="oauth")c.addIssue({code:"custom",message:"OAuth strategies require OAuth auth",path:["oauthStrategy"]});if(v.oauthStrategy&&!v.connectorProfile)c.addIssue({code:"custom",message:"Paperclip Cloud connector methods require connectorProfile",path:["connectorProfile"]});if(v.connectorProfile&&!v.oauthStrategy)c.addIssue({code:"custom",message:"connectorProfile requires a Paperclip Cloud OAuth strategy",path:["connectorProfile"]});if(v.credentialSources?.vercelConnect&&(v.transport!=="mcp_remote"||v.auth==="none"))c.addIssue({code:"custom",message:"Vercel Connect requires an authenticated remote MCP method",path:["credentialSources","vercelConnect"]});const keys=new Set([...(v.tenantFields??[]),...(v.extensionFields??[])].map((entry)=>entry.key));for(const key of v.configRequirements?.atLeastOneOf??[])if(!keys.has(key))c.addIssue({code:"custom",message:"Config requirement references an unknown field",path:["configRequirements","atLeastOneOf"]});if(v.defaults?.serverUrl&&v.defaults.serverUrlTemplate)c.addIssue({code:"custom",message:"Use either serverUrl or serverUrlTemplate",path:["defaults"]});for(const placeholder of v.defaults?.serverUrlTemplate?.matchAll(/\{([a-zA-Z0-9_-]+)\}/g)??[])if(!keys.has(placeholder[1]))c.addIssue({code:"custom",message:"Server URL template references an unknown field",path:["defaults","serverUrlTemplate"]})});
export const appDefinitionSchema=z.object({schemaVersion:z.literal(1),slug:z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),name:z.string().min(1),description:z.string().min(1),categories:z.array(z.enum(["ai","analytics","commerce","communication","content","data","developer","productivity","other"])).min(1),featured:z.boolean().optional(),branding:z.object({logoUrl:appBrandAssetUrlSchema,darkLogoUrl:appBrandAssetUrlSchema.optional(),backgroundColor:z.string().optional(),accentColor:z.string().optional()}),urlPatterns:z.array(z.string()),docsUrl:z.string().url().optional(),setupPrerequisite:z.object({title:z.string().min(1),description:z.string().min(1),steps:z.array(z.string().min(1)).min(1).optional(),actionLabel:z.string().min(1),actionUrl:z.string().url()} ).optional(),redirectConstraints:z.enum(["https-or-loopback-http"]).optional(),methods:z.array(connectionMethodDefSchema).min(1),suggestable:z.boolean().optional(),availability:z.object({available:z.boolean(),reason:z.string().optional(),robotEmail:z.string().optional()}).optional(),ownershipAvailability:z.object({platform_shared:z.boolean().optional(),platform_provisioned:z.boolean().optional(),customer:z.boolean().optional(),dcr:z.boolean().optional()}).optional()});
export const appDefinitionsSchema=z.array(appDefinitionSchema).superRefine((v,c)=>{const s=new Set<string>();v.forEach((a,i)=>{if(s.has(a.slug))c.addIssue({code:"custom",message:"Duplicate slug",path:[i,"slug"]});s.add(a.slug)})});
@@ -69,7 +69,7 @@ const chatEndpointCredentialsSchema = z
export const createChatEndpointSchema = z
.object({
provider: chatProviderSchema,
provider: chatProviderSchema.exclude(["agentmail"]),
assignedAgentId: z.string().uuid(),
applicationId: z.string().uuid().optional(),
name: z.string().trim().min(1).max(160).optional(),
+75
View File
@@ -0,0 +1,75 @@
import { z } from "zod";
const address = z.string().trim().email().max(320);
const addresses = z.array(address).max(50);
export const emailEndpointSetupSchema = z
.object({
assignedAgentId: z.string().uuid(),
applicationId: z.string().uuid().optional(),
apiKey: z.string().min(1).max(4096).optional(),
credentialConnectionId: z.string().uuid().optional(),
inboxId: address.optional(),
username: z
.string()
.regex(/^[a-zA-Z0-9._-]+$/)
.max(64)
.optional(),
domain: z.string().max(253).optional(),
receiveMode: z.enum(["websocket", "webhook"]).default("websocket"),
idempotencyKey: z.string().uuid(),
})
.strict()
.refine((v) => Boolean(v.apiKey) !== Boolean(v.credentialConnectionId), {
message: "Supply an API key or a saved connection, not both",
});
export const emailSendSchema = z
.object({
endpointId: z.string().uuid(),
parentIssueId: z.string().uuid().optional(),
conversationId: z.string().uuid().optional(),
replyToMessageId: z.string().min(1).max(998).optional(),
replyAll: z.boolean().default(false),
to: addresses.optional(),
cc: addresses.optional(),
bcc: addresses.optional(),
subject: z
.string()
.trim()
.min(1)
.max(998)
.regex(/^[^\r\n]+$/)
.optional(),
text: z.string().trim().min(1).max(100_000),
attachmentIds: z.array(z.string().uuid()).max(20).default([]),
idempotencyKey: z.string().uuid(),
})
.strict()
.superRefine((v, ctx) => {
const fail = (message: string) => ctx.addIssue({ code: "custom", message });
if (v.conversationId) {
if (!v.replyToMessageId) fail("A reply requires its exact message ID");
if (v.parentIssueId || v.to || v.cc || v.bcc || v.subject)
fail(
"Reply recipients come from the original message; use replyAll explicitly",
);
} else {
if (!v.parentIssueId || !v.to?.length || !v.subject)
fail("A new email requires a parent task, recipient, and subject");
if (v.replyToMessageId || v.replyAll)
fail("A new email cannot be a reply");
}
});
export type EmailEndpointSetupInput = z.infer<typeof emailEndpointSetupSchema>;
export type EmailSendInput = z.infer<typeof emailSendSchema>;
export const emailConnectionSchema = z
.object({
apiKey: z.string().min(1).max(4096),
grantKind: z.enum(["user", "organization"]).default("user"),
allAgents: z.boolean().default(false),
agentIds: z.array(z.string().uuid()).max(500).default([]),
idempotencyKey: z.string().uuid(),
})
.strict();
export type EmailConnectionInput = z.infer<typeof emailConnectionSchema>;
+2
View File
@@ -977,3 +977,5 @@ export * from "./skill-policy.js";
export * from "./provider-trace.js";
export * from "./app-definition.js";
export * from "./chat-channels.js";
export * from "./email.js";