@multiagency/sdk 0.3.1 → 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/CHANGES.md CHANGED
@@ -5,6 +5,31 @@ wire change all apps must update for together. The first change after a release
5
5
  version, so a package staged or packed from changed sources never carries a released version's number. The release
6
6
  candidate dates the heading. Under 0.x, any public-interface change bumps at least the minor version.
7
7
 
8
+ ## 0.4.0 (2026-11-02)
9
+
10
+ Additive: 0.3.x apps keep working, and a consent signed without the new scopes stays valid.
11
+
12
+ - **Treasury proposals** (`proposals/create`, `get`, `list`, `approve`, `reject`, `withdraw`, `execute`): an app proposes
13
+ swaps under its own `requestId`; the owner approves the exact terms later by signing `proposalText(proposal, "approve")`
14
+ (NEP-413, recipient the platform host); the app then asks for one execution, which re-quotes every swap and sends each
15
+ with a floor at least the signed one, through the same plan execution as any saved plan. A proposal lasts at most 7
16
+ days. New exports: `proposalCreateInput`, `proposalSigned`, `proposalRef`, `proposalView`, `proposalList`,
17
+ `ProposalState`, `proposalText`, `tokenAmount`, `PROPOSAL_TOKENS`, `MAX_PROPOSAL_MS`, `MAX_OPEN_PROPOSALS`,
18
+ `MAX_PROPOSAL_LEGS`.
19
+ - **Consent scopes `treasury` and `treasury-execute`** (`AppScope`, `APP_TREASURY`, `APP_TREASURY_EXECUTE`): `treasury`
20
+ reaches Treasury reads, strategy settings, autopilot on and off, sweep and proposals; `treasury-execute`, only beside
21
+ `treasury`, adds `rebalance`, `externalPlan` and `executePlan`, which run for an app only while the owner's live grant
22
+ binds the plan's strategy.
23
+ - **`appClient(...).agent(...).stream`**: an app reads the stream procedures its consent covers (`sweep`, and with
24
+ `treasury-execute` `rebalance` and `executePlan`), as `ownerClient.stream` does. `call` takes the other procedures.
25
+ - **Machine route (platform, not an SDK type):** `executePlan` through a Treasury machine credential (`tmc_`) now runs
26
+ only while the owner's live grant binds the plan's strategy, as an app's does; under a plain `treasury` grant it is
27
+ refused before anything is claimed. The owner signs the strategy-bound grant once per saved strategy. Clients of the
28
+ machine route (the treasury app's helper) need that grant before upgrading the platform.
29
+ - **Plans:** a plan's `source` may be `proposal`, with its `proposalId`; such a plan runs only as its proposal.
30
+ - **Owner capability** `treasury-proposals-v1`.
31
+ - **treasury-app skill.**
32
+
8
33
  ## 0.3.1 (2026-10-31)
9
34
 
10
35
  No code, type or wire change.
@@ -16,7 +16,19 @@ export declare const APP_OWNER: readonly ["owner/prepare", "owner/submit", "owne
16
16
  * nothing out.
17
17
  */
18
18
  export declare const APP_PAYOUT: readonly ["payouts/request", "payouts/status", "held", "deposit"];
19
- export type AppProcedure = (typeof APP_READS)[number] | (typeof APP_OWNER)[number] | (typeof APP_PAYOUT)[number];
19
+ /**
20
+ * Treasury with `treasury` consent (docs/enforcement.md, "Treasury proposals"): reads; strategy settings, which an
21
+ * app's plans, a machine credential's plans and the autopilot use only under a grant binding their digest; autopilot on
22
+ * (under such a grant) and off; sweeping public balances into the confidential one; and proposals, whose swaps execute
23
+ * only after the owner signs their exact terms.
24
+ */
25
+ export declare const APP_TREASURY: readonly ["strategy/get", "autopilot/get", "autopilot/evidence", "autopilot/log", "treasury/get", "strategy/save", "autopilot/enable", "autopilot/stop", "sweep", "proposals/create", "proposals/get", "proposals/list", "proposals/approve", "proposals/reject", "proposals/withdraw", "proposals/execute"];
26
+ /**
27
+ * With `treasury-execute` as well: plans prepared and executed within the owner's policy and the strategy's cost limit,
28
+ * without a signature per swap, and only under a grant binding the plan's strategy (src/planning/strategy-grant.ts).
29
+ */
30
+ export declare const APP_TREASURY_EXECUTE: readonly ["rebalance", "externalPlan", "executePlan"];
31
+ export type AppProcedure = (typeof APP_READS)[number] | (typeof APP_OWNER)[number] | (typeof APP_PAYOUT)[number] | (typeof APP_TREASURY)[number] | (typeof APP_TREASURY_EXECUTE)[number];
20
32
  /** Whether consent scopes cover a procedure (the gateway also requires the owner's own access to it). */
21
33
  export declare const appScopesCover: (scopes: readonly AppScope[], path: string) => boolean;
22
34
  /** Whether a parsed owner action is one the app may prepare (signer `wallet` is checked separately). */
@@ -63,6 +75,8 @@ export declare const appConsentView: z.ZodObject<{
63
75
  owner: "owner";
64
76
  payout: "payout";
65
77
  read: "read";
78
+ treasury: "treasury";
79
+ "treasury-execute": "treasury-execute";
66
80
  }>>;
67
81
  expiresAt: z.ZodNumber;
68
82
  createdAt: z.ZodNumber;
@@ -83,6 +97,8 @@ export declare const appConsentList: z.ZodObject<{
83
97
  owner: "owner";
84
98
  payout: "payout";
85
99
  read: "read";
100
+ treasury: "treasury";
101
+ "treasury-execute": "treasury-execute";
86
102
  }>>;
87
103
  expiresAt: z.ZodNumber;
88
104
  createdAt: z.ZodNumber;
@@ -104,6 +120,8 @@ export declare const appConsentStored: z.ZodObject<{
104
120
  owner: "owner";
105
121
  payout: "payout";
106
122
  read: "read";
123
+ treasury: "treasury";
124
+ "treasury-execute": "treasury-execute";
107
125
  }>>;
108
126
  expiresAt: z.ZodNumber;
109
127
  createdAt: z.ZodNumber;
@@ -127,6 +145,8 @@ export declare const appSetupStored: z.ZodObject<{
127
145
  owner: "owner";
128
146
  payout: "payout";
129
147
  read: "read";
148
+ treasury: "treasury";
149
+ "treasury-execute": "treasury-execute";
130
150
  }>>;
131
151
  expiresAt: z.ZodNumber;
132
152
  createdAt: z.ZodNumber;
@@ -16,9 +16,25 @@ export const APP_OWNER = ["owner/prepare", "owner/submit", "owner/status", "owne
16
16
  * nothing out.
17
17
  */
18
18
  export const APP_PAYOUT = ["payouts/request", "payouts/status", "held", "deposit"];
19
- const READS = new Set(APP_READS), OWNER = new Set(APP_OWNER), PAYOUT = new Set(APP_PAYOUT);
19
+ /**
20
+ * Treasury with `treasury` consent (docs/enforcement.md, "Treasury proposals"): reads; strategy settings, which an
21
+ * app's plans, a machine credential's plans and the autopilot use only under a grant binding their digest; autopilot on
22
+ * (under such a grant) and off; sweeping public balances into the confidential one; and proposals, whose swaps execute
23
+ * only after the owner signs their exact terms.
24
+ */
25
+ export const APP_TREASURY = ["strategy/get", "autopilot/get", "autopilot/evidence", "autopilot/log", "treasury/get", "strategy/save",
26
+ "autopilot/enable", "autopilot/stop", "sweep", "proposals/create", "proposals/get", "proposals/list", "proposals/approve",
27
+ "proposals/reject", "proposals/withdraw", "proposals/execute"];
28
+ /**
29
+ * With `treasury-execute` as well: plans prepared and executed within the owner's policy and the strategy's cost limit,
30
+ * without a signature per swap, and only under a grant binding the plan's strategy (src/planning/strategy-grant.ts).
31
+ */
32
+ export const APP_TREASURY_EXECUTE = ["rebalance", "externalPlan", "executePlan"];
33
+ const READS = new Set(APP_READS), OWNER = new Set(APP_OWNER), PAYOUT = new Set(APP_PAYOUT), TREASURY = new Set(APP_TREASURY), TREASURY_EXECUTE = new Set(APP_TREASURY_EXECUTE);
20
34
  /** Whether consent scopes cover a procedure (the gateway also requires the owner's own access to it). */
21
- export const appScopesCover = (scopes, path) => (scopes.includes("read") && READS.has(path)) || (scopes.includes("owner") && OWNER.has(path)) || (scopes.includes("payout") && PAYOUT.has(path));
35
+ export const appScopesCover = (scopes, path) => (scopes.includes("read") && READS.has(path)) || (scopes.includes("owner") && OWNER.has(path)) || (scopes.includes("payout") && PAYOUT.has(path))
36
+ || (scopes.includes("treasury") && TREASURY.has(path))
37
+ || (scopes.includes("treasury") && scopes.includes("treasury-execute") && TREASURY_EXECUTE.has(path));
22
38
  /**
23
39
  * What the app may ask an owner to sign. The app chooses what it asks for, but never what the owner reads: a prepared
24
40
  * intent reaches the app only when it is NEP-413, whose text the owner's wallet shows. A NEP-366 intent (creation,
@@ -1,7 +1,8 @@
1
1
  import type { z } from "zod";
2
2
  import { type AppOwnerAction, type AppProcedure } from "./app-access.js";
3
3
  import type { appConsentSubmission } from "./app-consent.js";
4
- import { type OwnerCallOptions, type OwnerOutput, type OwnerResult } from "./owner-client.js";
4
+ import { type OwnerCallOptions, type OwnerFailure, type OwnerOutput, type OwnerResult } from "./owner-client.js";
5
+ import type { ownerStreams } from "./owner-outputs.js";
5
6
  import type { OwnerInputs } from "./owner-inputs.js";
6
7
  type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
7
8
  export type AppBase = `https://${string}` | `http://${string}`;
@@ -45,6 +46,8 @@ export type AppInputs = {
45
46
  signer: "wallet";
46
47
  } : OwnerInputs[P];
47
48
  };
49
+ /** The app procedures that answer an event stream. */
50
+ type AppStream = Extract<AppProcedure, (typeof ownerStreams)[number]>;
48
51
  /**
49
52
  * A server-side client for an app deployment's routes under `/apps/v1` on the platform host. It adds the deployment's
50
53
  * service credential (a server secret: never give it to a browser). Agent calls go through `ownerClient`, with its result
@@ -78,7 +81,7 @@ export declare function appClient({ base, credential, fetch: send }: {
78
81
  owner: string;
79
82
  agent: string;
80
83
  agentId: string;
81
- scopes: ("owner" | "payout" | "read")[];
84
+ scopes: ("owner" | "payout" | "read" | "treasury" | "treasury-execute")[];
82
85
  expiresAt: number;
83
86
  createdAt: number;
84
87
  publicKey: string;
@@ -98,7 +101,7 @@ export declare function appClient({ base, credential, fetch: send }: {
98
101
  owner: string;
99
102
  agent: string;
100
103
  agentId: string;
101
- scopes: ("owner" | "payout" | "read")[];
104
+ scopes: ("owner" | "payout" | "read" | "treasury" | "treasury-execute")[];
102
105
  expiresAt: number;
103
106
  createdAt: number;
104
107
  publicKey: string;
@@ -117,7 +120,7 @@ export declare function appClient({ base, credential, fetch: send }: {
117
120
  owner: string;
118
121
  agent: string;
119
122
  agentId: string;
120
- scopes: ("owner" | "payout" | "read")[];
123
+ scopes: ("owner" | "payout" | "read" | "treasury" | "treasury-execute")[];
121
124
  expiresAt: number;
122
125
  createdAt: number;
123
126
  publicKey: string;
@@ -131,9 +134,11 @@ export declare function appClient({ base, credential, fetch: send }: {
131
134
  revoked: true;
132
135
  }>>;
133
136
  };
134
- /** One Organization's agent: reads and owner actions its live consents cover. */
137
+ /** One Organization's agent: reads, owner actions and Treasury procedures its live consents cover. */
135
138
  agent(organization: string, agent: string): {
136
- call<P extends AppProcedure>(path: P, ...[input, options]: undefined extends AppInputs[P] ? [input?: AppInputs[P], options?: OwnerCallOptions] : [input: AppInputs[P], options?: OwnerCallOptions]): Promise<OwnerResult<OwnerOutput<P>>>;
139
+ call<P extends Exclude<AppProcedure, AppStream>>(path: P, ...[input, options]: undefined extends AppInputs[P] ? [input?: AppInputs[P], options?: OwnerCallOptions] : [input: AppInputs[P], options?: OwnerCallOptions]): Promise<OwnerResult<OwnerOutput<P>>>;
140
+ /** A stream's events (`sweep`, and with `treasury-execute` `rebalance` and `executePlan`), as `ownerClient.stream` reads them. */
141
+ stream<P extends AppStream>(path: P, input: AppInputs[P], options?: OwnerCallOptions): AsyncGenerator<OwnerOutput<P>, OwnerFailure | undefined>;
137
142
  };
138
143
  };
139
144
  export type AppClient = ReturnType<typeof appClient>;
@@ -48,13 +48,17 @@ export function appClient({ base, credential, fetch: send = globalThis.fetch })
48
48
  /** Permanent: the owner signs a new consent to restore access. */
49
49
  revoke: (id) => route(`/consents/${encodeURIComponent(id)}`, appRevoked, { method: "DELETE" }),
50
50
  },
51
- /** One Organization's agent: reads and owner actions its live consents cover. */
51
+ /** One Organization's agent: reads, owner actions and Treasury procedures its live consents cover. */
52
52
  agent(organization, agent) {
53
53
  const owner = ownerClient({ base: `${root}/apps/v1/o/${encodeURIComponent(organization)}/a/${encodeURIComponent(agent)}`, fetch: authorized });
54
54
  return {
55
55
  call(path, ...[input, options]) {
56
56
  return owner.call(path, input, options);
57
57
  },
58
+ /** A stream's events (`sweep`, and with `treasury-execute` `rebalance` and `executePlan`), as `ownerClient.stream` reads them. */
59
+ stream(path, input, options) {
60
+ return owner.stream(path, input, options);
61
+ },
58
62
  };
59
63
  },
60
64
  };
