@usepraxis/sdk 0.1.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.
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Signs the wallet-ownership challenge. Implement this to bridge any wallet:
3
+ * - Node / backend agents: use {@link keypairSigner} with a secret key.
4
+ * - Browser: wrap a wallet-adapter, e.g.
5
+ * `{ address: wallet.publicKey.toBase58(), signMessage: (m) => wallet.signMessage(m) }`.
6
+ *
7
+ * `signMessage` must return the raw 64-byte Ed25519 signature; the SDK base58-
8
+ * encodes it for the API.
9
+ */
10
+ interface PraxisSigner {
11
+ /** base58-encoded Solana public key. */
12
+ readonly address: string;
13
+ signMessage(message: Uint8Array): Promise<Uint8Array> | Uint8Array;
14
+ }
15
+ type SecretKeyInput = Uint8Array | number[] | string;
16
+ /**
17
+ * Build a {@link PraxisSigner} from a Solana secret key — a 64-byte keypair or a
18
+ * 32-byte seed, as raw bytes, a number array, or a base58 string (Phantom /
19
+ * `solana-keygen` export). Node and browser friendly; no `@solana/web3.js`
20
+ * dependency.
21
+ */
22
+ declare function keypairSigner(secret: SecretKeyInput): PraxisSigner;
23
+
24
+ /**
25
+ * Praxis SDK wire types — the JSON contract exposed by `/api/praxis/*`.
26
+ *
27
+ * MONEY RULE: every monetary value crosses the wire as a **decimal string of
28
+ * integer base units** (lamports for SOL, token base units for SPL) — never a
29
+ * float. On the server these are `bigint`; the API serializes them to strings.
30
+ * The SDK keeps them as strings and leaves bigint conversion to the caller
31
+ * (see `toBaseUnits` / `fromBaseUnits` in `units.ts`). This file intentionally
32
+ * mirrors `@praxis/shared` but with `string` money, so the SDK ships a
33
+ * self-contained type contract with no internal workspace dependency.
34
+ */
35
+ /** A base58-encoded Solana public key. */
36
+ type Address = string;
37
+ /** Integer base units as a decimal string (e.g. "500000000" = 0.5 SOL). */
38
+ type BaseUnitString = string;
39
+ /** Mirrors the on-chain `ActionKind` discriminants. */
40
+ declare enum ActionKind {
41
+ Transfer = 0,
42
+ TransferSpl = 1
43
+ }
44
+ /** Why Aegis rejected an agent action. Mirrors the on-chain enum order. */
45
+ declare enum RejectReason {
46
+ Unauthorized = 0,
47
+ Paused = 1,
48
+ Expired = 2,
49
+ OverPerTx = 3,
50
+ OverDaily = 4,
51
+ RecipientNotAllowed = 5,
52
+ Overflow = 6,
53
+ MintNotAllowed = 7
54
+ }
55
+ /** Returned by `POST /auth/challenge`; sign `message` to prove wallet ownership. */
56
+ interface WalletChallenge {
57
+ address: Address;
58
+ /** Opaque signed token echoed back to `/auth/verify`. */
59
+ nonce: string;
60
+ /** The exact UTF-8 string the wallet must sign. */
61
+ message: string;
62
+ /** ISO-8601 expiry of the challenge. */
63
+ expiresAt: string;
64
+ }
65
+ interface SessionInfo {
66
+ authenticated: boolean;
67
+ walletAddress: Address;
68
+ }
69
+ interface TokenInfo {
70
+ symbol: string;
71
+ mint: Address;
72
+ decimals: number;
73
+ verified: boolean;
74
+ }
75
+ interface AddressBookEntry {
76
+ label: string;
77
+ name: string;
78
+ address: Address;
79
+ note?: string;
80
+ }
81
+ interface PolicyView {
82
+ address: Address;
83
+ owner: Address;
84
+ /** `11111…1111` (default) once the agent key is revoked. */
85
+ agentAuthority: Address;
86
+ maxPerTx: BaseUnitString;
87
+ dailyLimit: BaseUnitString;
88
+ spentToday: BaseUnitString;
89
+ dayStartTs: number;
90
+ allowedPrograms: Address[];
91
+ /** Empty array means "any recipient allowed". */
92
+ allowedRecipients: Address[];
93
+ allowedMints: Address[];
94
+ expiryTs: number;
95
+ paused: boolean;
96
+ vaultBalance: BaseUnitString;
97
+ tokenMint: Address;
98
+ tokenMaxPerTx: BaseUnitString;
99
+ tokenDailyLimit: BaseUnitString;
100
+ tokenSpentToday: BaseUnitString;
101
+ tokenDayStartTs: number;
102
+ }
103
+ interface PolicyCheckResult {
104
+ allowed: boolean;
105
+ reason?: string;
106
+ reasonCode?: RejectReason;
107
+ spentToday: BaseUnitString;
108
+ dailyLimit: BaseUnitString;
109
+ remaining: BaseUnitString;
110
+ }
111
+ interface PolicyUpdate {
112
+ maxPerTx?: BaseUnitString;
113
+ dailyLimit?: BaseUnitString;
114
+ expiryTs?: number;
115
+ paused?: boolean;
116
+ }
117
+ interface TokenEnvelopeConfig {
118
+ tokenMint: Address;
119
+ tokenMaxPerTx: BaseUnitString;
120
+ tokenDailyLimit: BaseUnitString;
121
+ }
122
+ type AllowListKind = "programs" | "recipients" | "mints";
123
+ interface TransferDetail {
124
+ kind: "transfer";
125
+ amount: BaseUnitString;
126
+ asset: TokenInfo;
127
+ recipientName: string;
128
+ recipientAddress: Address;
129
+ recipientNote?: string;
130
+ }
131
+ interface SwapDetail {
132
+ kind: "swap";
133
+ amountIn: BaseUnitString;
134
+ assetIn: TokenInfo;
135
+ estAmountOut: BaseUnitString;
136
+ assetOut: TokenInfo;
137
+ route: string;
138
+ priceImpactBps: number;
139
+ }
140
+ type ProposalDetail = TransferDetail | SwapDetail;
141
+ type ProposalState = "pending" | "signing" | "signed" | "blocked" | "cancelled";
142
+ interface ActionProposal {
143
+ id: string;
144
+ detail: ProposalDetail;
145
+ networkFee: BaseUnitString;
146
+ simulation: string;
147
+ check: PolicyCheckResult;
148
+ state: ProposalState;
149
+ sig?: string;
150
+ }
151
+ interface ClarifyOption {
152
+ label: string;
153
+ value: string;
154
+ hint?: string;
155
+ }
156
+ interface ResearchMetric {
157
+ label: string;
158
+ value: string;
159
+ trend?: "up" | "down" | "flat";
160
+ }
161
+ interface ResearchData {
162
+ token: string;
163
+ mint: Address;
164
+ metrics: ResearchMetric[];
165
+ summary: string;
166
+ }
167
+ type AgentBlock = {
168
+ type: "prose";
169
+ text: string;
170
+ } | {
171
+ type: "clarify";
172
+ text: string;
173
+ options: ClarifyOption[];
174
+ } | {
175
+ type: "proposal";
176
+ text: string;
177
+ proposalId: string;
178
+ } | {
179
+ type: "research";
180
+ text: string;
181
+ data: ResearchData;
182
+ };
183
+ type UserMessage = {
184
+ id: string;
185
+ role: "user";
186
+ ts: number;
187
+ text: string;
188
+ };
189
+ type AgentMessage = {
190
+ id: string;
191
+ role: "agent";
192
+ ts: number;
193
+ blocks: AgentBlock[];
194
+ };
195
+ type Message = UserMessage | AgentMessage;
196
+ interface Thread {
197
+ id: string;
198
+ title: string;
199
+ messages: Message[];
200
+ updatedAt: number;
201
+ }
202
+ interface ActivityEntry {
203
+ id: string;
204
+ kind: "transfer" | "swap";
205
+ label: string;
206
+ asset: string;
207
+ amount: BaseUnitString;
208
+ decimals: number;
209
+ result: "allowed" | "rejected";
210
+ reason?: string;
211
+ reasonCode?: RejectReason;
212
+ ts: number;
213
+ sig?: string;
214
+ }
215
+ interface UnsignedOwnerTransaction {
216
+ /** base64-encoded unsigned transaction for the owner wallet to sign. */
217
+ transaction: string;
218
+ blockhash: string;
219
+ lastValidBlockHeight: number;
220
+ }
221
+ /** Typed owner action accepted by `POST /owner/build`. */
222
+ type OwnerAction = {
223
+ kind: "bootstrapPolicy";
224
+ fundLamports?: BaseUnitString;
225
+ } | {
226
+ kind: "fundVault";
227
+ amount: BaseUnitString;
228
+ } | {
229
+ kind: "withdrawVault";
230
+ amount: BaseUnitString;
231
+ } | {
232
+ kind: "closePolicy";
233
+ } | {
234
+ kind: "revoke";
235
+ } | {
236
+ kind: "rotate";
237
+ } | {
238
+ kind: "updatePolicy";
239
+ patch: PolicyUpdate;
240
+ } | {
241
+ kind: "allowList";
242
+ listKind: AllowListKind;
243
+ address: Address;
244
+ mode: "add" | "remove";
245
+ };
246
+
247
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
248
+ interface PraxisClientOptions {
249
+ /** Base origin of the Praxis deployment, e.g. "http://localhost:3000". */
250
+ baseUrl: string;
251
+ /** Wallet signer for `connect()`. Optional if you only call public reads after an external login. */
252
+ signer?: PraxisSigner;
253
+ /** Custom fetch (defaults to global fetch). Required in runtimes without one. */
254
+ fetch?: FetchLike;
255
+ /** Per-request timeout in ms (default 20_000). */
256
+ timeoutMs?: number;
257
+ }
258
+ /** Result of {@link PraxisClient.ask} — the agent's reply, distilled. */
259
+ interface AskResult {
260
+ threadId: string;
261
+ /** The agent's reply message (prose / clarify / proposal / research blocks). */
262
+ message: AgentMessage;
263
+ /** Any action proposals the agent produced, hydrated for convenience. */
264
+ proposals: ActionProposal[];
265
+ }
266
+ /**
267
+ * Typed client for a hosted Praxis agent. The agent's LLM, scoped agent key, and
268
+ * Aegis policy enforcement all live server-side; this SDK is an authenticated
269
+ * client of the `/api/praxis/*` surface.
270
+ *
271
+ * ```ts
272
+ * const praxis = new PraxisClient({ baseUrl, signer: keypairSigner(secret) });
273
+ * await praxis.connect();
274
+ * const { proposals } = await praxis.ask("send 0.5 SOL to maya");
275
+ * if (proposals[0]?.check.allowed) await praxis.signProposal(proposals[0].id);
276
+ * ```
277
+ */
278
+ declare class PraxisClient {
279
+ private readonly baseUrl;
280
+ private readonly signer?;
281
+ private readonly fetchImpl;
282
+ private readonly timeoutMs;
283
+ /** Manual cookie jar — Node's fetch does not persist Set-Cookie across calls. */
284
+ private sessionCookie?;
285
+ constructor(options: PraxisClientOptions);
286
+ /** The signer's wallet address, if a signer was provided. */
287
+ get address(): string | undefined;
288
+ /**
289
+ * Run the wallet-ownership handshake: request a challenge, sign its message,
290
+ * verify it, and store the resulting session cookie. Idempotent.
291
+ */
292
+ connect(): Promise<SessionInfo>;
293
+ /** Current session, or `null` if not signed in. */
294
+ session(): Promise<SessionInfo | null>;
295
+ /** Clear the session (server-side cookie + local jar). */
296
+ logout(): Promise<void>;
297
+ /** Send a line to the agent. Creates a thread when `threadId` is omitted. */
298
+ send(text: string, threadId?: string | null): Promise<{
299
+ threadId: string;
300
+ }>;
301
+ /**
302
+ * Send a line and return the agent's reply in one call. The API resolves
303
+ * `send` only after the agent has finished, so this needs no polling.
304
+ */
305
+ ask(text: string, threadId?: string | null): Promise<AskResult>;
306
+ newThread(threadId?: string): Promise<{
307
+ threadId: string;
308
+ }>;
309
+ signProposal(proposalId: string): Promise<void>;
310
+ cancelProposal(proposalId: string): Promise<void>;
311
+ getThreads(): Promise<Thread[]>;
312
+ getThread(id: string): Promise<Thread>;
313
+ getProposal(id: string): Promise<ActionProposal>;
314
+ getPolicy(): Promise<PolicyView>;
315
+ getActivity(): Promise<ActivityEntry[]>;
316
+ getAddressBook(): Promise<AddressBookEntry[]>;
317
+ isThinking(threadId: string): Promise<boolean>;
318
+ getVersion(): Promise<number>;
319
+ bootstrapPolicy(fundLamports?: BaseUnitString): Promise<void>;
320
+ updatePolicy(patch: PolicyUpdate): Promise<void>;
321
+ configureToken(config: TokenEnvelopeConfig): Promise<void>;
322
+ prepareTokenAccounts(recipientAddresses?: string[]): Promise<void>;
323
+ revokeAgent(): Promise<void>;
324
+ rotateAgent(): Promise<void>;
325
+ addToAllowList(kind: AllowListKind, address: string): Promise<void>;
326
+ removeFromAllowList(kind: AllowListKind, address: string): Promise<void>;
327
+ /** Build an unsigned owner transaction for the wallet to sign. */
328
+ buildOwnerTransaction(action: OwnerAction): Promise<UnsignedOwnerTransaction>;
329
+ /** Submit a wallet-signed owner transaction. */
330
+ submitOwnerTransaction(signed: UnsignedOwnerTransaction): Promise<{
331
+ sig: string;
332
+ }>;
333
+ private get;
334
+ private post;
335
+ private request;
336
+ private captureCookie;
337
+ }
338
+
339
+ /**
340
+ * Thrown when the Praxis API returns a non-2xx response. The backend's error
341
+ * envelope is `{ error: string, type: string }` with a meaningful HTTP status
342
+ * (400 input, 401 auth, 404 not-found, 429 rate-limit, 503 config, 500 other).
343
+ */
344
+ declare class PraxisApiError extends Error {
345
+ readonly status: number;
346
+ /** The backend error class name, e.g. "PraxisAuthError", "PraxisRateLimitError". */
347
+ readonly type: string;
348
+ constructor(status: number, type: string, message: string);
349
+ get isAuth(): boolean;
350
+ get isRateLimited(): boolean;
351
+ get isInput(): boolean;
352
+ }
353
+ /** Thrown for SDK-side misconfiguration (no fetch, no signer, bad key, …). */
354
+ declare class PraxisConfigError extends Error {
355
+ constructor(message: string);
356
+ }
357
+
358
+ /**
359
+ * Money helpers. Praxis money crosses the wire as decimal strings of integer
360
+ * base units (lamports / token base units). These convert to/from `bigint` and
361
+ * human decimal amounts without floats.
362
+ */
363
+ /** Parse a base-unit string (or bigint) into a bigint. */
364
+ declare function toBaseUnits(value: string | bigint): bigint;
365
+ /** Serialize a bigint (or number of whole base units) into a base-unit string. */
366
+ declare function fromBaseUnits(value: bigint | number): string;
367
+ /** Convert a human decimal amount ("0.5") into base units for `decimals` places. */
368
+ declare function humanToBaseUnits(amount: string, decimals: number): string;
369
+ /** Convert base units into a human decimal string for `decimals` places. */
370
+ declare function baseUnitsToHuman(value: string | bigint, decimals: number): string;
371
+
372
+ export { ActionKind, type ActionProposal, type ActivityEntry, type Address, type AddressBookEntry, type AgentBlock, type AgentMessage, type AllowListKind, type AskResult, type BaseUnitString, type ClarifyOption, type FetchLike, type Message, type OwnerAction, type PolicyCheckResult, type PolicyUpdate, type PolicyView, PraxisApiError, PraxisClient, type PraxisClientOptions, PraxisConfigError, type PraxisSigner, type ProposalDetail, type ProposalState, RejectReason, type ResearchData, type ResearchMetric, type SecretKeyInput, type SessionInfo, type SwapDetail, type Thread, type TokenEnvelopeConfig, type TokenInfo, type TransferDetail, type UnsignedOwnerTransaction, type UserMessage, type WalletChallenge, baseUnitsToHuman, fromBaseUnits, humanToBaseUnits, keypairSigner, toBaseUnits };