@supercuts-walrus/backend 0.1.1 → 0.1.3
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/README.md +28 -5
- package/config/phase1.testnet.json +20 -0
- package/config/proving-manifest.json +11 -0
- package/dist/buyer-setup.js +27 -13
- package/dist/cli.js +10 -2
- package/dist/dashboard.js +1 -1
- package/dist/deal-adapter.js +0 -2
- package/dist/deal-engine.d.ts +13 -0
- package/dist/deal-engine.js +64 -39
- package/dist/deal.d.ts +35 -0
- package/dist/deal.js +9 -3
- package/dist/embedding-adapter.d.ts +26 -0
- package/dist/embedding-adapter.js +116 -0
- package/dist/facilitator.js +2 -1
- package/dist/funding.js +10 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/live-deal-ports.d.ts +2 -1
- package/dist/live-deal-ports.js +31 -5
- package/dist/live-runtime.d.ts +1 -1
- package/dist/live-runtime.js +27 -11
- package/dist/live-seller-transport.js +5 -1
- package/dist/main-module.d.ts +5 -0
- package/dist/main-module.js +16 -0
- package/dist/mandate.js +1 -1
- package/dist/mcp-bootstrap.js +3 -3
- package/dist/mcp.js +2 -0
- package/dist/memory-scorecard.d.ts +32 -0
- package/dist/memory-scorecard.js +76 -0
- package/dist/memwal.d.ts +28 -0
- package/dist/memwal.js +81 -2
- package/dist/onboarding-doctor.js +12 -5
- package/dist/phase3.d.ts +43 -0
- package/dist/phase3.js +147 -32
- package/dist/role-lock.d.ts +1 -1
- package/dist/role-lock.js +1 -1
- package/dist/seam.js +2 -1
- package/dist/seller-catalog-generate.js +4 -4
- package/dist/seller-directory-cli.js +2 -1
- package/dist/seller-live-cli.js +10 -1
- package/dist/seller-provision.js +15 -1
- package/dist/seller-research.d.ts +50 -0
- package/dist/seller-research.js +210 -0
- package/dist/seller-scaffold.d.ts +1 -0
- package/dist/seller-scaffold.js +35 -0
- package/dist/seller-service.d.ts +1 -1
- package/dist/seller-service.js +8 -20
- package/dist/seller-setup.d.ts +3 -0
- package/dist/seller-setup.js +109 -0
- package/dist/settlement.d.ts +32 -3
- package/dist/settlement.js +56 -11
- package/dist/testnet-defaults.d.ts +21 -0
- package/dist/testnet-defaults.js +26 -0
- package/package.json +4 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OpenAI-compatible embedding adapter for local models (`supercuts-embedding-adapter`).
|
|
4
|
+
*
|
|
5
|
+
* Walrus Mainnet Memory indexes 1536-dimensional embeddings. This loopback proxy forwards requests to a
|
|
6
|
+
* local OpenAI-compatible endpoint (Ollama by default) and zero-pads shorter vectors to 1536 dimensions.
|
|
7
|
+
* Zero-padding preserves cosine similarity and Euclidean distance as long as every write and recall uses
|
|
8
|
+
* this same adapter and upstream model. Memory plaintext only reaches the configured upstream.
|
|
9
|
+
*/
|
|
10
|
+
import { createServer } from "node:http";
|
|
11
|
+
import { isMainModule } from "./main-module.js";
|
|
12
|
+
export const TARGET_DIMENSIONS = 1536;
|
|
13
|
+
const MAX_REQUEST_BYTES = 1024 * 1024;
|
|
14
|
+
export function padEmbedding(embedding) {
|
|
15
|
+
if (!Array.isArray(embedding) ||
|
|
16
|
+
embedding.length < 1 ||
|
|
17
|
+
embedding.length > TARGET_DIMENSIONS) {
|
|
18
|
+
throw new Error(`expected an embedding with 1 to ${TARGET_DIMENSIONS} dimensions, received ${Array.isArray(embedding) ? embedding.length : "a non-array value"}`);
|
|
19
|
+
}
|
|
20
|
+
if (!embedding.every((value) => typeof value === "number" && Number.isFinite(value)))
|
|
21
|
+
throw new Error("upstream embedding contains a non-finite value");
|
|
22
|
+
return [
|
|
23
|
+
...embedding,
|
|
24
|
+
...Array(TARGET_DIMENSIONS - embedding.length).fill(0),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
export function transformEmbeddingResponse(payload) {
|
|
28
|
+
const value = payload;
|
|
29
|
+
if (!value || !Array.isArray(value.data) || !value.data.length)
|
|
30
|
+
throw new Error("upstream response has no embeddings");
|
|
31
|
+
const dimension = value.data[0]?.embedding?.length;
|
|
32
|
+
if (value.data.some((item) => item?.embedding?.length !== dimension))
|
|
33
|
+
throw new Error("upstream embeddings have inconsistent dimensions");
|
|
34
|
+
return {
|
|
35
|
+
...value,
|
|
36
|
+
data: value.data.map((item) => ({
|
|
37
|
+
...item,
|
|
38
|
+
embedding: padEmbedding(item?.embedding),
|
|
39
|
+
})),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function createEmbeddingAdapter({ fetchImpl = fetch, upstreamBaseUrl, upstreamApiKey, upstreamModel, }) {
|
|
43
|
+
if (!upstreamBaseUrl)
|
|
44
|
+
throw new Error("EMBEDDING_UPSTREAM_BASE_URL is required");
|
|
45
|
+
if (!upstreamModel)
|
|
46
|
+
throw new Error("EMBEDDING_UPSTREAM_MODEL is required");
|
|
47
|
+
const endpoint = `${upstreamBaseUrl.replace(/\/$/, "")}/embeddings`;
|
|
48
|
+
let sourceDimensions;
|
|
49
|
+
const send = (response, status, body) => {
|
|
50
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
51
|
+
response.end(typeof body === "string" ? body : JSON.stringify(body));
|
|
52
|
+
};
|
|
53
|
+
return createServer(async (request, response) => {
|
|
54
|
+
// Loopback only: reject other Host headers so a DNS-rebinding page cannot drive the local embedding model.
|
|
55
|
+
const host = (request.headers.host ?? "").replace(/:\d+$/, "").replace(/^\[|\]$/g, "");
|
|
56
|
+
if (!["127.0.0.1", "localhost", "::1"].includes(host))
|
|
57
|
+
return send(response, 403, { error: { message: "loopback host required" } });
|
|
58
|
+
if (request.method === "GET" && request.url === "/health")
|
|
59
|
+
return send(response, 200, {
|
|
60
|
+
status: "ok",
|
|
61
|
+
targetDimensions: TARGET_DIMENSIONS,
|
|
62
|
+
upstreamModel,
|
|
63
|
+
});
|
|
64
|
+
if (request.method !== "POST" || request.url !== "/v1/embeddings")
|
|
65
|
+
return send(response, 404, { error: { message: "not found" } });
|
|
66
|
+
try {
|
|
67
|
+
let body = "";
|
|
68
|
+
for await (const chunk of request) {
|
|
69
|
+
body += chunk;
|
|
70
|
+
if (body.length > MAX_REQUEST_BYTES)
|
|
71
|
+
throw new Error("embedding request is too large");
|
|
72
|
+
}
|
|
73
|
+
const requested = JSON.parse(body);
|
|
74
|
+
const upstream = await fetchImpl(endpoint, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: {
|
|
77
|
+
"content-type": "application/json",
|
|
78
|
+
...(upstreamApiKey
|
|
79
|
+
? { authorization: `Bearer ${upstreamApiKey}` }
|
|
80
|
+
: {}),
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({ ...requested, model: upstreamModel }),
|
|
83
|
+
signal: AbortSignal.timeout(60_000),
|
|
84
|
+
});
|
|
85
|
+
const upstreamBody = await upstream.text();
|
|
86
|
+
if (!upstream.ok)
|
|
87
|
+
return send(response, upstream.status, upstreamBody);
|
|
88
|
+
const payload = JSON.parse(upstreamBody), result = transformEmbeddingResponse(payload);
|
|
89
|
+
const dimension = payload.data[0].embedding.length;
|
|
90
|
+
if (sourceDimensions !== undefined && sourceDimensions !== dimension)
|
|
91
|
+
throw new Error("upstream embedding dimensions changed");
|
|
92
|
+
sourceDimensions = dimension;
|
|
93
|
+
send(response, 200, result);
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
send(response, 502, {
|
|
97
|
+
error: {
|
|
98
|
+
message: error instanceof Error ? error.message : "embedding adapter failed",
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (isMainModule(import.meta.url)) {
|
|
105
|
+
const port = Number(process.env.EMBEDDING_ADAPTER_PORT ?? 8787);
|
|
106
|
+
const upstreamBaseUrl = process.env.EMBEDDING_UPSTREAM_BASE_URL ?? "http://127.0.0.1:11434/v1";
|
|
107
|
+
const upstreamModel = process.env.EMBEDDING_UPSTREAM_MODEL ?? "embeddinggemma";
|
|
108
|
+
const server = createEmbeddingAdapter({
|
|
109
|
+
upstreamBaseUrl,
|
|
110
|
+
upstreamModel,
|
|
111
|
+
...(process.env.EMBEDDING_UPSTREAM_API_KEY
|
|
112
|
+
? { upstreamApiKey: process.env.EMBEDDING_UPSTREAM_API_KEY }
|
|
113
|
+
: {}),
|
|
114
|
+
});
|
|
115
|
+
server.listen(port, "127.0.0.1", () => process.stdout.write(`${JSON.stringify({ ok: true, listening: `http://127.0.0.1:${port}/v1`, upstream: upstreamBaseUrl, upstreamModel, targetDimensions: TARGET_DIMENSIONS, memoryConfig: { embeddingApiBase: `http://127.0.0.1:${port}/v1`, embeddingModel: `${upstreamModel}-1536` } })}\n`));
|
|
116
|
+
}
|
package/dist/facilitator.js
CHANGED
|
@@ -3,6 +3,7 @@ import { timingSafeEqual } from "node:crypto";
|
|
|
3
3
|
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
4
4
|
import { Transaction } from "@mysten/sui/transactions";
|
|
5
5
|
import { isValidTransactionSignature } from "@mysten/sui/verify";
|
|
6
|
+
import { isMainModule } from "./main-module.js";
|
|
6
7
|
const NETWORK = "sui:testnet";
|
|
7
8
|
const SUI = "0x2::sui::SUI";
|
|
8
9
|
/** Sui RPC may expand a type's address to 32 bytes; policy identifiers use its canonical short form. */
|
|
@@ -164,7 +165,7 @@ export class SuiTestnetFacilitator {
|
|
|
164
165
|
}
|
|
165
166
|
send(response, status, body) { const encoded = Buffer.from(JSON.stringify(body)); response.writeHead(status, { "content-type": "application/json", "content-length": String(encoded.length) }); response.end(encoded); }
|
|
166
167
|
}
|
|
167
|
-
if (
|
|
168
|
+
if (isMainModule(import.meta.url)) {
|
|
168
169
|
const key = process.env.FACILITATOR_SHARED_KEY ?? "";
|
|
169
170
|
const grpc = process.env.SUI_TESTNET_GRPC ?? "https://fullnode.testnet.sui.io:443";
|
|
170
171
|
const port = Number(process.env.PORT ?? "3000");
|
package/dist/funding.js
CHANGED
|
@@ -27,6 +27,8 @@ export async function prepareFunding(home, values) {
|
|
|
27
27
|
if (app.profile.role !== "buyer" || app.profile.mock)
|
|
28
28
|
fail("LIVE_PROFILE_REQUIRED", "funding requires a live buyer role home");
|
|
29
29
|
const doctor = await app.execute("doctor", {}), address = String(doctor.data.signer ?? "");
|
|
30
|
+
if (shieldedPayment > 0n && doctor.data.shieldedProofs !== "ready")
|
|
31
|
+
fail("SHIELDED_PROOFS_UNAVAILABLE", String(doctor.data.shieldedFixes?.[0] ?? "shielded proving artifacts are not installed"));
|
|
30
32
|
const endpoint = app.profile.live?.writeGrpcEndpoint ?? fail("LIVE_PROFILE_INVALID", "buyer write RPC is missing");
|
|
31
33
|
const [{ balance }, shielded] = await Promise.all([writeClient(endpoint).getBalance({ owner: address, coinType: "0x2::sui::SUI" }), app.execute("wallet.balance", {})]);
|
|
32
34
|
return calculateFundingPlan({ address, publicBalance: BigInt(balance.balance), shieldedBalance: BigInt(String(shielded.data.available ?? "0")), publicPayment, shieldedPayment, gasReserve, changeReserve });
|
|
@@ -42,7 +44,14 @@ export async function executeFunding(home, values) {
|
|
|
42
44
|
if (plan.faucetRequired) {
|
|
43
45
|
if (!values.requestFaucet)
|
|
44
46
|
fail("BUYER_FUNDING_REQUIRED", `buyer needs ${plan.publicShortfallMist} additional MIST; rerun with --request-faucet --confirm or fund ${plan.address} manually`);
|
|
45
|
-
|
|
47
|
+
try {
|
|
48
|
+
execFileSync("sui", ["client", "faucet", "--url", "https://faucet.testnet.sui.io", "--address", plan.address], { stdio: "inherit" });
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error.code === "ENOENT")
|
|
52
|
+
fail("SUI_CLI_MISSING", `the Sui CLI is not installed; request Testnet SUI for ${plan.address} at https://faucet.sui.io, then rerun funding execute without --request-faucet`);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
46
55
|
for (let attempt = 0; attempt < 15; attempt++) {
|
|
47
56
|
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
48
57
|
plan = await prepareFunding(home, values);
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./indexer.js";
|
|
|
15
15
|
export * from "./memory.js";
|
|
16
16
|
export * from "./settlement.js";
|
|
17
17
|
export * from "./seller-runtime.js";
|
|
18
|
+
export * from "./seller-research.js";
|
|
18
19
|
export * from "./seller-stdio.js";
|
|
19
20
|
export * from "./live-index.js";
|
|
20
21
|
export * from "./live-runtime.js";
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export * from "./indexer.js";
|
|
|
15
15
|
export * from "./memory.js";
|
|
16
16
|
export * from "./settlement.js";
|
|
17
17
|
export * from "./seller-runtime.js";
|
|
18
|
+
export * from "./seller-research.js";
|
|
18
19
|
export * from "./seller-stdio.js";
|
|
19
20
|
export * from "./live-index.js";
|
|
20
21
|
export * from "./live-runtime.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type SignedCapabilityRegistration } from "./capability.js";
|
|
2
|
-
import type { DealArtifact, DealNegotiation, DealPayment, DealPorts } from "./deal-engine.js";
|
|
2
|
+
import type { DealArtifact, DealNegotiation, DealPayment, DealPorts, SellerFault } from "./deal-engine.js";
|
|
3
3
|
import type { ExecuteDealInput } from "./deal.js";
|
|
4
4
|
import { type AgentMandate } from "./mandate.js";
|
|
5
5
|
import type { Phase3App } from "./phase3.js";
|
|
@@ -32,6 +32,7 @@ export declare class LiveDealPorts implements DealPorts {
|
|
|
32
32
|
negotiate(input: ExecuteDealInput, registration: SignedCapabilityRegistration, plan: SettlementPlanV1, agreementOperationId: string): Promise<LiveNegotiation>;
|
|
33
33
|
pay(input: ExecuteDealInput, plan: SettlementPlanV1, negotiation: DealNegotiation, paymentOperationId: string): Promise<DealPayment>;
|
|
34
34
|
deliver(input: ExecuteDealInput, plan: SettlementPlanV1, negotiation: DealNegotiation, payment: DealPayment, _deliveryOperationId: string): Promise<DealArtifact>;
|
|
35
|
+
rememberFailure(input: ExecuteDealInput, plan: SettlementPlanV1, fault: SellerFault): Promise<void>;
|
|
35
36
|
rememberOutcome(input: ExecuteDealInput, plan: SettlementPlanV1, payment: DealPayment, artifact: DealArtifact, _outcomeOperationId: string): Promise<{
|
|
36
37
|
queued: true;
|
|
37
38
|
payloadHash: string;
|
package/dist/live-deal-ports.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
5
5
|
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
6
|
-
import { verifySignedCapability } from "./capability.js";
|
|
6
|
+
import { verifySignedCapability, validateCapabilityPayload } from "./capability.js";
|
|
7
7
|
import { WalrusDealBlobs, wireDecrypt, wireEncrypt, wireHash, wireJsonHash, wireKey } from "./deal-wire.js";
|
|
8
8
|
import { fail } from "./errors.js";
|
|
9
9
|
import { LiveRuntime } from "./live-runtime.js";
|
|
@@ -64,7 +64,14 @@ export class LiveDealPorts {
|
|
|
64
64
|
return Ed25519Keypair.fromSecretKey(decoded.secretKey);
|
|
65
65
|
}
|
|
66
66
|
async request(origin, path, body) {
|
|
67
|
-
const
|
|
67
|
+
const url = new URL(path, origin);
|
|
68
|
+
let response;
|
|
69
|
+
try {
|
|
70
|
+
response = await this.http(url, { ...(body ? { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) } : {}), redirect: "error", signal: AbortSignal.timeout(90_000) });
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return fail("DEAL_SELLER_UNAVAILABLE", `seller ${url.origin} is unreachable; check its health endpoint and network route`, error);
|
|
74
|
+
}
|
|
68
75
|
let value;
|
|
69
76
|
try {
|
|
70
77
|
value = await response.json();
|
|
@@ -120,8 +127,21 @@ export class LiveDealPorts {
|
|
|
120
127
|
const candidates = (await this.catalog()).map(({ registration }) => registration).filter((registration) => (input.capabilityId && registration.manifest.capabilityId === input.capabilityId || input.capabilityClass && registration.manifest.capability === input.capabilityClass) && registration.manifest.asset === input.budget.asset);
|
|
121
128
|
if (!candidates.length)
|
|
122
129
|
fail("DEAL_CAPABILITY_NOT_FOUND", "configured sellers have no matching signed capability");
|
|
123
|
-
|
|
124
|
-
|
|
130
|
+
// Same capability class can carry different signed input schemas; only rank sellers that accept this task.
|
|
131
|
+
const accepting = candidates.filter((registration) => { try {
|
|
132
|
+
validateCapabilityPayload(registration.manifest, "input", input.input);
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
} });
|
|
138
|
+
if (!accepting.length)
|
|
139
|
+
fail("DEAL_INPUT_UNSUPPORTED", "no matching seller's signed input schema accepts this task input; check deal.catalog for the expected fields");
|
|
140
|
+
candidates.splice(0, candidates.length, ...accepting);
|
|
141
|
+
// Walrus Memory decides which seller gets paid: verified outcomes outrank a lower list price.
|
|
142
|
+
const ranking = await this.app.rankDealCandidates(input.operationId, candidates.map((item) => item.manifest), this.mandate, { budgetMaximum: input.budget.maximum, ...(input.requestedRail ? { requestedRail: input.requestedRail } : {}), ...(input.privacyHint ? { privacyHint: input.privacyHint } : {}) });
|
|
143
|
+
const chosen = ranking.ordered[0];
|
|
144
|
+
return candidates.find((item) => item.manifest.manifestHash === chosen.manifestHash);
|
|
125
145
|
}
|
|
126
146
|
async plan(input, registration, paymentOperationId) {
|
|
127
147
|
const existing = this.app.store.db.prepare("SELECT plan_json FROM settlement_plans WHERE operation_id=?").get(paymentOperationId);
|
|
@@ -130,10 +150,13 @@ export class LiveDealPorts {
|
|
|
130
150
|
validateSettlementPlan(saved);
|
|
131
151
|
if (saved.mandateHash !== settlementMandateHash(this.mandate) || saved.manifestHash !== registration.manifest.manifestHash || saved.operationId !== paymentOperationId)
|
|
132
152
|
fail("DEAL_PLAN_MISMATCH", "persisted plan differs from the owner mandate or seller capability");
|
|
153
|
+
this.app.recordDealMemoryInfluence(input.operationId, saved, this.mandate, { ...(input.requestedRail ? { requestedRail: input.requestedRail } : {}), ...(input.privacyHint ? { privacyHint: input.privacyHint } : {}) });
|
|
133
154
|
return saved;
|
|
134
155
|
}
|
|
135
156
|
const result = await executeMandated(this.app, this.mandate, "settlement.plan", { "operation-id": paymentOperationId, manifest: registration.manifest, "requested-amount": registration.manifest.priceBand.minimum, ...(input.privacyHint ? { "privacy-hint": input.privacyHint } : {}), ...(input.requestedRail ? { "requested-rail": input.requestedRail } : {}) });
|
|
136
|
-
|
|
157
|
+
const plan = result;
|
|
158
|
+
this.app.recordDealMemoryInfluence(input.operationId, plan, this.mandate, { ...(input.requestedRail ? { requestedRail: input.requestedRail } : {}), ...(input.privacyHint ? { privacyHint: input.privacyHint } : {}) });
|
|
159
|
+
return plan;
|
|
137
160
|
}
|
|
138
161
|
async negotiate(input, registration, plan, agreementOperationId) {
|
|
139
162
|
const sessionId = agreementOperationId, capabilityId = registration.manifest.capabilityId, buyerPublicKey = this.app.store.getSecret("identity:x25519-public")?.toString("base64url") ?? fail("IDENTITY_MISSING", "buyer transport key is missing");
|
|
@@ -202,6 +225,9 @@ export class LiveDealPorts {
|
|
|
202
225
|
fail("DEAL_DELIVERY_UNVERIFIED", "seller delivery bytes or metadata differ from the receipt");
|
|
203
226
|
return { blobId: receipt.blobId, ciphertextHash: receipt.ciphertextHash, artifactSha256: receipt.artifactSha256, contentType: receipt.contentType, metadata: receipt.metadata, artifactBase64: artifact.artifactBase64 };
|
|
204
227
|
}
|
|
228
|
+
async rememberFailure(input, plan, fault) {
|
|
229
|
+
this.app.recordVerifiedDealFailure(input.operationId, plan.planHash, fault);
|
|
230
|
+
}
|
|
205
231
|
async rememberOutcome(input, plan, payment, artifact, _outcomeOperationId) {
|
|
206
232
|
return this.app.recordVerifiedDealOutcome(input.operationId, plan.planHash, payment.transactionDigest, artifact.artifactSha256);
|
|
207
233
|
}
|
package/dist/live-runtime.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare class LiveRuntime {
|
|
|
47
47
|
readonly prover: Groth16Prover;
|
|
48
48
|
private constructor();
|
|
49
49
|
static create(profile: LiveRuntimeProfile, store: RoleStore): Promise<LiveRuntime>;
|
|
50
|
-
doctor(): Promise<Record<string, unknown>>;
|
|
50
|
+
doctor(proofsRequired?: boolean): Promise<Record<string, unknown>>;
|
|
51
51
|
balance(): Promise<Record<string, unknown>>;
|
|
52
52
|
private signed;
|
|
53
53
|
private operationContext;
|
package/dist/live-runtime.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { decodeSuiPrivateKey } from "@mysten/sui/cryptography";
|
|
3
3
|
import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
|
|
4
|
-
import { fail } from "./errors.js";
|
|
4
|
+
import { fail, SupercutsError } from "./errors.js";
|
|
5
5
|
import { assertSuiFinality, assertTatumFinality } from "./finality.js";
|
|
6
6
|
import { LivePoolIndex } from "./live-index.js";
|
|
7
7
|
import { ProtocolEngine } from "./protocol-engine.js";
|
|
@@ -89,8 +89,18 @@ export class LiveRuntime {
|
|
|
89
89
|
const engine = await ProtocolEngine.create({ deploymentDomain: profile.live.deploymentDomain, poolId: profile.poolId, coinTypeHash: profile.live.coinTypeHash, verifierVersion: profile.verifierVersion, initialRoot: profile.live.initialRoot });
|
|
90
90
|
return new LiveRuntime(profile, store, new TatumLedger(profile.live.tatumGrpcEndpoint, tatumKey), Ed25519Keypair.fromSecretKey(decoded.secretKey), engine);
|
|
91
91
|
}
|
|
92
|
-
async doctor() {
|
|
93
|
-
|
|
92
|
+
async doctor(proofsRequired = true) {
|
|
93
|
+
// Shielded proofs need separately installed artifacts; a public x402 buyer or seller stays usable without them.
|
|
94
|
+
let artifacts = { rapidSnark: false, artifacts: {} }, shieldedProofs = "ready", shieldedProofsReason;
|
|
95
|
+
try {
|
|
96
|
+
artifacts = this.prover.doctor();
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (!(error instanceof SupercutsError) || !["PROVING_ARTIFACT_MISSING", "ARTIFACT_HASH_MISMATCH", "PROVER_TOOL_MISSING"].includes(error.code))
|
|
100
|
+
throw error;
|
|
101
|
+
shieldedProofs = "unavailable";
|
|
102
|
+
shieldedProofsReason = error.code;
|
|
103
|
+
}
|
|
94
104
|
const [service, suiObject] = await Promise.all([this.tatum.getServiceInfo(), this.client.getObject({ objectId: this.profile.poolId, include: { json: true } })]);
|
|
95
105
|
if (!suiObject.object)
|
|
96
106
|
fail("POOL_OBJECT_MISSING", "pinned Testnet pool object is unavailable; check the Sui write RPC and deployment config");
|
|
@@ -98,9 +108,11 @@ export class LiveRuntime {
|
|
|
98
108
|
fail("TATUM_UNAVAILABLE", "Tatum read RPC did not return service information; check its endpoint and owner-only API key");
|
|
99
109
|
const babySeed = this.store.getSecret("identity:babyjub-seed") ?? fail("IDENTITY_MISSING", "BabyJub identity is missing");
|
|
100
110
|
const identity = this.engine.identity(babySeed);
|
|
101
|
-
const fixes = [];
|
|
102
|
-
if (
|
|
103
|
-
|
|
111
|
+
const fixes = [], shieldedFixes = [];
|
|
112
|
+
if (shieldedProofs === "unavailable")
|
|
113
|
+
shieldedFixes.push(`Shielded payments are unavailable (${shieldedProofsReason}): install the hash-pinned v2 proving artifacts, SnarkJS, and the converter. Public x402 payments do not need them.`);
|
|
114
|
+
else if (!artifacts.rapidSnark)
|
|
115
|
+
shieldedFixes.push("Set an executable RapidSnark path in the role profile for v2 proofs.");
|
|
104
116
|
if (!this.profile.live.walrusPublisherUrl)
|
|
105
117
|
fixes.push("Configure the Walrus publisher URL for capability and transcript writes.");
|
|
106
118
|
if (!this.profile.live.walrusAggregatorUrl)
|
|
@@ -117,9 +129,7 @@ export class LiveRuntime {
|
|
|
117
129
|
fixes.push(`Walrus ${name} is unreachable; check the configured URL and network access.`);
|
|
118
130
|
}
|
|
119
131
|
}
|
|
120
|
-
|
|
121
|
-
fixes.push("Configure an owner-only Walrus Memory file using the local 1536-dimensional embedding adapter.");
|
|
122
|
-
return { role: this.profile.role, network: "testnet", packageId: this.profile.packageId, poolId: this.profile.poolId, asset: this.profile.coinType, verifierVersion: this.profile.verifierVersion, keyPermissions: "owner-only", rpc: this.profile.live.writeGrpcEndpoint, healthy: true, readyForDemo: fixes.length === 0, fixes, runtime: "testnet-live", signer: this.signer.toSuiAddress(), babyJubPublicKey: identity.publicKey.map(String), x25519PublicKey: this.store.getSecret("identity:x25519-public")?.toString("base64"), rapidSnark: artifacts.rapidSnark, artifacts: artifacts.artifacts, poolObjectVerified: true, tatumServiceVerified: true };
|
|
132
|
+
return { role: this.profile.role, network: "testnet", packageId: this.profile.packageId, poolId: this.profile.poolId, asset: this.profile.coinType, verifierVersion: this.profile.verifierVersion, keyPermissions: "owner-only", rpc: this.profile.live.writeGrpcEndpoint, healthy: true, readyForDemo: fixes.length === 0 && (!proofsRequired || shieldedFixes.length === 0), fixes: [...fixes, ...(proofsRequired ? shieldedFixes : [])], publicFixes: fixes, shieldedProofs, shieldedFixes, readyForPublicRail: fixes.length === 0, runtime: "testnet-live", signer: this.signer.toSuiAddress(), babyJubPublicKey: identity.publicKey.map(String), x25519PublicKey: this.store.getSecret("identity:x25519-public")?.toString("base64"), memoryConfigured: Boolean(this.store.getSecret("live:memory-config")), proofsRequired, rapidSnark: artifacts.rapidSnark, artifacts: artifacts.artifacts, poolObjectVerified: true, tatumServiceVerified: true };
|
|
123
133
|
}
|
|
124
134
|
async balance() {
|
|
125
135
|
const state = await this.index.sync();
|
|
@@ -621,8 +631,14 @@ export class LiveRuntime {
|
|
|
621
631
|
const paymentPayload = { x402Version: 2, resource, accepted: requirements, payload: { signature: signed.signature, transaction: Buffer.from(signed.transaction).toString("base64") } };
|
|
622
632
|
const paid = await fetch(paymentUrl, { redirect: "error", headers: { "payment-signature": Buffer.from(JSON.stringify(paymentPayload)).toString("base64"), "x-supercuts-binding": Buffer.from(JSON.stringify({ ...unsignedBinding, payerSignature })).toString("base64") }, signal: AbortSignal.timeout(60_000) });
|
|
623
633
|
const response = Buffer.from(await paid.arrayBuffer());
|
|
624
|
-
if (!paid.ok)
|
|
625
|
-
|
|
634
|
+
if (!paid.ok) {
|
|
635
|
+
const detail = x402ResourceFailure(paid.status, response);
|
|
636
|
+
// Settlement status is unknown: the facilitator may have broadcast before failing. Resume reuses the journaled signed
|
|
637
|
+
// transaction for this operation ID, and Sui executes one digest at most once, so retrying cannot pay twice.
|
|
638
|
+
if (paid.status >= 502 && paid.status <= 504 || /X402_FACILITATOR_UNAVAILABLE/.test(detail))
|
|
639
|
+
fail("X402_FACILITATOR_UNAVAILABLE", `${detail}; settlement status is unknown and the payment may already be on chain. Resume with the same operation ID: the same signed transaction is reused and cannot be paid twice`);
|
|
640
|
+
fail("X402_DELIVERY_FAILED", detail);
|
|
641
|
+
}
|
|
626
642
|
const settlementHeader = paid.headers.get("payment-response") ?? fail("X402_SETTLEMENT_INVALID", "payment-response header is missing");
|
|
627
643
|
let settlement;
|
|
628
644
|
try {
|
|
@@ -97,6 +97,8 @@ export class LiveSellerTransport {
|
|
|
97
97
|
async handle(request, response) {
|
|
98
98
|
try {
|
|
99
99
|
const url = new URL(request.url ?? "/", this.config.origin);
|
|
100
|
+
if (request.method === "GET" && url.pathname === "/health")
|
|
101
|
+
return this.respond(response, 200, { ok: true, service: "supercuts-seller", capabilities: this.runtime.catalog().length });
|
|
100
102
|
if (request.method === "GET" && url.pathname === "/v2/catalog")
|
|
101
103
|
return this.respond(response, 200, { capabilities: this.runtime.catalog() });
|
|
102
104
|
if (request.method === "POST" && url.pathname === "/v2/prepare")
|
|
@@ -108,7 +110,9 @@ export class LiveSellerTransport {
|
|
|
108
110
|
if (url.pathname.startsWith(`${(resource.config.pathPrefix ?? "/capability").replace(/\/$/, "")}/`))
|
|
109
111
|
return resource.handle(request, response);
|
|
110
112
|
}
|
|
111
|
-
|
|
113
|
+
if (request.method === "GET" && [...this.#registrations.values()].some((item) => url.pathname === new URL(item.manifest.endpoint).pathname))
|
|
114
|
+
return this.respond(response, 404, { error: "NEGOTIATION_REQUIRED", message: "Protected resource URLs are issued for a negotiated session." });
|
|
115
|
+
return this.respond(response, 404, { error: "NOT_FOUND", message: "Seller route was not found." });
|
|
112
116
|
}
|
|
113
117
|
catch (error) {
|
|
114
118
|
const known = error instanceof SupercutsError ? error : new SupercutsError("SELLER_REQUEST_FAILED", "seller request failed");
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when this module is the process entry point. Package managers (pnpm in particular) launch bins
|
|
3
|
+
* through symlinks, so compare resolved real paths rather than raw `argv[1]` strings.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isMainModule(importMetaUrl: string, entry?: string | undefined): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
/**
|
|
4
|
+
* True when this module is the process entry point. Package managers (pnpm in particular) launch bins
|
|
5
|
+
* through symlinks, so compare resolved real paths rather than raw `argv[1]` strings.
|
|
6
|
+
*/
|
|
7
|
+
export function isMainModule(importMetaUrl, entry = process.argv[1]) {
|
|
8
|
+
if (!entry)
|
|
9
|
+
return false;
|
|
10
|
+
try {
|
|
11
|
+
return realpathSync(entry) === realpathSync(fileURLToPath(importMetaUrl));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
package/dist/mandate.js
CHANGED
|
@@ -125,7 +125,7 @@ export async function executeMandated(app, mandate, command, raw) {
|
|
|
125
125
|
fail("MANDATE_COMMAND_FORBIDDEN", `mandate does not allow ${command}`);
|
|
126
126
|
const args = {};
|
|
127
127
|
for (const [key, value] of Object.entries(raw))
|
|
128
|
-
args[key] = typeof value === "boolean" ? value : typeof value === "string" ? value : Buffer.from(
|
|
128
|
+
args[key] = typeof value === "boolean" ? value : typeof value === "string" ? value : Buffer.from(canonical(value)).toString("base64url");
|
|
129
129
|
if (!spec.mutating)
|
|
130
130
|
return (await app.execute(command, args)).data;
|
|
131
131
|
const operationId = String(raw["operation-id"]), journalOperationId = command === "settlement.plan" ? `settlement.plan:${operationId}` : operationId;
|
package/dist/mcp-bootstrap.js
CHANGED
|
@@ -37,17 +37,17 @@ else {
|
|
|
37
37
|
return;
|
|
38
38
|
if (request.method === "tools/list")
|
|
39
39
|
return send({ jsonrpc: "2.0", id: request.id, result: { tools: [
|
|
40
|
-
{ name: "setup.status", description: "Explain how the owner completes one-time buyer setup locally. Do not request secrets in chat.", inputSchema: { type: "object", properties: {}, additionalProperties: false } },
|
|
40
|
+
{ name: "setup.status", description: "Explain how the owner completes one-time buyer or seller setup locally. Do not request secrets in chat.", inputSchema: { type: "object", properties: {}, additionalProperties: false } },
|
|
41
41
|
{ name: "setup.refresh", description: "Reload this MCP connection after the owner completes local setup.", inputSchema: { type: "object", properties: {}, additionalProperties: false } },
|
|
42
42
|
] } });
|
|
43
43
|
if (request.method === "tools/call" && request.params?.name === "setup.status") {
|
|
44
44
|
const configured = existsSync(join(home, "profile.json"));
|
|
45
|
-
const result = { schemaVersion: "supercuts.setup.v1", ok: true, configured, home, nextStep: configured ? "Call setup.refresh to load the configured
|
|
45
|
+
const result = { schemaVersion: "supercuts.setup.v1", ok: true, configured, home, nextStep: configured ? "Call setup.refresh to load the configured role tools without restarting the connector." : `Run supercuts onboard buyer --home ${JSON.stringify(home)} or supercuts onboard seller --home ${JSON.stringify(home)} in a terminal, then call setup.refresh. Use a separate owner-only home for every role and identity. Do not paste keys or tokens into Codex.` };
|
|
46
46
|
return send({ jsonrpc: "2.0", id: request.id, result: { content: [{ type: "text", text: JSON.stringify(result) }], structuredContent: result } });
|
|
47
47
|
}
|
|
48
48
|
if (request.method === "tools/call" && request.params?.name === "setup.refresh") {
|
|
49
49
|
if (!existsSync(join(home, "profile.json"))) {
|
|
50
|
-
const result = { schemaVersion: "supercuts.setup.v1", ok: false, error: { code: "SETUP_REQUIRED", message: "
|
|
50
|
+
const result = { schemaVersion: "supercuts.setup.v1", ok: false, error: { code: "SETUP_REQUIRED", message: "role setup is not complete", recovery: `Run supercuts onboard buyer --home ${JSON.stringify(home)} or supercuts onboard seller --home ${JSON.stringify(home)} locally, then retry setup.refresh.` } };
|
|
51
51
|
return send({ jsonrpc: "2.0", id: request.id, result: { isError: true, content: [{ type: "text", text: JSON.stringify(result) }], structuredContent: result } });
|
|
52
52
|
}
|
|
53
53
|
const result = { schemaVersion: "supercuts.setup.v1", ok: true, configured: true, reloading: true };
|
package/dist/mcp.js
CHANGED
|
@@ -98,6 +98,8 @@ function logTool(name, data) {
|
|
|
98
98
|
const plan = journal.step(String(data.operationId), "planning");
|
|
99
99
|
if (plan)
|
|
100
100
|
append("supercuts", "settlement-plan", { phase: "after-memory", planHash: plan.planHash, capabilityId: plan.capabilityId, rail: plan.rail, openingAmount: plan.openingAmount, maximumAmount: plan.maximumAmount, riskLevel: plan.riskLevel, reasonCodes: plan.reasonCodes, blobIds: plan.citations.map((item) => item.blobId) });
|
|
101
|
+
if (plan && evidence.memoryInfluence)
|
|
102
|
+
append("supercuts", "memory-influence", { planHash: plan.planHash, changedSeller: evidence.memoryInfluence.changedSeller, changedRail: evidence.memoryInfluence.changedRail, changedRisk: evidence.memoryInfluence.changedRisk, changedMaximum: evidence.memoryInfluence.changedMaximum, withoutMemory: evidence.memoryInfluence.withoutMemory, withMemory: evidence.memoryInfluence.withMemory, scorecards: evidence.memoryRanking, blobIds: evidence.memoryInfluence.citedBlobIds });
|
|
101
103
|
append("supercuts", "deal-lifecycle", { dealId: data.dealId, state: data.state, rail: data.rail, agreement: data.evidence?.agreement, payment: data.evidence?.payment, delivery: data.evidence?.delivery, outcomeMemory: data.evidence?.outcomeMemory, agreementBlobId: evidence.agreementBlobId, deliveryBlobId: evidence.deliveryBlobId, outcomeMemoryBlobId: evidence.outcomeMemoryBlobId, paymentDigest: evidence.paymentDigest });
|
|
102
104
|
}
|
|
103
105
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { RecalledSettlementMemory, SettlementMemoryScope, SettlementMemoryV1 } from "./settlement.js";
|
|
2
|
+
/** Learned view of one counterparty, derived only from trusted Walrus Memory outcomes. */
|
|
3
|
+
export interface SellerScorecard {
|
|
4
|
+
counterparty: string;
|
|
5
|
+
observations: number;
|
|
6
|
+
successes: number;
|
|
7
|
+
failures: number;
|
|
8
|
+
unresolved: number;
|
|
9
|
+
/** Recency-weighted Beta(1,1) posterior mean; 0.5 when nothing has been learned. */
|
|
10
|
+
reliability: number;
|
|
11
|
+
lastOutcome?: "succeeded" | "failed" | "unresolved";
|
|
12
|
+
lastOutcomeAt?: number;
|
|
13
|
+
medianLatencyMs?: number;
|
|
14
|
+
citedBlobIds: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare const DEFAULT_HALF_LIFE_MS: number;
|
|
17
|
+
/** True when every field the filter names matches the memory scope. */
|
|
18
|
+
export declare function scopeWithin(scope: SettlementMemoryScope, filter: SettlementMemoryScope): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Trusted deal-level outcome memories for the filter, deduplicated by evidence (one deal may be recalled twice).
|
|
21
|
+
* Only completed deals and verified seller faults carry `signal.rail`; stage events (agreement, settlement,
|
|
22
|
+
* delivery) stay as evidence but are not counted as additional deals.
|
|
23
|
+
*/
|
|
24
|
+
export declare function trustedOutcomes(recalled: readonly RecalledSettlementMemory[], filter: SettlementMemoryScope): Array<{
|
|
25
|
+
blobId: string;
|
|
26
|
+
memory: SettlementMemoryV1;
|
|
27
|
+
}>;
|
|
28
|
+
export declare function buildScorecard(recalled: readonly RecalledSettlementMemory[], filter: SettlementMemoryScope & {
|
|
29
|
+
counterparty: string;
|
|
30
|
+
}, now: number, halfLifeMs?: number): SellerScorecard;
|
|
31
|
+
/** Sum of verified paid amounts since `since` within the filter scope; a paid-but-failed deal still spent money. */
|
|
32
|
+
export declare function spentSince(recalled: readonly RecalledSettlementMemory[], filter: SettlementMemoryScope, since: number): bigint;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const DEFAULT_HALF_LIFE_MS = 7 * 86_400_000;
|
|
2
|
+
const TRUSTED = new Set(["owner_policy", "verified_outcome"]);
|
|
3
|
+
/** True when every field the filter names matches the memory scope. */
|
|
4
|
+
export function scopeWithin(scope, filter) {
|
|
5
|
+
return Object.entries(filter).every(([key, value]) => value === undefined || scope[key] === value);
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Trusted deal-level outcome memories for the filter, deduplicated by evidence (one deal may be recalled twice).
|
|
9
|
+
* Only completed deals and verified seller faults carry `signal.rail`; stage events (agreement, settlement,
|
|
10
|
+
* delivery) stay as evidence but are not counted as additional deals.
|
|
11
|
+
*/
|
|
12
|
+
export function trustedOutcomes(recalled, filter) {
|
|
13
|
+
const seen = new Set(), result = [];
|
|
14
|
+
for (const item of recalled) {
|
|
15
|
+
const memory = item.memory;
|
|
16
|
+
if (!memory ||
|
|
17
|
+
memory.kind !== "outcome" ||
|
|
18
|
+
memory.signal.rail === undefined ||
|
|
19
|
+
!TRUSTED.has(memory.provenance) ||
|
|
20
|
+
!memory.signal.outcome ||
|
|
21
|
+
!scopeWithin(memory.scope, filter))
|
|
22
|
+
continue;
|
|
23
|
+
const key = memory.evidenceHash ?? item.blobId;
|
|
24
|
+
if (seen.has(key))
|
|
25
|
+
continue;
|
|
26
|
+
seen.add(key);
|
|
27
|
+
result.push({ blobId: item.blobId, memory });
|
|
28
|
+
}
|
|
29
|
+
return result.sort((a, b) => a.memory.createdAt - b.memory.createdAt ||
|
|
30
|
+
a.blobId.localeCompare(b.blobId));
|
|
31
|
+
}
|
|
32
|
+
export function buildScorecard(recalled, filter, now, halfLifeMs = DEFAULT_HALF_LIFE_MS) {
|
|
33
|
+
const outcomes = trustedOutcomes(recalled, filter);
|
|
34
|
+
let alpha = 1, beta = 1, successes = 0, failures = 0, unresolved = 0;
|
|
35
|
+
const latencies = [];
|
|
36
|
+
for (const { memory } of outcomes) {
|
|
37
|
+
const weight = Math.pow(0.5, Math.max(0, now - memory.createdAt) / halfLifeMs);
|
|
38
|
+
if (memory.signal.outcome === "succeeded") {
|
|
39
|
+
successes++;
|
|
40
|
+
alpha += weight;
|
|
41
|
+
}
|
|
42
|
+
else if (memory.signal.outcome === "failed") {
|
|
43
|
+
failures++;
|
|
44
|
+
beta += weight;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
unresolved++;
|
|
48
|
+
beta += weight / 2;
|
|
49
|
+
}
|
|
50
|
+
if (memory.signal.latencyMs !== undefined)
|
|
51
|
+
latencies.push(memory.signal.latencyMs);
|
|
52
|
+
}
|
|
53
|
+
latencies.sort((a, b) => a - b);
|
|
54
|
+
const last = outcomes.at(-1)?.memory;
|
|
55
|
+
return {
|
|
56
|
+
counterparty: filter.counterparty,
|
|
57
|
+
observations: outcomes.length,
|
|
58
|
+
successes,
|
|
59
|
+
failures,
|
|
60
|
+
unresolved,
|
|
61
|
+
reliability: alpha / (alpha + beta),
|
|
62
|
+
...(last
|
|
63
|
+
? { lastOutcome: last.signal.outcome, lastOutcomeAt: last.createdAt }
|
|
64
|
+
: {}),
|
|
65
|
+
...(latencies.length
|
|
66
|
+
? { medianLatencyMs: latencies[Math.floor((latencies.length - 1) / 2)] }
|
|
67
|
+
: {}),
|
|
68
|
+
citedBlobIds: outcomes.map((item) => item.blobId),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Sum of verified paid amounts since `since` within the filter scope; a paid-but-failed deal still spent money. */
|
|
72
|
+
export function spentSince(recalled, filter, since) {
|
|
73
|
+
return trustedOutcomes(recalled, filter)
|
|
74
|
+
.filter(({ memory }) => memory.signal.amount !== undefined && memory.createdAt >= since)
|
|
75
|
+
.reduce((total, { memory }) => total + BigInt(memory.signal.amount), 0n);
|
|
76
|
+
}
|
package/dist/memwal.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { MemoryFact } from "./memory.js";
|
|
2
2
|
import { type SettlementMemoryV1 } from "./settlement.js";
|
|
3
|
+
export declare const SIGNED_MEMORY_FORMAT: "supercuts.signed-memory.v1";
|
|
4
|
+
/** Sui address that signs this owner's trusted memories; derived from the owner-only memory key. */
|
|
5
|
+
export declare function memorySignerAddress(secret: string): string;
|
|
6
|
+
/** Plain-language line placed before the JSON so embeddings rank the record near scope queries. Not signed. */
|
|
7
|
+
export declare function describeMemory(memory: SettlementMemoryV1): string;
|
|
8
|
+
/** Wraps a trusted memory in a signed envelope so its authority survives a move to another machine. */
|
|
9
|
+
export declare function sealMemoryText(memory: SettlementMemoryV1, secret: string): string;
|
|
10
|
+
/** Opens recalled memory text. `signedBy` is set only when the envelope signature verifies. */
|
|
11
|
+
export declare function openMemoryText(text: string): {
|
|
12
|
+
memory: unknown;
|
|
13
|
+
signedBy?: string;
|
|
14
|
+
};
|
|
3
15
|
import { RoleStore } from "./store.js";
|
|
4
16
|
export interface ManualMemoryConfig {
|
|
5
17
|
key: string;
|
|
@@ -15,10 +27,26 @@ export interface ManualMemoryConfig {
|
|
|
15
27
|
walrusAggregatorUrl: string;
|
|
16
28
|
walrusPublisherUrl: string;
|
|
17
29
|
}
|
|
30
|
+
/** Public Walrus Mainnet Memory and local-adapter values; owners only need to supply their own account secrets. */
|
|
31
|
+
export declare const MEMORY_MAINNET_DEFAULTS: {
|
|
32
|
+
readonly serverUrl: "https://relayer.memory.walrus.xyz";
|
|
33
|
+
readonly packageId: "0xe7c16fbea0560e7057e2bf7422feaa4fb313749fc69c9e9092fac7a33b81d7f5";
|
|
34
|
+
readonly registryId: "0x8bf82c9e09e36b8d1c38298f68b7cb68e7b8762887e7592add9986d5e9cf199f";
|
|
35
|
+
readonly embeddingApiKey: "local-adapter";
|
|
36
|
+
readonly embeddingApiBase: "http://127.0.0.1:8787/v1";
|
|
37
|
+
readonly embeddingModel: "embeddinggemma-1536";
|
|
38
|
+
readonly walrusAggregatorUrl: "https://aggregator.walrus-mainnet.walrus.space";
|
|
39
|
+
readonly walrusPublisherUrl: "https://publisher.walrus-testnet.walrus.space";
|
|
40
|
+
};
|
|
41
|
+
/** Completes an owner memory config: `key`, `accountId`, and `suiPrivateKey` are required; the rest default. */
|
|
42
|
+
export declare function withMemoryDefaults(raw: Partial<ManualMemoryConfig>, role: string): ManualMemoryConfig;
|
|
18
43
|
/** Client-managed memory facade; plaintext never crosses the relayer boundary. */
|
|
19
44
|
export declare class ManualMemory {
|
|
20
45
|
private readonly store;
|
|
21
46
|
private readonly client;
|
|
47
|
+
private readonly signingKey;
|
|
48
|
+
/** Address whose signature marks a recalled memory as this owner's own trusted record. */
|
|
49
|
+
readonly signerAddress: string | undefined;
|
|
22
50
|
constructor(store: RoleStore, config: ManualMemoryConfig);
|
|
23
51
|
remember(fact: MemoryFact | SettlementMemoryV1): Promise<string>;
|
|
24
52
|
recall(query: string, limit?: number): Promise<Array<{
|