@multiagency/sdk 0.3.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.
Files changed (78) hide show
  1. package/CHANGES.md +21 -0
  2. package/LICENSE +21 -0
  3. package/README.md +142 -0
  4. package/THIRD-PARTY-NOTICES +127 -0
  5. package/bin/release-server.d.mts +29 -0
  6. package/bin/release-server.mjs +143 -0
  7. package/dist/compensation-contract.d.ts +370 -0
  8. package/dist/compensation-contract.js +51 -0
  9. package/dist/contract/agent-connection.d.ts +86 -0
  10. package/dist/contract/agent-connection.js +36 -0
  11. package/dist/contract/app-access.d.ts +141 -0
  12. package/dist/contract/app-access.js +57 -0
  13. package/dist/contract/app-client.d.ts +140 -0
  14. package/dist/contract/app-client.js +61 -0
  15. package/dist/contract/app-consent.d.ts +68 -0
  16. package/dist/contract/app-consent.js +91 -0
  17. package/dist/contract/assets.d.ts +6 -0
  18. package/dist/contract/assets.js +6 -0
  19. package/dist/contract/client.d.ts +347 -0
  20. package/dist/contract/client.js +81 -0
  21. package/dist/contract/gateway.d.ts +74 -0
  22. package/dist/contract/gateway.js +47 -0
  23. package/dist/contract/index.d.ts +26 -0
  24. package/dist/contract/index.js +25 -0
  25. package/dist/contract/inputs.d.ts +230 -0
  26. package/dist/contract/inputs.js +99 -0
  27. package/dist/contract/intents-account.d.ts +5 -0
  28. package/dist/contract/intents-account.js +8 -0
  29. package/dist/contract/orpc-client.js +2037 -0
  30. package/dist/contract/owner-capabilities.d.ts +81 -0
  31. package/dist/contract/owner-capabilities.js +33 -0
  32. package/dist/contract/owner-client.d.ts +99 -0
  33. package/dist/contract/owner-client.js +146 -0
  34. package/dist/contract/owner-contract.d.ts +1156 -0
  35. package/dist/contract/owner-contract.js +37 -0
  36. package/dist/contract/owner-inputs.d.ts +166 -0
  37. package/dist/contract/owner-inputs.js +1 -0
  38. package/dist/contract/owner-outputs.d.ts +5749 -0
  39. package/dist/contract/owner-outputs.js +751 -0
  40. package/dist/contract/package-version.d.ts +5 -0
  41. package/dist/contract/package-version.js +2 -0
  42. package/dist/contract/payee-entry.d.ts +10 -0
  43. package/dist/contract/payee-entry.js +10 -0
  44. package/dist/contract/payee.d.ts +326 -0
  45. package/dist/contract/payee.js +32 -0
  46. package/dist/contract/permissions.d.ts +20 -0
  47. package/dist/contract/permissions.js +64 -0
  48. package/dist/contract/proposals.d.ts +178 -0
  49. package/dist/contract/proposals.js +27 -0
  50. package/dist/contract/run-log-schema.d.ts +888 -0
  51. package/dist/contract/run-log-schema.js +150 -0
  52. package/dist/contract/run-progress-schema.d.ts +104 -0
  53. package/dist/contract/run-progress-schema.js +20 -0
  54. package/dist/payee-contract.d.ts +9 -0
  55. package/dist/payee-contract.js +15 -0
  56. package/package.json +58 -0
  57. package/provenance.json +61 -0
  58. package/schemas/agentConnection.json +76 -0
  59. package/schemas/agentConnectionIssue.json +22 -0
  60. package/schemas/agentConnectionList.json +88 -0
  61. package/schemas/agentConnectionStarted.json +90 -0
  62. package/schemas/compensationSend.json +40 -0
  63. package/schemas/errorBody.json +16 -0
  64. package/schemas/fileResult.json +67 -0
  65. package/schemas/invoicesView.json +130 -0
  66. package/schemas/ownerCompensationView.json +556 -0
  67. package/schemas/payeeCompensationView.json +546 -0
  68. package/schemas/payeeWorkOrderRequest.json +31 -0
  69. package/schemas/publicWorkOrder.json +94 -0
  70. package/schemas/reportCommand.json +82 -0
  71. package/schemas/requestRecord.json +149 -0
  72. package/schemas/session.json +20 -0
  73. package/schemas/workOrderList.json +43 -0
  74. package/schemas/workOrderRequestResult.json +20 -0
  75. package/skills/app-server/SKILL.md +190 -0
  76. package/skills/getting-started/SKILL.md +90 -0
  77. package/skills/owner-workflows/SKILL.md +157 -0
  78. package/skills/payee-workflows/SKILL.md +200 -0
