@brainai/satp-client 2.0.5 → 2.0.6
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 +37 -8
- package/examples/adoption-quickstarts.js +173 -0
- package/examples/runtime-policy-adapter.js +13 -1
- package/examples/x402-reputation-evidence-lookup-client.js +182 -0
- package/package.json +2 -1
- package/src/borsh-reader.d.ts +3 -1
- package/src/borsh-reader.js +23 -4
- package/src/constants.js +27 -4
- package/src/index.d.ts +1 -0
- package/src/index.js +8 -70
- package/src/runtime-policy-adapter.js +7 -6
- package/src/v3-sdk.d.ts +3 -1
- package/src/v3-sdk.js +2 -70
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**Solana Agent Trust Protocol** - JavaScript/TypeScript SDK for reading and
|
|
4
4
|
building reviewed SATP V3 program interactions.
|
|
5
5
|
|
|
6
|
-
Current stable npm package: **@brainai/satp-client@2.0.
|
|
6
|
+
Current stable npm package: **@brainai/satp-client@2.0.5** | unpublished source candidate: **@brainai/satp-client@2.0.6** | rc dist-tag: **@brainai/satp-client@2.0.2** | Programs: **6**
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -11,20 +11,25 @@ Choose stable, rc, or Git based on what the consumer needs to prove:
|
|
|
11
11
|
|
|
12
12
|
| Channel | Use when | Command |
|
|
13
13
|
| --- | --- | --- |
|
|
14
|
-
| Stable npm | Default production-style consumption of the stable public package. | `npm install @brainai/satp-client@2.0.
|
|
14
|
+
| Stable npm | Default production-style consumption of the stable public package. | `npm install @brainai/satp-client@2.0.5` |
|
|
15
15
|
| Historical rc exact version | Explicit HQ-assigned reproduction or lockfile evidence for the historical pre-stable artifact. | `npm install @brainai/satp-client@2.0.2` |
|
|
16
16
|
| Release candidate tag | Explicit HQ-assigned rc validation where a moving dist-tag is acceptable and the task names the tag as the target. | `npm install @brainai/satp-client@rc` |
|
|
17
17
|
| Reviewed Git commit | PR coordination or source-review installs tied to an exact SATP commit. | `npm install git+https://github.com/brainAI-bot/satp.git#<SATP_COMMIT>` |
|
|
18
18
|
|
|
19
|
-
Registry readback on 2026-07-
|
|
20
|
-
`@brainai/satp-client@2.0.
|
|
21
|
-
Stable consumers should use `latest`/`2.0.
|
|
19
|
+
Registry readback on 2026-07-31 shows npm `latest` resolves to
|
|
20
|
+
`@brainai/satp-client@2.0.5` and the `rc` dist-tag resolves to `2.0.2`.
|
|
21
|
+
Stable consumers should use `latest`/`2.0.5` unless HQ assigns an explicit
|
|
22
22
|
release-candidate validation task.
|
|
23
23
|
|
|
24
|
+
Source release-prep on 2026-08-02 advances package metadata to `2.0.6` as the
|
|
25
|
+
next unpublished stable semver candidate because npm `latest` is already
|
|
26
|
+
`2.0.5` and npm package versions are immutable. This does not publish the
|
|
27
|
+
package or move any npm dist-tag.
|
|
28
|
+
|
|
24
29
|
For stable consumer installs, pin the current published npm package:
|
|
25
30
|
|
|
26
31
|
```bash
|
|
27
|
-
npm install @brainai/satp-client@2.0.
|
|
32
|
+
npm install @brainai/satp-client@2.0.5
|
|
28
33
|
```
|
|
29
34
|
|
|
30
35
|
For rc validation:
|
|
@@ -56,6 +61,10 @@ a separate HQ approval and the relevant owner-gated runbook. See
|
|
|
56
61
|
`docs/mainnet-authority-decision-packet-6c8a5545.md` for the public authority
|
|
57
62
|
decision packet.
|
|
58
63
|
|
|
64
|
+
Legacy `SATPSDK` is V2-only compatibility. It defaults to devnet and rejects
|
|
65
|
+
`network: 'mainnet'` unless `allowLegacyV2Mainnet: true` is passed explicitly.
|
|
66
|
+
New integrations should prefer `SATPV3SDK` or `createSATPClient`.
|
|
67
|
+
|
|
59
68
|
RC-S6 semantic uncertainty outcomes are covered by the offline conformance gate
|
|
60
69
|
merged in `93db1b3` (PR #53, `[#43394290]`) and runnable with
|
|
61
70
|
`npm run test:conformance:rc-s6` from the repository root. Treat positive
|
|
@@ -73,6 +82,13 @@ For the root consumer quickstart covering stable npm, rc validation, Git-review
|
|
|
73
82
|
pins, read-only trust packets, conformance checks, and network boundaries, see
|
|
74
83
|
[`docs/quickstart.md`](../../docs/quickstart.md).
|
|
75
84
|
|
|
85
|
+
For copy-paste MCP, A2A/agent-runtime, and x402 paid-endpoint examples, see
|
|
86
|
+
[`docs/adoption-quickstarts.md`](../../docs/adoption-quickstarts.md) or run:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node packages/satp-client/examples/adoption-quickstarts.js
|
|
90
|
+
```
|
|
91
|
+
|
|
76
92
|
```javascript
|
|
77
93
|
const { SATPV3SDK } = require('@brainai/satp-client');
|
|
78
94
|
|
|
@@ -116,14 +132,14 @@ const tx = await sdk.buildCreateIdentity(creatorPubkey, 'myAgent', {
|
|
|
116
132
|
|
|
117
133
|
## Programs & Program IDs
|
|
118
134
|
|
|
119
|
-
| Program |
|
|
135
|
+
| Program | Mainnet | Description |
|
|
120
136
|
|---------|--------|-------------|
|
|
121
137
|
| `identity_v3` | `GTppU4E44BqXTQgbqMZ68ozFzhP1TLty3EGnzzjtNZfG` | Agent identity, names, wallets, face/birth |
|
|
122
138
|
| `reviews_v3` | `r9XX4frcqxxAZ6Au9V5PA3EAxs1zoNckqLLmoSRcNr4` | Peer reviews with 1-5 star ratings |
|
|
123
139
|
| `attestations_v3` | `6Xd1dAQJPvQRJ4Ntr6LtPTjDjPUZ8nfnmYLZaZ2DtrdD` | Third-party attestations & proofs |
|
|
124
140
|
| `reputation_v3` | `2Lz7KzMvKdrGeAuS8WPHu7jK2yScrnKVgacpYVEuDjkJ` | Weighted reputation scoring (CPI → identity) |
|
|
125
141
|
| `validation_v3` | `6rYRiCYidJYV7QvKrzKGgNu4oMh6BAvynked69R7xMbV` | Validation level computation (CPI → identity) |
|
|
126
|
-
| `escrow_v3` | `
|
|
142
|
+
| `escrow_v3` | `HXCUWKR2NvRcZ7rNAJHwPcH6QAAWaLR4bRFbfyuDND6C` | SOL escrow for agent jobs |
|
|
127
143
|
|
|
128
144
|
## API Reference
|
|
129
145
|
|
|
@@ -183,6 +199,15 @@ builds a runtime policy action descriptor. It asserts
|
|
|
183
199
|
`paymentAuthorization: false`, `actionAuthorization: false`,
|
|
184
200
|
`spendAuthorized: false`, and `livePaymentRequired: false`.
|
|
185
201
|
|
|
202
|
+
The proposed issue #14 Track C endpoint spec for third-party SATP reputation
|
|
203
|
+
and evidence consumers lives in
|
|
204
|
+
[`docs/x402-reputation-evidence-lookup-api.md`](../../docs/x402-reputation-evidence-lookup-api.md).
|
|
205
|
+
Its companion offline caller example is runnable with:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
node packages/satp-client/examples/x402-reputation-evidence-lookup-client.js
|
|
209
|
+
```
|
|
210
|
+
|
|
186
211
|
### Runtime Policy Adapter Helper
|
|
187
212
|
|
|
188
213
|
`createRuntimePolicyAdapter(opts)` provides a host-oriented wrapper around the
|
|
@@ -541,6 +566,10 @@ const genesis = deserializeGenesisRecord(acct.data);
|
|
|
541
566
|
console.log(genesis.agentName, genesis.reputationScore, genesis.isBorn);
|
|
542
567
|
```
|
|
543
568
|
|
|
569
|
+
Genesis parsing is layout-aware for RC-S6 review. Parsed records include
|
|
570
|
+
`layout` and `hasIsActiveField`; `isActive` is `null` when the account bytes do
|
|
571
|
+
not carry the historical `is_active` field.
|
|
572
|
+
|
|
544
573
|
### Usage: Auto-detect Account Type
|
|
545
574
|
|
|
546
575
|
```js
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const {
|
|
5
|
+
buildRuntimePolicyActionDescriptorFromX402Discovery,
|
|
6
|
+
buildSatpTrustPacket,
|
|
7
|
+
createRuntimePolicyAdapter,
|
|
8
|
+
getV3ProgramIds,
|
|
9
|
+
validateSatpTrustPacket,
|
|
10
|
+
} = require('../src');
|
|
11
|
+
|
|
12
|
+
const METADATA_HASH = '93d122f8879fe87c186c10a00db8fbc80a73cecd2ede44b9ffa6410be3c2b805';
|
|
13
|
+
|
|
14
|
+
async function mcpToolServerQuickstart() {
|
|
15
|
+
function createSatpMcpTools() {
|
|
16
|
+
return {
|
|
17
|
+
'satp.getPrograms': async ({ network = 'devnet' } = {}) => ({
|
|
18
|
+
network,
|
|
19
|
+
programs: getV3ProgramIds(network),
|
|
20
|
+
guardrails: {
|
|
21
|
+
readOnly: true,
|
|
22
|
+
writesSolanaState: false,
|
|
23
|
+
usesKeypairs: false,
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
|
|
27
|
+
'satp.prepareAttestationRequest': async ({
|
|
28
|
+
subjectWallet,
|
|
29
|
+
agentId,
|
|
30
|
+
claimType = 'identity',
|
|
31
|
+
metadataHash,
|
|
32
|
+
network = 'devnet',
|
|
33
|
+
}) => {
|
|
34
|
+
const packet = buildSatpTrustPacket({
|
|
35
|
+
subjectWallet,
|
|
36
|
+
agentId,
|
|
37
|
+
claimType,
|
|
38
|
+
metadataHash,
|
|
39
|
+
network,
|
|
40
|
+
});
|
|
41
|
+
const validation = validateSatpTrustPacket(packet);
|
|
42
|
+
if (!validation.ok) throw new Error(validation.errors.join('; '));
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
packet,
|
|
46
|
+
guardrails: {
|
|
47
|
+
readOnly: true,
|
|
48
|
+
livePaymentRequired: false,
|
|
49
|
+
transaction: null,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const tools = createSatpMcpTools();
|
|
57
|
+
const prepared = await tools['satp.prepareAttestationRequest']({
|
|
58
|
+
subjectWallet: '11111111111111111111111111111111',
|
|
59
|
+
agentId: 'example-mcp-agent',
|
|
60
|
+
metadataHash: METADATA_HASH,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
audience: 'mcp-tool-server-builders',
|
|
64
|
+
mode: prepared.packet.mode,
|
|
65
|
+
guardrails: prepared.guardrails,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function agentRuntimeQuickstart() {
|
|
70
|
+
const now = new Date().toISOString();
|
|
71
|
+
const adapter = createRuntimePolicyAdapter({
|
|
72
|
+
policy: {
|
|
73
|
+
minimumTrustScore: 70,
|
|
74
|
+
denyTrustScoreBelow: 25,
|
|
75
|
+
requireVerifiedIdentity: true,
|
|
76
|
+
maxAutoSpendUsd: 0,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
const identity = {
|
|
80
|
+
agentId: 'partner-runtime-agent',
|
|
81
|
+
active: true,
|
|
82
|
+
satpVerified: true,
|
|
83
|
+
trustScore: 82,
|
|
84
|
+
capabilities: ['a2a:task.delegate'],
|
|
85
|
+
evidenceUpdatedAt: now,
|
|
86
|
+
};
|
|
87
|
+
const action = adapter.action({
|
|
88
|
+
type: 'a2a_agent_runtime',
|
|
89
|
+
resource: 'a2a://task/delegate',
|
|
90
|
+
operation: 'delegate',
|
|
91
|
+
capability: 'a2a:task.delegate',
|
|
92
|
+
requiresFreshEvidence: true,
|
|
93
|
+
operatorApprovalRequired: false,
|
|
94
|
+
});
|
|
95
|
+
const result = adapter.evaluate(identity, action, { now });
|
|
96
|
+
const auditTrace = adapter.auditTrace(identity, action, { result, now });
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
audience: 'a2a-agent-runtime-builders',
|
|
100
|
+
decision: result.decision,
|
|
101
|
+
reasonCodes: result.reasonCodes,
|
|
102
|
+
localDecisionOnly: auditTrace.guardrails.localDecisionOnly,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function x402PaidEndpointQuickstart() {
|
|
107
|
+
const discovery = {
|
|
108
|
+
endpoint: 'https://api.example.test/satp/reputation',
|
|
109
|
+
action: 'lookup',
|
|
110
|
+
accepts: [{
|
|
111
|
+
scheme: 'exact',
|
|
112
|
+
network: 'solana-mainnet',
|
|
113
|
+
asset: 'USDC',
|
|
114
|
+
maxAmountRequired: '10000',
|
|
115
|
+
resource: 'https://api.example.test/satp/reputation',
|
|
116
|
+
description: 'SATP-backed reputation evidence lookup',
|
|
117
|
+
}],
|
|
118
|
+
};
|
|
119
|
+
const lookupAction = buildRuntimePolicyActionDescriptorFromX402Discovery(
|
|
120
|
+
discovery,
|
|
121
|
+
{ maxCostUsd: 0.01 }
|
|
122
|
+
);
|
|
123
|
+
const adapter = createRuntimePolicyAdapter({
|
|
124
|
+
policy: { maxAutoSpendUsd: 0, minimumTrustScore: 70 },
|
|
125
|
+
});
|
|
126
|
+
const identity = {
|
|
127
|
+
agentId: 'x402-consumer-agent',
|
|
128
|
+
active: true,
|
|
129
|
+
satpVerified: true,
|
|
130
|
+
trustScore: 90,
|
|
131
|
+
capabilities: ['satp:evidence.lookup'],
|
|
132
|
+
evidenceUpdatedAt: '2026-05-21T00:00:00Z',
|
|
133
|
+
};
|
|
134
|
+
const result = adapter.evaluate(identity, lookupAction, {
|
|
135
|
+
evidenceLookupPaymentPreapproved: false,
|
|
136
|
+
now: '2026-07-31T00:00:00Z',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
audience: 'x402-paid-endpoint-builders',
|
|
141
|
+
decision: result.decision,
|
|
142
|
+
reasonCodes: result.reasonCodes,
|
|
143
|
+
guardrail: lookupAction.evidenceLookup.guardrail,
|
|
144
|
+
paymentBoundary: {
|
|
145
|
+
satpPackageUseIsFreeOpen: true,
|
|
146
|
+
x402OnlyForHostedReputationEvidenceLookup: true,
|
|
147
|
+
paymentIsNotActionAuthorization: true,
|
|
148
|
+
livePaymentRequired: false,
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function run() {
|
|
154
|
+
const examples = [
|
|
155
|
+
await mcpToolServerQuickstart(),
|
|
156
|
+
agentRuntimeQuickstart(),
|
|
157
|
+
x402PaidEndpointQuickstart(),
|
|
158
|
+
];
|
|
159
|
+
console.log(JSON.stringify({ examples }, null, 2));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (require.main === module) {
|
|
163
|
+
run().catch((err) => {
|
|
164
|
+
console.error(err);
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
module.exports = {
|
|
170
|
+
agentRuntimeQuickstart,
|
|
171
|
+
mcpToolServerQuickstart,
|
|
172
|
+
x402PaidEndpointQuickstart,
|
|
173
|
+
};
|
|
@@ -44,7 +44,7 @@ const examples = [
|
|
|
44
44
|
},
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
|
-
name: '
|
|
47
|
+
name: 'AgentFolio trust-score degrade',
|
|
48
48
|
identity: { ...trustedIdentity, trustScore: 62 },
|
|
49
49
|
action: buildRuntimePolicyActionDescriptor({
|
|
50
50
|
type: 'agentfolio_trust_gate',
|
|
@@ -55,6 +55,18 @@ const examples = [
|
|
|
55
55
|
now: '2026-05-21T00:00:00Z',
|
|
56
56
|
},
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
name: 'Host trust-score gate alias',
|
|
60
|
+
identity: { ...trustedIdentity, trustScore: 74 },
|
|
61
|
+
action: buildRuntimePolicyActionDescriptor({
|
|
62
|
+
type: 'host_trust_gate',
|
|
63
|
+
profileId: 'brainchain-demo',
|
|
64
|
+
minimumTrustScore: 80,
|
|
65
|
+
}),
|
|
66
|
+
options: {
|
|
67
|
+
now: '2026-05-21T00:00:00Z',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
58
70
|
{
|
|
59
71
|
name: 'Optional paid x402 evidence lookup',
|
|
60
72
|
identity: { ...trustedIdentity, evidenceUpdatedAt: '2026-04-01T00:00:00Z' },
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
const {
|
|
6
|
+
X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION,
|
|
7
|
+
buildRuntimePolicyActionDescriptorFromX402Discovery,
|
|
8
|
+
} = require('../src');
|
|
9
|
+
|
|
10
|
+
const LOOKUP_SCHEMA_VERSION = 'satp.x402ReputationEvidenceLookup.v1';
|
|
11
|
+
|
|
12
|
+
const discoveryMetadata = {
|
|
13
|
+
schemaVersion: 'satp.x402DiscoveryMetadata.v1',
|
|
14
|
+
protocol: 'x402',
|
|
15
|
+
action: 'satp.reputationEvidence.lookup',
|
|
16
|
+
endpoint: 'https://satp-provider.example/v1/satp/evidence/reputation',
|
|
17
|
+
resource: 'satp://evidence/reputation',
|
|
18
|
+
paymentRequired: true,
|
|
19
|
+
accepts: [
|
|
20
|
+
{
|
|
21
|
+
scheme: 'exact',
|
|
22
|
+
network: '<provider-selected-payment-network>',
|
|
23
|
+
asset: '<provider-selected-asset>',
|
|
24
|
+
payTo: '<provider-controlled-recipient>',
|
|
25
|
+
maxAmountRequired: '<provider-disclosed-maximum>',
|
|
26
|
+
resource: 'https://satp-provider.example/v1/satp/evidence/reputation',
|
|
27
|
+
description: 'Read-only SATP reputation evidence lookup',
|
|
28
|
+
mimeType: 'application/json',
|
|
29
|
+
maxTimeoutSeconds: 60,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
guardrail: X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function buildLookupUrl(endpoint, request) {
|
|
36
|
+
const url = new URL(endpoint);
|
|
37
|
+
if (request.agentId) url.searchParams.set('agentId', request.agentId);
|
|
38
|
+
if (request.wallet) url.searchParams.set('wallet', request.wallet);
|
|
39
|
+
if (request.network) url.searchParams.set('network', request.network);
|
|
40
|
+
if (request.include) url.searchParams.set('include', request.include.join(','));
|
|
41
|
+
if (request.minEvidenceUpdatedAt) url.searchParams.set('minEvidenceUpdatedAt', request.minEvidenceUpdatedAt);
|
|
42
|
+
if (request.trace) url.searchParams.set('trace', request.trace);
|
|
43
|
+
return url;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertLookupBoundary(body) {
|
|
47
|
+
assert.equal(body.schemaVersion, LOOKUP_SCHEMA_VERSION);
|
|
48
|
+
assert.equal(body.policyBoundary.guardrail, X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION);
|
|
49
|
+
assert.equal(body.policyBoundary.paymentAuthorization, false);
|
|
50
|
+
assert.equal(body.policyBoundary.actionAuthorization, false);
|
|
51
|
+
assert.equal(body.policyBoundary.spendAuthorized, false);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function fetchSatpReputationEvidence({
|
|
55
|
+
fetchImpl,
|
|
56
|
+
endpoint = discoveryMetadata.endpoint,
|
|
57
|
+
request,
|
|
58
|
+
x402PaymentHeader,
|
|
59
|
+
}) {
|
|
60
|
+
if (typeof fetchImpl !== 'function') throw new Error('fetchImpl is required for this offline example');
|
|
61
|
+
const url = buildLookupUrl(endpoint, request);
|
|
62
|
+
const response = await fetchImpl(url, {
|
|
63
|
+
method: 'GET',
|
|
64
|
+
headers: {
|
|
65
|
+
Accept: 'application/json',
|
|
66
|
+
'X-PAYMENT': x402PaymentHeader,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
const body = await response.json();
|
|
70
|
+
assertLookupBoundary(body);
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(body.error && body.error.code ? body.error.code : 'satp_lookup_failed');
|
|
73
|
+
}
|
|
74
|
+
return body;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function createMockFetch() {
|
|
78
|
+
return async function mockFetch(url, options = {}) {
|
|
79
|
+
assert.equal(url.pathname, '/v1/satp/evidence/reputation');
|
|
80
|
+
assert.equal(url.searchParams.get('agentId'), 'brainChain');
|
|
81
|
+
assert.equal(options.headers.Accept, 'application/json');
|
|
82
|
+
assert.equal(options.headers['X-PAYMENT'], 'fixture-x402-payment-proof');
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
async json() {
|
|
87
|
+
return {
|
|
88
|
+
schemaVersion: LOOKUP_SCHEMA_VERSION,
|
|
89
|
+
lookup: {
|
|
90
|
+
lookupId: 'lkp_fixture_brainchain_devnet_01',
|
|
91
|
+
agentId: 'brainChain',
|
|
92
|
+
wallet: '7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgBNG',
|
|
93
|
+
network: 'devnet',
|
|
94
|
+
queriedAt: '2026-07-28T03:18:00Z',
|
|
95
|
+
},
|
|
96
|
+
reputation: {
|
|
97
|
+
score: 88,
|
|
98
|
+
tier: 'trusted',
|
|
99
|
+
sourceProgram: 'reputation_v3',
|
|
100
|
+
computedAt: '2026-07-28T03:10:00Z',
|
|
101
|
+
},
|
|
102
|
+
evidence: {
|
|
103
|
+
attestations: [
|
|
104
|
+
{
|
|
105
|
+
claimType: 'github_verified',
|
|
106
|
+
issuer: 'satp-attestation-authority',
|
|
107
|
+
status: 'valid',
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
proof: {
|
|
112
|
+
source: 'satp',
|
|
113
|
+
programSet: 'v3',
|
|
114
|
+
trace: 'redacted',
|
|
115
|
+
evidenceUpdatedAt: '2026-07-28T03:10:00Z',
|
|
116
|
+
},
|
|
117
|
+
policyBoundary: {
|
|
118
|
+
paymentAuthorization: false,
|
|
119
|
+
actionAuthorization: false,
|
|
120
|
+
spendAuthorized: false,
|
|
121
|
+
livePaymentRequiredByClient: false,
|
|
122
|
+
guardrail: X402_PAYMENT_IS_NOT_ACTION_AUTHORIZATION,
|
|
123
|
+
message: 'x402 payment grants lookup access only and does not authorize SATP agent actions.',
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function runExample() {
|
|
132
|
+
const descriptor = buildRuntimePolicyActionDescriptorFromX402Discovery(discoveryMetadata, {
|
|
133
|
+
sourceKind: 'well-known-x402',
|
|
134
|
+
maxCostUsd: 0,
|
|
135
|
+
});
|
|
136
|
+
assert.equal(descriptor.operation, 'satp.reputationEvidence.lookup');
|
|
137
|
+
assert.equal(descriptor.paymentAuthorization, false);
|
|
138
|
+
assert.equal(descriptor.actionAuthorization, false);
|
|
139
|
+
assert.equal(descriptor.spendAuthorized, false);
|
|
140
|
+
assert.equal(descriptor.livePaymentRequired, false);
|
|
141
|
+
|
|
142
|
+
const body = await fetchSatpReputationEvidence({
|
|
143
|
+
fetchImpl: createMockFetch(),
|
|
144
|
+
request: {
|
|
145
|
+
agentId: 'brainChain',
|
|
146
|
+
network: 'devnet',
|
|
147
|
+
include: ['identity', 'reputation', 'attestations', 'policy'],
|
|
148
|
+
trace: 'redacted',
|
|
149
|
+
},
|
|
150
|
+
x402PaymentHeader: 'fixture-x402-payment-proof',
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
endpoint: discoveryMetadata.endpoint,
|
|
155
|
+
lookupId: body.lookup.lookupId,
|
|
156
|
+
score: body.reputation.score,
|
|
157
|
+
guardrail: body.policyBoundary.guardrail,
|
|
158
|
+
paymentAuthorization: body.policyBoundary.paymentAuthorization,
|
|
159
|
+
actionAuthorization: body.policyBoundary.actionAuthorization,
|
|
160
|
+
spendAuthorized: body.policyBoundary.spendAuthorized,
|
|
161
|
+
livePaymentRequired: descriptor.livePaymentRequired,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (require.main === module) {
|
|
166
|
+
runExample()
|
|
167
|
+
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
168
|
+
.catch((err) => {
|
|
169
|
+
console.error(err.message);
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
module.exports = {
|
|
175
|
+
LOOKUP_SCHEMA_VERSION,
|
|
176
|
+
discoveryMetadata,
|
|
177
|
+
buildLookupUrl,
|
|
178
|
+
assertLookupBoundary,
|
|
179
|
+
fetchSatpReputationEvidence,
|
|
180
|
+
createMockFetch,
|
|
181
|
+
runExample,
|
|
182
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainai/satp-client",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "SATP client SDK surface prepared for release-candidate review; publish only after the release packet passes.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
+
"check:package-health": "node ../../scripts/check-satp-client-package-health.js",
|
|
29
30
|
"test": "node test.js",
|
|
30
31
|
"test:runtime-policy": "node --test test-runtime-policy-adapter.js",
|
|
31
32
|
"runtime-policy:example": "node examples/runtime-policy-adapter.js",
|
package/src/borsh-reader.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface ParsedGenesisRecord {
|
|
|
45
45
|
faceBurnTx: string | null;
|
|
46
46
|
genesisRecord: number;
|
|
47
47
|
isBorn: boolean;
|
|
48
|
-
isActive: boolean;
|
|
48
|
+
isActive: boolean | null;
|
|
49
49
|
authority: string;
|
|
50
50
|
pendingAuthority: string | null;
|
|
51
51
|
reputationScore: number;
|
|
@@ -55,6 +55,8 @@ export interface ParsedGenesisRecord {
|
|
|
55
55
|
createdAt: number;
|
|
56
56
|
updatedAt: number;
|
|
57
57
|
bump: number;
|
|
58
|
+
layout: 'idl-with-is-active' | 'deployed-no-is-active';
|
|
59
|
+
hasIsActiveField: boolean;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export interface ParsedLinkedWallet {
|
package/src/borsh-reader.js
CHANGED
|
@@ -64,8 +64,9 @@ class BorshReader {
|
|
|
64
64
|
|
|
65
65
|
/** Read u64 LE → number (safe for values < 2^53) */
|
|
66
66
|
readU64() {
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const val = Number(this.buf.readBigUInt64LE(this.offset));
|
|
68
|
+
this.offset += 8;
|
|
69
|
+
return val;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/** Read u64 LE → number, advances offset */
|
|
@@ -185,7 +186,7 @@ class BorshReader {
|
|
|
185
186
|
* @param {Buffer} data - Raw account data (with 8-byte discriminator)
|
|
186
187
|
* @returns {object} Parsed GenesisRecord
|
|
187
188
|
*/
|
|
188
|
-
function
|
|
189
|
+
function parseGenesisRecordLayout(data, includeIsActive) {
|
|
189
190
|
const r = new BorshReader(data).skipDiscriminator();
|
|
190
191
|
|
|
191
192
|
const agentIdHash = r.readFixedBytes32();
|
|
@@ -198,7 +199,7 @@ function deserializeGenesisRecord(data) {
|
|
|
198
199
|
const faceMint = r.readPubkey();
|
|
199
200
|
const faceBurnTx = r.readString();
|
|
200
201
|
const genesisRecord = r.readI64();
|
|
201
|
-
const isActive = r.readBool();
|
|
202
|
+
const isActive = includeIsActive ? r.readBool() : null;
|
|
202
203
|
const authority = r.readPubkeyBase58();
|
|
203
204
|
const pendingAuthority = r.readOptionPubkey();
|
|
204
205
|
const reputationScore = r.readU64Num();
|
|
@@ -209,6 +210,10 @@ function deserializeGenesisRecord(data) {
|
|
|
209
210
|
const updatedAt = r.readI64();
|
|
210
211
|
const bump = r.readU8();
|
|
211
212
|
|
|
213
|
+
if (r.remaining() !== 0) {
|
|
214
|
+
throw new Error(`GenesisRecord layout left ${r.remaining()} unread bytes`);
|
|
215
|
+
}
|
|
216
|
+
|
|
212
217
|
return {
|
|
213
218
|
agentIdHash: agentIdHash.toString('hex'),
|
|
214
219
|
agentName,
|
|
@@ -231,9 +236,23 @@ function deserializeGenesisRecord(data) {
|
|
|
231
236
|
createdAt,
|
|
232
237
|
updatedAt,
|
|
233
238
|
bump,
|
|
239
|
+
layout: includeIsActive ? 'idl-with-is-active' : 'deployed-no-is-active',
|
|
240
|
+
hasIsActiveField: includeIsActive,
|
|
234
241
|
};
|
|
235
242
|
}
|
|
236
243
|
|
|
244
|
+
function deserializeGenesisRecord(data) {
|
|
245
|
+
const errors = [];
|
|
246
|
+
for (const includeIsActive of [true, false]) {
|
|
247
|
+
try {
|
|
248
|
+
return parseGenesisRecordLayout(data, includeIsActive);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
errors.push(e.message);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
throw new Error(`Unable to deserialize GenesisRecord with known layouts: ${errors.join('; ')}`);
|
|
254
|
+
}
|
|
255
|
+
|
|
237
256
|
/**
|
|
238
257
|
* Deserialize a LinkedWallet from raw account data.
|
|
239
258
|
* @param {Buffer} data - Raw account data (with 8-byte discriminator)
|
package/src/constants.js
CHANGED
|
@@ -10,8 +10,10 @@ const DEVNET_PROGRAM_IDS = {
|
|
|
10
10
|
ESCROW: new PublicKey('UpJ7jmUzHkQ7EdBKiBv3zq8Dr1fVh6GVWKa7nYtwQ22'),
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const LEGACY_V2_MAINNET_FENCE_MESSAGE = 'Legacy SATP V2 mainnet program IDs are fenced; use getV3ProgramIds("mainnet") for V3 or pass allowLegacyV2Mainnet: true for explicit read-only legacy V2 access';
|
|
14
|
+
|
|
15
|
+
// SATP v2 Program IDs — Mainnet. Keep internal so direct public imports fail closed.
|
|
16
|
+
const LEGACY_V2_MAINNET_PROGRAM_IDS = {
|
|
15
17
|
IDENTITY: new PublicKey('97yL33fcu6iWT2TdERS5HeqrMSGiUnxuy6nUcTrKieSq'),
|
|
16
18
|
REVIEWS: new PublicKey('Ge1sD2qwmH8QaaKCPZzZERvsFXNVMvKbAgTp2p17yjLK'),
|
|
17
19
|
REPUTATION: new PublicKey('C9ogv8TBrvFy4pLKDoGQg9B73Q5rKPPsQ4kzkcDk6Jd'),
|
|
@@ -20,6 +22,18 @@ const MAINNET_PROGRAM_IDS = {
|
|
|
20
22
|
ESCROW: null,
|
|
21
23
|
};
|
|
22
24
|
|
|
25
|
+
const MAINNET_PROGRAM_IDS = new Proxy({}, {
|
|
26
|
+
get() {
|
|
27
|
+
throw new Error(LEGACY_V2_MAINNET_FENCE_MESSAGE);
|
|
28
|
+
},
|
|
29
|
+
ownKeys() {
|
|
30
|
+
throw new Error(LEGACY_V2_MAINNET_FENCE_MESSAGE);
|
|
31
|
+
},
|
|
32
|
+
getOwnPropertyDescriptor() {
|
|
33
|
+
throw new Error(LEGACY_V2_MAINNET_FENCE_MESSAGE);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
23
37
|
const MAINNET_RPC = 'https://api.mainnet-beta.solana.com';
|
|
24
38
|
const DEVNET_RPC = 'https://api.devnet.solana.com';
|
|
25
39
|
|
|
@@ -37,13 +51,21 @@ const ESCROW_SEED = 'escrow';
|
|
|
37
51
|
/**
|
|
38
52
|
* Get program IDs for a given network.
|
|
39
53
|
* @param {'mainnet'|'devnet'} network
|
|
54
|
+
* @param {object} [opts]
|
|
55
|
+
* @param {boolean} [opts.allowLegacyV2Mainnet=false]
|
|
40
56
|
* @returns {object} Program ID map
|
|
41
57
|
*/
|
|
42
|
-
function getProgramIds(network = 'devnet') {
|
|
58
|
+
function getProgramIds(network = 'devnet', opts = {}) {
|
|
43
59
|
if (network !== 'devnet' && network !== 'mainnet') {
|
|
44
60
|
throw new Error('Invalid network: expected devnet or mainnet');
|
|
45
61
|
}
|
|
46
|
-
|
|
62
|
+
if (network === 'mainnet') {
|
|
63
|
+
if (opts.allowLegacyV2Mainnet !== true) {
|
|
64
|
+
throw new Error(LEGACY_V2_MAINNET_FENCE_MESSAGE);
|
|
65
|
+
}
|
|
66
|
+
return LEGACY_V2_MAINNET_PROGRAM_IDS;
|
|
67
|
+
}
|
|
68
|
+
return DEVNET_PROGRAM_IDS;
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
/**
|
|
@@ -61,6 +83,7 @@ function getRpcUrl(network = 'devnet') {
|
|
|
61
83
|
module.exports = {
|
|
62
84
|
DEVNET_PROGRAM_IDS,
|
|
63
85
|
MAINNET_PROGRAM_IDS,
|
|
86
|
+
LEGACY_V2_MAINNET_FENCE_MESSAGE,
|
|
64
87
|
getProgramIds,
|
|
65
88
|
getRpcUrl,
|
|
66
89
|
MAINNET_RPC,
|
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
|
@@ -46,10 +46,15 @@ class SATPSDK {
|
|
|
46
46
|
*/
|
|
47
47
|
constructor(opts = {}) {
|
|
48
48
|
this.network = opts.network || 'devnet';
|
|
49
|
+
if (this.network === 'mainnet' && opts.allowLegacyV2Mainnet !== true) {
|
|
50
|
+
throw new Error('Legacy SATPSDK V2 mainnet access is fenced; use SATPV3SDK for V3 or pass allowLegacyV2Mainnet: true for explicit read-only legacy V2 access');
|
|
51
|
+
}
|
|
49
52
|
this.rpcUrl = opts.rpcUrl || getRpcUrl(this.network);
|
|
50
53
|
this.commitment = opts.commitment || 'confirmed';
|
|
51
54
|
this.connection = new Connection(this.rpcUrl, this.commitment);
|
|
52
|
-
this.programIds = getProgramIds(this.network
|
|
55
|
+
this.programIds = getProgramIds(this.network, {
|
|
56
|
+
allowLegacyV2Mainnet: opts.allowLegacyV2Mainnet === true,
|
|
57
|
+
});
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
// ─── Identity ──────────────────────────────────────────
|
|
@@ -822,71 +827,6 @@ function createSATPClient(opts = {}) {
|
|
|
822
827
|
const borshReader = require('./borsh-reader');
|
|
823
828
|
|
|
824
829
|
|
|
825
|
-
// Fixed genesis deserializer — matches actual on-chain struct (no isActive field)
|
|
826
|
-
function _deserializeGenesisFixed(data) {
|
|
827
|
-
if (!data || data.length < 8) return null;
|
|
828
|
-
try {
|
|
829
|
-
const { PublicKey } = require('@solana/web3.js');
|
|
830
|
-
let offset = 8; // skip discriminator
|
|
831
|
-
const agentIdHashBytes = data.slice(offset, offset + 32); offset += 32;
|
|
832
|
-
const readString = () => {
|
|
833
|
-
const len = data.readUInt32LE(offset); offset += 4;
|
|
834
|
-
const str = data.slice(offset, offset + len).toString('utf8'); offset += len;
|
|
835
|
-
return str;
|
|
836
|
-
};
|
|
837
|
-
const readVecString = () => {
|
|
838
|
-
const count = data.readUInt32LE(offset); offset += 4;
|
|
839
|
-
const arr = [];
|
|
840
|
-
for (let i = 0; i < count; i++) arr.push(readString());
|
|
841
|
-
return arr;
|
|
842
|
-
};
|
|
843
|
-
const agentName = readString();
|
|
844
|
-
const description = readString();
|
|
845
|
-
const category = readString();
|
|
846
|
-
const capabilities = readVecString();
|
|
847
|
-
const metadataUri = readString();
|
|
848
|
-
const faceImage = readString();
|
|
849
|
-
const faceMint = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
850
|
-
const faceBurnTx = readString();
|
|
851
|
-
const genesisRecord = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
852
|
-
// NOTE: No isActive field in deployed program (SDK bug — has phantom isActive)
|
|
853
|
-
const authority = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
854
|
-
const hasPending = data[offset]; offset += 1;
|
|
855
|
-
let pendingAuthority = null;
|
|
856
|
-
if (hasPending === 1) {
|
|
857
|
-
pendingAuthority = new PublicKey(data.slice(offset, offset + 32)).toBase58();
|
|
858
|
-
offset += 32;
|
|
859
|
-
}
|
|
860
|
-
const reputationScore = Number(data.readBigUInt64LE(offset)); offset += 8;
|
|
861
|
-
const verificationLevel = data[offset]; offset += 1;
|
|
862
|
-
const reputationUpdatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
863
|
-
const verificationUpdatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
864
|
-
const createdAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
865
|
-
const updatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
866
|
-
const bump = data[offset]; offset += 1;
|
|
867
|
-
return {
|
|
868
|
-
agentIdHash: Array.from(agentIdHashBytes),
|
|
869
|
-
agentName, description, category, capabilities, metadataUri, faceImage,
|
|
870
|
-
faceMint: faceMint.toBase58(),
|
|
871
|
-
faceBurnTx,
|
|
872
|
-
genesisRecord,
|
|
873
|
-
isBorn: genesisRecord > 0,
|
|
874
|
-
authority: authority.toBase58(),
|
|
875
|
-
pendingAuthority,
|
|
876
|
-
reputationScore,
|
|
877
|
-
verificationLevel,
|
|
878
|
-
verificationLabel: ['Unverified','Registered','Verified','Established','Trusted','Sovereign'][verificationLevel] || 'Unknown',
|
|
879
|
-
reputationPct: (reputationScore / 10000).toFixed(2),
|
|
880
|
-
reputationUpdatedAt, verificationUpdatedAt,
|
|
881
|
-
createdAt: createdAt > 0 ? new Date(createdAt * 1000).toISOString() : null,
|
|
882
|
-
updatedAt: updatedAt > 0 ? new Date(updatedAt * 1000).toISOString() : null,
|
|
883
|
-
bump,
|
|
884
|
-
};
|
|
885
|
-
} catch (e) {
|
|
886
|
-
return { error: e.message, raw: data.toString('hex').slice(0, 200) };
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
|
|
890
830
|
module.exports = {
|
|
891
831
|
// V2 SDK (backward compatible — legacy, kept for escrow V2 / old paths)
|
|
892
832
|
SATPSDK,
|
|
@@ -981,10 +921,8 @@ module.exports = {
|
|
|
981
921
|
evaluateRuntimePolicy,
|
|
982
922
|
|
|
983
923
|
// V3 Deserialization (local extracted scaffold)
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
deserializeGenesis: _deserializeGenesisFixed,
|
|
987
|
-
deserializeGenesisRecord: _deserializeGenesisFixed, // alias for old name
|
|
924
|
+
deserializeGenesis: v3Borsh.deserializeGenesisRecord,
|
|
925
|
+
deserializeGenesisRecord: v3Borsh.deserializeGenesisRecord,
|
|
988
926
|
deserializeLinkedWallet: v3Borsh.deserializeLinkedWallet,
|
|
989
927
|
deserializeMintTracker: v3Borsh.deserializeMintTracker,
|
|
990
928
|
deserializeNameRegistry: v3Borsh.deserializeNameRegistry,
|
|
@@ -36,6 +36,7 @@ const DEFAULT_POLICY = Object.freeze({
|
|
|
36
36
|
|
|
37
37
|
const RUNTIME_POLICY_AUDIT_TRACE_SCHEMA_VERSION = 'satp.runtimePolicyAuditTrace.v1';
|
|
38
38
|
const RUNTIME_POLICY_HOST_ACTION_DESCRIPTOR_SCHEMA_VERSION = 'satp.runtimePolicyHostActionDescriptor.v1';
|
|
39
|
+
const TRUST_SCORE_GATE_TYPES = new Set(['agentfolio_trust_gate', 'host_trust_gate']);
|
|
39
40
|
|
|
40
41
|
function createRuntimePolicyAdapter(config = {}) {
|
|
41
42
|
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
@@ -361,7 +362,7 @@ function firstDefined(...values) {
|
|
|
361
362
|
|
|
362
363
|
function defaultResourceForAction(type, action) {
|
|
363
364
|
if (type === 'mcp_protected_tool') return 'mcp://protected/tool';
|
|
364
|
-
if (type
|
|
365
|
+
if (TRUST_SCORE_GATE_TYPES.has(type)) {
|
|
365
366
|
if (action.profileId) {
|
|
366
367
|
return `https://agentfolio.bot/api/profile/${encodeURIComponent(String(action.profileId))}/trust-score`;
|
|
367
368
|
}
|
|
@@ -373,27 +374,27 @@ function defaultResourceForAction(type, action) {
|
|
|
373
374
|
|
|
374
375
|
function defaultOperationForAction(type) {
|
|
375
376
|
if (type === 'mcp_protected_tool') return 'invoke';
|
|
376
|
-
if (type
|
|
377
|
+
if (TRUST_SCORE_GATE_TYPES.has(type)) return 'trust-score-read';
|
|
377
378
|
if (type === 'x402_endpoint') return 'lookup';
|
|
378
379
|
return null;
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
function defaultCapabilityForAction(type) {
|
|
382
|
-
if (type
|
|
383
|
+
if (TRUST_SCORE_GATE_TYPES.has(type)) return 'agentfolio:trust-read';
|
|
383
384
|
return null;
|
|
384
385
|
}
|
|
385
386
|
|
|
386
387
|
function defaultMinimumTrustScoreForAction(type) {
|
|
387
|
-
if (type
|
|
388
|
+
if (TRUST_SCORE_GATE_TYPES.has(type)) return DEFAULT_POLICY.minimumTrustScore;
|
|
388
389
|
return null;
|
|
389
390
|
}
|
|
390
391
|
|
|
391
392
|
function defaultAllowDegradedForAction(type) {
|
|
392
|
-
return type
|
|
393
|
+
return TRUST_SCORE_GATE_TYPES.has(type);
|
|
393
394
|
}
|
|
394
395
|
|
|
395
396
|
function defaultRequiresFreshEvidenceForAction(type) {
|
|
396
|
-
return type === 'mcp_protected_tool' || type
|
|
397
|
+
return type === 'mcp_protected_tool' || TRUST_SCORE_GATE_TYPES.has(type);
|
|
397
398
|
}
|
|
398
399
|
|
|
399
400
|
function defaultCostUsdForAction(type) {
|
package/src/v3-sdk.d.ts
CHANGED
|
@@ -48,7 +48,9 @@ export interface GenesisRecord {
|
|
|
48
48
|
metadataUri: string;
|
|
49
49
|
reputationScore: number;
|
|
50
50
|
verificationLevel: number;
|
|
51
|
-
isActive: boolean;
|
|
51
|
+
isActive: boolean | null;
|
|
52
|
+
layout?: 'idl-with-is-active' | 'deployed-no-is-active';
|
|
53
|
+
hasIsActiveField?: boolean;
|
|
52
54
|
createdAt: number;
|
|
53
55
|
updatedAt: number;
|
|
54
56
|
pendingAuthority: string | null;
|
package/src/v3-sdk.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
SPL_TOKEN_PROGRAM_ID,
|
|
27
27
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
28
28
|
} = require('./v3-pda');
|
|
29
|
+
const { deserializeGenesisRecord } = require('./borsh-reader');
|
|
29
30
|
|
|
30
31
|
const DEVNET_RPC = 'https://api.devnet.solana.com';
|
|
31
32
|
const MAINNET_RPC = 'https://api.mainnet-beta.solana.com';
|
|
@@ -1960,78 +1961,9 @@ class SATPV3SDK {
|
|
|
1960
1961
|
if (!acct) return null;
|
|
1961
1962
|
|
|
1962
1963
|
try {
|
|
1963
|
-
const data = acct.data.slice(8); // skip Anchor discriminator
|
|
1964
|
-
let offset = 0;
|
|
1965
|
-
|
|
1966
|
-
const agentIdHash = data.slice(offset, offset + 32); offset += 32;
|
|
1967
|
-
|
|
1968
|
-
// Read strings
|
|
1969
|
-
const readString = () => {
|
|
1970
|
-
const len = data.readUInt32LE(offset); offset += 4;
|
|
1971
|
-
const str = data.slice(offset, offset + len).toString('utf8'); offset += len;
|
|
1972
|
-
return str;
|
|
1973
|
-
};
|
|
1974
|
-
|
|
1975
|
-
const readVecString = () => {
|
|
1976
|
-
const count = data.readUInt32LE(offset); offset += 4;
|
|
1977
|
-
const arr = [];
|
|
1978
|
-
for (let i = 0; i < count; i++) arr.push(readString());
|
|
1979
|
-
return arr;
|
|
1980
|
-
};
|
|
1981
|
-
|
|
1982
|
-
const agentName = readString();
|
|
1983
|
-
const description = readString();
|
|
1984
|
-
const category = readString();
|
|
1985
|
-
const capabilities = readVecString();
|
|
1986
|
-
const metadataUri = readString();
|
|
1987
|
-
const faceImage = readString();
|
|
1988
|
-
const faceMint = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
1989
|
-
const faceBurnTx = readString();
|
|
1990
|
-
const genesisRecord = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
1991
|
-
const isActive = data[offset] === 1; offset += 1;
|
|
1992
|
-
const authority = new PublicKey(data.slice(offset, offset + 32)); offset += 32;
|
|
1993
|
-
|
|
1994
|
-
// Option<Pubkey> — Borsh: 0x00 = None (1 byte only), 0x01 + 32 bytes = Some
|
|
1995
|
-
const hasPending = data[offset] === 1; offset += 1;
|
|
1996
|
-
let pendingAuthority = null;
|
|
1997
|
-
if (hasPending) {
|
|
1998
|
-
pendingAuthority = new PublicKey(data.slice(offset, offset + 32)).toBase58();
|
|
1999
|
-
offset += 32;
|
|
2000
|
-
}
|
|
2001
|
-
|
|
2002
|
-
const reputationScore = Number(data.readBigUInt64LE(offset)); offset += 8;
|
|
2003
|
-
const verificationLevel = data[offset]; offset += 1;
|
|
2004
|
-
const reputationUpdatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
2005
|
-
const verificationUpdatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
2006
|
-
const createdAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
2007
|
-
const updatedAt = Number(data.readBigInt64LE(offset)); offset += 8;
|
|
2008
|
-
const bump = data[offset]; offset += 1;
|
|
2009
|
-
|
|
2010
|
-
const isBorn = genesisRecord !== 0;
|
|
2011
|
-
|
|
2012
1964
|
return {
|
|
1965
|
+
...deserializeGenesisRecord(acct.data),
|
|
2013
1966
|
pda: pda.toBase58(),
|
|
2014
|
-
agentIdHash: Buffer.from(agentIdHash).toString('hex'),
|
|
2015
|
-
agentName,
|
|
2016
|
-
description,
|
|
2017
|
-
category,
|
|
2018
|
-
capabilities,
|
|
2019
|
-
metadataUri,
|
|
2020
|
-
faceImage: faceImage || null,
|
|
2021
|
-
faceMint: faceMint.equals(PublicKey.default) ? null : faceMint.toBase58(),
|
|
2022
|
-
faceBurnTx: faceBurnTx || null,
|
|
2023
|
-
genesisRecord,
|
|
2024
|
-
isBorn,
|
|
2025
|
-
isActive,
|
|
2026
|
-
authority: authority.toBase58(),
|
|
2027
|
-
pendingAuthority,
|
|
2028
|
-
reputationScore,
|
|
2029
|
-
verificationLevel,
|
|
2030
|
-
reputationUpdatedAt,
|
|
2031
|
-
verificationUpdatedAt,
|
|
2032
|
-
createdAt,
|
|
2033
|
-
updatedAt,
|
|
2034
|
-
bump,
|
|
2035
1967
|
};
|
|
2036
1968
|
} catch (e) {
|
|
2037
1969
|
return { pda: pda.toBase58(), raw: acct.data.toString('hex'), error: e.message };
|