@allorc/a423-sdk 0.2.0 → 0.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0
4
+
5
+ - Add durable single-host SQLite workflow/transaction/nonce/queue/event/spending storage and local-only reset.
6
+ - Add organization-approved agent enrollment, challenge proofs, credentials, public-key verification, revocation and renewal.
7
+ - Require escrow pre-sign/rebroadcast authorization; provide atomic per-transaction/day/total spending reservations.
8
+ - Add EOA/ERC-1271 consent, fresh evidence validation, reorg quarantine, allowlisted multi-deployment routing, log indexing, managed signing/delegation and bounded same-nonce replacement.
9
+ - Add task/evidence schemas, keeper/resume, checkpointed execution, signed webhook outbox, resumable SSE and fresh JSON/PDF receipts.
10
+ - Add shared app/chat projections, wallet consent/dashboard shell, safe legacy migration planning, local-EVM integration tests and dry-run deployment/source-verification tooling.
11
+ - Retain explicit reference-contract constraints. No external deployment or independent audit is claimed.
12
+
13
+ ## 0.3.0
14
+
15
+ - Add optional ethers-based `/escrow` entry point and a compiled ERC-20 reference contract.
16
+ - Enforce EIP-712 worker consent, exact custody amounts, one immutable submission, verifier approval before release, expiry refunds, disputes, arbitrated splits and agreed timeout allocation.
17
+ - Add pre-broadcast signed-transaction journaling, coordinated nonce allocation, canonical receipt/term/event validation and external transaction evidence adoption.
18
+ - Ship escrow-specific transitions and documentation. Contract is experimental, unaudited and not deployed by the package.
19
+ - Add local-EVM lifecycle, failure, recovery and confirmation tests.
20
+
3
21
  ## 0.2.0
4
22
 
5
23
  - Add opt-in `@allorc/a423-sdk/workflow` exports: workflow engine, Next.js-compatible handler, typed HTTP client and canonical agreement hashing.
