Use the agent lifecycle contract for plugin setup and status (#15391)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Some plugins must prepare resources before an agent can work.
> - Those resources must stop or be removed when the agent pauses or
terminates.
> - The agent lifecycle module now owns these steps and their durable
completion.
> - Plugins use its required callback contract instead of a separate
readiness query.
> - This PR documents that contract and shows its progress and retry
action on the agent page.

## Linked Issues or Issue Description

Refs: #15631

**Subsystem affected**

Required plugin lifecycle work and the agent detail page.

**Problem or motivation**

The lifecycle module exposes `onAgentLifecycle` and
`agents.lifecycle.manage`, but the plugin authoring guide does not
explain their use. The agent page displays legacy status. Setup
therefore looks paused and offers Resume. Termination leaves the page
before resource cleanup completes.

**Proposed solution**

Use the callback, admission guards, persisted state, and retry route
supplied by the lifecycle module. Show pending phases and fixed host
errors. Keep the detail page open during incomplete termination. Use
lifecycle state for pause and resume controls.

**Alternatives considered**

A separate plugin readiness query would duplicate the module's state and
admission rules. The final diff contains no readiness API, database
commit hooks, or second admission check.

## What Changed

- Document the manifest declaration, callback results, phase ordering,
capability scope, retry behavior, and provider revision requirements.
- Correct SDK comments for lifecycle, routing, and external object
callbacks.
- Show preparation, verification, pause, resume, termination, and
cleanup progress from the existing agent fields.
- Refresh pending steps every two seconds and other states every thirty
seconds. Keep these reads separate from the agent record used by
editable forms. Use the existing authorized retry route for failed
steps.
- Offer Pause during setup. Disable pause and resume while stop or
cleanup is incomplete. Keep cleanup failures reachable on the detail
page. Disable new work after termination starts.
- Test required resource work across setup, pause, resume, and confirmed
deletion. Test every worker callback phase, missing handlers, retry
authorization, and UI behavior.

## Verification

- Focused UI tests: 139 passed across lifecycle status, action buttons,
API tests, and the configuration form. A regression preserves unsaved
edits during an in-flight lifecycle refresh and a failed refresh.
- Focused host and shared tests: 113 passed across lifecycle
integration, retry authorization, plugin RPC, and manifest validation.
The database suite used isolated local PostgreSQL 18 through a temporary
loader outside the repository.
- Module boundary tests: 11 passed. Module import/write checks and UI
token gates passed.
- Plugin SDK build, server direct TypeScript check, and UI TypeScript
check passed.
- `git diff --check` and an added-line secrets/private-reference scan
passed. Fixtures use synthetic identities.
- Full `pnpm -r typecheck` and `pnpm build` passed using the existing
local Rust toolchain.
- Visually checked all six progress phases with the application
components and styles. Captured preparation and cleanup failure
screenshots from the production UI build with synthetic API data;
artifacts are kept outside git.
- Full local `pnpm test:run` was attempted and stopped after a
native-runner failure reproduced in isolation:
`native-session-resume.test.ts` / `archives a damaged prior epoch before
completing guarded same-task replacement with real runnerd` returns a
failed Codex fixture result instead of success. The native-runtime and
runner trees are unchanged from the base. No full local test pass is
claimed.
- All latest-head CI checks passed, including test shards, verification,
E2E, and the canary packaging check. Greptile reviewed commit
`96473e03a` at 5/5 with no unresolved comments.

## Risks

- Selected required plugins remain a dependency for lifecycle
completion. The lifecycle module retains their requirements when a
plugin is disabled or removed.
- Plugins must make repeated provider operations safe and fence older
revisions. Host revision checks do not cancel external requests.
- Polling adds one agent-detail read every two seconds during pending
work and every thirty seconds otherwise.
- No schema change, private VM provider implementation, resource
backfill, or deployment is included. No live provider test was run.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository inspection, code
editing, and command execution. The exact model revision and context
window were not reported by the runtime.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Nicky LeachandPaperclip authored and GitHub committed 2026-10-09 11:15:24 -07:00
1 parent 556daf9a99
commit 5badea4798
15 files changed
+388 -19

No files matched your search

+57
View File
@@ -57,6 +57,63 @@ the visible operation finishes. Its timeout and terminal cleanup paths need
stronger completion receipts before that restriction can be relaxed. Restarting
does not bypass the separate persisted lease and recovery checks.
## Required agent lifecycle work
Use `onAgentLifecycle` for resources that must be ready before an agent works or
removed before termination completes. Declare both `agentLifecycle: true` and
`agents.lifecycle.manage` in the manifest. The host calls the plugin after the
lifecycle command commits. The lifecycle module owns admission, progress, and
retries; a plugin does not need a readiness data handler or a polling job.
```ts
import { definePlugin } from "@paperclipai/plugin-sdk";
import { reconcileResources } from "./resources.js";
export default definePlugin({
async setup() {},
async onAgentLifecycle(request) {
// Provider code must persist revision fences and tolerate repeated calls.
const complete = await reconcileResources(request);
return {
operationId: request.operationId,
version: request.version,
status: complete ? "complete" : "pending",
};
},
});
```
`reconcileResources` above is plugin-owned provider code, not an SDK function.
Use `companyId` and `agentId` from the request to scope resources.
Request `agents.read` separately if the handler needs `ctx.agents.get`.
Lifecycle authority does not grant agent configuration writes or secret access.
| Phase | Resource action |
| --- | --- |
| `preparing` | Create or reconcile the agent's resources and wait for readiness. |
| `verifying` | Complete any remaining checks after the host tests the saved harness. |
| `pausing` | Stop resources after the host confirms execution has stopped. |
| `resuming` | Start resources and wait for readiness. |
| `terminating` | Stop resources after the host revokes keys and confirms execution has stopped. |
| `cleaning_up` | Remove owned resources and wait until removal completes. |
Return `pending` while an external operation continues. Return `complete` only
when its effect is confirmed; accepting a deletion request is not completion.
Throw on failure. The host records a fixed error without provider error text.
The agent page shows the phase and offers Retry for failed work.
Calls can repeat, including after a timeout or restart. Retain the highest
version per company and agent, reject older requests, and serialize external
effects or clean up late results. The host's revision fence protects its state;
it cannot cancel a provider request that has already left the process.
The host saves required plugin IDs at first selection. Company-disabled plugins
are excluded then. Disabling or removing a selected plugin blocks progress;
restore it to complete the step. Later installations do not backfill existing
agents. The durable event inbox below is separate: acknowledging an event does
not complete required lifecycle work. See [Agent lifecycle](../AGENT-LIFECYCLE.md)
for phase ordering, recovery, and existing-installation limits.
## Durable resource lifecycle inbox
Plugins with `events.subscribe` can read durable, company-scoped resource hooks