@@ -1,7 +1,9 @@
1
1
  import { z } from "zod";
2
2
  /**
3
3
  * An agent owner's consent for one app deployment to act for one Organization on one agent: `read` for the listed
4
- * reads, `owner` to prepare and submit owner actions that only the owner's own wallet signature completes.
4
+ * reads, `owner` to prepare and submit owner actions that only the owner's own wallet signature completes, `payout` for
5
+ * payout requests, `treasury` for Treasury reads, settings and proposals the owner approves later, and
6
+ * `treasury-execute` (only with `treasury`) to prepare and execute plans within the policy without a signature per swap.
5
7
  * The owner signs `consentMessage(...)` with NEP-413, recipient the platform host. The platform accepts only the exact
6
8
  * canonical text, so what was signed and what is stored are the same bytes.
7
9
  */
@@ -10,6 +12,8 @@ export declare const AppScope: z.ZodEnum<{
10
12
  owner: "owner";
11
13
  payout: "payout";
12
14
  read: "read";
15
+ treasury: "treasury";
16
+ "treasury-execute": "treasury-execute";
13
17
  }>;
14
18
  export type AppScope = z.infer<typeof AppScope>;
15
19
  /** Stands for an agent its server reports not created yet; only the platform's setup consent carries it, never a signed text. */