package/ESCROW.md ADDED
@@ -0,0 +1,162 @@
1
+ # Reference on-chain escrow (0.4.0)
2
+
3
+ This opt-in module implements a real ERC-20 escrow path. The generic workflow remains provider-independent. It includes Solidity source, reproducibly compiled ABI/bytecode, an ethers v6 adapter and local-EVM tests. It is experimental and has not received an independent security audit; deploy to a local chain/testnet for evaluation. No contract is deployed automatically and no funds move merely by installing the package.
4
+
5
+ ## Installation and deployment
6
+
7
+ ```sh
8
+ npm install @allorc/a423-sdk@0.4.0 ethers@^6.15.0
9
+ ```
10
+
11
+ ```ts
12
+ import { ContractFactory } from 'ethers';
13
+ import { ESCROW_ABI, ESCROW_BYTECODE } from '@allorc/a423-sdk/escrow';
14
+ const contract = await new ContractFactory(ESCROW_ABI, ESCROW_BYTECODE, deploymentSigner)
15
+ .deploy(standardTokenAddress);
16
+ await contract.waitForDeployment();
17
+ ```
18
+
19
+ Deployment is an explicit, separately authorised transaction. Each deployment supports one immutable token. There are no owner withdrawals, token replacements, upgrades or pause switches. Standard ERC-20 transfers must preserve exact sender/recipient amounts. Fee-on-transfer/rebasing/blacklisting/pausable tokens are unsupported; an issuer blocking transfers can defeat timeout liveness. Arbitrary token code cannot be made safe by SDK validation. Use a reviewed token and verify the deployed contract bytecode/configuration before trusting it. Forced token transfers do not create job credits; there is no recovery sweep for unsolicited deposits.
20
+
21
+ ## Exact agreement recipe
22
+
23
+ Include every application-specific condition in JSON terms. Include this `escrow` object with these field names. IDs, application identities and wallet addresses are separate concepts; use a trusted identity-to-wallet registry.
24
+
25
+ ```js
26
+ const terms = {
27
+ task: { description: 'Your task', acceptance: { /* your criteria */ } },
28
+ escrow: {
29
+ chain_id: '31337',
30
+ contract: escrowAddress,
31
+ token: tokenAddress,
32
+ amount: '100', // raw integer token units, NOT necessarily 100 displayed tokens
33
+ payer: payerAddress,
34
+ worker: workerAddress,
35
+ verifier: verifierAddress,
36
+ arbitrator: arbitratorAddress,
37
+ work_deadline: workDeadlineSeconds,
38
+ review_deadline: reviewDeadlineSeconds,
39
+ resolution_deadline: resolutionDeadlineSeconds,
40
+ timeout_worker_bps: 2500, // agreed fallback: 25% worker, 75% payer
41
+ },
42
+ };
43
+ ```
44
+
45
+ All deadlines are absolute Unix seconds and strictly increasing. The work deadline must be in the future at funding. `amount` is a positive uint256 decimal string. The SHA-256 hash of the entire canonical terms object is stored on-chain; it includes payment rules and the task criteria. The Solidity EIP-712 terms digest is a separate encoding for worker consent. `jobId` is SHA-256 of UTF-8 `a423:escrow:v1:` plus the application job ID. One job ID may only be funded once per deployed contract.
46
+
47
+ The arbitrator must differ from payer and worker. The verifier may be the payer: this is a trusted verifier policy, not trustless proof of output quality. The 0.4 reference contract supports EOA EIP-712 worker signatures and ERC-1271 wallet signatures. Existing 0.3 deployments remain EOA-only. For smart-account transaction execution, submit through the account wallet and supply verified `transaction_hash` evidence to the SDK; the raw-transaction sender itself is still an EOA/managed signer.
48
+
49
+ ## Connect Next.js and the workflow
50
+
51
+ ```ts
52
+ import { createWorkflow, createWorkflowHandler } from '@allorc/a423-sdk/workflow';
53
+ import { createEscrowAdapters, escrowTransitions } from '@allorc/a423-sdk/escrow';
54
+
55
+ const adapters = createEscrowAdapters({
56
+ provider,
57
+ chainId,
58
+ contractAddress: escrowAddress,
59
+ tokenAddress,
60
+ confirmations: requiredConfirmations,
61
+ transactions: durableTransactionStore,
62
+ signerFor: async actor => signerRegistry.getSigner(actor),
63
+ addressFor: async actor => signerRegistry.getAddress(actor),
64
+ authorizeTransaction, // REQUIRED: spending/delegation policy; see OPERATIONS.md
65
+ validateArtifact: async ({ job, resultHash }) => {
66
+ // Fetch exact bytes, check digest and evaluate accepted criteria.
67
+ // Return true only after all checks pass.
68
+ return artifactService.validate(job, resultHash);
69
+ },
70
+ });
71
+ const workflow = createWorkflow({
72
+ store: durableWorkflowStore,
73
+ adapters: { ...adapters, expire: applicationExpiryAdapter },
74
+ transitions: escrowTransitions,
75
+ validateTerms: applicationTermsValidator,
76
+ authorize: applicationPolicy,
77
+ });
78
+ const handler = createWorkflowHandler({ workflow, authenticate: authenticateRequest });
79
+ // In app/api/a423/[...path]/route.ts:
80
+ export const runtime = 'nodejs';
81
+ export { handler as GET, handler as POST };
82
+ ```
83
+
84
+ All application services in this example must be supplied. Configure explicit `arbitrator` alongside requester, worker and verifier in job participants. `addressFor` must map all four authenticated IDs to the agreed addresses. `signerFor` must reject unknown actors and apply spending/delegation approvals. The adapter uses offline `signTransaction`, so custodial/local/KMS signers must support it. It does not prompt a browser wallet or bypass human approval. Do not use keys in client React code.
85
+
86
+ ## Acceptance is not locking
87
+
88
+ 1. Create a job, quote terms, and record both actors accepting the same revision/hash using the workflow API.
89
+ 2. Obtain a worker signature over the final on-chain terms:
90
+
91
+ ```ts
92
+ import { workerConsent } from '@allorc/a423-sdk/escrow';
93
+ const consent = workerConsent(acceptedJob, { chainId, contractAddress: escrowAddress, tokenAddress });
94
+ const signature = await workerSigner.signTypedData(consent.domain, consent.types, consent.value);
95
+ ```
96
+
97
+ The worker should inspect the terms it signs. EIP-712 binds consent to chain, contract, job, parties, amount, deadlines, timeout allocation and the whole agreement hash. The payer authorises the actual funding transaction.
98
+ 3. Call `workflow.act(id, 'fund', { worker_signature: signature }, { actor: requesterId, idempotencyKey: 'fund_1' })`.
99
+ 4. The adapter grants token allowance if needed (zero reset first when necessary), then calls the reference contract's `fund`. **Approval alone does not lock money.** The contract checks the worker signature, transfers the exact amount into custody, stores all terms and emits `Funded` atomically.
100
+ 5. Pending results keep the workflow `accepted` with a `pending` operation. Display “funding pending”, not “locked”. Retry the same action/key to progress approval/funding. Only successful, sufficiently confirmed, canonically included receipts plus matching event and on-chain terms produce `funded`.
101
+ 6. Start work via `start`; it rereads the actual escrow and deadline. The SDK does not run the worker. Dispatch through a durable queue only after this gate succeeds.
102
+
103
+ To automatically initiate funding after the second acceptance, orchestrate the above from your application after obtaining payer authorisation. Off-chain acceptance cannot force a wallet to sign. Do not change the UI to “funded” at acceptance time.
104
+
105
+ ## Full lifecycle and exact inputs
106
+
107
+ | Action | Input | On-chain restriction / result |
108
+ |---|---|---|
109
+ | fund | `{ worker_signature }` | Payer only; valid worker consent; exact token transfer; new job ID |
110
+ | start | `{}` | Adapter checks all terms, funded state and unexpired work deadline; no transaction |
111
+ | submit | `{ result_hash: '0x…64 hex chars' }` | Worker only; before work deadline; nonzero artifact commitment |
112
+ | verify | `{}` | Callback validates actual artifact; verifier transaction must match submitted digest and review deadline |
113
+ | release | `{}` | Verifier only, verified state, before resolution deadline; full worker payout |
114
+ | refund | `{}` | Anyone on-chain after work deadline, only if no submission; full payer refund |
115
+ | dispute | `{ reason_hash: '0x…64 hex chars' }` | Party only, submitted/verified and before resolution deadline; blocks ordinary release |
116
+ | resolve | `{ worker_amount: '40' }` | Arbitrator only, disputed, by resolution deadline; remainder refunded to payer |
117
+ | timeout | `{}` | Funded/running: expiry refund. Submitted/verified/disputed: agreed fallback allocation after resolution deadline |
118
+
119
+ `escrowTransitions` makes dispute an actual on-chain operation, gives resolve to the arbitrator and adds timeout. Resolution and timeout end in workflow `settled`; inspect evidence `worker_paid`, `amount` and contract state to distinguish paid/refunded/split. Contract settlement states: 5 Paid, 6 Refunded, 7 Split. Event reasons: 0 release, 1 expired-unsubmitted refund, 2 arbitration, 3 submitted timeout.
120
+
121
+ A verifier can approve and release before a dispute is opened; there is no guaranteed challenge window or objective quality oracle. Rejected artifact verification leaves the workflow submitted and clears pending, allowing a dispute. The contract enforces that submission and verifier approval exist; the application verifier still judges task quality. For resubmission/milestones, use a separate contract policy: the reference contract permits one immutable submission and one settlement, not partial milestone releases. Arbitration can split the one escrow.
122
+
123
+ Cancellation and quote expiry before funding remain workflow/application policies and move no tokens. The optional operations/authority modules provide keeper ticks and an explicit registry; configure and run them as described in OPERATIONS.md.
124
+
125
+ ## Recovery and journal requirements
126
+
127
+ The workflow job is written before any effect. The transaction journal separately stores signed raw transactions **before broadcasting**, including hash, destination, calldata and signer. Retries reuse the winning journal record and rebroadcast the same bytes; they do not create another payment or nonce. Do not expose raw signed transactions in public workflow metadata/receipts. Treat the journal as protected operational storage.
128
+
129
+ Implement `TransactionStore`:
130
+
131
+ - `get(id)`: retrieve a durable record.
132
+ - `create(id, record)`: atomic insert-if-absent; return boolean. Never overwrite an existing signed transaction.
133
+ - `reserveNonce(scope, minimum, operationId)`: transactionally allocate per-chain/address nonces, at least the RPC pending nonce; the same operation must always retrieve its original reservation. Every process using the account must coordinate through this allocator. External wallet activity requires an explicit operational policy.
134
+
135
+ `createMemoryTransactionStore()` is only for tests. Production needs a real database implementation. A crash after nonce reservation but before journaling must resume that operation to fill its reserved nonce; later transactions may remain blocked behind it. The optional bounded fee replacement worker can journal a same-nonce fee increase under explicit caps; there is no automatic cancellation. A genuinely stuck transaction requires deliberate recovery with the same nonce and updated journal lineage; do not blindly assign a new nonce. Prefer dedicated signer accounts to uncontrolled mixed wallet usage.
136
+
137
+ `reconcile` only observes existing journal entries and never signs/broadcasts. If an approval is confirmed but the funding transaction has not yet been created, reconcile returns pending; retry the original fund action/key to authorise progression. After a signed transaction is journaled but not broadcast, execute retries rebroadcast the identical bytes. After broadcast/mining but before the workflow write, reconcile verifies and adopts its evidence. Explicit `rejected` results permit a deliberate new attempt with a new action key; an unknown network outcome stays pending or throws with the pending record retained.
138
+
139
+ For externally submitted transactions, pass `transaction_hash` in the action input from the beginning (in addition to the normal fields). The adapter observes it without broadcasting and checks the configured contract, exact agreement, action event and allocation. This supports recording permissionless refunds/timeouts submitted by a keeper or another account. Use the correct workflow action; it cannot adopt arbitrary unrelated transfers. Supplying an incorrect hash fails closed and leaves pending for investigation; do not change persisted input to hide errors.
140
+
141
+ RPC chain ID and immutable token are checked; event emitter, job ID, agreement hash, all stored terms, event amounts and results are verified. Evidence contains transaction/block hash and required confirmation depth. Readers need historical state access for the receipt block. The adapter checks current canonical inclusion at observation time; confirmations are a configurable risk policy, not an absolute finality guarantee. The operations watcher can continuously revalidate evidence and place affected jobs on a safety hold. It does not automatically roll back completed financial states or erase observations; reconcile re-included transactions through an explicitly reviewed recovery procedure. Chain outages preserve uncertainty and cannot be repaired by SDK code.
142
+
143
+ No timeout is automatic: a keeper or participant must submit a transaction, and the chain must process it. Deadlines provide an eligible escape path under the agreed allocation; they do not guarantee execution during censorship, chain failure or broken token transfers.
144
+
145
+ ## Verification and receipts
146
+
147
+ Artifact input is a bytes32 commitment. The application chooses the hashing scheme and storage format; for SHA-256, hash the exact delivered bytes, not a mutable URL. The verifier callback must check both bytes and accepted criteria. There is no generic LLM quality proof.
148
+
149
+ Funding, submission, verification and settlement effects return JSON-safe chain evidence. Workflow receipts retain those observations; they are not live PDFs or perpetual assertions of finality. Fresh PDF/evidence services should recheck canonical receipts and commitments at read time and format raw units with the actual token decimals.
150
+
151
+ ## Development checks
152
+
153
+ From the source repository:
154
+
155
+ ```sh
156
+ npm ci --ignore-scripts
157
+ npm run build:escrow
158
+ npm test
159
+ npm pack --dry-run --ignore-scripts
160
+ ```
161
+
162
+ The local-EVM suite exercises custody, consent binding, permissions, submission-before-release, expiry refund, arbitration splits, timeout allocation, unsupported token rejection, recovery, external evidence adoption and confirmation depth. Solidity compiler and OpenZeppelin versions are pinned in dev dependencies; ethers is an optional peer used only by `/escrow`. Core/workflow do not load it.
package/INTEGRATION.md CHANGED
@@ -1,13 +1,25 @@
1
+ # Integration update for 0.4
2
+
3
+ Read OPERATIONS.md before following the older integration checklist below. Persistence, authority credentials, renewal, spending authorization, application projections/chat tools, keeper/resume, execution queue, watcher/indexer, PDF receipts and a local UI shell are now provided as concrete modules. Choose the supplied implementation or adapt deliberately.
4
+
5
+ The remote MVP was not available in the SDK workspace; these utilities do not imply its existing routes or configuration have changed. Wire them into the actual app and run its tests. Deploy new contracts only with explicit deployment authorization; retain legacy custody records on their original contracts.
6
+
1
7
  # Integration instructions for the MVP coding agent
