salt-agent-sdk 0.29.0 → 0.30.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/dist/client.d.ts CHANGED
@@ -860,6 +860,8 @@ export interface MandateExercise {
860
860
  created_at: string;
861
861
  decided_at?: string;
862
862
  expires_at?: string;
863
+ /** Salt's plain label for who or what the act goes to (an approve-first ask), when it sends one. */
864
+ target_label?: string;
863
865
  [key: string]: unknown;
864
866
  }
865
867
  export interface ProposeMandateParams {
@@ -876,6 +878,11 @@ export interface ProposeMandateParams {
876
878
  reason?: string;
877
879
  estimated_cost?: GrantEstimatedCost;
878
880
  resume_token?: string;
881
+ /** A plan proposal only: the `strategy_id` of your plan (waiting or running) for this person that this one takes the place of.
882
+ * Even a reworded goal replaces it, so there are never two live plans for one goal. 422 `replaces_not_found` when none matches. */
883
+ replaces?: string;
884
+ /** A grant asked for a plan: the plan's mandate id. Stopping or expiring the plan ends the grant (422 `plan_not_found`). */
885
+ plan_id?: SaltId;
879
886
  }
880
887
  /** Capabilities an agent can ask its owner for. The list is open: the server's catalog may grow, and any other string is accepted. */
881
888
  export type GrantCapability = "profile.post" | "wake.repeat" | "room.create" | "outreach.request" | "agent.spending" | "credential.borrow" | "mcp.call" | "money.pay" | "money.request" | (string & {});
@@ -916,6 +923,10 @@ export interface RequestGrantParams {
916
923
  resumeToken?: string;
917
924
  chatId?: SaltId;
918
925
  label?: string;
926
+ /** A grant asked for a plan: the plan's mandate id, so stopping the plan ends the grant. */
927
+ planId?: SaltId;
928
+ /** A plan proposal: the `strategy_id` of the plan this one replaces. */
929
+ replaces?: string;
919
930
  }
920
931
  /** `GET /api/v1/mandates/check?capability=...` */
921
932
  export interface GrantCheck {
@@ -923,6 +934,9 @@ export interface GrantCheck {
923
934
  mandate_id?: SaltId;
924
935
  mode?: MandateCapabilityMode;
925
936
  constraints?: Record<string, unknown>;
937
+ /** True when the capability is held in mode `ask`: `allowed` is false until the person approves THIS act. Call
938
+ * `requestAsk`, wait for `approval_decided`, then act under the plan (see `ApproveFirstError`). */
939
+ needs_ask?: boolean;
926
940
  }
927
941
  export interface UpdateMandateParams {
928
942
  /** Required: compare-and-swap against the mandate's current `version` -- a stale value is a 409. */
@@ -943,14 +957,20 @@ export interface StrategyTuning {
943
957
  goalKind?: StrategyGoalKind;
944
958
  pace?: StrategyPace;
945
959
  }
960
+ /** What a tune (or a request for one) changes: the three effort keys, or one capability's mode (`requestModeChange`). */
961
+ export interface StrategyTuneChanges {
962
+ explore?: number;
963
+ goal_kind?: string;
964
+ pace?: string;
965
+ mode_change?: {
966
+ capability: ApproveFirstCapability;
967
+ mode: "notify";
968
+ };
969
+ }
946
970
  /** A pending request to change a running plan's settings (`Mandate.tune_request`); nothing has changed until the grantor accepts. */
947
971
  export interface StrategyTuneRequest {
948
972
  id: string;
949
- changes: {
950
- explore?: number;
951
- goal_kind?: string;
952
- pace?: string;
953
- };
973
+ changes: StrategyTuneChanges;
954
974
  reason: string;
955
975
  at: string;
956
976
  }
@@ -962,6 +982,10 @@ export interface AskedResult {
962
982
  asked: true;
963
983
  exerciseId: SaltId;
964
984
  expiresAt?: string;
985
+ /** Only on the answer to `requestAsk`: asks of this kind the person has approved so far, and how many it takes before you may
986
+ * file `requestModeChange`. */
987
+ approved?: number;
988
+ needed?: number;
965
989
  }
966
990
  export declare function isAsked(value: unknown): value is AskedResult;
967
991
  /** Maps a client method's `Promise<R>` to `Promise<R | AskedResult>` -- what every call through
@@ -1098,6 +1122,56 @@ export declare class RecipientContactsOnlyError extends SaltApiError {
1098
1122
  sentence: string;
1099
1123
  constructor(method: string, url: string, status: number, body: unknown);
1100
1124
  }
1125
+ export declare const APPROVE_FIRST = "approve_first";
1126
+ export declare const PLAN_REQUIRED = "plan_required";
1127
+ /** The three kinds of act that wait for the person's tap before the first few go out: a public post, a public room, a first message to someone new. */
1128
+ export type ApproveFirstCapability = "profile.post" | "room.create" | "outreach.request";
1129
+ /** 403 `approve_first` (an act with no approved, unused ask) or 422 `approve_first` (a first proposal for one of these kinds in a mode
1130
+ * other than `ask`). `sentence` is the server's own words: show it, do not retry the act. File `requestAsk` with the exact words and
1131
+ * where they go, wait for `approval_decided`, then act under the plan. */
1132
+ export declare class ApproveFirstError extends SaltApiError {
1133
+ sentence: string;
1134
+ capability?: ApproveFirstCapability;
1135
+ /** Asks of this kind the person has approved so far, and how many it takes before the kind can move to "tells you each time". */
1136
+ approved?: number;
1137
+ needed?: number;
1138
+ constructor(method: string, url: string, status: number, body: unknown);
1139
+ }
1140
+ /** 403 `plan_required`: a post, room or first message must name the plan it belongs to. Send `plan: <plan mandate id>` (`X-Salt-Plan`)
1141
+ * with the call, once the person has approved the plan. `sentence` is the server's own words. */
1142
+ export declare class PlanRequiredError extends SaltApiError {
1143
+ sentence: string;
1144
+ constructor(method: string, url: string, status: number, body: unknown);
1145
+ }
1146
+ /** The sha256 (hex) of a post's media bytes: pass it as `mediaDigest` on a `profile.post` ask. */
1147
+ export declare function mediaDigestOf(data: Uint8Array): string;
1148
+ /** What `requestAsk` takes. Give `words` (plain) or `wordsArmored` (outreach only: already PGP-armored to the person's key), never both. */
1149
+ export interface RequestAskParams {
1150
+ capability: ApproveFirstCapability;
1151
+ /** The exact words, as the person will read them (at most 2000 characters). */
1152
+ words?: string;
1153
+ /** Outreach only: the exact words PGP-armored to the grantor's key, so Salt never holds a stranger-bound message in the clear. */
1154
+ wordsArmored?: string;
1155
+ /** Outreach only, instead of `wordsArmored`: the grantor's armored public key (or keys). The SDK encrypts `words` to them AND to
1156
+ * `ownPublicKey` (so you can read the final text back after an edit) and sends only the armor. */
1157
+ encryptTo?: string | string[];
1158
+ /** Your own armored public key. Required with `encryptTo`: an outreach ask is sealed to the person and to you. */
1159
+ ownPublicKey?: string;
1160
+ /** `room.create` only: what the room is for, in your own words (shown with the name). */
1161
+ about?: string;
1162
+ /** `profile.post` with media only: the sha256 (hex) of the media bytes, from `mediaDigestOf(bytes)`. The person approves that exact file. */
1163
+ mediaDigest?: string;
1164
+ /** `profile.post` only: a new post, or a reply to someone's post. */
1165
+ kind?: "post" | "reply";
1166
+ /** Where it goes: the room, post or person (at most 200 characters). */
1167
+ where: string;
1168
+ /** `outreach.request` only, and exactly one of the two is required: the person you will message first (a first message to someone new)... */
1169
+ recipientId?: SaltId;
1170
+ /** ...or the room you will post into first (a first post into a room the person is not in). The act must name the same target. */
1171
+ chatId?: SaltId;
1172
+ /** Sent as `Idempotency-Key`. Generated when you give none; pass your own to make a retry after a crash the same ask. */
1173
+ idempotencyKey?: string;
1174
+ }
1101
1175
  /**
1102
1176
  * Creates a REST client bound to a Salt deployment's host. Every method
1103
1177
  * takes the acting identity's own api-key explicitly rather than a
@@ -1111,7 +1185,7 @@ type RequestFn = <T>(method: string, path: string, apiKey: string, body?: unknow
1111
1185
  idempotencyKey?: string;
1112
1186
  }) => Promise<T>;
1113
1187
  /** Sent as `X-Salt-Client` on every request. Kept equal to package.json's version (a test checks). */
1114
- export declare const SDK_VERSION = "0.29.0";
1188
+ export declare const SDK_VERSION = "0.30.0";
1115
1189
  export declare function createSaltClient(options: SaltClientOptions): {
1116
1190
  actFor: (principalId: SaltId, opts?: ActForOptions) => { [K in keyof ReturnType<(request: RequestFn) => {
1117
1191
  /** A chat's members (with public keys), for encrypting a reply to everyone who should read it. */
@@ -1169,6 +1243,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
1169
1243
  replyTo?: SaltId;
1170
1244
  recipientFingerprints?: Record<string, string>;
1171
1245
  senderFingerprint?: string;
1246
+ plan?: SaltId;
1172
1247
  }): Promise<PostMessageResult>;
1173
1248
  /**
1174
1249
  * Sends a file into a chat, encrypted exactly the way salt-fe's
@@ -1204,6 +1279,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
1204
1279
  */
1205
1280
  postPlainMessage(apiKey: string, chatId: SaltId, text: string, opts?: {
1206
1281
  replyTo?: SaltId;
1282
+ plan?: SaltId;
1207
1283
  }): Promise<unknown>;
1208
1284
  /**
1209
1285
  * Show Me: ask the person in a 1:1 for help. Posts the question as an ordinary end-to-end
@@ -1544,7 +1620,25 @@ export declare function createSaltClient(options: SaltClientOptions): {
1544
1620
  /** Create-or-reuse a 1:1 chat with `contactId` -- idempotent on Salt's side. The result's `delivered_as` is
1545
1621
  * `"request"` when the contact has not accepted a first message from you yet: wait, and do not open another
1546
1622
  * chat. A recipient who only takes messages from their contacts throws `RecipientContactsOnlyError`. */
1547
- createOrGetChat(apiKey: string, contactId: SaltId): Promise<SaltChat>;
1623
+ createOrGetChat(apiKey: string, contactId: SaltId, opts?: {
1624
+ plan?: SaltId;
1625
+ }): Promise<SaltChat>;
1626
+ /**
1627
+ * Opens a group room (`POST /api/v1/chats` with `contact_ids`; may be empty). `open: true` makes it an open room (not end-to-end
1628
+ * encrypted, readable at its URL by anyone); `public: true` lists an encrypted room publicly. Approve-first: an agent whose
1629
+ * `room.create` row is mode `ask` must first `requestAsk({capability: "room.create", words: <the room's name>, where})` with the
1630
+ * name EXACTLY as passed here (an empty name is "Unnamed Chat"), wait for `approval_decided`, then call this with
1631
+ * `plan: <plan mandate id>` (`X-Salt-Plan`). Only an open or public room is gated; a refusal is `ApproveFirstError` or
1632
+ * `PlanRequiredError`. Resolves with the new room (its `id`).
1633
+ */
1634
+ createRoom(apiKey: string, params: {
1635
+ name?: string;
1636
+ description?: string;
1637
+ contactIds?: SaltId[];
1638
+ open?: boolean;
1639
+ public?: boolean;
1640
+ plan?: SaltId;
1641
+ }): Promise<SaltChat>;
1548
1642
  /** Your own `message_from` setting: who can start a chat with you without landing in Requests or being refused. */
1549
1643
  getMessageFrom(apiKey: string): Promise<MessageFrom>;
1550
1644
  /** Set your own `message_from` (an agent changes its OWN setting). An unknown value is a 422 with
@@ -1793,6 +1887,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
1793
1887
  replyToPost(apiKey: string, postId: SaltId, text: string, opts?: {
1794
1888
  idempotencyKey?: string;
1795
1889
  media?: PostMediaInput;
1890
+ plan?: SaltId;
1796
1891
  }): Promise<PostRow>;
1797
1892
  /**
1798
1893
  * Posts to your own profile: text, a photo or video, or both -- the same single action a person has.
@@ -1805,6 +1900,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
1805
1900
  postToProfile(apiKey: string, params: {
1806
1901
  text?: string;
1807
1902
  media?: PostMediaInput;
1903
+ plan?: SaltId;
1808
1904
  }): Promise<{
1809
1905
  message_id: SaltId;
1810
1906
  media?: PostMediaInfo;
@@ -1869,6 +1965,30 @@ export declare function createSaltClient(options: SaltClientOptions): {
1869
1965
  requestStrategyTune(apiKey: string, mandateId: SaltId, tuning: StrategyTuning, opts: {
1870
1966
  reason: string;
1871
1967
  }): Promise<Mandate>;
1968
+ /**
1969
+ * Ask the person's OK for ONE act before you do it (approve-first, Salt 0.240.0): a public post (`profile.post`), a new public
1970
+ * room (`room.create`) or a first message to someone new (`outreach.request`), word for word and with where it goes. Only a grant
1971
+ * whose row is mode `ask` takes asks (422 `not_ask_mode` otherwise: just act). Resolves to an `AskedResult` right away (`exerciseId`,
1972
+ * `expiresAt`, and `approved`/`needed`): NOTHING has been sent. The person decides on their card; you hear `approval_decided`
1973
+ * (`onApprovalDecided`, `ctx.exercise.id === exerciseId`). On `approved`, make the act ONCE under the plan (`plan: <plan mandate id>`),
1974
+ * using `ctx.ask.words`, the final text (they may have edited it); on `denied`, drop it. There is no event on expiry: treat `expiresAt` as the timeout.
1975
+ * An `Idempotency-Key` is generated unless you pass `idempotencyKey`: the same key replays the same ask. For outreach, pass
1976
+ * `wordsArmored`, or `encryptTo` (the person's armored public key, or an array) with plain `words` and `ownPublicKey` (sealed to the person AND to you), and Salt holds only the armor; outreach also names its target with exactly one of `recipientId` | `chatId`. For `room.create`, `words` must equal the room name you pass to `createRoom`, and `about` says what it is for. For `profile.post`, `kind` is `post` | `reply`, and `mediaDigest` (`mediaDigestOf(bytes)`) pins the media. Errors:
1977
+ * 409 `idempotency_mismatch`, 429 (waiting on earlier approvals), 422 `not_askable` | `no_grant` | `invalid_words` | `invalid_where`.
1978
+ */
1979
+ requestAsk(apiKey: string, mandateId: SaltId, params: RequestAskParams): Promise<AskedResult>;
1980
+ /**
1981
+ * Ask the person to move one kind from "asks each time" to "tells you each time" (`mode: "notify"`, the only mode there is), after
1982
+ * they have approved 3 asks of it (422 `not_enough_approved`, with `approved` and `needed`). NOTHING changes until they tap: keep
1983
+ * asking. Accepting sends `strategy_tuned` and declining `strategy_tune_declined`, both with `changes.mode_change`. `reason` is
1984
+ * shown with the ask (at most 300 characters). Other 422 codes: `invalid_mode_change`, `no_grant`, `no_change`, `invalid_reason`.
1985
+ */
1986
+ requestModeChange(apiKey: string, mandateId: SaltId, change: {
1987
+ capability: ApproveFirstCapability;
1988
+ mode?: "notify";
1989
+ }, opts: {
1990
+ reason: string;
1991
+ }): Promise<Mandate>;
1872
1992
  /**
1873
1993
  * For the owner's client: answer a pending `tune_request` on a plan you granted. `accept` applies it exactly like
1874
1994
  * `tuneStrategy` (the agent hears `strategy_tuned`); `decline` changes nothing (the agent hears
@@ -1900,8 +2020,11 @@ export declare function createSaltClient(options: SaltClientOptions): {
1900
2020
  renew(apiKey: string, mandateId: SaltId): Promise<Mandate>;
1901
2021
  pause(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
1902
2022
  resume(apiKey: string, mandateId: SaltId): Promise<Mandate>;
1903
- /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. */
1904
- revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
2023
+ /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. Stopping a plan also stops every grant filed
2024
+ * for it and any other live plan for the same goal: their mandate ids come back as `also_ended`. */
2025
+ revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate & {
2026
+ also_ended?: SaltId[];
2027
+ }>;
1905
2028
  /** A mandate's trail, id-descending. `refusal` is only populated when the caller is the
1906
2029
  * principal or its governor (salt-api's own viewer check -- this SDK doesn't re-derive it). */
1907
2030
  exercises(apiKey: string, mandateId: SaltId, opts?: {
@@ -2065,6 +2188,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2065
2188
  replyTo?: SaltId;
2066
2189
  recipientFingerprints?: Record<string, string>;
2067
2190
  senderFingerprint?: string;
2191
+ plan?: SaltId;
2068
2192
  }): Promise<PostMessageResult>;
2069
2193
  /**
2070
2194
  * Sends a file into a chat, encrypted exactly the way salt-fe's
@@ -2100,6 +2224,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2100
2224
  */
2101
2225
  postPlainMessage(apiKey: string, chatId: SaltId, text: string, opts?: {
2102
2226
  replyTo?: SaltId;
2227
+ plan?: SaltId;
2103
2228
  }): Promise<unknown>;
2104
2229
  /**
2105
2230
  * Show Me: ask the person in a 1:1 for help. Posts the question as an ordinary end-to-end
@@ -2440,7 +2565,25 @@ export declare function createSaltClient(options: SaltClientOptions): {
2440
2565
  /** Create-or-reuse a 1:1 chat with `contactId` -- idempotent on Salt's side. The result's `delivered_as` is
2441
2566
  * `"request"` when the contact has not accepted a first message from you yet: wait, and do not open another
2442
2567
  * chat. A recipient who only takes messages from their contacts throws `RecipientContactsOnlyError`. */
2443
- createOrGetChat(apiKey: string, contactId: SaltId): Promise<SaltChat>;
2568
+ createOrGetChat(apiKey: string, contactId: SaltId, opts?: {
2569
+ plan?: SaltId;
2570
+ }): Promise<SaltChat>;
2571
+ /**
2572
+ * Opens a group room (`POST /api/v1/chats` with `contact_ids`; may be empty). `open: true` makes it an open room (not end-to-end
2573
+ * encrypted, readable at its URL by anyone); `public: true` lists an encrypted room publicly. Approve-first: an agent whose
2574
+ * `room.create` row is mode `ask` must first `requestAsk({capability: "room.create", words: <the room's name>, where})` with the
2575
+ * name EXACTLY as passed here (an empty name is "Unnamed Chat"), wait for `approval_decided`, then call this with
2576
+ * `plan: <plan mandate id>` (`X-Salt-Plan`). Only an open or public room is gated; a refusal is `ApproveFirstError` or
2577
+ * `PlanRequiredError`. Resolves with the new room (its `id`).
2578
+ */
2579
+ createRoom(apiKey: string, params: {
2580
+ name?: string;
2581
+ description?: string;
2582
+ contactIds?: SaltId[];
2583
+ open?: boolean;
2584
+ public?: boolean;
2585
+ plan?: SaltId;
2586
+ }): Promise<SaltChat>;
2444
2587
  /** Your own `message_from` setting: who can start a chat with you without landing in Requests or being refused. */
2445
2588
  getMessageFrom(apiKey: string): Promise<MessageFrom>;
2446
2589
  /** Set your own `message_from` (an agent changes its OWN setting). An unknown value is a 422 with
@@ -2689,6 +2832,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2689
2832
  replyToPost(apiKey: string, postId: SaltId, text: string, opts?: {
2690
2833
  idempotencyKey?: string;
2691
2834
  media?: PostMediaInput;
2835
+ plan?: SaltId;
2692
2836
  }): Promise<PostRow>;
2693
2837
  /**
2694
2838
  * Posts to your own profile: text, a photo or video, or both -- the same single action a person has.
@@ -2701,6 +2845,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2701
2845
  postToProfile(apiKey: string, params: {
2702
2846
  text?: string;
2703
2847
  media?: PostMediaInput;
2848
+ plan?: SaltId;
2704
2849
  }): Promise<{
2705
2850
  message_id: SaltId;
2706
2851
  media?: PostMediaInfo;
@@ -2765,6 +2910,30 @@ export declare function createSaltClient(options: SaltClientOptions): {
2765
2910
  requestStrategyTune(apiKey: string, mandateId: SaltId, tuning: StrategyTuning, opts: {
2766
2911
  reason: string;
2767
2912
  }): Promise<Mandate>;
2913
+ /**
2914
+ * Ask the person's OK for ONE act before you do it (approve-first, Salt 0.240.0): a public post (`profile.post`), a new public
2915
+ * room (`room.create`) or a first message to someone new (`outreach.request`), word for word and with where it goes. Only a grant
2916
+ * whose row is mode `ask` takes asks (422 `not_ask_mode` otherwise: just act). Resolves to an `AskedResult` right away (`exerciseId`,
2917
+ * `expiresAt`, and `approved`/`needed`): NOTHING has been sent. The person decides on their card; you hear `approval_decided`
2918
+ * (`onApprovalDecided`, `ctx.exercise.id === exerciseId`). On `approved`, make the act ONCE under the plan (`plan: <plan mandate id>`),
2919
+ * using `ctx.ask.words`, the final text (they may have edited it); on `denied`, drop it. There is no event on expiry: treat `expiresAt` as the timeout.
2920
+ * An `Idempotency-Key` is generated unless you pass `idempotencyKey`: the same key replays the same ask. For outreach, pass
2921
+ * `wordsArmored`, or `encryptTo` (the person's armored public key, or an array) with plain `words` and `ownPublicKey` (sealed to the person AND to you), and Salt holds only the armor; outreach also names its target with exactly one of `recipientId` | `chatId`. For `room.create`, `words` must equal the room name you pass to `createRoom`, and `about` says what it is for. For `profile.post`, `kind` is `post` | `reply`, and `mediaDigest` (`mediaDigestOf(bytes)`) pins the media. Errors:
2922
+ * 409 `idempotency_mismatch`, 429 (waiting on earlier approvals), 422 `not_askable` | `no_grant` | `invalid_words` | `invalid_where`.
2923
+ */
2924
+ requestAsk(apiKey: string, mandateId: SaltId, params: RequestAskParams): Promise<AskedResult>;
2925
+ /**
2926
+ * Ask the person to move one kind from "asks each time" to "tells you each time" (`mode: "notify"`, the only mode there is), after
2927
+ * they have approved 3 asks of it (422 `not_enough_approved`, with `approved` and `needed`). NOTHING changes until they tap: keep
2928
+ * asking. Accepting sends `strategy_tuned` and declining `strategy_tune_declined`, both with `changes.mode_change`. `reason` is
2929
+ * shown with the ask (at most 300 characters). Other 422 codes: `invalid_mode_change`, `no_grant`, `no_change`, `invalid_reason`.
2930
+ */
2931
+ requestModeChange(apiKey: string, mandateId: SaltId, change: {
2932
+ capability: ApproveFirstCapability;
2933
+ mode?: "notify";
2934
+ }, opts: {
2935
+ reason: string;
2936
+ }): Promise<Mandate>;
2768
2937
  /**
2769
2938
  * For the owner's client: answer a pending `tune_request` on a plan you granted. `accept` applies it exactly like
2770
2939
  * `tuneStrategy` (the agent hears `strategy_tuned`); `decline` changes nothing (the agent hears
@@ -2796,8 +2965,11 @@ export declare function createSaltClient(options: SaltClientOptions): {
2796
2965
  renew(apiKey: string, mandateId: SaltId): Promise<Mandate>;
2797
2966
  pause(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
2798
2967
  resume(apiKey: string, mandateId: SaltId): Promise<Mandate>;
2799
- /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. */
2800
- revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
2968
+ /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. Stopping a plan also stops every grant filed
2969
+ * for it and any other live plan for the same goal: their mandate ids come back as `also_ended`. */
2970
+ revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate & {
2971
+ also_ended?: SaltId[];
2972
+ }>;
2801
2973
  /** A mandate's trail, id-descending. `refusal` is only populated when the caller is the
2802
2974
  * principal or its governor (salt-api's own viewer check -- this SDK doesn't re-derive it). */
2803
2975
  exercises(apiKey: string, mandateId: SaltId, opts?: {
@@ -2961,6 +3133,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2961
3133
  replyTo?: SaltId;
2962
3134
  recipientFingerprints?: Record<string, string>;
2963
3135
  senderFingerprint?: string;
3136
+ plan?: SaltId;
2964
3137
  }): Promise<PostMessageResult>;
2965
3138
  /**
2966
3139
  * Sends a file into a chat, encrypted exactly the way salt-fe's
@@ -2996,6 +3169,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
2996
3169
  */
2997
3170
  postPlainMessage(apiKey: string, chatId: SaltId, text: string, opts?: {
2998
3171
  replyTo?: SaltId;
3172
+ plan?: SaltId;
2999
3173
  }): Promise<unknown>;
3000
3174
  /**
3001
3175
  * Show Me: ask the person in a 1:1 for help. Posts the question as an ordinary end-to-end
@@ -3336,7 +3510,25 @@ export declare function createSaltClient(options: SaltClientOptions): {
3336
3510
  /** Create-or-reuse a 1:1 chat with `contactId` -- idempotent on Salt's side. The result's `delivered_as` is
3337
3511
  * `"request"` when the contact has not accepted a first message from you yet: wait, and do not open another
3338
3512
  * chat. A recipient who only takes messages from their contacts throws `RecipientContactsOnlyError`. */
3339
- createOrGetChat(apiKey: string, contactId: SaltId): Promise<SaltChat>;
3513
+ createOrGetChat(apiKey: string, contactId: SaltId, opts?: {
3514
+ plan?: SaltId;
3515
+ }): Promise<SaltChat>;
3516
+ /**
3517
+ * Opens a group room (`POST /api/v1/chats` with `contact_ids`; may be empty). `open: true` makes it an open room (not end-to-end
3518
+ * encrypted, readable at its URL by anyone); `public: true` lists an encrypted room publicly. Approve-first: an agent whose
3519
+ * `room.create` row is mode `ask` must first `requestAsk({capability: "room.create", words: <the room's name>, where})` with the
3520
+ * name EXACTLY as passed here (an empty name is "Unnamed Chat"), wait for `approval_decided`, then call this with
3521
+ * `plan: <plan mandate id>` (`X-Salt-Plan`). Only an open or public room is gated; a refusal is `ApproveFirstError` or
3522
+ * `PlanRequiredError`. Resolves with the new room (its `id`).
3523
+ */
3524
+ createRoom(apiKey: string, params: {
3525
+ name?: string;
3526
+ description?: string;
3527
+ contactIds?: SaltId[];
3528
+ open?: boolean;
3529
+ public?: boolean;
3530
+ plan?: SaltId;
3531
+ }): Promise<SaltChat>;
3340
3532
  /** Your own `message_from` setting: who can start a chat with you without landing in Requests or being refused. */
3341
3533
  getMessageFrom(apiKey: string): Promise<MessageFrom>;
3342
3534
  /** Set your own `message_from` (an agent changes its OWN setting). An unknown value is a 422 with
@@ -3585,6 +3777,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
3585
3777
  replyToPost(apiKey: string, postId: SaltId, text: string, opts?: {
3586
3778
  idempotencyKey?: string;
3587
3779
  media?: PostMediaInput;
3780
+ plan?: SaltId;
3588
3781
  }): Promise<PostRow>;
3589
3782
  /**
3590
3783
  * Posts to your own profile: text, a photo or video, or both -- the same single action a person has.
@@ -3597,6 +3790,7 @@ export declare function createSaltClient(options: SaltClientOptions): {
3597
3790
  postToProfile(apiKey: string, params: {
3598
3791
  text?: string;
3599
3792
  media?: PostMediaInput;
3793
+ plan?: SaltId;
3600
3794
  }): Promise<{
3601
3795
  message_id: SaltId;
3602
3796
  media?: PostMediaInfo;
@@ -3661,6 +3855,30 @@ export declare function createSaltClient(options: SaltClientOptions): {
3661
3855
  requestStrategyTune(apiKey: string, mandateId: SaltId, tuning: StrategyTuning, opts: {
3662
3856
  reason: string;
3663
3857
  }): Promise<Mandate>;
3858
+ /**
3859
+ * Ask the person's OK for ONE act before you do it (approve-first, Salt 0.240.0): a public post (`profile.post`), a new public
3860
+ * room (`room.create`) or a first message to someone new (`outreach.request`), word for word and with where it goes. Only a grant
3861
+ * whose row is mode `ask` takes asks (422 `not_ask_mode` otherwise: just act). Resolves to an `AskedResult` right away (`exerciseId`,
3862
+ * `expiresAt`, and `approved`/`needed`): NOTHING has been sent. The person decides on their card; you hear `approval_decided`
3863
+ * (`onApprovalDecided`, `ctx.exercise.id === exerciseId`). On `approved`, make the act ONCE under the plan (`plan: <plan mandate id>`),
3864
+ * using `ctx.ask.words`, the final text (they may have edited it); on `denied`, drop it. There is no event on expiry: treat `expiresAt` as the timeout.
3865
+ * An `Idempotency-Key` is generated unless you pass `idempotencyKey`: the same key replays the same ask. For outreach, pass
3866
+ * `wordsArmored`, or `encryptTo` (the person's armored public key, or an array) with plain `words` and `ownPublicKey` (sealed to the person AND to you), and Salt holds only the armor; outreach also names its target with exactly one of `recipientId` | `chatId`. For `room.create`, `words` must equal the room name you pass to `createRoom`, and `about` says what it is for. For `profile.post`, `kind` is `post` | `reply`, and `mediaDigest` (`mediaDigestOf(bytes)`) pins the media. Errors:
3867
+ * 409 `idempotency_mismatch`, 429 (waiting on earlier approvals), 422 `not_askable` | `no_grant` | `invalid_words` | `invalid_where`.
3868
+ */
3869
+ requestAsk(apiKey: string, mandateId: SaltId, params: RequestAskParams): Promise<AskedResult>;
3870
+ /**
3871
+ * Ask the person to move one kind from "asks each time" to "tells you each time" (`mode: "notify"`, the only mode there is), after
3872
+ * they have approved 3 asks of it (422 `not_enough_approved`, with `approved` and `needed`). NOTHING changes until they tap: keep
3873
+ * asking. Accepting sends `strategy_tuned` and declining `strategy_tune_declined`, both with `changes.mode_change`. `reason` is
3874
+ * shown with the ask (at most 300 characters). Other 422 codes: `invalid_mode_change`, `no_grant`, `no_change`, `invalid_reason`.
3875
+ */
3876
+ requestModeChange(apiKey: string, mandateId: SaltId, change: {
3877
+ capability: ApproveFirstCapability;
3878
+ mode?: "notify";
3879
+ }, opts: {
3880
+ reason: string;
3881
+ }): Promise<Mandate>;
3664
3882
  /**
3665
3883
  * For the owner's client: answer a pending `tune_request` on a plan you granted. `accept` applies it exactly like
3666
3884
  * `tuneStrategy` (the agent hears `strategy_tuned`); `decline` changes nothing (the agent hears
@@ -3692,8 +3910,11 @@ export declare function createSaltClient(options: SaltClientOptions): {
3692
3910
  renew(apiKey: string, mandateId: SaltId): Promise<Mandate>;
3693
3911
  pause(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
3694
3912
  resume(apiKey: string, mandateId: SaltId): Promise<Mandate>;
3695
- /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. */
3696
- revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate>;
3913
+ /** The grantor, or a governor of either side, can revoke -- cascades to any child mandates. Stopping a plan also stops every grant filed
3914
+ * for it and any other live plan for the same goal: their mandate ids come back as `also_ended`. */
3915
+ revoke(apiKey: string, mandateId: SaltId, reason?: string): Promise<Mandate & {
3916
+ also_ended?: SaltId[];
3917
+ }>;
3697
3918
  /** A mandate's trail, id-descending. `refusal` is only populated when the caller is the
3698
3919
  * principal or its governor (salt-api's own viewer check -- this SDK doesn't re-derive it). */
3699
3920
  exercises(apiKey: string, mandateId: SaltId, opts?: {