@allorc/a423-sdk 0.5.0 → 0.8.1
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/CANCELLATION.md +106 -0
- package/CHANGELOG.md +36 -0
- package/ESCROW.md +2 -0
- package/INTEGRATION.md +15 -1
- package/OPERATIONS.md +4 -0
- package/PROFIT-POLICY.md +154 -0
- package/README.md +606 -63
- package/SECURITY.md +6 -0
- package/SETTLEMENT.md +4 -0
- package/examples/README.md +29 -0
- package/examples/operations-dashboard.html +3 -3
- package/examples/quickstart.mjs +33 -0
- package/package.json +14 -3
- package/src/cancellation.cjs +74 -0
- package/src/escrow/cancellation.cjs +34 -0
- package/src/escrow/index.cjs +41 -2
- package/src/escrow/index.d.mts +6 -2
- package/src/escrow/index.d.ts +6 -2
- package/src/escrow/index.mjs +1 -1
- package/src/escrow/negotiation.cjs +1 -1
- package/src/escrow/services.cjs +1 -1
- package/src/operations/application.cjs +14 -6
- package/src/operations/factory.cjs +6 -4
- package/src/operations/index.cjs +10 -0
- package/src/operations/index.d.ts +5 -3
- package/src/operations/index.mjs +1 -1
- package/src/policy/index.cjs +94 -0
- package/src/policy/index.d.ts +23 -0
- package/src/policy/index.mjs +2 -0
- package/src/workflow.cjs +97 -13
- package/src/workflow.d.ts +38 -5
- package/src/workflow.mjs +1 -1
package/README.md
CHANGED
|
@@ -1,99 +1,631 @@
|
|
|
1
|
-
# A423 SDK
|
|
1
|
+
# A423 SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Build agreements between agents, lock payment in escrow, and produce evidence of what actually happened.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A423 is a backend SDK for agent-to-agent, human-to-agent and agent-to-human jobs. You supply the task, identities, wallets, execution and validation. The SDK coordinates the agreement, permissions, durable workflow, on-chain settlement and receipts.
|
|
6
|
+
|
|
7
|
+
[npm package](https://www.npmjs.com/package/@allorc/a423-sdk) · [GitHub source](https://github.com/Allorc-official/A423-SDK) · [Changelog](./CHANGELOG.md) · [Security](./SECURITY.md) · [MIT license](./LICENSE)
|
|
8
|
+
|
|
9
|
+
**These docs describe source version 0.8.1.** Check the published npm version before installing; a source commit is not an npm release. If 0.8.1 is not published yet, use the source/tarball instructions below. Installing an older version will not provide newer APIs.
|
|
10
|
+
|
|
11
|
+
> Experimental, independently unaudited reference implementation. Acceptance is not payment. A transaction hash is not confirmation. Funds live in the deployed escrow contract, never inside the SDK or the worker's wallet while locked.
|
|
12
|
+
|
|
13
|
+
## Contents
|
|
14
|
+
|
|
15
|
+
- [What runs where](#what-runs-where)
|
|
16
|
+
- [Install the library](#install-the-library)
|
|
17
|
+
- [Try it without a blockchain](#try-it-without-a-blockchain)
|
|
18
|
+
- [Configure an escrow application](#configure-an-escrow-application)
|
|
19
|
+
- [Configuration arguments](#configuration-arguments)
|
|
20
|
+
- [Understand target, minimum and maximum](#understand-target-minimum-and-maximum)
|
|
21
|
+
- [Define a job agreement](#define-a-job-agreement)
|
|
22
|
+
- [End-to-end job lifecycle](#end-to-end-job-lifecycle)
|
|
23
|
+
- [Cancellation and partial-work settlement](#cancellation-and-partial-work-settlement)
|
|
24
|
+
- [Decline, cancel and reopen before funding](#decline-cancel-and-reopen-before-funding)
|
|
25
|
+
- [Use it from Next.js or another agent](#use-it-from-nextjs-or-another-agent)
|
|
26
|
+
- [Retries, receipts and recovery](#retries-receipts-and-recovery)
|
|
27
|
+
- [Supported scenarios and boundaries](#supported-scenarios-and-boundaries)
|
|
28
|
+
- [Modules and detailed guides](#modules-and-detailed-guides)
|
|
29
|
+
- [Develop and publish](#develop-and-publish)
|
|
30
|
+
|
|
31
|
+
## What runs where
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
flowchart TD
|
|
35
|
+
Caller["Your UI, human or AI agent"] --> Backend["Your backend with A423 SDK"]
|
|
36
|
+
Backend <--> Store["Private durable storage"]
|
|
37
|
+
Backend --> RPC["RPC connection to chosen network"]
|
|
38
|
+
RPC <--> Escrow["Deployed A423Escrow contract"]
|
|
39
|
+
Escrow --> Wallets["Worker payout and payer refund"]
|
|
40
|
+
Wallets -->|"Wallet approvals and signatures"| Backend
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Component | Responsibility |
|
|
44
|
+
|---|---|
|
|
45
|
+
| Your application | Task execution, artifacts, real cost measurement, wallet custody/delegation and business approvals |
|
|
46
|
+
| SDK backend | Agreements, role checks, workflow records, transaction journal, policy, reconciliation and receipts |
|
|
47
|
+
| Authority | Organization-approved enrollment and authenticated agent credentials |
|
|
48
|
+
| Blockchain contract | Holds the agreed ERC-20 token and enforces the supported signed settlement rules |
|
|
49
|
+
| RPC provider | Reads chain state and sends signed transactions to the selected network |
|
|
50
|
+
|
|
51
|
+
Installing the package does not deploy a contract, open API endpoints, create wallets or start an AI worker. Mount the handler, configure your services and run your operational worker explicitly. Public identity derivation, including `@allorc/identity`, is not authentication or permission to spend.
|
|
52
|
+
|
|
53
|
+
## Install the library
|
|
54
|
+
|
|
55
|
+
Use **Node.js 22.13+**; Node 24 is the tested runtime. SQLite uses persistent local disk. Run the SDK on the backend, not inside a browser bundle.
|
|
56
|
+
|
|
57
|
+
### Published package
|
|
6
58
|
|
|
7
59
|
```sh
|
|
8
|
-
npm
|
|
60
|
+
npm view @allorc/a423-sdk version
|
|
61
|
+
npm view @allorc/a423-sdk@0.8.1 version
|
|
9
62
|
```
|
|
10
63
|
|
|
11
|
-
|
|
64
|
+
If the required version is published:
|
|
12
65
|
|
|
13
|
-
|
|
66
|
+
```sh
|
|
67
|
+
npm install @allorc/a423-sdk@0.8.1 ethers@^6.15.0
|
|
68
|
+
# Optional: only if you need PDF receipts
|
|
69
|
+
npm install pdf-lib@^1.17.1
|
|
70
|
+
```
|
|
14
71
|
|
|
15
|
-
|
|
16
|
-
|---|---|
|
|
17
|
-
| `/workflow` | Versioned terms, bilateral acceptance, role guards, CAS state/events, pending operations, explicit observe/resume and safety holds |
|
|
18
|
-
| `/escrow` | Reference ERC-20 contract, full custody lifecycle, EOA/ERC-1271 worker consent, strict chain evidence, managed signing/delegation, allowlisted deployment registry, bounded fee replacements and incremental log indexing |
|
|
19
|
-
| `/storage` | Single-host SQLite workflow store, transaction/replacement journal, atomic nonce reservations, leased queue/checkpoints, spending reservations and authority/event storage |
|
|
20
|
-
| `/authority` | Operator-approved organization registry, organization-signed agent enrollment, single-use challenges, signed audience-bound bearer credentials, revocation, public-key verification and renewal |
|
|
21
|
-
| `/operations` | Typed task/evidence validation, spending authorizer, maintenance/keeper ticks, durable execution dispatcher, fresh receipts/PDF, reorg quarantine, SSE, webhook outbox, safe migration planning and app/chat integration handler |
|
|
22
|
-
| Root | Preserved original simple offer/accept/submit/approve API; [legacy documentation](./LEGACY.md) |
|
|
72
|
+
`ethers` is needed for escrow/chain functionality. It is optional for the core workflow and pure policy calculator. `pdf-lib` is optional for PDF rendering. See the [npm package and available versions](https://www.npmjs.com/package/@allorc/a423-sdk?activeTab=versions).
|
|
23
73
|
|
|
24
|
-
|
|
74
|
+
### Before publication: build an installable tarball
|
|
25
75
|
|
|
26
|
-
|
|
76
|
+
From a checkout containing the required version, or a source archive supplied by the maintainer:
|
|
27
77
|
|
|
28
|
-
|
|
78
|
+
```sh
|
|
79
|
+
git clone https://github.com/Allorc-official/A423-SDK.git
|
|
80
|
+
cd A423-SDK
|
|
81
|
+
node -p "require('./package.json').version"
|
|
82
|
+
npm ci --ignore-scripts
|
|
83
|
+
npm pack
|
|
84
|
+
```
|
|
29
85
|
|
|
30
|
-
|
|
86
|
+
For this version, `npm pack` creates `allorc-a423-sdk-0.8.1.tgz`. Install that file in your application:
|
|
31
87
|
|
|
32
|
-
|
|
88
|
+
```sh
|
|
89
|
+
npm install /absolute/path/allorc-a423-sdk-0.8.1.tgz ethers@^6.15.0
|
|
90
|
+
```
|
|
33
91
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
92
|
+
Keep the tarball or pin a source commit for reproducible evaluation. If the GitHub branch has not received the latest commit, cloning it cannot provide that commit's features. Never assume an npm update, GitHub push and contract deployment are the same operation.
|
|
93
|
+
|
|
94
|
+
## Try it without a blockchain
|
|
95
|
+
|
|
96
|
+
This example uses **in-memory storage** and moves **no money**. It demonstrates bilateral agreement and the optional pure policy calculator. Reference escrow jobs use the richer agreement schema shown later.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
import {createMemoryStore} from '@allorc/a423-sdk';
|
|
100
|
+
import {createWorkflow} from '@allorc/a423-sdk/workflow';
|
|
101
|
+
import {createSettlementPolicy} from '@allorc/a423-sdk/policy';
|
|
102
|
+
|
|
103
|
+
const workflow = createWorkflow({store: createMemoryStore()});
|
|
104
|
+
const id = 'research-001';
|
|
105
|
+
await workflow.create({
|
|
106
|
+
id,
|
|
107
|
+
participants: {requester: 'buyer', worker: 'researcher', verifier: 'reviewer'},
|
|
108
|
+
}, 'buyer');
|
|
109
|
+
|
|
110
|
+
let job = await workflow.act(id, 'quote', {
|
|
111
|
+
terms: {description: 'Prepare a research brief', amount_raw: '200'},
|
|
112
|
+
}, {actor: 'researcher', idempotencyKey: 'quote-1'});
|
|
113
|
+
|
|
114
|
+
const acceptance = {
|
|
115
|
+
revision: job.agreement.revision,
|
|
116
|
+
agreement_hash: job.agreement.hash,
|
|
117
|
+
};
|
|
118
|
+
await workflow.act(id, 'accept', acceptance, {
|
|
119
|
+
actor: 'buyer', idempotencyKey: 'buyer-accept-1',
|
|
120
|
+
});
|
|
121
|
+
job = await workflow.act(id, 'accept', acceptance, {
|
|
122
|
+
actor: 'researcher', idempotencyKey: 'worker-accept-1',
|
|
45
123
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
124
|
+
|
|
125
|
+
const plan = createSettlementPolicy().evaluate({
|
|
126
|
+
cost_basis_raw: '100', budget_raw: '300', escrow_raw: '300',
|
|
127
|
+
});
|
|
128
|
+
console.log(job.state); // accepted; no funding has occurred
|
|
129
|
+
console.log(plan.target_payout); // 200: 50% gross-margin target
|
|
130
|
+
console.log(plan.recommended_payout);// 200: fits budget and escrow caps
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Save as `demo.mjs` and run `node demo.mjs` after installing the required SDK version. In the source repository, the same example is available as:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
node examples/quickstart.mjs
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Calling `fund` on this minimal workflow fails because no funding adapter is configured. Use the application factory below for the reference escrow implementation.
|
|
140
|
+
|
|
141
|
+
## Configure an escrow application
|
|
142
|
+
|
|
143
|
+
First deploy a compatible reference contract on your chosen EVM network with its ERC-20 token address. Then configure the SDK against that deployment. The 0.8.1 SDK reuses the **0.5 reference contract**; installing a new npm version does not upgrade an older immutable contract.
|
|
144
|
+
|
|
145
|
+
The following is a backend wiring example. `authority`, `walletRegistry`, `approvals`, `artifacts`, `workMeter` and `budgets` are **your application's services**, not additional SDK exports. See [OPERATIONS.md](./OPERATIONS.md) for authority enrollment, protected signing and execution setup.
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
import {JsonRpcProvider} from 'ethers';
|
|
149
|
+
import {createSqliteStorage} from '@allorc/a423-sdk/storage';
|
|
150
|
+
import {createEscrowApplication} from '@allorc/a423-sdk/operations';
|
|
151
|
+
|
|
152
|
+
const storage = createSqliteStorage({filename: './private/a423.sqlite'});
|
|
153
|
+
const provider = new JsonRpcProvider(process.env.A423_RPC_URL);
|
|
154
|
+
|
|
155
|
+
const app = createEscrowApplication({
|
|
156
|
+
storage,
|
|
157
|
+
authority, // Your configured createAuthority(...) instance
|
|
158
|
+
escrows: [{
|
|
159
|
+
provider,
|
|
160
|
+
chainId: Number(process.env.A423_CHAIN_ID),
|
|
161
|
+
contractAddress: process.env.A423_ESCROW_ADDRESS,
|
|
162
|
+
tokenAddress: process.env.A423_TOKEN_ADDRESS,
|
|
163
|
+
confirmations: 2,
|
|
164
|
+
}],
|
|
165
|
+
signerFor: async actor => walletRegistry.signerFor(actor),
|
|
166
|
+
addressFor: async actor => walletRegistry.addressFor(actor),
|
|
167
|
+
limitsFor: async () => ({
|
|
168
|
+
perTransaction: '1000000', daily: '5000000', total: '20000000',
|
|
169
|
+
}),
|
|
170
|
+
approveSigning: async context => approvals.allowSigning(context),
|
|
171
|
+
validateArtifact: async context => artifacts.validate(context),
|
|
172
|
+
|
|
173
|
+
// Optional: remove this block to leave profit policy disabled.
|
|
174
|
+
settlementPolicy: {
|
|
175
|
+
thresholds: {
|
|
176
|
+
target_margin_bps: 5000, // Aim for 50% gross margin
|
|
177
|
+
min_margin_bps: 1000, // Below 10% needs an owner exception
|
|
178
|
+
max_margin_bps: 5000, // Do not ask above the 50% threshold
|
|
179
|
+
require_counter: true,
|
|
180
|
+
},
|
|
181
|
+
costBasisFor: async job => workMeter.costBasisRaw(job.id),
|
|
182
|
+
budgetFor: async job => budgets.maximumRaw(job.id),
|
|
183
|
+
approveException: async context => approvals.ownerException(context),
|
|
184
|
+
},
|
|
185
|
+
keeperEnabled: false,
|
|
186
|
+
forwardCancellations: true,
|
|
187
|
+
requireIndependentArbitrator: true,
|
|
50
188
|
});
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
189
|
+
|
|
190
|
+
export const {workflow, handler, applicationOptions, workerOptions} = app;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
All money values above are **raw integer token units**, not dollars or decimal token amounts. Choose limits for your own token's decimals and spending policy. Parent directories and the SQLite/WAL files must be on protected persistent local storage; the driver is not a distributed database or an ephemeral serverless disk substitute.
|
|
194
|
+
|
|
195
|
+
You can register multiple network/contract/token deployments through `escrows`. Each job is bound to its agreed deployment. Switching RPC URLs never moves an existing job's funds to another chain. The reference adapters check the RPC chain ID, escrow token and agreement fields before acting. Connection/eligibility checks occur when used; initialization is not proof that a contract was deployed or funded.
|
|
196
|
+
|
|
197
|
+
## Configuration arguments
|
|
198
|
+
|
|
199
|
+
### Application factory: `createEscrowApplication(options)`
|
|
200
|
+
|
|
201
|
+
| Argument | Required / default | Meaning |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| `storage` | Required | Durable jobs, transaction journal, nonce reservations, queue, spending and private KV |
|
|
204
|
+
| `authority` | Required | Configured authority with registered, active agents and credential verification |
|
|
205
|
+
| `escrows` | Required; nonempty | Allowlist of supported chain deployments; fields below |
|
|
206
|
+
| `signerFor(actor)` | Required callback | Returns the authorized wallet/signer for an actor; reject unknown actors |
|
|
207
|
+
| `addressFor(actor)` | Required callback | Returns that actor's trusted wallet address |
|
|
208
|
+
| `limitsFor(context)` | Required callback | Raw-token `perTransaction`, `daily`, `total` funding caps |
|
|
209
|
+
| `approveSigning(context)` | Required callback | Explicitly permits or denies a concrete signing/broadcast intent |
|
|
210
|
+
| `validateArtifact(context)` | Required for verification | Checks the submitted artifact commitment against accepted criteria |
|
|
211
|
+
| `execute(context)` | Optional | Your actual work runner; returns a real `result_hash` and honors its abort signal |
|
|
212
|
+
| `settlementPolicy` | Disabled when omitted | Enables the optional cost/profit policy described below |
|
|
213
|
+
| `keeperEnabled` | `false` | Allows the operational worker to execute eligible maintenance actions when run |
|
|
214
|
+
| `forwardCancellations` | `true` | Saves accepted cancellation JSON, then attempts escrow execution; `false` stages it only |
|
|
215
|
+
| `requireIndependentArbitrator` | `true` | Requires arbiter wallet to differ from payer, worker and verifier |
|
|
216
|
+
| `allowAuthorityRequest(request)` | Deny by default | Explicit request policy for optional authority challenge/exchange routes |
|
|
217
|
+
|
|
218
|
+
The factory returns `workflow`, `handler`, `applicationOptions`, `workerOptions`, `chatTools(actor)` and an optional `profit` service. No worker is scheduled just because `keeperEnabled` is set. Run [scripts/operations-worker.mjs](./scripts/operations-worker.mjs) with your exported configuration.
|
|
219
|
+
|
|
220
|
+
### Each entry in `escrows`
|
|
221
|
+
|
|
222
|
+
| Argument | Meaning |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `provider` | An ethers provider connected to the chosen network's RPC |
|
|
225
|
+
| `chainId` | Positive numeric network ID; testnet and mainnet have different IDs |
|
|
226
|
+
| `contractAddress` | Address of the compatible deployed escrow contract |
|
|
227
|
+
| `tokenAddress` | ERC-20 address configured in that contract |
|
|
228
|
+
| `confirmations` | Positive number of blocks required before the adapter confirms chain evidence |
|
|
229
|
+
|
|
230
|
+
Environment variable names in the example are an application convention. The SDK receives explicit values; it does not automatically discover a testnet, mainnet, RPC URL or token.
|
|
231
|
+
|
|
232
|
+
### Profit policy initialization
|
|
233
|
+
|
|
234
|
+
| Argument | Default / meaning |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `thresholds.target_margin_bps` | `5000`: desired gross margin of 50% |
|
|
237
|
+
| `thresholds.min_margin_bps` | `1000`: minimum normal gross margin of 10% |
|
|
238
|
+
| `thresholds.max_margin_bps` | `5000`: highest permitted payout threshold, corresponding to 50% margin |
|
|
239
|
+
| `thresholds.require_counter` | `true`: worker must counter a first requester offer before accepting it; requester can accept the worker's own ask |
|
|
240
|
+
| `costBasisFor(job)` | Required when enabled; returns completed-work cost plus agreed expenses as a raw-unit string |
|
|
241
|
+
| `budgetFor(job)` | Required when enabled; returns the original requester cap as a raw-unit string |
|
|
242
|
+
| `approveException(context)` | Optional trusted callback returning `{approved: true, approval_id: 'decision-id'}`; absent means no below-minimum exception |
|
|
243
|
+
| `canViewPrivate({job, actor})` | Optional; allows an already authorized operator to see worker economics; default denies extra viewers |
|
|
244
|
+
| `policy` | Alternative `createSettlementPolicy(...)` object; do not supply it together with `thresholds` |
|
|
245
|
+
|
|
246
|
+
**100 basis points = 1%.** Settings must satisfy `0 <= minimum <= target <= maximum < 10000`. For a 30% target, 5% minimum and 40% maximum, pass `3000`, `500` and `4000`. Percentages apply to **revenue**, not cost. Unknown option names, invalid types and inconsistent thresholds fail at initialization.
|
|
247
|
+
|
|
248
|
+
Each instance captures its effective settings. Inspect `app.profit.configuration`; mutating the original options object afterward does not change the policy. Already frozen negotiations retain their recorded cost/budget/configuration snapshot. Owner exceptions never override budget, escrow or maximum payout caps, and an agent-supplied approval flag is not owner authorization.
|
|
249
|
+
|
|
250
|
+
## Understand target, minimum and maximum
|
|
251
|
+
|
|
252
|
+
With the default **50% target / 10% minimum / 50% maximum**:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
profit = worker payout − cost basis
|
|
256
|
+
gross margin = profit / worker payout
|
|
257
|
+
target payout = ceil(cost basis / (1 − target margin))
|
|
258
|
+
minimum payout = ceil(cost basis / (1 − minimum margin))
|
|
259
|
+
affordable = min(requester budget, escrow amount)
|
|
260
|
+
recommendation = min(target payout, affordable)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
A 50% gross margin means **payout = 2 × cost**. A 50% markup instead means 1.5 × cost; these are different calculations. The policy uses gross margin.
|
|
264
|
+
|
|
265
|
+
For cost basis **100 raw units**:
|
|
266
|
+
|
|
267
|
+
| Budget and escrow availability | Recommended worker amount | Result |
|
|
268
|
+
|---|---:|---|
|
|
269
|
+
| At least 200 | 200 | Profit 100; target 50% margin |
|
|
270
|
+
| 150 | 150 | Profit 50; below target, above the minimum |
|
|
271
|
+
| 105 | 105 | Below minimum payout of 112; owner approval required |
|
|
272
|
+
| 80 | 80, blocked without approval | Below cost; decline or seek explicit owner approval |
|
|
273
|
+
| Cost basis is 0 | 0 | Full payer refund is appropriate |
|
|
274
|
+
|
|
275
|
+
A recommendation is **not** authorization, acceptance or payment. Theoretical `target_payout`, `min_payout` and `max_payout` may exceed the budget. `recommended_payout` is the capped amount; the guard still rejects an unacceptable recommendation until an explicit permitted exception exists.
|
|
276
|
+
|
|
277
|
+
| Policy/projection field | Meaning |
|
|
278
|
+
|---|---|
|
|
279
|
+
| `cost_basis_raw` | App-reported cost snapshot; not audited on-chain cost truth |
|
|
280
|
+
| `target_payout` | Amount needed for the desired gross margin, rounded up |
|
|
281
|
+
| `min_payout` | Amount needed for the minimum margin, rounded up |
|
|
282
|
+
| `max_payout` | Upper payout threshold derived from maximum margin |
|
|
283
|
+
| `affordable` | Lesser of budget and escrow |
|
|
284
|
+
| `recommended_payout` | Capped default worker proposal |
|
|
285
|
+
| `expected_profit`, `expected_margin_bps` | Economics of the recommendation |
|
|
286
|
+
| `projected_profit_raw`, `projected_margin_bps` | Economics of the current proposed allocation |
|
|
287
|
+
| `realized_payout_raw`, `realized_profit_raw`, `realized_margin_bps` | Actual result; null until settlement evidence freshly revalidates |
|
|
288
|
+
| `target_status` | `target_met` or `below_target`; advisory, based on the app's cost basis |
|
|
289
|
+
|
|
290
|
+
Costs freeze at the first closing proposal attempt, not on every counteroffer. Pure helpers do not sign or broadcast. Zero payout has undefined margin (`null`), and displayed basis points are conservatively rounded down. The backend keeps costs/profit private to the worker and explicitly permitted operators; requester projections expose allocation and confirmation status only.
|
|
291
|
+
|
|
292
|
+
## Define a job agreement
|
|
293
|
+
|
|
294
|
+
The application factory validates a typed task and reference escrow terms. A quote supplies `terms`:
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
const terms = {
|
|
298
|
+
task: {
|
|
299
|
+
id: 'brief',
|
|
300
|
+
type: 'research',
|
|
301
|
+
description: 'Produce a sourced research brief',
|
|
302
|
+
deliverables: [{id: 'report', media_type: 'text/markdown', description: 'Research brief'}],
|
|
303
|
+
acceptance: {validator: 'brief-checker', version: '1', criteria: {minSources: 3}},
|
|
304
|
+
},
|
|
305
|
+
escrow: {
|
|
306
|
+
chain_id: String(chainId), contract: contractAddress, token: tokenAddress,
|
|
307
|
+
amount: '100', // Raw token units
|
|
308
|
+
payer: payerAddress, worker: workerAddress,
|
|
309
|
+
verifier: verifierAddress, arbitrator: arbitratorAddress,
|
|
310
|
+
work_deadline: workDeadline,
|
|
311
|
+
review_deadline: reviewDeadline,
|
|
312
|
+
resolution_deadline: resolutionDeadline,
|
|
313
|
+
timeout_worker_bps: 2500,
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
| Field | Meaning |
|
|
319
|
+
|---|---|
|
|
320
|
+
| Job `id` | Stable workflow identifier; keep it instead of inventing/reconstructing IDs later |
|
|
321
|
+
| `participants` | Authenticated requester, worker, verifier and arbitrator **agent IDs**; not wallet addresses |
|
|
322
|
+
| `task.type` / `description` | Your use case; no research-only task logic is hardcoded |
|
|
323
|
+
| `task.deliverables` | Required artifacts, unique IDs, media types and descriptions |
|
|
324
|
+
| `task.acceptance` | Named/versioned validator and criteria used by your actual validation callback |
|
|
325
|
+
| `escrow.chain_id` | Decimal-string chain ID; factory configuration uses a number |
|
|
326
|
+
| `escrow.contract` / `token` | The agreed deployment and ERC-20 token |
|
|
327
|
+
| `escrow.amount` | Positive raw-unit decimal string; no floats or scientific notation |
|
|
328
|
+
| `payer`, `worker`, `verifier`, `arbitrator` | Agreed wallet addresses mapped from the participant registry |
|
|
329
|
+
| `work_deadline` | Unix seconds; last time permitted for submission |
|
|
330
|
+
| `review_deadline` | Unix seconds; last time the verifier may approve the submitted result |
|
|
331
|
+
| `resolution_deadline` | Unix seconds; deadline after which the contract's timeout exit becomes available |
|
|
332
|
+
| `timeout_worker_bps` | Agreed worker share after a submitted-job timeout; 2500 means 25%, not a profit target |
|
|
333
|
+
|
|
334
|
+
Require `work_deadline < review_deadline < resolution_deadline`. Exact eligibility depends on the action and reference contract; see [ESCROW.md](./ESCROW.md). **Profit margin, timeout share and spending limits are separate settings.**
|
|
335
|
+
|
|
336
|
+
Quotes receive a canonical agreement hash and monotonic revision. Both parties accept that exact returned pair. Requoting clears acceptances. Use returned terms/hashes, particularly after reopening; do not invent a hash from the original input or confuse the workflow ID with the on-chain bytes32 job ID.
|
|
337
|
+
|
|
338
|
+
## End-to-end job lifecycle
|
|
339
|
+
|
|
340
|
+
```mermaid
|
|
341
|
+
flowchart TD
|
|
342
|
+
Draft["draft: create job"] --> Quoted["quoted: propose terms"]
|
|
343
|
+
Quoted --> Accepted["accepted: both parties approve revision and hash"]
|
|
344
|
+
Accepted --> Funded["funded: confirmed custody"]
|
|
345
|
+
Funded --> Running["running: fresh eligibility gate"]
|
|
346
|
+
Running --> Submitted["submitted: confirmed artifact commitment"]
|
|
347
|
+
Submitted --> Verified["verified: artifact checks and chain approval"]
|
|
348
|
+
Verified --> Paid["paid: confirmed worker payout"]
|
|
349
|
+
Draft --> Cancelled["cancelled: no SDK funding attempt"]
|
|
350
|
+
Quoted --> Cancelled
|
|
351
|
+
Accepted --> Cancelled
|
|
352
|
+
Funded --> Refunded["refunded: expired without submission"]
|
|
353
|
+
Running --> Refunded
|
|
354
|
+
Submitted --> Disputed["disputed: settlement requires resolution"]
|
|
355
|
+
Verified --> Disputed
|
|
356
|
+
Disputed --> Settled["settled: arbitration or eligible timeout"]
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The chart shows common completion and dispute paths; funded/running jobs also support direct disputes and closing negotiation.
|
|
360
|
+
|
|
361
|
+
Use `workflow.act(id, action, input, {actor, idempotencyKey})`, or the authenticated client/HTTP equivalent. **Proceed to the next action only when `job.pending` is null and the expected state/evidence is present.**
|
|
362
|
+
|
|
363
|
+
| Step | Action / actor | Input and actual outcome |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| 1 | `workflow.create` / requester | `id`, participants, optional public metadata; creates `draft` |
|
|
366
|
+
| 2 | `quote` / requester or worker | `{terms}`; creates a revision/hash and clears previous acceptances |
|
|
367
|
+
| 3 | `accept` / each party | `{revision, agreement_hash}`; only bilateral acceptance produces `accepted` |
|
|
368
|
+
| 4 | Worker wallet consent | Sign `workerConsent(job, deployment)` typed data, using the agreed worker wallet |
|
|
369
|
+
| 5 | `fund` / requester | `{worker_signature}`; token allowance and funding may be separate journaled steps; only confirmed custody is `funded` |
|
|
370
|
+
| 6 | `start` / worker | Fresh chain/safety gate, then `running`; application worker performs actual work |
|
|
371
|
+
| 7 | `submit` / worker or runner | `{result_hash}` for actual artifact bytes; one immutable on-chain submission |
|
|
372
|
+
| 8 | `verify` / verifier | Validate artifact against criteria and confirm approval; then `verified` |
|
|
373
|
+
| 9 | `release` / verifier | Confirm the contract's worker transfer; then `paid` |
|
|
374
|
+
| 10 | Read receipts | Revalidate chain evidence before presenting payment/refund as current fact |
|
|
375
|
+
|
|
376
|
+
Wallet helpers are exported from `@allorc/a423-sdk/escrow`:
|
|
377
|
+
|
|
378
|
+
```js
|
|
379
|
+
import {workerConsent} from '@allorc/a423-sdk/escrow';
|
|
380
|
+
const typed = workerConsent(job, deployment);
|
|
381
|
+
const workerSignature = await workerWallet.signTypedData(
|
|
382
|
+
typed.domain, typed.types, typed.value,
|
|
383
|
+
);
|
|
384
|
+
// Use workerSignature as fund.input.worker_signature through the payer's authorized action.
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
`workerWallet` is your wallet integration, not a server-generated default key. With a browser wallet, use the authenticated worker-consent endpoint and wallet's typed-data signing UI. Protect private keys and keep authority keys separate from chain signers.
|
|
388
|
+
|
|
389
|
+
## Cancellation and partial-work settlement
|
|
390
|
+
|
|
391
|
+
After funding, ordinary `cancel` cannot simply unlock the money. Instead negotiate a **closing settlement**: worker compensation for agreed partial work/expenses, with the remainder refunded. This closes the entire job; it is not a continuing-work milestone payment.
|
|
392
|
+
|
|
393
|
+
```mermaid
|
|
394
|
+
flowchart TD
|
|
395
|
+
Active["Funded, running, submitted or verified job"] --> Proposal["Publish closing proposal; pause work"]
|
|
396
|
+
Proposal --> Decision{"Agreement on the split?"}
|
|
397
|
+
Decision -->|"Counter"| Counter["New revision and proposal hash"]
|
|
398
|
+
Counter --> Decision
|
|
399
|
+
Decision -->|"Approve"| Sign["Both wallets sign exact current terms"]
|
|
400
|
+
Sign --> Instruction["Save accepted cancellation JSON"]
|
|
401
|
+
Instruction --> Execute["Encode and send escrow transaction"]
|
|
402
|
+
Execute --> Confirm["Confirm actual payout and refund"]
|
|
403
|
+
Confirm --> Receipt["Final cancellation receipt; locked amount zero"]
|
|
404
|
+
Decision -->|"Dispute"| Arbiter["Arbitrator resolves supported split"]
|
|
405
|
+
Arbiter --> Confirm
|
|
406
|
+
Decision -->|"Original deadline reached"| Deadline["Eligible refund or timeout transaction"]
|
|
407
|
+
Deadline --> Confirm
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
For a funded 100-unit job, an agreed worker allocation of 30 means **30 to the worker, 70 to the payer**, once confirmed. JSON is an instruction artifact; the contract validates typed signatures and amounts, not arbitrary JSON.
|
|
411
|
+
|
|
412
|
+
```js
|
|
413
|
+
import {settlementConsent} from '@allorc/a423-sdk/escrow';
|
|
414
|
+
// Worker proposes a closing settlement. If the factory's profit policy is enabled,
|
|
415
|
+
// omit worker_amount to use its capped default instead of an explicit '30'.
|
|
416
|
+
// An explicit amount must still satisfy the enabled policy.
|
|
417
|
+
let job = await app.workflow.act(id, 'propose_settlement', {
|
|
418
|
+
worker_amount: '30',
|
|
419
|
+
expected_revision: 0,
|
|
420
|
+
expires_at: proposalExpiry, // Must respect the original resolution deadline
|
|
421
|
+
reason: 'Source collection is complete; remaining work is cancelled',
|
|
422
|
+
claims: [{kind: 'work', description: 'Checked source list', evidence: ['artifact:sources']}],
|
|
423
|
+
}, {actor: workerId, idempotencyKey: 'closing-proposal-1'});
|
|
424
|
+
// Wait/reconcile if job.pending before requesting signatures.
|
|
425
|
+
|
|
426
|
+
const closing = settlementConsent(job, deployment);
|
|
427
|
+
// Collect both wallet signatures over closing.domain/types/value.
|
|
428
|
+
job = await app.workflow.acceptCancellation(id, {
|
|
429
|
+
expected_revision: job.negotiation.current.escrow_version,
|
|
430
|
+
proposal_hash: job.negotiation.current.proposal_hash,
|
|
431
|
+
payer_signature: payerSignature,
|
|
432
|
+
worker_signature: workerSignature,
|
|
433
|
+
}, {actor: payerId, idempotencyKey: 'closing-accept-1', forward: false});
|
|
434
|
+
|
|
435
|
+
const instruction = await app.workflow.cancellationInstruction(id, payerId);
|
|
436
|
+
// Acceptance is now recorded, but tokens are still locked.
|
|
437
|
+
job = await app.workflow.act(id, 'execute_cancellation', {
|
|
438
|
+
instruction_id: instruction.id,
|
|
439
|
+
}, {actor: payerId, idempotencyKey: 'closing-execute-1'});
|
|
440
|
+
// After confirmed completion, use a fresh receipt to show the actual split.
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Import `settlementConsent` from `/escrow`. Counteroffers use `counter_settlement` with the current `expected_revision`. Claims describe work and agreed expenses; never put private AI usage/cost/margin data in public claims or peer messages.
|
|
444
|
+
|
|
445
|
+
The subflow distinguishes **negotiating → accepted instruction → execution pending/failed → completed receipt**. Accepted JSON permanently says `funds_status: 'not_executed'`; the separate final receipt proves execution. The default `acceptCancellation` forwards after acceptance; `forward:false` separates the steps. The SDK blocks counters after a saved acceptance. On-chain stale revisions, expired signatures or changed proposals cannot authorize the old split.
|
|
446
|
+
|
|
447
|
+
If parties disagree, original deadline exits and supported arbitration remain available. Unsubmitted work can reach an expiry refund; submitted work uses its agreed timeout allocation. Neither the keeper nor a deadline guarantees inclusion during chain censorship, RPC failure or broken token transfers. See [CANCELLATION.md](./CANCELLATION.md) and [SETTLEMENT.md](./SETTLEMENT.md).
|
|
448
|
+
|
|
449
|
+
## Decline, cancel and reopen before funding
|
|
450
|
+
|
|
451
|
+
Cancelled jobs keep their IDs and history. The requester uses `cancel`; the worker uses `decline`. Both are allowed from `draft`, `quoted` or `accepted`, **only before an SDK funding attempt**. Further quotes/acceptance are rejected until explicit eligible reopening.
|
|
452
|
+
|
|
453
|
+
```mermaid
|
|
454
|
+
flowchart TD
|
|
455
|
+
Cancelled["Cancelled job with retained history"] --> Eligible{"Any funding attempt or financial close?"}
|
|
456
|
+
Eligible -->|"Yes"| Terminal["Do not reopen; reconcile or create a new linked job"]
|
|
457
|
+
Eligible -->|"No"| Approvals{"Worker declined or both had accepted?"}
|
|
458
|
+
Approvals -->|"Yes"| Both["Requester and worker approve reopening"]
|
|
459
|
+
Approvals -->|"No"| Requester["Requester approves reopening"]
|
|
460
|
+
Both --> New["Same job ID; new generation; active agreement cleared"]
|
|
461
|
+
Requester --> New
|
|
462
|
+
New --> Fresh["Fresh quote and fresh bilateral acceptance"]
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
```js
|
|
466
|
+
let job = await app.workflow.act(id, 'decline', {
|
|
467
|
+
reason: 'Cannot meet the proposed budget', expected_epoch: currentJob.epoch || 0,
|
|
468
|
+
}, {actor: workerId, idempotencyKey: 'decline-1'});
|
|
469
|
+
|
|
470
|
+
const cancellation_id = job.pre_funding_cancellations.at(-1).id;
|
|
471
|
+
// For a worker decline, collect both approvals, using separate authenticated actors.
|
|
472
|
+
await app.workflow.act(id, 'reopen', {cancellation_id}, {
|
|
473
|
+
actor: payerId, idempotencyKey: 'reopen-payer-1',
|
|
54
474
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
authenticate, revalidate, consentFor,
|
|
475
|
+
job = await app.workflow.act(id, 'reopen', {cancellation_id}, {
|
|
476
|
+
actor: workerId, idempotencyKey: 'reopen-worker-1',
|
|
58
477
|
});
|
|
59
|
-
//
|
|
478
|
+
// Next quote/accept/fund/cancel/decline must include expected_epoch: job.epoch.
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
A requester cancellation before bilateral acceptance can be reopened by the requester alone. Both-party acceptance requires both reopening approvals. Reopening never restores old acceptances: it increments `epoch`, keeps quote revisions monotonic and commits the new generation in returned agreement terms. Old completed idempotency keys cannot execute again.
|
|
482
|
+
|
|
483
|
+
A pending or even rejected funding attempt blocks this shortcut, because signed intent may exist outside a cached state. Paid/refunded/settled jobs remain final; repeat work with a new job ID linked through your app's metadata. Off-chain cancellation cannot erase already shared wallet signatures; old externally funded agreements must be reconciled under their original terms.
|
|
484
|
+
|
|
485
|
+
## Use it from Next.js or another agent
|
|
486
|
+
|
|
487
|
+
Mount your configured handler using Next.js's **Node runtime**. The application handler serves more than `/api/a423/*`; use a catch-all covering all its paths, or mount the additional routes explicitly.
|
|
488
|
+
|
|
489
|
+
```ts
|
|
490
|
+
// app/api/[...path]/route.ts — adjust import to your configured backend module.
|
|
491
|
+
// If you have existing API routes, mount only the intended A423 paths there.
|
|
492
|
+
import {handler} from '@/lib/a423';
|
|
60
493
|
export const runtime = 'nodejs';
|
|
61
|
-
export
|
|
494
|
+
export const GET = handler;
|
|
495
|
+
export const POST = handler;
|
|
62
496
|
```
|
|
63
497
|
|
|
64
|
-
|
|
498
|
+
Existing more-specific routes such as `/api/state` need their own integration; a catch-all does not replace them. Use the SDK's single-host SQLite driver only where persistent local disk is available; choose an appropriately implemented durable store for other hosting models.
|
|
65
499
|
|
|
66
|
-
|
|
500
|
+
External agents call your backend with credentials; they do not need database access or private signer keys:
|
|
67
501
|
|
|
68
|
-
|
|
502
|
+
```js
|
|
503
|
+
import {createWorkflowClient} from '@allorc/a423-sdk/workflow';
|
|
69
504
|
|
|
70
|
-
|
|
505
|
+
const client = createWorkflowClient({
|
|
506
|
+
baseUrl: 'https://your-app.example/api/a423',
|
|
507
|
+
headers: async () => ({authorization: `Bearer ${await credentialForThisAgent()}`}),
|
|
508
|
+
});
|
|
509
|
+
const job = await client.get('research-001');
|
|
510
|
+
// The credential establishes the actor; a caller-supplied ID is not authentication.
|
|
511
|
+
```
|
|
71
512
|
|
|
72
|
-
|
|
513
|
+
| Endpoint | Purpose |
|
|
514
|
+
|---|---|
|
|
515
|
+
| `POST /api/a423/jobs` | Create draft job |
|
|
516
|
+
| `GET /api/a423/jobs/:id` | Read an accessible job, including cancelled history |
|
|
517
|
+
| `POST /api/a423/jobs/:id/actions` | `{action, input}` with `idempotency-key` |
|
|
518
|
+
| `POST /api/a423/jobs/:id/reconcile` | Observe the current pending operation |
|
|
519
|
+
| `GET /api/a423/jobs/:id/subflows` | Inspect child subflows |
|
|
520
|
+
| `POST /api/a423/jobs/:id/cancellation/accept` | Save exact signed acceptance and optionally forward |
|
|
521
|
+
| `POST /api/a423/jobs/:id/cancellation/execute` | Execute a saved accepted instruction |
|
|
522
|
+
| `GET /api/a423/jobs/:id/cancellation/instruction` | Read the accepted instruction, not proof of payment |
|
|
523
|
+
| `GET /api/a423/jobs/:id/cancellation/receipt` | Confirmed cancellation receipt; application handler freshly revalidates it |
|
|
524
|
+
| `GET /api/a423-worker-consent/:id` | Worker funding typed-data payload |
|
|
525
|
+
| `GET /api/a423-settlement-consent/:id` | Current closing typed-data payload |
|
|
526
|
+
| `GET /api/a423-profit/:id` | Role-filtered economics; requires configured profit policy |
|
|
527
|
+
| `GET /api/state` | Accessible workflow and wallet-history projections |
|
|
528
|
+
| `GET /api/a423-receipts/:id[.pdf]` | Fresh JSON/PDF receipt projection |
|
|
529
|
+
| `GET /api/a423-events` | Authenticated resumable SSE events |
|
|
73
530
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
531
|
+
Credentials come from the configured authority's approved organization/agent enrollment and challenge flow. They expire and can be revoked; use the renewal helpers in [OPERATIONS.md](./OPERATIONS.md). Do not use public agent IDs, an `x-agent-id` header or demo shared keys as production authentication.
|
|
532
|
+
|
|
533
|
+
`app.chatTools(actor)` exposes the same workflow plus `list_jobs`, `inspect_settlement_policy` and `reconcile_job`. Inject `app.profit.prompt({role:'worker'})` when policy is enabled; the SDK does not rewrite a remote LangGraph/LLM system prompt automatically. Actual AI execution still belongs to your application runner.
|
|
534
|
+
|
|
535
|
+
## Retries, receipts and recovery
|
|
536
|
+
|
|
537
|
+
| Argument / concept | Meaning |
|
|
538
|
+
|---|---|
|
|
539
|
+
| `actor` | Trusted authenticated agent ID; backend calls must bind it to credentials |
|
|
540
|
+
| `idempotencyKey` / `idempotency-key` | Stable key for one actor's exact action/input; keep it on retry, use a new key for new intent |
|
|
541
|
+
| `revision`, `agreement_hash` | Exact returned quote revision/hash for acceptance |
|
|
542
|
+
| `expected_revision` | Existing on-chain closing revision before a proposal/counter; current proposal revision when accepting |
|
|
543
|
+
| `proposal_hash` | Commitment to the exact closing proposal and claims |
|
|
544
|
+
| `worker_amount`, `payer_amount` | Raw allocations summing to escrow; payer amount is derived unless supplied for checking |
|
|
545
|
+
| `expires_at` | Proposal expiry in Unix seconds; cannot extend original resolution deadline |
|
|
546
|
+
| `result_hash` | Nonzero bytes32 commitment to actual artifact bytes |
|
|
547
|
+
| `reason_hash` | Nonzero bytes32 dispute-reason commitment |
|
|
548
|
+
| `expected_epoch` | Current reopening generation; required after reopening for quote/accept/fund/cancel/decline |
|
|
549
|
+
| `cancellation_id` | Exact pre-funding cancellation record being reopened |
|
|
550
|
+
| `instruction_id` | Accepted cancellation JSON ID being executed |
|
|
551
|
+
| `transaction_hash` | Explicit existing transaction evidence to adopt; validated, never accepted as payment by itself |
|
|
552
|
+
| `job.pending` | Durable operation intent that has not been definitively confirmed/rejected |
|
|
553
|
+
|
|
554
|
+
Effects can take more than one request. The SDK persists intent before external actions, journals signed transactions and reconciles after interruptions. Do not issue a different action while one is pending.
|
|
555
|
+
|
|
556
|
+
```js
|
|
557
|
+
const observed = await app.workflow.reconcile(id, actor); // Observation only
|
|
558
|
+
// If a next transaction step is still needed, explicitly authorize execution:
|
|
559
|
+
const resumed = await app.workflow.resume(id, actor);
|
|
77
560
|
```
|
|
78
561
|
|
|
79
|
-
|
|
562
|
+
Only call `resume` if execution is intended and still permitted. A keeper tick with execution enabled may resume actions; observation alone never authorizes a new transaction. Keep cost/price changes out of retries of the same intent.
|
|
80
563
|
|
|
81
|
-
|
|
564
|
+
Use `freshReceipts` / `freshCancellationReceipt` or application receipt endpoints when displaying current chain-backed payment. Raw workflow getters return durable records and integrity checks, not guaranteed fresh finality. Reorg monitoring can quarantine evidence; it does not silently erase paid history or reverse blockchain transfers.
|
|
82
565
|
|
|
83
|
-
|
|
84
|
-
- The transaction-store interface adds `list` and atomic `appendReplacement`. The supplied SQLite and memory implementations include them.
|
|
85
|
-
- Use a new deployment for ERC-1271 support. Existing 0.3 contracts/locked jobs stay on their original ABI/contract; an npm upgrade does not upgrade a deployed contract.
|
|
86
|
-
- `workflow.resume(id, actor)` explicitly authorizes the next pending step. `reconcile` remains observe-only. `runMaintenanceOnce` only resumes when `allowExecute: true`.
|
|
87
|
-
- Reference evidence now includes settlement reason/dispute commitment. Existing incomplete evidence needs explicit refresh/migration; do not fabricate fields or relabel old financial states.
|
|
88
|
-
- `getSafety`/`setSafety` enable quarantine. Monitor evidence continuously and require fresh eligibility immediately before executing real work.
|
|
566
|
+
The narrowly scoped `workflow.rejectPendingOperation(id, {operation_id, reason}, {actor})` can reject a provably impossible malformed submit. It refuses valid encodings, transaction references, journaled intent, on-chain submissions and safety holds. It is not a force-unlock, refund or generic “clear pending” button. See [PROFIT-POLICY.md](./PROFIT-POLICY.md#supported-malformed-pending-recovery).
|
|
89
567
|
|
|
90
|
-
##
|
|
568
|
+
## Supported scenarios and boundaries
|
|
91
569
|
|
|
92
|
-
|
|
570
|
+
| Scenario | Supported path / boundary |
|
|
571
|
+
|---|---|
|
|
572
|
+
| Research, coding or another artifact task | App-defined task/validator/runner; same agreement and escrow primitives |
|
|
573
|
+
| Straightforward completion | Create → quote → both accept → fund → start → submit → verify → release |
|
|
574
|
+
| Cannot agree on price before funding | Requester cancels or worker declines; history remains |
|
|
575
|
+
| Resume an eligible cancelled negotiation | Explicit reopening approvals, same ID, fresh generation/agreement |
|
|
576
|
+
| Cancel after partial work | Negotiate closing compensation and refund the remainder; closes the job |
|
|
577
|
+
| Zero completed-work cost | Optional policy recommends full refund, subject to signed settlement or eligible exit |
|
|
578
|
+
| Budget cannot meet target margin | Capped positive-margin recommendation; below minimum/cost needs named owner approval |
|
|
579
|
+
| First offer or stale offer | Counter policy plus exact revision/hash/signature checks |
|
|
580
|
+
| Unsubmitted job expires | Eligible expiry refund; transaction still required |
|
|
581
|
+
| Disagreement after funding but before submission | Supported funded dispute/closing negotiation; original expiry refund remains eligible under its conditions |
|
|
582
|
+
| Submitted work is disputed | Trusted arbiter split or original timeout allocation |
|
|
583
|
+
| Verifier/arbiter unavailable | Eligible timeout under agreed terms; not a quality oracle |
|
|
584
|
+
| RPC outage, crash or stuck transaction | Pending state, durable journal, reconciliation/resume and bounded explicit fee replacement |
|
|
585
|
+
| Different networks or tokens | Allowlisted deployments; reference contract has one token per deployment |
|
|
586
|
+
| Milestones / continuing partial releases | Not supported by this reference contract |
|
|
587
|
+
| Resubmitting work after an immutable submission | Not supported |
|
|
588
|
+
| Fee-on-transfer/rebasing tokens | Not supported; use a reviewed standard ERC-20 |
|
|
589
|
+
| Contract pause, upgrades or owner withdrawal | Not provided by this reference contract |
|
|
590
|
+
| Independent audit or production assurance | Not claimed |
|
|
93
591
|
|
|
94
|
-
|
|
592
|
+
SDK profit settings cannot constrain direct external contract calls or prove reported costs are real. Deadline/arbitration outcomes can realize a loss; report the actual payout. Authority approval establishes trust within your registry, not automatic legal-company verification. HSM/KMS signing integration does not provision a vendor account or certify hardware.
|
|
95
593
|
|
|
96
|
-
##
|
|
594
|
+
## Modules and detailed guides
|
|
595
|
+
|
|
596
|
+
| Import / guide | What to use it for |
|
|
597
|
+
|---|---|
|
|
598
|
+
| `@allorc/a423-sdk/workflow` | Agreements, roles, CAS workflow, typed HTTP client/handler, cancellation and reopening |
|
|
599
|
+
| `@allorc/a423-sdk/escrow` | Reference contract ABI/bytecode, chain adapters, wallet consent, journaled settlement, registry/indexer/signing helpers |
|
|
600
|
+
| `@allorc/a423-sdk/policy` | Pure profit calculator, configuration, private cost service, projections and prompt fragments |
|
|
601
|
+
| `@allorc/a423-sdk/storage` | Single-host durable SQLite jobs, transaction journal, queue, KV and spending reservations |
|
|
602
|
+
| `@allorc/a423-sdk/authority` | Approved enrollment, challenge credentials, revocation and renewal |
|
|
603
|
+
| `@allorc/a423-sdk/operations` | Application factory, execution/maintenance workers, spending, fresh receipts, events/webhooks and app/chat bridge |
|
|
604
|
+
| Root `@allorc/a423-sdk` | Preserved older simple API and memory-store utility; root simple API is distinct from `/workflow` |
|
|
605
|
+
| [ESCROW.md](./ESCROW.md) | Contract fields, roles, deadlines, deployment and custody rules |
|
|
606
|
+
| [OPERATIONS.md](./OPERATIONS.md) | Authority setup, storage, signing, workers, events, deployment and operations |
|
|
607
|
+
| [SETTLEMENT.md](./SETTLEMENT.md) | On-chain proposals, counters, arbitration, fallback and ledger |
|
|
608
|
+
| [CANCELLATION.md](./CANCELLATION.md) | Accepted JSON, final receipts, retry/recovery and controlled reopening |
|
|
609
|
+
| [PROFIT-POLICY.md](./PROFIT-POLICY.md) | Threshold math, privacy, owner exceptions, initialization and app migration |
|
|
610
|
+
| [INTEGRATION.md](./INTEGRATION.md) | Existing-app migration checklist |
|
|
611
|
+
| [LEGACY.md](./LEGACY.md) | Older root API; do not confuse it with the reference escrow workflow |
|
|
612
|
+
|
|
613
|
+
See [examples/README.md](./examples/README.md) to distinguish the modern workflow examples from the preserved older root API.
|
|
614
|
+
|
|
615
|
+
The complete public signatures are in [workflow types](./src/workflow.d.ts), [factory/operations types](./src/operations/index.d.ts), [policy types](./src/policy/index.d.ts), [escrow types](./src/escrow/index.d.ts), [authority types](./src/authority/index.d.ts) and [storage types](./src/storage/index.d.ts).
|
|
616
|
+
|
|
617
|
+
The source shell and worker use an explicitly configured module exporting `applicationOptions` and `workerOptions`:
|
|
618
|
+
|
|
619
|
+
```sh
|
|
620
|
+
A423_CONFIG_MODULE=/absolute/path/config.mjs node examples/operations-server.mjs
|
|
621
|
+
A423_CONFIG_MODULE=/absolute/path/config.mjs node scripts/operations-worker.mjs
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
The shell binds to localhost. There is no automatic bootstrap, default credential, built-in key, deployed contract or default arbiter. [examples/operations-dashboard.html](./examples/operations-dashboard.html) displays states, wallet actions, accepted JSON, receipts and role-aware economics.
|
|
625
|
+
|
|
626
|
+
## Develop and publish
|
|
627
|
+
|
|
628
|
+
From the source repository:
|
|
97
629
|
|
|
98
630
|
```sh
|
|
99
631
|
npm ci --ignore-scripts
|
|
@@ -102,4 +634,15 @@ npm test
|
|
|
102
634
|
npm pack --dry-run --ignore-scripts
|
|
103
635
|
```
|
|
104
636
|
|
|
105
|
-
|
|
637
|
+
The tests compile/deploy a local EVM contract and cover custody, consent, settlement, cancellation/reopening, policy, privacy, pending recovery, SQLite persistence, HTTP receipts and operational services. Local passing tests are not proof of a public deployment, production readiness or an independent audit.
|
|
638
|
+
|
|
639
|
+
The deployment script is **dry-run by default**; read [scripts/deploy-escrow.mjs](./scripts/deploy-escrow.mjs) and [OPERATIONS.md](./OPERATIONS.md) before an explicit broadcast. A source-verification submission is not the same as confirmed verification.
|
|
640
|
+
|
|
641
|
+
For maintainers, pushing code to GitHub and publishing to npm are separate:
|
|
642
|
+
|
|
643
|
+
```sh
|
|
644
|
+
npm login
|
|
645
|
+
npm publish --access public
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
`prepublishOnly` runs TypeScript and the full test suite. Confirm the intended version is unused and review the packed files first. Do not republish an existing npm version. Older funded deployments/records retain their original custody and ABI; see migration guides before upgrading.
|