@fased/fased 0.1.68 → 0.1.69

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 (103) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +20 -22
  3. package/config/sat-runtime.env +6 -2
  4. package/config/signer-protocol-v2.json +6 -0
  5. package/dist/agents/tools/wallet-action-tool.js +2 -0
  6. package/dist/agents/tools/wallet-tool.js +5 -3
  7. package/dist/brand.js +1 -1
  8. package/dist/build-info.json +3 -3
  9. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  10. package/dist/cli/program/register.wallet.js +88 -5
  11. package/dist/commands/wallet.js +842 -57
  12. package/dist/config/sat-runtime-ids.js +6 -1
  13. package/dist/control-ui/assets/{app-DyPTS0oM.js → app-BLFLoFPk.js} +8 -8
  14. package/dist/control-ui/assets/{federation-DgA9e-Pn.js → federation-SGMAnM_8.js} +2 -2
  15. package/dist/control-ui/assets/{index-Cu13yYHr.js → index-S88kp5ZQ.js} +3 -3
  16. package/dist/control-ui/assets/{mining-Dg-3ll0F.js → mining-ClI8RW4u.js} +2 -2
  17. package/dist/control-ui/assets/{wallet-ByKBcYhm.js → wallet-CRIhDQbW.js} +201 -116
  18. package/dist/control-ui/index.html +1 -1
  19. package/dist/control-ui/version.json +1 -1
  20. package/dist/gateway/server-http.js +46 -9
  21. package/dist/mining/mainnet-sync.js +67 -5
  22. package/dist/operator/operator-readiness.js +1 -1
  23. package/dist/plugin-sdk/{audio-preflight-_FVQjR1r.js → audio-preflight-CS7Q_7-R.js} +2 -2
  24. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  25. package/dist/plugin-sdk/{chunk-items-LH4uKSnh.js → chunk-items-BLBZNORg.js} +1 -1
  26. package/dist/plugin-sdk/command-status.js +2 -2
  27. package/dist/plugin-sdk/{config-schema-DjU_uD-6.js → config-schema-BiBOUW89.js} +255 -47
  28. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  29. package/dist/plugin-sdk/discord.d.ts +4 -4
  30. package/dist/plugin-sdk/index.d.ts +16 -16
  31. package/dist/plugin-sdk/index.js +2 -2
  32. package/dist/plugin-sdk/{monitor-DUMMbG4h.js → monitor-WXdn4mR-.js} +4 -4
  33. package/dist/plugin-sdk/{monitor-DXIM16Hj.js → monitor-xzVAPbg2.js} +6 -6
  34. package/dist/plugin-sdk/{openresponses-http-C8lKwta1.js → openresponses-http-DnI1LFgq.js} +2 -2
  35. package/dist/plugin-sdk/{pairing-message-mKO92L-M.d.ts → pairing-message-CiTcdD6m.d.ts} +1 -1
  36. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  37. package/dist/plugin-sdk/{pw-ai-DJ_gTc4M.js → pw-ai-DZYLDC-S.js} +1 -1
  38. package/dist/plugin-sdk/{runtime-dependency-nup8niQL.js → runtime-dependency-DczkvPxn.js} +9 -9
  39. package/dist/plugin-sdk/sat-runtime.d.ts +89 -10
  40. package/dist/plugin-sdk/sat-runtime.js +2 -2
  41. package/dist/plugin-sdk/{slack-B-nOOLM0.js → slack-CkIRVDUK.js} +4 -4
  42. package/dist/plugin-sdk/slack.d.ts +4 -4
  43. package/dist/plugin-sdk/{sticker-cache-BLiId8Em.js → sticker-cache-Dpap5ce_.js} +2 -2
  44. package/dist/plugin-sdk/{telegram-actions-1X9A32Dn.js → telegram-actions-Cewdga8H.js} +3 -3
  45. package/dist/plugin-sdk/telegram.d.ts +4 -4
  46. package/dist/plugin-sdk/{tmp-fased-dir-BkpWlGUe.d.ts → tmp-fased-dir-BSEHc8MQ.d.ts} +2 -2
  47. package/dist/plugin-sdk/{types-DtCOO_hW.d.ts → types-BakzIzGN.d.ts} +1 -1
  48. package/dist/plugin-sdk/{types-B-zL9gSv.d.ts → types-CzF0YQqK.d.ts} +2 -2
  49. package/dist/plugin-sdk/{web-BvjNyYBz.js → web-DGWi7i3B.js} +2 -2
  50. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  51. package/dist/plugin-sdk/{zod-schema.providers-core-BCDlgtls.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +63 -63
  52. package/dist/wallet/local-socket-signer-lifecycle.js +79 -3
  53. package/dist/wallet/local-socket-signer-protocol.js +70 -10
  54. package/dist/wallet/mining-wallet-retirement.js +256 -0
  55. package/dist/wallet/providers/local-socket-signer-adapter.js +2 -0
  56. package/dist/wallet/signer-protocol-v2.generated.js +6 -0
  57. package/dist/wallet/wallet-agent-selection.js +6 -10
  58. package/dist/wallet/wallet-provider-registry.js +70 -29
  59. package/dist/wallet/wallet-send-approvals.js +6 -2
  60. package/dist/wallet/wallet-status.js +56 -1
  61. package/dist/wizard/onboarding.js +32 -30
  62. package/dist/wizard/onboarding.wallet.js +10 -1
  63. package/extensions/acpx/package.json +1 -1
  64. package/extensions/bluebubbles/package.json +1 -1
  65. package/extensions/copilot-proxy/package.json +1 -1
  66. package/extensions/diagnostics-otel/package.json +1 -1
  67. package/extensions/fased-federation/package.json +1 -1
  68. package/extensions/google-gemini-cli-auth/package.json +1 -1
  69. package/extensions/imessage/package.json +1 -1
  70. package/extensions/irc/package.json +1 -1
  71. package/extensions/line/package.json +1 -1
  72. package/extensions/llm-task/package.json +1 -1
  73. package/extensions/lobster/package.json +1 -1
  74. package/extensions/matrix/CHANGELOG.md +6 -0
  75. package/extensions/matrix/package.json +1 -1
  76. package/extensions/mattermost/package.json +1 -1
  77. package/extensions/memory-core/package.json +2 -2
  78. package/extensions/memory-lancedb/package.json +1 -1
  79. package/extensions/minimax-portal-auth/package.json +1 -1
  80. package/extensions/msteams/CHANGELOG.md +6 -0
  81. package/extensions/msteams/package.json +1 -1
  82. package/extensions/nextcloud-talk/package.json +1 -1
  83. package/extensions/nostr/CHANGELOG.md +6 -0
  84. package/extensions/nostr/package.json +1 -1
  85. package/extensions/open-prose/package.json +1 -1
  86. package/extensions/sat-mining/package.json +1 -1
  87. package/extensions/signal/package.json +1 -1
  88. package/extensions/synology-chat/package.json +1 -1
  89. package/extensions/tlon/package.json +1 -1
  90. package/extensions/twitch/CHANGELOG.md +6 -0
  91. package/extensions/twitch/package.json +1 -1
  92. package/extensions/voice-call/CHANGELOG.md +6 -0
  93. package/extensions/voice-call/package.json +1 -1
  94. package/extensions/zalo/CHANGELOG.md +6 -0
  95. package/extensions/zalo/package.json +1 -1
  96. package/extensions/zalouser/CHANGELOG.md +6 -0
  97. package/extensions/zalouser/package.json +1 -1
  98. package/install.sh +276 -103
  99. package/package.json +4 -3
  100. package/scripts/install-fased-signerd.sh +4 -8
  101. package/scripts/install-runtime-profile.sh +27 -0
  102. package/scripts/start-managed.sh +13 -1
  103. package/scripts/fased-signer-wallet-import-hosting.sh +0 -78
@@ -10,7 +10,7 @@ function lockedLocalSignerPolicy(role) {
10
10
  assets: []
11
11
  };
12
12
  }