2
8
 
3
9
  Install after the maintainer publishes:
4
10
 
5
11
  ```sh
6
- npm install @allorc/a423-sdk@0.2.0
12
+ npm install @allorc/a423-sdk@0.4.0
7
13
  ```
8
14
 
9
15
  For testing before publication, install the maintainer's `npm pack` tarball instead. Do not silently install 0.1 and assume the workflow entry point exists.
10
16
 
17
+ ## Reference escrow option
18
+
19
+ Read `ESCROW.md` first. The package now includes a concrete `/escrow` adapter plus `ESCROW_ABI`/`ESCROW_BYTECODE` and Solidity source. This contract differs from the old MVP contract: it requires EIP-712 worker consent, verifier approval, explicit arbitration and timeout terms. Do not point this adapter at the old contract. Use a fresh local/testnet deployment for evaluation or write a separately tested legacy adapter.
20
+
21
+ Use `createEscrowAdapters` and `escrowTransitions` when testing the reference path. Implement both the durable workflow store and protected transaction journal with atomic nonce reservation, map all four roles to trusted wallets, and supply artifact validation. Retain the reference lifecycle tests below. Deployment or real fund movement requires separate authorisation.
22
+
11
23
  ## Task
12
24
 
13
25
  Integrate the opt-in `@allorc/a423-sdk/workflow` API into the existing MVP. Inspect the installed README, declarations and exports first. Preserve the existing app until a complete equivalent journey passes. Do not introduce demo keys, positional signer lookup, unknown-agent fallback, or authentication based only on x-agent-id. Do not deploy or move real funds as part of integration tests.
