mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 06:16:07 -10:00
Release docs for paperclip v2026.722.0 (#72)
Release docs for **paperclip v2026.722.0** (window `v2026.720.0 → v2026.722.0`; 256 changed parent files, 60 commits, diff window clean — 0 truncated leaves). Nothing here goes live until this PR is merged to `main`. ## Surfaces authored this run | Surface | Docs | Parent PR | |---|---|---| | **Run-bound agent secret access** — `access.`-prefixed / `api` delivery mode; `GET /api/agents/me/secrets`, `POST /api/agents/me/secrets/:key/value` (`Cache-Control: no-store`) | `api/secrets.md`, `api/agents.md` | #9921 | | **Local agents run natively on Windows** (ACPX, no Bash wrapper; env via session options; `.cmd` shim; symlink→copy on EPERM) | `adapters/acpx-local.md` | #9980 | | **`PAPERCLIP_*` bindings flow through**; only `PAPERCLIP_API_KEY` rejected (override removed); `DO_NOT_TRACK` / `PAPERCLIP_TELEMETRY_DISABLED` opt-out | `deploy/environment-variables.md` | #9974, #9946, #9906 | | **Safer `CODEX_HOME` sandbox sync** — explicit allowlist, not whole home | `adapters/codex.md` | #9972 | | **Plugin SDK** environment-sync exports + `onEnvironmentSyncIn/Out` hooks | `reference/plugins/sdk.md` | — | | **CLI `company export --force`** | `reference/cli/company.md` | — | | **Connections v3 (Apps)** experimental foundation — `remote_http`→`mcp_remote`, `connection_grants`, AppDefinition catalog | `experimental/connections-apps.md` (new) | #9958, #9981, #9982 | Riding along from earlier nightly drafts (part of this release): secret folders (`administration/secret-folders.md`), searchable agent picker, cloud-managed bootstrap gate (`api/instance-admin.md`). ## Verification All authored claims verified against parent code at tag `v2026.722.0` (`verify-edit`): **0 high-confidence unverified**. `docs:build`, `sync:lint-links`, and `sync:verify-nav` all pass. Frontmatter stamped `paperclip_version: v2026.722.0`. The 2 nav orphans (`cli/commands.md`, `cli/control-plane-commands.md`) are the intentional redirect anchors. ### ⚠ Drift (all resolved as false positives — no action) `sync:check-drift` flagged 4 `rest-route-missing` candidates: - `GET /api/agents/me/secrets` and `POST /api/agents/me/secrets/{key}/value` (in `api/agents.md` and `api/secrets.md`). These are the **new** routes this PR documents. The flags fire because the drift checker searched a stale `last_seen` sha and REST-route matching is medium-confidence (router-prefix registration hides the literal). Both routes were verified verbatim against `server/src/routes/secrets.ts` at tag `v2026.722.0`. No change needed. One `verify-edit` env-var flag (`PAPERCLIP_CLOUD_PROD_PROVIDER_RAILWAY_TOKEN`) is the release-note **illustrative example** of a user-chosen name, framed in prose as "for example a secret called …" — not a real product variable. No change needed. ### ↻ Renames detected None. ### ⚠ Reconcile None. ## Screenshots See `SCREENSHOTS_PENDING.md`. 13 unique captures (×2 light/dark) are genuinely stale — mainly agent-config surfaces (`AgentConfigForm.tsx` gained the Secret access editor) and secrets. Net-zero `+0/-0` UI touches were filtered out. Not auto-captured — recapture via `npm run screenshots:refresh` goes to a separate review PR. The new `secret-folders.md` and `connections-apps.md` pages have no captures yet. ## Post-merge (squash-only repo) After squash-merging, run `node scripts/sync/realign-nightly.mjs release/v2026.722.0 --push` and bump `.sync-state.json` `base_release_tag` to `v2026.722.0`. Skipping the realign arms the add/add-conflict trap at the next nightly run. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]> Co-authored-by: DevRel Gemini <[email protected]> Co-authored-by: Paperclip <[email protected]>
This commit is contained in:
15 files changed
+433
-448
No files matched your search
+3
-3
@@ -2,8 +2,8 @@
|
||||
"branch_mode": "release",
|
||||
"base_release_tag": "v2026.720.0",
|
||||
"base_release_sha": "903bd157c22a39c1b90813b8d2a7e63cfd10fa65",
|
||||
"last_seen_parent_sha": "903bd157c22a39c1b90813b8d2a7e63cfd10fa65",
|
||||
"last_applied_manifest_hash": "a9edb1817d88e801100732445407f1bc75539491e5a19e3d2bd113370009be12",
|
||||
"last_run_at": "2026-07-20T19:40:00Z",
|
||||
"last_seen_parent_sha": "e55d702916c4d3ddbcac49b697f879808b160f59",
|
||||
"last_applied_manifest_hash": "cf0e78427fa140aaba0f12122aec1304058c9359d9dba15c8346d0096434a300",
|
||||
"last_run_at": "2026-07-23T07:50:00Z",
|
||||
"last_run_outcome": "applied"
|
||||
}
|
||||
+41
-57
@@ -1,89 +1,73 @@
|
||||
# PENDING — nightly docs sync
|
||||
|
||||
Regenerated by `/sync-docs` in **nightly mode**. This is the current cumulative view from the last parent release; it retains already-applied work for context and only treats the newly-settled delta as authoring scope.
|
||||
Regenerated by `/sync-docs` in **nightly mode**. This is the current cumulative view from the last parent release, rebased onto the new release baseline.
|
||||
|
||||
Cumulative window: parent release `v2026.707.0` (`390627b`) → parent `master` HEAD `f12bb27`. The compare helper saw **195 commits / 1522 changed files** with **0 truncated leaves**. Every commit in the window predates the 24-hour quarantine cutoff (newest is `2026-07-18T19:39:37Z`), so nothing was held back this run.
|
||||
Cumulative window: parent release `v2026.720.0` (`903bd15`) → parent `master` HEAD `7985c1e`. The compare helper saw **41 commits / 176 changed files** with **0 truncated leaves**.
|
||||
|
||||
Newly settled since the prior run: `7947308…f12bb27` (**60 commits / 397 files**), headlined by four new REST surfaces and the summary-slot feature.
|
||||
**Quarantine bit hard this run.** The 24-hour cutoff (`2026-07-21T05:33Z`) held back **32 of the 41 commits** — parent shipped heavily in the last day. Authoring scope was the **9 settled commits / 26 files** up to `230126d8`. The held-back work is listed at the bottom; it is not lost, it simply authors on the next run once it has had time to stick.
|
||||
|
||||
## ✅ Applied in earlier nightly drafts
|
||||
This is the first nightly run on the `v2026.720.0` baseline, so the prior run's "surfaced but not applied" backlog (summary slots, the tool-access / MCP-gateway / Smoke Lab REST surface) is gone from this file — those pages shipped in the release and now live on `main`.
|
||||
|
||||
- **Cases API** — `docs/reference/api/cases.md`.
|
||||
- **Built-in Agents API** — `docs/reference/api/built-in-agents.md`.
|
||||
- **Human permission keys** — `docs/administration/roles-and-permissions.md`.
|
||||
- **Codex `gpt-5.4-mini`** — `docs/reference/adapters/codex.md`.
|
||||
- **Decisions, ACP, inbox, and interaction contracts** — API, guide, adapter, and CLI pages updated in PR #57.
|
||||
- **Redundant issue re-wake throttling** (`5dff526`) — `docs/how-to/debug-stuck-heartbeat.md`.
|
||||
- **`costStatus` on cost events** (`efcce9c`) — `docs/reference/api/costs.md`.
|
||||
- **The six `tools:*` permission keys** — `docs/administration/roles-and-permissions.md`.
|
||||
- **`PAPERCLIP_TOOL_ACTION_SIGNING_SECRET`** — `docs/reference/deploy/environment-variables.md`.
|
||||
## ✍️ Applied this run
|
||||
|
||||
## ✍️ Newly settled, applied
|
||||
- **Secret folders** — new page `docs/administration/secret-folders.md`, plus a cross-reference paragraph in `docs/administration/secret-scopes.md` and a nav entry under Administration.
|
||||
|
||||
- **Folders API** — new page `docs/reference/api/folders.md`. `server/src/routes/folders.ts` mounts under `/api` and covers list, create, rename/update, move-item, move-folder, and delete. Documented against the real zod schemas; `MAX_FOLDER_DEPTH = 4` and the reserved root slugs (`bundled`, `my`, `projects`) are service constants, and `path`/`depth` are computed in `buildFolderViews` rather than stored columns. Verification: 7/7 claims confirmed.
|
||||
Parent PR #9913 (`b94907f8`) teaches the Secrets screen to read slash-delimited secret names as a folder tree. The load-bearing fact, and the one the page is built around: **folders are derived purely from names — there is no server-side folder record.** `ui/src/pages/secrets/secret-path.ts` computes the whole tree from the name strings, so "New folder" creates nothing; it stages a path and pre-fills the create dialog's Name field with it, and the folder only becomes real once a secret is saved inside it.
|
||||
|
||||
- **Decision Training API** — new page `docs/reference/api/decision-training.md`. Covers create, preview, list, the `export.jsonl` endpoint, get, update-notes, and delete. Source kinds are exactly `interaction`, `approval`, `execution_decision`. The retention policy constant `scrub_deleted_comments_v1` was confirmed directly in `packages/shared/src/types/decision-training.ts`. Verification: 7/7 claims confirmed.
|
||||
Also covered: the Folders/Flat toggle (persisted in `localStorage` under `paperclip.secrets.viewMode`, defaulting to Folders once any name contains a slash), `?path=` deep links (which force folder view for that visit), search staying deliberately flat and global, and the `12 secrets · 3 folders` counts — where the secret count includes *all* descendants, not just direct children.
|
||||
|
||||
- **Company Skill Policy API** — new page `docs/reference/api/company-skill-policy.md`. Get / put / delete / evaluate under `/api/companies/{companyId}/skill-policy`. Note the policy routes wrap zod failures themselves into `422 skill_policy_validation_failed` rather than the usual 400. Verification: 4/4 claims confirmed.
|
||||
Folded in from PR #9918 (`b9f4073a`): the new `AgentSelect` in `ui/src/components/AgentMultiSelect.tsx` gives the Agent access panel a searchable picker with a **Filter agents** box. Too small for its own entry, and it belongs next to the secret it grants.
|
||||
|
||||
- **Inbox Agent Policy API** — new page `docs/reference/api/inbox-agent-policy.md`. Documents both the self routes (`/users/me/inbox-agent-policy`) and the admin-on-behalf routes (`/users/{userId}/inbox-agent-policy`). Verification: 4/4 claims confirmed.
|
||||
Verification: 34/34 claims confirmed against parent code.
|
||||
|
||||
- **`inbox:manage` permission key** (drift fix) — `docs/administration/roles-and-permissions.md`. `PERMISSION_KEYS` is now **twenty** keys; the count carrying a role default stays at **8**, so the explicit-only set goes from eleven to twelve. The important subtlety, confirmed in `server/src/services/authorization.ts`: `inbox:manage` is **not** checked by the inbox-agent-policy *routes* (those check `users:manage_permissions`). It gates an **agent acting on an inbox** — an agent may work its own responsible user's inbox without it, but cross-user inbox action needs an explicit scoped grant, and the scope must cover the target user. Agents on the low-trust review preset are denied it regardless of grant.
|
||||
- **Cloud-managed instances skip the first-admin claim** — `docs/reference/api/instance-admin.md`, one new `**Cloud-managed instances.**` paragraph in the existing claim section.
|
||||
|
||||
- **`summarize-status` bundled skill** — `docs/reference/skills.md`. New `paperclip-operations`-category bundled skill, `defaultInstall: false`, recommended for `general` and `manager`. Added to the bundled examples row and given a short paragraph.
|
||||
Parent PR #9912 (`2f42a496`) adds `isCloudManagedInstance()` to `server/src/middleware/auth.ts` and gates the health bootstrap check on it. Worth reading the subtlety before touching this again: the PR changed the **health** gate only. `POST /api/bootstrap/claim` in `server/src/routes/access.ts` did *not* get the same check, so on a cloud-managed instance the route still exists and still answers. What changed is that `GET /api/health` no longer reports `bootstrap_pending`, so the UI never routes anyone to the claim screen. The paragraph is worded around that user-visible outcome and deliberately does **not** claim the route 404s on cloud.
|
||||
|
||||
## 🚧 Newly settled, surfaced but NOT applied
|
||||
Verification: 5/5 claims confirmed.
|
||||
|
||||
- **Summary slots** — `packages/db/src/schema/summary_slots.ts`, `server/src/routes/summary-slots.ts` (mounted `api.use(summarySlotRoutes(db))`), and `ui/src/api/summarySlots.ts`. This is the feature the new `summarize-status` skill writes into: a scope (project, workspaces overview, or a single project workspace) carries a summary slot that accumulates Markdown **revisions**. It deserves its own API page plus a short guide section on where the summary renders next to the board — not a one-line mention. Currently only referenced obliquely via the skill paragraph in `docs/reference/skills.md`.
|
||||
## 🚧 Surfaced, NOT applied — needs a human call
|
||||
|
||||
- **The Tool access / MCP gateway / Smoke Lab REST surface (~60 endpoints)** — still outstanding from the prior run. `server/src/routes/tool-access.ts`, `tool-gateway.ts`, `smoke-lab.ts`. Groundwork retained:
|
||||
- **Mount prefixes differ, and this is a trap.** `toolAccessRoutes`, `toolGatewayRoutes`, and `smokeLabRoutes` mount on the `api` router (`app.use("/api", api)`), so they are `/api/...`. But `mcpGatewayProtocolRoutes(toolGateway)` mounts on `app` **directly** — so `GET|POST /mcp/gateways/{gatewayPublicId}` has **no** `/api` prefix.
|
||||
- `tool-gateway.ts` is **not** exported from `server/src/routes/index.ts`; it is imported straight into `app.ts`.
|
||||
- Vocabulary in `packages/shared/src/constants.ts`: `TOOL_APPLICATION_TYPES` (`mcp_http`, `mcp_stdio`, `paperclip_plugin`, `a2a`), `TOOL_APPLICATION_STATUSES` (`draft`, `active`, `disabled`, `archived`), `TOOL_CONNECTION_HEALTH_STATUSES`, `TOOL_MCP_GATEWAY_DEFAULT_PROFILE_MODES`.
|
||||
- **`PAPERCLIP_CLOUD_TENANT_SERVER_TOKEN` is undocumented.** It is the env var that flips `isCloudManagedInstance()`, and it appears nowhere under `docs/`. It reads as a control-plane-internal token rather than something a self-hoster sets, so it was left out of `docs/reference/deploy/environment-variables.md` on purpose. The instance-admin paragraph was written to avoid naming it, so the page stays correct either way. **Decide:** document it as internal-only, or leave it undocumented.
|
||||
|
||||
- **Tools, Profiles, and Apps board UI** — carried over, plus this window's UI work. New end-user surfaces needing guide coverage and screenshots.
|
||||
Note the drift checker cannot catch this class of gap — it verifies documented → parent, not parent → documented.
|
||||
|
||||
- **Decision Training UI** — `ui/src/components/DecisionTrainingDrawer.tsx` and `ui/src/api/decisionTraining.ts`. The API is now documented; the board-side drawer that produces the examples is not, and needs a guide page with screenshots.
|
||||
- **HTTP log redaction has no watcher.** `15683000` (PR #7977) added `server/src/middleware/http-log-redaction.ts` exporting `HTTP_LOG_REDACT_PATHS`, and widened pino's `redact` list from just `req.headers.authorization` to also cover `cookie`, `proxy-authorization`, `set-cookie` (both directions), `x-csrf-token`, `x-xsrf-token`, and `x-api-key`.
|
||||
|
||||
- **Folders UI** — `ui/src/components/folders/*` (`SkillFolderTree`, `FolderControls`, `skill-folder-tree.ts`). The API is documented; the skill-library folder tree the user actually clicks is not.
|
||||
This is a real operator-facing guarantee — "what does Paperclip put in its logs" is a question people ask before deploying — and nothing in `docs/` mentions it. It landed in `server/src/middleware/**`, which **no watcher in `anchor-map.json` covers**. See the watcher-maintenance note below.
|
||||
|
||||
## ✅ Reconcile — resolved this run
|
||||
## ℹ️ Context-only (no doc edit warranted)
|
||||
|
||||
- **`acpx_local` retired to a live tombstone.** `docs/reference/adapters/acpx-local.md` has been rewritten as a stub: it states the adapter is retired, points at `claude-code.md` / `codex.md` / `gemini-cli.md`, documents the automatic `0136` migration table (`codex` → `codex_local`, everything else → `claude_local`, both with `engine: "acp"`), and explains what to do if the `acpx_local` string still shows up in a run error. The `content.json` entry is kept, retitled **ACPX Local (retired)** so it stays findable when browsing the adapter list. The page was **not** deleted and its body was **not** duplicated forward — the old config fields (`agent`, `effort`, `reasoningEffort`, `thinkingEffort`) are exactly what the migration rewrites away.
|
||||
- `a59aa128` — `resolveCommentCreatedByRunId` nulls out unknown/malformed run ids so a stale id can't 500 a comment insert. Internal robustness fix, no documented behaviour changes.
|
||||
- `631cca50` — README roadmap and four-pillars asset refresh in the parent. Parent-repo presentation, not our surface.
|
||||
- CI-only churn: `.github/workflows/*`, `scripts/e2e-shard.mjs`, and the CLI test-only files under `cli/src/__tests__/`.
|
||||
|
||||
The investigation that produced this, retained because the helper gap is the reusable lesson:
|
||||
## 🔧 Watcher maintenance needed in `anchor-map.json`
|
||||
|
||||
`scripts/sync/detect-renames.mjs` reported `packages/adapters/acpx-local` under `removed_dirs_no_match`, which reads as a plain deletion. **That verdict is wrong.** Verified against parent `eedc7dd` ("Make ACP the default engine for local adapters", #9238):
|
||||
1. **`packages/db/migrations` is a stale path.** Preflight returned 404. The parent's migrations live at **`packages/db/src/migrations`** — this window contains `0182_connections_v3_schema_core.sql` and `0183_connection_user_authorization_state.sql` there. The `db-migrations` watcher is `context-only` so nothing broke, but it has been silently matching nothing.
|
||||
|
||||
1. **The runtime moved, it did not vanish.** `packages/adapters/acpx-local/` → `packages/adapter-utils/src/acpx-engine/` (module `@paperclipai/acpx-engine`), now imported by `claude-local`, `codex-local`, and `gemini-local`. ACP stopped being a separate *adapter choice* and became an *execution capability* of each harness adapter.
|
||||
2. **The `acpx_local` adapter type is still registered, deliberately.** `server/src/adapters/registry.ts:215` keeps an `acpxLocalAdapter` whose `execute` only emits a retirement message. Its own comment: *"Paperclip keeps this tombstone registered so stale `acpx_local` rows fail clearly instead of falling back to the process adapter."* The type is gone from `AGENT_ADAPTER_TYPES` (so it is unselectable for new agents) but still resolvable at runtime.
|
||||
3. **Existing rows were migrated.** `packages/db/src/migrations/0136_acpx_default_engine_migration.sql` rewrites `acpx_local` agents to `claude_local` or `codex_local` (branching on `adapter_config->>'agent'`) with `engine: 'acp'`, and consolidates the Codex reasoning-effort fields onto `modelReasoningEffort`.
|
||||
2. **`server/src/middleware/**` is unwatched.** The log-redaction change above proves the gap: security-relevant middleware can land with no watcher hit. Worth a watcher routing to `docs/administration/**` / `docs/reference/deploy/**`.
|
||||
|
||||
Note also that `@paperclipai/acpx-engine` is an **internal library**, not a successor adapter — it exports `createAcpxEngineExecutor`, `execute`, `sessionCodec`, `printAcpxStreamEvent`, `parseAcpxStdoutLine`, and appears nowhere in `packages/shared/src/constants.ts`. There is no adapter type to select, so it gets no adapter page. (`grok_local`, the only type added alongside `acpx_local`'s removal, is unrelated: `packages/adapters/grok-local` already existed at the base tag and was simply registered late.)
|
||||
All other concrete watcher paths preflighted OK.
|
||||
|
||||
**Helper gap — still open.** `detect-renames.mjs` matches top-level directories under watched roots, so it cannot see a move that crosses a package boundary into a nested path (`packages/adapters/<x>` → `packages/adapter-utils/src/<x>`). Any future cross-package extraction will be misreported the same way, and the failure mode points toward deleting docs that should be kept. Documented as a gotcha in [`maintenance/maintenance.md`](maintenance/maintenance.md#gotcha-detect-renamesmjs-cannot-see-cross-package-moves) with the verification commands. A code fix is still worth doing: match on file-content similarity, or at minimum scan for the removed dir's basename appearing as a new nested path elsewhere in the window.
|
||||
## ✅ Clean this run
|
||||
|
||||
## ↻ Renames detected
|
||||
- **Drift: 0 records** against `master` (`7985c1e`) — 8 parent paths, 57 CLI commands, 66 env vars, 389 REST routes, 18 permission keys, 4 permission roles all still exist upstream.
|
||||
- **Renames: none** detected by `detect-renames.mjs`.
|
||||
- **Reconciliation: none** — no previously-applied entry vanished from the cumulative diff.
|
||||
|
||||
None. `detect-renames.mjs` returned `renames: []` and `added_dirs_genuinely_new: []` for this window.
|
||||
## ⏳ Held by quarantine — expect these next run
|
||||
|
||||
## ⚠ Verification
|
||||
32 commits landed inside the 24-hour window. The substantial ones:
|
||||
|
||||
All six applied edits passed `verify-edit.mjs` against parent `master` (`f12bb27`) with **zero** unverified and **zero** suspicious records. `roles-and-permissions.md` extracts 0 machine-checkable claims (the helper does not parse permission-key tables), so its facts were confirmed by hand against `packages/shared/src/constants.ts` (`PERMISSION_KEYS`, 20 entries), `server/src/services/company-member-roles.ts` (`grantsForHumanRole`), and `server/src/services/authorization.ts` (the `inbox:manage` enforcement branches).
|
||||
- **Connections v3** (`7e00f671`, `d23fbf8a`, `cac3c0fa`) — the big one. New schema core (migrations `0182`/`0183`), an `AppDefinition` Wave 1 catalog under `packages/shared/src/app-definitions/` with twelve app JSON descriptors (anthropic, github, linear, notion, slack, sentry, vercel, zapier, google-sheets, context7, plus generic api-key and oauth), and runtime subjects/grants. `packages/shared/src/tool-app-gallery.ts` was **removed** in the same sweep — that is a rename-shaped change and will need the rename pass plus a look at whatever documents the app gallery.
|
||||
- **Telemetry client contract** (`bf636563`, `c81a089c`) — chunking, deterministic `batchId`, batched retry, bounded store, and a new caps/backoff config surface in `packages/shared/src/telemetry/config.ts`. Likely env-var and deploy-doc impact.
|
||||
- **Tool access service** (+465/-59 in `server/src/services/tool-access.ts`, +108/-5 in the routes) — check against `docs/reference/api/tool-gateway.md`.
|
||||
- **Fail loudly on invalid config files** (`68ba7cca`) — new `server/src/__tests__/config-file.test.ts`, `server/src/config-file.ts` changes. Operator-visible startup behaviour.
|
||||
- **Execution policy** (`3e1dc90b`) — final-stage approval now terminates the policy instead of rewinding to stage 1. Directly contradicts anything in `docs/guides/power/execution-policy.md` that describes the old rewind.
|
||||
- **Codex-local `CODEX_HOME` allowlist** (`7985c1e0`) — new `packages/adapters/codex-local/src/server/codex-home.ts`.
|
||||
- Smaller: inbox archive cache fix (`59eee482`), Office issue attachments (`b565603a`), issue-release terminal status (`1ba79d82`), CLI import-selection extension fix (`f4b65c12`).
|
||||
|
||||
## 🩺 Housekeeping
|
||||
---
|
||||
|
||||
- **Stale `last_seen_parent_sha`.** The value stored by the prior run, `992389480a243b97bda214227e0767eb8c3672af`, **no longer resolves** in `paperclipai/paperclip` — `npm run sync:check` fails its drift step on it. The commit was most likely rebased away or GC'd upstream. This run's state update to `f12bb27` clears it. The field is informational only, so nothing else was affected; the Phase 1.5 drift check ran cleanly against `master` explicitly.
|
||||
- **Orphan nav files** — `docs/reference/cli/commands.md` and `docs/reference/cli/control-plane-commands.md` remain intentional redirect anchors, not missed registrations. Expected warning.
|
||||
|
||||
## ↻ Newly settled, context-only
|
||||
|
||||
- **Codex auth precedence and copyback** — `packages/adapters/codex-local/src/server/auth-precedence.ts`, `codex-auth-copyback.ts`, `codex-auth-merge-scripts.ts` and friends. Internal auth-resolution behaviour; no documented adapter config field changed.
|
||||
- **Claude adapter parse changes** — `packages/adapters/claude-local/src/server/parse.ts`. Transcript parsing internals.
|
||||
- **Hot restart** — `server/src/services/hot-restart.ts` (new). No documented contract or env var yet.
|
||||
- **Company search extract** — `server/src/services/company-search-extract.ts` (new). Internal extraction split out of `company-search.ts`.
|
||||
- **Smoke Lab fixture demo servers** — `packages/google-sheets-mcp-server` and `packages/kv-demo-mcp-server` match the env-vars watcher via `packages/**/config.ts`, but their config is fixture-local, not instance configuration. Not documented.
|
||||
|
||||
## 📸 Screenshots
|
||||
|
||||
**248 of 298** registry entries are stale — see `SCREENSHOTS_PENDING.md` for the per-area breakdown. The cumulative window touched **614** files under `ui/src/**`, so this is closer to a full-surface refresh than a set of targeted recaptures. Consider `npm run screenshots:refresh:all` rather than the stale-only pass. Output PNGs go to a PR for human review; the skill never pushes them.
|
||||
*Manifest hash: `a60671c76280275572e7e5ede33a1d5ea9bffa59e104979d216cbe859a98a87a`*
|
||||
+28
-379
@@ -1,398 +1,47 @@
|
||||
# Screenshots pending recapture
|
||||
|
||||
Generated by `/sync-docs` release run for **paperclip v2026.720.0** (window v2026.707.0 to v2026.720.0).
|
||||
Regenerated by `/sync-docs` **release** run. Window: parent `v2026.720.0` → `v2026.722.0`.
|
||||
|
||||
**248 of 298** registry entries are stale.
|
||||
|
||||
This release landed a design-system convergence (single-source design tokens, a retuned theme, broad Card/Badge adoption, a multiplicative radius ladder, unified list surfaces, and Lucide task-status glyphs). That touches nearly every surface, so this is a full-overhaul cycle rather than a targeted refresh:
|
||||
Captured PNGs go to a PR for human review — they are **never auto-pushed**. For a targeted refresh:
|
||||
|
||||
```sh
|
||||
npm run screenshots:refresh:all
|
||||
npm run screenshots:refresh
|
||||
```
|
||||
|
||||
The captured PNGs go to a PR for human review; they are never auto-pushed.
|
||||
Staleness below is filtered to `depends_on` paths that changed with **real line edits** in this window; net-zero `+0/-0` UI touches (a large batch of `Inbox.tsx` / `IssueDetail.tsx` / design-system files) are excluded as false positives.
|
||||
|
||||
---
|
||||
|
||||
## Stale entries by area
|
||||
## Stale entries
|
||||
|
||||
### `dark/activity` (3)
|
||||
### `agents` + agent-config surfaces (12 shots, light + dark)
|
||||
|
||||
- `dark/activity/activity-filtered-by-agent.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
- `dark/activity/activity-filters.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
- `dark/activity/activity-log-full.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
`ui/src/components/AgentConfigForm.tsx` changed with real edits — this window adds the **Secret access** editor for run-bound agent secret grants (PR #9921), so agent-config captures may now show new UI. Recapture:
|
||||
|
||||
### `dark/adapters` (4)
|
||||
- `agents/adapter-type-dropdown.png`
|
||||
- `agents/budget-and-heartbeat-fields.png`
|
||||
- `agents/claude-local-config-filled.png`
|
||||
- `agents/codex-local-config.png`
|
||||
- `agents/heartbeat-toggle-disabled.png`
|
||||
- `agents/heartbeat-toggle-enabled.png`
|
||||
- `agents/http-adapter-config.png`
|
||||
- `agents/test-environment-fail.png`
|
||||
- `agents/test-environment-success.png`
|
||||
- `company/company-budget-setting.png`
|
||||
- `onboarding/budget-field.png`
|
||||
- `org/reassign.png`
|
||||
|
||||
- `dark/adapters/detail.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `dark/adapters/health.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `dark/adapters/install.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `dark/adapters/list.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
### `secrets`
|
||||
|
||||
### `dark/agents` (23)
|
||||
- `secrets/dispatch-check.png` (light + dark) — `server/src/services/secrets.ts` (+338 this window)
|
||||
- `secrets/user-secret-definition.png` (light + dark) — `ui/src/pages/Secrets.tsx` was reworked by PR #9913 (folder view): a Folders/Flat toggle, breadcrumb row, and folder rows. These captures are of that screen.
|
||||
- `secrets/per-user-value-entry.png` (light + dark) — `ui/src/pages/Secrets.tsx`, `ui/src/pages/secrets/MyUserSecretsTab.tsx`. `MyUserSecretsTab.tsx` now renders names through `SecretPathName` (muted directory, bold leaf), so slash-named secrets look different.
|
||||
|
||||
- `dark/agents/adapter-type-dropdown.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/agent-detail-idle.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/agent-detail-run-history.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/agent-status-running.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/agents-list-empty.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `dark/agents/budget-and-heartbeat-fields.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/budget.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/claude-local-config-filled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/codex-local-config.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/configuration.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/dashboard.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/heartbeat-toggle-disabled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/heartbeat-toggle-enabled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/http-adapter-config.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/instructions.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/list.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `dark/agents/new.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `dark/agents/run-history-in-progress.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/run-transcript-view.png` — deps changed: `ui/src/components/transcript/RunTranscriptView.tsx`, `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/runs.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/skills.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `dark/agents/test-environment-fail.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/agents/test-environment-success.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
## 📸 New pages with no screenshots yet
|
||||
|
||||
### `dark/approvals` (9)
|
||||
- `docs/administration/secret-folders.md` (drafted on nightly, ships in this release) has **no image references** — no capture exists for the folder view. Worth adding: the folder view (breadcrumbs, "Up to …" row, folder rows with `N secrets · N folders` counts, Folders/Flat toggle) and the create-secret dialog opened inside a folder (folder-path prefix chip on the Name field). Needs new registry entries with `depends_on: ["ui/src/pages/Secrets.tsx", "ui/src/pages/secrets/secret-path.ts"]`.
|
||||
- `docs/experimental/connections-apps.md` (new this release) has no screenshots — the Apps surfaces are experimental and still moving, so hold captures until the UX settles.
|
||||
|
||||
- `dark/approvals/approvals-list.png` — deps changed: `ui/src/pages/Approvals.tsx`, `ui/src/components/ApprovalCard.tsx`
|
||||
- `dark/approvals/approvals-queue-strategy.png` — deps changed: `ui/src/pages/Approvals.tsx`, `ui/src/components/ApprovalPayload.tsx`
|
||||
- `dark/approvals/approve-reject-buttons.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `dark/approvals/approve-reject-revision-buttons.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `dark/approvals/approved-approval.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `dark/approvals/hire-approval-detail.png` — deps changed: `ui/src/components/ApprovalPayload.tsx`, `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `dark/approvals/queue-filters.png` — deps changed: `ui/src/pages/Approvals.tsx`
|
||||
- `dark/approvals/revision-request-input.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `dark/approvals/strategy-approval-detail.png` — deps changed: `ui/src/components/ApprovalPayload.tsx`, `ui/src/pages/ApprovalDetail.tsx`
|
||||
|
||||
### `dark/company` (6)
|
||||
|
||||
- `dark/company/access.png` — deps changed: `ui/src/pages/CompanyAccess.tsx`
|
||||
- `dark/company/export.png` — deps changed: `ui/src/pages/CompanyExport.tsx`
|
||||
- `dark/company/import.png` — deps changed: `ui/src/pages/CompanyImport.tsx`
|
||||
- `dark/company/invites.png` — deps changed: `ui/src/pages/CompanyInvites.tsx`
|
||||
- `dark/company/join-requests.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/company/settings.png` — deps changed: `ui/src/pages/CompanySettings.tsx`
|
||||
|
||||
### `dark/costs` (13)
|
||||
|
||||
- `dark/costs/agent-budget-100pct-paused.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/agent-budget-50pct.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/agent-budget-80pct-warning.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/agent-budget-bar.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/agent-budget-field.png` — deps changed: `ui/src/pages/AgentDetail.tsx`, `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/billers.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/budgets.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/company-budget-field.png` — deps changed: `ui/src/pages/Costs.tsx`, `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `dark/costs/costs-dashboard-overview.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/finance.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/overview.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/per-run-cost-detail.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `dark/costs/providers.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
|
||||
### `dark/dashboard` (4)
|
||||
|
||||
- `dark/dashboard/activity-feed.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `dark/dashboard/agent-status-panel.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `dark/dashboard/dashboard-overview.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `dark/dashboard/task-breakdown-panel.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
|
||||
### `dark/interactions` (1)
|
||||
|
||||
- `dark/interactions/checkbox-confirmation-card.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueThreadInteractionCard.tsx`
|
||||
|
||||
### `dark/issues` (8)
|
||||
|
||||
- `dark/issues/detail-activity.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `dark/issues/detail-chat.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `dark/issues/detail-sidebar.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `dark/issues/inbox-requests.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/issues/inbox-unread.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/issues/inbox.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/issues/list.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `dark/issues/my-issues.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
|
||||
### `dark/onboarding` (4)
|
||||
|
||||
- `dark/onboarding/empty-dashboard.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `dark/onboarding/goal-field.png` — deps changed: `ui/src/components/OnboardingWizard.tsx`
|
||||
- `dark/onboarding/new-company-modal-empty.png` — deps changed: `ui/src/components/OnboardingWizard.tsx`, `ui/src/components/SidebarCompanyMenu.tsx`
|
||||
- `dark/onboarding/sidebar-new-company-button.png` — deps changed: `ui/src/components/SidebarCompanyMenu.tsx`
|
||||
|
||||
### `dark/org` (5)
|
||||
|
||||
- `dark/org/chart.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `dark/org/org-chart-small-team.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `dark/org/org-chart-view.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `dark/org/reassign.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `dark/org/skills-list.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
|
||||
### `dark/plugins` (5)
|
||||
|
||||
- `dark/plugins/detail.png` — deps changed: `ui/src/pages/PluginPage.tsx`
|
||||
- `dark/plugins/install.png` — deps changed: `ui/src/pages/PluginManager.tsx`
|
||||
- `dark/plugins/jobs-log.png` — deps changed: `ui/src/pages/PluginSettings.tsx`
|
||||
- `dark/plugins/list.png` — deps changed: `ui/src/pages/PluginManager.tsx`
|
||||
- `dark/plugins/settings.png` — deps changed: `ui/src/pages/PluginSettings.tsx`
|
||||
|
||||
### `dark/projects` (6)
|
||||
|
||||
- `dark/projects/budget.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `dark/projects/configuration.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `dark/projects/issues.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `dark/projects/list.png` — deps changed: `ui/src/pages/Projects.tsx`
|
||||
- `dark/projects/overview.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `dark/projects/workspaces.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
|
||||
### `dark/routines` (4)
|
||||
|
||||
- `dark/routines/cron-picker.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`, `ui/src/components/ScheduleEditor.tsx`
|
||||
- `dark/routines/detail.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`
|
||||
- `dark/routines/list.png` — deps changed: `ui/src/pages/Routines.tsx`
|
||||
- `dark/routines/run-history.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`
|
||||
|
||||
### `dark/secrets` (3)
|
||||
|
||||
- `dark/secrets/user-secret-definition.png` — deps changed: `ui/src/pages/Secrets.tsx`, `ui/src/pages/secrets/UserSecretDefinitionsTab.tsx`
|
||||
- `dark/secrets/per-user-value-entry.png` — deps changed: `ui/src/pages/Secrets.tsx`, `ui/src/pages/secrets/SetMyUserSecretDialog.tsx`, `ui/src/pages/secrets/MyUserSecretsTab.tsx`
|
||||
- `dark/secrets/dispatch-check.png` — deps changed: `ui/src/pages/secrets/MissingUserSecretsBanner.tsx`, `server/src/services/secrets.ts`
|
||||
|
||||
### `dark/settings` (6)
|
||||
|
||||
- `dark/settings/experimental.png` — deps changed: `ui/src/pages/InstanceExperimentalSettings.tsx`
|
||||
- `dark/settings/instance-access.png` — deps changed: `ui/src/pages/InstanceAccess.tsx`
|
||||
- `dark/settings/instance-adapters.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `dark/settings/instance-general.png` — deps changed: `ui/src/pages/InstanceGeneralSettings.tsx`
|
||||
- `dark/settings/profile.png` — deps changed: `ui/src/pages/ProfileSettings.tsx`
|
||||
- `dark/settings/scheduler-heartbeats.png` — deps changed: `ui/src/pages/InstanceSettings.tsx`
|
||||
|
||||
### `dark/skills` (5)
|
||||
|
||||
- `dark/skills/add-skill-dialog-confirm.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `dark/skills/add-skill-dialog.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `dark/skills/assign-to-agent.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `dark/skills/file-inventory.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `dark/skills/list.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
|
||||
### `dark/tasks` (3)
|
||||
|
||||
- `dark/tasks/inbox-newly-created-tasks.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/tasks/inbox-view.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `dark/tasks/task-detail-with-comments.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
|
||||
### `dark/watchdogs` (1)
|
||||
|
||||
- `dark/watchdogs/watchdog-thread-outcome.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `server/src/services/task-watchdogs.ts`
|
||||
|
||||
### `dark/work-modes` (4)
|
||||
|
||||
- `dark/work-modes/ask-mode-answer.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueChatThread.tsx`
|
||||
- `dark/work-modes/plan-mode-plan.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueDocumentsSection.tsx`
|
||||
- `dark/work-modes/standard-mode-run.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueChatThread.tsx`
|
||||
- `dark/work-modes/work-mode-picker.png` — deps changed: `ui/src/components/NewIssueDialog.tsx`
|
||||
|
||||
### `dark/work-timeline` (2)
|
||||
|
||||
- `dark/work-timeline/work-timeline-overview.png` — deps changed: `ui/src/pages/Timeline.tsx`, `server/src/services/work-timeline.ts`
|
||||
- `dark/work-timeline/work-timeline-handoff.png` — deps changed: `ui/src/pages/Timeline.tsx`, `server/src/services/work-timeline.ts`
|
||||
|
||||
### `dark/workspaces` (5)
|
||||
|
||||
- `dark/workspaces/configuration.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `dark/workspaces/issues.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `dark/workspaces/list.png` — deps changed: `ui/src/pages/Workspaces.tsx`
|
||||
- `dark/workspaces/runtime-logs.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `dark/workspaces/services.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
|
||||
### `light/activity` (3)
|
||||
|
||||
- `light/activity/activity-filtered-by-agent.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
- `light/activity/activity-filters.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
- `light/activity/activity-log-full.png` — deps changed: `ui/src/pages/Activity.tsx`
|
||||
|
||||
### `light/adapters` (4)
|
||||
|
||||
- `light/adapters/detail.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `light/adapters/health.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `light/adapters/install.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `light/adapters/list.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
|
||||
### `light/agents` (23)
|
||||
|
||||
- `light/agents/adapter-type-dropdown.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/agent-detail-idle.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/agent-detail-run-history.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/agent-status-running.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/agents-list-empty.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `light/agents/budget-and-heartbeat-fields.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/budget.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/claude-local-config-filled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/codex-local-config.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/configuration.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/dashboard.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/heartbeat-toggle-disabled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/heartbeat-toggle-enabled.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/http-adapter-config.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/instructions.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/list.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `light/agents/new.png` — deps changed: `ui/src/pages/Agents.tsx`
|
||||
- `light/agents/run-history-in-progress.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/run-transcript-view.png` — deps changed: `ui/src/components/transcript/RunTranscriptView.tsx`, `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/runs.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/skills.png` — deps changed: `ui/src/pages/AgentDetail.tsx`
|
||||
- `light/agents/test-environment-fail.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/agents/test-environment-success.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
|
||||
### `light/approvals` (9)
|
||||
|
||||
- `light/approvals/approvals-list.png` — deps changed: `ui/src/pages/Approvals.tsx`, `ui/src/components/ApprovalCard.tsx`
|
||||
- `light/approvals/approvals-queue-strategy.png` — deps changed: `ui/src/pages/Approvals.tsx`, `ui/src/components/ApprovalPayload.tsx`
|
||||
- `light/approvals/approve-reject-buttons.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `light/approvals/approve-reject-revision-buttons.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `light/approvals/approved-approval.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `light/approvals/hire-approval-detail.png` — deps changed: `ui/src/components/ApprovalPayload.tsx`, `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `light/approvals/queue-filters.png` — deps changed: `ui/src/pages/Approvals.tsx`
|
||||
- `light/approvals/revision-request-input.png` — deps changed: `ui/src/pages/ApprovalDetail.tsx`
|
||||
- `light/approvals/strategy-approval-detail.png` — deps changed: `ui/src/components/ApprovalPayload.tsx`, `ui/src/pages/ApprovalDetail.tsx`
|
||||
|
||||
### `light/company` (6)
|
||||
|
||||
- `light/company/access.png` — deps changed: `ui/src/pages/CompanyAccess.tsx`
|
||||
- `light/company/export.png` — deps changed: `ui/src/pages/CompanyExport.tsx`
|
||||
- `light/company/import.png` — deps changed: `ui/src/pages/CompanyImport.tsx`
|
||||
- `light/company/invites.png` — deps changed: `ui/src/pages/CompanyInvites.tsx`
|
||||
- `light/company/join-requests.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/company/settings.png` — deps changed: `ui/src/pages/CompanySettings.tsx`
|
||||
|
||||
### `light/costs` (13)
|
||||
|
||||
- `light/costs/agent-budget-100pct-paused.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/agent-budget-50pct.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/agent-budget-80pct-warning.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/agent-budget-bar.png` — deps changed: `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/agent-budget-field.png` — deps changed: `ui/src/pages/AgentDetail.tsx`, `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/billers.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/budgets.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/company-budget-field.png` — deps changed: `ui/src/pages/Costs.tsx`, `ui/src/components/BudgetPolicyCard.tsx`
|
||||
- `light/costs/costs-dashboard-overview.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/finance.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/overview.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/per-run-cost-detail.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
- `light/costs/providers.png` — deps changed: `ui/src/pages/Costs.tsx`
|
||||
|
||||
### `light/dashboard` (4)
|
||||
|
||||
- `light/dashboard/activity-feed.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `light/dashboard/agent-status-panel.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `light/dashboard/dashboard-overview.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `light/dashboard/task-breakdown-panel.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
|
||||
### `light/interactions` (1)
|
||||
|
||||
- `light/interactions/checkbox-confirmation-card.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueThreadInteractionCard.tsx`
|
||||
|
||||
### `light/issues` (8)
|
||||
|
||||
- `light/issues/detail-activity.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `light/issues/detail-chat.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `light/issues/detail-sidebar.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `light/issues/inbox-requests.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/issues/inbox-unread.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/issues/inbox.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/issues/list.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
- `light/issues/my-issues.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
|
||||
### `light/onboarding` (4)
|
||||
|
||||
- `light/onboarding/empty-dashboard.png` — deps changed: `ui/src/pages/Dashboard.tsx`
|
||||
- `light/onboarding/goal-field.png` — deps changed: `ui/src/components/OnboardingWizard.tsx`
|
||||
- `light/onboarding/new-company-modal-empty.png` — deps changed: `ui/src/components/OnboardingWizard.tsx`, `ui/src/components/SidebarCompanyMenu.tsx`
|
||||
- `light/onboarding/sidebar-new-company-button.png` — deps changed: `ui/src/components/SidebarCompanyMenu.tsx`
|
||||
|
||||
### `light/org` (5)
|
||||
|
||||
- `light/org/chart.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `light/org/org-chart-small-team.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `light/org/org-chart-view.png` — deps changed: `ui/src/pages/OrgChart.tsx`
|
||||
- `light/org/reassign.png` — deps changed: `ui/src/components/AgentConfigForm.tsx`
|
||||
- `light/org/skills-list.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
|
||||
### `light/plugins` (5)
|
||||
|
||||
- `light/plugins/detail.png` — deps changed: `ui/src/pages/PluginPage.tsx`
|
||||
- `light/plugins/install.png` — deps changed: `ui/src/pages/PluginManager.tsx`
|
||||
- `light/plugins/jobs-log.png` — deps changed: `ui/src/pages/PluginSettings.tsx`
|
||||
- `light/plugins/list.png` — deps changed: `ui/src/pages/PluginManager.tsx`
|
||||
- `light/plugins/settings.png` — deps changed: `ui/src/pages/PluginSettings.tsx`
|
||||
|
||||
### `light/projects` (6)
|
||||
|
||||
- `light/projects/budget.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `light/projects/configuration.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `light/projects/issues.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `light/projects/list.png` — deps changed: `ui/src/pages/Projects.tsx`
|
||||
- `light/projects/overview.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
- `light/projects/workspaces.png` — deps changed: `ui/src/pages/ProjectDetail.tsx`
|
||||
|
||||
### `light/routines` (4)
|
||||
|
||||
- `light/routines/cron-picker.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`, `ui/src/components/ScheduleEditor.tsx`
|
||||
- `light/routines/detail.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`
|
||||
- `light/routines/list.png` — deps changed: `ui/src/pages/Routines.tsx`
|
||||
- `light/routines/run-history.png` — deps changed: `ui/src/pages/RoutineDetail.tsx`
|
||||
|
||||
### `light/secrets` (3)
|
||||
|
||||
- `light/secrets/user-secret-definition.png` — deps changed: `ui/src/pages/Secrets.tsx`, `ui/src/pages/secrets/UserSecretDefinitionsTab.tsx`
|
||||
- `light/secrets/per-user-value-entry.png` — deps changed: `ui/src/pages/Secrets.tsx`, `ui/src/pages/secrets/SetMyUserSecretDialog.tsx`, `ui/src/pages/secrets/MyUserSecretsTab.tsx`
|
||||
- `light/secrets/dispatch-check.png` — deps changed: `ui/src/pages/secrets/MissingUserSecretsBanner.tsx`, `server/src/services/secrets.ts`
|
||||
|
||||
### `light/settings` (6)
|
||||
|
||||
- `light/settings/experimental.png` — deps changed: `ui/src/pages/InstanceExperimentalSettings.tsx`
|
||||
- `light/settings/instance-access.png` — deps changed: `ui/src/pages/InstanceAccess.tsx`
|
||||
- `light/settings/instance-adapters.png` — deps changed: `ui/src/pages/AdapterManager.tsx`
|
||||
- `light/settings/instance-general.png` — deps changed: `ui/src/pages/InstanceGeneralSettings.tsx`
|
||||
- `light/settings/profile.png` — deps changed: `ui/src/pages/ProfileSettings.tsx`
|
||||
- `light/settings/scheduler-heartbeats.png` — deps changed: `ui/src/pages/InstanceSettings.tsx`
|
||||
|
||||
### `light/skills` (5)
|
||||
|
||||
- `light/skills/add-skill-dialog-confirm.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `light/skills/add-skill-dialog.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `light/skills/assign-to-agent.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `light/skills/file-inventory.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
- `light/skills/list.png` — deps changed: `ui/src/pages/CompanySkills.tsx`
|
||||
|
||||
### `light/tasks` (3)
|
||||
|
||||
- `light/tasks/inbox-newly-created-tasks.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/tasks/inbox-view.png` — deps changed: `ui/src/pages/Inbox.tsx`
|
||||
- `light/tasks/task-detail-with-comments.png` — deps changed: `ui/src/pages/IssueDetail.tsx`
|
||||
|
||||
### `light/watchdogs` (1)
|
||||
|
||||
- `light/watchdogs/watchdog-thread-outcome.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `server/src/services/task-watchdogs.ts`
|
||||
|
||||
### `light/work-modes` (4)
|
||||
|
||||
- `light/work-modes/ask-mode-answer.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueChatThread.tsx`
|
||||
- `light/work-modes/plan-mode-plan.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueDocumentsSection.tsx`
|
||||
- `light/work-modes/standard-mode-run.png` — deps changed: `ui/src/pages/IssueDetail.tsx`, `ui/src/components/IssueChatThread.tsx`
|
||||
- `light/work-modes/work-mode-picker.png` — deps changed: `ui/src/components/NewIssueDialog.tsx`
|
||||
|
||||
### `light/work-timeline` (2)
|
||||
|
||||
- `light/work-timeline/work-timeline-overview.png` — deps changed: `ui/src/pages/Timeline.tsx`, `server/src/services/work-timeline.ts`
|
||||
- `light/work-timeline/work-timeline-handoff.png` — deps changed: `ui/src/pages/Timeline.tsx`, `server/src/services/work-timeline.ts`
|
||||
|
||||
### `light/workspaces` (5)
|
||||
|
||||
- `light/workspaces/configuration.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `light/workspaces/issues.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `light/workspaces/list.png` — deps changed: `ui/src/pages/Workspaces.tsx`
|
||||
- `light/workspaces/runtime-logs.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
- `light/workspaces/services.png` — deps changed: `ui/src/pages/ExecutionWorkspaceDetail.tsx`
|
||||
## ⚠️ Registry hygiene (carried over)
|
||||
|
||||
**38 entries have no `captured_sha` at all**, so they can never be evaluated for staleness — invisible to this check every run. Areas: `company`, `dashboard`, `export-import`, `org`, `skills`, `tasks`, `work-modes` (light + dark). Recapturing them once would stamp a SHA and bring them back into the staleness system.
|
||||
@@ -0,0 +1,76 @@
|
||||
# Secret folders
|
||||
|
||||
Once a company keeps more than a handful of credentials, a single long list stops being useful. The Secrets screen can read your secret names as a folder tree instead: a secret named `dev/github/oauth/clientid` shows up inside `dev`, then `github`, then `oauth`, with `clientid` as the thing you actually click. Nothing new is stored to make that happen — the folders are simply a different way of looking at the names you already chose.
|
||||
|
||||
## Why the folder view exists
|
||||
|
||||
Secret names tend to grow structure on their own. People write `prod/stripe/apikey` and `dev/stripe/apikey` long before anyone offers them folders, because the prefix is how they tell two near-identical credentials apart. A flat list keeps that structure visible but doesn't help you use it: everything for every environment sits in one column, and finding the four secrets that belong to one service means scanning past the fifty that don't.
|
||||
|
||||
The folder view takes the structure that's already in the names and lets you walk it. You open `prod`, you see only what lives under `prod`, and the rest of the company's secrets stay out of your way until you go looking for them.
|
||||
|
||||
## A mental model
|
||||
|
||||
Think of the slash in a secret name the way you think of the slash in a file path — with one important difference: there is no folder to create or delete. Folders in Paperclip are derived entirely from the names of your secrets. If three secrets start with `dev/github/`, a `github` folder appears inside `dev`. Delete or rename those three, and the folder is simply gone, because nothing ever recorded it in the first place.
|
||||
|
||||
That has a practical consequence worth holding onto: **the name is the filing decision**. Where a secret lives is not a property you set separately, it's the text you typed in the Name field. Get the name right and the folder takes care of itself.
|
||||
|
||||
## Switching between Folders and Flat
|
||||
|
||||
The Secrets tab has a small **Folders / Flat** toggle next to the search box and filters.
|
||||
|
||||
- **Folders** shows one level at a time: subfolders first, then the secrets that live directly at this level.
|
||||
- **Flat** shows every secret in one list, exactly as before, with full names spelled out.
|
||||
|
||||
Your choice is remembered in the browser (under `paperclip.secrets.viewMode` in local storage), so the screen comes back the way you left it. Until you pick a side, Paperclip guesses: if any secret name contains a slash you land in Folders, and if none do you get Flat — a company that never used prefixes never sees a folder tree it doesn't need.
|
||||
|
||||
## Moving around the tree
|
||||
|
||||
The folder you're in lives in the address bar as a `?path=` parameter, which means every folder is a link you can bookmark or paste to a colleague. Opening such a link drops you straight into that folder, even if your own preference is Flat — the deep link wins for that visit.
|
||||
|
||||
Inside a folder you get three ways back out:
|
||||
|
||||
- **Breadcrumbs** across the top, starting at **All secrets** and ending at the folder you're in. Every step before the last one is clickable. Deep paths are shortened in the middle with an ellipsis so the trail always fits.
|
||||
- An **Up to `<parent>`** row at the top of the list. At one level down, that reads "Up to All secrets".
|
||||
- On narrow screens, a back chevron next to the current folder name.
|
||||
|
||||
Folder rows carry their own counts, written like `12 secrets · 3 folders`. The first number counts every secret anywhere beneath that folder, not just the ones directly inside it, so you can tell a busy branch from an empty one without opening it. The second is dropped entirely when a folder has no folders inside it, so a leaf folder just reads `4 secrets`. The same summary appears on the right of the breadcrumb line for the folder you're currently in.
|
||||
|
||||
Folders and secrets are sorted by name, with numbers ordered the way people expect (`item2` before `item10`) and capitalisation ignored.
|
||||
|
||||
## Search always searches everywhere
|
||||
|
||||
Search is deliberately not folder-aware. Whatever you type in **Search by name, key, ref** is matched against every secret in the company — name, key, description, and external reference — and the results come back as one flat list with full paths shown, so you can see at a glance which branch each match came from. The header above the results says how many matches there are, and if you were inside a folder when you started typing it says so explicitly: *searching everywhere, not just `dev/github`*.
|
||||
|
||||
While a search is active the Folders / Flat toggle is disabled, because there's only one sensible answer. Clear the search and you're back where you were.
|
||||
|
||||
## Creating a folder, and putting a secret in it
|
||||
|
||||
**New folder** appears next to **New secret** whenever you're in the folder view. It asks for a single folder name and nothing else. A name is required, and it can't contain a slash — to nest further, create the folder and then create another one inside it.
|
||||
|
||||
Here's the part that surprises people: creating a folder doesn't save anything. It walks you into the folder you just named, and that folder stays a staging area until a secret lands in it. If you navigate away without creating a secret, nothing was created and nothing was lost. That's not a limitation to work around, it's the same rule as everywhere else on this page — folders exist because secrets are named that way.
|
||||
|
||||
So the second half matters. Once you're standing in a folder, **New secret** opens the create dialog with the folder path already attached to the Name field as a small chip, and a note underneath reading *Creating in `dev/github` — remove the chip to type a different path*. You type only the last part of the name — `clientsecret` — and Paperclip stores `dev/github/clientsecret`. Click the × on the chip if you'd rather type the whole path yourself.
|
||||
|
||||
An empty folder you've just staged shows **No secrets in this folder yet** with a **New secret here** button, which is the same flow with one fewer click.
|
||||
|
||||
## Naming secrets so the tree stays useful
|
||||
|
||||
Because folders come from names, a little consistency early saves a lot of tidying later.
|
||||
|
||||
- Put the thing that varies most at the front. Most teams find that's the environment: `prod/...`, `dev/...`, `staging/...`.
|
||||
- Then the service, then the specific credential — `prod/stripe/apikey` rather than `stripe-prod-apikey`.
|
||||
- Keep depth honest. Every extra slash is another click for whoever comes after you.
|
||||
- A name and a folder can coexist: if you have a secret called `dev/github` *and* secrets called `dev/github/oauth/...`, you'll see both a `github` secret and a `github` folder side by side inside `dev`. It works, but it reads oddly — worth avoiding.
|
||||
|
||||
Paths are rendered the same way everywhere they appear, including in search results and on the **My secrets** tab: the folder part is muted and the final segment is bold, so your eye lands on the credential rather than the prefix.
|
||||
|
||||
## Giving an agent access to a secret
|
||||
|
||||
Open any secret and you'll find an **Agent access** panel listing the agents that receive it as an environment variable when a run starts. For a user-scoped secret, what each agent receives is the value belonging to the [responsible user](./secret-scopes.md) for that run.
|
||||
|
||||
Adding an agent takes two fields. The **Agent** control opens a searchable list with a **Filter agents** box at the top — type a few letters of an agent's name or title to narrow it down, which beats scrolling once a company has grown a real roster. The **Env var** field is the name the agent will see the value under; Paperclip suggests one from the secret and uppercases what you type. Press **Add** to grant it. Each granted agent gets a row showing the environment variable it receives and an × to take the access away again.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Secret scopes and the responsible user](./secret-scopes.md) explains company versus per-user values and the dispatch check.
|
||||
- [Secrets reference](../reference/deploy/secrets.md) covers providers, vaults, and secret references.
|
||||
@@ -52,6 +52,8 @@ The trade-off is real. User scope adds an onboarding step per human: a new teamm
|
||||
|
||||
Scope answers a different question than the rest of the secret model. Providers and vaults, covered in the [Secrets reference](../reference/deploy/secrets.md), decide where secret material is stored and how Paperclip reaches external stores. Scope decides who supplies a value and which runs can use it. Reading that page first helps if you are new to secrets generally; this page assumes the basics and explains only the scoping layer.
|
||||
|
||||
Naming is a third, separate question. Slashes in a secret name give the Secrets screen a folder tree to browse, and that grouping is independent of scope — a folder happily holds both company-scoped and user-scoped secrets. See [Secret folders](./secret-folders.md) for how the tree is derived and how to file secrets into it.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Secrets reference](../reference/deploy/secrets.md) covers the default provider, provider vaults, strict mode, and migrating inline credentials to secret references.
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Connections v3 (Apps)
|
||||
|
||||
**Apps** is the experimental home for connecting Paperclip to the outside services your agents use — think Google Sheets, remote MCP servers, and REST APIs. Behind it sits **Connections v3**, a rebuilt foundation for how Paperclip stores, authorizes, and hands out those connections. This release lands the groundwork for one-click Connected Apps: a typed app catalog, a stable connection identity, and subject-aware authorization.
|
||||
|
||||
It's early. What ships in this release is the plumbing, not a finished app store. You can turn it on and explore, but treat it as a preview.
|
||||
|
||||
## Why it exists
|
||||
|
||||
Connecting an external service used to mean a loose bundle of config and secrets with no clear owner and no stable name. Connections v3 replaces that with an explicit model:
|
||||
|
||||
- **A stable connection identity.** Every connection gets a company-scoped `uid` (like `google-sheets/finance-sheet-1a2b3c4d`) that stays put even as names change, so other parts of Paperclip can reference it reliably.
|
||||
- **Explicit ownership, auth, and transport.** Each connection now records who owns it, how it authenticates (`oauth`, `api_key`, or `none`), and how it talks to the service.
|
||||
- **Subject-aware grants.** A new `connection_grants` table separates *the connection* from *who is allowed to use it*. A grant is either a `workspace` grant (shared by the whole company) or a `user` grant (scoped to one person), so a single connection can serve a shared default and per-user authorizations at the same time.
|
||||
- **Multi-key credentials.** Connections can hold more than one credential reference, which is what real-world apps with several keys or scopes need.
|
||||
|
||||
On top of that, a generated **AppDefinition** catalog gives the browse-and-connect experience a single typed contract, and a runtime layer adds subject-aware authorization state and scoped key handling. Grant management routes are registered in the OpenAPI surface and **fail closed** for scopes they don't recognize — an unknown scope is refused rather than quietly allowed.
|
||||
|
||||
## Turn it on
|
||||
|
||||
1. Go to **Settings → Instance settings → Experimental**.
|
||||
2. Turn on **Apps** — *"Show the Apps navigation and allow access to app connections, gateways, and advanced app tooling."*
|
||||
|
||||
The change takes effect immediately. An **Apps** section appears in the navigation, where you can browse the app gallery and set up connections. Flip the toggle back off and the surface hides again — your connections and grants aren't deleted.
|
||||
|
||||
## What you get today
|
||||
|
||||
With **Apps** on, you can:
|
||||
|
||||
- **Browse the app gallery** — the Wave 1 catalog of connectable apps, served from a single typed AppDefinition contract.
|
||||
- **Connect an app** — set up a connection, including the OAuth handshake for apps that need it.
|
||||
- **Manage grants** — see which workspace and user grants exist on a connection, add an installation, and revoke a grant.
|
||||
|
||||
Because this is a foundation, the depth varies by app and some flows are still bare. Expect the browse-and-setup experience to fill in over coming releases.
|
||||
|
||||
## Note for operators: the `remote_http` rename
|
||||
|
||||
Connections v3 renames the legacy `remote_http` transport to `mcp_remote`. This is what remote MCP server connections use.
|
||||
|
||||
You don't need to do anything. The change ships as two additive database migrations that run automatically on startup:
|
||||
|
||||
- `0182_connections_v3_schema_core` — backfills connection `uid`s, adds the ownership/auth fields, creates the `connection_grants` table, seeds a default `workspace` grant for every existing connection, and rewrites any `transport` of `remote_http` to `mcp_remote`.
|
||||
- `0183_connection_user_authorization_state` — adds subject-aware authorization state used by per-user grants.
|
||||
|
||||
The only thing to know: if you have tooling, scripts, or notes that refer to the `remote_http` transport by name, update them to `mcp_remote`. The valid transports are now `mcp_remote`, `rest_api`, and `local_stdio`.
|
||||
|
||||
## Still evolving
|
||||
|
||||
This is a foundation shipped behind an experimental flag, and it's still moving:
|
||||
|
||||
- **No compatibility guarantees.** Schema, routes, catalog entries, and the UI can change, break, or be removed between releases. Don't build critical workflows on top of it yet, and re-read the release notes after upgrading.
|
||||
- **The catalog is Wave 1.** The set of connectable apps is an early cut and will grow.
|
||||
- **Setup flows are partial.** The one-click Connected Apps experience is the destination, not what ships today.
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Experimental features overview](overview.md) — how experimental flags work and what "no compatibility guarantees" means.
|
||||
- [Cloud Sync](cloud-sync.md) — another connection-oriented experimental surface.
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.720.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# ACPX Local (retired)
|
||||
@@ -16,6 +16,19 @@ Now `claude_local`, `codex_local`, and `gemini_local` each speak ACP natively. L
|
||||
|
||||
Each of those pages documents the ACP fields — `agentCommand`, `mode`, `nonInteractivePermissions`, `stateDir`, `warmHandleIdleMs` — in its own configuration section.
|
||||
|
||||
## Runs natively on Windows
|
||||
|
||||
Good news if you work on Windows: your local ACP agents now run there too, right alongside Linux. The embedded ACPX engine used to launch each local agent command by wrapping it in a generated Bash script, which meant it only worked where a Bash shell was available. That wrapper is gone. Claude, Codex, Gemini, and any custom ACP adapter now spawn as a native process on whichever platform you're on — no shell script in the middle.
|
||||
|
||||
You don't need to change anything to get this. Set `engine` to `auto` (the default) or `acp`, pick the adapter that matches your harness, and the run starts the same way on Windows as it does on Linux.
|
||||
|
||||
Here's what changed under the hood so the native path behaves cleanly:
|
||||
|
||||
- **Environment variables travel through ACPX session options.** Instead of being baked into a shell script, your agent's environment (including the values Paperclip injects for a run) is handed to the ACPX runtime as part of the session options and applied when the process spawns.
|
||||
- **Agent output on stderr is captured in-process.** The engine reads the child process's stderr directly rather than relying on shell redirection, so diagnostics still show up in your run log on every platform.
|
||||
- **`.cmd` shims are preferred on Windows.** When the engine resolves a bundled `npm`/`pnpm` binary from `node_modules/.bin`, on Windows it looks for the `.cmd` shim first (for example `pnpm.cmd`) before falling back to the bare name — which is how these tools are actually installed on Windows.
|
||||
- **Symlinks fall back to copies.** Paperclip stages runtime skills and config files into place with symlinks. On Windows, creating a symlink can fail with an `EPERM` permission error; when that happens, the engine copies the file instead so setup still succeeds.
|
||||
|
||||
## If you had an ACPX agent
|
||||
|
||||
**You do not need to do anything.** Existing `acpx_local` agents were migrated for you when your instance upgraded. The migration reads each agent's old `agent` field and moves it to the matching adapter:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.720.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Codex
|
||||
@@ -155,6 +155,18 @@ Each `codex_local` agent runs against its own managed Codex home, so one agent c
|
||||
- **Your own `CODEX_HOME` is left alone.** If you point the adapter at a `CODEX_HOME` outside Paperclip's managed company tree, Paperclip treats it as self-managed and never seeds or overwrites it.
|
||||
- **No silent credential-less runs.** If a managed home ends up with no usable login and no configured API key, the run fails fast with a clear adapter error instead of starting Codex and hitting a `401` from the provider.
|
||||
|
||||
### CODEX_HOME sync into a sandbox
|
||||
|
||||
When this agent runs in a Paperclip sandbox, the adapter does not copy your whole Codex home into the sandbox. Instead it stages an explicit allowlist, so only the material a run actually needs crosses the boundary and none of your host-local runtime state comes along for the ride. The synced entries are:
|
||||
|
||||
- `config.json`
|
||||
- `config.toml`
|
||||
- `instructions.md`
|
||||
- `auth.json`
|
||||
- `skills`
|
||||
|
||||
Everything the stock `codex` binary writes at runtime — session databases (`*.sqlite`, `*-wal`), `plugins/`, `cache/`, `sessions/`, `shell_snapshots/`, and the like — stays on the host and is intentionally left out. This allowlist only applies to sandboxed execution; local and SSH targets read the managed home in place.
|
||||
|
||||
---
|
||||
|
||||
## Environment Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.618.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Agents
|
||||
@@ -190,6 +190,102 @@ inbox = res.json()
|
||||
|
||||
---
|
||||
|
||||
## Fetch Granted Secrets
|
||||
|
||||
Sometimes an agent needs a credential it wasn't handed at launch — a value that shouldn't live in the process environment for the whole run, or one an operator granted after the agent was already working. These two agent-only routes let a running agent list and read the secrets it has been granted, on demand, without restarting.
|
||||
|
||||
Both routes are run-bound. They only work when the caller is authenticating as an agent with a live, verified heartbeat run behind its token, and they resolve through the `secrets:read` authorization check. Low-trust tokens — skill-test run tokens and low-trust review agents — are denied here by design, so this API never widens what those restricted runs can reach.
|
||||
|
||||
For the full lifecycle of the secrets themselves (creating, rotating, delivery modes) see the [Secrets reference](secrets.md). Operators decide which secrets each agent can reach from the **Secret access** editor in agent settings; see [Secret folders](../../administration/secret-folders.md) for that picker.
|
||||
|
||||
### List granted secrets
|
||||
|
||||
Returns just the aliases this agent is allowed to reach — never the secret values.
|
||||
|
||||
`GET /api/agents/me/secrets`
|
||||
|
||||
The response is `{ "secrets": [...] }`. Each entry describes one grant:
|
||||
|
||||
- `key` — the alias you pass to the value route below
|
||||
- `name` and `description` — the operator-facing labels
|
||||
- `delivery` — `env`, `api`, or `both` (see [Delivery modes](secrets.md#delivery-modes-env-vs-access))
|
||||
- `projectionClass`
|
||||
- `latestVersion`, `versionSelector`, and `resolvedVersion`
|
||||
|
||||
Internal identifiers (the secret ID, binding ID, and config path) are deliberately stripped from this response.
|
||||
|
||||
<!-- tabs: cURL, JavaScript, Python -->
|
||||
|
||||
<!-- tab: cURL -->
|
||||
```bash
|
||||
curl -s \
|
||||
"http://localhost:3100/api/agents/me/secrets" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
```
|
||||
<!-- tab: JavaScript -->
|
||||
```js
|
||||
const res = await fetch("http://localhost:3100/api/agents/me/secrets", {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
const { secrets } = await res.json();
|
||||
```
|
||||
<!-- tab: Python -->
|
||||
```python
|
||||
import os, requests
|
||||
|
||||
res = requests.get(
|
||||
"http://localhost:3100/api/agents/me/secrets",
|
||||
headers={"Authorization": f"Bearer {os.environ['PAPERCLIP_API_KEY']}"},
|
||||
)
|
||||
secrets = res.json()["secrets"]
|
||||
```
|
||||
<!-- /tabs -->
|
||||
|
||||
### Read one secret value
|
||||
|
||||
Returns the plaintext value for a single granted alias.
|
||||
|
||||
`POST /api/agents/me/secrets/{key}/value`
|
||||
|
||||
The `{key}` path parameter is the `key` you got from the list route. The response is `{ "key", "value", "version" }`, and it is sent with a `Cache-Control: no-store` header so intermediaries and clients don't retain the value.
|
||||
|
||||
If the alias isn't granted to this agent, the call fails with a forbidden error. Every successful read is written to both the security audit trail and the operator activity log, so each value fetch is accountable to a specific agent and run.
|
||||
|
||||
<!-- tabs: cURL, JavaScript, Python -->
|
||||
|
||||
<!-- tab: cURL -->
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
"http://localhost:3100/api/agents/me/secrets/stripe-api-key/value" \
|
||||
-H "Authorization: Bearer $PAPERCLIP_API_KEY"
|
||||
```
|
||||
<!-- tab: JavaScript -->
|
||||
```js
|
||||
const res = await fetch(
|
||||
"http://localhost:3100/api/agents/me/secrets/stripe-api-key/value",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
const { value, version } = await res.json();
|
||||
```
|
||||
<!-- tab: Python -->
|
||||
```python
|
||||
import os, requests
|
||||
|
||||
res = requests.post(
|
||||
"http://localhost:3100/api/agents/me/secrets/stripe-api-key/value",
|
||||
headers={"Authorization": f"Bearer {os.environ['PAPERCLIP_API_KEY']}"},
|
||||
)
|
||||
secret = res.json()
|
||||
```
|
||||
<!-- /tabs -->
|
||||
|
||||
Both routes are agent-only.
|
||||
|
||||
---
|
||||
|
||||
## Create Agent
|
||||
|
||||
Creates a new agent in a company.
|
||||
|
||||
@@ -51,6 +51,8 @@ On a brand-new private instance that requires login, this lets the first person
|
||||
|
||||
**Availability.** This route only exists when the instance runs with `deploymentMode` set to `authenticated` **and** `deploymentExposure` set to `private`. On any other configuration it returns `404` with `Browser first-admin claim is not available`.
|
||||
|
||||
**Cloud-managed instances.** If your instance is managed by a Paperclip control plane rather than run by you, you never meet this claim screen at all — the instance comes up ready to use. The control plane owns identity there, and the users it signs in are deliberately never given the `instance_admin` role, so `GET /api/health` skips the first-admin check entirely and always reports `bootstrapStatus: "ready"`. Self-hosted instances are unaffected: if you run the server yourself, the claim flow behaves exactly as described here.
|
||||
|
||||
**Authentication.** The caller must be a signed-in browser session (a board actor whose session source is the browser). Other callers — agents, CLI tokens, unauthenticated requests — get `401` with `Sign in from a browser session before claiming first admin`.
|
||||
|
||||
**Behaviour.** If no `instance_admin` exists yet, the signed-in user is promoted to `instance_admin`. The claim is atomic — the table is locked so exactly one user can win. If the instance has already been claimed, the route returns `409` with `Someone else has already claimed this instance`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.525.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Secrets
|
||||
@@ -551,6 +551,46 @@ requests.post(
|
||||
|
||||
---
|
||||
|
||||
## Delivery modes: env vs access
|
||||
|
||||
There is more than one way for a secret to reach an agent, and the difference matters when you care about how long a value sits in memory.
|
||||
|
||||
- **Environment injection (`env`)** is the classic path described above. Paperclip resolves the secret at launch and injects the plaintext into the agent process environment. The value is present for the whole run.
|
||||
- **Run-bound access (`api`)** exposes the secret through an API the running agent calls on demand, instead of pre-loading it into the environment. Nothing is injected up front; the agent fetches the value only when it needs it.
|
||||
- **`both`** means the same secret is available through environment injection *and* the run-bound access API.
|
||||
|
||||
When you list an agent's granted secrets (see below), each entry reports its `delivery` as `env`, `api`, or `both`, so an agent can tell how a given credential will be delivered before it reaches for it.
|
||||
|
||||
Access-mode grants are the ones marked as API-only. Operators manage which secrets each agent can reach — and whether a grant is delivered as an environment variable or as run-bound access — from the **Secret access** editor in agent settings. The deep folder and picker UI for that editor is documented in [Secret folders](../../administration/secret-folders.md).
|
||||
|
||||
## Run-Bound Agent Secret Access
|
||||
|
||||
These two routes let a running agent read the secrets it has been granted, on demand, rather than relying only on values injected at launch. They are agent-only and run-bound: the caller must authenticate as an agent whose token is backed by a live, verified heartbeat run, and every request is evaluated through the `secrets:read` authorization check.
|
||||
|
||||
Low-trust tokens stay denied here on purpose — skill-test run tokens and low-trust review agents cannot use this API, so it never broadens what those restricted runs can reach.
|
||||
|
||||
### List granted secrets
|
||||
|
||||
```http
|
||||
GET /api/agents/me/secrets
|
||||
```
|
||||
|
||||
Returns just the aliases this agent is granted — never the secret values. The response is `{ "secrets": [...] }`, where each entry carries `key`, `name`, `description`, `delivery`, `projectionClass`, `latestVersion`, `versionSelector`, and `resolvedVersion`. The internal secret ID, binding ID, and config path are stripped from the payload.
|
||||
|
||||
Use the `key` from each entry as the alias for the value route below.
|
||||
|
||||
### Read one secret value
|
||||
|
||||
```http
|
||||
POST /api/agents/me/secrets/{key}/value
|
||||
```
|
||||
|
||||
Returns the plaintext value for a single granted alias. The `{key}` path parameter is the `key` from the list route. The response body is `{ "key", "value", "version" }`, and it is returned with a `Cache-Control: no-store` header so the value is not retained by caches or intermediaries.
|
||||
|
||||
If the alias is not granted to the calling agent, the request is rejected with a forbidden error. Every successful value read is written to both the security audit trail and the operator activity log, so each fetch is tied to a specific agent and run.
|
||||
|
||||
Full request and response examples for both routes live on the [Agents reference](agents.md#fetch-granted-secrets) page, since they are agent-run-scoped.
|
||||
|
||||
## `secret-ref` form fields
|
||||
|
||||
Some configs aren't typed by hand — they're driven by a JSON schema the server publishes, and the UI renders the form from that schema. Whenever a string field in such a schema declares `"format": "secret-ref"`, the UI swaps the plain text input for a secret binding picker.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.609.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Company Commands
|
||||
@@ -100,8 +100,9 @@ paperclipai company export <company-id> \
|
||||
| `--issues <values>` | Comma-separated issue identifiers/IDs to include. |
|
||||
| `--project-issues <values>` | Comma-separated project shortnames/IDs whose issues should be exported. |
|
||||
| `--expand-referenced-skills` | Vendor skill contents into the package instead of exporting upstream references. Defaults to off. |
|
||||
| `--force` | Overwrite a non-empty output directory without the interactive confirmation. Defaults to off. |
|
||||
|
||||
The default `--include` is deliberately small (`company,agents`) so a quick export does not drag along every task. Widen it explicitly when you want a full clone. If the output directory already contains files, the command asks you to confirm an overwrite in an interactive terminal, and refuses outright in a non-interactive one — choose an empty directory for headless runs.
|
||||
The default `--include` is deliberately small (`company,agents`) so a quick export does not drag along every task. Widen it explicitly when you want a full clone. If the output directory already contains files, the command asks you to confirm an overwrite in an interactive terminal, and refuses outright in a non-interactive one — pass `--force` to overwrite anyway, or choose an empty directory for headless runs. `--force` is what you want for automated exports (for example the nightly backup routine) that write into a directory that legitimately still holds files.
|
||||
|
||||
On success the command prints the resolved output path, the package root, the number of files written, the `.paperclip.yaml` extension path, and a warning count.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.720.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Environment Variables
|
||||
@@ -126,6 +126,15 @@ These variables control where the server forwards operator-submitted feedback (a
|
||||
|
||||
If neither variable is set, feedback submissions are stored locally and never leave the instance.
|
||||
|
||||
### Turning off telemetry
|
||||
|
||||
Paperclip ships anonymized usage telemetry (which adapters you run, and similar coarse signals) that helps the project understand how Paperclip is used. It carries no personal data, and you can turn it off completely by setting either of these variables. Both are checked for the exact value `1`, and either one on its own disables telemetry.
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `PAPERCLIP_TELEMETRY_DISABLED` | Set to `1` to disable Paperclip's anonymized telemetry. |
|
||||
| `DO_NOT_TRACK` | The cross-tool [Console Do Not Track](https://consoledonottrack.com/) convention. Set to `1` to disable telemetry. |
|
||||
|
||||
---
|
||||
|
||||
## Observability (OpenTelemetry)
|
||||
@@ -194,6 +203,16 @@ Those are mainly useful for adapter authors and agent-side tooling that need dir
|
||||
|
||||
> **Audit trail:** Every mutating API request from an agent run should include the `X-Paperclip-Run-Id: $PAPERCLIP_RUN_ID` header. The server uses it to attribute issue updates, comments, checkouts, and subtasks to the heartbeat run that produced them. Read-only requests do not require it.
|
||||
|
||||
### How `PAPERCLIP_*` env bindings reach the run
|
||||
|
||||
You can define your own environment variables on an agent, project, routine, or adapter config, and they flow into the run environment — including ones you deliberately name with a `PAPERCLIP_` prefix (for example a secret called `PAPERCLIP_CLOUD_PROD_PROVIDER_RAILWAY_TOKEN`). The server applies one simple policy when it builds each run's environment:
|
||||
|
||||
- **`PAPERCLIP_API_KEY` is never accepted** from your agent, project, routine, or adapter config. The harness mints a short-lived run token for every run, and that minted token is the only source of `PAPERCLIP_API_KEY`.
|
||||
- **Harness-assigned runtime variables always win.** The variables Paperclip sets for the run itself (like `PAPERCLIP_RUN_ID`, `PAPERCLIP_AGENT_ID`, and the wake/workspace variables above) take precedence over any same-named binding you define, so you cannot accidentally shadow them.
|
||||
- **Every other `PAPERCLIP_*` binding flows through** to the run environment just like a variable with no prefix. Naming a value with the `PAPERCLIP_` prefix no longer causes it to be dropped.
|
||||
|
||||
> **Upgrade note (v2026.722.0):** Earlier releases stripped *every* `PAPERCLIP_`-prefixed binding before resolving the run environment, which silently dropped custom secrets you had named with that prefix — those now pass through as expected. In the same change, a static `PAPERCLIP_API_KEY` you set in adapter or config env to override the run token no longer has any effect. If you relied on that override, drop it: the harness-minted run token is now the only source of the run API key.
|
||||
|
||||
---
|
||||
|
||||
## LLM Provider Keys
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
paperclip_version: v2026.618.0
|
||||
paperclip_version: v2026.722.0
|
||||
---
|
||||
|
||||
# Plugin SDK
|
||||
@@ -205,7 +205,29 @@ Protocol types: `JsonRpcId`, `JsonRpcRequest`, `JsonRpcSuccessResponse`, `JsonRp
|
||||
|
||||
External-object protocol shapes: `PluginExternalObjectUrlCandidate`, `PluginExternalObjectSourceContext`, `DetectExternalObjectsParams`, `PluginExternalObjectDetection`, `DetectExternalObjectsResult`, `PluginExternalObjectRecordSnapshot`, `ResolveExternalObjectParams`, `PluginExternalObjectResolvedSnapshot`, `PluginExternalObjectResolveResult`, `RefreshExternalObjectsParams`, `RefreshExternalObjectsResult`. See [External-object reference providers](#external-object-reference-providers) for the lifecycle that uses them.
|
||||
|
||||
Environment-driver protocol shapes: `PluginEnvironmentDiagnostic`, `PluginEnvironmentDriverBaseParams`, `PluginEnvironmentValidateConfigParams`, `PluginEnvironmentValidationResult`, `PluginEnvironmentProbeParams`, `PluginEnvironmentProbeResult`, `PluginEnvironmentLease`, `PluginEnvironmentAcquireLeaseParams`, `PluginEnvironmentResumeLeaseParams`, `PluginEnvironmentReleaseLeaseParams`, `PluginEnvironmentDestroyLeaseParams`, `PluginEnvironmentRealizeWorkspaceParams`, `PluginEnvironmentRealizeWorkspaceResult`, `PluginEnvironmentExecuteParams`, `PluginEnvironmentExecuteResult`, `PluginEnvironmentInteractiveSetupStatus`, `PluginEnvironmentInteractiveSetupConnectionType`, `PluginEnvironmentTemplateRefKind`, `PluginEnvironmentInteractiveSetupConnectionSummary`, `PluginEnvironmentInteractiveSetupConnectionPayload`, `PluginEnvironmentInteractiveSetupSession`, `PluginEnvironmentStartInteractiveSetupParams`, `PluginEnvironmentGetInteractiveSetupParams`, `PluginEnvironmentCaptureTemplateParams`, `PluginEnvironmentCaptureTemplateResult`, `PluginEnvironmentCancelInteractiveSetupParams`, `PluginEnvironmentCancelInteractiveSetupResult`, `PluginEnvironmentDeleteTemplateParams`, `PluginEnvironmentDeleteTemplateResult`, `PluginEnvironmentTemplateConfigBinding`. The last block backs the optional interactive-setup and template-capture hooks described below.
|
||||
Environment-driver protocol shapes: `PluginEnvironmentDiagnostic`, `PluginEnvironmentDriverBaseParams`, `PluginEnvironmentValidateConfigParams`, `PluginEnvironmentValidationResult`, `PluginEnvironmentProbeParams`, `PluginEnvironmentProbeResult`, `PluginEnvironmentLease`, `PluginEnvironmentAcquireLeaseParams`, `PluginEnvironmentResumeLeaseParams`, `PluginEnvironmentReleaseLeaseParams`, `PluginEnvironmentDestroyLeaseParams`, `PluginEnvironmentRealizeWorkspaceParams`, `PluginEnvironmentRealizeWorkspaceResult`, `PluginEnvironmentExecuteParams`, `PluginEnvironmentExecuteResult`, `PluginSyncFileMapping`, `PluginSyncOperation`, `PluginEnvironmentSyncInParams`, `PluginEnvironmentSyncOutParams`, `PluginEnvironmentSyncResult`, `PluginEnvironmentInteractiveSetupStatus`, `PluginEnvironmentInteractiveSetupConnectionType`, `PluginEnvironmentTemplateRefKind`, `PluginEnvironmentInteractiveSetupConnectionSummary`, `PluginEnvironmentInteractiveSetupConnectionPayload`, `PluginEnvironmentInteractiveSetupSession`, `PluginEnvironmentStartInteractiveSetupParams`, `PluginEnvironmentGetInteractiveSetupParams`, `PluginEnvironmentCaptureTemplateParams`, `PluginEnvironmentCaptureTemplateResult`, `PluginEnvironmentCancelInteractiveSetupParams`, `PluginEnvironmentCancelInteractiveSetupResult`, `PluginEnvironmentDeleteTemplateParams`, `PluginEnvironmentDeleteTemplateResult`, `PluginEnvironmentTemplateConfigBinding`. The `PluginSync*` and `PluginEnvironmentSync*` shapes back the optional sandbox file-sync hooks, and the interactive-setup and template-capture shapes back the setup hooks — both described below.
|
||||
|
||||
#### Sandbox file sync (optional)
|
||||
|
||||
By default the host moves files in and out of a leased sandbox with a byte-identical base64-over-`environmentExecute` fallback. If your driver can do better — a provider-native bulk upload, an internal tar stream, per-file enumeration — you can take over the transfer by implementing a matched pair of hooks on the object you pass to `definePlugin({...})`:
|
||||
|
||||
- `onEnvironmentSyncIn(params: PluginEnvironmentSyncInParams): Promise<PluginEnvironmentSyncResult>` — called before execution to place host files and directories at their target sandbox paths.
|
||||
- `onEnvironmentSyncOut(params: PluginEnvironmentSyncOutParams): Promise<PluginEnvironmentSyncResult>` — called after execution to copy sandbox files and directories back to their target host paths.
|
||||
|
||||
Both hooks are optional and opt-in, but they come as a pair: define **both** to advertise the `environmentSyncIn` / `environmentSyncOut` methods, and the host routes transfers through your driver. Leave them undefined and the base64 fallback stays in effect — a driver that only leases and executes can ignore them entirely.
|
||||
|
||||
Each params object carries the current `PluginEnvironmentLease` plus an ordered `operations` array of `PluginSyncOperation`. Operations are applied in array order; each one bundles an opaque, non-sensitive `operationId` (authored by the orchestrator — your driver must not interpret it) and a `files` list of `PluginSyncFileMapping`. A mapping describes one source→target transfer:
|
||||
|
||||
| Field | Type | Declares |
|
||||
|---|---|---|
|
||||
| `sourcePath` | `string` | Absolute path of the transfer source — a host path for syncIn, a sandbox path for syncOut. |
|
||||
| `targetPath` | `string` | Absolute path of the transfer target — a sandbox path for syncIn, a host path for syncOut. Sandbox paths are POSIX. |
|
||||
| `kind` | `"file" \| "directory"` | Whether the mapping moves a single regular file or a directory tree. |
|
||||
| `mode?` | `number` | POSIX file mode to apply at the target (e.g. `0o600` for secret material). When set, the target must be created with this mode and no world-readable window — create-with-mode or chmod-before-bytes, never after. |
|
||||
| `exclude?` | `string[]` | Glob patterns to skip when `kind` is `"directory"`. |
|
||||
| `followSymlinks?` | `boolean` | Symlink handling for directory transfers. Falsy preserves symlinks as links; `true` dereferences them to their target bytes (mirrors tar's `-h`). |
|
||||
|
||||
Return a `PluginEnvironmentSyncResult`: an `operations` array echoing each `operationId` with its `filesTransferred` and `bytesTransferred` counts, for host-side observability. The contract is provider-agnostic — transfer a directory however you like, as long as the observable result matches the mappings. For the full authoring rules, see the parent `doc/plugins/SANDBOX_FILE_SYNC_HOOKS.md`.
|
||||
|
||||
#### Interactive setup and reusable templates (optional)
|
||||
|
||||
|
||||
@@ -198,6 +198,10 @@
|
||||
"title": "Overview",
|
||||
"file": "../docs/experimental/overview.md"
|
||||
},
|
||||
{
|
||||
"title": "Connections v3 (Apps)",
|
||||
"file": "../docs/experimental/connections-apps.md"
|
||||
},
|
||||
{
|
||||
"title": "Environments",
|
||||
"file": "../docs/experimental/environments.md"
|
||||
@@ -380,6 +384,10 @@
|
||||
"title": "Secret Scopes",
|
||||
"file": "../docs/administration/secret-scopes.md"
|
||||
},
|
||||
{
|
||||
"title": "Secret Folders",
|
||||
"file": "../docs/administration/secret-folders.md"
|
||||
},
|
||||
{
|
||||
"title": "Settings",
|
||||
"file": "../docs/administration/settings.md"
|
||||
|
||||
Reference in new issue
Block a user