@poppykit/client 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +12 -3
- package/AUTH.md +97 -65
- package/CHANGELOG.md +18 -0
- package/CONFORMANCE.md +73 -0
- package/CONTRIBUTING.md +13 -10
- package/DELIVERY.md +30 -1
- package/INTEGRATIONS.md +63 -21
- package/OPERATIONS.md +120 -0
- package/PROVIDER.md +24 -14
- package/README.md +7 -13
- package/RELEASING.md +2 -0
- package/SECURITY.md +2 -0
- package/STORAGE.md +24 -5
- package/dist/adapter-contract.d.ts +6 -0
- package/dist/adapter-contract.js +31 -1
- package/dist/auth/challenge.d.ts +2 -0
- package/dist/auth/challenge.js +8 -8
- package/dist/auth/client-auth.d.ts +29 -13
- package/dist/auth/client-auth.js +498 -318
- package/dist/auth/sqlite-store.js +27 -4
- package/dist/auth/store.d.ts +6 -1
- package/dist/conversation.d.ts +6 -0
- package/dist/conversation.js +28 -14
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3 -1
- package/dist/integrations.d.ts +16 -4
- package/dist/integrations.js +30 -38
- package/dist/network/discovery.d.ts +51 -22
- package/dist/network/discovery.js +159 -69
- package/dist/network/redirect.d.ts +3 -0
- package/dist/network/redirect.js +38 -0
- package/dist/network/tasks.d.ts +23 -0
- package/dist/network/tasks.js +45 -0
- package/dist/operations.d.ts +103 -0
- package/dist/operations.js +261 -0
- package/dist/postgres.d.ts +11 -0
- package/dist/postgres.js +52 -2
- package/dist/protocol/operations.d.ts +51 -0
- package/dist/protocol/operations.js +98 -0
- package/dist/protocol/session.d.ts +28 -0
- package/dist/protocol/session.js +93 -0
- package/dist/protocol/sse.d.ts +1 -0
- package/dist/protocol/sse.js +1 -1
- package/dist/protocol/wire.d.ts +20 -15
- package/dist/protocol/wire.js +70 -22
- package/dist/provider-postgres.js +3 -3
- package/dist/provider-storage.js +4 -4
- package/dist/provider.d.ts +26 -10
- package/dist/provider.js +103 -32
- package/dist/sqlite.d.ts +12 -0
- package/dist/sqlite.js +42 -0
- package/examples/operations.mjs +25 -0
- package/examples/quickstart.mjs +2 -1
- package/package.json +5 -2
package/API.md
CHANGED
|
@@ -5,9 +5,10 @@ for complete option/result types. This map describes the supported integration s
|
|
|
5
5
|
|
|
6
6
|
| Entry point | Main exports | Guide |
|
|
7
7
|
| --- | --- | --- |
|
|
8
|
-
| `@poppykit/client` | `createMetadataResolver`, `createNodeTransport` | [README](README.md), [delivery](DELIVERY.md) |
|
|
8
|
+
| `@poppykit/client` | `createMetadataResolver`, `signInMethods`, `createNodeTransport` | [README](README.md), [delivery](DELIVERY.md) |
|
|
9
9
|
| `@poppykit/client` | `createClientAuth`, `AuthFailure`, authentication/identity/input types | [authentication](AUTH.md) |
|
|
10
10
|
| `@poppykit/client` | `createConversationClient`, `ConversationError`, `parseEventStream` | [delivery](DELIVERY.md) |
|
|
11
|
+
| `@poppykit/client` | `Author`, `AuthorType`, `Message`, `RecordedMessage`, conversation/event types | [delivery](DELIVERY.md) |
|
|
11
12
|
| `@poppykit/client` | `ApiTarget`, `AgentBrowserController`, `FeedbackReport` types | [integrations](INTEGRATIONS.md) |
|
|
12
13
|
| `@poppykit/client/sqlite` | `SqliteAuthStore`, `SqliteConversationStore` | [storage](STORAGE.md) |
|
|
13
14
|
| `@poppykit/client/memory` | `MemoryConversationStore` (non-durable) | [storage](STORAGE.md) |
|
|
@@ -17,7 +18,7 @@ for complete option/result types. This map describes the supported integration s
|
|
|
17
18
|
ClientAuth methods include `start/get/list/target`, `authorization`,
|
|
18
19
|
`beginSignIn/continueSignIn/completeSignIn/cancelSignIn/operation`, `renew`,
|
|
19
20
|
`signOut/retryCleanup/recover`, `authorizedFetch`, `apiTarget/apiFetch`,
|
|
20
|
-
`joinBrowser` and `
|
|
21
|
+
`joinBrowser`, `feedback` and `excludedTasks`. Conversation operations bind one connection, issuer
|
|
21
22
|
and endpoint; never substitute profile names or model-provided URLs for that binding.
|
|
22
23
|
|
|
23
24
|
The [quickstart](examples/quickstart.mjs) is executed by CI. Integration examples
|
|
@@ -28,7 +29,7 @@ The optional Postgres entry point ships its adapters, `createPostgresPool`,
|
|
|
28
29
|
`postgresMigration`, `migratePostgres` and schema-version export; see [storage](STORAGE.md).
|
|
29
30
|
TypeScript Postgres consumers also install `@types/pg@8.23.1` as a development dependency.
|
|
30
31
|
|
|
31
|
-
Custom `AuthStore` implementations must honor `AuthSelection.history` as described
|
|
32
|
+
Custom `AuthStore` implementations must honor shared-grant/account aggregation and `AuthSelection.history` as described
|
|
32
33
|
in [STORAGE.md](STORAGE.md). Provider callbacks now acknowledge identical consumed
|
|
33
34
|
results without making `takeResult` reusable; see [PROVIDER.md](PROVIDER.md).
|
|
34
35
|
|
|
@@ -36,3 +37,11 @@ results without making `takeResult` reusable; see [PROVIDER.md](PROVIDER.md).
|
|
|
36
37
|
`PostgresConnection` type are exported only by `@poppykit/client/provider/postgres`.
|
|
37
38
|
They explicitly enlist provider SQL in a host-owned guarded transaction; the host
|
|
38
39
|
retains commit/rollback, deadline and privilege responsibility.
|
|
40
|
+
|
|
41
|
+
## Business Operations v2
|
|
42
|
+
|
|
43
|
+
See [OPERATIONS.md](OPERATIONS.md) for the complete consent/effect contract. Root exports include `BusinessOperation`, `OperationApproval`, `OperationRequest`, `operationsV2`, `supportsOperations`, `parseBusinessOperation`, `parseOperationApproval`, `parseApprovalHeader`, `approvalHeader`, `mcpApproval`, `approvalMetaKey`, `captureApiOperationRequest`, `operationExpiry`, `operationRequiredResponse` and `operationToolError`.
|
|
44
|
+
|
|
45
|
+
Client exports `createOperationClient`, `createOperationSender`, `OperationClientError`, the three approved-channel serializers, `BusinessClientContext`, `BusinessAttempt`, `BusinessAttemptStore`, `TrustedOperationConsent` and `OperationDelivery`. `ClientAuth.businessContext` supplies a verified binding. SQLite/PostgreSQL subpaths export `SqliteBusinessAttemptStore`/`PostgresBusinessAttemptStore`. The adapter-contract subpath exports `runBusinessAttemptAdapterContract`. Conversation options accept verified `operations:{client,company}` metadata.
|
|
46
|
+
|
|
47
|
+
For business-operation sends, pass the saved `BusinessClientContext` as the final `expectedContext` argument to `apiFetch`/`authorizedFetch`. Channel sender callbacks receive that context. A changed Company/client/User/account or signed-out Session fails before dispatch; target discovery does not authorize a replacement account. See [Operations](OPERATIONS.md).
|
package/AUTH.md
CHANGED
|
@@ -1,67 +1,99 @@
|
|
|
1
1
|
# Authentication lifecycle
|
|
2
2
|
|
|
3
|
-
`createClientAuth` owns
|
|
4
|
-
|
|
5
|
-
startup.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
or
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
does not
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
3
|
+
`createClientAuth` owns discovery, local Sessions, Direct/Device/Mediated OAuth,
|
|
4
|
+
refresh rotation, revocation and abandoned-operation recovery. Call `recover()`
|
|
5
|
+
on startup. A dispatched exchange with an ambiguous result is never replayed.
|
|
6
|
+
|
|
7
|
+
`start(domain)` creates a random Session ID and a pairwise User ID for the verified
|
|
8
|
+
Company identifier: its issuer, or `https://{organization.domain}` when issuerless.
|
|
9
|
+
The User ID is stable across Sessions. No token request is needed to start or use
|
|
10
|
+
a signed-out Session. Changing the Company identifier never transfers its identity,
|
|
11
|
+
grants or pending work. Domain discovery errors do not establish an issuerless Company.
|
|
12
|
+
|
|
13
|
+
Every protected request gets a fresh ES256 `Poppy-Session` JWT, bound to its final
|
|
14
|
+
method, URL without query/fragment, Company identifier and Session/User identity.
|
|
15
|
+
A private delegated key and root-signed `pap-delegation+jwt` vouch for the request;
|
|
16
|
+
the root key does not sign Session JWTs directly. Delegated keys are private to one
|
|
17
|
+
User ID at one Company. A token-bearing request also binds the token with `ath`.
|
|
18
|
+
OAuth endpoints receive client assertions and optional DPoP, without a Session JWT.
|
|
19
|
+
|
|
20
|
+
OAuth account grants are independent of Sessions. To reuse one explicitly:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const other = await auth.start(domain, { accountFrom: signedInConnectionId });
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The source must belong to the same local user/client namespace and Company, be
|
|
27
|
+
regular rather than anonymous, and have an active grant. The new Session has its
|
|
28
|
+
own sid and signing keys. Refresh rotation is serialized on the shared grant;
|
|
29
|
+
ambiguous rotation stops every Session using it. A definite `invalid_grant` clears
|
|
30
|
+
the shared grant and continues those Sessions signed out.
|
|
31
|
+
|
|
32
|
+
Use `authorization(id, scopes)` for current granted/missing scopes and eligible
|
|
33
|
+
methods. Scopes may be documented outside `scopes_supported`; hints and descriptions
|
|
34
|
+
do not authorize anything. Direct requires a registered callback, high-entropy
|
|
35
|
+
browser binding, PKCE and exact callback `iss`. HTTPS callbacks stay on the client
|
|
36
|
+
metadata domain; RFC 8252 loopback IP callbacks permit only the port to vary.
|
|
37
|
+
The `dpop_jkt` is an OAuth proof-key binding, not the Poppy Session identifier.
|
|
38
|
+
|
|
39
|
+
Direct and Device are for the human's own sign-in and consent. Show the checked
|
|
40
|
+
Device link and code; keep the private device code out of model context. Never
|
|
41
|
+
complete the approval page for the User. Prefer Direct where viable, then Device.
|
|
42
|
+
Mediated v1 is transitional: the `mediated-sign-in` extension declares trusted
|
|
43
|
+
credential fields/scopes, and the issuer's token endpoint accepts
|
|
44
|
+
`https://personalagentprotocol.org/grant/mediated-sign-in` as a form grant.
|
|
45
|
+
Continuation sends `sign_in_id` and `one_time_code`, without original credentials
|
|
46
|
+
or scope. `TrustedInput` runs outside model tools and must not retain/log values.
|
|
47
|
+
The SDK clears supplied credential/code objects on exit and persists neither.
|
|
48
|
+
|
|
49
|
+
Successful fresh sign-in activates a new sid, preventing account mixing even after
|
|
50
|
+
sign-out. Failed replacement preserves the previous sid and grant. Partial grants
|
|
51
|
+
report granted/missing scopes. Replacement preserves old required scopes unless
|
|
52
|
+
`requiredExistingScopes` explicitly narrows that requirement; grants are not unioned.
|
|
53
|
+
An old refresh grant is retired when no other local Session still references it.
|
|
54
|
+
Accepting a newer grant does not expand a browser cookie's approved scopes.
|
|
55
|
+
|
|
56
|
+
Access requests use the response's Bearer or DPoP scheme. Missing `expires_in` uses
|
|
57
|
+
a conservative local 60-second lifetime. Missing rotated `refresh_token` retains
|
|
58
|
+
the existing one. Resource metadata selects narrower API/conversation/MCP tokens;
|
|
59
|
+
see [INTEGRATIONS.md](INTEGRATIONS.md). A repeated `invalid_session` restarts once
|
|
60
|
+
with a new sid and fresh signatures, then stops. Feedback replaces a definitely
|
|
61
|
+
ended sid but returns the failed POST without replay; an explicit later report
|
|
62
|
+
uses the new Session. Browser authorization can restart once before one code-only
|
|
63
|
+
browser submission. Session restart does not replay
|
|
64
|
+
an uncertain OAuth exchange or discard an otherwise valid account grant.
|
|
65
|
+
|
|
66
|
+
`signOut(id)` revokes every locally held refresh grant for that local User and
|
|
67
|
+
Company, clears their access credentials and continues regular Sessions signed out.
|
|
68
|
+
OAuth does not reveal a standard opaque account identifier, so this conservative
|
|
69
|
+
policy also signs out other accounts for that User at the same Company. Cleanup
|
|
70
|
+
references survive remote/vault failures; call `retryCleanup(id)`. A pending or
|
|
71
|
+
uncertain outcome is never reported as complete. Re-sign-in requires a User request.
|
|
72
|
+
|
|
73
|
+
`start(domain, { anonymous: true })` gets a fresh User ID, sid and private keys.
|
|
74
|
+
It never carries an account token. Sign-in ends it and starts a regular connection;
|
|
75
|
+
use the returned connection ID. Ending anonymous browsing deletes its local private
|
|
76
|
+
identity/key material, with durable cleanup references if deletion fails.
|
|
77
|
+
|
|
78
|
+
`AssertionProvider.assert` supplies constrained client authentication;
|
|
79
|
+
`delegate({operationId,jwk})` supplies a delegation for a locally generated public
|
|
80
|
+
key. `ProviderIdentity` supplies the authenticated Company-specific identity.
|
|
81
|
+
The optional bundled provider uses separate enrolled client identities; see
|
|
82
|
+
[PROVIDER.md](PROVIDER.md). A provider-only configuration cannot substitute its
|
|
83
|
+
regular identity for anonymous browsing. ES256 is supported directly; a host using
|
|
84
|
+
blind PS384 root signatures supplies its own assertion/delegation adapter and
|
|
85
|
+
protects cross-user isolation. Blind signing is not implemented by the bundled provider.
|
|
86
|
+
|
|
87
|
+
Private keys, refresh tokens, device codes and PKCE belong in the host SecretStore;
|
|
88
|
+
access tokens remain in process memory. The store and vault are separate commit
|
|
89
|
+
boundaries: sweep orphan staged secrets only after ownership checks and a grace
|
|
90
|
+
period. Preserve uncertain requests and cleanup references. See [STORAGE.md](STORAGE.md)
|
|
91
|
+
for record upgrades and [DELIVERY.md](DELIVERY.md) for operator-signature integration.
|
|
92
|
+
|
|
93
|
+
Delegations use coarse half-hour issuance buckets with a one-hour signed lifetime,
|
|
94
|
+
leaving 30–60 minutes at creation. Host providers may return a different valid
|
|
95
|
+
lifetime within the client's one-hour freshness bound. See [CONFORMANCE.md](CONFORMANCE.md).
|
|
96
|
+
|
|
97
|
+
Authentication lifecycle OperationRecord/operationId values never approve business operations. OAuth access consent, provider enrollment and mediated credential collection cannot supply business consent. [Operations v2](OPERATIONS.md) binds each actual user answer to the exact revision and request, and rechecks current Session/account authority for execution and protected replay.
|
|
98
|
+
|
|
99
|
+
For business-operation sends, pass the saved `BusinessClientContext` as the final `expectedContext` argument to `apiFetch`/`authorizedFetch`. Channel sender callbacks receive that context. A changed Company/client/User/account or signed-out Session fails before dispatch; target discovery does not authorize a replacement account. See [Operations](OPERATIONS.md).
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
Local migration block2 adds Operations v2 codecs, negotiated API/MCP/conversation mechanics and durable trusted-consent attempt storage/recovery. See [OPERATIONS.md](OPERATIONS.md). This is a local SDK checkpoint, not full CLI integration or package publication.
|
|
4
|
+
|
|
5
|
+
## 0.6.0 — current-spec core migration
|
|
6
|
+
|
|
7
|
+
Separate delegated resource Sessions from OAuth account grants; discover issuers and
|
|
8
|
+
service resources independently; support issuerless Companies, Bearer/DPoP tokens,
|
|
9
|
+
current mediated grants, code-only browser joining, current errors and feedback.
|
|
10
|
+
Client auth records upgrade to v3; reference issuer records upgrade to v5 without
|
|
11
|
+
resetting credentials or pending/recovery records. Stop old writers before opening
|
|
12
|
+
upgraded stores. Retired public APIs are removed; see [AUTH.md](AUTH.md) and
|
|
13
|
+
[STORAGE.md](STORAGE.md). Breaking local migration candidate; publication requires the recorded release gates.
|
|
14
|
+
|
|
15
|
+
## 0.5.0 — 2026-10-28
|
|
16
|
+
|
|
17
|
+
Historical snapshot of Personal Agent Protocol draft 0.3; the current live contract supersedes this entry. Break discovery, Session Key/token, author, browser-controller and feedback contracts; retain explicit storage subpaths. Upgrade normalized auth state 1→2; stop old writers and reset incompatible conversation/outbox state before cutover.
|
|
18
|
+
|
|
19
|
+
Local migration candidate only. Publication and deployment require separate approval.
|
|
20
|
+
|
|
3
21
|
## 0.4.1
|
|
4
22
|
|
|
5
23
|
Support Node 24.21.0 and later within Node 24 under the owner-accepted runtime policy.
|
package/CONFORMANCE.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Current protocol contract
|
|
2
|
+
|
|
3
|
+
The normative source is the [live draft 0.3 specification](https://personalagentprotocol.org/docs/0.3/spec)
|
|
4
|
+
and [Mediated Sign-In v1](https://personalagentprotocol.org/docs/0.3/extensions/mediated-sign-in).
|
|
5
|
+
The numbered URL is not a content pin. On 2026-10-31 the six migration sources were
|
|
6
|
+
re-fetched and matched their initial snapshots byte for byte. SHA-256 identities:
|
|
7
|
+
|
|
8
|
+
- Core: 0c8586c0477f523abe39b47e5a20232fc8128c24bf17bb78fff69133f97f9bb9
|
|
9
|
+
- Mediated v1: 68074a6178bf4c78f09b2a88313a4067fa9e4143a338f205a63d2d884ca09f94
|
|
10
|
+
- Guides: fe10657eca0b464c19cc198f3876d30f5055d8a5cb423cd147affe805206ecdd
|
|
11
|
+
|
|
12
|
+
The workspace requirement matrix and exact commands, tree hashes and focused test
|
|
13
|
+
receipts live in jobs/protocol-migration. Focused checks do not certify an installed
|
|
14
|
+
release or any deployed service. Operations v2 SDK mechanics are implemented with opt-in host contracts;
|
|
15
|
+
full candidate, native and cross-package release gates remain separate.
|
|
16
|
+
|
|
17
|
+
## Host contracts and recommendations
|
|
18
|
+
|
|
19
|
+
The [guides](https://personalagentprotocol.org/docs/0.3/guides) use SHOULD for each
|
|
20
|
+
recommendation, including items without uppercase words. The SDK supplies protocol
|
|
21
|
+
mechanics; hosts retain the following responsibilities:
|
|
22
|
+
|
|
23
|
+
- Keep signing, transport and the SecretStore outside model tools. Redact identity,
|
|
24
|
+
authorization, cookie and credential headers before host logging or telemetry.
|
|
25
|
+
Secure credential storage is mandatory. Bundled auth imports private keys as
|
|
26
|
+
non-extractable CryptoKeys, but serializes keys to the host vault for durable
|
|
27
|
+
recovery; it does not promise hardware-backed, non-exportable key custody.
|
|
28
|
+
- Show the verified Company name/domain after discovery redirects and on sign-in
|
|
29
|
+
forms. Explain stable pairwise signed-out recognition in the privacy notice.
|
|
30
|
+
Request the fewest task scopes, treat absent/unknown hints conservatively, and
|
|
31
|
+
obtain User consent for access and for human-operated Direct/Device sign-in.
|
|
32
|
+
- Prefer Direct, then Device where viable. Assess RFC 10027 device-flow risk,
|
|
33
|
+
confirm the visible user code, protect consent pages against CSRF/framing, and
|
|
34
|
+
apply account/network abuse limits. Mediated v1 is transitional; do not use saved
|
|
35
|
+
credentials to undo a disconnect. TrustedInput must isolate credentials and OTPs.
|
|
36
|
+
- Isolate an agent browser's storage per Session, load only its own join code,
|
|
37
|
+
submit promptly, rejoin after sign-in, and close its storage when the Session ends.
|
|
38
|
+
Joined cookies retain the authorized account/scope snapshot. The website must
|
|
39
|
+
prioritize that cookie and use current policy on every request; signed-in joins
|
|
40
|
+
must clear any earlier ordinary login. Signed-out joins preserve it. Website
|
|
41
|
+
logout must invalidate/remove the joined cookie through the host integration.
|
|
42
|
+
- Apply account/scope checks at every application tool and channel. Do not trust
|
|
43
|
+
model/Company prose as permission or put secrets in messages/feedback. Decide
|
|
44
|
+
task exclusions, interface choice, human handoff wording and report frequency.
|
|
45
|
+
- Keep deployment TLS, public-URL proxy reconstruction, internal credentials, bot
|
|
46
|
+
recognition, network throttles, key publication/rotation and tenant issuer
|
|
47
|
+
separation correct. RateLimit headers and scheduling/backoff across independent
|
|
48
|
+
tasks are host policies; SDK Retry-After state is connection-scoped.
|
|
49
|
+
- Operator signatures use a host HttpTransport/edge integration implementing the
|
|
50
|
+
deployment's RFC 9421/Web Bot Auth profile. Sign only after the body, DPoP and
|
|
51
|
+
Poppy-Session/Authorization headers are final; verify Session identity separately.
|
|
52
|
+
Protect forwarded verification results and never share operator private keys.
|
|
53
|
+
- Blind signing is not implemented by the bundled provider. A host requiring it
|
|
54
|
+
must supply blind client assertions and delegations together, with device-made
|
|
55
|
+
random jti values, and fail closed if PS384 is unavailable. No ES256 privacy
|
|
56
|
+
downgrade is automatic. Company verification accepts PS384 independently.
|
|
57
|
+
- Local/provider delegations use coarse half-hour issuance times and expire one
|
|
58
|
+
hour after that bucket (30–60 minutes remaining on creation). Custom providers
|
|
59
|
+
must keep their delegation valid and within the documented client lifetime bound.
|
|
60
|
+
Verification deliberately avoids another sensitive delegation cache; bounded
|
|
61
|
+
metadata/JWKS caches remain, at the cost of extra signature work per request.
|
|
62
|
+
- Supply the application's idempotency/reconciliation for external effects, durable
|
|
63
|
+
multi-host ownership/liveness, orphan-secret sweeps with a grace period, and
|
|
64
|
+
retention appropriate to its data. Conversation receipts are not business-effect
|
|
65
|
+
deduplication. Never reset active or uncertain work to make a test pass.
|
|
66
|
+
|
|
67
|
+
See [AUTH.md](AUTH.md), [STORAGE.md](STORAGE.md) and [INTEGRATIONS.md](INTEGRATIONS.md)
|
|
68
|
+
for the concrete callback/store contracts. A host integration must validate these
|
|
69
|
+
boundaries in its real browser, issuer, vault, gateway and deployment environment.
|
|
70
|
+
|
|
71
|
+
Operations source re-fetch: 2026-10-31, HTML SHA256 e69ade54433ece3fb2bd1ff6c441fecd11acc749eb6bac8b33939420d1f2b9eb, unchanged from block1. Core and guide hashes are unchanged as well. [Business Operations](OPERATIONS.md) defines trusted UI/business semantics, durable adapters and external idempotency/reconciliation obligations. Focused SQLite/PostgreSQL and process-crash evidence belongs to block2; full stable-candidate/package/platform gates remain block3. No deployed host or arbitrary network exactly-once claim is made.
|
|
72
|
+
|
|
73
|
+
Source revalidation on 2026-11-01 found all six core/current/changelog/Operations/mediated/guides pages byte-identical to the 2026-10-31 snapshots. A numbered draft URL remains mutable. Final candidate verification and external platform limits are recorded in the workspace migration STATUS; package publication is established only by its release receipt.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,30 +1,33 @@
|
|
|
1
1
|
# Contributing
|
|
2
2
|
|
|
3
|
-
Follow the live draft 0.
|
|
4
|
-
and [guides](https://personalagentprotocol.org/docs/0.
|
|
5
|
-
supporting evidence.
|
|
3
|
+
Follow the live draft 0.3 [specification](https://personalagentprotocol.org/docs/0.3/spec)
|
|
4
|
+
and [guides](https://personalagentprotocol.org/docs/0.3/guides). Tests/snapshots are
|
|
5
|
+
supporting evidence. Core protocol, Mediated Sign-In v1 and [Operations v2](OPERATIONS.md) are in scope.
|
|
6
|
+
Operations capability publication requires complete host consent/effect integration.
|
|
7
|
+
Application business workflows remain host responsibilities. Source fingerprints and
|
|
8
|
+
recommendation boundaries are in [CONFORMANCE.md](CONFORMANCE.md).
|
|
6
9
|
Code is [MIT](LICENSE); confirm rights for third-party contributions.
|
|
7
10
|
|
|
8
11
|
Use Node 24.21.0, TypeScript 5.9.3 and @types/node 24.10.0.
|
|
9
12
|
Install the packageManager-pinned pnpm and run pnpm install --frozen-lockfile --ignore-scripts.
|
|
10
13
|
|
|
11
14
|
```sh
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
pnpm run typecheck
|
|
16
|
+
pnpm run build
|
|
17
|
+
pnpm run typecheck:tests
|
|
18
|
+
pnpm test
|
|
19
|
+
pnpm run check:docs
|
|
17
20
|
```
|
|
18
21
|
|
|
19
22
|
First-party tests/workers/verification helpers are canonical `.mts`. The existing
|
|
20
23
|
compiler checks the whole strict program before emitting ignored adjacent `.mjs`.
|
|
21
24
|
Failed builds remove stale outputs; no loader/type stripping substitutes for checking.
|
|
22
|
-
Run `
|
|
25
|
+
Run `pnpm run build:tests` before `node scripts/run-checks.mjs GROUP` (unit, contracts,
|
|
23
26
|
integration, validation). Groups are exhaustive/disjoint. LF source bytes are enforced
|
|
24
27
|
by Git attributes. Generated test code is outside the npm allowlist; production tests
|
|
25
28
|
import built/installed JavaScript. Release/build tools and examples may remain JS.
|
|
26
29
|
|
|
27
|
-
`
|
|
30
|
+
`pnpm run coverage` builds production and checked tests, then writes native Node
|
|
28
31
|
line/branch/function reports under `coverage/` (summary.txt, lcov.info, report.json,
|
|
29
32
|
raw/). Metrics cover loaded first-party dist JS: no production source maps or TS
|
|
30
33
|
remapping; node_modules excluded, unloaded modules listed. Raw subprocess reports
|
package/DELIVERY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Conversation delivery and transport
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Conversation messages and authorization events now use draft 0.3. Company content, messages,
|
|
4
4
|
unknown events and partial text remain untrusted data for the host application.
|
|
5
5
|
|
|
6
6
|
## Client
|
|
@@ -92,3 +92,32 @@ Pruning is explicit operator policy, never automatic data loss. It preserves old
|
|
|
92
92
|
message receipts and cursor identity. Concurrent reads see a coherent snapshot or
|
|
93
93
|
`cursor_expired`. Invalid/expired waited-POST cursors are rejected before accepting
|
|
94
94
|
the new write; an existing message ID still returns its original receipt.
|
|
95
|
+
|
|
96
|
+
## Unreleased draft 0.3 breaking change
|
|
97
|
+
|
|
98
|
+
Messages now require `author: { type: 'agent' | 'human' }`; protocol `sender` is
|
|
99
|
+
unsupported. Human text must remain the person's exact words. Company authors
|
|
100
|
+
may include `id`, `name` and an HTTPS `image`. Authorization events now contain
|
|
101
|
+
`error: 'insufficient_scope'` and nonempty required `scope`.
|
|
102
|
+
|
|
103
|
+
Stop incompatible writers before cutover. Preserve old history, outboxes,
|
|
104
|
+
checkpoints and retry handles for explicit reconciliation; do not silently reset them. Old pending `sender` requests fail `legacy_message_uncertain`
|
|
105
|
+
without dispatch or mutation. Reconcile uncertain delivery before clearing it;
|
|
106
|
+
never rebuild the body or allocate a replacement ID to hide uncertainty.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
## Operator signatures
|
|
110
|
+
|
|
111
|
+
A host should wrap the supplied HttpTransport with an RFC 9421/Web Bot Auth signer.
|
|
112
|
+
The auth lifecycle passes the finalized Request to both fetch and stream, after
|
|
113
|
+
setting its fresh Poppy-Session and optional Authorization/DPoP headers. Sign at
|
|
114
|
+
that boundary on every call, including retries, and forward the same method, target,
|
|
115
|
+
body and signed headers without redirects. Cover poppy-session and authorization
|
|
116
|
+
when present, in addition to the Web Bot Auth required components. Apply identical
|
|
117
|
+
signing behavior to streaming calls. Never log the Request or signing material.
|
|
118
|
+
The host supplies operator keys and the standards-compliant signer; the SDK does
|
|
119
|
+
not invent a second operator-key registry or treat its signature as Session verification.
|
|
120
|
+
Operator keys and delegated Session keys have separate roles. Companies still
|
|
121
|
+
verify the Session independently, and may apply operator acceptance policy.
|
|
122
|
+
|
|
123
|
+
Business-operation retries use the operation ID and approved revision, independently of message/work IDs. A conversation acceptance is not proof the action completed. [Operations v2](OPERATIONS.md) preserves uncertain claims/attempts through timeout, process death, expiry and restart. A different operation returned on retry does not establish the earlier outcome.
|
package/INTEGRATIONS.md
CHANGED
|
@@ -19,29 +19,43 @@ const result = await fetchForClient(new Request('https://company.example/api/rec
|
|
|
19
19
|
|
|
20
20
|
OpenAPI `apiUrl` is the description URL from verified company discovery. The third
|
|
21
21
|
argument is the actual destination base; its host must independently serve verified
|
|
22
|
-
company discovery naming the same
|
|
22
|
+
company discovery naming the same verified Company identifier. This conservative policy requires API
|
|
23
23
|
hosts to publish discovery; a document's `servers` field alone grants no authority.
|
|
24
24
|
Final requests must stay within the verified origin and slash-delimited base path.
|
|
25
25
|
The proof binds the actual final method and URL. Destination changes require a new
|
|
26
26
|
explicit connection; saved work never moves silently.
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
Service authorization comes from RFC 9728 protected-resource metadata, fetched
|
|
29
|
+
without credentials before the first signed-in API/conversation request and cached
|
|
30
|
+
through the resolver. A real 404 permits the default Company token; network errors
|
|
31
|
+
and malformed metadata fail closed. A service resource other than the Company
|
|
32
|
+
domain selects a token for that exact resource via normal sign-in/refresh. Discovery
|
|
33
|
+
resource fields do not grant authority. An invalid_token challenge with
|
|
34
|
+
resource_metadata causes one bounded revalidation/refresh retry.
|
|
35
|
+
|
|
36
|
+
For MCP, use auth.apiTarget(connectionId, mcpUrl) and the same fetch closure in an
|
|
37
|
+
existing MCP HTTP client. The exact discovery URL is the resource and endpoint,
|
|
38
|
+
including any query; changed queries or child paths are refused. Its metadata must
|
|
39
|
+
name that resource and the Company's issuer when sign-in exists. ES256 DPoP
|
|
40
|
+
advertisement selects DPoP; absence selects Bearer. Account credentials use normal
|
|
41
|
+
OAuth grants, without token exchange. Requests use the token response's scheme.
|
|
42
|
+
|
|
43
|
+
The SDK does not encode JSON-RPC or manage MCP initialization/Mcp-Session-Id.
|
|
44
|
+
Poppy Session continuity is independent of earlier MCP transport lifecycles and
|
|
45
|
+
MCP 2026-07-28 per-request metadata. Signed-out issuerless APIs/MCP still get Session
|
|
46
|
+
JWTs. Redirects never forward tokens or proofs; unknown results are not retried.
|
|
39
47
|
|
|
40
48
|
## Agent-controlled browser
|
|
41
49
|
|
|
42
50
|
```ts
|
|
43
51
|
await auth.joinBrowser(connectionId, 'https://company.example/account', {
|
|
44
52
|
kind: 'agent-controlled',
|
|
53
|
+
async loadJoinForm(url) {
|
|
54
|
+
// Top-level GET in this isolated browser; verify the final URL and form action
|
|
55
|
+
// are exactly url. Read the hidden code only here, outside model context.
|
|
56
|
+
return controlledBrowser.loadJoinForm(url);
|
|
57
|
+
},
|
|
58
|
+
navigate: url => controlledBrowser.navigateWithoutPostBody(url),
|
|
45
59
|
async postForm({ url, fields, validateRedirect }) {
|
|
46
60
|
// Bind your existing browser controller here. It must enforce this contract:
|
|
47
61
|
// isolated per-session storage; verified TLS/actual peer addresses; no logging;
|
|
@@ -56,32 +70,60 @@ await auth.joinBrowser(connectionId, 'https://company.example/account', {
|
|
|
56
70
|
`controlledBrowser` is the host's existing controller, not an SDK driver. This hook
|
|
57
71
|
is trusted code. Its network layer must reject loopback/private/link-local actual
|
|
58
72
|
connected addresses unless narrowly configured, validate TLS and enforce redirect
|
|
59
|
-
interception. Never replay the
|
|
60
|
-
validator permits only 303 to
|
|
73
|
+
interception. Never replay the code on 307/308 or send it in a URL. The supplied
|
|
74
|
+
validator permits only 303 to an HTTPS company page. After that redirect, the SDK
|
|
75
|
+
asks the controller to navigate to the validated `returnTo`. HttpTransport cannot
|
|
61
76
|
enforce network policy inside an unrelated browser engine. Keep this capability
|
|
62
77
|
separate from the human consent-browser opener. Never join the user's browser.
|
|
63
78
|
Use separate cookies/local storage for each session/user, join just before browsing,
|
|
64
79
|
rejoin on lost recognition and close the context when the session ends. Custom
|
|
65
|
-
controllers own those guarantees
|
|
66
|
-
|
|
80
|
+
controllers own those guarantees. The SDK separately authorizes the code at the
|
|
81
|
+
endpoint path plus /authorize, preserving its query, with a signed Session request
|
|
82
|
+
and current account token when signed in. It requires 204 before the browser
|
|
83
|
+
submits only code. No JWT/token/proof enters the browser form. The cookie snapshots
|
|
84
|
+
the approved account/scopes; later sign-in or scope expansion requires rejoin.
|
|
67
85
|
|
|
68
86
|
## Feedback
|
|
69
87
|
|
|
70
88
|
```ts
|
|
71
89
|
await auth.feedback(connectionId, {
|
|
72
|
-
|
|
90
|
+
task: 'Export records',
|
|
73
91
|
description: 'There is no export operation in the API, conversation or website.',
|
|
74
92
|
});
|
|
75
93
|
```
|
|
76
94
|
|
|
77
95
|
Signed-out sessions are allowed and no account scope is required. Task and
|
|
78
|
-
description are nonempty strings of at most 200 and 2000 Unicode codepoints.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
96
|
+
description are nonempty strings of at most 200 and 2000 Unicode codepoints. Omit identifiers, personal data and secrets;
|
|
97
|
+
report task limitations rather than individual errors. Success is an empty 202
|
|
98
|
+
(returning undefined) or HTTP 200 with an `Alternative`. The SDK validates the latter
|
|
99
|
+
and returns instructions/references as company data, never as user consent. Unlisted
|
|
100
|
+
OpenAPI/MCP references and unknown reference types are ignored; known malformed
|
|
101
|
+
references fail. Web links must be HTTPS. No alternative is executed automatically.
|
|
102
|
+
Feedback does not automatically repeat a POST after nonce/auth/Session rejection. A lost
|
|
82
103
|
response can mean accepted; callers decide whether to repeat because the protocol
|
|
83
104
|
defines no deduplication key. Prefer one report per unfulfilled task.
|
|
84
105
|
|
|
85
106
|
The executable generic caller/controller fixtures are `tests/integrations.test.mts`.
|
|
86
107
|
They exercise the same hooks used by the CLI; installed CLI integration is
|
|
87
108
|
verified separately against immutable candidate tarballs.
|
|
109
|
+
|
|
110
|
+
## Task metadata
|
|
111
|
+
|
|
112
|
+
Call `auth.excludedTasks(connectionId)` before choosing how to perform a task.
|
|
113
|
+
It uses `MetadataResolver.resolveExcludedTasks(document)` (optional for custom
|
|
114
|
+
resolvers), fetches the advertised document without credentials and shares the
|
|
115
|
+
resolver's bounded HTTP cache/revalidation. Alternatives use the feedback validator;
|
|
116
|
+
cached references are checked against current discovery on every read.
|
|
117
|
+
|
|
118
|
+
Discovery exposes `auth.scope_hints`: both fields are required, unknown changes
|
|
119
|
+
become irreversible, and unknown fields are ignored. Missing hints remain unknown.
|
|
120
|
+
Hints and excluded task phrases are company claims, never user approval. The host
|
|
121
|
+
agent decides whether a phrase matches its task. The SDK/CLI intentionally omit the
|
|
122
|
+
SHOULD-level automatic pre-task check: they have no task planner; callers choose
|
|
123
|
+
when to read the metadata. No task-policy engine or alternative executor is supplied.
|
|
124
|
+
|
|
125
|
+
## Operations v2
|
|
126
|
+
|
|
127
|
+
[OPERATIONS.md](OPERATIONS.md) defines API403/Poppy-Approval, MCP structuredContent/_meta and conversation operation/approval integration. Publish support only after all interfaces in use have trusted consent and durable effect recovery. Ordinary MCP clients retain their OAuth flow; one unsupported tool returns a tool error. No v1 extension endpoint is added.
|
|
128
|
+
|
|
129
|
+
For business-operation sends, pass the saved `BusinessClientContext` as the final `expectedContext` argument to `apiFetch`/`authorizedFetch`. Channel sender callbacks receive that context. A changed Company/client/User/account or signed-out Session fails before dispatch; target discovery does not authorize a replacement account. See [Operations](OPERATIONS.md).
|