@@ -22,7 +34,7 @@ Integrate the opt-in `@allorc/a423-sdk/workflow` API into the existing MVP. Insp
22
34
  8. Implement `release.execute/reconcile`, checking caller role, recorded verification and actual payment evidence. Verify exact amount/recipient and relevant event. The SDK state guard does not change contract rules; document whether the contract itself requires submission. Never infer successful payment from a broadcast hash alone.
23
35
  9. Implement `refund.execute/reconcile` based on actual contract eligibility. The default rule allows requesting a refund while funded/running; the adapter must enforce the deadline and absence of submission. Submitted disputes require an explicit supported resolution policy. Implement `resolve` only when that policy exists; confirmed resolution evidence must state whether money was released, refunded or remains locked. `resolved` alone does not imply payment.
24
36
  10. Add application deadline/expiry checks to authorization and the `expire` adapter. Schedule sweeps with authorized roles. Cancellation only applies before funding in the default workflow. Do not claim automatic sweeps or a dispute timeout exist if they are not implemented.
25
- 11. Configure each adapter's `reconcile` to inspect the provider using durable operation records. Unknown outcomes remain pending. Poll/reconcile in a durable worker. Handle configured confirmations, disappearing observations and reorgs according to the app's policy; the SDK does not include a chain watcher. Add explicit corrective transitions/policies if required rather than editing records outside CAS.
37
+ 11. Configure each adapter's `reconcile` to inspect the provider using durable operation records. Unknown outcomes remain pending. Poll/reconcile in a durable worker. Handle configured confirmations, disappearing observations and reorgs according to the app's policy; configure watchEvidenceOnce and the incremental indexer. Add explicit corrective transitions/policies if required rather than editing records outside CAS.
26
38
  12. Expose `createWorkflowHandler` through a Node-runtime Next.js catch-all route. Use `createWorkflowClient` or documented HTTP endpoints externally. The old root API and new workflow API use distinct models; migrate the client deliberately.
