@aithos/sdk 0.1.0-alpha.14 → 0.1.0-alpha.17

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.
@@ -5,8 +5,18 @@ export interface ComputeMessage {
5
5
  readonly content: string;
6
6
  }
7
7
  export interface InvokeBedrockArgs {
8
- /** Mandate ID granting this app the right to spend the user's wallet. */
9
- readonly mandateId: string;
8
+ /**
9
+ * Mandate ID under which this call should be attributed.
10
+ *
11
+ * - **Owner sessions**: optional. The SDK uses the owner's own DID
12
+ * as a sentinel "self" mandate id — the proxy skips all
13
+ * mandate-related checks (scope, allowed_models, caps) when the
14
+ * envelope is owner-signed, so the value is informational only.
15
+ * - **Delegate sessions**: required. Must reference the imported
16
+ * mandate bundle the SDK signs with (the proxy enforces
17
+ * `compute.invoke` scope and any `allowed_models` filter).
18
+ */
19
+ readonly mandateId?: string;
10
20
  /**
11
21
  * Model id. Today the proxy accepts the canonical Aithos identifiers:
12
22
  * `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`.
@@ -44,6 +54,70 @@ export interface InvokeBedrockResult {
44
54
  /** Audit log id for traceability. */
45
55
  readonly auditId: string;
46
56
  }
57
+ /**
58
+ * Stable cross-provider image model ids supported by the Aithos compute
59
+ * proxy. New models can be added on the server side without an SDK
60
+ * release — but tagging the namespaced literal here gives consumers
61
+ * autocomplete + type-checking.
62
+ *
63
+ * The `image:` prefix is part of the wire contract: the server uses it
64
+ * to disambiguate text and image dispatch when a mandate's
65
+ * `allowed_models` mixes both.
66
+ */
67
+ export type ImageModelId = "image:flux-schnell" | "image:flux-dev" | "image:flux-pro-1.1" | "image:flux-pro-1.1-ultra" | "image:imagen-3" | "image:imagen-4" | "image:nano-banana";
68
+ /** Aspect ratios accepted by `invokeImage`. */
69
+ export type ImageAspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "21:9";
70
+ export interface InvokeImageArgs {
71
+ /**
72
+ * Mandate ID under which this call should be attributed.
73
+ *
74
+ * - **Owner sessions**: optional. The SDK uses the owner's own DID
75
+ * as a sentinel "self" mandate id — the proxy skips all
76
+ * mandate-related checks when the envelope is owner-signed.
77
+ * - **Delegate sessions**: required. Must reference the imported
78
+ * mandate bundle the SDK signs with.
79
+ */
80
+ readonly mandateId?: string;
81
+ /**
82
+ * Image model id. Defaults to `"image:flux-pro-1.1"` on the SDK side
83
+ * if omitted — the server allowlist is the source of truth for which
84
+ * ones actually work.
85
+ */
86
+ readonly model?: ImageModelId;
87
+ /** What to draw. */
88
+ readonly prompt: string;
89
+ /** Optional comma-separated list of things to avoid (e.g. "blurry, watermark"). */
90
+ readonly negativePrompt?: string;
91
+ /** Default 1:1. */
92
+ readonly aspectRatio?: ImageAspectRatio;
93
+ /** Deterministic seed (re-rolls). Omit for random. */
94
+ readonly seed?: number;
95
+ /** Number of images to generate. Default 1, max 4. */
96
+ readonly numberOfImages?: number;
97
+ /** Idempotency key for retries (generated if omitted). */
98
+ readonly idempotencyKey?: string;
99
+ /** Abort signal to cancel the request (network + provider call). */
100
+ readonly signal?: AbortSignal;
101
+ }
102
+ export interface InvokeImageImage {
103
+ /** Base64-encoded image bytes (raw, no data: URI prefix). */
104
+ readonly base64: string;
105
+ /** "image/png" | "image/jpeg". */
106
+ readonly contentType: string;
107
+ readonly width: number;
108
+ readonly height: number;
109
+ }
110
+ export interface InvokeImageResult {
111
+ readonly images: readonly InvokeImageImage[];
112
+ /** Seed actually used by the provider (echoed back even when caller didn't set one). */
113
+ readonly seed: number;
114
+ /** Microcredits debited from the wallet (exact — no reconcile path for images). */
115
+ readonly creditsCharged: number;
116
+ /** Wallet balance after debit. */
117
+ readonly walletBalance: number;
118
+ /** Audit log id for traceability. */
119
+ readonly auditId: string;
120
+ }
47
121
  export interface ComputeNamespaceDeps {
48
122
  readonly auth: AithosAuth;
49
123
  readonly appDid: string;
@@ -88,5 +162,25 @@ export declare class ComputeNamespace {
88
162
  * `mandate_revoked`, `insufficient_credits`, …).
89
163
  */
90
164
  invokeBedrock(args: InvokeBedrockArgs): Promise<InvokeBedrockResult>;
165
+ /**
166
+ * Generate one or more images through the Aithos compute proxy
167
+ * (currently powered by fal.ai FLUX models). Spec mirror of
168
+ * {@link invokeBedrock}: same envelope, same wallet path, same
169
+ * mandate-scope gate (`compute.invoke` + `allowed_models`). The
170
+ * separation at the JSON-RPC method level (`aithos.compute_invoke_image`
171
+ * vs `aithos.compute_invoke`) commits each envelope to a specific
172
+ * modality so a stolen text-invoke envelope cannot be replayed
173
+ * against the image endpoint.
174
+ *
175
+ * Default model: `"image:flux-pro-1.1"`. Default aspect ratio: 1:1.
176
+ * Default count: 1 image.
177
+ *
178
+ * Pricing is per image and deterministic (no token-based reconcile):
179
+ * - flux-schnell: 3 000 mc + fee per image
180
+ * - flux-dev: 25 000 mc + fee per image
181
+ * - flux-pro-1.1: 40 000 mc + fee per image
182
+ * - flux-pro-1.1-ultra: 60 000 mc + fee per image
183
+ */
184
+ invokeImage(args: InvokeImageArgs): Promise<InvokeImageResult>;
91
185
  }
