safehands-pharos 1.4.0 → 2.6.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.
- package/.agents/policies/default.json +15 -0
- package/.env.example +81 -64
- package/README.md +301 -333
- package/SECURITY.md +112 -0
- package/SKILL.md +59 -0
- package/contracts/SafeHandsAttestation.sol +182 -0
- package/contracts/SafeHandsRegistry.sol +149 -0
- package/dist/agent/SafeHandsGuardianAgent.d.ts +39 -0
- package/dist/agent/SafeHandsGuardianAgent.d.ts.map +1 -0
- package/dist/agent/SafeHandsGuardianAgent.js +98 -0
- package/dist/agent/SafeHandsGuardianAgent.js.map +1 -0
- package/dist/agent/agentDecisionFormatter.d.ts +43 -0
- package/dist/agent/agentDecisionFormatter.d.ts.map +1 -0
- package/dist/agent/agentDecisionFormatter.js +96 -0
- package/dist/agent/agentDecisionFormatter.js.map +1 -0
- package/dist/agent/agentEnrich.d.ts +28 -0
- package/dist/agent/agentEnrich.d.ts.map +1 -0
- package/dist/agent/agentEnrich.js +99 -0
- package/dist/agent/agentEnrich.js.map +1 -0
- package/dist/agent/agentIntentClassifier.d.ts +44 -0
- package/dist/agent/agentIntentClassifier.d.ts.map +1 -0
- package/dist/agent/agentIntentClassifier.js +77 -0
- package/dist/agent/agentIntentClassifier.js.map +1 -0
- package/dist/agent/agentPolicyResolver.d.ts +44 -0
- package/dist/agent/agentPolicyResolver.d.ts.map +1 -0
- package/dist/agent/agentPolicyResolver.js +94 -0
- package/dist/agent/agentPolicyResolver.js.map +1 -0
- package/dist/agent/agentRuntime.d.ts +12 -0
- package/dist/agent/agentRuntime.d.ts.map +1 -0
- package/dist/agent/agentRuntime.js +31 -0
- package/dist/agent/agentRuntime.js.map +1 -0
- package/dist/agent/agentToolRouter.d.ts +24 -0
- package/dist/agent/agentToolRouter.d.ts.map +1 -0
- package/dist/agent/agentToolRouter.js +89 -0
- package/dist/agent/agentToolRouter.js.map +1 -0
- package/dist/agent/guardianOperator.d.ts +35 -0
- package/dist/agent/guardianOperator.d.ts.map +1 -0
- package/dist/agent/guardianOperator.js +65 -0
- package/dist/agent/guardianOperator.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +14 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/api/activityRoutes.d.ts +70 -0
- package/dist/api/activityRoutes.d.ts.map +1 -0
- package/dist/api/activityRoutes.js +100 -0
- package/dist/api/activityRoutes.js.map +1 -0
- package/dist/api/agentRoutes.d.ts +16 -0
- package/dist/api/agentRoutes.d.ts.map +1 -0
- package/dist/api/agentRoutes.js +37 -0
- package/dist/api/agentRoutes.js.map +1 -0
- package/dist/api/broadcastRoutes.d.ts +34 -0
- package/dist/api/broadcastRoutes.d.ts.map +1 -0
- package/dist/api/broadcastRoutes.js +147 -0
- package/dist/api/broadcastRoutes.js.map +1 -0
- package/dist/api/consolePage.d.ts +2 -0
- package/dist/api/consolePage.d.ts.map +1 -0
- package/dist/api/consolePage.js +206 -0
- package/dist/api/consolePage.js.map +1 -0
- package/dist/api/httpHardening.d.ts +75 -0
- package/dist/api/httpHardening.d.ts.map +1 -0
- package/dist/api/httpHardening.js +122 -0
- package/dist/api/httpHardening.js.map +1 -0
- package/dist/api/indexerRoutes.d.ts +46 -0
- package/dist/api/indexerRoutes.d.ts.map +1 -0
- package/dist/api/indexerRoutes.js +75 -0
- package/dist/api/indexerRoutes.js.map +1 -0
- package/dist/api/paidRoutes.d.ts +8 -0
- package/dist/api/paidRoutes.d.ts.map +1 -0
- package/dist/api/paidRoutes.js +175 -0
- package/dist/api/paidRoutes.js.map +1 -0
- package/dist/api/prepareRoutes.d.ts +58 -0
- package/dist/api/prepareRoutes.d.ts.map +1 -0
- package/dist/api/prepareRoutes.js +90 -0
- package/dist/api/prepareRoutes.js.map +1 -0
- package/dist/api/response.d.ts +39 -0
- package/dist/api/response.d.ts.map +1 -0
- package/dist/api/response.js +53 -0
- package/dist/api/response.js.map +1 -0
- package/dist/api/routes.d.ts +175 -0
- package/dist/api/routes.d.ts.map +1 -0
- package/dist/api/routes.js +276 -0
- package/dist/api/routes.js.map +1 -0
- package/dist/api/schemas.d.ts +147 -0
- package/dist/api/schemas.d.ts.map +1 -0
- package/dist/api/schemas.js +81 -0
- package/dist/api/schemas.js.map +1 -0
- package/dist/api/server.d.ts +11 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +505 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/toolRoutes.d.ts +14 -0
- package/dist/api/toolRoutes.d.ts.map +1 -0
- package/dist/api/toolRoutes.js +138 -0
- package/dist/api/toolRoutes.js.map +1 -0
- package/dist/api/walletPrepareRoutes.d.ts +74 -0
- package/dist/api/walletPrepareRoutes.d.ts.map +1 -0
- package/dist/api/walletPrepareRoutes.js +127 -0
- package/dist/api/walletPrepareRoutes.js.map +1 -0
- package/dist/api/x402Gate.d.ts +51 -0
- package/dist/api/x402Gate.d.ts.map +1 -0
- package/dist/api/x402Gate.js +184 -0
- package/dist/api/x402Gate.js.map +1 -0
- package/dist/cli.d.ts +5 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +154 -124
- package/dist/cli.js.map +1 -1
- package/dist/data/ecosystemRegistry.data.d.ts +3 -0
- package/dist/data/ecosystemRegistry.data.d.ts.map +1 -0
- package/dist/data/ecosystemRegistry.data.js +582 -0
- package/dist/data/ecosystemRegistry.data.js.map +1 -0
- package/dist/demo.d.ts +1 -1
- package/dist/demo.d.ts.map +1 -1
- package/dist/demo.js +206 -171
- package/dist/demo.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +249 -164
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +127 -65
- package/dist/init.js.map +1 -1
- package/dist/lib/analysis/approval.d.ts +29 -0
- package/dist/lib/analysis/approval.d.ts.map +1 -0
- package/dist/lib/analysis/approval.js +162 -0
- package/dist/lib/analysis/approval.js.map +1 -0
- package/dist/lib/analysis/calldata.d.ts +60 -0
- package/dist/lib/analysis/calldata.d.ts.map +1 -0
- package/dist/lib/analysis/calldata.js +349 -0
- package/dist/lib/analysis/calldata.js.map +1 -0
- package/dist/lib/analysis/contractIntel.d.ts +65 -0
- package/dist/lib/analysis/contractIntel.d.ts.map +1 -0
- package/dist/lib/analysis/contractIntel.js +320 -0
- package/dist/lib/analysis/contractIntel.js.map +1 -0
- package/dist/lib/analysis/evm.d.ts +42 -0
- package/dist/lib/analysis/evm.d.ts.map +1 -0
- package/dist/lib/analysis/evm.js +185 -0
- package/dist/lib/analysis/evm.js.map +1 -0
- package/dist/lib/analysis/gas.d.ts +4 -0
- package/dist/lib/analysis/gas.d.ts.map +1 -0
- package/dist/lib/analysis/gas.js +13 -0
- package/dist/lib/analysis/gas.js.map +1 -0
- package/dist/lib/analysis/index.d.ts +11 -0
- package/dist/lib/analysis/index.d.ts.map +1 -0
- package/dist/lib/analysis/index.js +16 -0
- package/dist/lib/analysis/index.js.map +1 -0
- package/dist/lib/analysis/pharosTokens.d.ts +31 -0
- package/dist/lib/analysis/pharosTokens.d.ts.map +1 -0
- package/dist/lib/analysis/pharosTokens.js +78 -0
- package/dist/lib/analysis/pharosTokens.js.map +1 -0
- package/dist/lib/analysis/safeTx.d.ts +27 -0
- package/dist/lib/analysis/safeTx.d.ts.map +1 -0
- package/dist/lib/analysis/safeTx.js +118 -0
- package/dist/lib/analysis/safeTx.js.map +1 -0
- package/dist/lib/analysis/token.d.ts +21 -0
- package/dist/lib/analysis/token.d.ts.map +1 -0
- package/dist/lib/analysis/token.js +151 -0
- package/dist/lib/analysis/token.js.map +1 -0
- package/dist/lib/analysis/types.d.ts +49 -0
- package/dist/lib/analysis/types.d.ts.map +1 -0
- package/dist/lib/analysis/types.js +10 -0
- package/dist/lib/analysis/types.js.map +1 -0
- package/dist/lib/analysis/x402.d.ts +24 -0
- package/dist/lib/analysis/x402.d.ts.map +1 -0
- package/dist/lib/analysis/x402.js +60 -0
- package/dist/lib/analysis/x402.js.map +1 -0
- package/dist/lib/auditLog.d.ts +1 -0
- package/dist/lib/auditLog.d.ts.map +1 -1
- package/dist/lib/auditLog.js +0 -0
- package/dist/lib/auditLog.js.map +1 -1
- package/dist/lib/config.d.ts +77 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +120 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/constants.d.ts +452 -291
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +509 -292
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/dodoApi.d.ts +101 -78
- package/dist/lib/dodoApi.d.ts.map +1 -1
- package/dist/lib/dodoApi.js +259 -196
- package/dist/lib/dodoApi.js.map +1 -1
- package/dist/lib/ecosystemRegistry.d.ts +116 -0
- package/dist/lib/ecosystemRegistry.d.ts.map +1 -0
- package/dist/lib/ecosystemRegistry.js +178 -0
- package/dist/lib/ecosystemRegistry.js.map +1 -0
- package/dist/lib/envLoader.d.ts +2 -0
- package/dist/lib/envLoader.d.ts.map +1 -0
- package/dist/lib/envLoader.js +65 -0
- package/dist/lib/envLoader.js.map +1 -0
- package/dist/lib/goldsky.d.ts +58 -0
- package/dist/lib/goldsky.d.ts.map +1 -0
- package/dist/lib/goldsky.js +275 -0
- package/dist/lib/goldsky.js.map +1 -0
- package/dist/lib/guardian/decision.d.ts +23 -0
- package/dist/lib/guardian/decision.d.ts.map +1 -0
- package/dist/lib/guardian/decision.js +40 -0
- package/dist/lib/guardian/decision.js.map +1 -0
- package/dist/lib/http.d.ts +13 -14
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +383 -118
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/managedExecution.d.ts +19 -0
- package/dist/lib/managedExecution.d.ts.map +1 -0
- package/dist/lib/managedExecution.js +64 -0
- package/dist/lib/managedExecution.js.map +1 -0
- package/dist/lib/merkleBatcher.d.ts +23 -0
- package/dist/lib/merkleBatcher.d.ts.map +1 -0
- package/dist/lib/merkleBatcher.js +62 -0
- package/dist/lib/merkleBatcher.js.map +1 -0
- package/dist/lib/networks.d.ts +48 -0
- package/dist/lib/networks.d.ts.map +1 -0
- package/dist/lib/networks.js +103 -0
- package/dist/lib/networks.js.map +1 -0
- package/dist/lib/observability/accessContext.d.ts +29 -0
- package/dist/lib/observability/accessContext.d.ts.map +1 -0
- package/dist/lib/observability/accessContext.js +50 -0
- package/dist/lib/observability/accessContext.js.map +1 -0
- package/dist/lib/observability/activityStore.d.ts +70 -0
- package/dist/lib/observability/activityStore.d.ts.map +1 -0
- package/dist/lib/observability/activityStore.js +149 -0
- package/dist/lib/observability/activityStore.js.map +1 -0
- package/dist/lib/observability/apiKey.d.ts +55 -0
- package/dist/lib/observability/apiKey.d.ts.map +1 -0
- package/dist/lib/observability/apiKey.js +141 -0
- package/dist/lib/observability/apiKey.js.map +1 -0
- package/dist/lib/observability/logger.d.ts +30 -0
- package/dist/lib/observability/logger.d.ts.map +1 -0
- package/dist/lib/observability/logger.js +30 -0
- package/dist/lib/observability/logger.js.map +1 -0
- package/dist/lib/observability/quota.d.ts +33 -0
- package/dist/lib/observability/quota.d.ts.map +1 -0
- package/dist/lib/observability/quota.js +49 -0
- package/dist/lib/observability/quota.js.map +1 -0
- package/dist/lib/observability/requestId.d.ts +8 -0
- package/dist/lib/observability/requestId.d.ts.map +1 -0
- package/dist/lib/observability/requestId.js +24 -0
- package/dist/lib/observability/requestId.js.map +1 -0
- package/dist/lib/observability/sanitize.d.ts +77 -0
- package/dist/lib/observability/sanitize.d.ts.map +1 -0
- package/dist/lib/observability/sanitize.js +228 -0
- package/dist/lib/observability/sanitize.js.map +1 -0
- package/dist/lib/observability/scopes.d.ts +20 -0
- package/dist/lib/observability/scopes.d.ts.map +1 -0
- package/dist/lib/observability/scopes.js +77 -0
- package/dist/lib/observability/scopes.js.map +1 -0
- package/dist/lib/okxDexApi.d.ts +106 -0
- package/dist/lib/okxDexApi.d.ts.map +1 -0
- package/dist/lib/okxDexApi.js +221 -0
- package/dist/lib/okxDexApi.js.map +1 -0
- package/dist/lib/persistentJsonStore.d.ts +5 -0
- package/dist/lib/persistentJsonStore.d.ts.map +1 -0
- package/dist/lib/persistentJsonStore.js +39 -0
- package/dist/lib/persistentJsonStore.js.map +1 -0
- package/dist/lib/pharos/attestationPublisher.d.ts +50 -0
- package/dist/lib/pharos/attestationPublisher.d.ts.map +1 -0
- package/dist/lib/pharos/attestationPublisher.js +262 -0
- package/dist/lib/pharos/attestationPublisher.js.map +1 -0
- package/dist/lib/pharos/ecosystem.d.ts +52 -0
- package/dist/lib/pharos/ecosystem.d.ts.map +1 -0
- package/dist/lib/pharos/ecosystem.js +431 -0
- package/dist/lib/pharos/ecosystem.js.map +1 -0
- package/dist/lib/pharos/ecosystemEvidence.d.ts +53 -0
- package/dist/lib/pharos/ecosystemEvidence.d.ts.map +1 -0
- package/dist/lib/pharos/ecosystemEvidence.js +133 -0
- package/dist/lib/pharos/ecosystemEvidence.js.map +1 -0
- package/dist/lib/pharos/rpc.d.ts +44 -0
- package/dist/lib/pharos/rpc.d.ts.map +1 -0
- package/dist/lib/pharos/rpc.js +90 -0
- package/dist/lib/pharos/rpc.js.map +1 -0
- package/dist/lib/pharos/rpcEvidence.d.ts +49 -0
- package/dist/lib/pharos/rpcEvidence.d.ts.map +1 -0
- package/dist/lib/pharos/rpcEvidence.js +46 -0
- package/dist/lib/pharos/rpcEvidence.js.map +1 -0
- package/dist/lib/pharos/rpcMethods.d.ts +46 -0
- package/dist/lib/pharos/rpcMethods.d.ts.map +1 -0
- package/dist/lib/pharos/rpcMethods.js +176 -0
- package/dist/lib/pharos/rpcMethods.js.map +1 -0
- package/dist/lib/pharos/spvVerifier.d.ts +47 -0
- package/dist/lib/pharos/spvVerifier.d.ts.map +1 -0
- package/dist/lib/pharos/spvVerifier.js +105 -0
- package/dist/lib/pharos/spvVerifier.js.map +1 -0
- package/dist/lib/pharos/userSignedBroadcaster.d.ts +10 -0
- package/dist/lib/pharos/userSignedBroadcaster.d.ts.map +1 -0
- package/dist/lib/pharos/userSignedBroadcaster.js +50 -0
- package/dist/lib/pharos/userSignedBroadcaster.js.map +1 -0
- package/dist/lib/pharosClient.d.ts +53 -58
- package/dist/lib/pharosClient.d.ts.map +1 -1
- package/dist/lib/pharosClient.js +56 -63
- package/dist/lib/pharosClient.js.map +1 -1
- package/dist/lib/policy/actionPolicyEngine.d.ts +89 -53
- package/dist/lib/policy/actionPolicyEngine.d.ts.map +1 -1
- package/dist/lib/policy/actionPolicyEngine.js +373 -212
- package/dist/lib/policy/actionPolicyEngine.js.map +1 -1
- package/dist/lib/policy/agentPolicy.d.ts +29 -0
- package/dist/lib/policy/agentPolicy.d.ts.map +1 -0
- package/dist/lib/policy/agentPolicy.js +154 -0
- package/dist/lib/policy/agentPolicy.js.map +1 -0
- package/dist/lib/policy/policyPresets.d.ts +30 -0
- package/dist/lib/policy/policyPresets.d.ts.map +1 -0
- package/dist/lib/policy/policyPresets.js +124 -0
- package/dist/lib/policy/policyPresets.js.map +1 -0
- package/dist/lib/policy/writeExecutionGate.d.ts +23 -0
- package/dist/lib/policy/writeExecutionGate.d.ts.map +1 -0
- package/dist/lib/policy/writeExecutionGate.js +65 -0
- package/dist/lib/policy/writeExecutionGate.js.map +1 -0
- package/dist/lib/preparedTxStore.d.ts +21 -0
- package/dist/lib/preparedTxStore.d.ts.map +1 -0
- package/dist/lib/preparedTxStore.js +90 -0
- package/dist/lib/preparedTxStore.js.map +1 -0
- package/dist/lib/price/chainlinkPushProvider.d.ts +34 -0
- package/dist/lib/price/chainlinkPushProvider.d.ts.map +1 -0
- package/dist/lib/price/chainlinkPushProvider.js +36 -0
- package/dist/lib/price/chainlinkPushProvider.js.map +1 -0
- package/dist/lib/price/feedRegistry.d.ts +26 -0
- package/dist/lib/price/feedRegistry.d.ts.map +1 -0
- package/dist/lib/price/feedRegistry.js +67 -0
- package/dist/lib/price/feedRegistry.js.map +1 -0
- package/dist/lib/price/priceResolver.d.ts +35 -0
- package/dist/lib/price/priceResolver.d.ts.map +1 -0
- package/dist/lib/price/priceResolver.js +246 -0
- package/dist/lib/price/priceResolver.js.map +1 -0
- package/dist/lib/price/supraProvider.d.ts +9 -0
- package/dist/lib/price/supraProvider.d.ts.map +1 -0
- package/dist/lib/price/supraProvider.js +17 -0
- package/dist/lib/price/supraProvider.js.map +1 -0
- package/dist/lib/price/types.d.ts +67 -0
- package/dist/lib/price/types.d.ts.map +1 -0
- package/dist/lib/price/types.js +7 -0
- package/dist/lib/price/types.js.map +1 -0
- package/dist/lib/productionGuards.d.ts +16 -0
- package/dist/lib/productionGuards.d.ts.map +1 -0
- package/dist/lib/productionGuards.js +93 -0
- package/dist/lib/productionGuards.js.map +1 -0
- package/dist/lib/recipientSafety.d.ts +5 -0
- package/dist/lib/recipientSafety.d.ts.map +1 -0
- package/dist/lib/recipientSafety.js +28 -0
- package/dist/lib/recipientSafety.js.map +1 -0
- package/dist/lib/riskEngine.d.ts +45 -26
- package/dist/lib/riskEngine.d.ts.map +1 -1
- package/dist/lib/riskEngine.js +346 -283
- package/dist/lib/riskEngine.js.map +1 -1
- package/dist/lib/riskInclusion.d.ts +72 -0
- package/dist/lib/riskInclusion.d.ts.map +1 -0
- package/dist/lib/riskInclusion.js +252 -0
- package/dist/lib/riskInclusion.js.map +1 -0
- package/dist/lib/safeHandsRegistry.d.ts +32 -0
- package/dist/lib/safeHandsRegistry.d.ts.map +1 -0
- package/dist/lib/safeHandsRegistry.js +151 -0
- package/dist/lib/safeHandsRegistry.js.map +1 -0
- package/dist/lib/signer/index.d.ts +24 -24
- package/dist/lib/signer/index.d.ts.map +1 -1
- package/dist/lib/signer/index.js +110 -88
- package/dist/lib/signer/index.js.map +1 -1
- package/dist/lib/spendAccumulator.d.ts +24 -1
- package/dist/lib/spendAccumulator.d.ts.map +1 -1
- package/dist/lib/spendAccumulator.js +76 -6
- package/dist/lib/spendAccumulator.js.map +1 -1
- package/dist/lib/toolResponse.d.ts +30 -25
- package/dist/lib/toolResponse.d.ts.map +1 -1
- package/dist/lib/toolResponse.js +57 -53
- package/dist/lib/toolResponse.js.map +1 -1
- package/dist/lib/validation.d.ts +7 -0
- package/dist/lib/validation.d.ts.map +1 -0
- package/dist/lib/validation.js +62 -0
- package/dist/lib/validation.js.map +1 -0
- package/dist/lib/wallet/index.d.ts +33 -37
- package/dist/lib/wallet/index.d.ts.map +1 -1
- package/dist/lib/wallet/index.js +141 -128
- package/dist/lib/wallet/index.js.map +1 -1
- package/dist/lib/x402ReplayStore.d.ts +6 -0
- package/dist/lib/x402ReplayStore.d.ts.map +1 -0
- package/dist/lib/x402ReplayStore.js +79 -0
- package/dist/lib/x402ReplayStore.js.map +1 -0
- package/dist/safehands.zip +0 -0
- package/dist/sdk.d.ts +14 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +25 -0
- package/dist/sdk.js.map +1 -0
- package/dist/tools/approveToken.d.ts +66 -45
- package/dist/tools/approveToken.d.ts.map +1 -1
- package/dist/tools/approveToken.js +152 -85
- package/dist/tools/approveToken.js.map +1 -1
- package/dist/tools/assessRisk.d.ts +66 -79
- package/dist/tools/assessRisk.d.ts.map +1 -1
- package/dist/tools/assessRisk.js +63 -104
- package/dist/tools/assessRisk.js.map +1 -1
- package/dist/tools/checkAllowance.d.ts +49 -43
- package/dist/tools/checkAllowance.d.ts.map +1 -1
- package/dist/tools/checkAllowance.js +78 -56
- package/dist/tools/checkAllowance.js.map +1 -1
- package/dist/tools/checkTokenSecurity.d.ts +103 -46
- package/dist/tools/checkTokenSecurity.d.ts.map +1 -1
- package/dist/tools/checkTokenSecurity.js +240 -95
- package/dist/tools/checkTokenSecurity.js.map +1 -1
- package/dist/tools/createAgentWallet.d.ts +30 -26
- package/dist/tools/createAgentWallet.d.ts.map +1 -1
- package/dist/tools/createAgentWallet.js +87 -58
- package/dist/tools/createAgentWallet.js.map +1 -1
- package/dist/tools/estimateGas.d.ts +84 -79
- package/dist/tools/estimateGas.d.ts.map +1 -1
- package/dist/tools/estimateGas.js +130 -124
- package/dist/tools/estimateGas.js.map +1 -1
- package/dist/tools/executeSwap.d.ts +109 -61
- package/dist/tools/executeSwap.d.ts.map +1 -1
- package/dist/tools/executeSwap.js +289 -141
- package/dist/tools/executeSwap.js.map +1 -1
- package/dist/tools/explainRisk.d.ts +29 -29
- package/dist/tools/explainRisk.d.ts.map +1 -1
- package/dist/tools/explainRisk.js +33 -32
- package/dist/tools/explainRisk.js.map +1 -1
- package/dist/tools/getAgentPolicy.d.ts +15 -0
- package/dist/tools/getAgentPolicy.d.ts.map +1 -0
- package/dist/tools/getAgentPolicy.js +16 -0
- package/dist/tools/getAgentPolicy.js.map +1 -0
- package/dist/tools/getAgentReputation.d.ts +31 -0
- package/dist/tools/getAgentReputation.d.ts.map +1 -0
- package/dist/tools/getAgentReputation.js +44 -0
- package/dist/tools/getAgentReputation.js.map +1 -0
- package/dist/tools/getAgentWallet.d.ts +21 -21
- package/dist/tools/getAgentWallet.d.ts.map +1 -1
- package/dist/tools/getAgentWallet.js +37 -27
- package/dist/tools/getAgentWallet.js.map +1 -1
- package/dist/tools/getAgentWalletBalance.d.ts +11 -11
- package/dist/tools/getAgentWalletBalance.d.ts.map +1 -1
- package/dist/tools/getAgentWalletBalance.js +102 -70
- package/dist/tools/getAgentWalletBalance.js.map +1 -1
- package/dist/tools/getExecutionHistory.d.ts +49 -49
- package/dist/tools/getExecutionHistory.d.ts.map +1 -1
- package/dist/tools/getExecutionHistory.js +148 -154
- package/dist/tools/getExecutionHistory.js.map +1 -1
- package/dist/tools/getGasPrice.d.ts +43 -43
- package/dist/tools/getGasPrice.d.ts.map +0 -0
- package/dist/tools/getGasPrice.js +59 -59
- package/dist/tools/getGasPrice.js.map +1 -1
- package/dist/tools/getPoolInfo.d.ts +75 -75
- package/dist/tools/getPoolInfo.d.ts.map +1 -1
- package/dist/tools/getPoolInfo.js +142 -137
- package/dist/tools/getPoolInfo.js.map +1 -1
- package/dist/tools/getSpvProof.d.ts +24 -0
- package/dist/tools/getSpvProof.d.ts.map +1 -0
- package/dist/tools/getSpvProof.js +64 -0
- package/dist/tools/getSpvProof.js.map +1 -0
- package/dist/tools/getTokenPrice.d.ts +56 -113
- package/dist/tools/getTokenPrice.d.ts.map +1 -1
- package/dist/tools/getTokenPrice.js +110 -117
- package/dist/tools/getTokenPrice.js.map +1 -1
- package/dist/tools/getTransactionStatus.d.ts +43 -43
- package/dist/tools/getTransactionStatus.d.ts.map +0 -0
- package/dist/tools/getTransactionStatus.js +59 -59
- package/dist/tools/getTransactionStatus.js.map +0 -0
- package/dist/tools/getWalletBalance.d.ts +103 -68
- package/dist/tools/getWalletBalance.d.ts.map +1 -1
- package/dist/tools/getWalletBalance.js +127 -87
- package/dist/tools/getWalletBalance.js.map +1 -1
- package/dist/tools/publishRiskScore.d.ts +85 -63
- package/dist/tools/publishRiskScore.d.ts.map +1 -1
- package/dist/tools/publishRiskScore.js +136 -88
- package/dist/tools/publishRiskScore.js.map +1 -1
- package/dist/tools/queryGoldsky.d.ts +20 -0
- package/dist/tools/queryGoldsky.d.ts.map +1 -0
- package/dist/tools/queryGoldsky.js +29 -0
- package/dist/tools/queryGoldsky.js.map +1 -0
- package/dist/tools/queryRiskRegistry.d.ts +38 -38
- package/dist/tools/queryRiskRegistry.d.ts.map +1 -1
- package/dist/tools/queryRiskRegistry.js +59 -55
- package/dist/tools/queryRiskRegistry.js.map +1 -1
- package/dist/tools/safehandsPreflightCheck.d.ts +92 -77
- package/dist/tools/safehandsPreflightCheck.d.ts.map +1 -1
- package/dist/tools/safehandsPreflightCheck.js +174 -47
- package/dist/tools/safehandsPreflightCheck.js.map +1 -1
- package/dist/tools/safehandsRiskReport.d.ts +96 -81
- package/dist/tools/safehandsRiskReport.d.ts.map +1 -1
- package/dist/tools/safehandsRiskReport.js +48 -28
- package/dist/tools/safehandsRiskReport.js.map +1 -1
- package/dist/tools/safehandsSafeExecute.d.ts +20 -20
- package/dist/tools/safehandsSafeExecute.d.ts.map +1 -1
- package/dist/tools/safehandsSafeExecute.js +164 -81
- package/dist/tools/safehandsSafeExecute.js.map +1 -1
- package/dist/tools/safehandsWalletHealth.d.ts +28 -14
- package/dist/tools/safehandsWalletHealth.d.ts.map +1 -1
- package/dist/tools/safehandsWalletHealth.js +145 -103
- package/dist/tools/safehandsWalletHealth.js.map +1 -1
- package/dist/tools/safehandsX402Preflight.d.ts +26 -26
- package/dist/tools/safehandsX402Preflight.d.ts.map +1 -1
- package/dist/tools/safehandsX402Preflight.js +86 -65
- package/dist/tools/safehandsX402Preflight.js.map +1 -1
- package/dist/tools/sendPayment.d.ts +63 -57
- package/dist/tools/sendPayment.d.ts.map +1 -1
- package/dist/tools/sendPayment.js +144 -117
- package/dist/tools/sendPayment.js.map +1 -1
- package/dist/tools/setAgentPolicy.d.ts +75 -0
- package/dist/tools/setAgentPolicy.d.ts.map +1 -0
- package/dist/tools/setAgentPolicy.js +80 -0
- package/dist/tools/setAgentPolicy.js.map +1 -0
- package/dist/tools/simulateTransaction.d.ts +64 -60
- package/dist/tools/simulateTransaction.d.ts.map +1 -1
- package/dist/tools/simulateTransaction.js +116 -83
- package/dist/tools/simulateTransaction.js.map +1 -1
- package/dist/tools/tokenRegistryStatus.d.ts +46 -26
- package/dist/tools/tokenRegistryStatus.d.ts.map +1 -1
- package/dist/tools/tokenRegistryStatus.js +126 -96
- package/dist/tools/tokenRegistryStatus.js.map +1 -1
- package/dist/tools/verifyRiskInclusion.d.ts +59 -0
- package/dist/tools/verifyRiskInclusion.d.ts.map +1 -0
- package/dist/tools/verifyRiskInclusion.js +40 -0
- package/dist/tools/verifyRiskInclusion.js.map +1 -0
- package/dist/tools/x402PayAndFetch.d.ts +87 -81
- package/dist/tools/x402PayAndFetch.d.ts.map +1 -1
- package/dist/tools/x402PayAndFetch.js +295 -152
- package/dist/tools/x402PayAndFetch.js.map +1 -1
- package/dist/worker.d.ts +3 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +39 -0
- package/dist/worker.js.map +1 -0
- package/dist/x402Server.d.ts +1 -1
- package/dist/x402Server.d.ts.map +1 -1
- package/dist/x402Server.js +409 -252
- package/dist/x402Server.js.map +1 -1
- package/docs/REVIEWER_QUICKSTART.md +113 -0
- package/docs/SAFEHANDS_REVIEWER_DEMO_SCRIPT.md +228 -0
- package/package.json +130 -83
- package/contracts/RiskRegistry.json +0 -223
- package/contracts/RiskRegistry.sol +0 -58
- package/dist/lib/testDodoLive.d.ts +0 -2
- package/dist/lib/testDodoLive.d.ts.map +0 -1
- package/dist/lib/testDodoLive.js +0 -105
- package/dist/lib/testDodoLive.js.map +0 -1
- package/dist/lib/testLiveSafehands.d.ts +0 -2
- package/dist/lib/testLiveSafehands.d.ts.map +0 -1
- package/dist/lib/testLiveSafehands.js +0 -93
- package/dist/lib/testLiveSafehands.js.map +0 -1
- package/dist/lib/testRpc.d.ts +0 -2
- package/dist/lib/testRpc.d.ts.map +0 -1
- package/dist/lib/testRpc.js +0 -30
- package/dist/lib/testRpc.js.map +0 -1
- package/dist/lib/testRpcLive.d.ts +0 -2
- package/dist/lib/testRpcLive.d.ts.map +0 -1
- package/dist/lib/testRpcLive.js +0 -89
- package/dist/lib/testRpcLive.js.map +0 -1
- package/dist/lib/testTools.d.ts +0 -2
- package/dist/lib/testTools.d.ts.map +0 -1
- package/dist/lib/testTools.js +0 -398
- package/dist/lib/testTools.js.map +0 -1
- package/dist/lib/testX402Live.d.ts +0 -2
- package/dist/lib/testX402Live.d.ts.map +0 -1
- package/dist/lib/testX402Live.js +0 -160
- package/dist/lib/testX402Live.js.map +0 -1
- package/dist/scripts/checkDeploy.d.ts +0 -2
- package/dist/scripts/checkDeploy.d.ts.map +0 -1
- package/dist/scripts/checkDeploy.js +0 -25
- package/dist/scripts/checkDeploy.js.map +0 -1
- package/dist/scripts/deployRegistry.d.ts +0 -2
- package/dist/scripts/deployRegistry.d.ts.map +0 -1
- package/dist/scripts/deployRegistry.js +0 -101
- package/dist/scripts/deployRegistry.js.map +0 -1
- package/dist/scripts/testRegistry.d.ts +0 -2
- package/dist/scripts/testRegistry.d.ts.map +0 -1
- package/dist/scripts/testRegistry.js +0 -44
- package/dist/scripts/testRegistry.js.map +0 -1
- package/examples/dashboard/index.html +0 -337
- package/examples/pharos-skill-engine/SKILL.safehands.md +0 -85
- package/examples/pharos-skill-engine/assets/safehands/example-actions.json +0 -49
- package/examples/pharos-skill-engine/assets/safehands/policy-defaults.json +0 -11
- package/examples/pharos-skill-engine/references/safehands.md +0 -345
- package/examples/scenario-hack.ts +0 -38
- package/skill/SKILL.md +0 -133
- package/skill/assets/safehands/example-actions.json +0 -49
- package/skill/assets/safehands/policy-defaults.json +0 -11
- package/skill/references/safehands.md +0 -345
package/SECURITY.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Security Policy: SafeHands-Pharos
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
SafeHands is a **mainnet-first, read-only-by-default** transaction firewall for AI agents on **Pharos Pacific (chain 1672)**: read/check/analyze runs on mainnet, and the hosted backend holds no keys. **Attestations publish on Pacific Mainnet using a segregated operational key**. The safety architecture is production-inspired but is **not audited for mainnet custody**, which is why user execution/signing remains strictly offline/self-hosted.
|
|
6
|
+
|
|
7
|
+
## Threat Model
|
|
8
|
+
|
|
9
|
+
SafeHands protects agents from:
|
|
10
|
+
|
|
11
|
+
- Unintended **execution/signing on mainnet**, blocked **by default**: the hosted/default posture ships `WRITE_TOOLS_ENABLED=false` and holds no keys, so only read-only checks run on Pacific Mainnet. Self-hosted operators can opt in to execution via explicit env flags (see "Self-Hosted Backend"); those write paths are experimental and unaudited.
|
|
12
|
+
- Unlimited token approvals (blocked by default)
|
|
13
|
+
- SSRF via x402 URLs (localhost, private IPs, metadata IPs, IPv6 local all blocked)
|
|
14
|
+
- Overspending (per-tx + daily caps enforced; swaps of tokens that cannot be priced against the USD caps, outside PROS/WPROS/USDC/USDT and the official registry, are denied by default rather than silently uncounted)
|
|
15
|
+
- Executing/approving a token whose **security intelligence is missing** (GoPlus outage or token not yet indexed): the direct write path fails closed with `TOKEN_INTEL_UNAVAILABLE`; a caller `confirm` cannot substitute for a review that never happened. Registry-canonical tokens (identity verified on the official token registry) remain confirmable.
|
|
16
|
+
- Unauthorized managed wallet execution for the heavier write paths: swap/payment/approve via a custodial wallet require SafeHandsRegistry authorization (`REQUIRE_AUTHORIZED_AGENT_FOR_WRITE`, default on). x402 is permissionless-first: self-signed x402 needs no authorization, and managed x402 is allowlist-gated only when `REQUIRE_AUTHORIZED_AGENT_FOR_X402=true` is opted in.
|
|
17
|
+
- Invalid amounts, zero addresses, unknown tokens (strict validation)
|
|
18
|
+
- Sends/approvals to operator-denylisted recipients (hard-BLOCK via `SAFEHANDS_RECIPIENT_DENYLIST`; empty by default, no fabricated scam list. Deeper address-poisoning / first-time-recipient checks are indexer-backed and deferred, not faked.)
|
|
19
|
+
- Prompt/runtime injection of policy limits (policy stored in files, not runtime params)
|
|
20
|
+
|
|
21
|
+
**Confirmation trust anchor.** The `confirm=true` flag accepted by the soft decision tiers (`REQUIRE_CONFIRMATION` / `REQUIRE_TOKEN_REVIEW`) is **caller-attested**: it is supplied by the calling agent, the very party SafeHands is guarding. The real trust anchor is therefore the **MCP host / human operator** who relays that confirmation; SafeHands cannot verify that a human actually reviewed the action. Hard stops (`BLOCK`, `REQUIRE_FUNDING`, missing token intel) never accept it, and the HTTP broadcast relay refuses confirmation-tier records entirely (no trusted confirmation channel exists there). See `docs/DECISION_CONTRACT.md`.
|
|
22
|
+
|
|
23
|
+
## What SafeHands Does NOT Protect Against
|
|
24
|
+
|
|
25
|
+
- Smart contract vulnerabilities in third-party tokens or pools
|
|
26
|
+
- Private key compromise at the OS/process level
|
|
27
|
+
- Denial of service against the RPC endpoint
|
|
28
|
+
- Social engineering of the human operator
|
|
29
|
+
|
|
30
|
+
## Key Management
|
|
31
|
+
|
|
32
|
+
- Managed wallets are encrypted with AES-256-GCM (not KMS/Vault grade)
|
|
33
|
+
- Private keys are never returned in tool responses or logged
|
|
34
|
+
- The `SAFEHANDS_ATTESTER_PRIVATE_KEY` is an operational-only key for publishing attestations and must never be committed
|
|
35
|
+
- `.env` is gitignored; `.env.example` uses placeholders only
|
|
36
|
+
|
|
37
|
+
## Self-Hosted Backend: Security Profile
|
|
38
|
+
|
|
39
|
+
If you optionally self-host the reference backend on a public host, run it under the
|
|
40
|
+
**Mainnet Production Profile** and keep it zero-custody. A startup guard
|
|
41
|
+
(`src/lib/productionGuards.ts`) enforces this when `NODE_ENV=production`:
|
|
42
|
+
|
|
43
|
+
- **Zero-custody, enforced.** The public host must set `WALLET_MODE=none` and
|
|
44
|
+
`WRITE_TOOLS_ENABLED=false`. Managed execution (`WALLET_MODE=managed-mainnet` +
|
|
45
|
+
`WRITE_TOOLS_ENABLED=true`) **fails fast at boot** on a public host unless the
|
|
46
|
+
operator explicitly accepts the custody risk with
|
|
47
|
+
`SAFEHANDS_ALLOW_MANAGED_ON_PUBLIC=true` (self-hosted single-tenant only).
|
|
48
|
+
- **Wallet separation (three roles).**
|
|
49
|
+
- **Owner / deployer wallet**: governs the contracts. **Never store it on
|
|
50
|
+
a container host (Docker / VPS / Fly)** or any hosted env; use it locally for
|
|
51
|
+
deployment only.
|
|
52
|
+
- **Attester wallet** (`SAFEHANDS_ATTESTER_PRIVATE_KEY`): gas-only, dedicated,
|
|
53
|
+
**low balance**; only publishes attestations / risk records after validation.
|
|
54
|
+
- **x402 facilitator wallet** (`X402_FACILITATOR_PRIVATE_KEY`): gas-only,
|
|
55
|
+
dedicated, low balance; only settles x402 payments. `X402_PAY_TO` is a receiver
|
|
56
|
+
**address** (no key on the host).
|
|
57
|
+
- **Access model.** Public users/agents pay via **x402** (no API-key onboarding).
|
|
58
|
+
API keys (`SAFEHANDS_API_KEYS`) are **optional, admin/internal only**; the free
|
|
59
|
+
read tier stays open (`SAFEHANDS_REQUIRE_API_KEY=false`).
|
|
60
|
+
- **CORS.** Tighten `CORS_ORIGIN` / `SAFEHANDS_CORS_ORIGIN` to your domain(s) for
|
|
61
|
+
guarded/paid surfaces in production (wildcard is acceptable only for the public
|
|
62
|
+
read tier; the guard warns on wildcard in production).
|
|
63
|
+
- **Durable state.** Mount a persistent volume and set `SAFEHANDS_STATE_DIR` (e.g.
|
|
64
|
+
`/data`); the guard warns if state is ephemeral in production (an ephemeral host
|
|
65
|
+
with no persistent volume). Run a single instance; no horizontal replicas
|
|
66
|
+
(single authoritative state domain).
|
|
67
|
+
- **Not formally audited.** The safety architecture is production-inspired but has
|
|
68
|
+
**not** undergone a formal external security audit.
|
|
69
|
+
|
|
70
|
+
See [`docs/deployment/ARCHITECTURE_DECISION.md`](docs/deployment/ARCHITECTURE_DECISION.md).
|
|
71
|
+
|
|
72
|
+
## SafeHandsRiskRegistry
|
|
73
|
+
|
|
74
|
+
| Field | Value |
|
|
75
|
+
|---|---|
|
|
76
|
+
| Registry Address | `0x428e02bf85412e7242d991cd6725ec59e8b06c8d` |
|
|
77
|
+
| Attestation Address | `0x71a7a87b3b1ab6d86204cad691bb32fd75b4588c` |
|
|
78
|
+
| Network | Pharos Pacific Mainnet (1672) |
|
|
79
|
+
| Authorization | Required for attestation publishing only |
|
|
80
|
+
|
|
81
|
+
Both addresses are **live on Pharos Pacific Mainnet (1672)** and independently verifiable
|
|
82
|
+
(`eth_getCode` returns deployed bytecode; viewable on `https://www.pharosscan.xyz`). The
|
|
83
|
+
setup wizard (`src/init.ts`) writes them as defaults; redeploy with
|
|
84
|
+
`scripts/deploy-safehands.ts` to override.
|
|
85
|
+
|
|
86
|
+
Preflight, read-only, user-signed, and env wallet modes do **not** require RiskRegistry authorization.
|
|
87
|
+
|
|
88
|
+
**Operator-trust caveat.** The registry/attestation contracts are the intended trust anchor, but they are *operator-permissioned*: any address the owner has authorized can call `commitRiskRoot` (SafeHandsRiskRegistry) or `attest` (SafeHandsAttestation). A compromised or malicious authorized operator could therefore publish a poisoned risk root or mint a reputation attestation. The contracts are otherwise sound (`Ownable2Step`, `renounceOwnership` disabled, double-hashed Merkle leaves, write-once attestations, no reentrancy), so the centralization of the *authorized-operator set* is the real residual trust assumption whenever the registry is presented as an oracle. Consumers should treat published risk roots as **operator-attested, not trustless**, and pin/verify the operator set they rely on.
|
|
89
|
+
|
|
90
|
+
## Reporting Vulnerabilities
|
|
91
|
+
|
|
92
|
+
If you discover a security issue, please open a GitHub issue at [github.com/SZtch/safehands-pharos/issues](https://github.com/SZtch/safehands-pharos/issues) or contact the maintainer directly.
|
|
93
|
+
|
|
94
|
+
## Network & Execution Disclaimer
|
|
95
|
+
|
|
96
|
+
SafeHands is **mainnet-first for read/check/analyze on Pharos Pacific (1672)** and holds no keys in hosted mode. **Execution and signing are kept entirely offline**: mainnet *execution* is blocked by design on the hosted server. The safety architecture is production-inspired but has not undergone formal security audit.
|
|
97
|
+
|
|
98
|
+
## Runtime dependency audit note
|
|
99
|
+
|
|
100
|
+
`npm audit` currently reports vulnerabilities in the Hardhat/Mocha developer-tooling tree (`@nomicfoundation/hardhat-toolbox-viem`, `hardhat-ignition`, `mocha`, and transitive packages such as `lodash-es`, `serialize-javascript`, and `elliptic`). These packages are used for local contract testing and deployment tooling, not by the production SafeHands API/x402 runtime path.
|
|
101
|
+
|
|
102
|
+
For the Mainnet Production Profile (Single-Instance Production Architecture), deploy with:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npm ci # dev deps included: tsc lives there; the prepare hook compiles dist/
|
|
106
|
+
npm prune --omit=dev # then drop dev tooling (Hardhat/Mocha/tsc) from the host
|
|
107
|
+
NODE_ENV=production npm run start:api # the boot guards only run with NODE_ENV=production; always set it
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
(`npm ci --omit=dev` alone cannot work here: the `prepare` hook runs `tsc`, which is a devDependency. On container hosts, prefer the repo `Dockerfile`; its multi-stage build does the same compile-then-prune split.)
|
|
111
|
+
|
|
112
|
+
Do not run Hardhat/Mocha tooling on the public API host. A forced audit fix can downgrade/change Hardhat toolbox versions and may break the Hardhat 3 setup, so it is intentionally not applied blindly. Track these as dev-tooling findings and re-run `npm audit` after Hardhat/Mocha publish compatible patched releases.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: safehands-pharos
|
|
3
|
+
description: Pharos Pacific Mainnet-first SafeHands Agent for AI-agent transaction safety, x402 preflight, token checks, and gated execution.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SafeHands Pharos
|
|
7
|
+
|
|
8
|
+
SafeHands is an **agent surface of 33 registered MCP tools** for the Pharos ecosystem. It protects AI agents by checking transaction intent before the agent signs, approves, swaps, pays an x402 resource, or publishes risk data, and exposes an **on-chain agent reputation oracle** (`get_agent_reputation`) so any agent can read a counterparty's verified-safe track record as a composable trust signal.
|
|
9
|
+
|
|
10
|
+
SafeHands is:
|
|
11
|
+
|
|
12
|
+
> Read-only SafeHands by default, wallet handoff/user-signed prepare available, optional gated execution only with explicit env flags and authorization.
|
|
13
|
+
|
|
14
|
+
## Core capabilities
|
|
15
|
+
|
|
16
|
+
1. **Policy preflight**: Return deterministic `ALLOW`, `BLOCK`, `REQUIRE_CONFIRMATION`, or `PREPARE_ONLY` decisions for payments, approvals, swaps, contract calls, and x402 requests. In the read-only default, execution intents come back as `PREPARE_ONLY`: validated for you to sign externally, never executed here.
|
|
17
|
+
2. **Risk analysis**: Produce structured risk evidence, human-readable reports, and risk-score explanations.
|
|
18
|
+
3. **Pacific Mainnet token checks**: Classify tokens using the active Pharos network registry. Unsupported Pacific Mainnet tokens fail clearly instead of falling back to Atlantic/testnet addresses.
|
|
19
|
+
4. **x402 safety**: Validate URL, chain, token, payment limit, and signer readiness. Default Pacific Mainnet x402 token is USDC; WPROS/PROS-compatible wrapped payment is also supported. **Permissionless-first**: `safehands_x402_preflight` needs no key and no authorization, and self-signed x402 payments (the agent's own `X402_SIGNER_PRIVATE_KEY`) require no SafeHandsRegistry authorization; only the optional managed/custodial wallet path can be allowlist-gated.
|
|
20
|
+
5. **Gated execution**: Optional write tools for swaps, payments, approvals, x402 fetches, and risk publishing are disabled by default and require explicit operator configuration. These write tools are experimental and unaudited; they are opt-in, self-hosted, single-tenant only.
|
|
21
|
+
6. **On-chain layer**: Includes `SafeHandsRegistry` and `SafeHandsAttestation` contracts for registry and proof workflows when configured.
|
|
22
|
+
|
|
23
|
+
## What SafeHands refuses by default
|
|
24
|
+
|
|
25
|
+
- It will not hold or create a wallet in default mode.
|
|
26
|
+
- It will not sign transactions in default mode.
|
|
27
|
+
- It will not broadcast transactions in default mode.
|
|
28
|
+
- It will not allow unlimited token approvals by default.
|
|
29
|
+
- It will not execute or approve a token whose security intelligence is unavailable (provider outage / token not yet indexed): fail-closed, not confirmable; registry-canonical tokens excepted.
|
|
30
|
+
- It will not swap an input token it cannot price against the USD spend caps (denied by default).
|
|
31
|
+
- It will not fetch localhost/private-IP x402 URLs unless local demo mode is explicitly enabled.
|
|
32
|
+
- It will not use Atlantic/testnet token addresses for Pacific Mainnet token operations.
|
|
33
|
+
|
|
34
|
+
## Example tasks
|
|
35
|
+
|
|
36
|
+
- “Run SafeHands preflight before this agent approves USDC.”
|
|
37
|
+
- “Check whether this token is canonical on Pharos Pacific Mainnet.”
|
|
38
|
+
- “Explain why this approval was blocked.”
|
|
39
|
+
- “Check x402 preflight for this paid API before signing.”
|
|
40
|
+
- “Check my managed agent wallet health.”
|
|
41
|
+
|
|
42
|
+
## Information needed from the user or agent
|
|
43
|
+
|
|
44
|
+
- Target chain ID or network, if not using the default Pacific Mainnet.
|
|
45
|
+
- Transaction intent: payment, approval, swap, contract call, x402 fetch, or risk publishing.
|
|
46
|
+
- Token symbol/address and amount when relevant.
|
|
47
|
+
- Recipient, spender, target contract, or URL when relevant.
|
|
48
|
+
- Agent ID only when using managed-wallet mode.
|
|
49
|
+
|
|
50
|
+
## Deliverables
|
|
51
|
+
|
|
52
|
+
- Structured JSON response with `success`, `data`, `error`, and `timestamp`.
|
|
53
|
+
- SafeHands decision and risk level.
|
|
54
|
+
- Plain-English reasons and required actions.
|
|
55
|
+
- Network, token-registry, signer, and policy evidence where relevant.
|
|
56
|
+
|
|
57
|
+
## Required usage by other agents
|
|
58
|
+
|
|
59
|
+
Before executing any write transaction, a calling agent should call `safehands_preflight_check` or the relevant SafeHands tool. If SafeHands returns `BLOCK`, the agent must stop and explain the reason to the user. If SafeHands returns `REQUIRE_CONFIRMATION`, the agent should ask the user for explicit confirmation or further review before continuing.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
interface ISafeHandsRegistry {
|
|
5
|
+
function owner() external view returns (address);
|
|
6
|
+
|
|
7
|
+
function isAuthorizedOperator(address operator) external view returns (bool);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title SafeHandsAttestation
|
|
12
|
+
* @notice Immutable, append-only, privacy-preserving attestation ledger for
|
|
13
|
+
* SafeHands verified broadcasts — the proof half of the SafeHands
|
|
14
|
+
* on-chain layer (pair with the governed {SafeHandsRegistry}).
|
|
15
|
+
*
|
|
16
|
+
* "If SafeHands broadcasts it, SafeHands attests it."
|
|
17
|
+
*
|
|
18
|
+
* Attestations are write-once and never mutated or deleted: the integrity of the
|
|
19
|
+
* ledger IS its value. Only hashed context is published (preparedTransactionHash,
|
|
20
|
+
* txHash, policyHash, metadataHash) — never raw calldata, keys, recipients,
|
|
21
|
+
* amounts, or private intents.
|
|
22
|
+
*
|
|
23
|
+
* Attester authorization is delegated to {SafeHandsRegistry} so there is a single
|
|
24
|
+
* source of truth for "who is a SafeHands operator". Any contract or agent can
|
|
25
|
+
* compose on `isAttested(txHash)` as a trust gate.
|
|
26
|
+
*/
|
|
27
|
+
contract SafeHandsAttestation {
|
|
28
|
+
/// @notice The registry that governs operator authorization.
|
|
29
|
+
ISafeHandsRegistry public immutable registry;
|
|
30
|
+
|
|
31
|
+
// Action types as bytes32 (future-proof, privacy-preserving labels).
|
|
32
|
+
bytes32 public constant ACTION_PAYMENT = keccak256("PAYMENT");
|
|
33
|
+
bytes32 public constant ACTION_SWAP = keccak256("SWAP");
|
|
34
|
+
bytes32 public constant ACTION_BRIDGE = keccak256("BRIDGE");
|
|
35
|
+
bytes32 public constant ACTION_APPROVAL = keccak256("APPROVAL");
|
|
36
|
+
bytes32 public constant ACTION_CONTRACT_CALL = keccak256("CONTRACT_CALL");
|
|
37
|
+
bytes32 public constant ACTION_NFT = keccak256("NFT");
|
|
38
|
+
bytes32 public constant ACTION_DEFI = keccak256("DEFI");
|
|
39
|
+
bytes32 public constant ACTION_TREASURY = keccak256("TREASURY");
|
|
40
|
+
bytes32 public constant ACTION_AGENT_ACTION = keccak256("AGENT_ACTION");
|
|
41
|
+
bytes32 public constant ACTION_X402_PAYMENT = keccak256("X402_PAYMENT");
|
|
42
|
+
bytes32 public constant ACTION_UNKNOWN = keccak256("UNKNOWN");
|
|
43
|
+
|
|
44
|
+
struct Attestation {
|
|
45
|
+
bytes32 preparedTransactionHash;
|
|
46
|
+
bytes32 txHash;
|
|
47
|
+
uint256 chainId;
|
|
48
|
+
bytes32 actionType;
|
|
49
|
+
bytes32 policyHash;
|
|
50
|
+
bytes32 metadataHash;
|
|
51
|
+
address agent;
|
|
52
|
+
address attester;
|
|
53
|
+
uint64 timestamp;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
uint256 public attestationCount;
|
|
57
|
+
mapping(bytes32 => Attestation) private _byTxHash;
|
|
58
|
+
mapping(bytes32 => bool) public hasAttestedTx;
|
|
59
|
+
mapping(bytes32 => bool) public hasAttestedPreparedTx;
|
|
60
|
+
|
|
61
|
+
/// @notice Per-agent reputation: count of SafeHands-verified safe broadcasts
|
|
62
|
+
/// attributed to an address, plus recency. This is the composable
|
|
63
|
+
/// trust signal other agents can read via {reputationOf}.
|
|
64
|
+
mapping(address => uint256) public attestationCountByAgent;
|
|
65
|
+
mapping(address => uint64) public lastAttestationAt;
|
|
66
|
+
|
|
67
|
+
event SafeHandsAttested(
|
|
68
|
+
bytes32 indexed preparedTransactionHash,
|
|
69
|
+
bytes32 indexed txHash,
|
|
70
|
+
address indexed agent,
|
|
71
|
+
uint256 chainId,
|
|
72
|
+
bytes32 actionType,
|
|
73
|
+
bytes32 policyHash,
|
|
74
|
+
bytes32 metadataHash,
|
|
75
|
+
address attester
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
error ZeroRegistry();
|
|
79
|
+
error ZeroHash();
|
|
80
|
+
error InvalidChainId();
|
|
81
|
+
error UnauthorizedAttester();
|
|
82
|
+
error AlreadyAttested();
|
|
83
|
+
|
|
84
|
+
modifier onlyOperator() {
|
|
85
|
+
if (msg.sender != registry.owner() && !registry.isAuthorizedOperator(msg.sender)) {
|
|
86
|
+
revert UnauthorizedAttester();
|
|
87
|
+
}
|
|
88
|
+
_;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
constructor(address registry_) {
|
|
92
|
+
if (registry_ == address(0)) revert ZeroRegistry();
|
|
93
|
+
registry = ISafeHandsRegistry(registry_);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @notice Record an immutable attestation that SafeHands verified and broadcast
|
|
98
|
+
* an action. Reverts if either the txHash or preparedTransactionHash has
|
|
99
|
+
* already been attested (no overwrites, ever).
|
|
100
|
+
*/
|
|
101
|
+
function attest(
|
|
102
|
+
bytes32 preparedTransactionHash,
|
|
103
|
+
bytes32 txHash,
|
|
104
|
+
uint256 chainId,
|
|
105
|
+
bytes32 actionType,
|
|
106
|
+
bytes32 policyHash,
|
|
107
|
+
bytes32 metadataHash,
|
|
108
|
+
address agent
|
|
109
|
+
) external onlyOperator {
|
|
110
|
+
if (preparedTransactionHash == bytes32(0) || txHash == bytes32(0)) revert ZeroHash();
|
|
111
|
+
if (chainId == 0) revert InvalidChainId();
|
|
112
|
+
if (hasAttestedTx[txHash]) revert AlreadyAttested();
|
|
113
|
+
if (hasAttestedPreparedTx[preparedTransactionHash]) revert AlreadyAttested();
|
|
114
|
+
|
|
115
|
+
hasAttestedTx[txHash] = true;
|
|
116
|
+
hasAttestedPreparedTx[preparedTransactionHash] = true;
|
|
117
|
+
uint64 ts = uint64(block.timestamp);
|
|
118
|
+
_byTxHash[txHash] = Attestation({
|
|
119
|
+
preparedTransactionHash: preparedTransactionHash,
|
|
120
|
+
txHash: txHash,
|
|
121
|
+
chainId: chainId,
|
|
122
|
+
actionType: actionType,
|
|
123
|
+
policyHash: policyHash,
|
|
124
|
+
metadataHash: metadataHash,
|
|
125
|
+
agent: agent,
|
|
126
|
+
attester: msg.sender,
|
|
127
|
+
timestamp: ts
|
|
128
|
+
});
|
|
129
|
+
unchecked {
|
|
130
|
+
++attestationCount;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Per-agent reputation: only attribute when an agent address is provided,
|
|
134
|
+
// so the zero address is never polluted with a reputation.
|
|
135
|
+
if (agent != address(0)) {
|
|
136
|
+
unchecked {
|
|
137
|
+
++attestationCountByAgent[agent];
|
|
138
|
+
}
|
|
139
|
+
lastAttestationAt[agent] = ts;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
emit SafeHandsAttested(
|
|
143
|
+
preparedTransactionHash,
|
|
144
|
+
txHash,
|
|
145
|
+
agent,
|
|
146
|
+
chainId,
|
|
147
|
+
actionType,
|
|
148
|
+
policyHash,
|
|
149
|
+
metadataHash,
|
|
150
|
+
msg.sender
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Views (composable) ────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
function isAttested(bytes32 txHash) external view returns (bool) {
|
|
157
|
+
return hasAttestedTx[txHash];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function isPreparedAttested(bytes32 preparedTransactionHash) external view returns (bool) {
|
|
161
|
+
return hasAttestedPreparedTx[preparedTransactionHash];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function getAttestation(bytes32 txHash) external view returns (Attestation memory) {
|
|
165
|
+
return _byTxHash[txHash];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @notice Composable, privacy-preserving reputation for an agent/address:
|
|
170
|
+
* the count of SafeHands-verified safe broadcasts attributed to it and
|
|
171
|
+
* the most recent attestation time. Any contract or agent can read this
|
|
172
|
+
* as a trust signal. Zero count = no track record yet (treat as neutral,
|
|
173
|
+
* never as a negative — this ledger only records verified-safe actions).
|
|
174
|
+
*/
|
|
175
|
+
function reputationOf(address agent)
|
|
176
|
+
external
|
|
177
|
+
view
|
|
178
|
+
returns (uint256 verifiedCount, uint64 lastAt)
|
|
179
|
+
{
|
|
180
|
+
return (attestationCountByAgent[agent], lastAttestationAt[agent]);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.20;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
|
|
5
|
+
import "@openzeppelin/contracts/access/Ownable2Step.sol";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @title SafeHandsRegistry
|
|
9
|
+
* @notice L2 Merkle Batching architecture for SafeHands Risk Records.
|
|
10
|
+
* To prevent state bloat, risk records are no longer stored individually.
|
|
11
|
+
* Instead, operators commit a single Merkle Root representing thousands
|
|
12
|
+
* of risk assessments.
|
|
13
|
+
* @dev Ownership uses OpenZeppelin {Ownable2Step}: a transfer only completes when
|
|
14
|
+
* the incoming owner calls {acceptOwnership}, so a mistyped address can never
|
|
15
|
+
* orphan governance. {renounceOwnership} is disabled — this registry is the
|
|
16
|
+
* trust root for operators/agents and must always retain an owner.
|
|
17
|
+
*/
|
|
18
|
+
contract SafeHandsRegistry is Ownable2Step {
|
|
19
|
+
enum RiskLevel {
|
|
20
|
+
LOW,
|
|
21
|
+
MEDIUM,
|
|
22
|
+
HIGH,
|
|
23
|
+
CRITICAL
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
enum Recommendation {
|
|
27
|
+
PROCEED,
|
|
28
|
+
CAUTION,
|
|
29
|
+
BLOCK
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// @notice Operators may publish risk roots.
|
|
33
|
+
mapping(address => bool) public isAuthorizedOperator;
|
|
34
|
+
|
|
35
|
+
/// @notice Agents (managed wallets) cleared for guarded execution.
|
|
36
|
+
mapping(address => bool) public isAuthorizedAgent;
|
|
37
|
+
|
|
38
|
+
/// @notice The latest committed Merkle Root of all active risk records.
|
|
39
|
+
bytes32 public currentMerkleRoot;
|
|
40
|
+
|
|
41
|
+
/// @notice Off-chain availability pointer (e.g. IPFS CID or Arweave Tx)
|
|
42
|
+
string public currentDataURI;
|
|
43
|
+
|
|
44
|
+
event OperatorSet(address indexed operator, bool authorized);
|
|
45
|
+
event AgentSet(address indexed agent, bool authorized);
|
|
46
|
+
|
|
47
|
+
event RiskRootCommitted(
|
|
48
|
+
bytes32 indexed root,
|
|
49
|
+
string dataURI,
|
|
50
|
+
address indexed operator
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
error NotOperator();
|
|
54
|
+
error ZeroAddress();
|
|
55
|
+
error Expired();
|
|
56
|
+
error InvalidRoot();
|
|
57
|
+
error EmptyDataURI();
|
|
58
|
+
error RenounceDisabled();
|
|
59
|
+
|
|
60
|
+
modifier onlyOperator() {
|
|
61
|
+
if (msg.sender != owner() && !isAuthorizedOperator[msg.sender]) revert NotOperator();
|
|
62
|
+
_;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
constructor() Ownable(msg.sender) {}
|
|
66
|
+
|
|
67
|
+
// ─── Governance ────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
/// @notice Disabled. Hand ownership off via the two-step
|
|
70
|
+
/// {transferOwnership} + {acceptOwnership} instead — the registry
|
|
71
|
+
/// must never be left ownerless.
|
|
72
|
+
function renounceOwnership() public pure override {
|
|
73
|
+
revert RenounceDisabled();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setOperator(address operator, bool authorized) external onlyOwner {
|
|
77
|
+
if (operator == address(0)) revert ZeroAddress();
|
|
78
|
+
isAuthorizedOperator[operator] = authorized;
|
|
79
|
+
emit OperatorSet(operator, authorized);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function setAuthorizedAgent(address agent, bool authorized) external onlyOwner {
|
|
83
|
+
if (agent == address(0)) revert ZeroAddress();
|
|
84
|
+
isAuthorizedAgent[agent] = authorized;
|
|
85
|
+
emit AgentSet(agent, authorized);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function setAuthorizedAgents(address[] calldata agents, bool authorized) external onlyOwner {
|
|
89
|
+
for (uint256 i; i < agents.length; ++i) {
|
|
90
|
+
address agent = agents[i];
|
|
91
|
+
if (agent == address(0)) revert ZeroAddress();
|
|
92
|
+
isAuthorizedAgent[agent] = authorized;
|
|
93
|
+
emit AgentSet(agent, authorized);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ─── L2 Merkle Batching ────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @notice Commits a new Merkle root representing a batch of risk records.
|
|
101
|
+
* @param root The keccak256 Merkle root.
|
|
102
|
+
* @param dataURI Pointer to the off-chain JSON containing the leaves (IPFS).
|
|
103
|
+
*/
|
|
104
|
+
function commitRiskRoot(bytes32 root, string calldata dataURI) external onlyOperator {
|
|
105
|
+
if (root == bytes32(0)) revert InvalidRoot();
|
|
106
|
+
if (bytes(dataURI).length == 0) revert EmptyDataURI();
|
|
107
|
+
currentMerkleRoot = root;
|
|
108
|
+
currentDataURI = dataURI;
|
|
109
|
+
emit RiskRootCommitted(root, dataURI, msg.sender);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @notice Verifies if a specific risk record exists in the current Merkle tree.
|
|
114
|
+
* Contracts requiring composability must pass the proof.
|
|
115
|
+
*/
|
|
116
|
+
function verifyRiskRecord(
|
|
117
|
+
address target,
|
|
118
|
+
bytes32 actionHash,
|
|
119
|
+
uint8 score,
|
|
120
|
+
RiskLevel level,
|
|
121
|
+
Recommendation recommendation,
|
|
122
|
+
bytes32 policyVersionHash,
|
|
123
|
+
bytes32 evidenceHash,
|
|
124
|
+
uint64 expiresAt,
|
|
125
|
+
bytes32[] calldata proof
|
|
126
|
+
) public view returns (bool) {
|
|
127
|
+
// expiresAt == 0 means never expires. Non-zero values must be in the future.
|
|
128
|
+
if (expiresAt != 0 && expiresAt < block.timestamp) revert Expired();
|
|
129
|
+
|
|
130
|
+
// Double hashing to prevent leaf-node preimage attacks (standard practice)
|
|
131
|
+
bytes32 leaf = keccak256(
|
|
132
|
+
bytes.concat(
|
|
133
|
+
keccak256(
|
|
134
|
+
abi.encode(
|
|
135
|
+
target,
|
|
136
|
+
actionHash,
|
|
137
|
+
score,
|
|
138
|
+
level,
|
|
139
|
+
recommendation,
|
|
140
|
+
policyVersionHash,
|
|
141
|
+
evidenceHash,
|
|
142
|
+
expiresAt
|
|
143
|
+
)
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
);
|
|
147
|
+
return MerkleProof.verify(proof, currentMerkleRoot, leaf);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReadOnlyChainAccess } from "../lib/analysis/index.js";
|
|
2
|
+
import { type AgentRequest, type IntentResult } from "./agentIntentClassifier.js";
|
|
3
|
+
import { type GuardianPolicy, type PolicyOwner } from "./agentPolicyResolver.js";
|
|
4
|
+
import { type AgentDecision } from "./agentDecisionFormatter.js";
|
|
5
|
+
import { type CallerObligation } from "./agentRuntime.js";
|
|
6
|
+
export interface GuardianAgentOptions {
|
|
7
|
+
/** Policy overrides merged onto the owner baseline. */
|
|
8
|
+
policy?: Partial<GuardianPolicy>;
|
|
9
|
+
/** Policy owner (backend = public default). */
|
|
10
|
+
owner?: PolicyOwner;
|
|
11
|
+
/** Applied policy preset name — surfaced in the decision; never weakens. */
|
|
12
|
+
policyPreset?: string;
|
|
13
|
+
/** Optional read-only chain access (getCode/estimateGas/...). Omitted offline. */
|
|
14
|
+
access?: ReadOnlyChainAccess;
|
|
15
|
+
}
|
|
16
|
+
export interface AgentToAgentDecision extends AgentDecision {
|
|
17
|
+
caller: {
|
|
18
|
+
id: string;
|
|
19
|
+
obligation: CallerObligation;
|
|
20
|
+
responsibility: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export declare class SafeHandsGuardianAgent {
|
|
24
|
+
private readonly policy;
|
|
25
|
+
private readonly access?;
|
|
26
|
+
private readonly owner;
|
|
27
|
+
private readonly policyPreset?;
|
|
28
|
+
constructor(opts?: GuardianAgentOptions);
|
|
29
|
+
getPolicy(): GuardianPolicy;
|
|
30
|
+
/** Classify only (no routing) — useful for diagnostics and tests. */
|
|
31
|
+
classify(req: AgentRequest): IntentResult;
|
|
32
|
+
/** Full read-only SafeHands check: classify → route → policy → format. */
|
|
33
|
+
check(req: AgentRequest): Promise<AgentDecision>;
|
|
34
|
+
/** Agent-to-Agent entry: returns the verdict plus the caller's obligation. */
|
|
35
|
+
checkForAgent(callerId: string, req: AgentRequest): Promise<AgentToAgentDecision>;
|
|
36
|
+
}
|
|
37
|
+
/** Convenience factory. */
|
|
38
|
+
export declare function createGuardianAgent(opts?: GuardianAgentOptions): SafeHandsGuardianAgent;
|
|
39
|
+
//# sourceMappingURL=SafeHandsGuardianAgent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SafeHandsGuardianAgent.d.ts","sourceRoot":"","sources":["../../src/agent/SafeHandsGuardianAgent.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAClG,OAAO,EAA8B,KAAK,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE7G,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjF,OAAO,EAAiB,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAKzE,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACjC,+CAA+C;IAC/C,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B;AAED,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,gBAAgB,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAE3B,IAAI,GAAE,oBAAyB;IAO3C,SAAS,IAAI,cAAc;IAI3B,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,YAAY,GAAG,YAAY;IAIzC,0EAA0E;IACpE,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAgDtD,8EAA8E;IACxE,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAMxF;AAED,2BAA2B;AAC3B,wBAAgB,mBAAmB,CAAC,IAAI,CAAC,EAAE,oBAAoB,GAAG,sBAAsB,CAEvF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// ─── SafeHands Agent (read-only) ──────────────────────────────
|
|
2
|
+
// The complete pre-execution SafeHands Agent. It ORCHESTRATES existing
|
|
3
|
+
// capabilities — Phase 1 decision contract, Phase 2 analyzers, Phase 3 read-only
|
|
4
|
+
// handlers — and adds intent classification, policy enforcement, decision
|
|
5
|
+
// formatting, and the Agent-to-Agent obligation contract.
|
|
6
|
+
//
|
|
7
|
+
// It holds NO private keys, NO wallet, NO signer. There is no code path to sign,
|
|
8
|
+
// send, approve, swap, create/load a wallet, or publish. Read-only by
|
|
9
|
+
// construction; execution is disabled by default.
|
|
10
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
11
|
+
import { isExecutionAvailable } from "../lib/config.js";
|
|
12
|
+
import { classifyIntent } from "./agentIntentClassifier.js";
|
|
13
|
+
import { resolvePolicy, applyPolicy } from "./agentPolicyResolver.js";
|
|
14
|
+
import { routeIntent } from "./agentToolRouter.js";
|
|
15
|
+
import { formatDecision } from "./agentDecisionFormatter.js";
|
|
16
|
+
import { obligationFor } from "./agentRuntime.js";
|
|
17
|
+
import { enrich } from "./agentEnrich.js";
|
|
18
|
+
import { isDenylistedRecipient } from "../lib/recipientSafety.js";
|
|
19
|
+
import { POLICY_VERSION } from "../lib/policy/policyPresets.js";
|
|
20
|
+
export class SafeHandsGuardianAgent {
|
|
21
|
+
policy;
|
|
22
|
+
access;
|
|
23
|
+
owner;
|
|
24
|
+
policyPreset;
|
|
25
|
+
constructor(opts = {}) {
|
|
26
|
+
this.owner = opts.owner ?? "backend";
|
|
27
|
+
this.policy = resolvePolicy(this.owner, opts.policy);
|
|
28
|
+
this.access = opts.access;
|
|
29
|
+
this.policyPreset = opts.policyPreset;
|
|
30
|
+
}
|
|
31
|
+
getPolicy() {
|
|
32
|
+
return this.policy;
|
|
33
|
+
}
|
|
34
|
+
/** Classify only (no routing) — useful for diagnostics and tests. */
|
|
35
|
+
classify(req) {
|
|
36
|
+
return classifyIntent(req);
|
|
37
|
+
}
|
|
38
|
+
/** Full read-only SafeHands check: classify → route → policy → format. */
|
|
39
|
+
async check(req) {
|
|
40
|
+
const { intent } = classifyIntent(req);
|
|
41
|
+
try {
|
|
42
|
+
const routed = await routeIntent(intent, req, { access: this.access, policy: this.policy });
|
|
43
|
+
const enrichment = await enrich(req);
|
|
44
|
+
const policyOutcome = applyPolicy({
|
|
45
|
+
base: routed.decision,
|
|
46
|
+
intent,
|
|
47
|
+
req,
|
|
48
|
+
policy: this.policy,
|
|
49
|
+
analyzerDetails: routed.primaryDetails,
|
|
50
|
+
});
|
|
51
|
+
const decision = formatDecision({ intent, req, routed, policyOutcome, executionAvailable: isExecutionAvailable(), policyPreset: this.policyPreset, enrichment });
|
|
52
|
+
// Recipient denylist — escalate-only hard block (only tightens, never weakens).
|
|
53
|
+
if (decision.decision !== "BLOCK" && isDenylistedRecipient(req.to)) {
|
|
54
|
+
return {
|
|
55
|
+
...decision,
|
|
56
|
+
decision: "BLOCK",
|
|
57
|
+
riskLevel: "CRITICAL",
|
|
58
|
+
summary: `SafeHands → BLOCK: recipient ${req.to} is on the operator denylist (known-bad address).`,
|
|
59
|
+
reasons: ["Recipient is on the operator denylist (known-bad address).", ...decision.reasons],
|
|
60
|
+
recommendedAction: "Do not send to this address; it is flagged as known-bad by the operator.",
|
|
61
|
+
callerObligation: obligationFor("BLOCK").obligation,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return decision;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
// Fail safe — invalid/un-analyzable input never yields a silent ALLOW.
|
|
68
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
69
|
+
return {
|
|
70
|
+
intent,
|
|
71
|
+
decision: "REQUIRE_CONFIRMATION",
|
|
72
|
+
riskLevel: "UNKNOWN",
|
|
73
|
+
summary: `SafeHands → REQUIRE_CONFIRMATION for ${intent.replace(/_/g, " ")}. Input could not be analyzed.`,
|
|
74
|
+
reasons: [`Input could not be analyzed (validation/decoding failed): ${message}`],
|
|
75
|
+
recommendedAction: "Obtain explicit user/admin confirmation before any execution.",
|
|
76
|
+
evidence: { error: message },
|
|
77
|
+
readOnly: true,
|
|
78
|
+
executionAvailable: isExecutionAvailable(),
|
|
79
|
+
nextStep: "Pause and request explicit confirmation; correct the malformed input.",
|
|
80
|
+
callerObligation: obligationFor("REQUIRE_CONFIRMATION").obligation,
|
|
81
|
+
tool: "validation",
|
|
82
|
+
policyPreset: this.policyPreset ?? null,
|
|
83
|
+
policyVersion: POLICY_VERSION,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Agent-to-Agent entry: returns the verdict plus the caller's obligation. */
|
|
88
|
+
async checkForAgent(callerId, req) {
|
|
89
|
+
const result = await this.check(req);
|
|
90
|
+
const spec = obligationFor(result.decision);
|
|
91
|
+
return { ...result, caller: { id: callerId, obligation: spec.obligation, responsibility: spec.responsibility } };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Convenience factory. */
|
|
95
|
+
export function createGuardianAgent(opts) {
|
|
96
|
+
return new SafeHandsGuardianAgent(opts);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=SafeHandsGuardianAgent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SafeHandsGuardianAgent.js","sourceRoot":"","sources":["../../src/agent/SafeHandsGuardianAgent.ts"],"names":[],"mappings":"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"}
|