@perkos/agent-sdk 0.7.1 → 0.8.0-rc.1

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 (83) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +69 -17
  3. package/dist/builders.d.ts +5 -1
  4. package/dist/builders.d.ts.map +1 -1
  5. package/dist/builders.js +155 -11
  6. package/dist/builders.js.map +1 -1
  7. package/dist/client.d.ts +8 -1
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +168 -26
  10. package/dist/client.js.map +1 -1
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/constants.js +2 -0
  13. package/dist/constants.js.map +1 -1
  14. package/dist/custody/backend.d.ts +4 -0
  15. package/dist/custody/backend.d.ts.map +1 -0
  16. package/dist/custody/backend.js +149 -0
  17. package/dist/custody/backend.js.map +1 -0
  18. package/dist/custody/cli.d.ts +3 -0
  19. package/dist/custody/cli.d.ts.map +1 -0
  20. package/dist/custody/cli.js +43 -0
  21. package/dist/custody/cli.js.map +1 -0
  22. package/dist/custody/engine.d.ts +54 -0
  23. package/dist/custody/engine.d.ts.map +1 -0
  24. package/dist/custody/engine.js +115 -0
  25. package/dist/custody/engine.js.map +1 -0
  26. package/dist/custody/ledger.d.ts +24 -0
  27. package/dist/custody/ledger.d.ts.map +1 -0
  28. package/dist/custody/ledger.js +122 -0
  29. package/dist/custody/ledger.js.map +1 -0
  30. package/dist/custody/permit.d.ts +42 -0
  31. package/dist/custody/permit.d.ts.map +1 -0
  32. package/dist/custody/permit.js +91 -0
  33. package/dist/custody/permit.js.map +1 -0
  34. package/dist/custody/socket.d.ts +11 -0
  35. package/dist/custody/socket.d.ts.map +1 -0
  36. package/dist/custody/socket.js +118 -0
  37. package/dist/custody/socket.js.map +1 -0
  38. package/dist/evaluation-commitments.d.ts +47 -0
  39. package/dist/evaluation-commitments.d.ts.map +1 -0
  40. package/dist/evaluation-commitments.js +96 -0
  41. package/dist/evaluation-commitments.js.map +1 -0
  42. package/dist/index.d.ts +6 -2
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/mcp/cli.d.ts +3 -0
  47. package/dist/mcp/cli.d.ts.map +1 -0
  48. package/dist/mcp/cli.js +36 -0
  49. package/dist/mcp/cli.js.map +1 -0
  50. package/dist/mcp/evaluation.d.ts +33 -0
  51. package/dist/mcp/evaluation.d.ts.map +1 -0
  52. package/dist/mcp/evaluation.js +141 -0
  53. package/dist/mcp/evaluation.js.map +1 -0
  54. package/dist/mcp/server.d.ts +27 -0
  55. package/dist/mcp/server.d.ts.map +1 -0
  56. package/dist/mcp/server.js +207 -0
  57. package/dist/mcp/server.js.map +1 -0
  58. package/dist/policy.d.ts.map +1 -1
  59. package/dist/policy.js +7 -2
  60. package/dist/policy.js.map +1 -1
  61. package/dist/service-fees.d.ts +15 -0
  62. package/dist/service-fees.d.ts.map +1 -0
  63. package/dist/service-fees.js +109 -0
  64. package/dist/service-fees.js.map +1 -0
  65. package/dist/types.d.ts +66 -2
  66. package/dist/types.d.ts.map +1 -1
  67. package/docs/ARCHITECTURE.md +18 -2
  68. package/docs/EXISTING_AGENT.md +123 -0
  69. package/docs/HERMES_BUYER.md +91 -0
  70. package/docs/HERMES_CHECKPOINTS.md +88 -0
  71. package/docs/HERMES_CUSTODY.md +164 -0
  72. package/docs/HERMES_MCP.md +157 -0
  73. package/docs/HERMES_PROVIDER.md +98 -0
  74. package/docs/RELEASE_0.8.0_RC1.md +60 -0
  75. package/docs/SERVICE_FEES.md +108 -0
  76. package/docs/TESTNET_QUICKSTART.md +123 -0
  77. package/docs/VALIDATION_AND_RELEASE.md +82 -0
  78. package/docs/WALLET_SIGNER_SETUP.md +52 -0
  79. package/examples/service-fees.ts +36 -0
  80. package/examples/testnet-journal.ts +57 -0
  81. package/examples/testnet-lifecycle.ts +188 -294
  82. package/examples/testnet.env.example +12 -14
  83. package/package.json +7 -1
