@weaccelerateinc/agent-sdk 0.0.0-stage → 0.11.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/README.md +151 -2
- package/approved-extension-releases/0.0.0.1.json +41 -0
- package/approved-extension-releases/0.0.0.2.json +34 -0
- package/approved-extension.d.ts +24 -0
- package/approved-extension.js +90 -0
- package/browser-connector.d.ts +109 -0
- package/browser-connector.js +218 -0
- package/browser-operations.d.ts +56 -0
- package/browser-operations.js +144 -0
- package/browser-use-host.d.ts +46 -0
- package/browser-use-host.js +149 -0
- package/checkout-diagnostics.d.ts +34 -0
- package/checkout-diagnostics.js +81 -0
- package/cli.js +105 -0
- package/customer.env.example +6 -0
- package/host-adapter.d.ts +90 -0
- package/host-adapter.js +357 -0
- package/index.d.ts +253 -0
- package/index.js +504 -0
- package/linq-messaging.d.ts +45 -0
- package/linq-messaging.js +123 -0
- package/managed-mcp.d.ts +156 -0
- package/managed-mcp.js +427 -0
- package/package.json +114 -4
- package/partner-starter/.env.example +13 -0
- package/partner-starter/README.md +61 -0
- package/partner-starter/gitignore.template +5 -0
- package/partner-starter/package.json +21 -0
- package/partner-starter/runtime.mjs +33 -0
- package/partner-starter/server.mjs +13 -0
- package/partner-starter/smoke.mjs +61 -0
- package/partner-starter/synthetic.mjs +256 -0
- package/partner-starter/tests/starter.test.mjs +119 -0
- package/partner-starter/trusted-context.example.mjs +23 -0
- package/partner.d.ts +27 -0
- package/partner.js +53 -0
- package/postgres-host.d.ts +14 -0
- package/postgres-host.js +102 -0
- package/private-chat.d.ts +104 -0
- package/private-chat.js +316 -0
- package/private-ingress.d.ts +46 -0
- package/private-ingress.js +132 -0
- package/starter/.env.example +19 -0
- package/starter/README.md +82 -0
- package/starter/gitignore.template +5 -0
- package/starter/package.json +21 -0
- package/starter/runtime.mjs +66 -0
- package/starter/server.mjs +13 -0
- package/starter/smoke.mjs +55 -0
- package/starter/synthetic.mjs +163 -0
- package/starter/tests/starter.test.mjs +224 -0
- package/starter/trusted-context.example.mjs +49 -0
package/README.md
CHANGED
|
@@ -1,3 +1,152 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @weaccelerateinc/agent-sdk 0.11.0
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Version 0.11.0 is a breaking release: **Accelerate never sends the final payment click.** A purchase arms one short click, the customer's agent performs it once through its own browser connection (`BrowserConnectorHost.performFinalClick`) in the customer's existing browser, then Accelerate observes the outcome. Purchase requires `final_click_actor:'customer'` and that host; the managed Kernel browser is fill-only. `client.checkouts.submit` rejects locally with `customer_click_required`. See [customer-owned final click](../../docs/customer-final-click.md). Release 0.10.3 keeps its existing behavior. Merchant layouts remain limited to the qualified sandbox page; this SDK release does not by itself qualify any merchant layout, a service deployment or production payments.
|
|
4
|
+
|
|
5
|
+
Server-only ESM SDK with TypeScript declarations, Node 22+, zero runtime dependencies. New partners use their existing browser and explicitly select `managed_review_v2`, which continues an armed fill immediately after private CVV verification. Existing callers keep `managed_review_v1` unless they opt in. Public npm releases use `@weaccelerateinc/agent-sdk`; install an exact available version with `npm install @weaccelerateinc/agent-sdk@0.11.0`. Check `npm view @weaccelerateinc/agent-sdk@0.11.0 version` to confirm registry availability. The package remains `UNLICENSED`.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install --ignore-scripts @weaccelerateinc/agent-sdk@0.11.0
|
|
9
|
+
npx --no-install accelerate-agent init ./partner-demo --browser existing
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The platform creates the browser and installs the approved extension using its existing setup. Accelerate attaches it only at payment. SDK checks fail before private collection if ownership, recording, approved runtime, lifetime, drain/fencing or qualification is unsupported. No provider replacement fallback is created.
|
|
13
|
+
|
|
14
|
+
| Export | Purpose |
|
|
15
|
+
| --- | --- |
|
|
16
|
+
| `@weaccelerateinc/agent-sdk` | `AccelerateClient`, safe errors/status/REST checkout methods. |
|
|
17
|
+
| `/partner` | `registerPartnerCheckoutTools`: composes common connector, durable host, private ingress and canonical MCP tools with existing-browser custody. |
|
|
18
|
+
| `/browser-connector` | Provider-independent attachment/readiness/fence/cleanup contract, generic connector and Browser Use recipe. |
|
|
19
|
+
| `/browser-use-host` | Fixed-origin bounded Browser Use v4 metadata/extension/recording/lifetime reads and stopped-resource proof, using the platform's existing document automation. No browser creation/upload. |
|
|
20
|
+
| `/approved-extension` | Validate trusted release metadata, verify ZIP checksum and live runtime compatibility. |
|
|
21
|
+
| `/approved-extension-releases/*` | Versioned capability/compatibility/qualification manifests. Candidate builds rejected for live use. |
|
|
22
|
+
| `/host-adapter` | Durable ownership, independent effect admissions, safe outbox, native review receipts and uncertainty fences. |
|
|
23
|
+
| `/postgres-host` | AES-256-GCM records with cross-process session locks; accepts your pg Pool. Explicit `initialize()` migration; no implicit database provisioning. |
|
|
24
|
+
| `/private-ingress` | Authenticated sensitive interception before AI/history and safe outbox recovery. |
|
|
25
|
+
| `/linq-messaging` | Linq/iMessage native private image+text/receipt transport and webhook authenticator using your actual signature verifier. |
|
|
26
|
+
| `/private-chat` | Private state/reply/review APIs and bounded `deliverPrivateCheckoutReview`; never register as model tools. |
|
|
27
|
+
| `/managed-mcp` | Lower-level canonical tool registration and explicit legacy/managed browser compatibility. |
|
|
28
|
+
| `/browser-operations` | Strict bounded operation schemas; attached-customer mode permits pre-fill reads only. |
|
|
29
|
+
|
|
30
|
+
The matching [partner guide](https://github.com/weaccelerateinc/accelerate/blob/main/products/agent-sdk/docs/new-customer-integration.md) and packaged `partner-starter/` show composition. Connect actual backend authentication, document controls, durable queues and private webhook before live use; examples intentionally fail without them.
|
|
31
|
+
|
|
32
|
+
Store `ACCELERATE_API_KEY` and `ACCELERATE_MERCHANT_ID` on the server. `ACCELERATE_BASE_URL` defaults to `https://accelerate-agent-sdk.vercel.app`. `accelerate-agent doctor` checks only that credential/API pair, never live downstream readiness. `new AccelerateClient({baseUrl,apiKey,merchantId,fetchImpl?})` supports custom transport; `allowLoopbackForTests:true` is local-test-only.
|
|
33
|
+
|
|
34
|
+
For v2, create declares `fill_after_verification:true` and requires the service acknowledgement before OTP. That declaration alone cannot fill. The later verified quote repeats the flag to arm one exact revision; the successful private CVV reply fills immediately under the same backend lease and returns `review_delivery_required`. Private ingress delivers and acknowledges the masked image without asking the model to call fill. The v2 MCP registry omits the fill tool; an armed checkout rejects direct manual fill with `fill_continuation_required`. Read only its safe status. Final submit remains explicit. See [managed review v2](https://github.com/weaccelerateinc/accelerate/blob/main/products/agent-sdk/docs/managed-review-v2.md).
|
|
35
|
+
|
|
36
|
+
Required sequence: shop → payment → same-browser attachment → private OTP/card → address/final amount → private CVV → fill once → private masked image/amount → five-minute decision → explicit submit → cleanup. PAN stars+last four, CVV stars, expiry visible. Compatible layouts require the matching qualified service/executor; unsupported fields or safe review stop before private collection. Provider-owner access remains trusted.
|
|
37
|
+
|
|
38
|
+
`registerPartnerCheckoutTools({profile:'managed_review_v2', ...})` selects automatic private continuation and retains its API defaults `purpose:'sandbox_fill_only',enableSubmit:false`. Pass `purpose:'purchase',enableSubmit:true` for purchase registration; it also requires a connector whose host implements `performFinalClick`. New `init --browser existing` projects from 0.11.0 select purchase mode by default; already generated apps keep their copied runtime. Its returned private ingress must run before framework logging/parsing/history/AI. A fresh private receipt/approval enables purchase; it never triggers submission. Fill-only tests cancel after delivered informational review. Private replies/browser credentials are never tool inputs/results. Recover only safe deliveries; never replay fill/submit.
|
|
39
|
+
|
|
40
|
+
Compatibility: `accelerate-agent init <dir>` without `--browser existing` keeps the original managed Kernel starter. `registerManagedCheckoutTools` still defaults to `browserMode:'accelerate_managed'`. Existing before-fill integrations explicitly use `legacy_before_fill_v1`. Low-level hosted/raw APIs keep their compatibility defaults; a package upgrade does not change custody.
|
|
41
|
+
|
|
42
|
+
## Bounded status waiting
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const selected = await accelerate.checkouts.waitFor(id, {
|
|
46
|
+
statuses: ['selection_ready'],
|
|
47
|
+
timeoutMs: 120_000,
|
|
48
|
+
}, { signal });
|
|
49
|
+
if (selected.status !== 'selection_ready') {
|
|
50
|
+
// Handle the actual state; do not advance a payment automatically.
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`waitFor` uses sequential GET requests only. Defaults: 60-second overall timeout and one-second interval. Maximum overall timeout: five minutes; interval range: 250–5000ms. It returns immediately for any requested state, `ready_for_submit` or any terminal state, even if not requested. It aborts hung requests within the overall deadline, rejects as `wait_timeout`, and never retries errors. Readiness must not be hidden behind a long polling loop.
|
|
56
|
+
|
|
57
|
+
## Browser preparation and tool schemas
|
|
58
|
+
|
|
59
|
+
Supported operations: `browser_snapshot`, `browser_text`, `browser_find`, `browser_wait_for`, `browser_act`, `navigate`.
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { browserOperationJsonSchema } from '@weaccelerateinc/agent-sdk/browser-operations';
|
|
63
|
+
// Use this framework-neutral schema as the browser-operation tool input schema.
|
|
64
|
+
|
|
65
|
+
const snapshot = await accelerate.checkouts.browser(id, {
|
|
66
|
+
operation: 'browser_snapshot',
|
|
67
|
+
arguments: { filter: 'interactive', depth: 5 },
|
|
68
|
+
});
|
|
69
|
+
await accelerate.checkouts.browser(id, {
|
|
70
|
+
operation: 'browser_act',
|
|
71
|
+
arguments: {
|
|
72
|
+
steps: [{ type: 'click', ref: 'e1_1' }], // current snapshot reference
|
|
73
|
+
successor: { filter: 'all', depth: 5 },
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
await accelerate.checkouts.browser(id, {
|
|
77
|
+
operation: 'browser_wait_for',
|
|
78
|
+
arguments: { expect: { type: 'url', contains: '/payment' }, timeout_ms: 8000 },
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Wait conditions follow their `type`: text waits require nonempty `text`; URL/title waits require nonempty `equals` or `contains`. Do not use `text` for a URL/title condition, or URL comparators for text. `exists: false` negates the match. If both URL/title comparators are provided, `equals` takes precedence. Malformed conditions are rejected locally before an HTTP request; a valid condition that fails to appear still stops the checkout under the existing browser failure policy.
|
|
83
|
+
|
|
84
|
+
The JSON schema and input normalizer share operation bounds and discriminated shapes. For framework values typed as unknown, `parseBrowserOperation(value)` returns a validated, normalized `BrowserOperation` without a TypeScript assertion. The normalizer and server remain the security authority. References have shape `eN_N`; newer snapshots replace them. Actions: click, hover, scroll_to, fill, type, key and bounded wait. Batches allow at most eight steps and action waits at most eight seconds. Arbitrary scripts, selectors in actions, screenshots, tabs, payment controls and final purchase clicks are excluded. Responses contain bounded text without browser control URLs or raw input values.
|
|
85
|
+
|
|
86
|
+
## Verify a quote before private approval
|
|
87
|
+
|
|
88
|
+
After `applyDetails`, inspect the current amount using the original total selector and ask the backend to verify its currency. `expected_currency` is an expectation to check, not evidence supplied by the agent; a dollar symbol alone does not establish USD.
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
const verified = await accelerate.checkouts.verifyQuote(id, {
|
|
92
|
+
revision: details.revision,
|
|
93
|
+
checkout_url: exactCheckoutUrl,
|
|
94
|
+
expected_amount_cents: 1299,
|
|
95
|
+
expected_currency: 'USD',
|
|
96
|
+
checkout_total_selector: originalTotalSelector,
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The bounded receipt contains only `checkout_id`, the unchanged `revision`, `verified:true` and `{checkout_origin,amount_cents,currency}` in `quote`. It requires the current selected-address details, exact checkout URL (including query and fragment) and authoritative backend currency proof. It does not arm filling, request CVV or approve a purchase. Use its verified amount and currency in the existing `requestApproval` flow, which independently inspects the original quote again. A rejected inspection is a blocker, not permission to infer currency or repeat uncertain payment work.
|
|
101
|
+
|
|
102
|
+
For `sandbox_fill_only`, retain the actual visible original payment continuation as `submit_selector`, such as Review Your Order, even when disabled. Filling never clicks it. Purchase purpose retains its final-submit readiness and separate private approval requirements.
|
|
103
|
+
|
|
104
|
+
## Inspect a failed creation without replay
|
|
105
|
+
|
|
106
|
+
`checkouts.inspectCreation({customer_id,workspace_id,conversation_id,idempotency_key,browser})` is a server-only read. Derive scope, the original key and exact Browser Use binding from authenticated ownership and the application's permanent reservation; never expose these fields as model input.
|
|
107
|
+
|
|
108
|
+
The service looks up only that tenant/key and requires the exact owner, private conversation and browser, a failed revision-zero creation before authentication, no operation lock or private effects, and independent stopped-provider/closed-lease proof. A positive receipt contains `confirmed:true`, the opaque checkout ID, revision zero, immutable purpose, the key's SHA-256 and strict retirement booleans. Missing or incomplete proof returns only `confirmed:false`. Inspection never creates, replays, verifies, fills, cancels, recovers a lock or writes checkout state. Keep the old browser permanently reserved. A separately authorized fresh request still needs a new ordinary browser and normal fresh-start checks.
|
|
109
|
+
|
|
110
|
+
## Private chat helpers
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import {
|
|
114
|
+
AcceleratePrivateChatClient,
|
|
115
|
+
renderPrivateChatState,
|
|
116
|
+
privateChatProgress,
|
|
117
|
+
} from '@weaccelerateinc/agent-sdk/private-chat';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use this entry point only in authenticated ingress **before** history, queues, analytics or model calls. `chat.start/get/reply/cancel` require the exact customer/workspace/conversation binding. `chat.reply` also requires a stable provider message ID and transient `text`; the mutable input text is cleared after the request, including failures.
|
|
121
|
+
|
|
122
|
+
`renderPrivateChatState(state,{messageId})` returns a safe prompt or `undefined`. `privateChatProgress` returns a fixed safe preparation/verification/closure event. Both revalidate safe metadata and provide a hashed delivery idempotency key. They do not verify webhooks, authorize owners or replace durable ingress/delivery deduplication. Reuse your existing authenticated webhook, ownership lock and message sender. See the [private ingress example](https://github.com/weaccelerateinc/accelerate/blob/main/products/agent-sdk/examples/private-chat-ingress.js).
|
|
123
|
+
|
|
124
|
+
## Safe results and recovery
|
|
125
|
+
|
|
126
|
+
Ordinary status includes only opaque checkout ID, state, revision, expiry, purpose/policy, optional masked wallet and quote metadata. It contains no PAN, expiry, CVV, wallet auth capability, source ID or unrestricted browser access.
|
|
127
|
+
|
|
128
|
+
Current and compatible earlier clients opt ordinary requests into `X-Accelerate-Diagnostics: v1`. When the service supports it, safe checkout `diagnostics` includes fixed `stage`, `next_actions`, opaque `support_reference`, `cleanup_status` and the exact `submit_deadline` at readiness. Cleanup `confirmed` means worker acknowledgement, not independently verified provider destruction. `AccelerateError.supportReference` correlates negotiated errors without exposing upstream text. Older ordinary response bodies are unchanged without opt-in; private-chat and hosted bodies retain their existing contract. Current clients accept older service responses without diagnostics, but the supported review profile still requires its policy echo and actual full-window proof. A compatible status response is not a profile-readiness preflight.
|
|
129
|
+
|
|
130
|
+
Every request accepts `{signal}` last. Bounds including server/cleanup headroom: create 245s; fill 235s; details/verifyQuote/approval/submit 125s; browser 85s; inspectCreation 60s; status/cancel/connection 45s. These are transport bounds, not provider-destruction guarantees.
|
|
131
|
+
|
|
132
|
+
No request automatically retries. `AccelerateError` provides fixed `code`, `status`, `message`, `nextAction`, `retrySafe:false`, `paymentConfirmed:false`; upstream bodies and credential-bearing diagnostics are excluded. `nextAction` is guidance only:
|
|
133
|
+
|
|
134
|
+
| Value | Backend action |
|
|
135
|
+
| --- | --- |
|
|
136
|
+
| `check_configuration` | Check provisioned key, merchant and service configuration |
|
|
137
|
+
| `check_request` | Correct local request shape |
|
|
138
|
+
| `complete_wallet` / `complete_verification` | Continue the existing private shopper flow |
|
|
139
|
+
| `refresh_quote` / `refresh_revision` | Inspect safe state and prepare a new valid quote/revision if allowed |
|
|
140
|
+
| `check_status` | Read the existing checkout; do not repeat payment effects |
|
|
141
|
+
| `reconcile_order` | Check the merchant independently before any replacement purchase |
|
|
142
|
+
| `stop` | Stop and investigate |
|
|
143
|
+
|
|
144
|
+
An uncertain fill/submit outcome must be reconciled; do not automatically replay or create a replacement purchase. `submit_attempted` and `outcome_unknown` never confirm an order. The SDK does not certify PCI scope or source correctness.
|
|
145
|
+
|
|
146
|
+
## Development
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
npm test --workspace @weaccelerateinc/agent-sdk
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Tests use synthetic data and local HTTP; they do not collect live credentials or make purchases.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"release_id": "accelerate-extension-0.0.0.1",
|
|
4
|
+
"status": "approved",
|
|
5
|
+
"extension_version": "0.0.0.1",
|
|
6
|
+
"artifact": {
|
|
7
|
+
"format": "zip",
|
|
8
|
+
"sha256": "65574cbb27ab344687963f72a136f2653551b57458b284e486946b27699099c8",
|
|
9
|
+
"bytes": 29397270
|
|
10
|
+
},
|
|
11
|
+
"bridge": {
|
|
12
|
+
"protocol": "accelerate_extension_bridge_v1",
|
|
13
|
+
"sha256": "1ba039eb78a5b18bfab4486c7835f1ac188b96682ec67c92bfef889c763811a6",
|
|
14
|
+
"runtime_reporting": "legacy_pinned_artifact"
|
|
15
|
+
},
|
|
16
|
+
"capabilities": {
|
|
17
|
+
"manual_card_fill": true,
|
|
18
|
+
"document_bound_single_use": true,
|
|
19
|
+
"autosave_disabled": true,
|
|
20
|
+
"automatic_submit": false,
|
|
21
|
+
"maximum_retention_ms": 480000
|
|
22
|
+
},
|
|
23
|
+
"compatibility": {
|
|
24
|
+
"sdk_versions": [
|
|
25
|
+
"0.10.0",
|
|
26
|
+
"0.10.1",
|
|
27
|
+
"0.10.2",
|
|
28
|
+
"0.10.3",
|
|
29
|
+
"0.10.4",
|
|
30
|
+
"0.11.0"
|
|
31
|
+
],
|
|
32
|
+
"executor_contract": "accelerate_manual_checkout_v1"
|
|
33
|
+
},
|
|
34
|
+
"qualifications": [
|
|
35
|
+
{
|
|
36
|
+
"provider": "browser_use",
|
|
37
|
+
"scope": "sandbox_demo_normal_fill_review_cancel",
|
|
38
|
+
"evidence": "docs/evidence/browser-use-wallet-fill-review-2026-10-27.json"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"release_id": "accelerate-extension-0.0.0.2",
|
|
4
|
+
"status": "candidate_pending_browser_qualification",
|
|
5
|
+
"extension_version": "0.0.0.2",
|
|
6
|
+
"artifact": {
|
|
7
|
+
"format": "zip",
|
|
8
|
+
"sha256": "7a7a4ab8a874f7b8e2387433534ca0c6e78df3c4465f2e4d7f7d56a128951839",
|
|
9
|
+
"bytes": 29397375
|
|
10
|
+
},
|
|
11
|
+
"bridge": {
|
|
12
|
+
"protocol": "accelerate_extension_bridge_v1",
|
|
13
|
+
"sha256": "e86412da654acaca1ed108b624a112c6c5439d9d0a0f1beb90a59c86b68ed40b"
|
|
14
|
+
},
|
|
15
|
+
"capabilities": {
|
|
16
|
+
"manual_card_fill": true,
|
|
17
|
+
"document_bound_single_use": true,
|
|
18
|
+
"autosave_disabled": true,
|
|
19
|
+
"automatic_submit": false,
|
|
20
|
+
"maximum_retention_ms": 480000
|
|
21
|
+
},
|
|
22
|
+
"compatibility": {
|
|
23
|
+
"sdk_versions": [
|
|
24
|
+
"0.10.0",
|
|
25
|
+
"0.10.1",
|
|
26
|
+
"0.10.2",
|
|
27
|
+
"0.10.3",
|
|
28
|
+
"0.10.4",
|
|
29
|
+
"0.11.0"
|
|
30
|
+
],
|
|
31
|
+
"executor_contract": "accelerate_manual_checkout_v1"
|
|
32
|
+
},
|
|
33
|
+
"qualifications": []
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ApprovedExtensionRelease {
|
|
2
|
+
schema_version: 1;
|
|
3
|
+
release_id: `accelerate-extension-${string}`;
|
|
4
|
+
status: 'approved' | 'candidate_pending_browser_qualification';
|
|
5
|
+
extension_version: string;
|
|
6
|
+
artifact: { format: 'zip'; sha256: string; bytes: number };
|
|
7
|
+
bridge: { protocol: 'accelerate_extension_bridge_v1'; sha256: string; runtime_reporting?: 'protocol_and_version' | 'legacy_pinned_artifact' };
|
|
8
|
+
capabilities: { manual_card_fill: true; document_bound_single_use: true; autosave_disabled: true; automatic_submit: false; maximum_retention_ms: 480000 };
|
|
9
|
+
compatibility: { sdk_versions: string[]; executor_contract: 'accelerate_manual_checkout_v1' };
|
|
10
|
+
qualifications: Array<{ provider: string; scope: string; evidence: string }>;
|
|
11
|
+
}
|
|
12
|
+
export interface ApprovedExtensionRuntime {
|
|
13
|
+
extension_version: string;
|
|
14
|
+
artifact_sha256: string;
|
|
15
|
+
success: true; initialized: true; wasmInitialized: true; autosaveDisabled: true;
|
|
16
|
+
boundedRetentionSupported: true; maximumRetentionMs: 480000;
|
|
17
|
+
bridge_protocol?: 'accelerate_extension_bridge_v1';
|
|
18
|
+
}
|
|
19
|
+
export class ApprovedExtensionError extends Error { readonly code: string; }
|
|
20
|
+
export function validateApprovedExtensionRelease(input: unknown, options?: { allowCandidate?: boolean }): Readonly<ApprovedExtensionRelease>;
|
|
21
|
+
export function verifyApprovedExtensionArtifact(bytes: Uint8Array, release: ApprovedExtensionRelease): Readonly<{ verified: true; extension_version: string; artifact_sha256: string }>;
|
|
22
|
+
export function verifyApprovedExtensionRuntime(runtime: ApprovedExtensionRuntime, release: ApprovedExtensionRelease, options: {
|
|
23
|
+
sdkVersion?: string; executorContract?: 'accelerate_manual_checkout_v1'; provider: string;
|
|
24
|
+
}): Readonly<{ verified: true; extension_version: string; artifact_sha256: string; bridge_protocol: 'accelerate_extension_bridge_v1'; provider: string }>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
const SDK_VERSION = '0.11.0';
|
|
4
|
+
const EXECUTOR_CONTRACT = 'accelerate_manual_checkout_v1';
|
|
5
|
+
const BRIDGE_PROTOCOL = 'accelerate_extension_bridge_v1';
|
|
6
|
+
const LEGACY_SHA256 = '65574cbb27ab344687963f72a136f2653551b57458b284e486946b27699099c8';
|
|
7
|
+
const hashPattern = /^[a-f0-9]{64}(?![\s\S])/;
|
|
8
|
+
const versionPattern = /^(?:0|[1-9]\d{0,4})(?:\.(?:0|[1-9]\d{0,4})){0,3}(?![\s\S])/;
|
|
9
|
+
const identifier = /^[a-z][a-z0-9_-]{0,79}(?![\s\S])/;
|
|
10
|
+
const reject = code => { throw new ApprovedExtensionError(code); };
|
|
11
|
+
const check = (condition, code = 'invalid_extension_release') => { if (!condition) reject(code); };
|
|
12
|
+
const object = value => value !== null && typeof value === 'object' && !Array.isArray(value) &&
|
|
13
|
+
[Object.prototype, null].includes(Object.getPrototypeOf(value));
|
|
14
|
+
function keys(value, required, optional = []) {
|
|
15
|
+
check(object(value));
|
|
16
|
+
check(required.every(key => Object.hasOwn(value, key)) &&
|
|
17
|
+
Object.keys(value).every(key => required.includes(key) || optional.includes(key)));
|
|
18
|
+
}
|
|
19
|
+
const version = value => typeof value === 'string' && versionPattern.test(value) &&
|
|
20
|
+
value.split('.').every(part => Number(part) <= 65535) && value.split('.').some(part => Number(part) > 0);
|
|
21
|
+
const freeze = value => {
|
|
22
|
+
if (value && typeof value === 'object') { Object.values(value).forEach(freeze); Object.freeze(value); }
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export class ApprovedExtensionError extends Error {
|
|
27
|
+
constructor(code) { super('The Accelerate extension could not be verified.'); this.name = 'ApprovedExtensionError'; this.code = code; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Validate operator-supplied metadata. Metadata must come from trusted backend
|
|
31
|
+
* configuration; this validates a contract and is not a publisher signature. */
|
|
32
|
+
export function validateApprovedExtensionRelease(input, { allowCandidate = false } = {}) {
|
|
33
|
+
check(typeof allowCandidate === 'boolean');
|
|
34
|
+
keys(input, ['schema_version', 'release_id', 'status', 'extension_version', 'artifact', 'bridge', 'capabilities', 'compatibility', 'qualifications']);
|
|
35
|
+
check(input.schema_version === 1 && version(input.extension_version) && input.release_id === `accelerate-extension-${input.extension_version}`);
|
|
36
|
+
check(['approved', 'candidate_pending_browser_qualification'].includes(input.status));
|
|
37
|
+
check(input.status === 'approved' || allowCandidate, 'extension_release_not_approved');
|
|
38
|
+
keys(input.artifact, ['format', 'sha256', 'bytes']);
|
|
39
|
+
check(input.artifact.format === 'zip' && typeof input.artifact.sha256 === 'string' && hashPattern.test(input.artifact.sha256) &&
|
|
40
|
+
Number.isSafeInteger(input.artifact.bytes) && input.artifact.bytes > 0 && input.artifact.bytes <= 100 * 1024 * 1024);
|
|
41
|
+
keys(input.bridge, ['protocol', 'sha256'], ['runtime_reporting']);
|
|
42
|
+
check(input.bridge.protocol === BRIDGE_PROTOCOL && typeof input.bridge.sha256 === 'string' && hashPattern.test(input.bridge.sha256));
|
|
43
|
+
const reporting = input.bridge.runtime_reporting ?? 'protocol_and_version';
|
|
44
|
+
check(['protocol_and_version', 'legacy_pinned_artifact'].includes(reporting));
|
|
45
|
+
if (reporting === 'legacy_pinned_artifact') check(input.extension_version === '0.0.0.1' && input.artifact.sha256 === LEGACY_SHA256);
|
|
46
|
+
keys(input.capabilities, ['manual_card_fill', 'document_bound_single_use', 'autosave_disabled', 'automatic_submit', 'maximum_retention_ms']);
|
|
47
|
+
check(input.capabilities.manual_card_fill === true && input.capabilities.document_bound_single_use === true &&
|
|
48
|
+
input.capabilities.autosave_disabled === true && input.capabilities.automatic_submit === false && input.capabilities.maximum_retention_ms === 480000);
|
|
49
|
+
keys(input.compatibility, ['sdk_versions', 'executor_contract']);
|
|
50
|
+
check(Array.isArray(input.compatibility.sdk_versions) && input.compatibility.sdk_versions.length > 0 && input.compatibility.sdk_versions.length <= 20 &&
|
|
51
|
+
input.compatibility.sdk_versions.every(item => typeof item === 'string' && /^\d+\.\d+\.\d+(?![\s\S])/.test(item)) &&
|
|
52
|
+
new Set(input.compatibility.sdk_versions).size === input.compatibility.sdk_versions.length && input.compatibility.executor_contract === EXECUTOR_CONTRACT);
|
|
53
|
+
check(Array.isArray(input.qualifications) && input.qualifications.length <= 32);
|
|
54
|
+
for (const qualification of input.qualifications) {
|
|
55
|
+
keys(qualification, ['provider', 'scope', 'evidence']);
|
|
56
|
+
check(typeof qualification.provider === 'string' && identifier.test(qualification.provider) &&
|
|
57
|
+
typeof qualification.scope === 'string' && identifier.test(qualification.scope) &&
|
|
58
|
+
typeof qualification.evidence === 'string' && /^docs\/evidence\/[a-z0-9][a-z0-9._-]{0,150}\.json(?![\s\S])/.test(qualification.evidence));
|
|
59
|
+
}
|
|
60
|
+
check(input.status !== 'approved' || input.qualifications.length > 0, 'extension_qualification_missing');
|
|
61
|
+
return freeze(structuredClone(input));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Verify local ZIP bytes before a platform installs them. No upload occurs. */
|
|
65
|
+
export function verifyApprovedExtensionArtifact(bytes, release) {
|
|
66
|
+
const approved = validateApprovedExtensionRelease(release);
|
|
67
|
+
check(bytes instanceof Uint8Array && bytes.byteLength === approved.artifact.bytes, 'extension_artifact_mismatch');
|
|
68
|
+
const actual = createHash('sha256').update(bytes).digest('hex');
|
|
69
|
+
check(actual === approved.artifact.sha256, 'extension_artifact_mismatch');
|
|
70
|
+
return freeze({ verified: true, extension_version: approved.extension_version, artifact_sha256: actual });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Verify private connector observations. The artifact pin must be derived from
|
|
74
|
+
* trusted installation records, not a field supplied by an agent/model. */
|
|
75
|
+
export function verifyApprovedExtensionRuntime(runtime, release, {
|
|
76
|
+
sdkVersion = SDK_VERSION, executorContract = EXECUTOR_CONTRACT, provider,
|
|
77
|
+
} = {}) {
|
|
78
|
+
const approved = validateApprovedExtensionRelease(release);
|
|
79
|
+
check(approved.compatibility.sdk_versions.includes(sdkVersion) && executorContract === approved.compatibility.executor_contract, 'extension_compatibility_mismatch');
|
|
80
|
+
check(typeof provider === 'string' && identifier.test(provider) && approved.qualifications.some(item => item.provider === provider), 'extension_provider_unqualified');
|
|
81
|
+
keys(runtime, ['extension_version', 'artifact_sha256', 'success', 'initialized', 'wasmInitialized', 'autosaveDisabled', 'boundedRetentionSupported', 'maximumRetentionMs'], ['bridge_protocol']);
|
|
82
|
+
check(runtime.extension_version === approved.extension_version && runtime.artifact_sha256 === approved.artifact.sha256, 'extension_runtime_mismatch');
|
|
83
|
+
check(runtime.success === true && runtime.initialized === true && runtime.wasmInitialized === true && runtime.autosaveDisabled === true &&
|
|
84
|
+
runtime.boundedRetentionSupported === true && runtime.maximumRetentionMs === approved.capabilities.maximum_retention_ms, 'extension_runtime_not_ready');
|
|
85
|
+
if (approved.bridge.runtime_reporting === 'legacy_pinned_artifact') {
|
|
86
|
+
check(runtime.bridge_protocol === undefined || runtime.bridge_protocol === BRIDGE_PROTOCOL, 'extension_runtime_mismatch');
|
|
87
|
+
} else check(runtime.bridge_protocol === BRIDGE_PROTOCOL, 'extension_runtime_mismatch');
|
|
88
|
+
return freeze({ verified: true, extension_version: approved.extension_version, artifact_sha256: approved.artifact.sha256,
|
|
89
|
+
bridge_protocol: BRIDGE_PROTOCOL, provider });
|
|
90
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { CheckoutId, CustomerBrowserBinding, RequestOptions } from './index.js';
|
|
2
|
+
import type { ManagedCheckoutContext } from './managed-mcp.js';
|
|
3
|
+
import type { ApprovedExtensionRelease, ApprovedExtensionRuntime } from './approved-extension.js';
|
|
4
|
+
|
|
5
|
+
/** Private, authenticated host projection. Never put this record in MCP arguments/results. */
|
|
6
|
+
export interface OwnedBrowserDocument {
|
|
7
|
+
owner: ManagedCheckoutContext;
|
|
8
|
+
browser: CustomerBrowserBinding;
|
|
9
|
+
document_id: string;
|
|
10
|
+
checkout_url: string;
|
|
11
|
+
expires_at: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FencedBrowserDocument extends OwnedBrowserDocument {
|
|
14
|
+
fence_id: string;
|
|
15
|
+
}
|
|
16
|
+
export interface BrowserReadiness extends OwnedBrowserDocument {
|
|
17
|
+
recording_disabled: true;
|
|
18
|
+
/** Actual runtime receipt plus the independently pinned installed ZIP checksum. */
|
|
19
|
+
extension: ApprovedExtensionRuntime;
|
|
20
|
+
automation: {
|
|
21
|
+
ordinary_operations_drained: boolean;
|
|
22
|
+
agent_access_fenced: boolean;
|
|
23
|
+
fence_id: string | null;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/** Durable fencing is a host responsibility; acquiring a JavaScript mutex is insufficient. */
|
|
27
|
+
export interface BrowserConnectorHost {
|
|
28
|
+
resolveOwnedBrowser(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
29
|
+
attempt_key: string; checkout_url: string;
|
|
30
|
+
}>, options: RequestOptions): Promise<OwnedBrowserDocument>;
|
|
31
|
+
inspectBrowser(context: Readonly<ManagedCheckoutContext>, browser: Readonly<OwnedBrowserDocument>, options: RequestOptions): Promise<BrowserReadiness>;
|
|
32
|
+
/** Persist the fence, reject new ordinary work and await all in-flight automation before resolving. */
|
|
33
|
+
drainAndFence(context: Readonly<ManagedCheckoutContext>, browser: Readonly<OwnedBrowserDocument>, request: Readonly<{
|
|
34
|
+
attempt_key: string;
|
|
35
|
+
}>, options: RequestOptions): Promise<{
|
|
36
|
+
fence_id: string; ordinary_operations_drained: true; agent_access_fenced: true;
|
|
37
|
+
}>;
|
|
38
|
+
/** Persist exact attempt/browser/document/fence binding. A lost ACK must retain its uncertainty fence. */
|
|
39
|
+
bindCheckout(context: Readonly<ManagedCheckoutContext>, request: Readonly<FencedBrowserDocument & {
|
|
40
|
+
checkout_id: CheckoutId; attempt_key: string;
|
|
41
|
+
}>, options: RequestOptions): Promise<void>;
|
|
42
|
+
resolveCheckoutBrowser(context: Readonly<ManagedCheckoutContext>, request: Readonly<{ checkout_id: CheckoutId }>, options: RequestOptions): Promise<FencedBrowserDocument>;
|
|
43
|
+
/** Verify cleanup independently after Accelerate reports closure. Never release an uncertain-payment fence. */
|
|
44
|
+
confirmCleanup(context: Readonly<ManagedCheckoutContext>, browser: Readonly<FencedBrowserDocument>, request: Readonly<{
|
|
45
|
+
checkout_id: CheckoutId;
|
|
46
|
+
}>, options: RequestOptions): Promise<{
|
|
47
|
+
provider_browser_stopped: true; credentials_cleared: true; fence_retained: true;
|
|
48
|
+
}>;
|
|
49
|
+
/** Optional, but REQUIRED for purchase. Perform exactly one trusted pointer click on the unique selector in the exact bound
|
|
50
|
+
* browser, document and URL under the existing owner fence. No retry, no general DOM reads, never lift the fence.
|
|
51
|
+
* Accelerate sends no physical input. A thrown error or dispatched:false is observation only; the caller then completes once. */
|
|
52
|
+
performFinalClick?(context: Readonly<ManagedCheckoutContext>, request: Readonly<FinalClickRequest>, options: RequestOptions): Promise<{ dispatched: boolean }>;
|
|
53
|
+
}
|
|
54
|
+
/** Private request to the customer-owned browser host for the ONE physical final click. Never model/MCP data. */
|
|
55
|
+
export interface FinalClickRequest extends FencedBrowserDocument {
|
|
56
|
+
checkout_id: CheckoutId;
|
|
57
|
+
/** Unique selector bound at approval, inside the exact fenced document. */
|
|
58
|
+
submit_selector: string;
|
|
59
|
+
armed_expires_at: string;
|
|
60
|
+
}
|
|
61
|
+
declare const attachmentBrand: unique symbol;
|
|
62
|
+
declare const finalClickPreparationBrand: unique symbol;
|
|
63
|
+
/** One-use private readiness proof from prepareFinalClick; valid for one performFinalClick on this connector instance. */
|
|
64
|
+
export interface PreparedFinalClick { readonly [finalClickPreparationBrand]: true }
|
|
65
|
+
/** Private one-connector capability returned only to the authenticated backend. */
|
|
66
|
+
export interface PreparedBrowserAttachment {
|
|
67
|
+
readonly browser: Readonly<CustomerBrowserBinding>;
|
|
68
|
+
readonly [attachmentBrand]: true;
|
|
69
|
+
}
|
|
70
|
+
export interface BrowserConnector {
|
|
71
|
+
readonly contract: 'accelerate_existing_browser_v1';
|
|
72
|
+
readonly provider: 'browser_use' | 'connector';
|
|
73
|
+
readonly connector_id?: string;
|
|
74
|
+
prepareAttachment(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
75
|
+
attempt_key: string; checkout_url: string;
|
|
76
|
+
}>, options?: RequestOptions): Promise<PreparedBrowserAttachment>;
|
|
77
|
+
commitAttachment(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
78
|
+
checkout_id: CheckoutId; attachment: PreparedBrowserAttachment;
|
|
79
|
+
}>, options?: RequestOptions): Promise<void>;
|
|
80
|
+
/** Fresh check before private OTP dispatch and before quote/CVV dispatch. */
|
|
81
|
+
assertPaymentReady(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
82
|
+
checkout_id: CheckoutId; checkout_url?: string;
|
|
83
|
+
}>, options?: RequestOptions): Promise<void>;
|
|
84
|
+
confirmCleanup(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
85
|
+
checkout_id: CheckoutId;
|
|
86
|
+
}>, options?: RequestOptions): Promise<void>;
|
|
87
|
+
/** Present only when the host implements performFinalClick. Call BEFORE arming: resolves the fenced owned document, checks
|
|
88
|
+
* provider/extension readiness and the drained fence, without lifting it, so none of that work consumes the short click window. */
|
|
89
|
+
prepareFinalClick?(context: Readonly<ManagedCheckoutContext>, request: Readonly<{ checkout_id: CheckoutId }>, options?: RequestOptions): Promise<PreparedFinalClick>;
|
|
90
|
+
/** Cheap checks only (one-use preparation, exact URL, arm window <= 15s, browser outlives the arm), then asks the customer's
|
|
91
|
+
* host for one physical click. The arm token never reaches the host. No retry. */
|
|
92
|
+
performFinalClick?(context: Readonly<ManagedCheckoutContext>, request: Readonly<{
|
|
93
|
+
checkout_id: CheckoutId; preparation: PreparedFinalClick; checkout_url: string; submit_selector: string; armed_expires_at: string;
|
|
94
|
+
}>, options?: RequestOptions): Promise<{ dispatched: boolean }>;
|
|
95
|
+
}
|
|
96
|
+
export interface ExistingBrowserConnectorOptions {
|
|
97
|
+
/** Provisioned server connector ID; the Accelerate executor must enable this ID separately. */
|
|
98
|
+
connectorId: string;
|
|
99
|
+
extensionRelease: ApprovedExtensionRelease;
|
|
100
|
+
host: BrowserConnectorHost;
|
|
101
|
+
/** Can only increase the required ten-minute headroom, never shorten approval. */
|
|
102
|
+
minimumRemainingMs?: number;
|
|
103
|
+
/** Test clock only. */
|
|
104
|
+
now?: () => number;
|
|
105
|
+
}
|
|
106
|
+
/** Generic registered-executor connector. Does not create/upload/replace a browser or provision a provider. */
|
|
107
|
+
export function createExistingBrowserConnector(options: ExistingBrowserConnectorOptions): BrowserConnector;
|
|
108
|
+
/** First provider recipe. Uses Browser Use IDs through the same host contract; no API key is accepted. */
|
|
109
|
+
export function createBrowserUseConnector(options: Omit<ExistingBrowserConnectorOptions, 'connectorId'>): BrowserConnector;
|