@@ -27,6 +31,8 @@ export declare const appConsent: z.ZodObject<{
27
31
  owner: "owner";
28
32
  payout: "payout";
29
33
  read: "read";
34
+ treasury: "treasury";
35
+ "treasury-execute": "treasury-execute";
30
36
  }>>;
31
37
  expiresAt: z.ZodISODateTime;
32
38
  }, z.core.$strict>;
@@ -1,12 +1,14 @@
1
1
  import { z } from "zod";
2
2
  /**
3
3
  * An agent owner's consent for one app deployment to act for one Organization on one agent: `read` for the listed
4
- * reads, `owner` to prepare and submit owner actions that only the owner's own wallet signature completes.
4
+ * reads, `owner` to prepare and submit owner actions that only the owner's own wallet signature completes, `payout` for
5
+ * payout requests, `treasury` for Treasury reads, settings and proposals the owner approves later, and
6
+ * `treasury-execute` (only with `treasury`) to prepare and execute plans within the policy without a signature per swap.
5
7
  * The owner signs `consentMessage(...)` with NEP-413, recipient the platform host. The platform accepts only the exact
6
8
  * canonical text, so what was signed and what is stored are the same bytes.
7
9
  */
8
10
  export const APP_CONSENT_TYPE = "multiagency.app-consent";