27
39
  13. Build machine-readable receipts through `workflow.receipts`. Revalidate chain evidence before generating chain-backed PDFs; derive amounts/participants from validated observations and use actual token decimals. Distinguish cached observations from freshly checked evidence.
28
40
 
package/LEGACY.md ADDED
@@ -0,0 +1,251 @@
1
+ # Legacy root API
2
+
3
+ This document describes only the original root exports. Use `/workflow`, `/escrow` and `/operations` for the current escrow implementation. Statements below about absent escrow/queues refer only to that original API.
4
+
5
+
6
+
7
+ # @allorc/a423-sdk
8
+
9
+ One experimental package for exposing an agent job API from a Next.js App Router application. Configure a handler, export it from a catch-all route, and use the typed client to offer, accept, submit and approve work.
10
+
11
+ **Status:** local experimental v0.1.0. The npm package has not been published by this checkout. Source repository: https://github.com/Allorc-official/A423-SDK. This implements an initial workflow, not a finalized A423 standard or a production escrow system. Node.js 22+. ESM, CommonJS and TypeScript declarations; zero runtime dependencies.
12
+
13
+ ## Quick setup in Next.js
14
+
15
+ Until publication, install the local package into your existing Next.js application:
16
+
17
+ ```sh
18
+ npm install /absolute/path/to/servers/server/a423_sdk
19
+ ```
20
+
21
+ After publication: `npm install @allorc/a423-sdk`.
22
+
23
+ Create `lib/a423.ts`:
24
+
25
+ ```ts
26
+ import 'server-only';
27
+ import { createA423Handler, createMemoryStore } from '@allorc/a423-sdk';
28
+
29
+ // Development only. Replace with a durable StateStore before deployment.
30
+ const store = createMemoryStore();
31
+ const token = process.env.A423_API_TOKEN;
32
+ const actorId = process.env.A423_AGENT_ID;
33
+ if (!token || !actorId) throw new Error('Configure A423_API_TOKEN and A423_AGENT_ID');
34
+
35
+ export const a423 = createA423Handler({
36
+ store,
37
+ authenticate(request) {
38
+ return request.headers.get('authorization') === `Bearer ${token}`
39
+ ? actorId
40
+ : null;
41
+ }
42
+ });
43
+ ```
44
+
45
+ This minimal authentication example maps one secret credential to one public actor ID. A complete workflow requires separate requester and worker credentials. The two-actor example is in `examples/nextjs/lib/a423.ts`; replace it with your own authenticated identity integration when connecting real agents.
46
+
47
+ Create `app/api/a423/[...path]/route.ts`:
48
+
49
+ ```ts
50
+ import { a423 } from '@/lib/a423';
51
+
52
+ export const runtime = 'nodejs';
53
+ export const GET = a423;
54
+ export const POST = a423;
55
+ ```
56
+
57
+ The `@/` import assumes your application's usual root alias; adjust it if your app uses `src/` or relative imports. This is one catch-all route, not one file per action. Do not run it in the Edge runtime. The SDK does not start a listener or publish routes on its own: Next.js exposes the route you configure.
58
+
59
+ Keep credentials in server environment variables without `NEXT_PUBLIC_`. The package's client is designed for Node/server use, not for embedding worker credentials in client-side React. Your existing Next.js application supplies Next.js, React, TypeScript and `server-only`; the SDK itself does not import framework internals.
60
+
61
+ ## Use the typed client
62
+
63
+ ```ts
64
+ import { createA423Client } from '@allorc/a423-sdk';
65
+
66
+ const requester = createA423Client({
67
+ baseUrl: 'https://your-app.example/api/a423',
68
+ headers: { authorization: `Bearer ${requesterToken}` }
69
+ });
70
+ const worker = createA423Client({
71
+ baseUrl: 'https://your-app.example/api/a423',
72
+ headers: { authorization: `Bearer ${workerToken}` }
73
+ });
74
+
75
+ const job = await requester.offer({
76
+ worker_id: workerAgentId,
77
+ title: 'Prepare a short brief',
78
+ description: 'Research the topic and return a deliverable link.',
79
+ price: { amount_minor: 500, currency: 'USD' }
80
+ }, { idempotencyKey: 'brief-request-001' });
81
+
82
+ await worker.accept(job.id);
83
+ await worker.submit(job.id, { deliverable: 'https://example.com/brief.md' });
84
+ await requester.approve(job.id);
85
+ const receipts = await requester.receipts(job.id);
86
+ ```
87
+
88
+ `requesterToken`, `workerToken` and `workerAgentId` come from your application, not the SDK. The requester and worker are distinct authenticated actors. The example orchestrates both sides for demonstration; in a real integration they normally run independently.
89
+
90
+ The price is an agreed term: 500 USD minor units means $5.00 when your adapter uses USD cents. Currency codes are uppercase strings of 3–12 letters/digits starting with a letter. The SDK does not calculate exchange rates, currency precision, fees or blockchain token decimals; define those semantics in your settlement integration. `amount_minor` must be a nonnegative safe integer. Zero-price jobs are allowed.
91
+
92
+ A deliverable is an opaque string, such as a URL, repository reference or storage path. The SDK stores it and never downloads or executes it. It does not perform the work, evaluate quality or run an agent automatically.
93
+
94
+ Client methods return `Job`, except `receipts`, which returns `Receipts`. `headers` may be an async function for fresh credentials. `fetch` can be injected for tests or custom networking. Requests default to a 30-second timeout, refuse redirects and do not retry automatically.
95
+
96
+ ## HTTP API
97
+
98
+ Every request requires successful `authenticate(request)`. POSTs use `Content-Type: application/json`.
99
+
100
+ | Method | Route | Body | Who can call |
101
+ | --- | --- | --- | --- |
102
+ | POST | `/api/a423/offers` | `{worker_id,title,description,price}` and `Idempotency-Key` header | Authenticated requester |
103
+ | GET | `/api/a423/jobs/:id` | None | Requester or assigned worker |
104
+ | POST | `/api/a423/jobs/:id/accept` | `{}` | Assigned worker |
105
+ | POST | `/api/a423/jobs/:id/submit` | `{deliverable}` | Assigned worker |
106
+ | POST | `/api/a423/jobs/:id/approve` | `{}` | Requester |
107
+ | GET | `/api/a423/jobs/:id/receipts` | None | Requester or assigned worker |
108
+
109
+ Offer creation returns HTTP 201 with `{job}`; an identical retry returns 200 with the existing job. Other actions return `{job}`. Receipts return `{job_id,state,settlement,events}`. Responses use `Cache-Control: no-store`. Unknown jobs and jobs belonging to other actors both return 404.
110
+
111
+ External agents can use plain HTTP without installing the SDK. For example:
112
+
113
+ ```sh
114
+ curl https://your-app.example/api/a423/offers \
115
+ -H "Authorization: Bearer $A423_REQUESTER_TOKEN" \
116
+ -H 'Content-Type: application/json' \
117
+ -H 'Idempotency-Key: brief-request-001' \
118
+ -d '{"worker_id":"worker-agent-id","title":"Brief","description":"Prepare a short brief","price":{"amount_minor":500,"currency":"USD"}}'
119
+ ```
120
+
121
+ Adjust `basePath` if you mount at a different URL. This initial implementation has no list/search, cancellation, disputes, amendments, webhooks, discovery endpoint, background scheduler or UI. Poll known job IDs or coordinate workers through your own queue. Authentication is mandatory; signatures and message-level cryptographic verification are not built in.
122
+
123
+ ## Workflow and retry behaviour
124
+
125
+ A job follows `offered → accepted → submitted → completed`. With a settlement adapter it follows `submitted → settlement_pending → completed` during approval.
126
+
127
+ The server checks the actor and current state before each change. Every successful change increments the job version and appends an event. Submission is immutable: retrying with the same deliverable succeeds, but replacing it is rejected. Repeating acceptance after acceptance or approval after completion returns the current job without adding a duplicate event.
128
+
129
+ Offer IDs are deterministic SHA-256 values scoped by protocol version, authenticated requester ID and `Idempotency-Key`. Reuse the same key and exact logical offer when retrying after a network failure. Reusing it with different offer details returns 409. Store the key on your side; a freshly generated key means a new offer. Keys are visible ASCII without spaces, at most 200 characters, and should not contain secrets.
130
+
131
+ Storage changes use compare-and-set. Concurrent writes can return 409 `CONFLICT`; reload the job and retry the intended action. The SDK does not keep an HTTP request open for the duration of a task. Worker execution and scheduling belong in your application's background jobs.
132
+
133
+ ## Storage adapter
134
+
135
+ `store` is required. `createMemoryStore()` is only for local development/tests: it loses data on restart and is not shared across serverless instances or processes. There is no silent in-memory default.
136
+
137
+ Implement the `StateStore` interface against your database:
138
+
139
+ ```ts
140
+ interface StateStore {
141
+ get(id: string): Promise<Job | null>;
142
+ create(job: Job): Promise<boolean>;
143
+ compareAndSet(id: string, expectedVersion: number, job: Job): Promise<boolean>;
144
+ }
145
+ ```
146
+
147
+ `create` must atomically insert only if the ID is absent; enforce a unique ID constraint. `compareAndSet` must atomically update only when both ID and version match, then report whether one row changed. Store job fields and events in the same transaction. A read followed by an unconditional update is not sufficient. Return independent snapshots so caller mutations do not change stored state.
148
+
149
+ For example, a relational adapter can use an ID primary key, a version column and a JSON document. Create with insert-on-conflict-do-nothing; update using `WHERE id = ? AND version = ?`. This is a contract, not a shipped SQL adapter. Durable storage must also support pending settlement recovery.
150
+
151
+ ## Identity and authentication
152
+
153
+ `authenticate(request)` is the integration point for your identity system. It returns an authenticated actor's public agent ID or `null` to reject. It may verify a bearer credential, a session, or an externally signed request. The returned ID determines permissions; an untrusted ID in a request body/header does not.
154
+
155
+ `@allorc/identity` 0.3.0 can form a public agent ID:
156
+
157
+ ```ts
158
+ import { VERSION, createIdentity } from '@allorc/identity';
159
+
160
+ const identity = createIdentity({
161
+ version: VERSION,
162
+ organization_public_key: 'externally-supplied-public-value',
163
+ agent_reference: 'worker-42',
164
+ agent_name: 'Alice',
165
+ agent_email: 'alice@example.com'
166
+ });
167
+ // Register identity.agent_id with your credential/authority system separately.
168
+ ```
169
+
170
+ Install that optional package separately when needed. A hash match proves record consistency, not ownership or authentication. This SDK does not claim to verify the organisation behind a public key, and does not depend on the identity package to authenticate actors. Changes to that package's public ingredients change its derived agent ID; your identity integration must manage those updates.
171
+
172
+ For cookie-based authentication, your integration must also enforce its CSRF/origin policy. The SDK does not configure sessions, CORS, network access, rate limits or tenant onboarding for you. The supplied example uses server-issued bearer tokens with fixed actor bindings.
173
+
174
+ ## Optional settlement adapter
175
+
176
+ Without an adapter, approval marks the job completed with:
177
+
178
+ ```json
179
+ {"status":"not_configured","reference":null}
180
+ ```
181
+
182
+ **This records task approval and moves no money.** A configured adapter is called only after requester approval and after the job is persisted as `settlement_pending`:
183
+
184
+ ```ts
185
+ const handler = createA423Handler({
186
+ store,
187
+ authenticate,
188
+ settlement: {
189
+ async settle({ job, idempotencyKey }) {
190
+ // Invoke your own escrow/payment service with this stable key.
191
+ // Persistently deduplicate that key, including concurrent invocations.
192
+ const result = await yourSettlementService.release({
193
+ jobId: job.id,
194
+ requesterId: job.requester_id,
195
+ workerId: job.worker_id,
196
+ price: job.price,
197
+ idempotencyKey
198
+ });
199
+ return { reference: result.receiptId };
200
+ }
201
+ }
202
+ });
203
+ ```
204
+
205
+ `yourSettlementService` is your external integration, not an included export. The SDK passes `a423:settle:<job-id>` as a stable provider idempotency key. Your adapter MUST durably deduplicate it and return the same result on retries, including simultaneous calls from multiple server instances. The SDK cannot guarantee exactly-once movement of funds across an external provider and a database transaction.
206
+
207
+ On a provider error the server returns 502 and leaves the job pending; retry `approve` as the requester. If the provider succeeds but the database write fails, retrying approval uses the same key. If you run a recovery worker, route it through equivalent actor and idempotency rules. No automatic retry queue is included.
208
+
209
+ This is not funded escrow: the SDK does not reserve funds at offer/acceptance, validate wallets, sign financial authorisations, collect mandates or guarantee payment. Your adapter must enforce those rules before moving funds. `settled` records the adapter's reported success, not an independently verified blockchain confirmation.
210
+
211
+ ## Errors and limits
212
+
213
+ Errors are `{error:{code,message}}`. The client throws `A423Error` with `code` and `status` for HTTP failures. Network/timeout errors propagate from fetch. Internal exception details stay server-side; optional `onError(error)` lets your application record them.
214
+
215
+ | HTTP status | Typical code |
216
+ | --- | --- |
217
+ | 400 | `INVALID_INPUT`, `INVALID_JSON` |
218
+ | 401 | `UNAUTHENTICATED` |
219
+ | 403 | `FORBIDDEN` |
220
+ | 404 | `NOT_FOUND` |
221
+ | 405 | `METHOD_NOT_ALLOWED` |
222
+ | 409 | `INVALID_STATE`, `CONFLICT`, `IDEMPOTENCY_CONFLICT` |
223
+ | 413 | `BODY_TOO_LARGE` |
224
+ | 415 | `UNSUPPORTED_MEDIA_TYPE` |
225
+ | 500 | `INTERNAL_ERROR` |
226
+ | 502 | `SETTLEMENT_UNAVAILABLE` |
227
+
228
+ Request bodies default to 64 KiB and are limited while streaming, even without `Content-Length`; `maxBodyBytes` can set 1 byte through 1 MiB. Description limit: 16,000 characters; deliverable: 8,000; actor IDs and titles: 256. Client response bodies are limited to 1 MiB. Bound requests at your ingress too. There is no multipart/file upload or arbitrary server-side URL fetching.
229
+
230
+ ## Package exports
231
+
232
+ | Export | Purpose |
233
+ | --- | --- |
234
+ | `createA423Handler(options)` | Standard Web Request/Response handler usable in Next.js routes |
235
+ | `createA423Client(options)` | Typed HTTP client |
236
+ | `createMemoryStore()` | Explicit local development store |
237
+ | `A423Error` | Structured client/protocol error |
238
+ | `PROTOCOL_VERSION` | `a423.experimental.v1` |
239
+
240
+ ESM and CommonJS expose the same names. Interfaces for `Job`, `StateStore`, `SettlementAdapter` and configuration are in `src/index.d.ts`. The handler is framework-independent at runtime, but the included integration targets Next.js App Router. Installing this SDK does not publish an API or establish protocol compatibility with other A423 implementations.
241
+
242
+ ## Development
243
+
244
+ ```sh
245
+ npm test
246
+ npm run pack:check
247
+ ```
248
+
249
+ Tests exercise the complete workflow through actual Web Request/Response objects, permissions, malformed input, streaming limits, concurrent updates, retry semantics and settlement recovery. An additional socket-based test runs where local listeners are permitted, and explicitly skips when the execution environment blocks them. Next.js example files are templates for an existing application; they are not a standalone generated Next.js app.
250
+
251
+ No runtime dependencies or build step. Before a public release, review the package allowlist, configure the repository/private security reporting channel, and verify a real Next.js deployment and durable adapter. Publication is a separate maintainer action; this package has not been published by creating these files.
package/NOTICE ADDED
@@ -0,0 +1,24 @@
1
+ The compiled A423Escrow artifact incorporates OpenZeppelin Contracts 5.4.0.
2
+
3
+ The MIT License (MIT)
4
+
5
+ Copyright (c) 2016-2025 Zeppelin Group Ltd
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included
16
+ in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.