92
186
  //# sourceMappingURL=compute.d.ts.map
@@ -60,46 +60,13 @@ export class ComputeNamespace {
60
60
  * `mandate_revoked`, `insufficient_credits`, …).
61
61
  */
62
62
  async invokeBedrock(args) {
63
- const { auth, appDid, endpoints, fetch: fetchImpl } = this.#deps;
64
- const owner = auth._getOwnerSigners();
65
- const ownerLoaded = owner !== null && !owner.destroyed;
66
- let choice;
67
- if (ownerLoaded) {
68
- const publicKp = ownerKeyPair(owner, "public");
69
- choice = {
70
- kind: "owner",
71
- iss: owner.did,
72
- verificationMethod: `${owner.did}#public`,
73
- signer: publicKp,
74
- mandate: undefined,
75
- };
76
- }
77
- else {
78
- // Delegate-only path. Find a session whose mandate id matches.
79
- const actor = auth._getDelegateActor(args.mandateId);
80
- if (!actor || actor.destroyed) {
81
- throw new AithosSDKError("sdk_no_delegate_for_mandate", `no owner signed in and no imported delegate mandate matches '${args.mandateId}'. Sign in as an owner, or import a delegate bundle for that mandate via auth.importMandate.`);
82
- }
83
- const kp = delegateKeyPair(actor);
84
- choice = {
85
- kind: "delegate",
86
- iss: actor.subjectDid,
87
- verificationMethod: actor.granteePubkeyMultibase,
88
- signer: kp,
89
- // The DelegateActor stores the SignedMandate as a structurally
90
- // opaque object so the SDK doesn't have to import the
91
- // protocol-client type at the storage boundary. Round-trip
92
- // through `unknown` for the TS cast — at runtime the bytes are
93
- // the canonical SignedMandate the bundle parser already
94
- // validated.
95
- mandate: actor.mandate,
96
- };
97
- }
63
+ const { endpoints, fetch: fetchImpl } = this.#deps;
64
+ const choice = this.#resolveSigner(args.mandateId);
98
65
  const url = computeInvokeUrl(endpoints);
99
66
  const idempotencyKey = args.idempotencyKey ?? generateIdempotencyKey();
100
67
  const params = {
101
- app_did: appDid,
102
- mandate_id: args.mandateId,
68
+ app_did: this.#deps.appDid,
69
+ mandate_id: this.#resolveMandateIdForWire(args.mandateId, choice),
103
70
  model: args.model,
104
71
  messages: args.messages,
105
72
  idempotency_key: idempotencyKey,
@@ -110,10 +77,152 @@ export class ComputeNamespace {
110
77
  params.max_tokens = args.maxTokens;
111
78
  if (args.temperature !== undefined)
112
79
  params.temperature = args.temperature;
80
+ return await this.#signAndPost({
81
+ url,
82
+ method: "aithos.compute_invoke",
83
+ params,
84
+ choice,
85
+ fetchImpl,
86
+ signal: args.signal,
87
+ });
88
+ }
89
+ /**
90
+ * Generate one or more images through the Aithos compute proxy
91
+ * (currently powered by fal.ai FLUX models). Spec mirror of
92
+ * {@link invokeBedrock}: same envelope, same wallet path, same
93
+ * mandate-scope gate (`compute.invoke` + `allowed_models`). The
94
+ * separation at the JSON-RPC method level (`aithos.compute_invoke_image`
95
+ * vs `aithos.compute_invoke`) commits each envelope to a specific
96
+ * modality so a stolen text-invoke envelope cannot be replayed
97
+ * against the image endpoint.
98
+ *
99
+ * Default model: `"image:flux-pro-1.1"`. Default aspect ratio: 1:1.
100
+ * Default count: 1 image.
101
+ *
102
+ * Pricing is per image and deterministic (no token-based reconcile):
103
+ * - flux-schnell: 3 000 mc + fee per image
104
+ * - flux-dev: 25 000 mc + fee per image
105
+ * - flux-pro-1.1: 40 000 mc + fee per image
106
+ * - flux-pro-1.1-ultra: 60 000 mc + fee per image
107
+ */
108
+ async invokeImage(args) {
109
+ const { endpoints, fetch: fetchImpl } = this.#deps;
110
+ const choice = this.#resolveSigner(args.mandateId);
111
+ const url = computeInvokeUrl(endpoints);
112
+ const idempotencyKey = args.idempotencyKey ?? generateIdempotencyKey();
113
+ // Default is Imagen 4 since alpha.17 — flat-illustration style is
114
+ // the right match for brand-mascot use cases. FLUX Pro 1.1 remains
115
+ // available via explicit `model: "image:flux-pro-1.1"`.
116
+ const model = args.model ?? "image:imagen-4";
117
+ const params = {
118
+ app_did: this.#deps.appDid,
119
+ mandate_id: this.#resolveMandateIdForWire(args.mandateId, choice),
120
+ model,
121
+ prompt: args.prompt,
122
+ idempotency_key: idempotencyKey,
123
+ };
124
+ if (args.negativePrompt !== undefined)
125
+ params.negative_prompt = args.negativePrompt;
126
+ if (args.aspectRatio !== undefined)
127
+ params.aspect_ratio = args.aspectRatio;
128
+ if (args.seed !== undefined)
129
+ params.seed = args.seed;
130
+ if (args.numberOfImages !== undefined)
131
+ params.number_of_images = args.numberOfImages;
132
+ return await this.#signAndPost({
133
+ url,
134
+ method: "aithos.compute_invoke_image",
135
+ params,
136
+ choice,
137
+ fetchImpl,
138
+ signal: args.signal,
139
+ });
140
+ }
141
+ /**
142
+ * Resolve the active signer (owner takes precedence over delegate).
143
+ *
144
+ * - When an owner is signed in: returns the owner-signer regardless
145
+ * of `mandateId` (the proxy ignores params.mandate_id on
146
+ * owner-signed envelopes, so owners can omit it).
147
+ * - When delegate-only: requires `mandateId` to find the matching
148
+ * imported bundle. Throws `sdk_no_delegate_for_mandate` if absent
149
+ * or no match.
150
+ */
151
+ #resolveSigner(mandateId) {
152
+ const { auth } = this.#deps;
153
+ const owner = auth._getOwnerSigners();
154
+ const ownerLoaded = owner !== null && !owner.destroyed;
155
+ if (ownerLoaded) {
156
+ const publicKp = ownerKeyPair(owner, "public");
157
+ return {
158
+ kind: "owner",
159
+ iss: owner.did,
160
+ verificationMethod: `${owner.did}#public`,
161
+ signer: publicKp,
162
+ mandate: undefined,
163
+ };
164
+ }
165
+ if (mandateId === undefined || mandateId.length === 0) {
166
+ throw new AithosSDKError("sdk_no_signer", "no owner signed in and no mandateId provided — pass a mandateId for a delegate session, or sign in as an owner first.");
167
+ }
168
+ const actor = auth._getDelegateActor(mandateId);
169
+ if (!actor || actor.destroyed) {
170
+ throw new AithosSDKError("sdk_no_delegate_for_mandate", `no owner signed in and no imported delegate mandate matches '${mandateId}'. Sign in as an owner, or import a delegate bundle for that mandate via auth.importMandate.`);
171
+ }
172
+ const kp = delegateKeyPair(actor);
173
+ return {
174
+ kind: "delegate",
175
+ iss: actor.subjectDid,
176
+ verificationMethod: actor.granteePubkeyMultibase,
177
+ signer: kp,
178
+ // The DelegateActor stores the SignedMandate as a structurally
179
+ // opaque object so the SDK doesn't have to import the
180
+ // protocol-client type at the storage boundary. Round-trip
181
+ // through `unknown` for the TS cast — at runtime the bytes are
182
+ // the canonical SignedMandate the bundle parser already
183
+ // validated.
184
+ mandate: actor.mandate,
185
+ };
186
+ }
187
+ /**
188
+ * Resolve the `mandate_id` value the SDK writes into the JSON-RPC
189
+ * params for the wire. The proxy requires this field to be a
190
+ * non-empty string but only consults it for the delegate path —
191
+ * for owner-signed envelopes it's audit-log metadata, with no
192
+ * security implication.
193
+ *
194
+ * Strategy:
195
+ * - Caller-provided id always wins (owner who wants to attribute
196
+ * to a specific minted mandate can still pass it).
197
+ * - Delegate path: the choice carries the real mandate.id — use it
198
+ * so a delegate cannot accidentally lie about which mandate
199
+ * it claims to spend under.
200
+ * - Owner path with no explicit id: use the owner DID followed
201
+ * by `#self` — a stable sentinel that's unambiguously not a
202
+ * real mandate id (mandate ids are ULIDs).
203
+ */
204
+ #resolveMandateIdForWire(explicit, choice) {
205
+ if (explicit && explicit.length > 0)
206
+ return explicit;
207
+ if (choice.kind === "delegate")
208
+ return choice.mandate.id;
209
+ // Owner-direct sentinel. The proxy never inspects this value
210
+ // beyond non-empty-string validation when no mandate is attached.
211
+ return `${choice.iss}#self`;
212
+ }
213
+ /**
214
+ * Sign the params with the resolved signer and POST a JSON-RPC
215
+ * request. Shared between `invokeBedrock` and `invokeImage` — the
216
+ * only difference between those two is the method name and the
217
+ * params shape; the envelope construction + transport + error
218
+ * mapping is identical.
219
+ */
220
+ async #signAndPost(opts) {
221
+ const { url, method, params, choice, fetchImpl, signal } = opts;
113
222
  const envelope = buildSignedEnvelope({
114
223
  iss: choice.iss,
115
224
  aud: url,
116
- method: "aithos.compute_invoke",
225
+ method,
117
226
  verificationMethod: choice.verificationMethod,
118
227
  params,
119
228
  signer: choice.signer,
@@ -126,11 +235,11 @@ export class ComputeNamespace {
126
235
  headers: { "content-type": "application/json" },
127
236
  body: JSON.stringify({
128
237
  jsonrpc: "2.0",
129
- id: "aithos.compute_invoke",
130
- method: "aithos.compute_invoke",
238
+ id: method,
239
+ method,
131
240
  params: { ...params, _envelope: envelope },
132
241
  }),
133
- ...(args.signal ? { signal: args.signal } : {}),
242
+ ...(signal ? { signal } : {}),
134
243
  });
135
244
  }