9
- export const AppScope = z.enum(["owner", "payout", "read"]);
11
+ export const AppScope = z.enum(["owner", "payout", "read", "treasury", "treasury-execute"]);
10
12
  const name = z.string().regex(/^[a-z0-9-]{1,32}$/);
11
13
  /** Stands for an agent its server reports not created yet; only the platform's setup consent carries it, never a signed text. */
12
14
  export const UNBORN_AGENT = "unborn";
@@ -21,7 +23,8 @@ export const appConsent = z.strictObject({
21
23
  agent: name,
22
24
  /** The agent's provider ID when the owner signs: a replacement agent under the same name isn't covered. */
23
25
  agentId: z.string().min(1).max(128).refine(id => id !== UNBORN_AGENT, "Not an agent ID"),
24
- scopes: z.array(AppScope).min(1).refine(values => new Set(values).size === values.length, "Scopes must be distinct"),
26
+ scopes: z.array(AppScope).min(1).refine(values => new Set(values).size === values.length, "Scopes must be distinct")
27
+ .refine(values => !values.includes("treasury-execute") || values.includes("treasury"), "treasury-execute needs treasury"),
25
28
  expiresAt: z.iso.datetime(),
26
29
  });
27
30
  /** The canonical text to sign: fixed key order, sorted scopes, no whitespace. */