@@ -0,0 +1,52 @@
1
+ # Operator-owned wallet and signer preparation
2
+
3
+ This is a separate operator checklist, **not a Nayori wallet-generation or custody service**.
4
+ Your existing agent and LLM are already running. Return to [agent onboarding](EXISTING_AGENT.md)
5
+ after preparing a signer. Nayori never needs your private key or model-provider credentials.
6
+
7
+ ## Existing wallet or new wallet
8
+
9
+ Reuse a dedicated testnet signer if it meets these controls. Otherwise create a dedicated wallet
10
+ using your reviewed Stacks-compatible wallet tooling or a separately maintained Stacks.js operator
11
+ utility. Verify the dependency/version and network; do not ask an LLM to invent a key or use a
12
+ public test fixture. Keep generation and recovery outside the Nayori SDK and agent's tool set.
13
+
14
+ 1. Store the key in your own protected signer storage, outside repositories and agent-readable
15
+ files. Encrypt backups under operator control. A mode-0600 file alone is not an encrypted backup.
16
+ 2. Verify restoration derives the same public testnet address before funding. Never show the key
17
+ or recovery phrase in tickets, prompts, logs, terminal recordings or documentation.
18
+ 3. Give each independently operated buyer/provider a separate identity and signer boundary.
19
+ Keep transaction sender and the intended registered metadata wallet aligned; registry ownership
20
+ follows `creator` (transaction sender), not an arbitrary metadata wallet.
21
+ 4. Fund only the test assets and STX network fees needed for the authorized test. Do not send
22
+ mainnet assets to an example/fixture or assume a displayed testnet balance is spendable mainnet value.
23
+
24
+ ## Human-approved or autonomous signatures
25
+
26
+ - A browser wallet can request human approval through the SDK's browser signer interface.
27
+ - An autonomous agent requests a bounded operation from **your own signer service**. An
28
+ operator-maintained Stacks.js implementation can validate, construct and sign transactions.
29
+ This library option is not a turnkey managed signing service supplied by Nayori.
30
+ - Integrate your signer through the documented `PerkOSSigner` interface or the reviewed QA
31
+ custody bridge. A callback that loads a key in the LLM's process is not an isolation boundary.
32
+
33
+ Before enabling autonomous signing, enforce:
34
+
35
+ - Authenticated caller and role; allowed network, contracts, functions and recipients.
36
+ - Explicit per-operation amount, total budget, expiry, asset and STX gas caps.
37
+ - Exact arguments/post-conditions; no arbitrary transaction bytes or module paths selected by an LLM.
38
+ - Separate OS identity/process and private mounts; no shared key access, sudo or Docker socket.
39
+ - Durable intent/nonce/txid journal and serialized use of one wallet across workers.
40
+ - Stop on ambiguous signing/broadcast; inspect chain/mempool before retrying, never delete the journal.
41
+ - An operator-controlled disable switch; test denied actions with signing off first.
42
+
43
+ SDK spending policies are per-process checks, not a replacement for persistent signer-side
44
+ authorization. The candidate [custody guide](HERMES_CUSTODY.md) documents a restricted QA example;
45
+ it is not a general-purpose production custody service or externally audited wallet manager.
46
+
47
+ ## Safe handoff to the agent
48
+
49
+ Provide only public network/identity/configuration and the restricted tool or authenticated IPC
50
+ entry point. Keep signer credentials out of prompts and tool arguments. Prove that an allowed
51
+ testnet registration can be requested, a forbidden role/action is denied and the agent cannot
52
+ read key material. Then continue with [registration and confirmation](EXISTING_AGENT.md#4-register-and-verify-the-agent).
@@ -0,0 +1,36 @@
1
+ import {
2
+ PerkOSClient,
3
+ quoteServiceFee,
4
+ type AmountLike,
5
+ type PaymentAsset,
6
+ } from "../src/index.js";
7
+
8
+ /** Signer-free report. It neither grants consent nor broadcasts a transaction. */
9
+ export async function inspectEarnedServiceFee(
10
+ client: PerkOSClient,
11
+ asset: PaymentAsset,
12
+ jobId: AmountLike
13
+ ) {
14
+ if (!client.supportsServiceFees(asset))
15
+ return { kind: "no-service-fee-generation" as const };
16
+ const job = await client.getJob(asset, jobId);
17
+ if (!job) throw new Error("Job not found");
18
+ const fees = await client.getJobServiceFee(asset, jobId);
19
+ const quote = quoteServiceFee(job.budget);
20
+ return {
21
+ kind: "earned-service-fee" as const,
22
+ asset,
23
+ jobId: job.id.toString(),
24
+ gross: quote.gross.toString(),
25
+ potentialFee: quote.fee.toString(),
26
+ netIfEvaluated: (fees.waiver ? quote.gross : quote.net).toString(),
27
+ treasury: fees.treasury,
28
+ chargedFee: (fees.settlement?.chargedFee ?? 0n).toString(),
29
+ refundedFee: (fees.settlement?.refundedFee ?? 0n).toString(),
30
+ refundOutstanding:
31
+ !!fees.waiver &&
32
+ !!fees.settlement &&
33
+ fees.settlement.chargedFee > fees.settlement.refundedFee,
34
+ gas: "Separate STX network fee",
35
+ };
36
+ }
@@ -0,0 +1,57 @@
1
+ import { closeSync, constants, fstatSync, fsyncSync, openSync, readFileSync, writeSync } from "node:fs";
2
+ import { createHash } from "node:crypto";
3
+ import { isAbsolute } from "node:path";
4
+
5
+ type Entry = { key: string; intent: string; state: "attempting" | "broadcast"; txid?: string };
6
+
7
+ /** Append-only journal; caller must hold its exclusive lock. Keep outside Git. */
8
+ export class QuickstartJournal {
9
+ private readonly fd: number;
10
+ private readonly entries: Entry[];
11
+ constructor(path: string) {
12
+ if (!isAbsolute(path)) throw new Error("journal_absolute_path_required");
13
+ this.fd = openSync(path, constants.O_RDWR | constants.O_APPEND | constants.O_CREAT | constants.O_NOFOLLOW, 0o600);
14
+ const stat = fstatSync(this.fd);
15
+ if (!stat.isFile() || (stat.mode & 0o777) !== 0o600 || stat.nlink !== 1 || stat.size > 1_048_576 ||
16
+ (process.getuid && stat.uid !== process.getuid())) {
17
+ closeSync(this.fd); throw new Error("unsafe_journal");
18
+ }
19
+ try {
20
+ this.entries = readFileSync(this.fd, "utf8").split("\n").filter(Boolean).map(line => JSON.parse(line) as Entry);
21
+ if (this.entries.some(item => !item || typeof item.key !== "string" || !/^[0-9a-f]{64}$/.test(item.intent) ||
22
+ !["attempting", "broadcast"].includes(item.state) ||
23
+ (item.state === "broadcast" && !/^0x[0-9a-f]{64}$/.test(item.txid ?? "")))) throw new Error("invalid_journal");
24
+ } catch { closeSync(this.fd); throw new Error("journal_requires_reconciliation"); }
25
+ }
26
+ close() { closeSync(this.fd); }
27
+ lookup(key: string, intent: string): Entry | undefined {
28
+ const entries = this.entries.filter(item => item.key === key);
29
+ if (entries.some(item => item.intent !== intent)) throw new Error("journal_intent_mismatch");
30
+ return entries.at(-1);
31
+ }
32
+ append(entry: Entry) {
33
+ const bytes = Buffer.from(JSON.stringify(entry) + "\n");
34
+ let offset = 0;
35
+ while (offset < bytes.length) offset += writeSync(this.fd, bytes, offset);
36
+ fsyncSync(this.fd); this.entries.push(entry);
37
+ }
38
+ }
39
+ export function intentHash(value: unknown): string {
40
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
41
+ }
42
+ export async function checkpointedTransaction(
43
+ journal: QuickstartJournal, key: string, intent: string, send: () => Promise<string>,
44
+ ): Promise<string> {
45
+ const prior = journal.lookup(key, intent);
46
+ if (prior) {
47
+ if (prior.state !== "broadcast" || !/^0x[0-9a-f]{64}$/.test(prior.txid ?? "")) {
48
+ throw new Error("ambiguous_attempt_requires_reconciliation");
49
+ }
50
+ return prior.txid!;
51
+ }
52
+ journal.append({ key, intent, state: "attempting" });
53
+ const txid = await send();
54
+ if (!/^0x[0-9a-f]{64}$/.test(txid)) throw new Error("invalid_txid_requires_reconciliation");
55
+ journal.append({ key, intent, state: "broadcast", txid });
56
+ return txid;
57
+ }