@perkos/agent-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/LICENSE +21 -0
  3. package/README.md +226 -0
  4. package/SECURITY.md +67 -0
  5. package/dist/builders.d.ts +19 -0
  6. package/dist/builders.d.ts.map +1 -0
  7. package/dist/builders.js +180 -0
  8. package/dist/builders.js.map +1 -0
  9. package/dist/clarity.d.ts +11 -0
  10. package/dist/clarity.d.ts.map +1 -0
  11. package/dist/clarity.js +80 -0
  12. package/dist/clarity.js.map +1 -0
  13. package/dist/client.d.ts +39 -0
  14. package/dist/client.d.ts.map +1 -0
  15. package/dist/client.js +290 -0
  16. package/dist/client.js.map +1 -0
  17. package/dist/constants.d.ts +12 -0
  18. package/dist/constants.d.ts.map +1 -0
  19. package/dist/constants.js +72 -0
  20. package/dist/constants.js.map +1 -0
  21. package/dist/errors.d.ts +14 -0
  22. package/dist/errors.d.ts.map +1 -0
  23. package/dist/errors.js +11 -0
  24. package/dist/errors.js.map +1 -0
  25. package/dist/index.d.ts +14 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +10 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/policy.d.ts +14 -0
  30. package/dist/policy.d.ts.map +1 -0
  31. package/dist/policy.js +75 -0
  32. package/dist/policy.js.map +1 -0
  33. package/dist/signers.d.ts +51 -0
  34. package/dist/signers.d.ts.map +1 -0
  35. package/dist/signers.js +145 -0
  36. package/dist/signers.js.map +1 -0
  37. package/dist/tracker.d.ts +21 -0
  38. package/dist/tracker.d.ts.map +1 -0
  39. package/dist/tracker.js +175 -0
  40. package/dist/tracker.js.map +1 -0
  41. package/dist/txid.d.ts +2 -0
  42. package/dist/txid.d.ts.map +1 -0
  43. package/dist/txid.js +13 -0
  44. package/dist/txid.js.map +1 -0
  45. package/dist/types.d.ts +204 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +2 -0
  48. package/dist/types.js.map +1 -0
  49. package/dist/validation.d.ts +17 -0
  50. package/dist/validation.d.ts.map +1 -0
  51. package/dist/validation.js +124 -0
  52. package/dist/validation.js.map +1 -0
  53. package/docs/ARCHITECTURE.md +111 -0
  54. package/examples/quickstart.ts +19 -0
  55. package/examples/testnet-api.ts +23 -0
  56. package/examples/testnet-lifecycle.ts +305 -0
  57. package/examples/testnet.env.example +14 -0
  58. package/package.json +69 -0