@@ -22,5 +22,6 @@ export { intentsAccount, isNearcomAddress, validIntentsAccount } from "./intents
22
22
  export { mayCall, type Access } from "./permissions.js";
23
23
  export * from "./gateway.js";
24
24
  export * from "./proposals.js";
25
+ export * from "./treasury-proposals.js";
25
26
  export * from "./run-progress-schema.js";
26
27
  export { RecordingSchema, MAX_RECORDING_BYTES, AGUI_VERSION, AGUI_PROTOCOL_VERSION, type Recording, type RunLogEvent } from "./run-log-schema.js";
@@ -21,5 +21,6 @@ export { intentsAccount, isNearcomAddress, validIntentsAccount } from "./intents
21
21
  export { mayCall } from "./permissions.js";
22
22
  export * from "./gateway.js";
23
23
  export * from "./proposals.js";
24
+ export * from "./treasury-proposals.js";
24
25
  export * from "./run-progress-schema.js";
25
26
  export { RecordingSchema, MAX_RECORDING_BYTES, AGUI_VERSION, AGUI_PROTOCOL_VERSION } from "./run-log-schema.js";
@@ -42,6 +42,11 @@ export declare const ownerCapabilities: {
42
42
  readonly routes: readonly [];
43
43
  readonly procedures: readonly ["rebalance", "externalPlan", "executePlan", "steward/propose"];
44
44
  };
