Files
PaperClipAI/packages/plugins/sdk/src/index.ts
T
DottaandPaperclip b3343dbd64 feat(connections): add self-serve intent runtime (#12345)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need a governed way to request app connections during issue
work.
> - The catalog now describes the available providers and setup methods.
> - A request must become a durable, company-scoped intent before an
operator acts on it.
> - This pull request adds that intent runtime across server, agent,
CLI, and shared contracts.
> - The benefit is a safe bridge from agent need to operator-approved
setup.

## Linked Issues or Issue Description

Refs #11965

This is stack 7 of 11. It depends on stack 6 and replaces another
reviewable part of #11965.

## What Changed

- Add connection intent types, validation, service logic, and routes.
- Add agent runtime tools and CLI support for connection requests.
- Add issue-thread interaction support for connection intents.
- Add runtime, route, adapter, and contract tests.
- Hold the final resolved-continuation row lock through asynchronous
adapter preparation until an actual process spawn, so parking or
reassignment cannot cross that boundary.
- Report Hermes Gateway's first remote run request through the shared
dispatch hook so the resolved-intent lock is released at the true
dispatch boundary.
- Revalidate the addressed user's live non-viewer membership and
connection-management authority for every intent mutation, including
OAuth completion.

## Verification

- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/tool-access-service.test.ts`
- Result: 176 tests passed.
- `pnpm build`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-stale-queue-invalidation.test.ts` (32 passed;
includes non-process dispatch lock-release coverage)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/connection-intents-service.test.ts -t
"addressed-user mutation"` (1 passed)
- `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/tool-access-service.test.ts -t "binds OAuth
callback completion to the initiating board session"` (1 passed)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter test --
src/gateway/server/execute.test.ts` (23 passed; includes dispatch-hook
ordering and exactly-once coverage)
- `pnpm --filter @paperclipai/hermes-paperclip-adapter typecheck`

## Risks

- A malformed intent could create an unusable operator request.
- Validators and company checks reject invalid or cross-company
requests.
- The final continuation gate holds the issue row lock through adapter
preparation until process or remote dispatch; later operator changes use
the normal active-run interruption path.
- The change does not add a database migration.

> I checked `ROADMAP.md`. This stack continues the existing app
connection work from #11965 and does not duplicate another planned item.

## Model Used

OpenAI Codex, GPT-5. The runtime model ID and context window were not
exposed. The model used reasoning, tool use, and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have linked the public source pull request with `Refs #`
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-08-29 12:08:34 -05:00

448 lines
12 KiB
TypeScript

/**
* `@paperclipai/plugin-sdk` — Paperclip plugin worker-side SDK.
*
* This is the main entrypoint for plugin worker code. For plugin UI bundles,
* import from `@paperclipai/plugin-sdk/ui` instead.
*
* @example
* ```ts
* // Plugin worker entrypoint (dist/worker.ts)
* import { definePlugin, runWorker, z } from "@paperclipai/plugin-sdk";
*
* const plugin = definePlugin({
* async setup(ctx) {
* ctx.logger.info("Plugin starting up");
*
* ctx.events.on("issue.created", async (event) => {
* ctx.logger.info("Issue created", { issueId: event.entityId });
* });
*
* ctx.jobs.register("full-sync", async (job) => {
* ctx.logger.info("Starting full sync", { runId: job.runId });
* // ... sync implementation
* });
*
* ctx.data.register("sync-health", async ({ companyId }) => {
* const state = await ctx.state.get({
* scopeKind: "company",
* scopeId: String(companyId),
* stateKey: "last-sync-at",
* });
* return { lastSync: state };
* });
* },
*
* async onHealth() {
* return { status: "ok" };
* },
* });
*
* export default plugin;
* runWorker(plugin, import.meta.url);
* ```
*
* @see PLUGIN_SPEC.md §14 — SDK Surface
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
*/
// ---------------------------------------------------------------------------
// Main factory
// ---------------------------------------------------------------------------
export { definePlugin } from "./define-plugin.js";
export { createTestHarness, createEnvironmentTestHarness, createFakeEnvironmentDriver, filterEnvironmentEvents, assertEnvironmentEventOrder, assertLeaseLifecycle, assertWorkspaceRealizationLifecycle, assertExecutionLifecycle, assertEnvironmentError } from "./testing.js";
export { createPluginBundlerPresets } from "./bundlers.js";
export { startPluginDevServer, getUiBuildSnapshot } from "./dev-server.js";
export { startWorkerRpcHost, runWorker } from "./worker-rpc-host.js";
export {
createHostClientHandlers,
getRequiredCapability,
CapabilityDeniedError,
InvocationScopeDeniedError,
} from "./host-client-factory.js";
// JSON-RPC protocol helpers and constants
export {
JSONRPC_VERSION,
JSONRPC_ERROR_CODES,
PLUGIN_RPC_ERROR_CODES,
HOST_TO_WORKER_REQUIRED_METHODS,
HOST_TO_WORKER_OPTIONAL_METHODS,
MESSAGE_DELIMITER,
createRequest,
createSuccessResponse,
createErrorResponse,
createNotification,
isJsonRpcRequest,
isJsonRpcNotification,
isJsonRpcResponse,
isJsonRpcSuccessResponse,
isJsonRpcErrorResponse,
serializeMessage,
parseMessage,
JsonRpcParseError,
JsonRpcCallError,
LOGIN_PTY_OUTPUT_NOTIFICATION,
LOGIN_PTY_EXIT_NOTIFICATION,
DUPLEX_CHANNEL_DATA_NOTIFICATION,
DUPLEX_CHANNEL_EXIT_NOTIFICATION,
encodeChannelBytes,
decodeChannelBytes,
_resetIdCounter,
} from "./protocol.js";
// ---------------------------------------------------------------------------
// Type exports
// ---------------------------------------------------------------------------
// Plugin definition and lifecycle types
export type {
PluginDefinition,
PaperclipPlugin,
PluginHealthDiagnostics,
PluginConfigChangeContext,
PluginConfigValidationResult,
PluginWebhookInput,
PluginApiRequestInput,
PluginApiResponse,
} from "./define-plugin.js";
export type {
TestHarness,
TestHarnessOptions,
TestHarnessLogEntry,
EnvironmentTestHarness,
EnvironmentTestHarnessOptions,
EnvironmentEventRecord,
FakeEnvironmentDriverOptions,
} from "./testing.js";
export type {
PluginBundlerPresetInput,
PluginBundlerPresets,
EsbuildLikeOptions,
RollupLikeConfig,
} from "./bundlers.js";
export type { PluginDevServer, PluginDevServerOptions } from "./dev-server.js";
export type {
WorkerRpcHostOptions,
WorkerRpcHost,
RunWorkerOptions,
} from "./worker-rpc-host.js";
export type {
HostServices,
HostClientFactoryOptions,
HostClientHandlers,
} from "./host-client-factory.js";
// JSON-RPC protocol types
export type {
JsonRpcId,
JsonRpcInvocationScope,
JsonRpcInvocationContext,
JsonRpcRequest,
JsonRpcSuccessResponse,
JsonRpcError,
JsonRpcErrorResponse,
JsonRpcResponse,
JsonRpcNotification,
JsonRpcMessage,
JsonRpcErrorCode,
PluginRpcErrorCode,
PluginInvocationScope,
PluginInvocationContext,
WorkerHostCallContext,
InitializeParams,
InitializeResult,
ConfigChangedParams,
ValidateConfigParams,
OnEventParams,
RunJobParams,
GetDataParams,
PerformActionParams,
PluginPerformActionActorType,
PluginPerformActionActorContext,
PluginPerformActionContext,
ExecuteToolParams,
PluginExternalObjectUrlCandidate,
PluginExternalObjectSourceContext,
DetectExternalObjectsParams,
PluginExternalObjectDetection,
DetectExternalObjectsResult,
PluginExternalObjectRecordSnapshot,
ResolveExternalObjectParams,
PluginExternalObjectResolvedSnapshot,
PluginExternalObjectResolveResult,
RefreshExternalObjectsParams,
RefreshExternalObjectsResult,
PluginEnvironmentDiagnostic,
PluginEnvironmentDriverBaseParams,
PluginEnvironmentValidateConfigParams,
PluginEnvironmentValidationResult,
PluginEnvironmentProbeParams,
PluginEnvironmentProbeResult,
PluginEnvironmentLease,
PluginEnvironmentAcquireLeaseParams,
PluginEnvironmentResumeLeaseParams,
PluginEnvironmentReleaseLeaseParams,
PluginEnvironmentDestroyLeaseParams,
PluginEnvironmentRealizeWorkspaceParams,
PluginEnvironmentRealizeWorkspaceResult,
PluginEnvironmentExecuteParams,
PluginEnvironmentExecuteResult,
PluginSyncFileMapping,
PluginPostUploadCommand,
PluginSyncOperation,
PluginEnvironmentSyncInParams,
PluginEnvironmentSyncOutParams,
PluginEnvironmentSyncResult,
PluginEnvironmentInteractiveSetupStatus,
PluginEnvironmentInteractiveSetupConnectionType,
PluginEnvironmentTemplateRefKind,
PluginEnvironmentInteractiveSetupConnectionSummary,
PluginEnvironmentInteractiveSetupConnectionPayload,
PluginEnvironmentInteractiveSetupSession,
PluginEnvironmentStartInteractiveSetupParams,
PluginEnvironmentGetInteractiveSetupParams,
PluginEnvironmentCaptureTemplateParams,
PluginEnvironmentCaptureTemplateResult,
PluginEnvironmentCancelInteractiveSetupParams,
PluginEnvironmentCancelInteractiveSetupResult,
PluginEnvironmentDeleteTemplateParams,
PluginEnvironmentDeleteTemplateResult,
PluginModalBoundsRequest,
PluginRenderCloseEvent,
PluginLauncherRenderContextSnapshot,
HostToWorkerMethods,
HostToWorkerMethodName,
WorkerToHostMethods,
WorkerToHostMethodName,
HostToWorkerRequest,
HostToWorkerResponse,
WorkerToHostRequest,
WorkerToHostResponse,
WorkerToHostNotifications,
WorkerToHostNotificationName,
} from "./protocol.js";
// Plugin context and all client interfaces
export type {
PluginContext,
PluginConfigClient,
PluginLocalFolderProblem,
PluginLocalFolderStatus,
PluginLocalFolderConfigureInput,
PluginLocalFolderListOptions,
PluginLocalFolderEntry,
PluginLocalFolderListing,
PluginLocalFoldersClient,
PluginEventsClient,
PluginJobsClient,
PluginLaunchersClient,
PluginHttpClient,
PluginSecretsClient,
PluginActivityClient,
PluginActivityLogEntry,
PluginStateClient,
PluginEntitiesClient,
PluginProjectsClient,
PluginExecutionWorkspacesClient,
PluginSkillsClient,
PluginCompaniesClient,
PluginIssuesClient,
PluginIssueMutationActor,
PluginIssueRelationsClient,
PluginIssueRelationSummary,
PluginIssueCheckoutOwnership,
PluginIssueWakeupResult,
PluginIssueWakeupBatchResult,
PluginIssueRunSummary,
PluginIssueApprovalSummary,
PluginIssueCostSummary,
PluginBudgetIncidentSummary,
PluginIssueInvocationBlockSummary,
PluginIssueOrchestrationSummary,
PluginIssueSubtreeOptions,
PluginIssueAssigneeSummary,
PluginIssueSubtree,
PluginIssueSummariesClient,
PluginAgentsClient,
PluginAccessClient,
PluginAccessMembersClient,
PluginAccessInvitesClient,
PluginAccessMember,
PluginAccessInvite,
PluginAuthorizationClient,
PluginAuthorizationPolicySummary,
PluginAuthorizationPolicyRecord,
PluginAssignmentPreviewInput,
PluginAuthorizationDecisionResult,
PluginAuthorizationAuditEntry,
PluginAgentSessionsClient,
AgentSession,
AgentSessionEvent,
AgentSessionSendResult,
PluginGoalsClient,
PluginDataClient,
PluginActionsClient,
PluginStreamsClient,
PluginToolsClient,
PluginMetricsClient,
PluginTelemetryClient,
PluginLogger,
PluginTracer,
PluginSpan,
} from "./types.js";
// Tracer no-op default (a value, so it re-exports here, not in the type block).
export { NOOP_PLUGIN_TRACER, NOOP_PLUGIN_SPAN } from "./types.js";
// Supporting types for context clients
export type {
ScopeKey,
EventFilter,
PluginEvent,
PluginJobContext,
PluginLauncherRegistration,
ToolRunContext,
ToolResult,
PluginEntityUpsert,
PluginEntityRecord,
PluginEntityQuery,
PluginWorkspace,
PluginExecutionWorkspaceMetadata,
Company,
Project,
Issue,
IssueComment,
IssueDocumentSummary,
Agent,
Goal,
PermissionKey,
PrincipalPermissionGrant,
PrincipalType,
PluginDatabaseClient,
HumanCompanyMembershipRole,
MembershipStatus,
EnvSecretRefBinding,
} from "./types.js";
// Manifest and constant types re-exported from @paperclipai/shared
// Plugin authors import manifest types from here so they have a single
// dependency (@paperclipai/plugin-sdk) for all plugin authoring needs.
export type {
PaperclipPluginManifestV1,
PluginJobDeclaration,
PluginWebhookDeclaration,
PluginToolDeclaration,
PluginEnvironmentDriverDeclaration,
PluginEnvironmentTemplateConfigBinding,
PluginManagedAgentDeclaration,
PluginManagedAgentResolution,
PluginManagedProjectDeclaration,
PluginManagedProjectResolution,
PluginManagedRoutineDeclaration,
PluginManagedRoutineResolution,
PluginManagedSkillDeclaration,
PluginManagedSkillFileDeclaration,
PluginManagedSkillResolution,
CompanySkill,
PluginManagedResourceKind,
PluginManagedResourceRef,
PluginUiSlotDeclaration,
PluginUiDeclaration,
PluginLauncherActionDeclaration,
PluginLauncherRenderDeclaration,
PluginLauncherDeclaration,
PluginMinimumHostVersion,
PluginDatabaseDeclaration,
PluginApiRouteCompanyResolution,
PluginApiRouteDeclaration,
PluginLocalFolderDeclaration,
PluginCompanySettings,
PluginObjectReferenceRefreshPolicy,
PluginObjectReferenceProviderDeclaration,
PluginRecord,
PluginDatabaseNamespaceRecord,
PluginMigrationRecord,
PluginConfig,
JsonSchema,
PluginStatus,
PluginCategory,
PluginCapability,
PluginUiSlotType,
PluginUiSlotEntityType,
PluginLauncherPlacementZone,
PluginLauncherAction,
PluginLauncherBounds,
PluginLauncherRenderEnvironment,
PluginStateScopeKind,
PluginJobStatus,
PluginJobRunStatus,
PluginJobRunTrigger,
PluginWebhookDeliveryStatus,
PluginDatabaseCoreReadTable,
PluginDatabaseMigrationStatus,
PluginDatabaseNamespaceMode,
PluginDatabaseNamespaceStatus,
PluginApiRouteAuthMode,
PluginApiRouteCheckoutPolicy,
PluginApiRouteMethod,
PluginEventType,
PluginBridgeErrorCode,
ConnectionIntentInteraction,
ConnectionIntentPayload,
ConnectionIntentResult,
ConnectionIntentSetupOptions,
ConnectionRequestResult,
ConnectionsSearchResult,
} from "./types.js";
// ---------------------------------------------------------------------------
// Zod re-export
// ---------------------------------------------------------------------------
/**
* Zod is re-exported for plugin authors to use when defining their
* `instanceConfigSchema` and tool `parametersSchema`.
*
* Plugin authors do not need to add a separate `zod` dependency.
*
* @see PLUGIN_SPEC.md §14.1 — Example SDK Shape
*
* @example
* ```ts
* import { z } from "@paperclipai/plugin-sdk";
*
* const configSchema = z.object({
* apiKey: z.string().describe("Your API key"),
* workspace: z.string().optional(),
* });
* ```
*/
export { z } from "zod";
// ---------------------------------------------------------------------------
// Constants re-exports (for plugin code that needs to check values at runtime)
// ---------------------------------------------------------------------------
export {
PLUGIN_API_VERSION,
PLUGIN_STATUSES,
PLUGIN_CATEGORIES,
PLUGIN_CAPABILITIES,
PLUGIN_UI_SLOT_TYPES,
PLUGIN_UI_SLOT_ENTITY_TYPES,
PLUGIN_RESERVED_COMPANY_SETTINGS_ROUTE_SEGMENTS,
PLUGIN_STATE_SCOPE_KINDS,
PLUGIN_JOB_STATUSES,
PLUGIN_JOB_RUN_STATUSES,
PLUGIN_JOB_RUN_TRIGGERS,
PLUGIN_WEBHOOK_DELIVERY_STATUSES,
PLUGIN_EVENT_TYPES,
PLUGIN_BRIDGE_ERROR_CODES,
PERMISSION_KEYS,
HUMAN_COMPANY_MEMBERSHIP_ROLES,
HUMAN_COMPANY_MEMBERSHIP_ROLE_LABELS,
MEMBERSHIP_STATUSES,
PRINCIPAL_TYPES,
} from "@paperclipai/shared";