@@ -0,0 +1,305 @@
1
+ import {
2
+ HeadlessSigner,
3
+ PerkOSClient,
4
+ PerkOSError,
5
+ } from "@perkos/agent-sdk";
6
+ import type {
7
+ ContractCallPlan,
8
+ PerkOSSigner,
9
+ TransactionConfirmation,
10
+ TransactionReceipt,
11
+ } from "@perkos/agent-sdk";
12
+ import { fetchChainTip } from "./testnet-api.js";
13
+
14
+ const NETWORK = "testnet" as const;
15
+ const DEFAULT_API_URL = "https://api.testnet.hiro.so";
16
+ const DRY_RUN_CLIENT = "ST1THWXQ8368SDN2MJGE4BMDKMCHZ2GSVTSQDA7QF";
17
+ const DRY_RUN_PROVIDER = "ST3AZN3BSQYJ5VWMNG92N88Z4G9498VYSHDZD9EK";
18
+ const DRY_RUN_EVALUATOR = "ST1YXCNCJT2NJZR6G4NYNE6NZ0CPDKPWKVJDRPKTJ";
19
+ const DEMO_JOB_ID = 1n;
20
+ const DEMO_AMOUNT = 100n;
21
+
22
+ function json(value: unknown): string {
23
+ return JSON.stringify(
24
+ value,
25
+ (_key, current) =>
26
+ typeof current === "bigint" ? current.toString() : current,
27
+ 2
28
+ );
29
+ }
30
+
31
+ function printPlan(label: string, plan: ContractCallPlan): void {
32
+ console.log(`\n${label}`);
33
+ console.log(
34
+ json({
35
+ network: plan.network,
36
+ contract: plan.contract,
37
+ functionName: plan.functionName,
38
+ intent: plan.intent,
39
+ postConditionMode: plan.postConditionMode,
40
+ postConditionCount: plan.postConditions.length,
41
+ })
42
+ );
43
+ }
44
+
45
+ function dryRun(): void {
46
+ const preview = new PerkOSClient({
47
+ network: NETWORK,
48
+ spendingPolicy: {
49
+ allowedNetworks: [NETWORK],
50
+ allowedAssets: ["sbtc"],
51
+ maxPerTransaction: { sbtc: DEMO_AMOUNT },
52
+ maxPerSession: { sbtc: DEMO_AMOUNT },
53
+ },
54
+ });
55
+ const expiresAt = 1_000_000n;
56
+ const plans = [
57
+ [
58
+ "1. Register provider agent",
59
+ preview.transactions.registerAgent({
60
+ name: "PerkOS Testnet Provider",
61
+ description: "Agent SDK transactional quickstart provider",
62
+ wallet: DRY_RUN_PROVIDER,
63
+ endpoints: [{ name: "mcp", url: "https://example.com/mcp" }],
64
+ }),
65
+ ],
66
+ [
67
+ "2. Create sBTC job with distinct provider and evaluator",
68
+ preview.transactions.createJob({
69
+ asset: "sbtc",
70
+ provider: DRY_RUN_PROVIDER,
71
+ evaluator: DRY_RUN_EVALUATOR,
72
+ expiredAt: expiresAt,
73
+ description: "Create a signed testnet lifecycle receipt",
74
+ }),
75
+ ],
76
+ [
77
+ "3. Set job budget",
78
+ preview.transactions.setBudget({
79
+ asset: "sbtc",
80
+ jobId: DEMO_JOB_ID,
81
+ amount: DEMO_AMOUNT,
82
+ }),
83
+ ],
84
+ [
85
+ "4. Fund exact sBTC escrow",
86
+ preview.transactions.fundJob({
87
+ asset: "sbtc",
88
+ jobId: DEMO_JOB_ID,
89
+ amount: DEMO_AMOUNT,
90
+ sender: DRY_RUN_CLIENT,
91
+ }),
92
+ ],
93
+ [
94
+ "5. Provider submits deliverable",
95
+ preview.transactions.submitWork({
96
+ asset: "sbtc",
97
+ jobId: DEMO_JOB_ID,
98
+ deliverable: "ipfs:bafy-perkos-testnet-receipt",
99
+ }),
100
+ ],
101
+ [
102
+ "6. Evaluator releases escrow",
103
+ preview.transactions.completeJob({
104
+ asset: "sbtc",
105
+ jobId: DEMO_JOB_ID,
106
+ amount: DEMO_AMOUNT,
107
+ recipient: DRY_RUN_PROVIDER,
108
+ }),
109
+ ],
110
+ [
111
+ "7. Client rates provider",
112
+ preview.transactions.rateProvider({
113
+ asset: "sbtc",
114
+ jobId: DEMO_JOB_ID,
115
+ score: 5n,
116
+ comment: "Completed through the Agent SDK quickstart",
117
+ }),
118
+ ],
119
+ ] as const;
120
+
121
+ console.log("PerkOS sBTC lifecycle: safe testnet preview");
122
+ console.log("No wallet, key, transaction, or network request was used.");
123
+ for (const [label, plan] of plans) printPlan(label, plan);
124
+ console.log("\nFunding policy decision");
125
+ console.log(json(preview.preview(plans[3][1])));
126
+ console.log(
127
+ "\nTo broadcast, copy examples/testnet.env.example, fund all three testnet roles with fee STX, fund the client with testnet sBTC, and set PERKOS_CONFIRM_TESTNET_BROADCAST=yes."
128
+ );
129
+ }
130
+
131
+ function requiredEnvironment(name: string): string {
132
+ const value = process.env[name]?.trim();
133
+ if (!value) throw new Error(`${name} is required for live testnet mode.`);
134
+ return value;
135
+ }
136
+
137
+ function amountFromEnvironment(): bigint {
138
+ const value = requiredEnvironment("PERKOS_AMOUNT");
139
+ if (!/^\d+$/.test(value) || BigInt(value) === 0n) {
140
+ throw new Error("PERKOS_AMOUNT must be a positive integer number of satoshis.");
141
+ }
142
+ return BigInt(value);
143
+ }
144
+
145
+ function okUint(confirmation: TransactionConfirmation, label: string): bigint {
146
+ const repr = confirmation.result?.repr;
147
+ const match = repr?.match(/^\(ok u(\d+)\)$/);
148
+ if (!match?.[1]) {
149
+ throw new Error(`${label} did not return an (ok uint) result: ${repr ?? "missing"}.`);
150
+ }
151
+ return BigInt(match[1]);
152
+ }
153
+
154
+ async function broadcastAndConfirm(
155
+ label: string,
156
+ client: PerkOSClient,
157
+ action: () => Promise<TransactionReceipt>
158
+ ): Promise<TransactionConfirmation> {
159
+ console.log(`\n${label}`);
160
+ const broadcast = await action();
161
+ console.log(`Broadcast: ${broadcast.explorerUrl}`);
162
+ const confirmation = await client.confirm(broadcast, {
163
+ pollIntervalMs: 5_000,
164
+ timeoutMs: 10 * 60_000,
165
+ });
166
+ console.log(`Confirmation: ${confirmation.status}`);
167
+ if (confirmation.status !== "success") {
168
+ throw new PerkOSError(
169
+ "CONFIRMATION_FAILED",
170
+ `${label} ended with ${confirmation.status}.`,
171
+ { txid: confirmation.txid, result: confirmation.result }
172
+ );
173
+ }
174
+ return confirmation;
175
+ }
176
+
177
+ function liveClient(
178
+ signer: PerkOSSigner,
179
+ apiUrl: string,
180
+ amount?: bigint
181
+ ): PerkOSClient {
182
+ return new PerkOSClient({
183
+ network: NETWORK,
184
+ apiUrl,
185
+ signer,
186
+ ...(amount === undefined
187
+ ? {}
188
+ : {
189
+ spendingPolicy: {
190
+ allowedNetworks: [NETWORK],
191
+ allowedAssets: ["sbtc"],
192
+ maxPerTransaction: { sbtc: amount },
193
+ maxPerSession: { sbtc: amount },
194
+ },
195
+ }),
196
+ });
197
+ }
198
+
199
+ async function liveRun(): Promise<void> {
200
+ const amount = amountFromEnvironment();
201
+ const apiUrl = (process.env.PERKOS_API_URL?.trim() || DEFAULT_API_URL).replace(
202
+ /\/+$/,
203
+ ""
204
+ );
205
+ const clientSigner = new HeadlessSigner({
206
+ network: NETWORK,
207
+ apiUrl,
208
+ privateKeyProvider: () => requiredEnvironment("PERKOS_CLIENT_PRIVATE_KEY"),
209
+ });
210
+ const providerSigner = new HeadlessSigner({
211
+ network: NETWORK,
212
+ apiUrl,
213
+ privateKeyProvider: () => requiredEnvironment("PERKOS_PROVIDER_PRIVATE_KEY"),
214
+ });
215
+ const evaluatorSigner = new HeadlessSigner({
216
+ network: NETWORK,
217
+ apiUrl,
218
+ privateKeyProvider: () => requiredEnvironment("PERKOS_EVALUATOR_PRIVATE_KEY"),
219
+ });
220
+ const [clientAddress, providerAddress, evaluatorAddress, tip] = await Promise.all([
221
+ clientSigner.getAddress(),
222
+ providerSigner.getAddress(),
223
+ evaluatorSigner.getAddress(),
224
+ fetchChainTip(apiUrl),
225
+ ]);
226
+ if (new Set([clientAddress, providerAddress, evaluatorAddress]).size !== 3) {
227
+ throw new Error("Client, provider, and evaluator keys must control distinct addresses.");
228
+ }
229
+
230
+ const client = liveClient(clientSigner, apiUrl, amount);
231
+ const provider = liveClient(providerSigner, apiUrl);
232
+ const evaluator = liveClient(evaluatorSigner, apiUrl);
233
+ const expiresAt = tip + 1_000n;
234
+ const run = Date.now().toString(36);
235
+
236
+ console.log("PerkOS sBTC lifecycle: LIVE TESTNET MODE");
237
+ console.log(
238
+ json({
239
+ network: NETWORK,
240
+ amountSatoshis: amount,
241
+ client: clientAddress,
242
+ provider: providerAddress,
243
+ evaluator: evaluatorAddress,
244
+ expiresAt,
245
+ })
246
+ );
247
+
248
+ await broadcastAndConfirm("1. Register provider agent", provider, () =>
249
+ provider.registerAgent({
250
+ name: `Testnet Provider ${run}`,
251
+ description: "PerkOS Agent SDK transactional quickstart provider",
252
+ wallet: providerAddress,
253
+ endpoints: [{ name: "mcp", url: "https://example.com/mcp" }],
254
+ })
255
+ );
256
+ const created = await broadcastAndConfirm("2. Create sBTC job", client, () =>
257
+ client.createJob({
258
+ asset: "sbtc",
259
+ provider: providerAddress,
260
+ evaluator: evaluatorAddress,
261
+ expiredAt: expiresAt,
262
+ description: `Agent SDK testnet lifecycle ${run}`,
263
+ })
264
+ );
265
+ const jobId = okUint(created, "create-job");
266
+ console.log(`Job ID: ${jobId}`);
267
+
268
+ await broadcastAndConfirm("3. Set job budget", client, () =>
269
+ client.setBudget({ asset: "sbtc", jobId, amount })
270
+ );
271
+ await broadcastAndConfirm("4. Fund exact sBTC escrow", client, () =>
272
+ client.fundJob({ asset: "sbtc", jobId, amount })
273
+ );
274
+ await broadcastAndConfirm("5. Provider submits deliverable", provider, () =>
275
+ provider.submitWork({
276
+ asset: "sbtc",
277
+ jobId,
278
+ deliverable: `ipfs:perkos-${run}`,
279
+ })
280
+ );
281
+ await broadcastAndConfirm("6. Evaluator releases escrow", evaluator, () =>
282
+ evaluator.completeJob("sbtc", jobId)
283
+ );
284
+ await broadcastAndConfirm("7. Client rates provider", client, () =>
285
+ client.rateProvider({
286
+ asset: "sbtc",
287
+ jobId,
288
+ score: 5n,
289
+ comment: "Completed through the Agent SDK quickstart",
290
+ })
291
+ );
292
+
293
+ const [job, reputation] = await Promise.all([
294
+ client.getJob("sbtc", jobId),
295
+ client.getReputation(providerAddress),
296
+ ]);
297
+ console.log("\nFinal on-chain state");
298
+ console.log(json({ job, reputation }));
299
+ }
300
+
301
+ if (process.env.PERKOS_CONFIRM_TESTNET_BROADCAST === "yes") {
302
+ await liveRun();
303
+ } else {
304
+ dryRun();
305
+ }
@@ -0,0 +1,14 @@
1
+ # The transactional quickstart is a dry run unless this exact value is set.
2
+ PERKOS_CONFIRM_TESTNET_BROADCAST=yes
3
+
4
+ # Use three distinct testnet identities. Never commit real key material.
5
+ PERKOS_CLIENT_PRIVATE_KEY=
6
+ PERKOS_PROVIDER_PRIVATE_KEY=
7
+ PERKOS_EVALUATOR_PRIVATE_KEY=
8
+
9
+ # Amount is denominated in satoshis. The client must hold this testnet sBTC
10
+ # plus enough testnet STX for transaction fees. The other roles also need STX.
11
+ PERKOS_AMOUNT=100
12
+
13
+ # Optional custom Stacks API endpoint.
14
+ # PERKOS_API_URL=https://api.testnet.hiro.so
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@perkos/agent-sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for agent identity, escrow settlement, and reputation on Stacks.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "CHANGELOG.md",
18
+ "SECURITY.md",
19
+ "docs",
20
+ "examples",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.build.json",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "verify": "npm run typecheck && npm test && npm run build",
29
+ "prepublishOnly": "npm run verify",
30
+ "prequickstart": "npm run build",
31
+ "quickstart": "node --import tsx examples/quickstart.ts",
32
+ "prequickstart:testnet": "npm run build",
33
+ "quickstart:testnet": "node --import tsx examples/testnet-lifecycle.ts"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/PerkOS-xyz/PerkOS-Agent-SDK.git"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/PerkOS-xyz/PerkOS-Agent-SDK/issues"
41
+ },
42
+ "homepage": "https://stacks.perkos.xyz",
43
+ "keywords": [
44
+ "ai-agents",
45
+ "agentic-commerce",
46
+ "stacks",
47
+ "sbtc",
48
+ "escrow",
49
+ "bitcoin"
50
+ ],
51
+ "author": "PerkOS",
52
+ "license": "MIT",
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "provenance": true
56
+ },
57
+ "engines": {
58
+ "node": ">=20"
59
+ },
60
+ "dependencies": {
61
+ "@stacks/transactions": "^7.6.0"
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^22.15.0",
65
+ "tsx": "^4.23.1",
66
+ "typescript": "^5.9.3",
67
+ "vitest": "^4.1.10"
68
+ }
69
+ }