136
245
  catch (e) {
@@ -1,11 +1,11 @@
1
- export declare const VERSION = "0.1.0-alpha.5";
1
+ export declare const VERSION = "0.1.0-alpha.17";
2
2
  export { AithosSDK } from "./sdk.js";
3
3
  export type { AithosSDKConfig } from "./types.js";
4
4
  export { AithosSDKError } from "./types.js";
5
5
  export { AithosRpcError } from "@aithos/protocol-client";
6
6
  export type { AithosSdkEndpoints } from "./endpoints.js";
7
7
  export { DEFAULT_SDK_ENDPOINTS } from "./endpoints.js";
8
- export type { ComputeMessage, InvokeBedrockArgs, InvokeBedrockResult, StopReason, } from "./compute.js";
8
+ export type { ComputeMessage, ImageAspectRatio, ImageModelId, InvokeBedrockArgs, InvokeBedrockResult, InvokeImageArgs, InvokeImageImage, InvokeImageResult, StopReason, } from "./compute.js";
9
9
  export { ComputeNamespace } from "./compute.js";
10
10
  export type { CreditPackId, CreateTopupSessionArgs, CreateTopupSessionResult, GetBalanceArgs, GetBalanceResult, } from "./wallet.js";
11
11
  export { WalletNamespace } from "./wallet.js";
package/dist/src/index.js CHANGED
@@ -17,7 +17,7 @@
17
17
  // Public types specific to the SDK (`AithosSDKConfig`, `AithosSDKError`)
18
18
  // are exported from here. Endpoint config (`AithosSdkEndpoints`,
19
19
  // `DEFAULT_SDK_ENDPOINTS`) likewise.
20
- export const VERSION = "0.1.0-alpha.5";
20
+ export const VERSION = "0.1.0-alpha.17";
21
21
  export { AithosSDK } from "./sdk.js";
22
22
  export { AithosSDKError } from "./types.js";
23
23
  // Re-export protocol-client's JSON-RPC error type so consumers can
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aithos/sdk",
3
- "version": "0.1.0-alpha.14",
3
+ "version": "0.1.0-alpha.17",
4
4
  "description": "Aithos SDK — high-level TypeScript developer kit for building agentic apps on the Aithos protocol. Wraps @aithos/protocol-client and exposes the Aithos compute proxy and wallet (Stripe top-up) endpoints.",
5
5
  "keywords": [
6
6
  "aithos",
@@ -39,15 +39,6 @@
39
39
  "README.md",
40
40
  "LICENSE"
41
41
  ],
42
- "scripts": {
43
- "build": "tsc",
44
- "build:test": "tsc -p tsconfig.test.json",
45
- "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
46
- "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
47
- "test:watch": "cd dist && node --test --watch",
48
- "clean": "rm -rf dist",
49
- "prepublishOnly": "npm run clean && npm run build && npm test"
50
- },
51
42
  "engines": {
52
43
  "node": ">=20"
53
44
  },
@@ -63,5 +54,13 @@
63
54
  "publishConfig": {
64
55
  "access": "public",
65
56
  "tag": "alpha"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc",
60
+ "build:test": "tsc -p tsconfig.test.json",
61
+ "check-types": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
62
+ "test": "npm run clean && npm run build && npm run build:test && cd dist && node --test",
63
+ "test:watch": "cd dist && node --test --watch",
64
+ "clean": "rm -rf dist"
66
65
  }
67
- }
66
+ }