45
+ /** Proposals an app makes and the owner approves later (docs/enforcement.md, "Treasury proposals"); apps only. */
46
+ readonly "treasury-proposals-v1": {
47
+ readonly routes: readonly [];
48
+ readonly procedures: readonly ["proposals/create", "proposals/get", "proposals/list", "proposals/approve", "proposals/reject", "proposals/withdraw", "proposals/execute"];
49
+ };
45
50
  readonly "treasury-recovery-v1": {
46
51
  readonly routes: readonly [];
47
52
  readonly procedures: readonly ["recover"];
@@ -15,6 +15,8 @@ export const ownerCapabilities = {
15
15
  "treasury-observe-v1": { routes: [], procedures: ["strategy/get", "autopilot/evidence", "autopilot/log", "ledger", "ledgerCsv", "reconcileBooks"] },
16
16
  "treasury-strategy-v1": { routes: [], procedures: ["strategy/save", "autopilot/set", "autopilot/enable", "autopilot/tick"] },
17
17
  "treasury-plans-v1": { routes: [], procedures: ["rebalance", "externalPlan", "executePlan", "steward/propose"] },
18
+ /** Proposals an app makes and the owner approves later (docs/enforcement.md, "Treasury proposals"); apps only. */
19
+ "treasury-proposals-v1": { routes: [], procedures: ["proposals/create", "proposals/get", "proposals/list", "proposals/approve", "proposals/reject", "proposals/withdraw", "proposals/execute"] },
18
20
  "treasury-recovery-v1": { routes: [], procedures: ["recover"] },
19
21
  "payables-payments-v1": { routes: [], procedures: ["pay", "paymentHistory", "paymentStatus", "invoices", "cancelPreparedInvoice", "ledger", "ledgerCsv", "reconcileBooks"] },
20
22
  "payables-payout-requests-v1": { routes: [], procedures: ["payouts/request", "payouts/status"] },
@@ -912,7 +912,7 @@ export declare const ownerContract: {
912
912
  plan: {
913
913
  [x: string]: unknown;
914
914
  id: string;
915
- source: "external" | "rebalance" | "steward";
915
+ source: "external" | "proposal" | "rebalance" | "steward";
916
916
  createdAt: string;
917
917
  expiresAt: string;
918
918
  instruction: string;
@@ -940,6 +940,7 @@ export declare const ownerContract: {
940
940
  public: string;
941
941
  confidential: string;
942
942
  }>;
943
+ proposalId?: string | undefined;
943
944
  };
944
945
  steps: (number | undefined)[];
945
946
  }, unknown, void>, import("@orpc/contract").AsyncIteratorClass<{
@@ -1008,7 +1009,7 @@ export declare const ownerContract: {
1008
1009
  plan: {
1009
1010
  [x: string]: unknown;
1010
1011
  id: string;
1011
- source: "external" | "rebalance" | "steward";
1012
+ source: "external" | "proposal" | "rebalance" | "steward";
1012
1013
  createdAt: string;
1013
1014
  expiresAt: string;
1014
1015
  instruction: string;
@@ -1036,6 +1037,7 @@ export declare const ownerContract: {
1036
1037
  public: string;
1037
1038
  confidential: string;
1038
1039
  }>;
1040
+ proposalId?: string | undefined;
1039
1041
  };
1040
1042
  steps: (number | undefined)[];
1041
1043
  }, unknown, void>>, {
@@ -1,5 +1,6 @@
1
1
  import type { z } from "zod";
2
2
  import type { agreementCommand, compensationSend, paymentPreparation, reviewCommand } from "../compensation-contract.js";
3
+ import type { proposalCreateInput, proposalRef, proposalSigned } from "./treasury-proposals.js";
3
4
  import type { depositInput, executePlanInput, ownerAction, ownerPrepareInput, ownerStatusInput, ownerSubmitInput, payoutRequestInput, payoutStatusInput } from "./inputs.js";
4
5
  /**
5
6
  * Owner procedure inputs as the router accepts them (before server-side parsing). Procedures in the owner contract
@@ -154,6 +155,13 @@ export type OwnerInputs = {
154
155
  minAmountOut?: string;
155
156
  }[];
156
157
  };
158
+ "proposals/create": In<typeof proposalCreateInput>;
159
+ "proposals/get": In<typeof proposalRef>;
160
+ "proposals/list": undefined;
161
+ "proposals/approve": In<typeof proposalSigned>;
162
+ "proposals/reject": In<typeof proposalSigned>;
163
+ "proposals/withdraw": In<typeof proposalRef>;
164
+ "proposals/execute": In<typeof proposalRef>;
157
165
  rebalance: undefined;
158
166
  executePlan: In<typeof executePlanInput>;
159
167
  sweep: {