@aithos/sdk 0.1.0-alpha.52 → 0.1.0-alpha.54
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/src/auth.d.ts +1 -1
- package/dist/src/auth.js +7 -2
- package/dist/src/compute.d.ts +98 -0
- package/dist/src/compute.js +47 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/internal/owner-signers.d.ts +5 -2
- package/dist/src/internal/owner-signers.js +22 -1
- package/dist/src/internal/recovery-file.d.ts +2 -0
- package/dist/src/internal/recovery-file.js +7 -0
- package/dist/src/key-store.d.ts +6 -0
- package/dist/src/key-store.js +6 -0
- package/dist/src/sdk.d.ts +26 -2
- package/dist/src/sdk.js +45 -1
- package/dist/test/converse.test.d.ts +2 -0
- package/dist/test/converse.test.js +162 -0
- package/dist/test/data-sphere.test.d.ts +2 -0
- package/dist/test/data-sphere.test.js +57 -0
- package/package.json +3 -3
package/dist/src/auth.d.ts
CHANGED
|
@@ -420,7 +420,7 @@ export declare class AithosAuth {
|
|
|
420
420
|
* Choose `"root"`, `"circle"`, or `"self"` only if the receiving
|
|
421
421
|
* server specifically expects one of those (rare).
|
|
422
422
|
*/
|
|
423
|
-
readonly sphere?: "root" | "public" | "circle" | "self";
|
|
423
|
+
readonly sphere?: "root" | "public" | "circle" | "self" | "data";
|
|
424
424
|
/**
|
|
425
425
|
* Envelope lifetime in seconds. Default 60. Aithos servers cap
|
|
426
426
|
* at 300; third-party servers may apply their own cap.
|
package/dist/src/auth.js
CHANGED
|
@@ -192,8 +192,9 @@ export class AithosAuth {
|
|
|
192
192
|
if (sphere !== "root" &&
|
|
193
193
|
sphere !== "public" &&
|
|
194
194
|
sphere !== "circle" &&
|
|
195
|
-
sphere !== "self"
|
|
196
|
-
|
|
195
|
+
sphere !== "self" &&
|
|
196
|
+
sphere !== "data") {
|
|
197
|
+
throw new AithosSDKError("auth_invalid_sphere", `signEnvelope: invalid sphere "${sphere}". Expected one of: root, public, circle, self, data.`);
|
|
197
198
|
}
|
|
198
199
|
const signer = this.#ownerSigners.signerForSphere(sphere);
|
|
199
200
|
return signOwnerEnvelope({
|
|
@@ -412,6 +413,7 @@ export class AithosAuth {
|
|
|
412
413
|
public: identity.public.seed,
|
|
413
414
|
circle: identity.circle.seed,
|
|
414
415
|
self: identity.self.seed,
|
|
416
|
+
...(identity.data ? { data: identity.data.seed } : {}),
|
|
415
417
|
},
|
|
416
418
|
delegates: [],
|
|
417
419
|
});
|
|
@@ -462,6 +464,7 @@ export class AithosAuth {
|
|
|
462
464
|
public: bytesToHex(identity.public.seed),
|
|
463
465
|
circle: bytesToHex(identity.circle.seed),
|
|
464
466
|
self: bytesToHex(identity.self.seed),
|
|
467
|
+
...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
|
|
465
468
|
},
|
|
466
469
|
savedAt: new Date().toISOString(),
|
|
467
470
|
});
|
|
@@ -833,6 +836,7 @@ export class AithosAuth {
|
|
|
833
836
|
public: identity.public.seed,
|
|
834
837
|
circle: identity.circle.seed,
|
|
835
838
|
self: identity.self.seed,
|
|
839
|
+
...(identity.data ? { data: identity.data.seed } : {}),
|
|
836
840
|
},
|
|
837
841
|
delegates: [],
|
|
838
842
|
});
|
|
@@ -869,6 +873,7 @@ export class AithosAuth {
|
|
|
869
873
|
public: bytesToHex(identity.public.seed),
|
|
870
874
|
circle: bytesToHex(identity.circle.seed),
|
|
871
875
|
self: bytesToHex(identity.self.seed),
|
|
876
|
+
...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
|
|
872
877
|
},
|
|
873
878
|
savedAt: new Date().toISOString(),
|
|
874
879
|
});
|
package/dist/src/compute.d.ts
CHANGED
|
@@ -90,6 +90,89 @@ export interface InvokeBedrockResult {
|
|
|
90
90
|
*/
|
|
91
91
|
readonly sponsoredRemainingForUser?: number;
|
|
92
92
|
}
|
|
93
|
+
/** A decrypted ethos section in the working-set. */
|
|
94
|
+
export interface WorkingSetSection {
|
|
95
|
+
readonly id: string;
|
|
96
|
+
readonly title: string;
|
|
97
|
+
readonly body: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Client-decrypted data the agentic loop may read server-side. Built in the
|
|
101
|
+
* browser (where the keys live) from exactly the zones / collections the
|
|
102
|
+
* mandate grants — the proxy never holds a standing decryption key
|
|
103
|
+
* (see PLATFORM-COMPUTE-AGENTIC-MCP.md §4). Use {@link ComputeNamespace}
|
|
104
|
+
* with a `mcp` reference + this working-set.
|
|
105
|
+
*/
|
|
106
|
+
export interface ComputeWorkingSet {
|
|
107
|
+
/** Decrypted ethos sections, per zone. Only granted zones appear. */
|
|
108
|
+
readonly ethos?: {
|
|
109
|
+
readonly public?: readonly WorkingSetSection[];
|
|
110
|
+
readonly circle?: readonly WorkingSetSection[];
|
|
111
|
+
readonly self?: readonly WorkingSetSection[];
|
|
112
|
+
};
|
|
113
|
+
/** Decrypted structured data, per collection name. */
|
|
114
|
+
readonly data?: Record<string, readonly Record<string, unknown>[]>;
|
|
115
|
+
}
|
|
116
|
+
/** A tool invocation the loop performed (trace for UI / debugging). */
|
|
117
|
+
export interface ConverseToolCall {
|
|
118
|
+
readonly name: string;
|
|
119
|
+
readonly ok: boolean;
|
|
120
|
+
readonly turn: number;
|
|
121
|
+
}
|
|
122
|
+
export type ConverseStopReason = "end_turn" | "max_tokens" | "stop_sequence" | "max_iterations" | "budget_exhausted";
|
|
123
|
+
export interface RunConversationArgs {
|
|
124
|
+
/** Mandate id — optional for owner sessions, required for delegate sessions. */
|
|
125
|
+
readonly mandateId?: string;
|
|
126
|
+
/** Claude model id (text or vision). */
|
|
127
|
+
readonly model: string;
|
|
128
|
+
/** Initial conversation. The loop's internal tool turns are server-side. */
|
|
129
|
+
readonly messages: readonly ComputeMessage[];
|
|
130
|
+
/** Optional system prompt. */
|
|
131
|
+
readonly system?: string;
|
|
132
|
+
/**
|
|
133
|
+
* MCP tools to expose to the model. v1 only supports the Aithos server.
|
|
134
|
+
* Omit `tools` to expose the full Aithos catalogue; pass a subset to narrow.
|
|
135
|
+
*/
|
|
136
|
+
readonly mcp?: {
|
|
137
|
+
readonly server: "aithos";
|
|
138
|
+
readonly tools?: readonly string[];
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* Client-decrypted working-set the tools read from. Build it with
|
|
142
|
+
* {@link ComputeNamespace.buildWorkingSet}. Required for any tool that
|
|
143
|
+
* touches private (circle/self) or structured data.
|
|
144
|
+
*/
|
|
145
|
+
readonly workingSet?: ComputeWorkingSet;
|
|
146
|
+
/** Cap on Bedrock turns. Server clamps to its own hard cap. Default 6. */
|
|
147
|
+
readonly maxIterations?: number;
|
|
148
|
+
/** Per-turn output token cap. */
|
|
149
|
+
readonly maxTokens?: number;
|
|
150
|
+
readonly temperature?: number;
|
|
151
|
+
/** Idempotency key for the WHOLE conversation (generated if omitted). */
|
|
152
|
+
readonly idempotencyKey?: string;
|
|
153
|
+
readonly signal?: AbortSignal;
|
|
154
|
+
}
|
|
155
|
+
export interface RunConversationResult {
|
|
156
|
+
/** Final assistant text. */
|
|
157
|
+
readonly content: string;
|
|
158
|
+
readonly stopReason: ConverseStopReason;
|
|
159
|
+
/** Number of Bedrock turns performed (each was a billed call). */
|
|
160
|
+
readonly iterations: number;
|
|
161
|
+
/** Token usage summed over all turns. */
|
|
162
|
+
readonly usage: {
|
|
163
|
+
readonly inputTokens: number;
|
|
164
|
+
readonly outputTokens: number;
|
|
165
|
+
};
|
|
166
|
+
/** Trace of the tool calls the loop made. */
|
|
167
|
+
readonly toolCalls: readonly ConverseToolCall[];
|
|
168
|
+
/** Total microcredits debited for the whole conversation. */
|
|
169
|
+
readonly creditsCharged: number;
|
|
170
|
+
readonly walletBalance: number;
|
|
171
|
+
readonly auditId: string;
|
|
172
|
+
readonly fundedBy?: "sponsored" | "grant" | "purchase";
|
|
173
|
+
readonly receiptId?: string;
|
|
174
|
+
readonly sponsoredBy?: string;
|
|
175
|
+
}
|
|
93
176
|
/**
|
|
94
177
|
* Stable cross-provider image model ids supported by the Aithos compute
|
|
95
178
|
* proxy. New models can be added on the server side without an SDK
|
|
@@ -399,6 +482,21 @@ export declare class ComputeNamespace {
|
|
|
399
482
|
* `mandate_revoked`, `insufficient_credits`, …).
|
|
400
483
|
*/
|
|
401
484
|
invokeBedrock(args: InvokeBedrockArgs): Promise<InvokeBedrockResult>;
|
|
485
|
+
/**
|
|
486
|
+
* Run an agentic conversation: a multi-turn Bedrock tool-calling loop that
|
|
487
|
+
* runs server-side in a single POST and is billed once for the cumulative
|
|
488
|
+
* token usage. Tools come from the Aithos MCP (declared via `mcp`); the
|
|
489
|
+
* model reads private user data from the client-decrypted `workingSet`.
|
|
490
|
+
*
|
|
491
|
+
* The loop, tool dispatch, and billing all happen on the proxy — the SDK
|
|
492
|
+
* makes ONE signed request and gets the final answer. Same signer paths as
|
|
493
|
+
* {@link invokeBedrock} (owner direct or delegate-under-mandate).
|
|
494
|
+
*
|
|
495
|
+
* @throws {AithosSDKError} `sdk_no_signer`, `sdk_no_delegate_for_mandate`,
|
|
496
|
+
* `network`, `http`, `empty`, or any proxy code (`quota_exceeded`,
|
|
497
|
+
* `mandate_revoked`, `insufficient_credits`, …).
|
|
498
|
+
*/
|
|
499
|
+
runConversation(args: RunConversationArgs): Promise<RunConversationResult>;
|
|
402
500
|
/**
|
|
403
501
|
* Multimodal Bedrock invoke — image + text → text response.
|
|
404
502
|
* Default model: `claude-sonnet-4-6` (vision-capable, reliable JSON).
|
package/dist/src/compute.js
CHANGED
|
@@ -101,6 +101,53 @@ export class ComputeNamespace {
|
|
|
101
101
|
signal: args.signal,
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Run an agentic conversation: a multi-turn Bedrock tool-calling loop that
|
|
106
|
+
* runs server-side in a single POST and is billed once for the cumulative
|
|
107
|
+
* token usage. Tools come from the Aithos MCP (declared via `mcp`); the
|
|
108
|
+
* model reads private user data from the client-decrypted `workingSet`.
|
|
109
|
+
*
|
|
110
|
+
* The loop, tool dispatch, and billing all happen on the proxy — the SDK
|
|
111
|
+
* makes ONE signed request and gets the final answer. Same signer paths as
|
|
112
|
+
* {@link invokeBedrock} (owner direct or delegate-under-mandate).
|
|
113
|
+
*
|
|
114
|
+
* @throws {AithosSDKError} `sdk_no_signer`, `sdk_no_delegate_for_mandate`,
|
|
115
|
+
* `network`, `http`, `empty`, or any proxy code (`quota_exceeded`,
|
|
116
|
+
* `mandate_revoked`, `insufficient_credits`, …).
|
|
117
|
+
*/
|
|
118
|
+
async runConversation(args) {
|
|
119
|
+
const { endpoints, fetch: fetchImpl } = this.#deps;
|
|
120
|
+
const choice = this.#resolveSigner(args.mandateId);
|
|
121
|
+
const url = computeInvokeUrl(endpoints);
|
|
122
|
+
const idempotencyKey = args.idempotencyKey ?? generateIdempotencyKey();
|
|
123
|
+
const params = {
|
|
124
|
+
app_did: this.#deps.appDid,
|
|
125
|
+
mandate_id: this.#resolveMandateIdForWire(args.mandateId, choice),
|
|
126
|
+
model: args.model,
|
|
127
|
+
messages: args.messages,
|
|
128
|
+
idempotency_key: idempotencyKey,
|
|
129
|
+
};
|
|
130
|
+
if (args.system !== undefined)
|
|
131
|
+
params.system = args.system;
|
|
132
|
+
if (args.mcp !== undefined)
|
|
133
|
+
params.mcp = args.mcp;
|
|
134
|
+
if (args.workingSet !== undefined)
|
|
135
|
+
params.working_set = args.workingSet;
|
|
136
|
+
if (args.maxTokens !== undefined)
|
|
137
|
+
params.max_tokens = args.maxTokens;
|
|
138
|
+
if (args.maxIterations !== undefined)
|
|
139
|
+
params.max_iterations = args.maxIterations;
|
|
140
|
+
if (args.temperature !== undefined)
|
|
141
|
+
params.temperature = args.temperature;
|
|
142
|
+
return await this.#signAndPost({
|
|
143
|
+
url,
|
|
144
|
+
method: "aithos.compute_converse",
|
|
145
|
+
params,
|
|
146
|
+
choice,
|
|
147
|
+
fetchImpl,
|
|
148
|
+
signal: args.signal,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
104
151
|
/**
|
|
105
152
|
* Multimodal Bedrock invoke — image + text → text response.
|
|
106
153
|
* Default model: `claude-sonnet-4-6` (vision-capable, reliable JSON).
|
package/dist/src/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ 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, ImageAspectRatio, ImageModelId, InvokeBedrockArgs, InvokeBedrockResult, InvokeBedrockVisionArgs, InvokeBedrockVisionResult, InvokeImageArgs, InvokeImageImage, InvokeImageResult, InvokeSegmentationArgs, InvokeSegmentationResult, SegmentPolygon, StopReason, TranscribeModelId, TranscribeProgressState, TranscribeSegment, TranscribeWord, InvokeTranscribeArgs, InvokeTranscribeResult, PrepareTranscribeArgs, PrepareTranscribeResult, StartTranscribeArgs, StartTranscribeResult, TranscribeStatusResult, TranscribeJobSummary, } from "./compute.js";
|
|
8
|
+
export type { ComputeMessage, ImageAspectRatio, ImageModelId, InvokeBedrockArgs, InvokeBedrockResult, InvokeBedrockVisionArgs, InvokeBedrockVisionResult, InvokeImageArgs, InvokeImageImage, InvokeImageResult, InvokeSegmentationArgs, InvokeSegmentationResult, SegmentPolygon, StopReason, RunConversationArgs, RunConversationResult, ComputeWorkingSet, WorkingSetSection, ConverseToolCall, ConverseStopReason, TranscribeModelId, TranscribeProgressState, TranscribeSegment, TranscribeWord, InvokeTranscribeArgs, InvokeTranscribeResult, PrepareTranscribeArgs, PrepareTranscribeResult, StartTranscribeArgs, StartTranscribeResult, TranscribeStatusResult, TranscribeJobSummary, } from "./compute.js";
|
|
9
9
|
export { ComputeNamespace } from "./compute.js";
|
|
10
10
|
export type { LocalPendingEntry, LocalPendingStatus, TranscribeDraftMeta, TranscribeDraftRecord, } from "./transcribe-resilience.js";
|
|
11
11
|
export { LocalPendingTranscribeTracker, TranscribeDraftStore, TranscribeDraftUnavailableError, } from "./transcribe-resilience.js";
|
|
@@ -17,6 +17,8 @@ export declare class OwnerSigners {
|
|
|
17
17
|
readonly public: Signer;
|
|
18
18
|
readonly circle: Signer;
|
|
19
19
|
readonly self: Signer;
|
|
20
|
+
/** Optional dedicated #data sphere signer (absent on legacy owners). */
|
|
21
|
+
readonly data?: Signer;
|
|
20
22
|
constructor(args: {
|
|
21
23
|
did: string;
|
|
22
24
|
handle: string;
|
|
@@ -25,6 +27,7 @@ export declare class OwnerSigners {
|
|
|
25
27
|
public: Signer;
|
|
26
28
|
circle: Signer;
|
|
27
29
|
self: Signer;
|
|
30
|
+
data?: Signer;
|
|
28
31
|
});
|
|
29
32
|
/**
|
|
30
33
|
* Build from a {@link BrowserIdentity}. The seeds inside `identity`
|
|
@@ -59,7 +62,7 @@ export declare class OwnerSigners {
|
|
|
59
62
|
* have the sphere as a string ("public" | "circle" | "self") rather
|
|
60
63
|
* than a typed accessor.
|
|
61
64
|
*/
|
|
62
|
-
signerForSphere(sphere: "root" | "public" | "circle" | "self"): Signer;
|
|
65
|
+
signerForSphere(sphere: "root" | "public" | "circle" | "self" | "data"): Signer;
|
|
63
66
|
/**
|
|
64
67
|
* Internal — project to a {@link StoredIdentity} for protocol-client
|
|
65
68
|
* interop. Reads seed bytes via {@link RawSeedSigner._unsafeKeyPair}
|
|
@@ -71,7 +74,7 @@ export declare class OwnerSigners {
|
|
|
71
74
|
* @internal
|
|
72
75
|
*/
|
|
73
76
|
_unsafeStoredIdentity(): StoredIdentity;
|
|
74
|
-
/** Zeroize all
|
|
77
|
+
/** Zeroize all private seeds. Idempotent. */
|
|
75
78
|
destroy(): void;
|
|
76
79
|
get destroyed(): boolean;
|
|
77
80
|
}
|
|
@@ -34,6 +34,8 @@ export class OwnerSigners {
|
|
|
34
34
|
public;
|
|
35
35
|
circle;
|
|
36
36
|
self;
|
|
37
|
+
/** Optional dedicated #data sphere signer (absent on legacy owners). */
|
|
38
|
+
data;
|
|
37
39
|
#destroyed = false;
|
|
38
40
|
constructor(args) {
|
|
39
41
|
this.did = args.did;
|
|
@@ -43,6 +45,7 @@ export class OwnerSigners {
|
|
|
43
45
|
this.public = args.public;
|
|
44
46
|
this.circle = args.circle;
|
|
45
47
|
this.self = args.self;
|
|
48
|
+
this.data = args.data;
|
|
46
49
|
}
|
|
47
50
|
/**
|
|
48
51
|
* Build from a {@link BrowserIdentity}. The seeds inside `identity`
|
|
@@ -59,6 +62,11 @@ export class OwnerSigners {
|
|
|
59
62
|
public: new RawSeedSigner(identity.public.seed, identity.public.publicKey),
|
|
60
63
|
circle: new RawSeedSigner(identity.circle.seed, identity.circle.publicKey),
|
|
61
64
|
self: new RawSeedSigner(identity.self.seed, identity.self.publicKey),
|
|
65
|
+
...(identity.data
|
|
66
|
+
? {
|
|
67
|
+
data: new RawSeedSigner(identity.data.seed, identity.data.publicKey),
|
|
68
|
+
}
|
|
69
|
+
: {}),
|
|
62
70
|
});
|
|
63
71
|
}
|
|
64
72
|
/**
|
|
@@ -120,6 +128,11 @@ export class OwnerSigners {
|
|
|
120
128
|
return this.circle;
|
|
121
129
|
case "self":
|
|
122
130
|
return this.self;
|
|
131
|
+
case "data":
|
|
132
|
+
if (!this.data) {
|
|
133
|
+
throw new Error("OwnerSigners: this owner has no #data sphere (legacy account)");
|
|
134
|
+
}
|
|
135
|
+
return this.data;
|
|
123
136
|
}
|
|
124
137
|
}
|
|
125
138
|
/**
|
|
@@ -146,11 +159,18 @@ export class OwnerSigners {
|
|
|
146
159
|
public: bytesToHexLocal(asRawSeed(this.public, "public")._unsafeKeyPair().seed),
|
|
147
160
|
circle: bytesToHexLocal(asRawSeed(this.circle, "circle")._unsafeKeyPair().seed),
|
|
148
161
|
self: bytesToHexLocal(asRawSeed(this.self, "self")._unsafeKeyPair().seed),
|
|
162
|
+
// Preserve the optional #data seed on vault re-encryption — dropping it
|
|
163
|
+
// here would silently lose the data sphere on the next delegate-add.
|
|
164
|
+
...(this.data
|
|
165
|
+
? {
|
|
166
|
+
data: bytesToHexLocal(asRawSeed(this.data, "data")._unsafeKeyPair().seed),
|
|
167
|
+
}
|
|
168
|
+
: {}),
|
|
149
169
|
},
|
|
150
170
|
savedAt: new Date().toISOString(),
|
|
151
171
|
};
|
|
152
172
|
}
|
|
153
|
-
/** Zeroize all
|
|
173
|
+
/** Zeroize all private seeds. Idempotent. */
|
|
154
174
|
destroy() {
|
|
155
175
|
if (this.#destroyed)
|
|
156
176
|
return;
|
|
@@ -158,6 +178,7 @@ export class OwnerSigners {
|
|
|
158
178
|
this.public.destroy();
|
|
159
179
|
this.circle.destroy();
|
|
160
180
|
this.self.destroy();
|
|
181
|
+
this.data?.destroy();
|
|
161
182
|
this.#destroyed = true;
|
|
162
183
|
}
|
|
163
184
|
get destroyed() {
|
|
@@ -44,6 +44,11 @@ export function parseRecoveryFile(text) {
|
|
|
44
44
|
throw bad(`seeds_hex.${k}: expected 64-char lowercase hex`);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
// #data is optional (absent on legacy recovery files); validate when present.
|
|
48
|
+
const dataSeed = seeds["data"];
|
|
49
|
+
if (dataSeed !== undefined && (typeof dataSeed !== "string" || !HEX_64.test(dataSeed))) {
|
|
50
|
+
throw bad("seeds_hex.data: expected 64-char lowercase hex");
|
|
51
|
+
}
|
|
47
52
|
return {
|
|
48
53
|
handle,
|
|
49
54
|
displayName,
|
|
@@ -53,6 +58,7 @@ export function parseRecoveryFile(text) {
|
|
|
53
58
|
public: seeds["public"],
|
|
54
59
|
circle: seeds["circle"],
|
|
55
60
|
self: seeds["self"],
|
|
61
|
+
...(typeof dataSeed === "string" ? { data: dataSeed } : {}),
|
|
56
62
|
},
|
|
57
63
|
};
|
|
58
64
|
}
|
|
@@ -78,6 +84,7 @@ export function serializeRecoveryFile(identity) {
|
|
|
78
84
|
public: bytesToHex(identity.public.seed),
|
|
79
85
|
circle: bytesToHex(identity.circle.seed),
|
|
80
86
|
self: bytesToHex(identity.self.seed),
|
|
87
|
+
...(identity.data ? { data: bytesToHex(identity.data.seed) } : {}),
|
|
81
88
|
},
|
|
82
89
|
saved_at: new Date().toISOString(),
|
|
83
90
|
};
|
package/dist/src/key-store.d.ts
CHANGED
|
@@ -19,6 +19,12 @@ export interface StoredOwnerKeys {
|
|
|
19
19
|
readonly public: string;
|
|
20
20
|
readonly circle: string;
|
|
21
21
|
readonly self: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional dedicated #data sphere seed (spec/data/02-key-hierarchy.md).
|
|
24
|
+
* Present on owners created since the #data sphere landed; absent on legacy
|
|
25
|
+
* owners (which sign data/asset PDS ops under #root).
|
|
26
|
+
*/
|
|
27
|
+
readonly data?: string;
|
|
22
28
|
};
|
|
23
29
|
/** ISO-8601 timestamp of the original save. Informational. */
|
|
24
30
|
readonly savedAt: string;
|
package/dist/src/key-store.js
CHANGED
|
@@ -213,6 +213,12 @@ function validOwnerOrNull(v) {
|
|
|
213
213
|
if (s[k].length !== 64)
|
|
214
214
|
return null;
|
|
215
215
|
}
|
|
216
|
+
// #data is optional (absent on legacy owners); validate only when present.
|
|
217
|
+
if (s["data"] !== undefined) {
|
|
218
|
+
if (typeof s["data"] !== "string" || s["data"].length !== 64) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
216
222
|
if (typeof o["savedAt"] !== "string")
|
|
217
223
|
return null;
|
|
218
224
|
return o;
|
package/dist/src/sdk.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AithosAuth } from "./auth.js";
|
|
2
2
|
import { AppsNamespace } from "./apps.js";
|
|
3
|
-
import { ComputeNamespace } from "./compute.js";
|
|
3
|
+
import { ComputeNamespace, type ComputeWorkingSet } from "./compute.js";
|
|
4
4
|
import { type AithosSdkEndpoints } from "./endpoints.js";
|
|
5
|
-
import { EthosNamespace } from "./ethos.js";
|
|
5
|
+
import { EthosNamespace, type ZoneName } from "./ethos.js";
|
|
6
6
|
import { MandatesNamespace } from "./mandates.js";
|
|
7
7
|
import { WalletNamespace } from "./wallet.js";
|
|
8
8
|
import { WebNamespace } from "./web.js";
|
|
@@ -57,5 +57,29 @@ export declare class AithosSDK {
|
|
|
57
57
|
constructor(config: AithosSDKConfig);
|
|
58
58
|
/** DID of the currently signed-in owner, or null if no owner is loaded. */
|
|
59
59
|
get userDid(): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* Build the client-decrypted working-set for an agentic conversation.
|
|
62
|
+
*
|
|
63
|
+
* Reads the requested ethos zones for `did` through {@link EthosNamespace}
|
|
64
|
+
* (which decrypts client-side using the active owner/delegate keys) and
|
|
65
|
+
* packages them into the {@link ComputeWorkingSet} shape that
|
|
66
|
+
* `sdk.compute.runConversation({ workingSet })` consumes.
|
|
67
|
+
*
|
|
68
|
+
* Only zones the session can actually read are included: a zone the
|
|
69
|
+
* mandate does not grant (or whose delegate wrap is missing) throws
|
|
70
|
+
* `ethos_zone_unreadable` / `ethos_anonymous_private_zone` on read and is
|
|
71
|
+
* silently skipped — so the working-set is naturally bounded by what the
|
|
72
|
+
* caller is authorized to see. The proxy never holds a decryption key;
|
|
73
|
+
* this is where the plaintext is produced (see PLATFORM-COMPUTE-AGENTIC-MCP.md §4).
|
|
74
|
+
*
|
|
75
|
+
* Structured (gamma) data collections are not loaded here in v1 — attach
|
|
76
|
+
* them to the returned object's `data` field yourself if needed.
|
|
77
|
+
*
|
|
78
|
+
* @param did Subject DID whose ethos to read (usually the signed-in owner).
|
|
79
|
+
* @param opts.zones Zones to attempt. Defaults to all three.
|
|
80
|
+
*/
|
|
81
|
+
buildWorkingSet(did: string, opts?: {
|
|
82
|
+
readonly zones?: readonly ZoneName[];
|
|
83
|
+
}): Promise<ComputeWorkingSet>;
|
|
60
84
|
}
|
|
61
85
|
//# sourceMappingURL=sdk.d.ts.map
|
package/dist/src/sdk.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
// Copyright 2026 Mathieu Colla
|
|
3
3
|
import { AppsNamespace } from "./apps.js";
|
|
4
|
-
import { ComputeNamespace } from "./compute.js";
|
|
4
|
+
import { ComputeNamespace, } from "./compute.js";
|
|
5
5
|
import { resolveEndpoints } from "./endpoints.js";
|
|
6
6
|
import { EthosNamespace } from "./ethos.js";
|
|
7
7
|
import { MandatesNamespace } from "./mandates.js";
|
|
8
|
+
import { AithosSDKError } from "./types.js";
|
|
8
9
|
import { WalletNamespace } from "./wallet.js";
|
|
9
10
|
import { WebNamespace } from "./web.js";
|
|
10
11
|
export class AithosSDK {
|
|
@@ -80,5 +81,48 @@ export class AithosSDK {
|
|
|
80
81
|
get userDid() {
|
|
81
82
|
return this.auth.getOwnerInfo()?.did ?? null;
|
|
82
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Build the client-decrypted working-set for an agentic conversation.
|
|
86
|
+
*
|
|
87
|
+
* Reads the requested ethos zones for `did` through {@link EthosNamespace}
|
|
88
|
+
* (which decrypts client-side using the active owner/delegate keys) and
|
|
89
|
+
* packages them into the {@link ComputeWorkingSet} shape that
|
|
90
|
+
* `sdk.compute.runConversation({ workingSet })` consumes.
|
|
91
|
+
*
|
|
92
|
+
* Only zones the session can actually read are included: a zone the
|
|
93
|
+
* mandate does not grant (or whose delegate wrap is missing) throws
|
|
94
|
+
* `ethos_zone_unreadable` / `ethos_anonymous_private_zone` on read and is
|
|
95
|
+
* silently skipped — so the working-set is naturally bounded by what the
|
|
96
|
+
* caller is authorized to see. The proxy never holds a decryption key;
|
|
97
|
+
* this is where the plaintext is produced (see PLATFORM-COMPUTE-AGENTIC-MCP.md §4).
|
|
98
|
+
*
|
|
99
|
+
* Structured (gamma) data collections are not loaded here in v1 — attach
|
|
100
|
+
* them to the returned object's `data` field yourself if needed.
|
|
101
|
+
*
|
|
102
|
+
* @param did Subject DID whose ethos to read (usually the signed-in owner).
|
|
103
|
+
* @param opts.zones Zones to attempt. Defaults to all three.
|
|
104
|
+
*/
|
|
105
|
+
async buildWorkingSet(did, opts) {
|
|
106
|
+
const zones = opts?.zones ?? ["public", "circle", "self"];
|
|
107
|
+
const client = await this.ethos.of(did);
|
|
108
|
+
const ethos = {};
|
|
109
|
+
for (const zone of zones) {
|
|
110
|
+
try {
|
|
111
|
+
const sections = await client.zone(zone).sections();
|
|
112
|
+
ethos[zone] = sections.map((s) => ({
|
|
113
|
+
id: s.id,
|
|
114
|
+
title: s.title,
|
|
115
|
+
body: s.body,
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// Zone not granted / not decryptable for this session → skip it.
|
|
120
|
+
if (e instanceof AithosSDKError)
|
|
121
|
+
continue;
|
|
122
|
+
throw e;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { ethos };
|
|
126
|
+
}
|
|
83
127
|
}
|
|
84
128
|
//# sourceMappingURL=sdk.js.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// Unit tests for sdk.compute.runConversation with a mock fetch.
|
|
4
|
+
//
|
|
5
|
+
// Mirrors compute.test.ts: a real BrowserIdentity drives the actual
|
|
6
|
+
// envelope-signing path, and we assert on the JSON-RPC body posted to
|
|
7
|
+
// the compute proxy (method name + camelCase→snake_case param mapping).
|
|
8
|
+
import { strict as assert } from "node:assert";
|
|
9
|
+
import { describe, it } from "node:test";
|
|
10
|
+
import { createBrowserIdentity } from "@aithos/protocol-client";
|
|
11
|
+
import { AithosAuth, AithosSDK, AithosSDKError, memoryKeyStore, noopStore, } from "../src/index.js";
|
|
12
|
+
import { serializeRecoveryFile } from "../src/internal/recovery-file.js";
|
|
13
|
+
const APP_DID = "did:aithos:app:test";
|
|
14
|
+
async function makeSdk(fetchImpl) {
|
|
15
|
+
const id = createBrowserIdentity("test-handle", "Test User");
|
|
16
|
+
const auth = new AithosAuth({
|
|
17
|
+
authBaseUrl: "https://auth.test",
|
|
18
|
+
fetch: (() => {
|
|
19
|
+
throw new Error("auth not used in converse tests");
|
|
20
|
+
}),
|
|
21
|
+
sessionStore: noopStore(),
|
|
22
|
+
keyStore: memoryKeyStore(),
|
|
23
|
+
});
|
|
24
|
+
const { text } = serializeRecoveryFile(id);
|
|
25
|
+
await auth.signInWithRecovery({ file: text });
|
|
26
|
+
return new AithosSDK({
|
|
27
|
+
auth,
|
|
28
|
+
appDid: APP_DID,
|
|
29
|
+
endpoints: { compute: "https://compute.example.test" },
|
|
30
|
+
fetch: fetchImpl,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const HAPPY_RESULT = {
|
|
34
|
+
content: "Voici le résumé.",
|
|
35
|
+
stopReason: "end_turn",
|
|
36
|
+
iterations: 2,
|
|
37
|
+
usage: { inputTokens: 420, outputTokens: 95 },
|
|
38
|
+
toolCalls: [{ name: "ethos_read_section", ok: true, turn: 1 }],
|
|
39
|
+
creditsCharged: 130,
|
|
40
|
+
walletBalance: 99_870,
|
|
41
|
+
auditId: "audit-cv-1",
|
|
42
|
+
fundedBy: "purchase",
|
|
43
|
+
};
|
|
44
|
+
describe("compute.runConversation — happy path + param mapping", () => {
|
|
45
|
+
it("posts aithos.compute_converse with mapped params and parses the result", async () => {
|
|
46
|
+
let capturedUrl;
|
|
47
|
+
let capturedInit;
|
|
48
|
+
const fakeFetch = async (input, init) => {
|
|
49
|
+
capturedUrl = typeof input === "string" ? input : input.toString();
|
|
50
|
+
capturedInit = init;
|
|
51
|
+
return new Response(JSON.stringify({ result: HAPPY_RESULT }), {
|
|
52
|
+
status: 200,
|
|
53
|
+
headers: { "content-type": "application/json" },
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const sdk = await makeSdk(fakeFetch);
|
|
57
|
+
const out = await sdk.compute.runConversation({
|
|
58
|
+
mandateId: "mandate:abc",
|
|
59
|
+
model: "claude-sonnet-4-6",
|
|
60
|
+
system: "Tu agis dans la voix de l'utilisateur.",
|
|
61
|
+
messages: [{ role: "user", content: "Résume mon ethos." }],
|
|
62
|
+
mcp: { server: "aithos", tools: ["ethos_list_sections", "ethos_read_section"] },
|
|
63
|
+
workingSet: {
|
|
64
|
+
ethos: { public: [{ id: "p1", title: "Bio", body: "..." }] },
|
|
65
|
+
},
|
|
66
|
+
maxIterations: 5,
|
|
67
|
+
maxTokens: 800,
|
|
68
|
+
temperature: 0.4,
|
|
69
|
+
});
|
|
70
|
+
assert.deepEqual(out, HAPPY_RESULT);
|
|
71
|
+
assert.equal(capturedUrl, "https://compute.example.test/v1/invoke");
|
|
72
|
+
assert.equal(capturedInit?.method, "POST");
|
|
73
|
+
const body = JSON.parse(capturedInit?.body);
|
|
74
|
+
assert.equal(body.jsonrpc, "2.0");
|
|
75
|
+
assert.equal(body.method, "aithos.compute_converse");
|
|
76
|
+
assert.equal(body.params.app_did, APP_DID);
|
|
77
|
+
assert.equal(body.params.mandate_id, "mandate:abc");
|
|
78
|
+
assert.equal(body.params.model, "claude-sonnet-4-6");
|
|
79
|
+
assert.equal(body.params.system, "Tu agis dans la voix de l'utilisateur.");
|
|
80
|
+
// camelCase → snake_case mapping on the wire.
|
|
81
|
+
assert.equal(body.params.max_iterations, 5);
|
|
82
|
+
assert.equal(body.params.max_tokens, 800);
|
|
83
|
+
assert.equal(body.params.temperature, 0.4);
|
|
84
|
+
assert.ok(body.params.working_set, "working_set must be on the wire");
|
|
85
|
+
assert.deepEqual(body.params.mcp, {
|
|
86
|
+
server: "aithos",
|
|
87
|
+
tools: ["ethos_list_sections", "ethos_read_section"],
|
|
88
|
+
});
|
|
89
|
+
assert.match(body.params.idempotency_key, /^[0-9a-f]{32}$/);
|
|
90
|
+
assert.ok(body.params._envelope, "request must carry a signed envelope");
|
|
91
|
+
// SDK-only camelCase keys must NOT leak onto the wire.
|
|
92
|
+
assert.equal(body.params.maxIterations, undefined);
|
|
93
|
+
assert.equal(body.params.workingSet, undefined);
|
|
94
|
+
});
|
|
95
|
+
it("omits optional fields when not provided", async () => {
|
|
96
|
+
let capturedInit;
|
|
97
|
+
const fakeFetch = async (_input, init) => {
|
|
98
|
+
capturedInit = init;
|
|
99
|
+
return new Response(JSON.stringify({ result: HAPPY_RESULT }), {
|
|
100
|
+
status: 200,
|
|
101
|
+
headers: { "content-type": "application/json" },
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
const sdk = await makeSdk(fakeFetch);
|
|
105
|
+
await sdk.compute.runConversation({
|
|
106
|
+
mandateId: "mandate:abc",
|
|
107
|
+
model: "claude-haiku-4-5",
|
|
108
|
+
messages: [{ role: "user", content: "Salut" }],
|
|
109
|
+
});
|
|
110
|
+
const body = JSON.parse(capturedInit?.body);
|
|
111
|
+
assert.equal(body.params.system, undefined);
|
|
112
|
+
assert.equal(body.params.mcp, undefined);
|
|
113
|
+
assert.equal(body.params.working_set, undefined);
|
|
114
|
+
assert.equal(body.params.max_iterations, undefined);
|
|
115
|
+
assert.equal(body.params.max_tokens, undefined);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe("compute.runConversation — errors", () => {
|
|
119
|
+
it("maps a JSON-RPC error to AithosSDKError with the proxy code", async () => {
|
|
120
|
+
const fakeFetch = async () => new Response(JSON.stringify({
|
|
121
|
+
error: { code: -32071, message: "insufficient credits" },
|
|
122
|
+
}), { status: 200, headers: { "content-type": "application/json" } });
|
|
123
|
+
const sdk = await makeSdk(fakeFetch);
|
|
124
|
+
await assert.rejects(() => sdk.compute.runConversation({
|
|
125
|
+
mandateId: "mandate:abc",
|
|
126
|
+
model: "claude-sonnet-4-6",
|
|
127
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
128
|
+
}), (err) => {
|
|
129
|
+
assert.ok(err instanceof AithosSDKError);
|
|
130
|
+
assert.equal(err.code, "-32071");
|
|
131
|
+
return true;
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
it("throws sdk_no_signer when no owner and no mandate", async () => {
|
|
135
|
+
// Build an SDK with no signed-in owner.
|
|
136
|
+
const auth = new AithosAuth({
|
|
137
|
+
authBaseUrl: "https://auth.test",
|
|
138
|
+
fetch: (() => {
|
|
139
|
+
throw new Error("unused");
|
|
140
|
+
}),
|
|
141
|
+
sessionStore: noopStore(),
|
|
142
|
+
keyStore: memoryKeyStore(),
|
|
143
|
+
});
|
|
144
|
+
const sdk = new AithosSDK({
|
|
145
|
+
auth,
|
|
146
|
+
appDid: APP_DID,
|
|
147
|
+
endpoints: { compute: "https://compute.example.test" },
|
|
148
|
+
fetch: (() => {
|
|
149
|
+
throw new Error("fetch must not be reached");
|
|
150
|
+
}),
|
|
151
|
+
});
|
|
152
|
+
await assert.rejects(() => sdk.compute.runConversation({
|
|
153
|
+
model: "claude-sonnet-4-6",
|
|
154
|
+
messages: [{ role: "user", content: "Hi" }],
|
|
155
|
+
}), (err) => {
|
|
156
|
+
assert.ok(err instanceof AithosSDKError);
|
|
157
|
+
assert.equal(err.code, "sdk_no_signer");
|
|
158
|
+
return true;
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
//# sourceMappingURL=converse.test.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 Mathieu Colla
|
|
3
|
+
// Tests for the optional #data sphere through the SDK's owner key material:
|
|
4
|
+
// the recovery file and OwnerSigners (vault re-encryption projection).
|
|
5
|
+
// Mirrors the protocol-core / protocol-client data-sphere tests.
|
|
6
|
+
import { strict as assert } from "node:assert";
|
|
7
|
+
import { describe, it } from "node:test";
|
|
8
|
+
import { createBrowserIdentity } from "@aithos/protocol-client";
|
|
9
|
+
import { OwnerSigners } from "../src/internal/owner-signers.js";
|
|
10
|
+
import { serializeRecoveryFile, parseRecoveryFile, } from "../src/internal/recovery-file.js";
|
|
11
|
+
describe("#data sphere — recovery file", () => {
|
|
12
|
+
it("round-trips the #data seed for a fresh (eager) identity", () => {
|
|
13
|
+
const id = createBrowserIdentity("alice", "Alice");
|
|
14
|
+
assert.ok(id.data, "createBrowserIdentity should be eager about #data");
|
|
15
|
+
const { text } = serializeRecoveryFile(id);
|
|
16
|
+
const parsed = parseRecoveryFile(text);
|
|
17
|
+
assert.equal(typeof parsed.seedsHex.data, "string");
|
|
18
|
+
assert.equal(parsed.seedsHex.data.length, 64);
|
|
19
|
+
});
|
|
20
|
+
it("legacy identity without #data serializes/parses without a data seed", () => {
|
|
21
|
+
const id = { ...createBrowserIdentity("dave", "Dave"), data: undefined };
|
|
22
|
+
const { text } = serializeRecoveryFile(id);
|
|
23
|
+
const parsed = parseRecoveryFile(text);
|
|
24
|
+
assert.equal(parsed.seedsHex.data, undefined);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("#data sphere — OwnerSigners", () => {
|
|
28
|
+
it("preserves the #data seed on the vault re-encryption projection", () => {
|
|
29
|
+
const id = createBrowserIdentity("bob", "Bob");
|
|
30
|
+
const signers = OwnerSigners.fromBrowserIdentity(id);
|
|
31
|
+
try {
|
|
32
|
+
// _unsafeStoredIdentity() is what the vault re-encryption path reads —
|
|
33
|
+
// dropping #data here would silently lose the sphere on delegate-add.
|
|
34
|
+
const projected = signers._unsafeStoredIdentity();
|
|
35
|
+
assert.ok(projected.seeds.data, "#data seed must survive projection");
|
|
36
|
+
// signerForSphere exposes the #data signer.
|
|
37
|
+
assert.ok(signers.data, "OwnerSigners.data signer should be present");
|
|
38
|
+
assert.equal(signers.signerForSphere("data"), signers.data);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
signers.destroy();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
it("legacy owner (no #data) → no data signer, signerForSphere('data') throws", () => {
|
|
45
|
+
const id = { ...createBrowserIdentity("erin", "Erin"), data: undefined };
|
|
46
|
+
const signers = OwnerSigners.fromBrowserIdentity(id);
|
|
47
|
+
try {
|
|
48
|
+
assert.equal(signers.data, undefined);
|
|
49
|
+
assert.equal(signers._unsafeStoredIdentity().seeds.data, undefined);
|
|
50
|
+
assert.throws(() => signers.signerForSphere("data"), /no #data sphere/);
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
signers.destroy();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=data-sphere.test.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aithos/sdk",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.54",
|
|
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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@aithos/assets-crypto": ">=0.1.0-alpha.1 <0.2.0",
|
|
60
|
-
"@aithos/protocol-client": "^0.1.0-alpha.
|
|
60
|
+
"@aithos/protocol-client": "^0.1.0-alpha.19",
|
|
61
61
|
"@aithos/protocol-core": ">=0.6.5 <0.7.0",
|
|
62
62
|
"react": "^18.0.0 || ^19.0.0"
|
|
63
63
|
},
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@aithos/assets-crypto": "^0.1.0-alpha.1",
|
|
74
|
-
"@aithos/protocol-client": "^0.1.0-alpha.
|
|
74
|
+
"@aithos/protocol-client": "^0.1.0-alpha.19",
|
|
75
75
|
"@aithos/protocol-core": ">=0.6.5 <0.7.0",
|
|
76
76
|
"@types/node": "^24.12.2",
|
|
77
77
|
"fake-indexeddb": "^6.2.5",
|