mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-30 21:49:18 +01:00
## Thinking Path > - Paperclip lets people manage AI agents and govern their access to outside services. > - Apps connections keep provider credentials in the instance vault and mediate every action through grants, policies, and audit. > - Agents currently have no Apps connection for reading or managing 1Password items with an operator-supplied service-account token. > - 1Password supports this through its official SDK; its documentation MCP server does not expose vault operations. > - This pull request adds a one-field service-account connection and registers eight reviewed SDK actions through the existing native-provider catalog. > - Operators can select vault permissions in 1Password and agent/action access in Paperclip without installing a CLI or handing durable credentials to agents. ## Linked Issues or Issue Description Related: #16144 adds 1Password as Paperclip's secret-storage backend. This PR adds the separate Apps/tool-access flow with a per-connection credential; it does not change secret providers or use a server-wide ambient token. **Subsystem affected** Apps catalog, native tool-provider registry, and shared connection setup. **Problem or motivation** People need to paste their own `OP_SERVICE_ACCOUNT_TOKEN`, find official service-account setup instructions, and authorize agents to use the selected vaults through ordinary Paperclip governance. **Proposed solution** Add `1password/service-account`, with a password field labeled **Paste your 1Password Service Account token** and a link to [1Password's creation instructions](https://www.1password.dev/service-accounts/get-started#create-a-service-account). Use the pinned official `@1password/sdk` 0.5.0 for vault/item listing, item and secret-reference reads, and item create/update/archive/delete. **Alternatives considered** A remote MCP card would not provide vault access. A local CLI or a third-party MCP package adds installation and credential surfaces that the official SDK avoids. ## What Changed - Added the source and generated catalog definition, permission review, official light/dark artwork, documentation, and capability-map references. - Added a provider-owned SDK bridge with strict schemas, account-domain validation, contract hashes, fixed errors, bounded output, and disposable workers with cancellation/deadlines. - Registered the provider through master's native-provider catalog; existing setup, health, discovery, gateway, policy, and audit owners remain in place. - Labeled the single password field **Paste your 1Password Service Account token**. The shared formatter preserves explicit credential prompts; ordinary key labels retain their existing presentation. - Added credential-ownership, gateway read/write, policy/revocation, worker, setup UI, and interactive Storybook coverage. ## Verification - Final review follow-up: added two deterministic worker-entry tests for nested exact-token redaction and oversized-output rejection. Removing each guard caused exactly its regression test to fail. The focused provider suite passed **15 tests**, and the official server typecheck passed after rebuilding stale workspace SDK artifacts. Current head `569cb7f9bd57db791fb13a6034ce69bf50878cb1` passed the full [CI run](https://github.com/paperclipai/paperclip/actions/runs/40016166260), including typecheck, build, unit/integration tests, and all eight browser shards. The required `ci / verify` and `ci / e2e` gates passed. [Greptile](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6353731805) and [Paperclip Review](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6372165895) each rated this exact head **5/5**, with no open findings. Paperclip Review completed with a neutral check because its scoped reviewer could not inspect CI; the full CI result was verified separately. Other neutral checks and intentionally skipped Storybook jobs contain no actionable findings. - CI recovery on the same commit: the first attempt lost six jobs. Five logged simultaneous runner shutdowns at 18:49 UTC on 2026-10-23; the sixth stopped logging before a test result and its root cause was not established. No assertion failure appeared in the retrieved logs. All six jobs and the aggregate gates passed one failed-job retry. [CI run](https://github.com/paperclipai/paperclip/actions/runs/40005754936). - Copy follow-up: the catalog, provider, and setup UI suites passed **275 tests**. The governed gateway lifecycle passed **3 tests** (one live-only test skipped), including create, update, archive, and delete. UI token gates and map checks passed. The branch was rebased onto `9c442cf48e` to resolve additive catalog conflicts while retaining the Vercel and Supabase entries. The existing live write proof below remains applicable; this follow-up changes presentation only. - Five affected suites, including the existing Daytona consumer, passed **290 tests** (one opt-in live test skipped in fixture mode). Full `pnpm -r typecheck` and `pnpm build` passed. The local broad-suite attempt could not complete because PostgreSQL semaphore exhaustion prevented a competing integration suite from starting; the complete affected-file retry passed after disposable-process cleanup. The prior commit `f208a8aff6dc28859cc3f343497e18d85f124579` passed the complete CI matrix. All CI build, typecheck, test, and browser gates also passed on the earlier commit `9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d`. The separate Paperclip Review reported a worker-result regression-test gap on that head; the follow-up above addresses it. Its review thread is resolved, and both fresh current-head reviews are clean as recorded above. - Token gates, module boundaries, brand validation, map checks, and Storybook catalog checks passed. - Direct SDK create/archive/delete and rejected-call coverage passed (13 provider tests); the Storybook Permissions/Test play completed successfully in Chromium. Server and UI typechecks passed after those fixes. Greptile passed prior commit `9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d` at **5/5**. Both reviewers also passed the worker-test follow-up at **5/5** as recorded above. - Live 1Password proof on 2026-10-22: connect, eight-action discovery, vault listing through an actual run-scoped gateway, refresh, reconnect preserving identity, another read, removal, and denial through the retained session passed. Credential absence was asserted in responses/audit; an exact-token scan of inspected logs and changed files passed. - Real browser: Connectors → 1Password → token entry → personal access limited to one disposable agent → completion → Permissions → Test `onepassword_list_vaults` succeeded. The disposable connection was removed afterward. - Compiled-worker smoke initially exposed inherited Node launch flags. Workers now use explicit empty `execArgv`; the compiled worker then authenticated successfully and listed one vault. - Live write proof on **2026-10-23**: the replacement Keychain token passed all eight actions through the actual run-scoped gateway for both personal and shared credentials. The test created, listed, read, updated, resolved a secret from, archived, and deleted two synthetic items. Both were absent from subsequent item lists. Refresh/reconnect, identity preservation, removal, retained-session denial, and credential absence passed. The live suite passed **3 tests** (one fixture-only test skipped) in 116.65 seconds. The expanded fixture suite passed **3 tests** (one live-only test skipped); the 13 provider tests and server typecheck passed. An exact-token scan passed over 64 source/log files. - Failure history: the original token denied creation on 2026-10-22 and created no item. The first 2026-10-23 attempt stopped before provider calls because host PostgreSQL semaphore limits prevented test-database startup. A temporary ten-connection test profile allowed the complete retry to pass without changing shared services or host limits. A new delete-result assertion also exposed an outdated fixture response (`success` instead of `deleted`); the fixture was corrected and the complete fixture suite passed. Official contract sources: [service accounts](https://www.1password.dev/service-accounts/get-started), [SDKs](https://www.1password.dev/sdks), [item management](https://www.1password.dev/sdks/manage-items), [official JavaScript SDK](https://github.com/1Password/onepassword-sdk-js). ## Risks - Authorized item/secret reads intentionally return secret values to the agent and ordinary tool-result history. Select trusted agents and use restricted service-account vaults; the connection token itself is redacted. - New connections retain existing Paperclip action defaults, including Allowed writes/destructive actions for selected agents. 1Password still enforces the token's permissions. - Service-account access cannot be changed in place. Create a replacement account and reconnect to change vault permissions; read-only keys can connect but cannot write. - SDK authentication happens per action in a disposable worker. Calls have a 30-second maximum; cancellation stops local work, while a mutation already accepted by 1Password can still finish. Writes are never automatically retried. - Additive dependency and catalog change; no database migration. Disable/remove the connection to withdraw access, or remove the provider from catalog visibility for rollback. ## Model Used OpenAI Codex, based on GPT-6, with code editing, shell/browser tools, and live provider verification. A more specific runtime model ID and context-window limit were not exposed in this session. ## 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 either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) 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>
187 lines
6.2 KiB
TypeScript
187 lines
6.2 KiB
TypeScript
/**
|
|
* Prosumer copy for the Apps surface (PAP-10856).
|
|
*
|
|
* The P1a gallery manifest carries developer-flavoured taglines and credential
|
|
* labels (e.g. "Connect Zapier-hosted MCP actions", "Zapier MCP token"). Those
|
|
* strings would fail the vocabulary gate from PAP-10827 — no "MCP", "server",
|
|
* "profile", "policy", "gateway", or "transport" anywhere on this surface. So
|
|
* the UI never renders the raw manifest copy directly: it looks up plain copy
|
|
* here, and `sanitizeProsumerCopy` is a final backstop for any free-text we do
|
|
* surface (app names, fallback taglines).
|
|
*/
|
|
|
|
/** Words that must never appear in prosumer-facing copy on the Apps surface. */
|
|
const BANNED_WORDS = [
|
|
"mcp",
|
|
"server",
|
|
"profile",
|
|
"policy",
|
|
"gateway",
|
|
"transport",
|
|
"stdio",
|
|
"endpoint",
|
|
];
|
|
|
|
const BANNED_RE = new RegExp(`\\b(${BANNED_WORDS.join("|")})s?\\b`, "gi");
|
|
|
|
/**
|
|
* Strip banned vocabulary from a free-text string as a last-resort backstop.
|
|
* Prefer curated copy below; this only protects against manifest text we can't
|
|
* fully control (e.g. a newly added gallery app with no curated entry yet).
|
|
*/
|
|
export function sanitizeProsumerCopy(text: string): string {
|
|
return text
|
|
.replace(BANNED_RE, "")
|
|
.replace(/\s{2,}/g, " ")
|
|
.replace(/\s+([.,])/g, "$1")
|
|
.trim();
|
|
}
|
|
|
|
export interface AppCopy {
|
|
/** Two short lines for the gallery card (M2). */
|
|
tagline: string;
|
|
/** Single line for the connect step header (M3b). */
|
|
short: string;
|
|
}
|
|
|
|
/**
|
|
* Curated prosumer copy keyed by gallery key. Taken from the M-series wires
|
|
* (https://happy-grove-jzyc.here.now/). Apps without an entry fall back to a
|
|
* generic, gate-safe line.
|
|
*/
|
|
const APP_COPY: Record<string, AppCopy> = {
|
|
zapier: {
|
|
tagline: "Reach 9,000+ apps your team already uses.",
|
|
short: "Reach 9,000+ apps from your agents.",
|
|
},
|
|
github: {
|
|
tagline: "Read code and pull requests, comment on issues.",
|
|
short: "Read code and pull requests, comment on issues.",
|
|
},
|
|
slack: {
|
|
tagline: "Send and read messages in your team's channels.",
|
|
short: "Send and read messages in your channels.",
|
|
},
|
|
notion: {
|
|
tagline: "Read and update pages in your workspace.",
|
|
short: "Read and update pages in your workspace.",
|
|
},
|
|
railway: {
|
|
tagline: "Inspect services, read logs, and manage deployments.",
|
|
short: "Connect Railway for deployments, logs, and container access.",
|
|
},
|
|
posthog: {
|
|
tagline: "Explore product usage, errors, flags, and experiments.",
|
|
short: "Sign in with PostHog. Project pinning and access controls are optional.",
|
|
},
|
|
ditto: {
|
|
tagline: "Reuse approved product copy and follow your style guide.",
|
|
short: "Sign in with Ditto. API keys are optional.",
|
|
},
|
|
gauge: {
|
|
tagline: "Track your brand in AI answers and run content workflows.",
|
|
short: "Sign in with Gauge and pick an organization. API keys are optional.",
|
|
},
|
|
neon: {
|
|
tagline: "Manage Postgres projects, branches, and queries.",
|
|
short: "Sign in with Neon. Project pinning and read-only mode are optional.",
|
|
},
|
|
superagent: {
|
|
tagline: "Review security findings and run red-team checks.",
|
|
short: "Connect with a Superagent API key. Billable and destructive tools stay under action policies.",
|
|
},
|
|
linear: {
|
|
tagline: "Create, update and read tickets.",
|
|
short: "Create, update and read tickets.",
|
|
},
|
|
"google-sheets": {
|
|
tagline: "Read and update selected spreadsheets.",
|
|
short: "Read spreadsheets or update the files you choose.",
|
|
},
|
|
gmail: {
|
|
tagline: "Read mail and create drafts for your review.",
|
|
short: "Read mail and create drafts for your review.",
|
|
},
|
|
"google-drive": {
|
|
tagline: "Find, read, and create files in Drive.",
|
|
short: "Find, read, and create files in Drive.",
|
|
},
|
|
"google-docs": {
|
|
tagline: "Read and update documents.",
|
|
short: "Read and update documents.",
|
|
},
|
|
"google-slides": {
|
|
tagline: "Read and update presentations.",
|
|
short: "Read and update presentations.",
|
|
},
|
|
"google-calendar": {
|
|
tagline: "Review calendars and manage events.",
|
|
short: "Review calendars and manage events.",
|
|
},
|
|
"google-chat": {
|
|
tagline: "Read conversations and send messages.",
|
|
short: "Read conversations and send messages.",
|
|
},
|
|
"google-people": {
|
|
tagline: "Look up contacts and people in your directory.",
|
|
short: "Look up contacts and people in your directory.",
|
|
},
|
|
"google-workspace-search": {
|
|
tagline: "Search across your Google workspace.",
|
|
short: "Search across your Google workspace.",
|
|
},
|
|
hubspot: {
|
|
tagline: "Look up contacts and update deal stages.",
|
|
short: "Look up contacts and update deal stages.",
|
|
},
|
|
intercom: {
|
|
tagline: "Read and reply to customer conversations.",
|
|
short: "Read and reply to customer conversations.",
|
|
},
|
|
figma: {
|
|
tagline: "Read files and post comments on frames.",
|
|
short: "Read files and post comments on frames.",
|
|
},
|
|
stripe: {
|
|
tagline: "Read customers, invoices, and payouts.",
|
|
short: "Read customers, invoices, and payouts.",
|
|
},
|
|
context7: {
|
|
tagline: "Look up up-to-date docs for your libraries.",
|
|
short: "Look up up-to-date docs for your libraries.",
|
|
},
|
|
};
|
|
|
|
const GENERIC: AppCopy = {
|
|
tagline: "Give your agents access to this app.",
|
|
short: "Give your agents access to this app.",
|
|
};
|
|
|
|
/** Curated, gate-safe copy for a gallery app. */
|
|
export function appCopyFor(key: string, fallbackTagline?: string | null): AppCopy {
|
|
const curated = APP_COPY[key];
|
|
if (curated) return curated;
|
|
if (fallbackTagline) {
|
|
const cleaned = sanitizeProsumerCopy(fallbackTagline);
|
|
if (cleaned) return { tagline: cleaned, short: cleaned };
|
|
}
|
|
return GENERIC;
|
|
}
|
|
|
|
/**
|
|
* Label for a single credential field on the key-paste step (M3b). The raw
|
|
* manifest label can contain banned vocab ("Zapier MCP token"), so for the
|
|
* common single-field case we present "Your {App} key" per the wires; multi-
|
|
* field apps fall back to a sanitized version of the manifest label.
|
|
*/
|
|
export function credentialFieldLabel(
|
|
appName: string,
|
|
rawLabel: string,
|
|
fieldCount: number,
|
|
): string {
|
|
if (/^Paste your\b/.test(rawLabel)) return sanitizeProsumerCopy(rawLabel);
|
|
if (fieldCount <= 1) return `Your ${appName} key`;
|
|
const cleaned = sanitizeProsumerCopy(rawLabel);
|
|
return cleaned || `Your ${appName} key`;
|
|
}
|