@@ -0,0 +1,82 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "commandId": {
6
+ "type": "string",
7
+ "format": "uuid",
8
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
9
+ },
10
+ "id": {
11
+ "type": "string",
12
+ "format": "uuid",
13
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
14
+ },
15
+ "expectedVersion": {
16
+ "type": "integer",
17
+ "minimum": 0,
18
+ "maximum": 9007199254740991
19
+ },
20
+ "report": {
21
+ "type": "object",
22
+ "properties": {
23
+ "agreementId": {
24
+ "type": "string",
25
+ "format": "uuid",
26
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
27
+ },
28
+ "amount": {
29
+ "type": "string",
30
+ "pattern": "^[1-9][0-9]{0,17}$"
31
+ },
32
+ "start": {
33
+ "type": "string",
34
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
35
+ },
36
+ "end": {
37
+ "type": "string",
38
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
39
+ },
40
+ "contributions": {
41
+ "type": "string",
42
+ "minLength": 1,
43
+ "maxLength": 4000
44
+ },
45
+ "assessment": {
46
+ "type": "string",
47
+ "minLength": 1,
48
+ "maxLength": 2000
49
+ },
50
+ "evidence": {
51
+ "maxItems": 12,
52
+ "type": "array",
53
+ "items": {
54
+ "type": "string",
55
+ "maxLength": 1000,
56
+ "format": "uri"
57
+ }
58
+ }
59
+ },
60
+ "required": [
61
+ "agreementId",
62
+ "start",
63
+ "end",
64
+ "contributions",
65
+ "assessment",
66
+ "evidence"
67
+ ],
68
+ "additionalProperties": false
69
+ },
70
+ "submit": {
71
+ "type": "boolean"
72
+ }
73
+ },
74
+ "required": [
75
+ "commandId",
76
+ "id",
77
+ "expectedVersion",
78
+ "report",
79
+ "submit"
80
+ ],
81
+ "additionalProperties": false
82
+ }
@@ -0,0 +1,149 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "id": {
6
+ "type": "string",
7
+ "format": "uuid",
8
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
9
+ },
10
+ "version": {
11
+ "type": "integer",
12
+ "exclusiveMinimum": 0,
13
+ "maximum": 9007199254740991
14
+ },
15
+ "payee": {
16
+ "type": "string",
17
+ "pattern": "^(?=.{2,64}$)([a-z\\d]+[-_])*[a-z\\d]+(\\.([a-z\\d]+[-_])*[a-z\\d]+)*$"
18
+ },
19
+ "agreementVersion": {
20
+ "type": "integer",
21
+ "exclusiveMinimum": 0,
22
+ "maximum": 9007199254740991
23
+ },
24
+ "report": {
25
+ "type": "object",
26
+ "properties": {
27
+ "agreementId": {
28
+ "type": "string",
29
+ "format": "uuid",
30
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
31
+ },
32
+ "amount": {
33
+ "type": "string",
34
+ "pattern": "^[1-9][0-9]{0,17}$"
35
+ },
36
+ "start": {
37
+ "type": "string",
38
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
39
+ },
40
+ "end": {
41
+ "type": "string",
42
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
43
+ },
44
+ "contributions": {
45
+ "type": "string",
46
+ "minLength": 1,
47
+ "maxLength": 4000
48
+ },
49
+ "assessment": {
50
+ "type": "string",
51
+ "minLength": 1,
52
+ "maxLength": 2000
53
+ },
54
+ "evidence": {
55
+ "maxItems": 12,
56
+ "type": "array",
57
+ "items": {
58
+ "type": "string",
59
+ "maxLength": 1000,
60
+ "format": "uri"
61
+ }
62
+ }
63
+ },
64
+ "required": [
65
+ "agreementId",
66
+ "start",
67
+ "end",
68
+ "contributions",
69
+ "assessment",
70
+ "evidence"
71
+ ],
72
+ "additionalProperties": false
73
+ },
74
+ "state": {
75
+ "type": "string",
76
+ "enum": [
77
+ "draft",
78
+ "submitted",
79
+ "changes_requested",
80
+ "approved",
81
+ "declined"
82
+ ]
83
+ },
84
+ "approvedAmount": {
85
+ "type": "string",
86
+ "pattern": "^[1-9][0-9]{0,17}$"
87
+ },
88
+ "feedback": {
89
+ "type": "string"
90
+ },
91
+ "reviewedBy": {
92
+ "type": [
93
+ "string",
94
+ "null"
95
+ ]
96
+ },
97
+ "at": {
98
+ "type": "string"
99
+ },
100
+ "payment": {
101
+ "default": null,
102
+ "anyOf": [
103
+ {
104
+ "type": "object",
105
+ "properties": {
106
+ "mode": {
107
+ "type": "string",
108
+ "enum": [
109
+ "shared",
110
+ "isolated"
111
+ ]
112
+ },
113
+ "id": {
114
+ "type": "string",
115
+ "format": "uuid",
116
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
117
+ },
118
+ "until": {
119
+ "type": "string",
120
+ "format": "date-time",
121
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
122
+ }
123
+ },
124
+ "required": [
125
+ "mode",
126
+ "id"
127
+ ],
128
+ "additionalProperties": false
129
+ },
130
+ {
131
+ "type": "null"
132
+ }
133
+ ]
134
+ }
135
+ },
136
+ "required": [
137
+ "id",
138
+ "version",
139
+ "payee",
140
+ "agreementVersion",
141
+ "report",
142
+ "state",
143
+ "feedback",
144
+ "reviewedBy",
145
+ "at",
146
+ "payment"
147
+ ],
148
+ "additionalProperties": false
149
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "account": {
6
+ "type": [
7
+ "string",
8
+ "null"
9
+ ]
10
+ },
11
+ "agreed": {
12
+ "type": "boolean"
13
+ }
14
+ },
15
+ "required": [
16
+ "account",
17
+ "agreed"
18
+ ],
19
+ "additionalProperties": false
20
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "orders": {
6
+ "type": "array",
7
+ "items": {
8
+ "type": "object",
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "format": "uuid",
13
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
14
+ },
15
+ "amount": {
16
+ "type": "string"
17
+ },
18
+ "note": {
19
+ "type": "string"
20
+ },
21
+ "until": {
22
+ "type": "string"
23
+ },
24
+ "createdAt": {
25
+ "type": "string"
26
+ }
27
+ },
28
+ "required": [
29
+ "id",
30
+ "amount",
31
+ "note",
32
+ "until",
33
+ "createdAt"
34
+ ],
35
+ "additionalProperties": false
36
+ }
37
+ }
38
+ },
39
+ "required": [
40
+ "orders"
41
+ ],
42
+ "additionalProperties": false
43
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "id": {
6
+ "type": "string",
7
+ "format": "uuid",
8
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
9
+ },
10
+ "status": {
11
+ "type": "string",
12
+ "const": "requested"
13
+ }
14
+ },
15
+ "required": [
16
+ "id",
17
+ "status"
18
+ ],
19
+ "additionalProperties": false
20
+ }
@@ -0,0 +1,190 @@
1
+ ---
2
+ name: app-server
3
+ description: >
4
+ Use when building an app's server (for example the MultiAgency dashboard's) that reaches MultiAgency agents for its
5
+ Organizations through @multiagency/sdk: the deployment's service credential, owner consents and hosted setup signed
6
+ with the owner's wallet, reads and the owner actions an app may relay, and payout requests sent at most once per
7
+ requestId and followed by status. Server side only; never in a browser. Not for an owner's own browser app (use
8
+ owner-workflows) or payees (use payee-workflows).
9
+ metadata:
10
+ purpose: 'An app server''s access to agents: credential, consents, relayed owner actions, and payout requests by requestId with recovery by state.'
11
+ type: core
12
+ library: '@multiagency/sdk'
13
+ library_version: '0.3.1'
14
+ requires:
15
+ - getting-started
16
+ - owner-workflows
17
+ sources:
18
+ - 'MultiAgency/platform:src/contract/app-client.ts'
19
+ - 'MultiAgency/platform:src/contract/app-access.ts'
20
+ - 'MultiAgency/platform:src/contract/app-consent.ts'
21
+ - 'MultiAgency/platform:docs/agent-access.md'
22
+ ---
23
+
24
+ # An app's server with appClient
25
+
26
+ Read getting-started and owner-workflows first. `appClient` runs on your server with the deployment's service credential
27
+ (`tap_…`), issued by the platform operator: a server secret, never sent to a browser. A call reaches an agent only
28
+ under a live owner consent for that Organization and agent, and only within the consent's scopes: `read`, `owner`, and
29
+ `payout`. No production MultiAgency host serves the `/apps/v1` routes yet; the operator runs them on a staging host for
30
+ its own tests. Until a production host does, build against the SDK repository's local stack
31
+ (`pnpm test:e2e:stack --app <deployment>`). Use only `/apps/v1` and the confirm page on the platform's host: it serves
32
+ nothing else (no sign-in or owner page), and setup there is for Payables agents only.
33
+
34
+ ## Setup
35
+
36
+ ```ts
37
+ import { appClient } from "@multiagency/sdk";
38
+
39
+ // `platform` is the platform host's HTTPS origin; `credential` comes from your server's secret store.
40
+ export const platformFor = (platform: `https://${string}`, credential: string) => appClient({ base: platform, credential });
41
+ ```
42
+
43
+ Result: `await app.identity("payout")` answers `{ ok: true, value: { agent, owner, agentId, created, host, deployment } }`.
44
+ `session` (401) means the credential is invalid, expired or revoked: an operator matter, not the user's.
45
+
46
+ ## Connect an Organization's agent: the owner signs a consent
47
+
48
+ Your server builds the exact consent text and a 32-byte nonce; the owner's wallet signs it with NEP-413, recipient the
49
+ platform host, no callback URL; your server submits it. Show the owner the text the wallet signs.
50
+
51
+ ```ts
52
+ import { appClient, consentMessage, type AppConsentSubmission } from "@multiagency/sdk";
53
+
54
+ type SignNep413 = (message: string, recipient: string, nonce: Uint8Array) => Promise<{ publicKey: string; signature: string }>;
55
+
56
+ export async function connect(app: ReturnType<typeof appClient>, host: string, deployment: string, organization: string,
57
+ agent: string, sign: SignNep413) {
58
+ const identity = await app.identity(agent);
59
+ if (!identity.ok) return identity;
60
+ if (!identity.value.agentId) throw new Error("Not created yet: set it up and create it first");
61
+ const message = consentMessage({ host, deployment, organization, owner: identity.value.owner, agent, agentId: identity.value.agentId,
62
+ scopes: ["read", "owner"], expiresAt: new Date(Date.now() + 90 * 86_400_000).toISOString() });
63
+ const nonce = crypto.getRandomValues(new Uint8Array(32));
64
+ const signed = await sign(message, host, nonce);
65
+ const submission: AppConsentSubmission = { message, nonce: btoa(String.fromCharCode(...nonce)), ...signed };
66
+ let stored = await app.consents.submit(submission);
67
+ // No answer: the same signed body is safe to submit again; never ask the owner to sign again for that.
68
+ if (!stored.ok && stored.kind === "unavailable") stored = await app.consents.submit(submission);
69
+ return stored;
70
+ }
71
+ ```
72
+
73
+ Result: `{ ok: true, value: { consent: { id, scopes, expiresAt, … } } }`; keep `consent.id` with the Organization. A
74
+ `refused` 400 means the text or signature was not accepted; 409 means the agent changed: read `identity` again and ask
75
+ the owner to sign a new text. Later calls answer `forbidden` once the consent ends (expired, revoked, or the owner's
76
+ signing key removed): the owner signs a new one. A 503 means a check went unanswered: read again.
77
+
78
+ ## Relay an owner action
79
+
80
+ With `owner` scope an app may prepare `agent_create` without payees (naming the key that signed the setup),
81
+ `agent_freeze`, `agent_unfreeze`, `grant_issue`, `grant_revoke`, `policy_update`, and `approval_vote` on a held payout,
82
+ through `app.agent(organization, agent).call(...)`. Everything else is refused before it reaches the agent. A vote the
83
+ platform can't verify (an expired or changed approval, other terms, approving a held withdrawal) is refused before
84
+ anything reaches the provider (`refused`, code `local_authorization_held`), at prepare or at submit.
85
+
86
+ ```ts
87
+ import { appClient, type AppOwnerAction } from "@multiagency/sdk";
88
+
89
+ type SignNep413Intent = (intent: { standard: "nep413"; payload: { message: string; recipient: string; nonce: string } }) => Promise<Record<string, unknown>>;
90
+
91
+ /** Prepares an owner action; a readable one the owner signs in your app, any other on the platform's confirm page. */
92
+ export async function ownerAction(app: ReturnType<typeof appClient>, organization: string, agent: string, action: AppOwnerAction,
93
+ signReadable: SignNep413Intent, openForOwner: (url: string) => void) {
94
+ const calls = app.agent(organization, agent);
95
+ const prepared = await calls.call("owner/prepare", { action, signer: "wallet" });
96
+ if (!prepared.ok) return prepared;
97
+ const { correlationId, intent, confirm } = prepared.value;
98
+ if (confirm) openForOwner(confirm.url); // NEP-366: the owner reads the platform's record and signs there
99
+ else if (intent?.standard === "nep413")
100
+ await calls.call("owner/submit", { correlationId, signedData: await signReadable(intent) });
101
+ return calls.call("owner/status", { correlationId });
102
+ }
103
+ ```
104
+
105
+ Result: `owner/status` answers the outcome; read it again until it is final. Open a `confirm` link for the owner right
106
+ away (the intent expires in minutes). It shows the action's terms to whoever holds it; the bytes to sign go only to
107
+ whoever presents the owner's signature over the link's text, which the page asks the wallet for, and `owner/submit`
108
+ through the app is refused for it.
109
+
110
+ A vote on a held payout names it from `held` (`approvalId`, `correlationId`). The prepared vote's `payment.terms`
111
+ (recipient, asset, amount) are what the platform proved against the provider's evidence: show those to the owner, not
112
+ your own record of the request. The vote's signed text holds only an identifier and a hash, so your display is the only
113
+ place the owner reads the terms. A payout an owner approves can still read `processing` until the provider settles it;
114
+ follow `payouts/status`.
115
+
116
+ ## Set up a hosted agent for an Organization
117
+
118
+ Before an Organization has an agent, your server builds `setupMessage({ host, deployment, organization, owner, area,
119
+ expiresAt })` (at most an hour ahead) and the owner signs it like a consent; submit it with `app.setup(...)`. The answer
120
+ names the Organization's own agent and a setup consent that lets you relay only its creation: `agent_create` without
121
+ payees, naming the key that signed the setup. Then read `identity` until it is created, and ask the owner to sign a
122
+ consent naming its `agentId`. A 409 means it already exists or the deployment reached its agent limit on the host; a
123
+ 503 means a check went unanswered: submit the same body again.
124
+
125
+ ## Request a payout once, and follow it
126
+
127
+ To fund the agent first, `agentCalls.call("deposit")` (payout scope) answers a one-time address (with `memo`,
128
+ `minAmount` and `expiresAt` when given) that credits USDC to the agent's confidential balance; send exactly there.
129
+
130
+ Write the `requestId` (1–64 of `[A-Za-z0-9_-]`) and the exact terms to your database before calling. The platform sends
131
+ one request at most once per deployment, Organization, agent and `requestId`. Never send the same payout under a new `requestId` until
132
+ the first is `refused`, or `failed` and checked by a person.
133
+
134
+ ```ts
135
+ import { appClient } from "@multiagency/sdk";
136
+
137
+ type Terms = { requestId: string; asset: string; recipient: string; amount: string; expectedPolicy: { revision: number; digest: string } };
138
+
139
+ export async function payOnce(app: ReturnType<typeof appClient>, organization: string, agent: string, terms: Terms) {
140
+ const agentCalls = app.agent(organization, agent);
141
+ const asked = await agentCalls.call("payouts/request", { ...terms, delivery: "intents" });
142
+ if (asked.ok || (asked.kind !== "unknown" && asked.kind !== "unavailable")) return asked;
143
+ // The answer was lost: read the request by its id. Asking again with the same terms would also send nothing new.
144
+ return agentCalls.call("payouts/status", { requestId: terms.requestId });
145
+ }
146
+ ```
147
+
148
+ Result: `{ ok: true, value: { requestId, state, status, correlationId, code } }`. `state` is `succeeded`, `failed`
149
+ (FAILED or REFUNDED: not proof that nothing moved, so a person checks), `held` (waiting for the owner's vote),
150
+ `processing`, `refused` (proven not sent, `code` says why, for example `insufficient_balance`; recorded, so a new
151
+ attempt needs a new `requestId`), `unknown`, or
152
+ `not_found` (nothing recorded yet: a request still on its way can read so; ask again with the same `requestId`
153
+ and terms, which sends at most once). `processing` and `unknown` block that agent's payouts until resolved: read
154
+ `payouts/status` later; never pay that request another way meanwhile. Other terms under a used `requestId` are refused
155
+ (`code: "request_conflict"`). A named recipient that doesn't exist on NEAR is refused before anything is recorded
156
+ (`recipient_not_found`: fix the payee, then a new `requestId`); `recipient_unchecked` means the chain didn't answer:
157
+ ask again with the same `requestId`. These two come back as a refused result, not a state.
158
+
159
+ To pay a NEAR account directly, the owner first agrees it as a withdrawal payee (a `policy_update` with
160
+ `withdrawals: [{ address }]`; the first one turns withdrawals on, an on-chain edit signed on the confirm page). To pay
161
+ wNEAR, the owner also adds `nep141:wrap.near` to the policy's `assets` and sets its per-asset `limits` (the payout
162
+ template has USDC only; without a limit only the USD budget caps it). Then request with `delivery: { chain: "near" }` and
163
+ `asset` wNEAR (arriving as native NEAR, per the provider's docs) or USDC; an asset the policy doesn't include is refused
164
+ `asset_not_allowed` before sending. It is paid from the confidential balance, is public on chain, and may arrive less a
165
+ route fee. A USDC withdrawal's recipient must be registered on the USDC contract (a one-time `storage_deposit` the payee
166
+ makes from any wallet): otherwise it is refused `recipient_not_registered` before anything is recorded, and the same
167
+ `requestId` can be asked again once it is. A `nearcom:` address is never a withdrawal recipient; any other chain is refused `chain_not_supported`. A held
168
+ withdrawal can be rejected, not approved, for now: show the owner the reject choice only. Under the payout template every
169
+ payout is held, so a withdrawal is paid only once the owner turns `owner_approval` off. Fund wNEAR with `deposit` and
170
+ `{ asset: "nep141:wrap.near" }`.
171
+
172
+ ## Common mistakes
173
+
174
+ - **Asking an owner to sign `Sign in to <platform host>`** (CRITICAL): a signed sign-in is a full platform session that
175
+ can send payouts without another signature. An app asks only for consent and setup texts.
176
+ - **Sending the credential to a browser** (CRITICAL): whoever holds it acts within every consent the deployment holds.
177
+ - **A new requestId after `unknown`** (CRITICAL): the first may have been sent. Read `payouts/status`.
178
+ - **Treating `failed` as unpaid** (HIGH): a partial payout fails too. A person checks before a new attempt.
179
+ - **Showing your own summary instead of the signed text** (HIGH): the owner must read what the wallet signs.
180
+ - **Asking the owner to sign the confirm page's text ("Open a MultiAgency confirm page.") in your app** (CRITICAL): it
181
+ is the owner's proof to the platform's page; signed elsewhere, it hands out the bytes for a blind signature.
182
+ - **Withdrawing to a `nearcom:` address** (CRITICAL): it is not on any chain; near.com users are paid through `intents`.
183
+ - **Showing your own record of a held payout when asking for a vote** (HIGH): show the prepared vote's
184
+ `payment.terms`, which the platform proved; your record may name another payout than the one the vote releases.
185
+
186
+ ## Completion
187
+
188
+ Every consent you submitted has its id stored or its refusal shown; every payout request has a read state, and none
189
+ reads `unknown` or `processing` without a later `payouts/status` read. Report anything you could not read; do not act
190
+ again.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: getting-started
3
+ description: >
4
+ Use when deciding how an app or external agent should work with MultiAgency agents through @multiagency/sdk: which client
5
+ to use (owner or payee), how each kind of identity signs in and what the server enforces for it, how results report
6
+ refused, unavailable and unknown outcomes, and what this package version supports. For owner tasks use owner-workflows;
7
+ for payee tasks use payee-workflows.
8
+ metadata:
9
+ purpose: 'Orients an app or agent author: the two clients, the four identities and their server-side limits, recovery kinds, and the supported surface of this package version.'
10
+ type: lifecycle
11
+ library: '@multiagency/sdk'
12
+ library_version: '0.3.1'
13
+ sources:
14
+ - 'MultiAgency/platform:src/contract/index.ts'
15
+ - 'MultiAgency/platform:src/contract/payee-entry.ts'
16
+ - 'MultiAgency/platform:docs/agent-access.md'
17
+ ---
18
+
19
+ # Working with MultiAgency agents
20
+
21
+ Each agent moves funds for its **owner** (a NEAR account or a Sputnik DAO) only inside the policy the owner signed.
22
+ This package gives apps and agents typed access to two kinds of work. It grants no authority: every call is authorized
23
+ by the gateway and the provider, never by the client or by this guidance.
24
+
25
+ 1. Pick the client for the identity you act as (table below). Result: one of `ownerClient` (entry `@multiagency/sdk`),
26
+ `appClient` (same entry, an app's server) or `payeeClient` (entry `@multiagency/sdk/payee`, zod only).
27
+ 2. Get that identity's session the supported way. Never handle a wallet's private key or copy browser cookies into an
28
+ agent. Result: calls return data; a `session` (401) result means sign in again.
29
+ 3. Do the task with owner-workflows, app-server or payee-workflows.
30
+ 4. Keep every identity you send (`correlationId`, `commandId`, request `id`, `planId`) until you have read its outcome.
31
+ Some owner actions (`pay`, `sweep`, `autopilot/tick`) carry no identity; owner-workflows says how to recover them.
32
+
33
+ ## Identities and what the server enforces
34
+
35
+ | Identity | How it is obtained | Reaches | Enforced server-side |
36
+ | --- | --- | --- | --- |
37
+ | Owner session | NEP-413 sign-in in a browser at the agent's host (`/gw/challenge` → wallet signs → `/gw/session`) | `/a/<agent>/rpc/*` through `ownerClient` | Access per agent from the wallet owner, or from the DAO policy for a member (propose, vote; execute and manage derived). A member never acts or spends as the DAO alone: owner actions need the owner's signature (a DAO vote). Calls outside access: 403. |
38
+ | Treasury machine credential | Issued by a wallet owner of a Treasury agent from the owner's session (`tmc_…` bearer) | `/machine/v1/a/<agent>/rpc/*` (Treasury's skill helper, not this package) | Fixed scopes (read, prepare, execute-plan, sweep), expiry, revocation, the owner's fresh access re-checked per call. Treasury only; DAO owners cannot issue it. |
39
+ | App deployment credential | Issued by the platform operator per app deployment (`tap_…` bearer, a server secret), plus an owner-signed consent per Organization and agent | `/apps/v1/*` through `appClient` (server side only) | The consent's scopes (`read`, `owner`, `payout`), Organization and agent; owner actions only those the owner can read or the platform builds; the owner's key and ownership re-checked per call; revocation. Wallet owners only. |
40
+ | Payee session | NEP-413 sign-in on the payee entry (`/api/signin`) | `/api/*` on that entry through `payeeClient()` | Only the signed-in account's own invoices, work orders and reports. An account that has only requested onboarding terms is not yet a payee: an agreement makes it one. |
41
+ | Agent connection | Started by the payee from their signed-in browser session (`tac_…` bearer, shown once) | `GET`/`POST /api/compensation`, `GET /api/compensation/session` through `payeeClient({ base, token })` | Bound to host, entry, account and payout agent; expiry (at most 24h) and revocation; every other route 403 with no effect; never a browser session; each call attributed. |
42
+
43
+ ## Results: decide by `kind`, never by message text
44
+
45
+ | `kind` | Meaning | Nothing was sent? | What to do |
46
+ | --- | --- | --- | --- |
47
+ | `ok: true` | The answer, validated against this version's schema | n/a | Use `value` |
48
+ | `session` | 401: no valid session | Yes | Sign in again (agent connection: start a new one) |
49
+ | `forbidden` | 403: this identity may not do this | Yes | Do not retry; it is a permission, not an outage |
50
+ | `refused` | Refused before anything was sent (optional `code`; owner results also carry the HTTP `status` and oRPC `rpcCode`) | Yes | Correct the input or state, then act again |
51
+ | `busy`, `unresolved` | Owner money actions: another action holds the lock, or an earlier one has no proven outcome | Yes | Follow existing operations, then try again |
52
+ | `provider` | After an owner action, the provider's answer proves it refused; after a read, the provider read failed | Yes, for an action | Check the recorded outcome before acting again |
53
+ | `unavailable` | A read failed or lost its answer | n/a (a read) | Reading again is safe |
54
+ | `unknown` | An action was sent and its outcome is not known (`identity` is the input you sent) | Not known | Never resend. Read the record by the kept identity, or follow owner-workflows when the action has none |
55
+
56
+ `unavailable` and `unknown` may carry `httpStatus` when the answer was a plain HTTP error rather than an encoded one (a
57
+ proxy page, or a 404 for a procedure the host doesn't have). It is a diagnostic: it doesn't prove that nothing ran. A
58
+ `fetch` you pass in that throws (a timeout, a refused redirect, your own origin check) is a lost answer too: `unavailable`
59
+ for a read and `unknown` for an action. A command-line helper can map these to exit codes, for example `0` for `ok`, `2`
60
+ for a kind that proves nothing was sent, and `3` for `unknown`, so a script never treats an unknown outcome as a failure
61
+ it may retry.
62
+
63
+ ## What this package version supports
64
+
65
+ `@multiagency/sdk` 0.3.1, entries:
66
+ - `@multiagency/sdk`: `ownerClient` with `OwnerResult`/`OwnerFailure`, `appClient` with `AppRouteResult` and the
67
+ consent and setup texts (`consentMessage`, `setupMessage`), `ownerOutputs` and `OwnerInputs`/`OwnerOutput`
68
+ per procedure, `ownerCapabilities` and `mayCall` (display only), gateway discovery (`gatewayDiscovery`,
69
+ `gatewayDenialScope`), schemas for runs (`RecordingSchema`) and proposals, `ASSETS` and account helpers. It also
70
+ re-exports the payee entry. Peer: zod ^4.6.5. It bundles `@orpc/client` 2.0.0-beta.47 and loads no other oRPC
71
+ package, so it runs beside a host that shares its own oRPC. It depends on `@ag-ui/core` 1.0.3.
72
+ - `@multiagency/sdk/contract`: `ownerContract`, `ownerErrors` and `OwnerContract`, for an app implementing a server
73
+ from the contract. It loads `@orpc/contract` 2.0.0-beta.47, a dependency.
74
+ - `@multiagency/sdk/payee`: `payeeClient` with `Result`/`Failure`, payee and compensation schemas, agent-connection
75
+ schemas, `payeeContract` and `compatiblePayeeFrontend`. Peer: zod only.
76
+ - `@multiagency/sdk/release-server`: `stage`, `activate`, `readRelease`, `digest` and `releaseServer` for app
77
+ releases, and the `multiagency-release-server` command. Node only: its types use `@types/node`.
78
+ - `@multiagency/sdk/schemas/*`: JSON Schemas of the payee and compensation wire for non-JavaScript consumers.
79
+
80
+ Other exports (the owner contract object, server input schemas, capability helpers the gateway uses) are available,
81
+ but they follow the platform's needs and may change without notice.
82
+
83
+ The payee entry's capability ids are still `supplier-*-v1` (check them with `payeeClient().capabilities()`); an agent
84
+ connection's `role` is `PAYEE_ROLE`. Compare against the exported constants, never against literal strings.
85
+
86
+ ## Completion
87
+
88
+ The task is done when the client answers `ok` for the reads you need and every action's outcome has been read by its
89
+ kept identity. If a result is `unknown`, the task is not done until the record is read; if it stays unreadable, report
90
+ it rather than acting again.