13
- async function requireSignerOwnedProtocolV2(socketPath) {
13
+ async function requireSignerOwnedProtocolV2(socketPath, requiredFeatures = []) {
14
14
  const result = await callLocalSocketSigner(socketPath, { op: "v2.capabilities" });
15
15
  const capabilities = result.capabilities;
16
16
  const required = [
@@ -19,13 +19,89 @@ async function requireSignerOwnedProtocolV2(socketPath) {
19
19
  "signerOwnedKeys",
20
20
  "applicationNetworkBootstrap",
21
21
  "atomicMultiAssetCaps",
22
- "signerControlledNativeFeeCaps"
22
+ "signerControlledNativeFeeCaps",
23
+ ...requiredFeatures
23
24
  ];
24
25
  const missing = required.filter((feature) => !capabilities?.features.includes(feature));
25
26
  if (result.ready !== true || capabilities?.protocol.current !== 2 || capabilities.protocol.min > 2 || capabilities.protocol.max < 2 || capabilities.nativeFeeReservationLamports !== LOCAL_SIGNER_NATIVE_FEE_RESERVATION_LAMPORTS_V2 || missing.length > 0) {
26
27
  throw new Error(`fased-signerd does not provide the required signer-owned protocol-v2 capabilities${missing.length > 0 ? `: ${missing.join(", ")}` : ""}`);
27
28
  }
28
29
  }
30
+ function assertRoleBaselineRecord(record, role) {
31
+ if (record.policy.role !== role) {
32
+ throw new Error(`signer-owned wallet ${record.wallet.walletId} has role=${record.policy.role}, not ${role}`);
33
+ }
34
+ if (record.policy.baselineVersion !== 1 || record.policy.operations.length === 0 || record.policy.programs.length === 0 || record.policy.assets.length === 0) {
35
+ throw new Error(`signer-owned wallet ${record.wallet.walletId} is not role-ready; select Activate role baseline before using it`);
36
+ }
37
+ return record;
38
+ }
39
+ async function createRoleReadySignerOwnedWallet(params) {
40
+ const walletId = params.walletId.trim();
41
+ if (!/^[a-zA-Z0-9_-]+$/.test(walletId)) {
42
+ throw new Error("walletId must contain only letters, numbers, hyphens, or underscores");
43
+ }
44
+ await requireSignerOwnedProtocolV2(params.socketPath, ["signerOwnedRoleBaselines"]);
45
+ try {
46
+ const existing = await readSignerOwnedWallet({
47
+ socketPath: params.socketPath,
48
+ walletId
49
+ });
50
+ if (!params.allowExisting) {
51
+ throw new Error(`signer-owned wallet already exists: ${walletId}`);
52
+ }
53
+ return assertRoleBaselineRecord(existing, params.role);
54
+ } catch (error) {
55
+ const message = error instanceof Error ? error.message : String(error);
56
+ if (message.startsWith("signer-owned wallet")) {
57
+ throw error;
58
+ }
59
+ }
60
+ try {
61
+ const created = await callLocalSocketSigner(params.socketPath, {
62
+ op: "v2.wallet.create",
63
+ walletId,
64
+ request: {
65
+ expectedPolicyVersion: 0,
66
+ baseline: {
67
+ version: 1,
68
+ role: params.role
69
+ }
70
+ }
71
+ });
72
+ return assertRoleBaselineRecord(created, params.role);
73
+ } catch (error) {
74
+ if (!params.allowExisting) {
75
+ throw error;
76
+ }
77
+ const existing = await readSignerOwnedWallet({
78
+ socketPath: params.socketPath,
79
+ walletId
80
+ });
81
+ return assertRoleBaselineRecord(existing, params.role);
82
+ }
83
+ }
84
+ async function activateSignerOwnedRoleBaseline(params) {
85
+ await requireSignerOwnedProtocolV2(params.socketPath, ["signerOwnedRoleBaselines"]);
86
+ return await callLocalSocketSigner(params.socketPath, {
87
+ op: "v2.policy.activateBaseline",
88
+ walletId: params.walletId,
89
+ request: {
90
+ expectedPolicyVersion: params.expectedPolicyVersion,
91
+ baseline: {
92
+ version: 1,
93
+ role: params.role
94
+ }
95
+ }
96
+ });
97
+ }
98
+ async function readSignerOwnedWalletReadiness(params) {
99
+ await requireSignerOwnedProtocolV2(params.socketPath, ["liveWalletReadiness"]);
100
+ return await callLocalSocketSigner(params.socketPath, {
101
+ op: "v2.wallet.readiness",
102
+ walletId: params.walletId
103
+ });
104
+ }
29
105
  async function readSignerOwnedWallet(params) {
30
106
  const [wallet, policy] = await Promise.all([callLocalSocketSigner(params.socketPath, {
31
107
  op: "v2.wallet.get",
@@ -122,4 +198,4 @@ async function configureSignerOwnedWalletPrimaryRpc(params) {
122
198
  }
123
199
 
124
200
  //#endregion
125
- export { configureSignerOwnedWalletPrimaryRpc, createLockedSignerOwnedWallet };
201
+ export { activateSignerOwnedRoleBaseline, configureSignerOwnedWalletPrimaryRpc, createRoleReadySignerOwnedWallet, readSignerOwnedWallet, readSignerOwnedWalletReadiness };
@@ -36,14 +36,18 @@ const SignerPolicyAssetV2Schema = Type.Object({
36
36
  asset: Type.String(),
37
37
  destinations: Type.Array(Type.String()),
38
38
  maxPerTx: Type.String(),
39
- maxDaily: Type.String()
39
+ maxDaily: Type.String(),
40
+ reviewedDestinations: Type.Optional(Type.Boolean()),
41
+ typedSatDestinations: Type.Optional(Type.Boolean())
40
42
  }, { additionalProperties: false });
41
43
  const SignerPolicyInputV2Schema = Type.Object({
42
44
  walletId: Type.Optional(Type.String()),
43
45
  role: SignerWalletRoleSchema,
44
46
  version: Type.Optional(Type.Integer({ minimum: 0 })),
47
+ baselineVersion: Type.Optional(Type.Integer({ minimum: 1 })),
45
48
  operations: Type.Array(Type.String()),
46
49
  programs: Type.Array(Type.String()),
50
+ typedSatPrograms: Type.Optional(Type.Boolean()),
47
51
  assets: Type.Array(SignerPolicyAssetV2Schema),
48
52
  hash: Type.Optional(Type.String())
49
53
  }, { additionalProperties: false });
@@ -51,8 +55,10 @@ const LocalSocketSignerPolicyV2Schema = Type.Object({
51
55
  walletId: Type.String(),
52
56
  role: SignerWalletRoleSchema,
53
57
  version: Type.Integer({ minimum: 1 }),
58
+ baselineVersion: Type.Optional(Type.Integer({ minimum: 1 })),
54
59
  operations: Type.Array(Type.String()),
55
60
  programs: Type.Array(Type.String()),
61
+ typedSatPrograms: Type.Optional(Type.Boolean()),
56
62
  assets: Type.Array(SignerPolicyAssetV2Schema),
57
63
  hash: Type.String({ pattern: "^sha256:[0-9a-f]{64}$" })
58
64
  }, { additionalProperties: false });
@@ -238,10 +244,13 @@ const SignerSolanaTransactionEnvelopeV2Schema = Type.Object({
238
244
  }),
239
245
  submission: Type.Literal("rpc")
240
246
  }, { additionalProperties: false });
241
- const SignerReviewAuthorizationV2Schema = Type.Object({
247
+ const SignerReviewAuthorizationV2Schema = Type.Union([Type.Object({
242
248
  type: Type.Literal("webauthn"),
243
249
  proof: Type.Object({ proofId: Type.String({ minLength: 1 }) }, { additionalProperties: false })
244
- }, { additionalProperties: false });
250
+ }, { additionalProperties: false }), Type.Object({
251
+ type: Type.Literal("control-ui"),
252
+ proof: Type.Object({ proofId: Type.String({ pattern: "^[0-9a-f]{64}$" }) }, { additionalProperties: false })
253
+ }, { additionalProperties: false })]);
245
254
  const LocalSocketSignerJupiterTriggerHistoryV2Schema = Type.Object({ orders: Type.Array(Type.Object({
246
255
  orderId: Type.String({ minLength: 1 }),
247
256
  orderState: Type.String({ minLength: 1 }),
@@ -266,10 +275,17 @@ const LocalSocketSignerJupiterTriggerHistoryV2Schema = Type.Object({ orders: Typ
266
275
  program: Type.String({ minLength: 1 })
267
276
  }, { additionalProperties: false }))
268
277
  }, { additionalProperties: false })) }, { additionalProperties: false });
269
- const SignerWalletPolicyCreateV2Schema = Type.Object({
278
+ const SignerRoleBaselineV1Schema = Type.Object({
279
+ version: Type.Literal(1),
280
+ role: SignerWalletRoleSchema
281
+ }, { additionalProperties: false });
282
+ const SignerWalletPolicyCreateV2Schema = Type.Union([Type.Object({
270
283
  expectedPolicyVersion: Type.Literal(0),
271
284
  policy: SignerPolicyInputV2Schema
272
- }, { additionalProperties: false });
285
+ }, { additionalProperties: false }), Type.Object({
286
+ expectedPolicyVersion: Type.Literal(0),
287
+ baseline: SignerRoleBaselineV1Schema
288
+ }, { additionalProperties: false })]);
273
289
  const SignerOperationLookupV2Schema = Type.Object({ requestId: Type.String() }, { additionalProperties: false });
274
290
  const SignerSatLookupBindingRequestV2Schema = Type.Object({
275
291
  cycleId: Type.String({ pattern: "^(0|[1-9][0-9]*)$" }),
@@ -348,6 +364,18 @@ const LocalSocketSignerRequestSchema = Type.Union([
348
364
  op: Type.Literal("v2.wallet.get"),
349
365
  walletId: Type.String()
350
366
  }, { additionalProperties: false }),
367
+ Type.Object({
368
+ op: Type.Literal("v2.wallet.readiness"),
369
+ walletId: Type.String()
370
+ }, { additionalProperties: false }),
371
+ Type.Object({
372
+ op: Type.Literal("v2.policy.activateBaseline"),
373
+ walletId: Type.String(),
374
+ request: Type.Object({
375
+ expectedPolicyVersion: Type.Integer({ minimum: 1 }),
376
+ baseline: SignerRoleBaselineV1Schema
377
+ }, { additionalProperties: false })
378
+ }, { additionalProperties: false }),
351
379
  Type.Object({
352
380
  op: Type.Literal("v2.wallet.create"),
353
381
  walletId: Type.String(),
@@ -356,21 +384,30 @@ const LocalSocketSignerRequestSchema = Type.Union([
356
384
  Type.Object({
357
385
  op: Type.Literal("v2.wallet.import"),
358
386
  walletId: Type.String(),
359
- request: Type.Object({
387
+ request: Type.Union([Type.Object({
360
388
  expectedPolicyVersion: Type.Literal(0),
361
389
  policy: SignerPolicyInputV2Schema,
362
390
  path: Type.String()
363
- }, { additionalProperties: false })
391
+ }, { additionalProperties: false }), Type.Object({
392
+ expectedPolicyVersion: Type.Literal(0),
393
+ baseline: SignerRoleBaselineV1Schema,
394
+ path: Type.String()
395
+ }, { additionalProperties: false })])
364
396
  }, { additionalProperties: false }),
365
397
  Type.Object({
366
398
  op: Type.Literal("v2.wallet.importLegacy"),
367
399
  walletId: Type.String(),
368
- request: Type.Object({
400
+ request: Type.Union([Type.Object({
369
401
  expectedPolicyVersion: Type.Literal(0),
370
402
  policy: SignerPolicyInputV2Schema,
371
403
  path: Type.String(),
372
404
  passphrasePath: Type.String()
373
- }, { additionalProperties: false })
405
+ }, { additionalProperties: false }), Type.Object({
406
+ expectedPolicyVersion: Type.Literal(0),
407
+ baseline: SignerRoleBaselineV1Schema,
408
+ path: Type.String(),
409
+ passphrasePath: Type.String()
410
+ }, { additionalProperties: false })])
374
411
  }, { additionalProperties: false }),
375
412
  Type.Object({
376
413
  op: Type.Literal("v2.wallet.reencrypt"),
@@ -650,6 +687,27 @@ const LocalSocketSignerWalletPolicyResultV2Schema = Type.Object({
650
687
  wallet: LocalSocketSignerWalletV2Schema,
651
688
  policy: LocalSocketSignerPolicyV2Schema
652
689
  }, { additionalProperties: false });
690
+ const LocalSocketSignerWalletReadinessV2Schema = Type.Object({
691
+ walletId: Type.String({ minLength: 1 }),
692
+ publicKey: Type.String({ minLength: 1 }),
693
+ walletVersion: Type.Optional(Type.Integer({ minimum: 1 })),
694
+ role: SignerWalletRoleSchema,
695
+ baselineVersion: Type.Integer({ minimum: 0 }),
696
+ policyVersion: Type.Integer({ minimum: 1 }),
697
+ policyHash: Type.String({ pattern: "^sha256:[0-9a-f]{64}$" }),
698
+ networkVersion: Type.Integer({ minimum: 0 }),
699
+ networkHash: Type.Optional(Type.String({ pattern: "^hmac-sha256:[0-9a-f]{64}$" })),
700
+ keyReady: Type.Boolean(),
701
+ policyReady: Type.Boolean(),
702
+ networkReady: Type.Boolean(),
703
+ operationLane: Type.Union([
704
+ Type.Literal("blocked"),
705
+ Type.Literal("agent-reviewed-and-autonomous"),
706
+ Type.Literal("mining-typed-sat"),
707
+ Type.Literal("vault-reviewed-only")
708
+ ]),
709
+ ready: Type.Boolean()
710
+ }, { additionalProperties: false });
653
711
  function isPositiveUnsignedInteger(value) {
654
712
  return typeof value === "string" && /^[1-9][0-9]*$/.test(value);
655
713
  }
@@ -694,9 +752,11 @@ function validateLocalSocketSignerResult(op, result) {
694
752
  case "v2.network.bootstrap": return Value.Check(LocalSocketSignerNetworkSummaryV2Schema, result);
695
753
  case "v2.policy.get":
696
754
  case "v2.policy.put":
697
- case "v2.policy.tighten": return Value.Check(LocalSocketSignerPolicyV2Schema, result);
755
+ case "v2.policy.tighten":
756
+ case "v2.policy.activateBaseline": return Value.Check(LocalSocketSignerPolicyV2Schema, result);
698
757
  case "v2.wallet.get":
699
758
  case "v2.wallet.reencrypt": return Value.Check(LocalSocketSignerWalletV2Schema, result);
759
+ case "v2.wallet.readiness": return Value.Check(LocalSocketSignerWalletReadinessV2Schema, result);
700
760
  case "v2.wallet.create":
701
761
  case "v2.wallet.import":
702
762
  case "v2.wallet.importLegacy": return Value.Check(LocalSocketSignerWalletPolicyResultV2Schema, result);
@@ -0,0 +1,256 @@
1
+ import { resolveStateDir } from "../config/paths.js";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { createHash, randomBytes } from "node:crypto";
5
+
6
+ //#region src/wallet/mining-wallet-retirement.ts
7
+ function record(value) {
8
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
9
+ }
10
+ function decimal(value, field) {
11
+ const normalized = typeof value === "string" ? value.trim() : "";
12
+ if (!/^\d+$/u.test(normalized)) {
13
+ throw new Error(`Mining retirement cannot verify ${field}`);
14
+ }
15
+ return normalized;
16
+ }
17
+ function nonNegativeCount(value) {
18
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : 0;
19
+ }
20
+ function sha256(raw) {
21
+ return `sha256:${createHash("sha256").update(raw).digest("hex")}`;
22
+ }
23
+ function base64UrlByteLength(value) {
24
+ if (typeof value !== "string" || !/^[A-Za-z0-9_-]+$/u.test(value)) {
25
+ return -1;
26
+ }
27
+ try {
28
+ return Buffer.from(value, "base64url").byteLength;
29
+ } catch {
30
+ return -1;
31
+ }
32
+ }
33
+ function normalizedWalletStateKey(walletId) {
34
+ return walletId.replace(/[^a-zA-Z0-9._-]+/gu, "_") || "unattached";
35
+ }
36
+ function readStrictJSONFile(filePath, maxBytes) {
37
+ const info = fs.lstatSync(filePath);
38
+ if (!info.isFile() || info.isSymbolicLink() || info.size <= 0 || info.size > maxBytes) {
39
+ throw new Error(`Mining retirement evidence file is invalid: ${filePath}`);
40
+ }
41
+ const raw = fs.readFileSync(filePath);
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(raw.toString("utf8"));
45
+ } catch {
46
+ throw new Error(`Mining retirement evidence file is not valid JSON: ${filePath}`);
47
+ }
48
+ const value = record(parsed);
49
+ if (!value) {
50
+ throw new Error(`Mining retirement evidence file must contain one JSON object: ${filePath}`);
51
+ }
52
+ return {
53
+ raw,
54
+ value
55
+ };
56
+ }
57
+ function unwrapMiningGatewayPayload(value) {
58
+ let current = value;
59
+ for (let index = 0; index < 4; index += 1) {
60
+ const currentRecord = record(current);
61
+ if (!currentRecord) {
62
+ break;
63
+ }
64
+ if (record(currentRecord.status)) {
65
+ return record(currentRecord.status);
66
+ }
67
+ if (!("payload" in currentRecord)) {
68
+ return currentRecord;
69
+ }
70
+ current = currentRecord.payload;
71
+ }
72
+ return record(current) ?? {};
73
+ }
74
+ function verifyMiningRecoveryPackage(params) {
75
+ const recoveryFile = params.recoveryFile.trim();
76
+ if (!path.isAbsolute(recoveryFile) || path.resolve(recoveryFile) !== recoveryFile) {
77
+ throw new Error("--recovery-file must be an absolute clean path");
78
+ }
79
+ const info = fs.lstatSync(recoveryFile);
80
+ if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.mode & 63) {
81
+ throw new Error("Mining recovery package must be one owner-only regular non-symlink file");
82
+ }
83
+ if (typeof process.getuid === "function" && info.uid !== process.getuid()) {
84
+ throw new Error("Mining recovery package must be owned by the current terminal user");
85
+ }
86
+ const { raw, value } = readStrictJSONFile(recoveryFile, 16 * 1024);
87
+ const kdf = record(value.kdf);
88
+ const encryption = record(value.encryption);
89
+ if (value.kind !== "fased-signer-wallet-recovery" || value.version !== 1 || value.walletId !== params.walletId || value.role !== "mining" || value.publicKey !== params.publicKey || typeof value.createdAt !== "string" || !Number.isFinite(Date.parse(value.createdAt)) || kdf?.name !== "argon2id" || kdf.memoryKiB !== 64 * 1024 || kdf.iterations !== 3 || kdf.parallelism !== 1 || base64UrlByteLength(kdf.salt) !== 16 || encryption?.name !== "aes-256-gcm" || base64UrlByteLength(encryption.nonce) !== 12 || base64UrlByteLength(encryption.ciphertext) !== 80) {
90
+ throw new Error("Encrypted recovery package does not match the source Mining wallet");
91
+ }
92
+ return {
93
+ packageHash: sha256(raw),
94
+ walletId: params.walletId,
95
+ publicKey: params.publicKey,
96
+ role: "mining"
97
+ };
98
+ }
99
+ function buildMiningRetirementEvidence(params) {
100
+ const env = params.env ?? process.env;
101
+ const status = unwrapMiningGatewayPayload(params.liveStatus);
102
+ if (typeof status.retirementGatewayError === "string") {
103
+ throw new Error(`Could not stop and inspect Mining: ${status.retirementGatewayError}`);
104
+ }
105
+ if (status.walletId !== params.walletId && status.walletId !== params.signerWalletId) {
106
+ throw new Error("Live Mining status is not bound to the source wallet");
107
+ }
108
+ if (status.statusFresh === false || status.degraded === true) {
109
+ throw new Error("Live Mining status is degraded; normal retirement refuses unverifiable state");
110
+ }
111
+ if (status.running === true || status.drainOnly === true || status.enabledWanted === true) {
112
+ throw new Error("Mining is still running or draining; retry retirement after Clearing finishes");
113
+ }
114
+ const workers = record(status.workers) ?? {};
115
+ if (Object.values(workers).some((worker) => record(worker)?.running === true)) {
116
+ throw new Error("A Mining worker is still running");
117
+ }
118
+ const solBalanceLamports = decimal(status.currentSolBalanceLamports, "live SOL balance");
119
+ const signerBalance = decimal(params.signerSolBalanceLamports, "signer SOL balance");
120
+ if (solBalanceLamports !== signerBalance) {
121
+ throw new Error("Live Mining and signer SOL balance observations disagree");
122
+ }
123
+ const satBalanceRaw = decimal(status.currentSatBalanceRaw, "live SAT balance");
124
+ for (const [field, value] of [
125
+ ["funded Mining capital", status.currentCapitalFundedLamports],
126
+ ["locked Mining capital", status.currentCapitalLockedLamports],
127
+ ["free Mining capital", status.currentCapitalFreeLamports]
128
+ ]) {
129
+ if (decimal(value, field) !== "0") {
130
+ throw new Error(`${field} must be zero before normal Mining retirement`);
131
+ }
132
+ }
133
+ if (nonNegativeCount(status.currentCapitalPendingCycleCount) !== 0 || nonNegativeCount(record(status.claimBacklog)?.total) !== 0 || nonNegativeCount(status.missingCycleCount) !== 0 || Array.isArray(status.pendingCycleIds) && status.pendingCycleIds.length > 0 || status.exactPendingCycleId != null) {
134
+ throw new Error("Mining still has pending cycles, claims, or reconciliation gaps");
135
+ }
136
+ const walletStateDir = path.join(resolveStateDir(env), "sat-mining", "wallets", normalizedWalletStateKey(params.walletId));
137
+ const runtime = readStrictJSONFile(path.join(walletStateDir, "runtime-store.json"), 16 * 1024 * 1024);
138
+ if (runtime.value.enabledWanted === true) {
139
+ throw new Error("Durable Mining state still permits new jobs");
140
+ }
141
+ const durableWorkers = record(runtime.value.workers) ?? {};
142
+ if (Object.values(durableWorkers).some((worker) => record(worker)?.running === true)) {
143
+ throw new Error("Durable Mining state still records a running worker");
144
+ }
145
+ if (Array.isArray(runtime.value.pendingPlannerCycles) && runtime.value.pendingPlannerCycles.length) {
146
+ throw new Error("Durable Mining planner work remains pending");
147
+ }
148
+ if (Array.isArray(runtime.value.claimBacklog) && runtime.value.claimBacklog.length) {
149
+ throw new Error("Durable Mining claim backlog remains pending");
150
+ }
151
+ const lastKnown = record(runtime.value.lastKnownStatus);
152
+ if (!lastKnown || lastKnown.walletId !== params.walletId && lastKnown.walletId !== params.signerWalletId) {
153
+ throw new Error("Durable Mining balance state is missing or belongs to another wallet");
154
+ }
155
+ if (decimal(lastKnown.currentCapitalFundedLamports, "durable funded capital") !== "0" || decimal(lastKnown.currentCapitalLockedLamports, "durable locked capital") !== "0" || decimal(lastKnown.currentCapitalFreeLamports, "durable free capital") !== "0" || nonNegativeCount(lastKnown.currentCapitalPendingCycleCount) !== 0 || lastKnown.exactPendingCycleId != null) {
156
+ throw new Error("Durable Mining state still contains pending capital or protocol work");
157
+ }
158
+ const ledgerPath = path.join(walletStateDir, "submission-ledger.json");
159
+ let ledgerRaw = Buffer.from("{\"version\":1,\"records\":{}}\n", "utf8");
160
+ let ledger = {
161
+ version: 1,
162
+ records: {}
163
+ };
164
+ if (fs.existsSync(ledgerPath)) {
165
+ const loaded = readStrictJSONFile(ledgerPath, 16 * 1024 * 1024);
166
+ ledgerRaw = loaded.raw;
167
+ ledger = loaded.value;
168
+ }
169
+ if (ledger.version !== 1 || !record(ledger.records)) {
170
+ throw new Error("Durable Mining submission ledger is invalid");
171
+ }
172
+ const pendingByKind = {
173
+ commit: 0,
174
+ reveal: 0,
175
+ settlement: 0,
176
+ claim: 0,
177
+ cleanup: 0,
178
+ alt: 0
179
+ };
180
+ for (const rawEntry of Object.values(record(ledger.records))) {
181
+ const entry = record(rawEntry);
182
+ if (!entry) {
183
+ throw new Error("Durable Mining submission ledger contains an invalid record");
184
+ }
185
+ const state = typeof entry.state === "string" ? entry.state : "";
186
+ if (![
187
+ "prepared",
188
+ "reserved",
189
+ "broadcast",
190
+ "unknown"
191
+ ].includes(state)) {
192
+ continue;
193
+ }
194
+ const action = typeof entry.action === "string" ? entry.action.toLowerCase() : "";
195
+ if (action.includes("commit")) {
196
+ pendingByKind.commit += 1;
197
+ } else if (action.includes("reveal") || action.includes("submit")) {
198
+ pendingByKind.reveal += 1;
199
+ } else if (action.includes("settle") || action.includes("score") || action.includes("distribute") || action.includes("finalize")) {
200
+ pendingByKind.settlement += 1;
201
+ } else if (action.includes("claim")) {
202
+ pendingByKind.claim += 1;
203
+ } else if (action.includes("lookup") || action.includes("alt")) {
204
+ pendingByKind.alt += 1;
205
+ } else {
206
+ pendingByKind.cleanup += 1;
207
+ }
208
+ }
209
+ if (Object.values(pendingByKind).some((count) => count > 0)) {
210
+ throw new Error("Reserved, broadcast, or unknown Mining submissions must be reconciled first");
211
+ }
212
+ const observedAt = typeof status.updatedAt === "string" ? status.updatedAt : typeof status.snapshotAt === "string" ? status.snapshotAt : typeof lastKnown.updatedAt === "string" ? lastKnown.updatedAt : "";
213
+ if (!observedAt || !Number.isFinite(Date.parse(observedAt))) {
214
+ throw new Error("Live Mining balance observation has no verifiable timestamp");
215
+ }
216
+ return {
217
+ version: 1,
218
+ walletId: params.signerWalletId,
219
+ publicKey: params.publicKey,
220
+ observedAt: new Date(observedAt).toISOString(),
221
+ newJobsStopped: true,
222
+ workersDrained: true,
223
+ clearingDrained: true,
224
+ submissionsReconciled: true,
225
+ pendingCommits: pendingByKind.commit,
226
+ pendingReveals: pendingByKind.reveal,
227
+ pendingSettlements: pendingByKind.settlement,
228
+ pendingClaims: pendingByKind.claim,
229
+ pendingCleanup: pendingByKind.cleanup,
230
+ pendingAltMutations: pendingByKind.alt,
231
+ solBalanceLamports,
232
+ satBalanceRaw,
233
+ runtimeStateHash: sha256(runtime.raw),
234
+ submissionLedgerHash: sha256(ledgerRaw)
235
+ };
236
+ }
237
+ function writeMiningRetirementReceipt(params) {
238
+ const root = path.join(resolveStateDir(params.env ?? process.env), "wallet-retirements");
239
+ fs.mkdirSync(root, {
240
+ recursive: true,
241
+ mode: 448
242
+ });
243
+ const filePath = path.join(root, `${normalizedWalletStateKey(params.sourceWalletId)}.json`);
244
+ const tempPath = `${filePath}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
245
+ fs.writeFileSync(tempPath, `${JSON.stringify(params.receipt, null, 2)}\n`, {
246
+ encoding: "utf8",
247
+ flag: "wx",
248
+ mode: 384
249
+ });
250
+ fs.renameSync(tempPath, filePath);
251
+ fs.chmodSync(filePath, 384);
252
+ return filePath;
253
+ }
254
+
255
+ //#endregion
256
+ export { buildMiningRetirementEvidence, verifyMiningRecoveryPackage, writeMiningRetirementReceipt };
@@ -20,7 +20,9 @@ const SIGNER_SOCKET_TIMEOUT_MS = {
20
20
  "v2.policy.get": 5e3,
21
21
  "v2.policy.put": 5e3,
22
22
  "v2.policy.tighten": 5e3,
23
+ "v2.policy.activateBaseline": 1e4,
23
24
  "v2.wallet.get": 5e3,
25
+ "v2.wallet.readiness": 5e3,
24
26
  "v2.wallet.create": 1e4,
25
27
  "v2.wallet.import": 2e4,
26
28
  "v2.wallet.importLegacy": 3e4,
@@ -35,12 +35,16 @@ const SIGNER_PROTOCOL_V2 = {
35
35
  "atomicIdempotency",
36
36
  "ambiguousBroadcastReconciliation",
37
37
  "signerOwnedKeys",
38
+ "signerOwnedRoleBaselines",
39
+ "liveWalletReadiness",
38
40
  "signerOwnedSuccessorRotation",
41
+ "signerOwnedMiningRetirementEvidence",
39
42
  "permanentRetiredWalletPolicies",
40
43
  "signerOwnedRPC",
41
44
  "applicationNetworkBootstrap",
42
45
  "signerOwnedEncryptedRecovery",
43
46
  "signerOwnedRawExport",
47
+ "restrictedOperatorLifecycle",
44
48
  "typedSolanaTransactions",
45
49
  "typedA2aPaymentMemos",
46
50
  "typedA2aRefundMemos",
@@ -77,6 +81,8 @@ const SIGNER_PROTOCOL_V2_REQUIRED_CLIENT_FEATURES = [
77
81
  "atomicIdempotency",
78
82
  "ambiguousBroadcastReconciliation",
79
83
  "signerOwnedKeys",
84
+ "signerOwnedRoleBaselines",
85
+ "liveWalletReadiness",
80
86
  "signerOwnedRPC",
81
87
  "applicationNetworkBootstrap",
82
88
  "typedSolanaTransactions",
@@ -42,16 +42,12 @@ function resolveWalletActionSelection(params) {
42
42
  providerId: params.providerId,
43
43
  env
44
44
  });
45
- } else if (registry.defaultWalletId) {
46
- const fallback = registry.wallets.find((entry) => entry.id === registry.defaultWalletId);
47
- selection = fallback ? {
48
- walletId: fallback.id,
49
- walletName: fallback.name,
50
- providerId: fallback.providerId,
51
- source: "fallback"
52
- } : { source: "none" };
53
45
  } else {
54
- selection = { source: "none" };
46
+ selection = resolveWalletSelection({
47
+ agentId: params.agentId,
48
+ skillWalletId: params.skillWalletId,
49
+ env
50
+ });
55
51
  }
56
52
  } catch (error) {
57
53
  const message = error instanceof Error ? error.message : "wallet selection failed";
@@ -61,7 +57,7 @@ function resolveWalletActionSelection(params) {
61
57
  throw new WalletActionSelectionError("wallet_not_found", message);
62
58
  }
63
59
  if (!selection.walletId) {
64
- throw new WalletActionSelectionError("wallet_handle_required", "Agent wallet handle, walletId, or default Agent wallet required");
60
+ throw new WalletActionSelectionError("wallet_handle_required", "Select an Agent wallet: use an explicit wallet, skill override, Agent assignment, or Default Agent wallet fallback");
65
61
  }
66
62
  const wallet = registry.wallets.find((entry) => entry.id === selection.walletId);
67
63
  if (!wallet) {