@carfiedli/runtime-guardrail 0.1.19

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 (86) hide show
  1. package/README.fe.md +256 -0
  2. package/README.hooks-security.md +1017 -0
  3. package/README.md +1316 -0
  4. package/dist/adapters/index.d.ts +1 -0
  5. package/dist/adapters/persistence/file-store.d.ts +18 -0
  6. package/dist/adapters/persistence/index.d.ts +4 -0
  7. package/dist/adapters/persistence/json-event-log.d.ts +31 -0
  8. package/dist/adapters/persistence/queue-store.d.ts +19 -0
  9. package/dist/adapters/persistence/snapshot-store.d.ts +14 -0
  10. package/dist/approval/approval-service.d.ts +27 -0
  11. package/dist/approval/approval-state-machine.d.ts +5 -0
  12. package/dist/approval/hitl/hitl-connector.d.ts +9 -0
  13. package/dist/approval/index.d.ts +4 -0
  14. package/dist/approval/run-hold-service.d.ts +16 -0
  15. package/dist/audit/audit-event-store.d.ts +12 -0
  16. package/dist/audit/audit-read-model-builder.d.ts +17 -0
  17. package/dist/audit/audit-service.d.ts +18 -0
  18. package/dist/audit/incident-query-service.d.ts +7 -0
  19. package/dist/audit/index.d.ts +5 -0
  20. package/dist/audit/metrics-projection.d.ts +10 -0
  21. package/dist/bootstrap/create-runtime-guardrail-plugin.d.ts +3 -0
  22. package/dist/bootstrap/dependency-container.d.ts +2 -0
  23. package/dist/bootstrap/index.d.ts +3 -0
  24. package/dist/bootstrap/runtime-facade.d.ts +31 -0
  25. package/dist/compat/index.d.ts +1 -0
  26. package/dist/compat/legacy-types.d.ts +29 -0
  27. package/dist/contracts/core.d.ts +277 -0
  28. package/dist/contracts/events.d.ts +35 -0
  29. package/dist/contracts/host.d.ts +239 -0
  30. package/dist/contracts/index.d.ts +6 -0
  31. package/dist/contracts/operator.d.ts +110 -0
  32. package/dist/execution/egress-mediator.d.ts +7 -0
  33. package/dist/execution/execution-broker.d.ts +13 -0
  34. package/dist/execution/execution-plan-builder.d.ts +12 -0
  35. package/dist/execution/index.d.ts +4 -0
  36. package/dist/execution/model-governance-service.d.ts +7 -0
  37. package/dist/index.d.ts +29 -0
  38. package/dist/index.js +23 -0
  39. package/dist/openclaw/hooks/egress-adapter.d.ts +9 -0
  40. package/dist/openclaw/hooks/hook-registry.d.ts +21 -0
  41. package/dist/openclaw/hooks/hook-result-mapper.d.ts +43 -0
  42. package/dist/openclaw/hooks/hook-types.d.ts +31 -0
  43. package/dist/openclaw/hooks/index.d.ts +8 -0
  44. package/dist/openclaw/hooks/ingress-adapter.d.ts +14 -0
  45. package/dist/openclaw/hooks/llm-request-adapter.d.ts +9 -0
  46. package/dist/openclaw/hooks/persist-adapter.d.ts +30 -0
  47. package/dist/openclaw/hooks/tool-call-adapter.d.ts +7 -0
  48. package/dist/openclaw/index.d.ts +4 -0
  49. package/dist/openclaw/plugin-runtime.d.ts +103 -0
  50. package/dist/openclaw/rpc-handlers.d.ts +20 -0
  51. package/dist/openclaw/skills-availability.d.ts +10 -0
  52. package/dist/openclaw/skills-upload.d.ts +17 -0
  53. package/dist/openclaw/testing/index.d.ts +1 -0
  54. package/dist/openclaw/testing/mock-openclaw-api.d.ts +74 -0
  55. package/dist/operator/cli/register-cli.d.ts +4 -0
  56. package/dist/operator/command-service.d.ts +15 -0
  57. package/dist/operator/index.d.ts +5 -0
  58. package/dist/operator/query-service.d.ts +21 -0
  59. package/dist/operator/reporting/report-service.d.ts +9 -0
  60. package/dist/operator/rpc/register-rpc.d.ts +5 -0
  61. package/dist/policy/detectors/detector-port.d.ts +23 -0
  62. package/dist/policy/finding-normalizer.d.ts +3 -0
  63. package/dist/policy/index.d.ts +4 -0
  64. package/dist/policy/policy-engine.d.ts +8 -0
  65. package/dist/policy/stage-resolver.d.ts +7 -0
  66. package/dist/runtime-core/device-id.d.ts +15 -0
  67. package/dist/runtime-core/evaluate-service.d.ts +91 -0
  68. package/dist/runtime-core/index.d.ts +10 -0
  69. package/dist/runtime-core/memory-audit-logger.d.ts +55 -0
  70. package/dist/runtime-core/memory-store.d.ts +141 -0
  71. package/dist/runtime-core/remote-guard-request-builder.d.ts +15 -0
  72. package/dist/runtime-core/remote-guard-transport.d.ts +79 -0
  73. package/dist/runtime-core/remote-guard-types.d.ts +183 -0
  74. package/dist/runtime-core/remote-policy-evaluator.d.ts +51 -0
  75. package/dist/runtime-core/skill-name-resolver.d.ts +31 -0
  76. package/dist/runtime-core/sync-remote-evaluate.d.ts +29 -0
  77. package/dist/runtime-core/sync-remote-worker.d.ts +14 -0
  78. package/dist/runtime-core/sync-remote-worker.js +2 -0
  79. package/dist/runtime-core/telemetry-service.d.ts +94 -0
  80. package/dist/runtime-core/telemetry-types.d.ts +181 -0
  81. package/dist/types.d.ts +224 -0
  82. package/dist/version.d.ts +1 -0
  83. package/openclaw.plugin.json +76 -0
  84. package/package.json +71 -0
  85. package/remote-guard-config.json +30 -0
  86. package/scripts/runtime-guardrailctl.mjs +864 -0
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ /* @tencent/runtime-guardrail - UNLICENSED */
2
+ "use strict";var Ke=Object.defineProperty;var jo=Object.getOwnPropertyDescriptor;var Ko=Object.getOwnPropertyNames;var zo=Object.prototype.hasOwnProperty;var Wo=(t,e)=>{for(var o in e)Ke(t,o,{get:e[o],enumerable:!0})},Qo=(t,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Ko(e))!zo.call(t,n)&&n!==o&&Ke(t,n,{get:()=>e[n],enumerable:!(r=jo(e,n))||r.enumerable});return t};var Jo=t=>Qo(Ke({},"__esModule",{value:!0}),t);var Xn={};Wo(Xn,{ApprovalStateMachine:()=>le,ApprovalStore:()=>xe,AuditReadModelBuilder:()=>ke,DefaultAuditService:()=>ye,DefaultEgressMediator:()=>me,DefaultExecutionBroker:()=>fe,DefaultExecutionPlanBuilder:()=>ge,DefaultModelGovernanceService:()=>pe,DefaultOperatorCommandService:()=>we,DefaultOperatorQueryService:()=>Se,DefaultReportService:()=>Re,EvaluateServiceImpl:()=>F,InMemoryApprovalService:()=>ce,InMemoryEventLogCorruptionSink:()=>J,InMemoryFileStore:()=>re,InMemoryRunHoldService:()=>de,IncidentQueryService:()=>ve,IncidentStore:()=>Te,JsonAuditEventStore:()=>he,JsonEventLog:()=>ne,JsonQueueStore:()=>Ot,JsonSnapshotStore:()=>ie,MemoryAuditLogger:()=>De,MemoryStore:()=>Z,MockOpenClawApi:()=>Be,NoopHitlConnector:()=>ue,NoopPolicyDetector:()=>se,PluginRuntime:()=>Q,PolicyEngine:()=>ae,RemotePolicyEvaluator:()=>_e,RunHoldStore:()=>He,SHIELD_CLI_COMMANDS:()=>P,SHIELD_GATEWAY_EVENTS:()=>No,SHIELD_RPC_METHODS:()=>k,TelemetryService:()=>$e,activate:()=>Vn,buildDecisionAuditEvent:()=>X,buildMetricsProjection:()=>$t,buildRemoteGuardRequest:()=>ft,buildRemoteGuardRequestSync:()=>ht,buildTelemetryConfigFromRemoteGuard:()=>yo,clearRemoteGuardConfigCache:()=>yt,createAutoConfigRemoteGuardTransport:()=>fr,createAutoConfigRemoteGuardTransportSync:()=>hr,createBeforeMessageWriteAdapter:()=>dt,createEgressAdapter:()=>ct,createIngressAdapter:()=>it,createLlmRequestAdapter:()=>at,createMemoryAuditLogger:()=>nr,createMockOpenClawApi:()=>Kn,createNoopAuditLogger:()=>mt,createNoopPolicyEvaluator:()=>Xo,createPromptBuildAdapter:()=>st,createRemoteGuardHttpTransport:()=>te,createRemotePolicyEvaluator:()=>gt,createRuntimeFacade:()=>jn,createRuntimeGuardrailPlugin:()=>Uo,createSyncRemoteEvaluateFn:()=>St,createToolCallAdapter:()=>lt,createToolResultPersistAdapter:()=>ut,default:()=>Yn,getDefaultRemoteGuardAuthConfigPath:()=>ao,getDefaultRemoteGuardConfigPath:()=>so,getRemoteGuardConfig:()=>mr,loadRemoteGuardConfig:()=>uo,loadRemoteGuardConfigSync:()=>C,mapApprovalRequestToTicket:()=>tr,mapBeforeMessageWriteToEnvelope:()=>Ye,mapDecisionToBeforeMessageWriteResult:()=>Ae,mapDecisionToBeforePromptBuildResult:()=>ot,mapDecisionToBeforeToolCallResult:()=>rt,mapDecisionToHookResult:()=>Yo,mapDecisionToMessageSendingResult:()=>nt,mapDecisionToToolResultPersistResult:()=>Pe,mapLlmRequestToEnvelope:()=>Qe,mapMessageReceivedToEnvelope:()=>ze,mapMessageSendingToEnvelope:()=>Ve,mapPolicyResultToDecision:()=>er,mapPromptBuildToEnvelope:()=>We,mapRemoteGuardResponseToPolicyResponse:()=>Ne,mapRunHoldToTicket:()=>or,mapToolCallToEnvelope:()=>Je,mapToolResultPersistToEnvelope:()=>Xe,mergeHookResults:()=>Ut,normalizeSignals:()=>Gt,register:()=>Ft,registerRpcAndCliHandlers:()=>Mt,registerShieldCli:()=>qn,registerShieldHooks:()=>pt,registerShieldRpc:()=>Bn,registerTelemetryService:()=>bt,resolveRuntimeRemoteGuardConfigPath:()=>vt,resolveStageAction:()=>Nt,resolveTelemetryServiceConfig:()=>Fe,setRemoteGuardConfig:()=>gr,syncRemoteEvaluate:()=>mo});module.exports=Jo(Xn);var b=t=>typeof t=="object"&&t!==null?t:{},R=(t,e)=>{for(let o of e)if(typeof t[o]=="string")return t[o]},K=(t,e)=>{for(let o of e)if(o in t)return t[o]},L=t=>R(t,["agentId"]),O=(t,e)=>R(t,["runId"])??R(e,["runId"]),G=(t,e)=>R(t,["sessionId"])??R(e,["sessionId"]),N=(t,e)=>R(t,["sessionKey"])??R(e,["sessionKey"]),$={hostType:"openclaw"},ze=(t,e)=>{let o=b(e),r=b(t);return{canonicalHook:"message_received",hostHookName:"message_received",surface:"ingress",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{message:K(r,["content","message"])}}},We=(t,e)=>{let o=b(e),r=b(t);return{canonicalHook:"message_received",hostHookName:"before_prompt_build",surface:"ingress",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{message:K(r,["prompt","message","content"])}}},Qe=(t,e)=>{let o=b(e),r=b(t);return{canonicalHook:"before_llm_request",hostHookName:"llm_input",surface:"llm",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{provider:R(r,["provider"])??"unknown",model:R(r,["model"])??"unknown",prompt:R(r,["prompt"])??"",systemPrompt:R(r,["systemPrompt"]),historyMessages:Array.isArray(r.historyMessages)?r.historyMessages:[],skillNames:Array.isArray(r.skillNames)?r.skillNames.filter(n=>typeof n=="string"):void 0}}},Je=(t,e)=>{let o=b(e),r=b(t),n=K(r,["params","toolParams"]),i=typeof n=="object"&&n!==null?n:{};return{canonicalHook:"before_tool_call",hostHookName:"before_tool_call",surface:"tool",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{toolName:R(r,["toolName"])??"unknown_tool",toolParams:i}}},Ve=(t,e)=>{let o=b(e),r=b(t);return{canonicalHook:"message_sending",hostHookName:"message_sending",surface:"egress",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{message:K(r,["content","message"]),channel:R(r,["channel"])??R(o,["channelId"])}}},Ye=(t,e)=>{let o=b(e),r=b(t);return{canonicalHook:"before_message_write",hostHookName:"before_message_write",surface:"persist",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{message:K(r,["message","content"])}}},Xe=(t,e)=>{let o=b(e),r=b(t),n=K(r,["message"]);return{canonicalHook:"tool_result_persist",hostHookName:"tool_result_persist",surface:"persist",timestamp:Date.now(),agentId:L(o),runId:O(r,o),sessionId:G(r,o),sessionKey:N(r,o),hostInfo:$,hostContext:o,payload:{message:n,toolName:R(r,["toolName"]),toolResult:n}}};var Ze=()=>({action:"allow",modifications:[],decision:"allow",reason:"",findings:[],auditLog:null}),Bt=t=>({action:"hold",modifications:[],decision:"hold",reason:t,findings:[],auditLog:null}),qt=t=>{switch(t){case"block":return 0;case"redact":return 1;case"hold":return 2;default:return 3}},Vo=t=>{switch(t.type){case"block":return{action:"block",modifications:[],decision:"block",reason:t.reason,findings:[],auditLog:null};case"redact":return{action:"redact",modifications:t.targets.map(o=>({type:"redact",target:o.field,replacement:o.replacement,ruleId:o.ruleId})),decision:"redact",reason:`Redacted ${t.targets.length} field(s)`,findings:[],auditLog:null};case"queue_approval":return Bt("Waiting for approval");case"create_run_hold":return Bt("Execution paused by run hold");default:return Ze()}},Ut=t=>{if(t.length===0)return Ze();let[e]=[...t].sort((o,r)=>qt(o.action)-qt(r.action));return{...e,modifications:t.flatMap(o=>o.modifications)}},Yo=t=>t.effects.length===0?{...Ze(),findings:t.findings.map(o=>({detector:o.detector,reasonCode:o.reasonCode,severity:o.severity,summary:o.summary,tags:o.tags,evidence:o.evidence,ruleIds:o.matchedRuleIds}))}:{...Ut(t.effects.map(Vo)),findings:t.findings.map(o=>({detector:o.detector,reasonCode:o.reasonCode,severity:o.severity,summary:o.summary,tags:o.tags,evidence:o.evidence,ruleIds:o.matchedRuleIds})),auditLog:{decisionId:t.decisionId,reasonCodes:t.reasonCodes,simulated:t.simulated}},T=(t,e)=>{for(let o of t.effects)if(o.type==="redact"){for(let r of o.targets)if(e.includes(r.field)){if(typeof r.value=="string")return r.value;if(typeof r.replacement=="string")return r.replacement}}},Y=t=>t.effects.find(e=>e.type==="block")?.reason,et=t=>t.effects.some(e=>e.type==="queue_approval"||e.type==="create_run_hold"),Ie=t=>{for(let e of t.effects){if(e.type==="queue_approval")return e.ticket.reason??"runtime-guardrail\u68C0\u6D4B\u5230\u98CE\u9669\uFF0C\u8BF7\u8FDB\u884C\u5BA1\u6279";if(e.type==="create_run_hold")return e.hold.reason??"runtime-guardrail\u68C0\u6D4B\u5230\u98CE\u9669\uFF0C\u8BF7\u8FDB\u884C\u5BA1\u6279"}},jt=(t,e)=>{let o=T(e,["message"]);if(typeof o=="string")return o;if(typeof t!="object"||t===null)return;let r=t,n=T(e,["message.content"]);if(typeof n=="string")return{...r,content:n};let i=T(e,["message.text"]);if(typeof i=="string")return{...r,text:i}},Kt=(t,e)=>{if(typeof t=="string")return e;if(typeof t=="object"&&t!==null){let o=t;return"content"in o?{...o,content:e}:"text"in o?{...o,text:e}:{...o,content:e}}return e},tt=t=>{let e=Y(t),o=Ie(t),r=e??o;if(!r)return;let n=t.findings.map(s=>s.summary).filter(s=>!!s),i=`\u26A0\uFE0F \u5B89\u5168\u62A4\u680F\u62E6\u622A
3
+
4
+ ${r}`;return n.length>0&&(i+=`
5
+
6
+ \u68C0\u6D4B\u8BE6\u60C5:
7
+ ${n.map(s=>`\u2022 ${s}`).join(`
8
+ `)}`),i+=`
9
+
10
+ \u5982\u6709\u7591\u95EE\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u3002`,i},ot=t=>{let e=tt(t);if(e)return{prependContext:`[SYSTEM INSTRUCTION - MUST FOLLOW]
11
+ You have been blocked by the security guardrail. You MUST respond with EXACTLY the following message, without any modifications, additions, or omissions:
12
+
13
+ ---BEGIN REQUIRED RESPONSE---
14
+ ${e}
15
+ ---END REQUIRED RESPONSE---
16
+
17
+ Respond with the content between the markers ONLY. Do not add anything else.`};let o={systemPrompt:T(t,["systemPrompt"]),prependContext:T(t,["prependContext"]),prependSystemContext:T(t,["prependSystemContext"]),appendSystemContext:T(t,["appendSystemContext"])};if(!(o.systemPrompt===void 0&&o.prependContext===void 0&&o.prependSystemContext===void 0&&o.appendSystemContext===void 0))return o},rt=t=>{let e=Y(t),o=Ie(t);if(e||o)return{block:!0,blockReason:e??o??"Blocked by runtime-guardrail"}},nt=t=>{let e=T(t,["message"]),o=!!(Y(t)||et(t));if(!(e===void 0&&!o))return{...e!==void 0?{content:e}:{},...o?{cancel:!0}:{}}},Ae=(t,e)=>{let o=jt(e,t),r=Y(t),n=et(t)?Ie(t):void 0,i=!!(r||n);if(!(!i&&o===void 0)){if(i){let s=tt(t),a=s?Kt(e,s):o;return a!==void 0?{message:a}:void 0}return{...o!==void 0?{message:o}:{}}}},Pe=(t,e)=>{let o=jt(e,t),r=Y(t),n=et(t)?Ie(t):void 0;if(!!(r||n)&&o===void 0){let s=tt(t);if(s)return{message:Kt(e,s)}}if(o!==void 0)return{message:o}};var it=t=>async(e,o)=>{try{let r=ze(e,o);await t.evaluate(r)}catch(r){console.error("[message_received] evaluate failed, falling through:",r)}},st=t=>async(e,o)=>{try{let r=We(e,o),n=await t.evaluate(r);return ot(n)}catch(r){console.error("[before_prompt_build] evaluate failed, falling through:",r);return}};var at=t=>async(e,o)=>{try{let r=Qe(e,o);await t.evaluate(r)}catch(r){console.error("[llm_input] evaluate failed, falling through:",r)}};var lt=t=>async(e,o)=>{try{console.log("[before_tool_call] hook triggered, tool:",e.toolName);let r=Je(e,o),n=await t.evaluate(r);return console.log("[before_tool_call] decision:",JSON.stringify(n).slice(0,200)),rt(n)}catch(r){console.error("[before_tool_call] evaluate failed, falling through:",r);return}};var ct=t=>async(e,o)=>{try{let r=Ve(e,o),n=await t.evaluate(r);return nt(n)}catch(r){console.error("[message_sending] evaluate failed, falling through:",r);return}};var Ee=t=>t.effects.some(e=>e.type==="block"||e.type==="redact"),dt=(t,e)=>(o,r)=>{try{let n=Ye(o,r);if(e){let i=e(n);return Ee(i)?Ae(i,o.message):void 0}if(typeof t.evaluateSync=="function"){let i=t.evaluateSync(n);if(i&&Ee(i))return Ae(i,o.message)}}catch(n){console.error("[before_message_write] evaluate failed, falling through:",n)}},ut=(t,e)=>(o,r)=>{try{let n=Xe(o,r);if(e){let i=e(n);return Ee(i)?Pe(i,o.message):void 0}if(typeof t.evaluateSync=="function"){let i=t.evaluateSync(n);if(i&&Ee(i))return Pe(i,o.message)}}catch(n){console.error("[tool_result_persist] evaluate failed, falling through:",n)}};var pt=(t,e,o)=>{let r=["message_received","before_prompt_build","llm_input","before_tool_call","message_sending","before_message_write","tool_result_persist"];console.log("[runtime-guardrail] Registering shield hooks..."),t.on("message_received",it(e)),t.on("before_prompt_build",st(e)),t.on("llm_input",at(e)),t.on("before_tool_call",lt(e)),t.on("message_sending",ct(e)),t.on("before_message_write",dt(e,o)),t.on("tool_result_persist",ut(e,o)),console.log("[runtime-guardrail] All hooks registered:",r.join(", ")),o&&console.log("[runtime-guardrail] Sync remote evaluate enabled for persist hooks (Worker + SAB + Atomics.wait)");let n=t.log;n&&typeof n.info=="function"&&n.info(`[runtime-guardrail] registered shield hooks: ${r.join(", ")}`)};var zt=t=>({rawAction:"allow",effectiveAction:"allow",severity:"low",simulatedBlock:!1,simulatedRedaction:!1,redactions:[],redactedTexts:{},matched:[],appliedExceptions:[],redactionRules:[],tags:[],durationMs:t,findings:[],modifications:[]}),Xo=()=>({async evaluate(){return zt(0)},evaluateSync(){return zt(0)}}),mt=()=>({async logDecision(){},logDecisionSync(){}}),F=class{constructor(e,o,r){this.config=e;this.policyEvaluator=o;this.auditLogger=r}async evaluate(e){let o=Date.now(),r=this.createDecisionId();try{let n=this.mapEnvelopeToPolicyInput(e),i=await this.policyEvaluator.evaluate(n,"enforce_all"),s=this.mapPolicyResultToDecision(r,e,i,o);return await this.auditLogger.logDecision(s),s}catch(n){console.error("[runtime-guardrail] Policy evaluation failed, applying failMode:",this.config.failMode,n);let i=this.buildFailModeDecision(r,e,o,n);try{await this.auditLogger.logDecision(i)}catch{}return i}}evaluateSync(e){if(typeof this.policyEvaluator.evaluateSync!="function")return;let o=Date.now(),r=this.createDecisionId();try{let n=this.mapEnvelopeToPolicyInput(e),i=this.policyEvaluator.evaluateSync(n,"enforce_all"),s=this.mapPolicyResultToDecision(r,e,i,o);return typeof this.auditLogger.logDecisionSync=="function"?this.auditLogger.logDecisionSync(s):this.auditLogger.logDecision(s),s}catch(n){console.error("[runtime-guardrail] Sync policy evaluation failed, applying failMode:",this.config.failMode,n);let i=this.buildFailModeDecision(r,e,o,n);try{typeof this.auditLogger.logDecisionSync=="function"?this.auditLogger.logDecisionSync(i):this.auditLogger.logDecision(i)}catch{}return i}}createDecisionId(){return`decision_${Date.now()}_${Math.random().toString(36).slice(2,10)}`}mapEnvelopeToPolicyInput(e){switch(e.canonicalHook){case"before_llm_request":return{surface:e.surface,agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,sessionKey:e.sessionKey,provider:e.payload.provider,model:e.payload.model,prompt:e.payload.prompt,systemPrompt:e.payload.systemPrompt,historyMessages:e.payload.historyMessages,skillNames:e.payload.skillNames};case"before_tool_call":return{surface:e.surface,agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,sessionKey:e.sessionKey,toolName:e.payload.toolName,toolParams:e.payload.toolParams};case"message_sending":return{surface:e.surface,agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,sessionKey:e.sessionKey,message:e.payload.message};case"before_message_write":case"tool_result_persist":return{surface:e.surface,agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,sessionKey:e.sessionKey,message:e.payload.message,toolName:e.payload.toolName,toolResult:e.payload.toolResult};default:return{surface:e.surface,agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,sessionKey:e.sessionKey,message:e.payload.message}}}mapPolicyResultToDecision(e,o,r,n){let i=(r.findings??[]).map((a,l)=>({findingId:`${e}_finding_${l}`,detector:a.detector,reasonCode:this.normalizeReasonCode(a.reasonCode),severity:a.severity,summary:a.summary,tags:a.tags,evidence:a.evidence,matchedRuleIds:a.ruleIds})),s=this.mapPolicyResultToEffects(r,i);return{decisionId:e,envelopeRef:{canonicalHook:o.canonicalHook,hostHookName:o.hostHookName,surface:o.surface,agentId:o.agentId,runId:o.runId,sessionId:o.sessionId},stage:"enforce_all",severity:r.severity,findings:i,effects:s,reasonCodes:i.map(a=>a.reasonCode),tags:r.tags,simulated:this.config.simulated,durationMs:Math.max(Date.now()-n,r.durationMs)}}mapPolicyResultToEffects(e,o){let r=[];return e.effectiveAction==="block"&&r.push({type:"block",reason:e.blockReason??o[0]?.summary??"Blocked by policy"}),e.effectiveAction==="redact"&&r.push({type:"redact",targets:e.redactions.map(n=>({field:n.field,replacement:n.replacement,ruleId:n.ruleId,value:e.redactedTexts?.[n.field]}))}),e.approvalRequest&&r.push({type:"queue_approval",ticket:{category:e.approvalRequest.category,reason:e.approvalRequest.reason,severity:e.approvalRequest.severity,tags:e.approvalRequest.tags,agentId:e.approvalRequest.agentId,runId:e.approvalRequest.runId,sessionId:e.approvalRequest.sessionId,toolName:e.approvalRequest.toolName,sourceBinding:e.approvalRequest.source?{source:e.approvalRequest.source,externalApprovalId:e.approvalRequest.externalApprovalId}:void 0}}),e.runHold&&r.push({type:"create_run_hold",hold:{toolName:e.runHold.toolName,toolParams:e.runHold.toolParams,stageAtHold:e.runHold.stageAtHold,failModeAtHold:e.runHold.failModeAtHold,category:e.runHold.category,severity:e.runHold.severity,reason:e.runHold.reason,agentId:e.runHold.agentId,runId:e.runHold.runId,sessionId:e.runHold.sessionId}}),r.length===0&&r.push({type:"allow"}),r}normalizeReasonCode(e){switch(e){case"prompt_injection":case"retrieval_poisoning":case"sensitive_action":case"egress_exfiltration":case"mcp_allowlist_violation":case"model_governance_violation":case"approval_required":return e;default:return"unknown"}}buildFailModeDecision(e,o,r,n){let i=this.config.failMode==="open",s=n instanceof Error?n.message:String(n),a=i?[{type:"allow"}]:[{type:"block",reason:`Policy evaluation failed (failMode=closed): ${s}`}];return{decisionId:e,envelopeRef:{canonicalHook:o.canonicalHook,hostHookName:o.hostHookName,surface:o.surface,agentId:o.agentId,runId:o.runId,sessionId:o.sessionId},stage:"enforce_all",severity:i?"low":"critical",findings:[],effects:a,reasonCodes:[],tags:[`failMode:${this.config.failMode}`,"evaluation_error"],simulated:this.config.simulated,durationMs:Math.max(Date.now()-r,0)}}};var Ce=(t,e="allow")=>({rawAction:e,effectiveAction:e,severity:"low",blockReason:e==="block"?"Blocked by remote policy fallback":void 0,simulatedBlock:!1,simulatedRedaction:!1,redactions:[],redactedTexts:{},matched:[],appliedExceptions:[],redactionRules:[],tags:[],durationMs:t,findings:[],modifications:[]}),Zo=(t,e,o)=>{if(!t)return Ce(e,o);let r=t.rawAction??t.effectiveAction??o,n=t.effectiveAction??r;return{rawAction:r,effectiveAction:n,severity:t.severity??"low",blockReason:t.blockReason,simulatedBlock:t.simulatedBlock??!1,simulatedRedaction:t.simulatedRedaction??!1,redactions:t.redactions??[],redactedTexts:t.redactedTexts??{},matched:t.matched??[],appliedExceptions:t.appliedExceptions??[],redactionRules:t.redactionRules??[],tags:t.tags??[],durationMs:t.durationMs??e,findings:t.findings??[],modifications:t.modifications??[],approvalRequest:t.approvalRequest,runHold:t.runHold}},_e=class{constructor(e={}){this.transport=e.transport,this.enableWarnLog=e.enableWarnLog??!0,this.fallbackAction=e.fallbackAction??"allow"}async evaluate(e,o){let r=Date.now();if(!this.transport)return Ce(Date.now()-r,this.fallbackAction);try{let n=await this.transport({input:e,stage:o});return Zo(n,Date.now()-r,this.fallbackAction)}catch(n){return this.enableWarnLog&&console.warn("[runtime-guardrail] remote policy transport failed, fallback applied",n),Ce(Date.now()-r,this.fallbackAction)}}evaluateSync(e,o){return Ce(0,this.fallbackAction)}},gt=t=>new _e(t);var er=(t,e,o,r)=>{let n=`decision_${Date.now()}_${Math.random().toString(36).slice(2,10)}`,i=(t.findings??[]).map((a,l)=>({findingId:`${n}_finding_${l}`,detector:a.detector,reasonCode:rr(a.reasonCode),severity:a.severity,summary:a.summary,tags:a.tags,evidence:a.evidence,matchedRuleIds:a.ruleIds})),s=[];return t.effectiveAction==="block"&&s.push({type:"block",reason:t.blockReason??i[0]?.summary??"Blocked by policy"}),t.effectiveAction==="redact"&&s.push({type:"redact",targets:t.redactions.map(a=>({field:a.field,replacement:a.replacement,ruleId:a.ruleId}))}),t.approvalRequest&&s.push({type:"queue_approval",ticket:{category:t.approvalRequest.category,reason:t.approvalRequest.reason,severity:t.approvalRequest.severity,tags:t.approvalRequest.tags,agentId:t.approvalRequest.agentId,runId:t.approvalRequest.runId,sessionId:t.approvalRequest.sessionId,toolName:t.approvalRequest.toolName,sourceBinding:t.approvalRequest.source?{source:t.approvalRequest.source,externalApprovalId:t.approvalRequest.externalApprovalId}:void 0}}),t.runHold&&s.push({type:"create_run_hold",hold:{toolName:t.runHold.toolName,toolParams:t.runHold.toolParams,stageAtHold:t.runHold.stageAtHold,failModeAtHold:t.runHold.failModeAtHold,category:t.runHold.category,severity:t.runHold.severity,reason:t.runHold.reason,agentId:t.runHold.agentId,runId:t.runHold.runId,sessionId:t.runHold.sessionId}}),s.length===0&&s.push({type:"allow"}),{decisionId:n,envelopeRef:e,stage:o,severity:t.severity,findings:i,effects:s,reasonCodes:i.map(a=>a.reasonCode),tags:t.tags,simulated:r,durationMs:t.durationMs}},tr=t=>({approvalId:t.approvalId,incidentId:t.incidentId,status:t.status,category:t.category,severity:t.severity,reason:t.reason,tags:t.tags,agentId:t.agentId,runId:t.runId,sessionId:t.sessionId,toolName:t.toolName,sourceBinding:t.source?{source:t.source,externalApprovalId:t.externalApprovalId}:void 0,createdAt:t.createdAt,resolvedAt:t.resolvedAt,resolvedBy:t.resolvedBy,note:t.note}),or=t=>({holdId:t.holdId,approvalId:t.approvalId,status:t.status,toolName:t.toolName,toolParams:t.toolParams,stageAtHold:t.stageAtHold,failModeAtHold:t.failModeAtHold,category:t.category,severity:t.severity,reason:t.reason,agentId:t.agentId,runId:t.runId,sessionId:t.sessionId,createdAt:t.createdAt,resumedAt:t.resumedAt,cancelledAt:t.cancelledAt}),X=t=>({eventId:`audit_${t.decisionId}`,eventType:t.effects.some(e=>e.type==="block")?"decision.blocked":"decision.made",timestamp:Date.now(),decisionId:t.decisionId,agentId:t.envelopeRef.agentId,runId:t.envelopeRef.runId,sessionId:t.envelopeRef.sessionId,payload:{canonicalHook:t.envelopeRef.canonicalHook,hostHookName:t.envelopeRef.hostHookName,surface:t.envelopeRef.surface,stage:t.stage,severity:t.severity,findings:t.findings,effects:t.effects,reasonCodes:t.reasonCodes,tags:t.tags,simulated:t.simulated,durationMs:t.durationMs},schemaVersion:1}),rr=t=>{switch(t){case"prompt_injection":case"retrieval_poisoning":case"sensitive_action":case"egress_exfiltration":case"mcp_allowlist_violation":case"model_governance_violation":case"approval_required":return t;default:return"unknown"}};var Wt=["silent","error","warn","info","debug"],De=class{constructor(e){this.events=[];this.config={logLevel:e?.logLevel??"info",maxEvents:e?.maxEvents??1e3,prefix:e?.prefix??"[shield-audit]"}}async logDecision(e){this.logDecisionSync(e)}logDecisionSync(e){let o=X(e);this.storeEvent(o),this.printEvent(e)}logEvent(e){this.storeEvent(e),this.shouldLog("info")&&console.log(`${this.config.prefix} [${e.eventType}] ${e.eventId}`,JSON.stringify(e.payload,null,2))}getEvents(){return[...this.events]}getEventsByType(e){return this.events.filter(o=>o.eventType===e)}getEventsByDecisionId(e){return this.events.filter(o=>o.decisionId===e)}getEventCount(){return this.events.length}clear(){this.events.length=0}storeEvent(e){for(this.events.push(e);this.events.length>this.config.maxEvents;)this.events.shift()}printEvent(e){let o=e.effects.some(n=>n.type==="block"),r=e.effects.some(n=>n.type==="redact");if(o&&this.shouldLog("warn")){if(console.warn(`${this.config.prefix} \u{1F6AB} BLOCKED [${e.envelopeRef.canonicalHook}] severity=${e.severity} findings=${e.findings.length} reasonCodes=[${e.reasonCodes.join(",")}] (${e.durationMs}ms)`+(e.simulated?" [SIMULATED]":"")),this.shouldLog("debug"))for(let n of e.findings)console.debug(` \u2514\u2500 ${n.detector}: ${n.summary}`)}else r&&this.shouldLog("info")?console.info(`${this.config.prefix} \u270F\uFE0F REDACT [${e.envelopeRef.canonicalHook}] severity=${e.severity} findings=${e.findings.length} (${e.durationMs}ms)`):e.findings.length>0&&this.shouldLog("info")?console.info(`${this.config.prefix} \u26A0\uFE0F FINDINGS [${e.envelopeRef.canonicalHook}] severity=${e.severity} findings=${e.findings.length} action=allow (${e.durationMs}ms)`):this.shouldLog("debug")&&console.debug(`${this.config.prefix} \u2705 ALLOW [${e.envelopeRef.canonicalHook}] (${e.durationMs}ms)`)}shouldLog(e){return Wt.indexOf(this.config.logLevel)>=Wt.indexOf(e)}},nr=t=>new De(t);var Te=class{constructor(e=1e4){this.incidents=[];this.reportedIds=new Set;this.gcThreshold=e}createFromDecision(e){let o=e.effects.find(n=>n.type==="block"),r={id:`incident_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,timestamp:Date.now(),surface:e.envelopeRef.surface,agentId:e.envelopeRef.agentId,runId:e.envelopeRef.runId,sessionId:e.envelopeRef.sessionId,rawAction:o?"block":e.effects.some(n=>n.type==="redact")?"redact":"allow",effectiveAction:o?e.simulated?"allow":"block":e.effects.some(n=>n.type==="redact")?"redact":"allow",blockReason:o?.reason,severity:e.severity,tags:e.tags,sample:{decisionId:e.decisionId,canonicalHook:e.envelopeRef.canonicalHook,hostHookName:e.envelopeRef.hostHookName,findings:e.findings.map(n=>({detector:n.detector,summary:n.summary}))}};return this.incidents.push(r),this.maybeGc(),r}list(e){let o=this.incidents;e?.agentId&&(o=o.filter(a=>a.agentId===e.agentId)),e?.runId&&(o=o.filter(a=>a.runId===e.runId));let r=o.length,n=e?.offset??0,i=e?.limit??50,s=o.slice(n,n+i);return{total:r,items:s}}get(e){return this.incidents.find(o=>o.id===e)}count(){return this.incidents.length}markReported(e){for(let o of e)this.reportedIds.add(o)}clear(){this.incidents.length=0,this.reportedIds.clear()}maybeGc(){if(this.incidents.length<=this.gcThreshold)return;let e=this.incidents.length;for(let o=this.incidents.length-1;o>=0;o--)this.reportedIds.has(this.incidents[o].id)&&(this.reportedIds.delete(this.incidents[o].id),this.incidents.splice(o,1));this.incidents.length>this.gcThreshold?console.warn(`[memory-store] IncidentStore: ${this.incidents.length} incidents after GC (all unreported), threshold=${this.gcThreshold}`):e!==this.incidents.length&&console.info(`[memory-store] IncidentStore GC: ${e} \u2192 ${this.incidents.length}`)}},Me=class Me{constructor(e=5e3){this.approvals=[];this.gcThreshold=e}create(e,o){let r={...e,approvalId:`approval_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,incidentId:o,status:"pending",createdAt:Date.now()};return this.approvals.push(r),this.maybeGc(),r}resolve(e,o,r,n){if(o!=="approved"&&o!=="rejected")return;let i=this.approvals.find(s=>s.approvalId===e);if(!(!i||i.status!=="pending"))return i.status=o,i.resolvedAt=Date.now(),i.resolvedBy=r,i.note=n,i}list(e){let o=this.approvals;e?.status&&e.status.length>0&&(o=o.filter(a=>e.status.includes(a.status))),e?.category&&e.category.length>0&&(o=o.filter(a=>e.category.includes(a.category)));let r=o.length,n=e?.offset??0,i=e?.limit??50,s=o.slice(n,n+i);return{total:r,items:s}}get(e){return this.approvals.find(o=>o.approvalId===e)}pendingCount(){return this.approvals.filter(e=>e.status==="pending").length}count(){return this.approvals.length}clear(){this.approvals.length=0}maybeGc(){if(this.approvals.length<=this.gcThreshold)return;let e=this.approvals.length;for(let o=this.approvals.length-1;o>=0;o--)Me.TERMINAL_STATUSES.has(this.approvals[o].status)&&this.approvals.splice(o,1);this.approvals.length>this.gcThreshold?console.warn(`[memory-store] ApprovalStore: ${this.approvals.length} approvals after GC (all pending), threshold=${this.gcThreshold}`):e!==this.approvals.length&&console.info(`[memory-store] ApprovalStore GC: ${e} \u2192 ${this.approvals.length}`)}};Me.TERMINAL_STATUSES=new Set(["approved","rejected"]);var xe=Me,Le=class Le{constructor(e=5e3){this.holds=[];this.gcThreshold=e}create(e,o){let r={...e,holdId:`hold_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,approvalId:o,status:"pending",createdAt:Date.now()};return this.holds.push(r),this.maybeGc(),r}resume(e){let o=this.holds.find(r=>r.holdId===e);if(!(!o||o.status!=="pending"&&o.status!=="approved_waiting_resume"))return o.status="resumed",o.resumedAt=Date.now(),o}cancel(e){let o=this.holds.find(r=>r.holdId===e);if(!(!o||o.status!=="pending"))return o.status="cancelled",o.cancelledAt=Date.now(),o}list(e){let o=this.holds;e?.agentId&&(o=o.filter(a=>a.agentId===e.agentId)),e?.status&&e.status.length>0&&(o=o.filter(a=>e.status.includes(a.status)));let r=o.length,n=e?.offset??0,i=e?.limit??50,s=o.slice(n,n+i);return{total:r,items:s}}get(e){return this.holds.find(o=>o.holdId===e)}pendingCount(){return this.holds.filter(e=>e.status==="pending").length}count(){return this.holds.length}clear(){this.holds.length=0}maybeGc(){if(this.holds.length<=this.gcThreshold)return;let e=this.holds.length;for(let o=this.holds.length-1;o>=0;o--)Le.TERMINAL_STATUSES.has(this.holds[o].status)&&this.holds.splice(o,1);this.holds.length>this.gcThreshold?console.warn(`[memory-store] RunHoldStore: ${this.holds.length} holds after GC (all active), threshold=${this.gcThreshold}`):e!==this.holds.length&&console.info(`[memory-store] RunHoldStore GC: ${e} \u2192 ${this.holds.length}`)}};Le.TERMINAL_STATUSES=new Set(["resumed","cancelled"]);var He=Le,Z=class{constructor(){this.incidents=new Te;this.approvals=new xe;this.runHolds=new He}getStats(){return{incidents:this.incidents.count(),approvals:this.approvals.count(),pendingApprovals:this.approvals.pendingCount(),runHolds:this.runHolds.count(),pendingRunHolds:this.runHolds.pendingCount()}}clearAll(){this.incidents.clear(),this.approvals.clear(),this.runHolds.clear()}processDecisionEffects(e){let o={};e.findings.length>0&&(o.incident=this.incidents.createFromDecision(e));for(let r of e.effects)if(r.type==="queue_approval"&&(o.approval=this.approvals.create(r.ticket,o.incident?.id)),r.type==="create_run_hold"){let n=o.approval?.approvalId??"auto";o.runHold=this.runHolds.create(r.hold,n)}return o}};var ee=null,I=()=>{if(ee!==null)return ee;try{let t=require("os"),e=t.platform()||"unknown",o=t.release()||"unknown";ee=`${e}-${o}`}catch{ee="Unknown"}return ee};var Qt=require("fs/promises"),Oe=require("path"),Jt=(t,e)=>{if(t!=="read")return null;let o=e.path;return typeof o!="string"?null:o.toLowerCase().endsWith("/skill.md")||o.toLowerCase().endsWith("\\skill.md")||o.toLowerCase()==="skill.md"?o:null},ir=t=>{if(!t.startsWith("---"))return null;let e=t.indexOf(`
18
+ ---`,3);if(e===-1)return null;let r=t.substring(3,e).match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m);return r&&r[1]?r[1].trim():null},Vt=t=>{let e=(0,Oe.dirname)(t);return(0,Oe.basename)(e)||""},sr=t=>{if(t.startsWith("~/")||t==="~"){let e=process.env.HOME||process.env.USERPROFILE||"";return t.replace(/^~/,e)}return t},Yt=async(t,e)=>{let o=Jt(t,e);if(!o)return{isSkillRead:!1,skillName:"",source:"none"};try{let n=sr(o),i=await(0,Qt.readFile)(n,"utf-8"),s=ir(i);if(s)return{isSkillRead:!0,skillName:s,source:"frontmatter"}}catch{console.log("[skill-resolver] Failed to read SKILL.md, falling back to path extraction")}return{isSkillRead:!0,skillName:Vt(o),source:"path"}},Xt=(t,e)=>{let o=Jt(t,e);return o?{isSkillRead:!0,skillName:Vt(o),source:"path"}:{isSkillRead:!1,skillName:"",source:"none"}};var Zt=()=>({message_received:{},before_prompt_build:{},llm_input:{},before_tool_call:{},tool_result_persist:{},before_message_write:{},message_sending:{}}),A=(t,e="")=>typeof t=="string"?t:e;var E=t=>{if(typeof t=="string")return t;if(typeof t!="object"||t===null)return"";let e=t;return typeof e.content=="string"?e.content:Array.isArray(e.content)?e.content.filter(r=>typeof r=="object"&&r!==null&&r.type==="text"&&typeof r.text=="string").map(r=>r.text).join(`
19
+ `):""},eo=t=>{if(typeof t!="object"||t===null)return"user";let e=t,o=e.role;return o==="assistant"?"assistant":o==="tool"||o==="toolResult"||e.toolResultId?"toolResult":"user"},to=(t,e)=>({agentId:t.agentId,sessionId:t.sessionId,sessionKey:t.sessionKey,runId:t.runId,toolName:t.toolName}),oo=(t,e)=>{switch(t.surface){case"ingress":return"message_received";case"llm":return"llm_input";case"tool":return"before_tool_call";case"egress":return"message_sending";case"persist":return t.toolName||t.toolResult?"tool_result_persist":"before_message_write";default:return"message_received"}},ar=async(t,e)=>{let o=Zt(),r=Date.now();switch(t){case"message_received":{o.message_received={from:"",content:E(e.message??e.prompt),metadata:{provider:"openclaw",surface:e.surface,originatingChannel:"unknown",messageId:`msg_${r}`,senderId:e.agentId??"unknown"}};break}case"before_prompt_build":{o.before_prompt_build={prompt:A(e.prompt)||E(e.message)};break}case"llm_input":{o.llm_input={runId:e.runId??`run_${r}`,sessionId:e.sessionId??"",provider:A(e.provider,"unknown"),model:A(e.model,"unknown"),prompt:A(e.prompt,""),imagesCount:0};break}case"before_tool_call":{let n=A(e.toolName,"unknown"),i=e.toolParams??{},s=await Yt(n,i);o.before_tool_call={toolName:n,params:i,runId:e.runId??`run_${r}`,toolCallId:`call_${r}`,skillName:s.skillName};break}case"tool_result_persist":{o.tool_result_persist={toolName:A(e.toolName,"unknown"),toolCallId:`call_${r}`,content:E(e.toolResult??e.message),isError:!1,isSynthetic:!1};break}case"before_message_write":{let n=e.message,i=eo(n),s=E(n);o.before_message_write={role:i,content:s,...i==="toolResult"&&{toolName:e.toolName,isError:!1}};break}case"message_sending":{o.message_sending={to:"unknown",content:E(e.message),metadata:{}};break}}return o},ft=async(t,e,o)=>{let r=oo(t,e),n=Date.now(),i={hook:r,timestamp:n,events:await ar(r,t),ctx:to(t,e)};return{Type:1,AgentId:I(),DeviceId:I(),Data:i}},lr=(t,e)=>{let o=Zt(),r=Date.now();switch(t){case"message_received":{o.message_received={from:"",content:E(e.message??e.prompt),metadata:{provider:"openclaw",surface:e.surface,originatingChannel:"unknown",messageId:`msg_${r}`,senderId:e.agentId??"unknown"}};break}case"before_prompt_build":{o.before_prompt_build={prompt:A(e.prompt)||E(e.message)};break}case"llm_input":{o.llm_input={runId:e.runId??`run_${r}`,sessionId:e.sessionId??"",provider:A(e.provider,"unknown"),model:A(e.model,"unknown"),prompt:A(e.prompt,""),imagesCount:0};break}case"before_tool_call":{let n=A(e.toolName,"unknown"),i=e.toolParams??{},s=Xt(n,i);o.before_tool_call={toolName:n,params:i,runId:e.runId??`run_${r}`,toolCallId:`call_${r}`,skillName:s.skillName};break}case"tool_result_persist":{o.tool_result_persist={toolName:A(e.toolName,"unknown"),toolCallId:`call_${r}`,content:E(e.toolResult??e.message),isError:!1,isSynthetic:!1};break}case"before_message_write":{let n=e.message,i=eo(n),s=E(n);o.before_message_write={role:i,content:s,...i==="toolResult"&&{toolName:e.toolName,isError:!1}};break}case"message_sending":{o.message_sending={to:"unknown",content:E(e.message),metadata:{}};break}}return o},ht=(t,e,o)=>{let r=oo(t,e),n=Date.now(),i={hook:r,timestamp:n,events:lr(r,t),ctx:to(t,e)};return{Type:1,AgentId:I(),DeviceId:I(),Data:i}};var ro="remote-guard-config.json",cr="remote-guard-config.local.json",no="runtime-guardrail",dr="remote-guard-auth.json",D=null,yt=()=>{D=null},io=()=>{try{return require("path").resolve(__dirname,"..")}catch{return process.cwd()}},Ge=(t,e,o)=>{let r=t.trim();return r&&(r==="~"?o.homedir():r.startsWith("~/")?e.join(o.homedir(),r.slice(2)):r)},so=t=>{let e=t?.path??require("path"),o=t?.os??require("os"),r=t?.env?.OPENCLAW_STATE_DIR?.trim()||process.env?.OPENCLAW_STATE_DIR?.trim(),n=r?Ge(r,e,o):e.join(o.homedir(),".openclaw");return e.resolve(n,no,ro)},ao=t=>{let e=t?.path??require("path"),o=t?.os??require("os"),r=t?.env?.OPENCLAW_STATE_DIR?.trim()||process.env?.OPENCLAW_STATE_DIR?.trim(),n=r?Ge(r,e,o):e.join(o.homedir(),".openclaw");return e.resolve(n,no,dr)},ur=(t,e)=>e.resolve(t,ro),lo=t=>{let e=process.env?.RUNTIME_GUARDRAIL_CONFIG_PATH?.trim();if(e)return t.path.resolve(Ge(e,t.path,t.os));let o=t.configPath?.trim();if(o)return t.path.resolve(Ge(o,t.path,t.os));let r=t.path.resolve(t.pluginRoot,cr);if(t.fs.existsSync(r))return r;let n=ur(t.pluginRoot,t.path);if(t.fs.existsSync(n))return n;let i=so({env:process.env,path:t.path,os:t.os});return t.fs.existsSync(i)?i:n},vt=t=>{let e=require("fs"),o=require("path"),r=require("os");return lo({configPath:t,pluginRoot:io(),path:o,fs:e,os:r})},pr=t=>{let e=ao({env:process.env,path:t.path,os:t.os});if(t.fs.existsSync(e))try{let o=t.fs.readFileSync(e,"utf-8"),r=JSON.parse(o),n=r.auth?.key;if(typeof n=="string"&&n.trim())return n.trim();if(typeof r.key=="string"&&r.key.trim())return r.key.trim()}catch{return}},co=t=>{let e=lo({configPath:t.configPath,pluginRoot:io(),path:t.path,fs:t.fs,os:t.os});if(!t.fs.existsSync(e))return{config:null,resolvedPath:e};let o=t.fs.readFileSync(e,"utf-8"),r=JSON.parse(o),n=pr(t),i=n?{...r,auth:{...r.auth??{key:""},key:n}}:r;return!i.server?.ip||!i.server?.port||!i.auth?.key?.trim()?{config:null,resolvedPath:e}:{config:i,resolvedPath:e}},uo=async t=>{if(D)return D;try{let e=require("fs"),o=require("path"),r=require("os"),{config:n,resolvedPath:i}=co({configPath:t,fs:e,path:o,os:r});return n?(D=n,console.info(`[remote-guard] Config loaded: ${n.server.ip}:${n.server.port}${n.server.path}`),n):(console.warn(`[remote-guard] Config file is unavailable or missing auth.key: ${i}`),null)}catch(e){return console.error("[remote-guard] Failed to load config:",e),null}},C=(t,e=!1)=>{if(D&&!e)return D;try{let o=require("fs"),r=require("path"),n=require("os"),{config:i}=co({configPath:t,fs:o,path:r,os:n});return i?(D=i,i):null}catch{return null}},mr=()=>D,gr=t=>{D=t},Ne=(t,e)=>{let o=t.RuleId??"unknown",r=t.RequestId??"unknown",n=[`rule:${o}`,`request:${r}`];switch(t.Action){case"deny":return{rawAction:"block",effectiveAction:"block",severity:"high",blockReason:t.Message??"runtime-guardrail\u68C0\u6D4B\u5230\u5371\u9669\uFF0C\u8BF7\u505C\u6B62",tags:n,findings:[{detector:"remote-guard",reasonCode:"sensitive_action",severity:"high",summary:`\u8FDC\u7AEF\u68C0\u6D4B\u963B\u65AD: ${t.Message??`\u89C4\u5219 ${o} \u547D\u4E2D`}`,tags:n,evidence:{requestId:r,ruleId:o,action:"deny"},ruleIds:[o]}]};case"approval":return{rawAction:"block",effectiveAction:"block",severity:"high",blockReason:"runtime-guardrail\u68C0\u6D4B\u5230\u98CE\u9669\uFF0C\u8BF7\u8FDB\u884C\u5BA1\u6279",tags:n,findings:[{detector:"remote-guard",reasonCode:"approval_required",severity:"high",summary:`\u8FDC\u7AEF\u68C0\u6D4B\u9700\u8981\u5BA1\u6279: ${t.Message??`\u89C4\u5219 ${o} \u547D\u4E2D`}`,tags:n,evidence:{requestId:r,ruleId:o,action:"approval"},ruleIds:[o]}],approvalRequest:{approvalId:`approval_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,incidentId:`incident_${r}`,createdAt:Date.now(),status:"pending",category:"unknown",severity:"high",reason:t.Message??`\u8FDC\u7AEF\u68C0\u6D4B\u9700\u8981\u5BA1\u6279 (\u89C4\u5219: ${o})`,tags:n,agentId:e?.agentId,runId:e?.runId,sessionId:e?.sessionId,toolName:e?.toolName,source:"remote-guard",externalApprovalId:r},runHold:{holdId:`hold_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,approvalId:`approval_${r}`,incidentId:`incident_${r}`,createdAt:Date.now(),status:"pending",category:"unknown",severity:"high",reason:t.Message??`\u8FDC\u7AEF\u68C0\u6D4B\u9700\u8981\u5BA1\u6279 (\u89C4\u5219: ${o})`,agentId:e?.agentId,runId:e?.runId,sessionId:e?.sessionId,toolName:e?.toolName??"unknown",toolParams:{},toolParamsHash:"",stageAtHold:"enforce_all",failModeAtHold:"closed",source:"remote-guard",externalApprovalId:r}};case"redact":return{rawAction:"redact",effectiveAction:"redact",severity:"medium",redactions:t.RedactedContent?[{field:"content",replacement:t.RedactedContent,ruleId:t.RuleId}]:[],redactedTexts:t.RedactedContent?{content:t.RedactedContent}:{},tags:n};case"permit":return{rawAction:"allow",effectiveAction:"allow",severity:"low",tags:[...n,"action:permit"]};case"log":return console.log(`[remote-guard] Action=log received, rule=${o}, message=${t.Message??"none"}`),{rawAction:"allow",effectiveAction:"allow",severity:"low",tags:[...n,"action:log"]};default:return{rawAction:"allow",effectiveAction:"allow",severity:"low",tags:n}}},te=t=>{let e=`http://${t.server.ip}:${t.server.port}${t.server.path}`;return async o=>{let{input:r,stage:n}=o,i=await ft(r,n,t);console.log(`
20
+ `+"=".repeat(60)),console.log("[remote-guard] Sending request to:",e),console.log("[remote-guard] Hook:",i.Data.hook),console.log("[remote-guard] Request body:"),console.log(JSON.stringify(i,null,2));try{let s=new AbortController,a=setTimeout(()=>s.abort(),t.options.timeoutMs),l=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.auth.key}`},body:JSON.stringify(i),signal:s.signal});if(clearTimeout(a),console.log("[remote-guard] Response status:",l.status),!l.ok){let g=await l.text();return console.error("[remote-guard] Response error:",g),console.log("=".repeat(60)+`
21
+ `),{rawAction:"allow",effectiveAction:"allow",severity:"medium",tags:["remote-guard-error",`status:${l.status}`],findings:[{detector:"remote-guard",reasonCode:"remote_call_failed",severity:"medium",summary:`\u8FDC\u7AEF\u5B89\u5168\u68C0\u6D4B\u8C03\u7528\u5931\u8D25: HTTP ${l.status}`,tags:["remote-guard-error",`status:${l.status}`],evidence:{httpStatus:l.status,errorText:g.slice(0,500),hook:i.Data.hook,toolName:r.toolName,timestamp:Date.now()},ruleIds:["remote_guard_http_error"]}]}}let d=await l.json();console.log("[remote-guard] Response body:"),console.log(JSON.stringify(d,null,2)),console.log("=".repeat(60)+`
22
+ `);let u={toolName:r.toolName,agentId:r.agentId,runId:r.runId,sessionId:r.sessionId};return Ne(d,u)}catch(s){let a,l;return s instanceof Error&&s.name==="AbortError"?(console.error("[remote-guard] Request timeout after",t.options.timeoutMs,"ms"),a=`\u8BF7\u6C42\u8D85\u65F6 (${t.options.timeoutMs}ms)`,l="timeout"):(console.error("[remote-guard] Request failed:",s),a=s instanceof Error?s.message:String(s),l="network_error"),console.log("=".repeat(60)+`
23
+ `),{rawAction:"allow",effectiveAction:"allow",severity:"medium",tags:["remote-guard-error",`error-type:${l}`],findings:[{detector:"remote-guard",reasonCode:"remote_call_failed",severity:"medium",summary:`\u8FDC\u7AEF\u5B89\u5168\u68C0\u6D4B\u8C03\u7528\u5931\u8D25: ${a}`,tags:["remote-guard-error",`error-type:${l}`],evidence:{errorType:l,errorMessage:a,hook:i.Data.hook,toolName:r.toolName,timestamp:Date.now()},ruleIds:["remote_guard_network_error"]}]}}}},fr=async t=>{let e=await uo(t);if(!e||!e.enabled){console.info("[remote-guard] Remote guard is disabled or config not found");return}return te(e)},hr=t=>{let e=C(t);if(!(!e||!e.enabled))return te(e)};var po=8,yr=65536,vr=po+yr,kr=500,Sr=100,wr=0,Rr=1;var br=()=>{let t=require("path"),e=require("fs"),o=[t.resolve(__dirname,"sync-remote-worker.js"),t.resolve(__dirname,"runtime-core","sync-remote-worker.js"),t.resolve(__dirname,"sync-remote-worker.ts")];for(let r of o)if(e.existsSync(r))return r;return o[0]},Ir=t=>{let{Worker:e}=require("worker_threads"),o=br(),r=new SharedArrayBuffer(vr),n=new Int32Array(r,0,2),i=new e(o,{workerData:{sab:r,url:t.url,method:"POST",headers:t.headers,body:t.body,timeoutMs:t.timeoutMs}});if(Atomics.wait(n,0,wr,t.timeoutMs+Sr)==="timed-out")return console.warn(`[sync-remote-evaluate] Worker timed out after ${t.timeoutMs}ms, fallback allow`),i.terminate(),null;let a=Atomics.load(n,0),l=Atomics.load(n,1),d=new Uint8Array(r,po,l),u=new TextDecoder().decode(d);return i.terminate(),{ok:a===Rr,body:u}},Ar=t=>{let e=t.payload;return{surface:t.surface,agentId:t.agentId,runId:t.runId,sessionId:t.sessionId,message:e.message,toolName:e.toolName,toolResult:e.toolResult}},kt=(t,e,o)=>({decisionId:`decision_${Date.now()}_${Math.random().toString(36).slice(2,10)}`,envelopeRef:{canonicalHook:t.canonicalHook,hostHookName:t.hostHookName,surface:t.surface,agentId:t.agentId,runId:t.runId,sessionId:t.sessionId},stage:"enforce_all",severity:"low",findings:[],effects:[{type:"allow"}],reasonCodes:[],tags:["sync-remote-fallback",o],simulated:!1,durationMs:e}),mo=(t,e)=>{let o=Date.now(),r=e.options.syncHookTimeoutMs??kr;try{let n=Ar(t),i="enforce_all",s=ht(n,i,e),a=`http://${e.server.ip}:${e.server.port}${e.server.path}`;console.log(`[sync-remote-evaluate] Sending sync request to: ${a}`),console.log(`[sync-remote-evaluate] Hook: ${s.Data.hook}`);let l=Ir({url:a,headers:{"Content-Type":"application/json",Authorization:`Bearer ${e.auth.key}`},body:JSON.stringify(s),timeoutMs:r}),d=Date.now()-o;if(l===null)return kt(t,d,"timeout");if(console.log(`[sync-remote-evaluate] Response ok=${l.ok}, duration=${d}ms`),console.log(`[sync-remote-evaluate] Response body: ${l.body.slice(0,500)}`),!l.ok)return console.warn("[sync-remote-evaluate] HTTP error response:",l.body.slice(0,300)),kt(t,d,"http_error");let u=JSON.parse(l.body);console.log(`[sync-remote-evaluate] Response action: ${u.Action}`);let g={toolName:n.toolName,agentId:n.agentId,runId:n.runId,sessionId:n.sessionId},c=Ne(u,g),y=c.rawAction??c.effectiveAction??"allow",f=c.effectiveAction??y,S=(c.findings??[]).map((v,H)=>({findingId:`sync_finding_${Date.now()}_${H}`,detector:v.detector,reasonCode:v.reasonCode,severity:v.severity,summary:v.summary,tags:v.tags,evidence:v.evidence,matchedRuleIds:v.ruleIds})),h=[];return f==="block"&&h.push({type:"block",reason:c.blockReason??S[0]?.summary??"Blocked by remote policy"}),f==="redact"&&h.push({type:"redact",targets:(c.redactions??[]).map(v=>({field:v.field,replacement:v.replacement,ruleId:v.ruleId,value:c.redactedTexts?.[v.field]}))}),c.approvalRequest&&h.push({type:"queue_approval",ticket:{category:c.approvalRequest.category,reason:c.approvalRequest.reason,severity:c.approvalRequest.severity,tags:c.approvalRequest.tags,agentId:c.approvalRequest.agentId,runId:c.approvalRequest.runId,sessionId:c.approvalRequest.sessionId,toolName:c.approvalRequest.toolName,sourceBinding:c.approvalRequest.source?{source:c.approvalRequest.source,externalApprovalId:c.approvalRequest.externalApprovalId}:void 0}}),c.runHold&&h.push({type:"create_run_hold",hold:{toolName:c.runHold.toolName,toolParams:c.runHold.toolParams,stageAtHold:c.runHold.stageAtHold,failModeAtHold:c.runHold.failModeAtHold,category:c.runHold.category,severity:c.runHold.severity,reason:c.runHold.reason,agentId:c.runHold.agentId,runId:c.runHold.runId,sessionId:c.runHold.sessionId}}),h.length===0&&h.push({type:"allow"}),{decisionId:`decision_${Date.now()}_${Math.random().toString(36).slice(2,10)}`,envelopeRef:{canonicalHook:t.canonicalHook,hostHookName:t.hostHookName,surface:t.surface,agentId:t.agentId,runId:t.runId,sessionId:t.sessionId},stage:i,severity:c.severity??"low",findings:S,effects:h,reasonCodes:S.map(v=>v.reasonCode),tags:c.tags??[],simulated:!1,durationMs:d}}catch(n){let i=Date.now()-o;return console.error("[sync-remote-evaluate] Failed, fallback allow:",n),kt(t,i,"evaluate_error")}},St=t=>{if(!(!t||!t.enabled))return e=>mo(e,t)};var B="0.1.19";var Pr={enabled:!0,websocket:{ip:"localhost",port:8081,protocol:"wss"},identity:{agentId:""},auth:{apiKey:""},intervals:{heartbeatMs:3e4,dataReportMs:6e5},reconnect:{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4}},wt=t=>`${t}_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,fo=()=>{let t=globalThis.process?.argv;return Array.isArray(t)?t:[]},go=()=>fo().map(t=>t.trim()).filter(Boolean).join(" "),ho=()=>{let t=fo().map(r=>r.trim().toLowerCase()).filter(Boolean);if(t.length===0)return"cli-lifecycle";let e=t.indexOf("gateway");if(e<0)return"none";let o=t[e+1];return o===void 0||o.startsWith("-")?"hosted-process":o==="start"||o==="restart"?"cli-lifecycle":"none"},Rt=()=>ho()!=="none",Er=()=>ho()==="hosted-process",$e=class{constructor(e,o,r){this.config=e;this.store=o;this.logger=r;this.ws=null;this.heartbeatInterval=null;this.dataReportInterval=null;this.reconnectTimeout=null;this.authTimeout=null;this.authRetryInterval=null;this.isRunning=!1;this.isAuthenticated=!1;this.pendingAuthRequestId=null;this.status={connected:!1,reconnectAttempts:0};this.pendingReports=new Map}getStatus(){return{...this.status}}async start(){if(this.isRunning){this.log("info","Telemetry service already running");return}if(!this.config.enabled){this.log("info","Telemetry service is disabled");return}this.isRunning=!0,this.status.startedAt=Date.now(),this.log("info","Starting telemetry service..."),this.connect()}async stop(){this.isRunning&&(this.log("info","Stopping telemetry service..."),this.isRunning=!1,this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.dataReportInterval&&(clearInterval(this.dataReportInterval),this.dataReportInterval=null),this.reconnectTimeout&&(clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null),this.authTimeout&&(clearTimeout(this.authTimeout),this.authTimeout=null),this.stopAuthRetry(),this.ws&&(this.ws.close(1e3,"service stopping"),this.ws=null),this.pendingReports.clear(),this.status.connected=!1,this.isAuthenticated=!1,this.pendingAuthRequestId=null,this.log("info","Telemetry service stopped"))}async reload(e){let o=this.isRunning;await this.stop(),this.config=e??{...Pr,enabled:!1},this.status={connected:!1,reconnectAttempts:0},this.isAuthenticated=!1,this.pendingAuthRequestId=null,(o||this.config.enabled)&&await this.start()}connect(){if(!this.isRunning)return;let{ip:e,port:o,protocol:r}=this.config.websocket,n=`${r??"wss"}://${e}:${o}`;this.log("info",`Connecting to WebSocket: ${n}`);try{this.createWebSocketConnection(n)}catch(i){this.log("error",`Failed to create WebSocket: ${i}`),this.scheduleReconnect()}}createWebSocketConnection(e){try{let o=require("ws");this.ws=new o(e)}catch{if(typeof WebSocket<"u")this.ws=new WebSocket(e);else{this.log("error","WebSocket not available in this environment");return}}this.ws.onopen=()=>this.handleOpen(),this.ws.onmessage=o=>this.handleMessage(o),this.ws.onclose=o=>this.handleClose(o),this.ws.onerror=o=>this.handleError(o)}handleOpen(){this.log("info","WebSocket connected, starting authentication..."),this.status.connected=!0,this.status.reconnectAttempts=0,this.status.lastError=void 0,this.isAuthenticated=!1,this.sendAuth()}sendAuth(){if(!this.ws||this.ws.readyState!==1||this.isAuthenticated)return;let e=wt("auth");this.pendingAuthRequestId=e;let o={type:"auth",request_id:e,timestamp:Math.floor(Date.now()/1e3),api_key:this.config.auth.apiKey};try{this.ws.send(JSON.stringify(o)),this.log("info",`Auth message sent: request_id=${o.request_id}`),this.authTimeout=setTimeout(()=>{!this.isAuthenticated&&this.pendingAuthRequestId===e&&(this.log("warn","Authentication timeout, will retry..."),this.pendingAuthRequestId=null,this.scheduleAuthRetry())},3e4),this.authTimeout.unref?.()}catch(r){this.log("error",`Failed to send auth message: ${r}`),this.pendingAuthRequestId=null,this.scheduleAuthRetry()}}scheduleAuthRetry(){this.isAuthenticated||this.authRetryInterval||(this.log("info",`Scheduling auth retry every ${this.config.intervals.heartbeatMs}ms`),this.authRetryInterval=setInterval(()=>{if(this.isAuthenticated){this.stopAuthRetry();return}if(!this.ws||this.ws.readyState!==1){this.stopAuthRetry();return}this.log("info","Retrying authentication..."),this.sendAuth()},this.config.intervals.heartbeatMs),this.authRetryInterval.unref?.())}stopAuthRetry(){this.authRetryInterval&&(clearInterval(this.authRetryInterval),this.authRetryInterval=null)}startHeartbeatAndDataReport(){this.heartbeatInterval||(this.log("info","Starting heartbeat and data report..."),this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.dataReportInterval&&(clearInterval(this.dataReportInterval),this.dataReportInterval=null),this.sendHeartbeat(),this.heartbeatInterval=setInterval(()=>{this.sendHeartbeat()},this.config.intervals.heartbeatMs),this.heartbeatInterval.unref?.(),this.dataReportInterval=setInterval(()=>{this.sendDataReport()},this.config.intervals.dataReportMs),this.dataReportInterval.unref?.(),this.log("info",`Heartbeat interval: ${this.config.intervals.heartbeatMs}ms, Data report interval: ${this.config.intervals.dataReportMs}ms`))}onAuthenticationSuccess(){this.authTimeout&&(clearTimeout(this.authTimeout),this.authTimeout=null),this.stopAuthRetry(),this.isAuthenticated=!0,this.pendingAuthRequestId=null,this.log("info","Authentication successful, starting heartbeat (no api_key needed)"),this.startHeartbeatAndDataReport()}handleMessage(e){if(e.data instanceof Blob){e.data.text().then(r=>{this.processMessageData(r)}).catch(r=>{this.log("warn",`Failed to read Blob data: ${r}`)});return}if(e.data instanceof ArrayBuffer){let n=new TextDecoder("utf-8").decode(e.data);this.processMessageData(n);return}let o=typeof e.data=="string"?e.data:e.data.toString("utf-8");this.processMessageData(o)}processMessageData(e){try{let o=JSON.parse(e);switch(this.log("info",`Received message: type=${o.type}, request_id=${o.request_id}`),o.type){case"auth_ack":{let r=o.payload;this.log("info",`Auth ACK received: success=${r.success}, message=${r.message}`),r.success?this.onAuthenticationSuccess():(this.log("warn",`Authentication failed: ${r.message}, will retry...`),this.pendingAuthRequestId=null,this.scheduleAuthRetry());break}case"heartbeat":this.log("info",`Heartbeat ACK received, server_time=${o.payload.server_time}`);break;case"data_report":{let r=o.payload;if(this.log("info",`Data report ACK: accepted=${r.accepted}, message=${r.message}`),r.accepted){let n=this.pendingReports.get(o.request_id);n&&(this.store.incidents.markReported(n),this.pendingReports.delete(o.request_id),this.log("info",`Marked ${n.length} incidents as reported for request_id=${o.request_id}`))}break}case"config_push":this.log("info",`Config push received: ${JSON.stringify(o.payload)}`);break;case"device_push":this.log("info",`Device push received: ${JSON.stringify(o.payload)}`);break;case"error":{let r=o.payload;this.log("error",`Server error: ${r.message}`);break}}}catch(o){this.log("warn",`Failed to parse message: ${o}`)}}handleClose(e){this.log("warn",`WebSocket closed: code=${e.code}, reason=${e.reason}`),this.status.connected=!1,this.isAuthenticated=!1,this.pendingAuthRequestId=null,this.ws=null,this.authTimeout&&(clearTimeout(this.authTimeout),this.authTimeout=null),this.stopAuthRetry(),this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.dataReportInterval&&(clearInterval(this.dataReportInterval),this.dataReportInterval=null),this.scheduleReconnect()}handleError(e){let o=e.message??"Unknown WebSocket error";this.log("error",`WebSocket error: ${o}`),this.status.lastError=o}scheduleReconnect(){if(!this.isRunning)return;if(this.status.reconnectAttempts>=this.config.reconnect.maxAttempts){this.log("error",`Max reconnect attempts (${this.config.reconnect.maxAttempts}) reached, stopping telemetry service`);return}this.status.reconnectAttempts++;let e=Math.min(this.config.reconnect.baseDelayMs*Math.pow(2,this.status.reconnectAttempts-1),this.config.reconnect.maxDelayMs);this.log("info",`Scheduling reconnect in ${e}ms (attempt ${this.status.reconnectAttempts}/${this.config.reconnect.maxAttempts})`),this.reconnectTimeout=setTimeout(()=>{this.connect()},e),this.reconnectTimeout.unref?.()}sendHeartbeat(){if(!this.ws||this.ws.readyState!==1)return;if(!this.isAuthenticated){this.log("warn","Cannot send heartbeat: not authenticated yet");return}let e={type:"heartbeat",request_id:wt("hb"),timestamp:Math.floor(Date.now()/1e3),payload:{plugin_id:I(),device_id:I(),agent_id:I(),plugin_version:B,running_status:"healthy"}};try{this.ws.send(JSON.stringify(e)),this.status.lastHeartbeatAt=Date.now(),this.log("info",`Heartbeat sent: request_id=${e.request_id}`)}catch(o){this.log("error",`Failed to send heartbeat: ${o}`)}}sendDataReport(){if(!this.ws||this.ws.readyState!==1)return;if(!this.isAuthenticated){this.log("warn","Cannot send data report: not authenticated yet");return}let e=new Set;for(let i of this.pendingReports.values())for(let s of i)e.add(s);let r=this.store.incidents.list({limit:1e3}).items.filter(i=>!e.has(i.id));if(r.length===0){this.log("info","No incidents to report, skipping data report");return}let n={type:"data_report",request_id:wt("dr"),timestamp:Math.floor(Date.now()/1e3),payload:{plugin_id:I(),report_type:"security_incidents",data:{device_id:I(),agent_id:I(),total:r.length,incidents:r.map(i=>({id:i.id,timestamp:i.timestamp,surface:i.surface,agent_id:i.agentId,run_id:i.runId,session_id:i.sessionId,raw_action:i.rawAction,effective_action:i.effectiveAction,block_reason:i.blockReason,severity:i.severity,tags:i.tags,sample:i.sample}))}}};try{this.ws.send(JSON.stringify(n)),this.status.lastDataReportAt=Date.now(),this.log("info",`Data report sent: request_id=${n.request_id}, incidents=${r.length}`),this.pendingReports.set(n.request_id,r.map(i=>i.id))}catch(i){this.log("error",`Failed to send data report: ${i}`)}}log(e,o){let r="[telemetry-service]";this.logger?this.logger[e](`${r} ${o}`):console[e](`${r} ${o}`)}},yo=t=>({enabled:t.enabled&&t.telemetry?.enabled!==!1,websocket:{ip:t.server.ip,port:t.websocket?.port??8081,protocol:t.websocket?.protocol??"wss"},identity:{agentId:t.identity.agentId},auth:{apiKey:t.auth.key},intervals:{heartbeatMs:t.telemetry?.heartbeatIntervalMs??3e4,dataReportMs:t.telemetry?.dataReportIntervalMs??6e5},reconnect:{maxAttempts:10,baseDelayMs:1e3,maxDelayMs:6e4}}),Fe=t=>{let e=t?.remoteGuardConfig??C(t?.remoteGuardConfigPath);if(!e||!e.enabled)return;let o=yo(e);return t?.config&&(o={...o,...t.config,websocket:{...o.websocket,...t.config.websocket??{}},identity:{...o.identity,...t.config.identity??{}},auth:{...o.auth,...t.config.auth??{}},intervals:{...o.intervals,...t.config.intervals??{}},reconnect:{...o.reconnect,...t.config.reconnect??{}}}),t?.heartbeatIntervalMs&&(o.intervals.heartbeatMs=t.heartbeatIntervalMs),t?.dataReportIntervalMs&&(o.intervals.dataReportMs=t.dataReportIntervalMs),o},bt=(t,e,o)=>{if(o?.enabled===!1){console.info("[telemetry-service] Telemetry service explicitly disabled");return}let r=Fe(o);if(!r){console.info("[telemetry-service] Remote guard config not found or disabled, telemetry service will not start");return}let n=t.log,i=n&&typeof n.info=="function"&&typeof n.warn=="function"&&typeof n.error=="function"?n:void 0,s=new $e(r,e,i),a=t.registerService,l=!1,d=!1,u=(f,S)=>{if(l)return;l=!0,console.info(`[telemetry-service] Received ${f}, cleaning up...`);let h=setTimeout(()=>{console.warn("[telemetry-service] Cleanup timeout, forcing exit"),process.exit(S)},3e3);h.unref?.(),s.stop().then(()=>{clearTimeout(h),console.info("[telemetry-service] Cleanup completed"),process.exit(S)}).catch(v=>{clearTimeout(h),console.error("[telemetry-service] Cleanup error:",v),process.exit(S)})},g=()=>{d||(d=!0,process.on("SIGINT",()=>u("SIGINT",0)),process.on("SIGTERM",()=>u("SIGTERM",0)),process.on("beforeExit",()=>{l||(console.info("[telemetry-service] Process beforeExit, cleaning up..."),s.stop())}))},c=async()=>{g(),await s.start()},y=async f=>{if(!Rt()){console.info(`[telemetry-service] Skipping telemetry start from ${f}: current command is not a gateway lifecycle/hosted process (argv=${go()||"[unknown]"})`);return}await c()};return typeof a=="function"?(a({id:"runtime-guardrail-telemetry",start:async()=>{await y("host-service-start")},stop:async()=>{await s.stop()}}),Rt()&&console.info("[telemetry-service] Telemetry service registered with host; startup is gated to gateway lifecycle commands and hosted gateway processes"),Er()&&(console.info("[telemetry-service] Hosted gateway process detected, eagerly starting telemetry service..."),c()),s):Rt()?(console.info("[telemetry-service] Host registerService unavailable, starting telemetry service for gateway lifecycle command..."),y("standalone-fallback"),s):(console.info(`[telemetry-service] Host registerService unavailable; telemetry remains idle because current command is not a gateway lifecycle/hosted process (argv=${go()||"[unknown]"})`),s)};var m=require("fs"),p=require("path"),x=require("os"),Cr=require("crypto"),_t=require("child_process"),_r="SKILL.md",oe="skills_package.zip",Dr="runtime-guardrail-skills-upload.marker.json",Tr="runtime-guardrail-skills-snapshot.latest.json",xr="runtime-guardrail-skills-snapshot.json",Hr="runtime-guardrail-skills-upload.lock",Mr="owner.json",Lr="RUNTIME_GUARDRAIL_OPENCLAW_BIN",Ao="RUNTIME_GUARDRAIL_DISABLE_SKILLS_LIST_MIRROR",Or=15e3,Gr="application/zip",z="runtime-guardrail",Nr=3e4,vo="__runtime_guardrail_enable_upload_dedup_state__",Po=6e4,ko="__runtime_guardrail_global_upload_singleton__",$r=()=>{let t=globalThis,e=t[ko];if(e&&typeof e=="object")return e;let o={};return t[ko]=o,o},It=6e4,Eo=new Set(["node_modules",".git","dist"]),q=()=>globalThis.process,Dt=t=>q()?.env?.[t],Fr=()=>({info:t=>{try{console.error(t)}catch{}},warn:t=>{try{console.error(t)}catch{}},error:t=>{try{console.error(t)}catch{}},debug:t=>{try{console.error(t)}catch{}}}),Br=Fr(),At=t=>{let e=t.logger??t.log;return e||Br},Pt=(t,e)=>t===e?t:{info:t.info??e.info,warn:t.warn??e.warn,error:t.error??e.error,debug:t.debug??e.debug},Tt=t=>typeof t!="string"?t:typeof TextEncoder=="function"?new TextEncoder().encode(t):new Uint8Array(t.split("").map(e=>e.charCodeAt(0)&255)),xt=t=>Cr.createHash("sha256").update(Tt(t)).digest("hex"),So=t=>t.eligible?"ready":t.disabled?"disabled":t.blockedByAllowlist?"blocked":"missing",qr=()=>{let t=Dt(Lr)?.trim();return t||"openclaw"},Ur=()=>Dt(Ao)==="1",jr=t=>{if(!t||typeof t!="object")return;let e=t;if(!Array.isArray(e.skills))return;let o=[];for(let r of e.skills){if(!r||typeof r!="object")continue;let n=r;typeof n.name=="string"&&o.push({name:n.name,description:typeof n.description=="string"?n.description:"",source:typeof n.source=="string"?n.source:"",eligible:n.eligible===!0,disabled:n.disabled===!0,blockedByAllowlist:n.blockedByAllowlist===!0,bundled:n.bundled===!0?!0:void 0,primaryEnv:typeof n.primaryEnv=="string"?n.primaryEnv:void 0,homepage:typeof n.homepage=="string"?n.homepage:void 0,missing:n.missing??null})}return{workspaceDir:typeof e.workspaceDir=="string"?e.workspaceDir:void 0,managedSkillsDir:typeof e.managedSkillsDir=="string"?e.managedSkillsDir:void 0,skills:o}},Kr=t=>{let e=t.indexOf("{");if(e<0)return;let o=0,r=!1,n=!1;for(let i=e;i<t.length;i+=1){let s=t[i];if(r){n?n=!1:s==="\\"?n=!0:s==='"'&&(r=!1);continue}if(s==='"'){r=!0;continue}if(s==="{"){o+=1;continue}if(s==="}"&&(o-=1,o===0))return t.slice(e,i+1)}},zr=t=>{let e=t.trim();if(!e)throw new Error("empty skills list output");try{return JSON.parse(e)}catch{let o=Kr(e);if(!o)throw new Error("skills list output does not contain JSON object");return JSON.parse(o)}},Wr=(t,e)=>{if(!t||Ur())return;let o=qr();try{let r=_t.execFileSync(o,["skills","list","--json"],{cwd:t,env:{...q()?.env,[Ao]:"1"},encoding:"utf-8",stdio:["ignore","pipe","pipe"]}),n=zr(r),i=jr(n);if(!i){e.warn?.("[runtime-guardrail] skills list parse failed: invalid JSON schema");return}return i}catch(r){let n=r instanceof Error?r.message:JSON.stringify(r);e.warn?.(`[runtime-guardrail] skills list unavailable: ${n}`);return}},Qr=(t,e,o)=>{let r=o.trim(),n=r==="~"?x.homedir():r.startsWith("~/")?p.join(x.homedir(),r.slice(2)):r;return typeof t.resolvePath=="function"&&!p.isAbsolute(n)?t.resolvePath(n):p.isAbsolute(n)?p.resolve(n):e?p.resolve(e,n):p.resolve(n)},Jr=()=>{let t=x.homedir(),e=[p.join(t,".nvm","versions","node"),"/usr/local/lib/node_modules","/usr/lib/node_modules",p.join(t,".npm-global","lib","node_modules")];for(let o of e)if(m.existsSync(o))if(o.includes(".nvm"))try{let r=m.readdirSync(o,{withFileTypes:!0});for(let n of r){if(!n.isDirectory())continue;let i=p.join(o,n.name,"lib","node_modules","openclaw","skills");if(m.existsSync(i))return i}}catch{}else{let r=p.join(o,"openclaw","skills");if(m.existsSync(r))return r}try{let o=_t.execFileSync("which",["openclaw"],{encoding:"utf-8",stdio:["ignore","pipe","pipe"]}).trim();if(o){let r=p.dirname(o),n=p.join(r,"..","lib","node_modules","openclaw","skills");if(m.existsSync(n))return p.resolve(n)}}catch{}},Vr=(t,e)=>{let o=[],r=e.workspaceDir;r&&(o.push({source:"workspace-skills",root:p.join(r,"skills")}),o.push({source:"workspace-agents-skills",root:p.join(r,".agents","skills")}));let n=x.homedir();n&&o.push({source:"user-agents-skills",root:p.join(n,".agents","skills")});let i=Jr();i&&o.push({source:"openclaw-bundled",root:i});let s=e.config?.skills?.load?.extraDirs;if(Array.isArray(s))for(let l of s){if(typeof l!="string")continue;let d=l.trim();d&&o.push({source:"config-extra-skills",root:Qr(t,r,d)})}let a=new Map;for(let l of o){let d=p.resolve(l.root);a.has(d)||a.set(d,{source:l.source,root:d})}return Array.from(a.values())},Yr=t=>{if(!m.existsSync(t))return[];let e;try{e=m.statSync(t)}catch{return[]}if(!e.isDirectory())return[];let o=[],r=[t];for(;r.length>0;){let n=r.pop();if(!n)continue;let i;try{i=m.readdirSync(n,{withFileTypes:!0})}catch{continue}let s=!1;for(let a of i){if(a.name.startsWith(".")||Eo.has(a.name))continue;let l=p.join(n,a.name);if(a.isDirectory()){r.push(l);continue}a.isFile()&&a.name===_r&&(s=!0)}s&&o.push(n)}return o.sort((n,i)=>n.localeCompare(i))},Co=t=>{let e=[],o=[t];for(;o.length>0;){let r=o.pop();if(!r)continue;let n;try{n=m.readdirSync(r,{withFileTypes:!0})}catch{continue}for(let i of n){if(i.name.startsWith(".")||Eo.has(i.name))continue;let s=p.join(r,i.name);i.isDirectory()?o.push(s):i.isFile()&&e.push(s)}}return e.sort((r,n)=>r.localeCompare(n))},Et=t=>t.split(p.sep).join("/"),Xr=t=>{let e=t.trim();return e&&e.replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-+|-+$/g,"")||"unknown-skill"},Zr=t=>{let e=new Map;for(let o of t)for(let r of Yr(o.root)){let n=p.resolve(r);e.has(n)||e.set(n,{name:p.basename(r),source:o.source,skillDirPath:n})}return Array.from(e.values()).sort((o,r)=>o.skillDirPath.localeCompare(r.skillDirPath))},en=t=>p.join(t,Dr),_o=t=>p.join(t,Tr),Do=t=>p.join(t,Hr),tn=t=>p.join(Do(t),Mr),wo=t=>{if(!t||typeof t!="object")return;let e=t.code;return typeof e=="string"?e:void 0},on=async t=>{let e={pid:q()?.pid,startedAt:new Date().toISOString(),argv:Oo()};await m.promises.writeFile(tn(t),JSON.stringify(e,null,2),"utf-8")},rn=async t=>{await m.promises.mkdir(t.stateDir,{recursive:!0});let e=Do(t.stateDir),o=t.staleTtlMs??Po,r=async()=>(await m.promises.mkdir(e),await on(t.stateDir).catch(()=>{}),{stateDir:t.stateDir,lockDir:e,release:async()=>{await m.promises.rm(e,{recursive:!0,force:!0}).catch(()=>{})}});try{return await r()}catch(n){if(wo(n)!=="EEXIST")throw n}try{let n=await m.promises.stat(e),i=Date.now()-n.mtimeMs;if(i>=o)return t.logger.warn?.(`[runtime-guardrail] skills upload lock stale detected: stateDir=${t.stateDir}, age=${i}ms, ttl=${o}ms; recycling lock`),await m.promises.rm(e,{recursive:!0,force:!0}),await r();t.logger.info?.(`[runtime-guardrail] skills upload skipped (cross-process dedup): another process holds lock, stateDir=${t.stateDir}, age=${i}ms`);return}catch(n){if(wo(n)==="ENOENT")return await r();throw n}},nn=async(t,e)=>{await m.promises.mkdir(t,{recursive:!0});let o=_o(t);await m.promises.writeFile(o,e,"utf-8");let r=await m.promises.stat(o);return{filePath:o,sizeBytes:r.size}},sn=async t=>{try{let e=await m.promises.readFile(_o(t),"utf-8"),o=JSON.parse(e);return!o||typeof o!="object"||!Array.isArray(o.skills)?void 0:o}catch{return}},an=async(t,e)=>{await m.promises.mkdir(t,{recursive:!0}),await m.promises.writeFile(en(t),JSON.stringify(e,null,2),"utf-8")},ln=async t=>{await m.promises.mkdir(p.dirname(t),{recursive:!0})},To=t=>{let e={name:t.name,source:t.source,skillDirPath:t.skillDirPath,packagedDir:t.packagedDir,status:t.status,eligible:t.eligible,disabled:t.disabled,blockedByAllowlist:t.blockedByAllowlist,description:t.description??"",files:[...t.files].map(o=>({relativePath:o.relativePath,sizeBytes:o.sizeBytes,sha256:o.sha256})).sort((o,r)=>o.relativePath.localeCompare(r.relativePath))};return xt(JSON.stringify(e))},Ro=t=>{if(typeof t.contentSha256=="string"&&t.contentSha256.trim())return t.contentSha256;let{contentSha256:e,...o}=t;return To(o)},cn=async t=>{let e=Vr(t.api,t.ctx),o=Wr(t.ctx.workspaceDir,t.logger);if(!o)throw new Error("cannot load openclaw skills list");let r=Zr(e),n=new Map;for(let a of r)n.has(a.name)||n.set(a.name,{source:a.source,skillDirPath:a.skillDirPath});let i=new Set,s=[];for(let a of o.skills){let l=n.get(a.name),d=Xr(a.name),u=p.join("skills",d),g=1;for(;i.has(u);)u=p.join("skills",`${d}-${g}`),g+=1;i.add(u);let c=[];if(l?.skillDirPath)for(let f of Co(l.skillDirPath)){let S=Et(p.relative(l.skillDirPath,f));try{let h=await m.promises.stat(f),v=Tt(await m.promises.readFile(f));c.push({relativePath:S,absolutePath:f,sizeBytes:h.size,modifiedAt:h.mtimeMs,sha256:xt(v)})}catch{}}let y={name:a.name,source:a.source||l?.source||"",skillDirPath:l?.skillDirPath||"",packagedDir:Et(u),status:So(a),eligible:a.eligible,disabled:a.disabled,blockedByAllowlist:a.blockedByAllowlist,description:a.description,files:c};s.push({...y,contentSha256:To(y)})}return{generatedAt:new Date().toISOString(),pluginId:z,workspaceDir:t.ctx.workspaceDir,skillCount:s.length,archiveFileName:oe,skills:s,openclawSkillsList:{workspaceDir:o.workspaceDir,managedSkillsDir:o.managedSkillsDir,skillCount:o.skills.length,skills:o.skills.map(a=>({...a,status:So(a)}))}}},dn=t=>{if(t.forceUpload||!t.previousSnapshot)return t.currentSnapshot.skills;let e=new Map;for(let o of t.previousSnapshot.skills)e.has(o.name)||e.set(o.name,o);return t.currentSnapshot.skills.filter(o=>{let r=e.get(o.name);return r?Ro(r)!==Ro(o):!0})},un=t=>{if(!t.previousSnapshot)return{...t.currentSnapshot,generatedAt:new Date().toISOString(),archiveFileName:oe,archiveSha256:t.archiveSha256};let e=[...t.previousSnapshot.skills],o=new Map;e.forEach((r,n)=>{o.has(r.name)||o.set(r.name,n)});for(let r of t.changedSkills){let n=o.get(r.name);if(typeof n=="number"){e[n]=r;continue}o.set(r.name,e.length),e.push(r)}return{...t.previousSnapshot,generatedAt:new Date().toISOString(),pluginId:z,workspaceDir:t.currentSnapshot.workspaceDir??t.previousSnapshot.workspaceDir,skillCount:e.length,archiveFileName:oe,archiveSha256:t.archiveSha256,skills:e,openclawSkillsList:t.previousSnapshot.openclawSkillsList??t.currentSnapshot.openclawSkillsList}},pn=async t=>{let e=await m.promises.mkdtemp(p.join(x.tmpdir(),"runtime-guardrail-skills-"));try{let o=p.join(e,"skills");await m.promises.mkdir(o,{recursive:!0});for(let l of t.skills){let d=p.join(e,l.packagedDir);if(await m.promises.mkdir(d,{recursive:!0}),!(!l.skillDirPath||!m.existsSync(l.skillDirPath)))for(let u of Co(l.skillDirPath)){let g=Et(p.relative(l.skillDirPath,u)),c=p.join(d,g);await ln(c),await m.promises.copyFile(u,c)}}let r=p.join(e,xr);await m.promises.writeFile(r,JSON.stringify(t.snapshotForArchive,null,2),"utf-8"),await m.promises.mkdir(t.stateDir,{recursive:!0});let n=oe,i=p.join(t.stateDir,n);await m.promises.rm(i,{force:!0}).catch(()=>{});try{_t.execFileSync("zip",["-rq",i,"."],{cwd:e,env:q()?.env,encoding:"utf-8",stdio:["ignore","pipe","pipe"]})}catch(l){let d=l instanceof Error?l.message:JSON.stringify(l);throw new Error(`zip archive creation failed: ${d}`)}let s=Tt(await m.promises.readFile(i)),a=xt(s);return{archivePath:i,archiveBytes:s,archiveSha256:a,archiveFileName:n}}finally{await m.promises.rm(e,{recursive:!0,force:!0}).catch(()=>{})}},mn=t=>{let e=t?.remoteGuardConfig??C(t?.remoteGuardConfigPath);if(!e||!e.enabled)return;let o=e.server?.ip,r=e.server?.port,n=e.server?.corPresignPath,i=e.server?.detectPath?.trim(),s=e.auth?.key;if(!(!o||!r||!n||!s))return{presignEndpoint:`http://${o}:${r}${n}`,detectEndpoint:i?`http://${o}:${r}${i}`:void 0,authKey:s,timeoutMs:t?.timeoutMs??e.options?.timeoutMs??Or,contentType:t?.contentType?.trim()||Gr,skillsStateDir:typeof e.options?.skillsStateDir=="string"&&e.options.skillsStateDir.trim()||void 0}},gn=async t=>{let e=globalThis.fetch;if(typeof e!="function")throw new Error("Global fetch is unavailable in current runtime");let o=typeof AbortController=="function"?new AbortController:void 0,r=o?setTimeout(()=>o.abort(),t.timeoutMs):void 0;try{let n=await e(t.endpoint,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.authKey}`},body:JSON.stringify({filename:t.fileName,content_type:t.contentType}),signal:o?.signal});if(!n?.ok){let s=typeof n?.text=="function"?await n.text():"";throw new Error(`HTTP ${n?.status??0} ${n?.statusText??""} ${s}`.trim())}let i=typeof n?.json=="function"?await n.json():{};if(!i?.upload_url||typeof i.upload_url!="string")throw new Error("presign response missing upload_url");return i}finally{r&&clearTimeout(r)}},fn=t=>{let e=t.trim();return e?/\.zip$/i.test(e)?[e]:[e,`${e}.zip`]:["skills_package.zip"]},hn=async t=>{let e=globalThis.fetch;if(typeof e!="function")throw new Error("Global fetch is unavailable in current runtime");let o=typeof AbortController=="function"?new AbortController:void 0,r=o?setTimeout(()=>o.abort(),t.timeoutMs):void 0;try{let n=await e(t.uploadUrl,{method:"PUT",headers:{"content-type":t.contentType},body:t.archiveBytes,signal:o?.signal});if(!n?.ok){let i=typeof n?.text=="function"?await n.text():"";throw new Error(`HTTP ${n?.status??0} ${n?.statusText??""} ${i}`.trim())}}finally{r&&clearTimeout(r)}},yn=t=>{let e=t.trim();if(!e)throw new Error("upload_url is empty, cannot derive CosKey");try{let o=new URL(e);if(o.pathname&&o.pathname!=="/")return o.pathname}catch{let o=e.match(/^https?:\/\/[^/]+(\/[^?]+)(?:\?|$)/i);if(o?.[1])return o[1]}throw new Error(`cannot derive CosKey from upload_url: ${e}`)},vn=async t=>{let e=globalThis.fetch;if(typeof e!="function")throw new Error("Global fetch is unavailable in current runtime");let r={CosKey:t.cosKey.replace(/^\/+/,"")};t.logger?.info?.(`[runtime-guardrail] skills detection submit request: ts=${Ct()}, endpoint=${t.endpoint}, payload=${JSON.stringify(r)}`);let n=typeof AbortController=="function"?new AbortController:void 0,i=n?setTimeout(()=>n.abort(),t.timeoutMs):void 0;try{let s=await e(t.endpoint,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.authKey}`},body:JSON.stringify(r),signal:n?.signal}),a=typeof s?.text=="function"?await s.text():"";if(t.logger?.info?.(`[runtime-guardrail] skills detection submit response: ts=${Ct()}, status=${s?.status??0}, statusText=${s?.statusText??""}, body=${a}`),!s?.ok)throw new Error(`HTTP ${s?.status??0} ${s?.statusText??""} ${a}`.trim())}finally{i&&clearTimeout(i)}},kn=t=>t?.enabled!==!1,xo=t=>{let e=t instanceof Error?t.message:JSON.stringify(t),o=e.toLowerCase();return o.includes("401")&&(o.includes("invalid api key")||o.includes("invalid_api_key")||o.includes("unauthorized"))?`${e}\uFF1B\u5DF2\u8BFB\u53D6\u5230\u672C\u5730 API Key\uFF0C\u4F46\u8FDC\u7AEF\u9274\u6743\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u5F53\u524D OpenClaw state dir \u4E0B\u7684 remote-guard-auth.json\uFF08\u9ED8\u8BA4\u662F ~/.openclaw/runtime-guardrail/remote-guard-auth.json\uFF09\u4E2D\u7684\u503C\u662F\u5426\u6B63\u786E\uFF1B\u8FD0\u884C\u65F6\u4E0D\u4F1A\u76F4\u63A5\u4F7F\u7528 RUNTIME_GUARDRAIL_API_KEY\u3002`:e},Ho=()=>{let t=q()?.argv;return!Array.isArray(t)||t.length===0?[]:t.map(e=>e.trim().toLowerCase()).filter(Boolean)},Sn=()=>{let t=Ho(),e=t.includes("plugins"),o=t.includes("enable"),r=t.includes(z);return e&&o&&r},wn=()=>{let t=Ho();if(t.length===0)return!1;let e=t.indexOf("gateway");if(e<0)return!1;let o=t[e+1];return o?o.startsWith("-"):!0},Mo=()=>{let t=globalThis,e=t[vo];if(e&&typeof e=="object")return e;let o={};return t[vo]=o,o},bo=(t=Date.now())=>{let e=Mo().lastTriggeredAt;return typeof e=="number"&&t-e<Nr},Rn=(t=Date.now())=>{Mo().lastTriggeredAt=t},bn=()=>{let t=Dt("OPENCLAW_STATE_DIR")?.trim();return t?t==="~"?x.homedir():t.startsWith("~/")?p.join(x.homedir(),t.slice(2)):p.resolve(t):p.join(x.homedir(),".openclaw")},Lo=t=>{let e=typeof t?.stateDir=="string"?t.stateDir.trim():"",o=e?p.resolve(e):bn();return p.basename(o)===z?o:p.join(o,z)},In=(t,e,o)=>{let r=Lo(e),n=o?.trim();return n?p.isAbsolute(n)?p.resolve(n):p.resolve(r,n):r},An=async(t,e,o)=>{let r=e.logger,n=mn(o);if(!n){r.info?.("[runtime-guardrail] skills upload skipped: remote-guard-config (enabled/ip/port/corPresignPath/auth.key) is unavailable");return}let i=e.workspaceDir??(typeof t.resolvePath=="function"?t.resolvePath("."):void 0),s=In(t,e,n.skillsStateDir),a={...e,workspaceDir:i,stateDir:s},l=await rn({stateDir:a.stateDir,logger:r,staleTtlMs:Math.max(Po,n.timeoutMs*4)});if(l)try{let d=await cn({api:t,ctx:a,logger:r}),u=await sn(a.stateDir),g=o?.force===!0,c=dn({currentSnapshot:d,previousSnapshot:u,forceUpload:g});if(c.length===0){r.info?.("[runtime-guardrail] skills upload skipped: no changed skills detected");return}let y={...d,generatedAt:new Date().toISOString(),skillCount:c.length,archiveFileName:oe,skills:c,archiveSha256:void 0},f=await pn({stateDir:a.stateDir,skills:c,snapshotForArchive:y});r.info?.(`[runtime-guardrail] skills archive prepared: ${f.archivePath} (${f.archiveBytes.length} bytes, changed=${c.length})`);let S=fn(f.archiveFileName),h,v;for(let M of S)try{h=await gn({endpoint:n.presignEndpoint,authKey:n.authKey,timeoutMs:n.timeoutMs,fileName:M,contentType:n.contentType}),M!==f.archiveFileName&&r.warn?.(`[runtime-guardrail] skills presign filename fallback applied: ${f.archiveFileName} -> ${M}`);break}catch(je){v=je}if(!h){let M=v instanceof Error?v.message:JSON.stringify(v);throw new Error(`skills presign request failed with all filename candidates: ${S.join(", ")} (${M})`)}await hn({uploadUrl:h.upload_url,contentType:n.contentType,archiveBytes:f.archiveBytes,timeoutMs:n.timeoutMs});let H=yn(h.upload_url),qe=un({previousSnapshot:u,currentSnapshot:d,changedSkills:c,archiveSha256:f.archiveSha256}),Ue=JSON.stringify(qe,null,2),be=await nn(a.stateDir,Ue);if(await an(a.stateDir,{presignEndpoint:n.presignEndpoint,digest:f.archiveSha256,uploadedAt:new Date().toISOString(),objectKey:typeof h.object_key=="string"?h.object_key:void 0}),r.info?.(`[runtime-guardrail] skills snapshot saved locally: ${be.filePath} (${be.sizeBytes} bytes)`),r.info?.(`[runtime-guardrail] skills archive upload succeeded: ${c.length} changed skills, objectKey=${h.object_key??""}`),n.detectEndpoint)try{await vn({endpoint:n.detectEndpoint,authKey:n.authKey,timeoutMs:n.timeoutMs,cosKey:H,logger:r}),r.info?.(`[runtime-guardrail] skills detection submit succeeded: CosKey=${H.replace(/^\/+/,"")}`)}catch(M){let je=xo(M);r.warn?.(`[runtime-guardrail] skills detection submit failed after upload success: ${je}`)}else r.info?.("[runtime-guardrail] skills detection submit skipped: server.detectPath is unavailable")}finally{await l.release()}},Ct=()=>new Date().toISOString(),Oo=()=>{let t=q()?.argv;return Array.isArray(t)?t.map(e=>e.trim()).filter(Boolean).join(" "):""},Io=t=>{t.logger.info?.(`[runtime-guardrail] skills upload startup marker: ts=${Ct()}, pid=${q()?.pid??""}, source=${t.source}, pluginsEnable=${t.isPluginsEnableCommand}, workspace=${t.ctx.workspaceDir??""}, stateDir=${t.ctx.stateDir}, argv=${Oo()}`)},Go=(t,e)=>{let o=At(t);if(!kn(e)){o.info?.("[runtime-guardrail] skills upload service disabled by config");return}if(typeof t.registerService!="function"){o.warn?.("[runtime-guardrail] registerService is unavailable on current host API, skills upload service not registered");return}let r=async(g,c)=>{let y=Pt(g.logger,o),f={...g,logger:y};Io({logger:y,source:c,isPluginsEnableCommand:i,ctx:f});try{await An(t,f,e)}catch(S){let h=xo(S);y.error?.(`[runtime-guardrail] skills upload failed: ${h}`)}},n=(g,c)=>{let y=$r(),f=Date.now();if(y.promise&&y.startedAt){let S=f-y.startedAt;if(S<It)return Pt(g.logger,o).info?.(`[runtime-guardrail] skills upload skipped (global dedup): source=${c}, elapsed=${S}ms, ttl=${It}ms`),y.promise;o.info?.(`[runtime-guardrail] global upload singleton TTL expired (${S}ms >= ${It}ms), allowing re-trigger`)}return y.startedAt=f,y.promise=r(g,c),y.promise},i=Sn(),s=wn(),a=i&&bo(),l=typeof t.resolvePath=="function"?t.resolvePath("."):void 0,d=Lo();i&&!a&&(Rn(),n({config:t.config,workspaceDir:l,stateDir:d,logger:At(t)},"plugins-enable")),s&&n({config:t.config,workspaceDir:l,stateDir:d,logger:At(t)},"gateway-startup");let u={id:`${z}.skills-uploader`,start:async g=>{let c=Pt(g.logger,o);if(i&&bo()){Io({logger:c,source:"service-start-dedup-skip",isPluginsEnableCommand:i,ctx:{...g,logger:c}}),c.info?.("[runtime-guardrail] skills upload start skipped: deduped during plugins enable");return}await n({...g,logger:c},"service-start")}};t.registerService(u),o.info?.("[runtime-guardrail] skills upload service registered; upload runs on `plugins enable runtime-guardrail`, gateway hosted startup, or when the host starts this service")};var k={status:"shield.status",releaseInfo:"shield.release.info",rulePacksList:"shield.rulepacks.list",policyValidate:"shield.policy.validate",policyTest:"shield.policy.test",metrics:"shield.metrics",rolloutSummary:"shield.rollout.summary",reportGenerate:"shield.report.generate",reportGet:"shield.report.get",incidentList:"shield.incident.list",incidentGet:"shield.incident.get",incidentResolve:"shield.incident.resolve",approvalList:"shield.approval.list",approvalResolve:"shield.approval.resolve",runHoldList:"shield.runhold.list",runHoldResume:"shield.runhold.resume",runHoldCancel:"shield.runhold.cancel",configRefresh:"shield.config.refresh"},P={status:"shield-status",validate:"shield-validate",report:"shield-report",incidents:"shield-incidents",approvals:"shield-approvals",runHolds:"shield-runholds",configRefresh:"shield-config-refresh"};var No={decision:"shield.decision",blocked:"shield.blocked",stageChanged:"shield.stage.changed",approvalQueued:"shield.approval.queued",approvalResolved:"shield.approval.resolved",hitlDeadLetterQueued:"shield.hitl.deadletter.queued",hitlDeadLetterResolved:"shield.hitl.deadletter.resolved"};var Mt=(t,e)=>{Pn(t,e),En(t,e)};function Pn(t,e){let{config:o,store:r}=e;t.registerGatewayMethod(k.status,async n=>{let i=r.getStats(),s={success:!0,data:{version:B,config:{simulated:o.simulated,failMode:o.failMode},stats:{incidents:i.incidents,approvals:i.approvals,runHolds:i.runHolds}}};return w(n,s)}),t.registerGatewayMethod(k.incidentList,async n=>{let i=j(U(n))??{},s=r.incidents.list(i);return w(n,{success:!0,data:{total:s.total,items:s.items}})}),t.registerGatewayMethod(k.incidentGet,async n=>{let i=j(U(n));if(!i?.id)return w(n,{success:!1,error:{code:"MISSING_ID",message:"Incident ID is required"}});let s=r.incidents.get(i.id);return s?w(n,{success:!0,data:s}):w(n,{success:!1,error:{code:"NOT_FOUND",message:`Incident ${i.id} not found`}})}),t.registerGatewayMethod(k.approvalList,async n=>{let i=j(U(n))??{},s=r.approvals.list(i);return w(n,{success:!0,data:{total:s.total,items:s.items}})}),t.registerGatewayMethod(k.approvalResolve,async n=>{let i=j(U(n));if(!i?.approvalId||!i?.decision)return w(n,{success:!1,error:{code:"INVALID_PARAMS",message:"approvalId and decision (approved/rejected) are required"}});let s=r.approvals.resolve(i.approvalId,i.decision,i.resolvedBy,i.note);return s?(Ht(t,`Approval ${i.approvalId} resolved: ${i.decision}`),w(n,{success:!0,data:s})):w(n,{success:!1,error:{code:"RESOLVE_FAILED",message:`Cannot resolve approval ${i.approvalId} (not found or not pending)`}})}),t.registerGatewayMethod(k.runHoldList,async n=>{let i=j(U(n))??{},s=r.runHolds.list(i);return w(n,{success:!0,data:{total:s.total,items:s.items}})}),t.registerGatewayMethod(k.runHoldResume,async n=>{let i=j(U(n));if(!i?.holdId)return w(n,{success:!1,error:{code:"MISSING_ID",message:"holdId is required"}});let s=r.runHolds.resume(i.holdId);return s?(Ht(t,`RunHold ${i.holdId} resumed`),w(n,{success:!0,data:s})):w(n,{success:!1,error:{code:"RESUME_FAILED",message:`Cannot resume run hold ${i.holdId}`}})}),t.registerGatewayMethod(k.runHoldCancel,async n=>{let i=j(U(n));if(!i?.holdId)return w(n,{success:!1,error:{code:"MISSING_ID",message:"holdId is required"}});let s=r.runHolds.cancel(i.holdId);return s?(Ht(t,`RunHold ${i.holdId} cancelled`),w(n,{success:!0,data:s})):w(n,{success:!1,error:{code:"CANCEL_FAILED",message:`Cannot cancel run hold ${i.holdId}`}})}),t.registerGatewayMethod(k.metrics,async n=>{let i=r.getStats();return w(n,{success:!0,data:{...i,uptime:typeof globalThis<"u"&&"process"in globalThis?globalThis.process.uptime():0}})}),t.registerGatewayMethod(k.releaseInfo,async n=>w(n,{success:!0,data:{name:"runtime-guardrail",version:B,description:"OpenClaw runtime guardrail plugin with remote policy, bundled guardrail config, skills upload, and telemetry."}}))}function En(t,e){Cn(t,e);try{_n(t,e)}catch{}}function Cn(t,e){let{config:o}=e;t.registerCli(P.status,()=>({simulated:o.simulated,failMode:o.failMode,stats:{incidents:0,approvals:0,pendingApprovals:0,runHolds:0,pendingRunHolds:0},e:"Stats shown are from plugin initialization (empty). For runtime stats, use: openclaw gateway call shield.status --json"})),t.registerCli(P.incidents,()=>({total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.incident.list --json"})),t.registerCli(P.approvals,()=>({total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.approval.list --json"})),t.registerCli(P.runHolds,()=>({total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.runhold.list --json"})),t.registerCli(P.configRefresh,async()=>e.onRemoteGuardRefresh?await e.onRemoteGuardRefresh():{error:"Remote guard config refresh is unavailable"})}function _n(t,e){let{config:o}=e;t.registerCli(({program:r})=>{let n=r.command("shield");n.description("Runtime guardrail commands").action(()=>({simulated:o.simulated,failMode:o.failMode})),n.command("status").description("Show guardrail status").action(()=>{let i={simulated:o.simulated,failMode:o.failMode,stats:{incidents:0,approvals:0,pendingApprovals:0,runHolds:0,pendingRunHolds:0},e:"Stats shown are from plugin initialization (empty). For runtime stats, use: openclaw gateway call shield.status --json"};return W(i),i}),n.command("incidents").description("List incidents").option("--limit <n>","limit",20).option("--offset <n>","offset",0).action(()=>{let i={total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.incident.list --json"};return W(i),i}),n.command("approvals").description("List approvals").option("--status <status>","pending|approved|rejected").option("--limit <n>","limit",20).action(()=>{let i={total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.approval.list --json"};return W(i),i}),n.command("runholds").description("List run holds").option("--status <status>","held|resumed|cancelled").option("--limit <n>","limit",20).action(()=>{let i={total:0,items:[],e:"Data shown is from plugin initialization (empty). For runtime data, use: openclaw gateway call shield.runhold.list --json"};return W(i),i}),n.command("config-refresh").description("Reload local guardrail config and hot-reload runtime services").action(async()=>{if(!e.onRemoteGuardRefresh){let s={error:"Remote guard config refresh is unavailable"};return W(s),s}let i=await e.onRemoteGuardRefresh();return W(i),i})},{commands:["shield","shield status","shield incidents","shield approvals","shield runholds","shield config-refresh"]})}function U(t){if(typeof t!="object"||t===null)return;let e=t;return e.params??e.data}function j(t){if(typeof t!="object"||t===null)return;let e=t;return e.params??e}function Ht(t,e){if(t.log&&typeof t.log.info=="function"){t.log.info(e);return}t.logger&&typeof t.logger.info=="function"&&t.logger.info(e)}function W(t){try{console.log(JSON.stringify(t,null,2))}catch{}}function w(t,e){let o=t;if(typeof o.respond=="function"){if(e.success){o.respond(!0,e.data??{});return}o.respond(!1,e.error??{code:"UNKNOWN",message:"unknown error"})}}var Dn=t=>{let e=t,o=[];if(typeof e.on!="function"&&o.push("on"),typeof e.registerGatewayMethod!="function"&&o.push("registerGatewayMethod"),typeof e.registerCli!="function"&&o.push("registerCli"),o.length>0)throw new Error(`[runtime-guardrail] OpenClawApi contract mismatch, missing/invalid: ${o.join(", ")}`)},Lt=class{constructor(e,o){this.inner=e;this.store=o}async evaluate(e){let o=await this.inner.evaluate(e);return this.store.processDecisionEffects(o),o}evaluateSync(e){if(typeof this.inner.evaluateSync!="function")return;let o=this.inner.evaluateSync(e);if(o)return this.store.processDecisionEffects(o),o}},Q=class{constructor(e,o={}){this.api=e;this.options=o}info(e){let o=this.api.log;o&&typeof o.info=="function"&&o.info(e)}initialize(){Dn(this.api),this.runtimeConfig={simulated:this.options.config?.simulated??!1,failMode:this.options.config?.failMode??"open"},this.store=new Z;let e=this.options.policyEvaluator??(()=>{let{evaluator:n,transportEnabled:i}=this.buildRemoteEvaluator();this.remoteEvaluatorRef={evaluator:n,rebuild:()=>{let a=this.buildRemoteEvaluator();return this.remoteEvaluatorRef.evaluator=a.evaluator,a.transportEnabled}},console.info(i?"[runtime-guardrail] Remote guard transport loaded from config":"[runtime-guardrail] Remote guard transport not configured, using fallback");let s=this;return{async evaluate(a,l){return s.remoteEvaluatorRef.evaluator.evaluate(a,l)},evaluateSync(a,l){let d=s.remoteEvaluatorRef.evaluator;return typeof d.evaluateSync=="function"?d.evaluateSync(a,l):{rawAction:"allow",effectiveAction:"allow",severity:"low",simulatedBlock:!1,simulatedRedaction:!1,redactions:[],redactedTexts:{},matched:[],appliedExceptions:[],redactionRules:[],tags:[],durationMs:0,findings:[],modifications:[]}}}})(),o=new F(this.runtimeConfig,e,this.options.auditLogger??mt());this.evaluateService=new Lt(o,this.store);let r;if(!this.options.policyEvaluator){let n=this.options.remoteGuard?.config??C(this.options.remoteGuard?.configPath,!0);r=St(n)}pt(this.api,this.evaluateService,r),this.options.enableRpcHandlers!==!1&&Mt(this.api,{config:this.runtimeConfig,store:this.store,onRemoteGuardRefresh:async()=>await this.refreshRemoteGuardConfig()}),Go(this.api,{...this.options.skillsUpload,remoteGuardConfigPath:this.options.remoteGuard?.configPath,remoteGuardConfig:this.options.remoteGuard?.config}),this.telemetryService=bt(this.api,this.store,{...this.options.telemetry,remoteGuardConfigPath:this.options.remoteGuard?.configPath,remoteGuardConfig:this.options.remoteGuard?.config}),this.info("runtime-guardrail plugin runtime initialized")}getEvaluateService(){if(!this.evaluateService)throw new Error("PluginRuntime has not been initialized");return this.evaluateService}getStore(){if(!this.store)throw new Error("PluginRuntime has not been initialized");return this.store}getConfig(){return this.runtimeConfig}getTelemetryService(){return this.telemetryService}buildRemoteEvaluator(){yt();let e=this.options.remoteGuard?.config??C(this.options.remoteGuard?.configPath,!0),o=e&&e.enabled?te(e):void 0,r={...this.options.remotePolicy,transport:this.options.remotePolicy?.transport??o};return{evaluator:gt(r),transportEnabled:!!o}}async refreshRemoteGuardConfig(){let e=this.options.remoteGuard?.config?"[in-memory-config]":vt(this.options.remoteGuard?.configPath),o=this.remoteEvaluatorRef?this.remoteEvaluatorRef.rebuild():!!(this.options.remoteGuard?.config??C(this.options.remoteGuard?.configPath,!0)),r=!1;if(this.telemetryService){let i=Fe({...this.options.telemetry,remoteGuardConfigPath:this.options.remoteGuard?.configPath,remoteGuardConfig:this.options.remoteGuard?.config});await this.telemetryService.reload(i),r=!0}let n=o?`Remote guard config reloaded from ${e}`:`Remote guard config reloaded from ${e}, but remote transport is disabled or auth.key is unavailable`;return this.info(n),{ok:!0,configPath:e,transportEnabled:o,telemetryReloaded:r,message:n}}async dispose(){this.info("Disposing runtime-guardrail plugin runtime..."),this.telemetryService&&(await this.telemetryService.stop(),this.telemetryService=void 0),this.store&&(this.store.incidents.clear(),this.store=void 0),this.evaluateService=void 0,this.runtimeConfig=void 0,this.remoteEvaluatorRef=void 0,this.info("runtime-guardrail plugin runtime disposed")}};var re=class{constructor(){this.files=new Map}async read(e){return this.files.get(e)?.content}async write(e,o){this.files.set(e,{key:e,content:o,updatedAt:Date.now()})}async delete(e){this.files.delete(e)}async list(e){let o=[...this.files.values()];return e?o.filter(r=>r.key.startsWith(e)):o}};var J=class{constructor(){this.records=[]}async record(e){this.records.push(e)}async list(){return[...this.records]}},ne=class{constructor(e,o,r=new J){this.fileStore=e;this.key=o;this.corruptionSink=r}async append(e){let o=await this.readEntries();o.push(e),await this.fileStore.write(this.key,JSON.stringify(o,null,2))}async list(){return this.readEntries()}async replace(e){await this.fileStore.write(this.key,JSON.stringify(e,null,2))}async readEntries(){let e=await this.fileStore.read(this.key);if(!e)return[];try{let o=JSON.parse(e);if(!Array.isArray(o))throw new Error("Event log payload must be an array");return o}catch(o){return await this.corruptionSink.record({key:this.key,detectedAt:Date.now(),reason:o instanceof Error?o.message:"Unknown JSON parse error",rawContent:e}),[]}}};var ie=class{constructor(e,o){this.fileStore=e;this.key=o}async load(){let e=await this.fileStore.read(this.key);if(e)return JSON.parse(e)}async save(e){await this.fileStore.write(this.key,JSON.stringify(e,null,2))}async clear(){await this.fileStore.delete(this.key)}};var Ot=class{constructor(e,o){this.fileStore=e;this.key=o}async enqueue(e){let o=await this.list(),r={id:`queue_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,createdAt:Date.now(),payload:e};return o.push(r),await this.fileStore.write(this.key,JSON.stringify(o,null,2)),r}async list(){let e=await this.fileStore.read(this.key);if(!e)return[];let o=JSON.parse(e);return Array.isArray(o)?o:[]}async remove(e){let o=(await this.list()).filter(r=>r.id!==e);await this.fileStore.write(this.key,JSON.stringify(o,null,2))}};var se=class{async detect(){return[]}};var Tn=t=>{switch(t){case"prompt_injection":case"retrieval_poisoning":case"sensitive_action":case"egress_exfiltration":case"mcp_allowlist_violation":case"model_governance_violation":case"approval_required":return t;default:return"unknown"}},Gt=(t,e)=>e.map((o,r)=>({findingId:`${t}_finding_${r}`,detector:o.detector,reasonCode:Tn(o.reasonCode),severity:o.severity,summary:o.summary,tags:o.tags,evidence:o.evidence,matchedRuleIds:o.matchedRuleIds}));var $o={low:1,medium:2,high:3,critical:4},xn=t=>$o[t]>=$o.high,Nt=(t,e,o)=>{if(e==="allow")return{effectiveAction:"allow",simulatedBlock:!1,simulatedRedaction:!1};switch(t){case"observe":case"shadow":return{effectiveAction:"allow",simulatedBlock:e==="block",simulatedRedaction:e==="redact"};case"enforce_high":return xn(o)?{effectiveAction:e,simulatedBlock:!1,simulatedRedaction:!1}:{effectiveAction:"allow",simulatedBlock:e==="block",simulatedRedaction:e==="redact"};default:return{effectiveAction:e,simulatedBlock:!1,simulatedRedaction:!1}}};var Fo={low:1,medium:2,high:3,critical:4},Hn=t=>t.length===0?"low":[...t].sort((e,o)=>Fo[o]-Fo[e])[0],Mn=t=>t.length===0?"allow":t.some(e=>e.severity==="high"||e.severity==="critical")?"block":"redact",Ln=t=>t.flatMap(e=>(e.ruleIds??[]).map(o=>({ruleId:o,action:e.severity==="high"||e.severity==="critical"?"block":"redact",severity:e.severity,reason:e.summary}))),ae=class{constructor(e=[new se]){this.detectors=e}async evaluate(e,o){let r=Date.now(),n=(await Promise.all(this.detectors.map(u=>u.detect(e,{stage:o,now:r})))).flat(),s=Gt(`policy_${r}`,n).map(u=>({detector:u.detector,reasonCode:u.reasonCode,severity:u.severity,summary:u.summary,tags:u.tags,evidence:u.evidence,ruleIds:u.matchedRuleIds})),a=Hn(s.map(u=>u.severity)),l=Mn(s),d=Nt(o,l,a);return{rawAction:l,effectiveAction:d.effectiveAction,severity:a,blockReason:l==="block"?s[0]?.summary??"Blocked by policy":void 0,simulatedBlock:d.simulatedBlock,simulatedRedaction:d.simulatedRedaction,redactions:n.flatMap(u=>u.redactions??[]),matched:Ln(s),appliedExceptions:[],redactionRules:[],tags:[...new Set(s.flatMap(u=>u.tags))],durationMs:Date.now()-r,findings:s,modifications:n.flatMap(u=>(u.redactions??[]).map(g=>({type:"redact",target:g.field,replacement:g.replacement,ruleId:g.ruleId})))}}};var On={pending:["approved","rejected","expired","cancelled"],approved:[],rejected:[],expired:[],cancelled:[]},le=class{canTransition(e,o){return On[e].includes(o)}transition(e,o){if(!this.canTransition(e,o))throw new Error(`Invalid approval transition: ${e} -> ${o}`);return o}};var ce=class{constructor(e,o){this.stateMachine=e;this.hitlConnector=o;this.approvals=new Map}async createFromDecision(e){let r=e.effects.filter(n=>n.type==="queue_approval").map((n,i)=>{let s=`approval_${e.decisionId}_${i}`,a={...n.ticket,approvalId:s,incidentId:e.decisionId,status:"pending",createdAt:Date.now()};return this.approvals.set(s,a),a});return await Promise.all(r.map(n=>this.hitlConnector.enqueueApproval(n))),r}async getById(e){return this.approvals.get(e)}async list(e){let o=[...this.approvals.values()];e?.status?.length&&(o=o.filter(i=>e.status?.includes(i.status))),e?.category?.length&&(o=o.filter(i=>e.category?.includes(i.category)));let r=e?.offset??0,n=e?.limit??o.length;return o.slice(r,r+n)}async resolve(e){let o=this.approvals.get(e.approvalId);if(!o)throw new Error(`Approval not found: ${e.approvalId}`);let r=this.stateMachine.transition(o.status,e.decision),n={...o,status:r,resolvedAt:Date.now(),resolvedBy:e.resolvedBy,note:e.note,sourceBinding:e.source?{source:e.source,externalApprovalId:e.externalApprovalId}:o.sourceBinding};return this.approvals.set(e.approvalId,n),await this.hitlConnector.notifyResolution(n),n}};var de=class{constructor(){this.holds=new Map}async createFromDecision(e,o=[]){return e.effects.filter(i=>i.type==="create_run_hold").map((i,s)=>{let a=`hold_${e.decisionId}_${s}`,l={...i.hold,holdId:a,approvalId:o[s]??`unbound_${e.decisionId}`,status:"pending",createdAt:Date.now()};return this.holds.set(a,l),l})}async getById(e){return this.holds.get(e)}async list(e){let o=[...this.holds.values()];e?.agentId&&(o=o.filter(i=>i.agentId===e.agentId)),e?.status?.length&&(o=o.filter(i=>e.status?.includes(i.status)));let r=e?.offset??0,n=e?.limit??o.length;return o.slice(r,r+n)}async resume(e){let o=this.holds.get(e);if(!o)throw new Error(`Run hold not found: ${e}`);let r={...o,status:"resumed",resumedAt:Date.now()};return this.holds.set(e,r),r}async cancel(e){let o=this.holds.get(e);if(!o)throw new Error(`Run hold not found: ${e}`);let r={...o,status:"cancelled",cancelledAt:Date.now()};return this.holds.set(e,r),r}};var ue=class{async enqueueApproval(){}async notifyResolution(){}};var pe=class{async apply(e,o,r){let n={...e};if(o.canonicalHook==="before_llm_request"){let i=o;n={...n,allowedProviderIds:n.allowedProviderIds??[i.payload.provider],allowedModelIds:n.allowedModelIds??[i.payload.model]}}for(let i of r.effects)i.type==="require_model_profile"&&(n={...n,requiredModelProfile:i.profileId}),i.type==="restrict_model"&&(n={...n,allowedProviderIds:i.allowedProviderIds??n.allowedProviderIds,allowedModelIds:i.allowedModelIds??n.allowedModelIds});return n}};var me=class{async apply(e,o){return o.canonicalHook!=="message_sending"?e:{...e,egressPolicy:e.egressPolicy??{channelAllowlist:o.payload.channel?[o.payload.channel]:void 0}}}};var Gn=t=>{switch(t.canonicalHook){case"before_llm_request":return"llm_request";case"before_tool_call":return"tool_call";case"message_sending":return"message_send";default:return}},ge=class{constructor(e,o){this.modelGovernanceService=e;this.egressMediator=o}async build(e,o){let r=Gn(e);if(!r)return;let n={planId:`plan_${o.decisionId}`,actionType:r,approvalMode:o.effects.some(s=>s.type==="queue_approval")?"required":"none"};n=await this.modelGovernanceService.apply(n,e,o),n=await this.egressMediator.apply(n,e);let i=o.effects.find(s=>s.type==="route_execution");if(i?.type==="route_execution"){let s=i.plan,{planId:a,actionType:l,approvalMode:d,...u}=s;n={...n,...u}}return n}};var fe=class{constructor(e,o,r){this.planBuilder=e;this.approvalService=o;this.runHoldService=r}async prepare(e,o){return this.planBuilder.build(e,o)}};var he=class{constructor(e){this.eventLog=e}async append(e){await this.eventLog.append(e)}async list(){return this.eventLog.list()}};var ye=class{constructor(e,o){this.eventStore=e;this.readModelBuilder=o}async append(e){await this.eventStore.append(e),this.readModelBuilder&&await this.readModelBuilder.refresh()}async appendDecision(e){await this.append(X(e))}async logDecision(e){await this.appendDecision(e)}async listEvents(){return this.eventStore.list()}};var ve=class{constructor(e){this.readModelBuilder=e}async listIncidents(){return(await this.readModelBuilder.getCurrent()).incidents}};var Nn=()=>({low:0,medium:0,high:0,critical:0}),$t=t=>{let e={totalEvents:t.length,eventTypeCounts:{},blockedDecisionCount:0,decisionCount:0,severityCounts:Nn()};for(let o of t){e.eventTypeCounts[o.eventType]=(e.eventTypeCounts[o.eventType]??0)+1,(o.eventType==="decision.made"||o.eventType==="decision.blocked")&&(e.decisionCount+=1),o.eventType==="decision.blocked"&&(e.blockedDecisionCount+=1);let r=o.payload.severity;(r==="low"||r==="medium"||r==="high"||r==="critical")&&(e.severityCounts[r]+=1)}return e};var $n=t=>{let e=t.payload.effects;if(Array.isArray(e))for(let o of e){if(typeof o!="object"||o===null)continue;let r=o;if(r.type==="block"&&typeof r.reason=="string")return r.reason}},Fn=t=>t.filter(e=>e.eventType==="decision.blocked").map(e=>({id:e.decisionId??e.eventId,timestamp:e.timestamp,surface:e.payload.surface??"tool",agentId:e.agentId,runId:e.runId,sessionId:e.sessionId,rawAction:"block",effectiveAction:"block",blockReason:$n(e),severity:e.payload.severity??"medium",tags:Array.isArray(e.payload.tags)?e.payload.tags:[]})),ke=class{constructor(e,o){this.auditEventStore=e;this.snapshotStore=o}async getCurrent(){let e=await this.snapshotStore.load();return e||this.refresh()}async refresh(){let e=await this.auditEventStore.list(),o={schemaVersion:1,generatedAt:Date.now(),incidents:Fn(e),metrics:$t(e)};return await this.snapshotStore.save(o),o}};var Se=class{constructor(e,o,r,n){this.runtimeConfig=e;this.auditReadModelBuilder=o;this.approvalService=r;this.runHoldService=n}async getStatus(){let[e,o,r]=await Promise.all([this.auditReadModelBuilder.getCurrent(),this.approvalService.list(),this.runHoldService.list()]);return{version:B,config:{simulated:this.runtimeConfig.simulated,failMode:this.runtimeConfig.failMode},stats:{incidents:e.metrics.blockedDecisionCount,approvals:o.length,runHolds:r.length}}}async listIncidents(e){let o=(await this.auditReadModelBuilder.getCurrent()).incidents;e?.agentId&&(o=o.filter(s=>s.agentId===e.agentId)),e?.runId&&(o=o.filter(s=>s.runId===e.runId));let r=o.length,n=e?.offset??0,i=e?.limit??r;return{total:r,items:o.slice(n,n+i)}}async listApprovals(e){let o=await this.approvalService.list(e);return{total:o.length,items:o}}async listRunHolds(e){let o=await this.runHoldService.list(e);return{total:o.length,items:o}}};var we=class{constructor(e,o){this.approvalService=e;this.runHoldService=o}async resolveApproval(e){return this.approvalService.resolve({approvalId:e.approvalId,decision:e.decision,resolvedBy:e.resolvedBy,note:e.note,source:e.source,externalApprovalId:e.externalApprovalId})}async resumeRunHold(e){return this.runHoldService.resume(e.holdId)}async cancelRunHold(e){return this.runHoldService.cancel(e.holdId)}};var V=t=>typeof t=="object"&&t!==null?t:{},Bn=(t,e,o,r)=>{t.registerGatewayMethod(k.status,async()=>{await e.getStatus()}),t.registerGatewayMethod(k.incidentList,async n=>{await e.listIncidents(V(n.data))}),t.registerGatewayMethod(k.approvalList,async n=>{await e.listApprovals(V(n.data))}),t.registerGatewayMethod(k.approvalResolve,async n=>{let i=V(n.data);typeof i.approvalId=="string"&&(i.decision==="approved"||i.decision==="rejected")&&await o.resolveApproval({approvalId:i.approvalId,decision:i.decision,resolvedBy:typeof i.resolvedBy=="string"?i.resolvedBy:void 0,note:typeof i.note=="string"?i.note:void 0,source:typeof i.source=="string"?i.source:void 0,externalApprovalId:typeof i.externalApprovalId=="string"?i.externalApprovalId:void 0})}),t.registerGatewayMethod(k.runHoldList,async n=>{await e.listRunHolds(V(n.data))}),t.registerGatewayMethod(k.runHoldResume,async n=>{let i=V(n.data);typeof i.holdId=="string"&&await o.resumeRunHold({holdId:i.holdId})}),t.registerGatewayMethod(k.runHoldCancel,async n=>{let i=V(n.data);typeof i.holdId=="string"&&await o.cancelRunHold({holdId:i.holdId})}),t.registerGatewayMethod(k.reportGenerate,async()=>{await r.generateSummary()})};var qn=(t,e,o)=>{t.registerCli(P.status,async()=>e.getStatus()),t.registerCli(P.approvals,async()=>e.listApprovals()),t.registerCli(P.runHolds,async()=>e.listRunHolds())};var Re=class{constructor(e){this.queryService=e}async generateSummary(){let[e,o,r,n]=await Promise.all([this.queryService.getStatus(),this.queryService.listIncidents(),this.queryService.listApprovals(),this.queryService.listRunHolds()]);return{generatedAt:Date.now(),status:e,incidents:{total:o.total},approvals:{total:r.total},runHolds:{total:n.total}}}};var Un=(t,e)=>({async logDecision(o){try{await t.logDecision(o)}catch(r){console.error("[runtime-guardrail] Primary audit logger failed:",r)}try{await e.logDecision(o)}catch(r){console.error("[runtime-guardrail] Secondary audit logger failed:",r)}},logDecisionSync(o){try{typeof t.logDecisionSync=="function"&&t.logDecisionSync(o)}catch(r){console.error("[runtime-guardrail] Primary audit logger sync failed:",r)}try{typeof e.logDecisionSync=="function"&&e.logDecisionSync(o)}catch(r){console.error("[runtime-guardrail] Secondary audit logger sync failed:",r)}}}),jn=(t={})=>{let e={simulated:t.config?.simulated??!1,failMode:t.config?.failMode??"open"},o=new re,r=new J,n=new ne(o,"audit/events.json",r),i=new ie(o,"audit/read-model.json"),s=t.policyEvaluator??new ae,a=new he(n),l=new ke(a,i),d=new ye(a,l),u=t.auditLogger?Un(d,t.auditLogger):d,g=new ve(l),c=new ce(new le,new ue),y=new de,f=new pe,S=new me,h=new ge(f,S),v=new fe(h,c,y),H=new Se(e,l,c,y),qe=new we(c,y),Ue=new Re(H),be=new F(e,s,u);return{runtimeConfig:e,fileStore:o,policyEvaluator:s,auditLogger:u,evaluateService:be,auditEventStore:a,auditReadModelBuilder:l,auditService:d,incidentQueryService:g,approvalService:c,runHoldService:y,modelGovernanceService:f,egressMediator:S,executionPlanBuilder:h,executionBroker:v,operatorQueryService:H,operatorCommandService:qe,reportService:Ue}};var Be=class{constructor(e){this.hookHandlers=new Map;this.gatewayMethods=new Map;this.cliCommands=new Map;this.logEntries=[];this.log={info:e=>{this.logEntries.push({level:"info",message:e,timestamp:Date.now()}),this.silent||console.log(`[mock-host] INFO: ${e}`)},warn:e=>{this.logEntries.push({level:"warn",message:e,timestamp:Date.now()}),this.silent||console.warn(`[mock-host] WARN: ${e}`)},error:e=>{this.logEntries.push({level:"error",message:e,timestamp:Date.now()}),this.silent||console.error(`[mock-host] ERROR: ${e}`)},debug:e=>{this.logEntries.push({level:"debug",message:e,timestamp:Date.now()}),this.silent||console.debug(`[mock-host] DEBUG: ${e}`)}};this.silent=e?.silent??!1}on(e,o){let r=this.hookHandlers.get(e)??[];r.push(o),this.hookHandlers.set(e,r)}registerGatewayMethod(e,o){this.gatewayMethods.set(e,o)}registerCli(e,o){if(typeof e=="string"){let a=o;a&&this.cliCommands.set(e,a);return}let r=new Map,n=a=>{let l={command:d=>n(a?`${a} ${d}`.trim():d.trim()),description:d=>l,option:(d,u,g)=>l,action:d=>(r.set(a,async u=>d(u)),l)};return l};e({program:{command:a=>n(a.trim())},config:{},workspaceDir:"/mock/workspace",logger:this.log});for(let[a,l]of r.entries())this.cliCommands.set(a,l)}async triggerHook(e,o,r){let n=this.hookHandlers.get(e)??[],i=[];for(let s of n){let a=await s(o,r??{});i.push(a)}return i}async callRpc(e,o){let r=this.gatewayMethods.get(e);if(!r)throw new Error(`RPC method "${e}" not registered`);await r({eventName:e,data:{params:o}})}async executeCli(e,o){let r=this.cliCommands.get(e);if(!r)throw new Error(`CLI command "${e}" not registered`);return r(o??{},{projectPath:"/mock/project",api:this})}getRegisteredHooks(){return Array.from(this.hookHandlers.keys())}getRegisteredGatewayMethods(){return Array.from(this.gatewayMethods.keys())}getRegisteredCliCommands(){return Array.from(this.cliCommands.keys())}getLogs(){return[...this.logEntries]}getLogsByLevel(e){return this.logEntries.filter(o=>o.level===e)}clearLogs(){this.logEntries.length=0}setSilent(e){this.silent=e}},Kn=t=>new Be(t);var _=t=>!t||typeof t!="object"||Array.isArray(t)?{}:t,Bo=t=>typeof t=="boolean"?t:void 0,zn=t=>typeof t=="number"&&Number.isFinite(t)?t:void 0,qo=t=>{if(typeof t!="string")return;let e=t.trim();return e||void 0},Wn=t=>{let e={..._(t.legacyOptionsRoot.skillsUpload),..._(t.configRoot.skillsUpload)},o={},r=Bo(e.enabled);typeof r=="boolean"&&(o.enabled=r);let n=Bo(e.force);typeof n=="boolean"&&(o.force=n);let i=zn(e.timeoutMs);typeof i=="number"&&(o.timeoutMs=i);let s=qo(e.contentType);return s&&(o.contentType=s),Object.keys(o).length>0?o:void 0},Qn=async t=>{let e=t.runtime?.config?.loadConfig;if(typeof e=="function")try{let o=await Promise.resolve(e()),r=_(o),n=_(r.plugins),i=_(n.entries),s=_(i["runtime-guardrail"]),a=_(s.config),l=_(s.options),d={},u=Wn({configRoot:a,legacyOptionsRoot:l});u&&(d.skillsUpload=u);let g={..._(l.remoteGuard),..._(a.remoteGuard)},c=qo(g.configPath);return c&&(d.remoteGuard={configPath:c}),Object.keys(d).length>0?d:void 0}catch{return}},Jn=(t,e)=>{if(!t&&!e)return;let o={...t??{},...e??{}},r={...t?.skillsUpload??{},...e?.skillsUpload??{}};Object.keys(r).length>0&&(o.skillsUpload=r);let n={...t?.remoteGuard??{},...e?.remoteGuard??{}};return Object.keys(n).length>0&&(o.remoteGuard=n),o},Uo=async(t,e)=>{let o=await Qn(t),r=Jn(o,e),n=new Q(t,r);return n.initialize(),n},Ft=t=>{Uo(t).catch(e=>{console.error("[runtime-guardrail] Plugin initialization failed:",e)})},Vn=Ft,Yn=Ft;0&&(module.exports={ApprovalStateMachine,ApprovalStore,AuditReadModelBuilder,DefaultAuditService,DefaultEgressMediator,DefaultExecutionBroker,DefaultExecutionPlanBuilder,DefaultModelGovernanceService,DefaultOperatorCommandService,DefaultOperatorQueryService,DefaultReportService,EvaluateServiceImpl,InMemoryApprovalService,InMemoryEventLogCorruptionSink,InMemoryFileStore,InMemoryRunHoldService,IncidentQueryService,IncidentStore,JsonAuditEventStore,JsonEventLog,JsonQueueStore,JsonSnapshotStore,MemoryAuditLogger,MemoryStore,MockOpenClawApi,NoopHitlConnector,NoopPolicyDetector,PluginRuntime,PolicyEngine,RemotePolicyEvaluator,RunHoldStore,SHIELD_CLI_COMMANDS,SHIELD_GATEWAY_EVENTS,SHIELD_RPC_METHODS,TelemetryService,activate,buildDecisionAuditEvent,buildMetricsProjection,buildRemoteGuardRequest,buildRemoteGuardRequestSync,buildTelemetryConfigFromRemoteGuard,clearRemoteGuardConfigCache,createAutoConfigRemoteGuardTransport,createAutoConfigRemoteGuardTransportSync,createBeforeMessageWriteAdapter,createEgressAdapter,createIngressAdapter,createLlmRequestAdapter,createMemoryAuditLogger,createMockOpenClawApi,createNoopAuditLogger,createNoopPolicyEvaluator,createPromptBuildAdapter,createRemoteGuardHttpTransport,createRemotePolicyEvaluator,createRuntimeFacade,createRuntimeGuardrailPlugin,createSyncRemoteEvaluateFn,createToolCallAdapter,createToolResultPersistAdapter,getDefaultRemoteGuardAuthConfigPath,getDefaultRemoteGuardConfigPath,getRemoteGuardConfig,loadRemoteGuardConfig,loadRemoteGuardConfigSync,mapApprovalRequestToTicket,mapBeforeMessageWriteToEnvelope,mapDecisionToBeforeMessageWriteResult,mapDecisionToBeforePromptBuildResult,mapDecisionToBeforeToolCallResult,mapDecisionToHookResult,mapDecisionToMessageSendingResult,mapDecisionToToolResultPersistResult,mapLlmRequestToEnvelope,mapMessageReceivedToEnvelope,mapMessageSendingToEnvelope,mapPolicyResultToDecision,mapPromptBuildToEnvelope,mapRemoteGuardResponseToPolicyResponse,mapRunHoldToTicket,mapToolCallToEnvelope,mapToolResultPersistToEnvelope,mergeHookResults,normalizeSignals,register,registerRpcAndCliHandlers,registerShieldCli,registerShieldHooks,registerShieldRpc,registerTelemetryService,resolveRuntimeRemoteGuardConfigPath,resolveStageAction,resolveTelemetryServiceConfig,setRemoteGuardConfig,syncRemoteEvaluate});
@@ -0,0 +1,9 @@
1
+ import type { PluginHookHandler } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 创建出口适配器(message_sending 钩子处理器)
5
+ * 在向用户发送消息前进行安全评估,并尽量返回 OpenClaw 真正可消费的改写结果:
6
+ * - block → cancel
7
+ * - redact → content
8
+ */
9
+ export declare const createEgressAdapter: (evaluateService: EvaluateService) => PluginHookHandler<"message_sending">;
@@ -0,0 +1,21 @@
1
+ import type { GuardrailDecision, OpenClawApi } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 同步远端评估函数类型(可选注入)
5
+ * 由 syncRemoteEvaluate 模块提供,在同步 hook 中通过 Worker + SAB 同步等待远端结果
6
+ */
7
+ type SyncRemoteEvaluateFn = (envelope: Parameters<EvaluateService["evaluate"]>[0]) => GuardrailDecision;
8
+ /**
9
+ * 向宿主平台注册所有安全 hook(核心注册入口)
10
+ *
11
+ * 说明:
12
+ * - 插件内部 canonical 生命周期仍然是 6 个安全检测点
13
+ * - 但在 OpenClaw 宿主里,当前实际会注册 7 个 hook 名称
14
+ * (其中 `before_prompt_build` 是 ingress 的补位入口,`llm_input` 是宿主真实 LLM 观察 hook)
15
+ *
16
+ * @param api 宿主平台 API
17
+ * @param evaluateService 评估服务
18
+ * @param syncRemoteEvaluateFn 可选的同步远端评估函数,用于同步 persist hooks
19
+ */
20
+ export declare const registerShieldHooks: (api: OpenClawApi, evaluateService: EvaluateService, syncRemoteEvaluateFn?: SyncRemoteEvaluateFn) => void;
21
+ export {};
@@ -0,0 +1,43 @@
1
+ import type { GuardrailDecision } from "../../contracts";
2
+ import type { ShieldHookResult } from "../../types";
3
+ /**
4
+ * 合并多个钩子结果为一个最终结果
5
+ */
6
+ export declare const mergeHookResults: (results: ShieldHookResult[]) => ShieldHookResult;
7
+ /**
8
+ * 将护栏决策(GuardrailDecision)映射为通用 ShieldHookResult
9
+ */
10
+ export declare const mapDecisionToHookResult: (decision: GuardrailDecision) => ShieldHookResult;
11
+ /** 将决策映射为 OpenClaw before_prompt_build 结果 */
12
+ export declare const mapDecisionToBeforePromptBuildResult: (decision: GuardrailDecision) => {
13
+ systemPrompt?: string;
14
+ prependContext?: string;
15
+ prependSystemContext?: string;
16
+ appendSystemContext?: string;
17
+ } | undefined;
18
+ /** 将决策映射为 OpenClaw before_tool_call 结果 */
19
+ export declare const mapDecisionToBeforeToolCallResult: (decision: GuardrailDecision) => {
20
+ params?: Record<string, unknown>;
21
+ block?: boolean;
22
+ blockReason?: string;
23
+ } | undefined;
24
+ /** 将决策映射为 OpenClaw message_sending 结果 */
25
+ export declare const mapDecisionToMessageSendingResult: (decision: GuardrailDecision) => {
26
+ content?: string;
27
+ cancel?: boolean;
28
+ } | undefined;
29
+ /**
30
+ * 将决策映射为 OpenClaw before_message_write 结果
31
+ *
32
+ * 设计说明:deny 场景下不使用 block:true,而是将有害内容替换为拦截信息后写入 session。
33
+ * 原因:openclaw 在 block=true 时直接丢弃消息(return null),完全忽略 message 字段,
34
+ * 导致前端无法展示拦截信息,用户只看到消息"消失"了。
35
+ * 改为纯消息替换后,拦截信息会写入 session JSONL,前端能实时展示。
36
+ */
37
+ export declare const mapDecisionToBeforeMessageWriteResult: (decision: GuardrailDecision, originalMessage: unknown) => {
38
+ message?: unknown;
39
+ } | undefined;
40
+ /** 将决策映射为 OpenClaw tool_result_persist 结果 */
41
+ export declare const mapDecisionToToolResultPersistResult: (decision: GuardrailDecision, originalMessage: unknown) => {
42
+ message?: unknown;
43
+ } | undefined;
@@ -0,0 +1,31 @@
1
+ import type { EgressEnvelope, IngressEnvelope, LlmRequestEnvelope, PersistEnvelope, PluginHookBeforeMessageWriteEvent, PluginHookBeforeToolCallEvent, PluginHookLlmInputEvent, PluginHookMessageReceivedEvent, PluginHookMessageSendingEvent, PluginHookToolResultPersistEvent, ToolCallEnvelope } from "../../contracts";
2
+ /**
3
+ * 将"收到消息"宿主事件映射为入口钩子信封(IngressEnvelope)
4
+ */
5
+ export declare const mapMessageReceivedToEnvelope: (event: PluginHookMessageReceivedEvent, ctx: unknown) => IngressEnvelope;
6
+ /**
7
+ * 将 before_prompt_build 事件映射为入口信封
8
+ * 该钩子在 agent CLI 场景更稳定,可用于覆盖 message_received 未触发的情况
9
+ */
10
+ export declare const mapPromptBuildToEnvelope: (event: unknown, ctx: unknown) => IngressEnvelope;
11
+ /**
12
+ * 将 llm_input 事件映射为逻辑上的 before_llm_request 信封
13
+ */
14
+ export declare const mapLlmRequestToEnvelope: (event: PluginHookLlmInputEvent, ctx: unknown) => LlmRequestEnvelope;
15
+ /**
16
+ * 将"工具调用前"宿主事件映射为工具调用钩子信封(ToolCallEnvelope)
17
+ * 兼容 toolParams 与 params 字段
18
+ */
19
+ export declare const mapToolCallToEnvelope: (event: PluginHookBeforeToolCallEvent, ctx: unknown) => ToolCallEnvelope;
20
+ /**
21
+ * 将"发送消息前"宿主事件映射为出口钩子信封(EgressEnvelope)
22
+ */
23
+ export declare const mapMessageSendingToEnvelope: (event: PluginHookMessageSendingEvent, ctx: unknown) => EgressEnvelope;
24
+ /**
25
+ * 将"写入消息前"宿主事件映射为持久化钩子信封(PersistEnvelope)
26
+ */
27
+ export declare const mapBeforeMessageWriteToEnvelope: (event: PluginHookBeforeMessageWriteEvent, ctx: unknown) => PersistEnvelope;
28
+ /**
29
+ * 将"工具结果持久化前"宿主事件映射为持久化钩子信封(PersistEnvelope)
30
+ */
31
+ export declare const mapToolResultPersistToEnvelope: (event: PluginHookToolResultPersistEvent, ctx: unknown) => PersistEnvelope;
@@ -0,0 +1,8 @@
1
+ export * from "./hook-types";
2
+ export * from "./hook-result-mapper";
3
+ export * from "./ingress-adapter";
4
+ export * from "./llm-request-adapter";
5
+ export * from "./tool-call-adapter";
6
+ export * from "./egress-adapter";
7
+ export * from "./persist-adapter";
8
+ export * from "./hook-registry";
@@ -0,0 +1,14 @@
1
+ import type { PluginHookHandler } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 创建入口适配器(message_received 钩子处理器)
5
+ * 将用户消息到达事件转换为信封,交给评估服务评估
6
+ * 注意:入口钩子不返回结果(仅做观察/记录),因为消息已经到达
7
+ */
8
+ export declare const createIngressAdapter: (evaluateService: EvaluateService) => PluginHookHandler<"message_received">;
9
+ /**
10
+ * 创建提示词构建前适配器(before_prompt_build)
11
+ * 用于覆盖某些入口链路不会触发 message_received 的场景。
12
+ * 同时对齐 OpenClaw 宿主能力:可返回 systemPrompt/prependContext 等字段修改提示词。
13
+ */
14
+ export declare const createPromptBuildAdapter: (evaluateService: EvaluateService) => PluginHookHandler<"before_prompt_build">;
@@ -0,0 +1,9 @@
1
+ import type { PluginHookHandler } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 创建 LLM 输入适配器(llm_input 钩子处理器)
5
+ *
6
+ * 关键事实:OpenClaw 的 `llm_input` 是观察型 hook,返回值不会参与阻断或改写。
7
+ * 因此这里仅负责把事件送入评估服务,做审计、incident 留痕和策略统计。
8
+ */
9
+ export declare const createLlmRequestAdapter: (evaluateService: EvaluateService) => PluginHookHandler<"llm_input">;
@@ -0,0 +1,30 @@
1
+ import type { GuardrailDecision, PluginHookHandler } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 同步远端评估函数类型
5
+ * 由 syncRemoteEvaluate 模块提供,通过 Worker + SharedArrayBuffer + Atomics.wait
6
+ * 在同步 hook 中同步等待远端检测结果
7
+ */
8
+ type SyncRemoteEvaluateFn = (envelope: Parameters<EvaluateService["evaluate"]>[0]) => GuardrailDecision;
9
+ /**
10
+ * 创建消息写入前适配器(before_message_write 钩子处理器)
11
+ *
12
+ * OpenClaw 该 hook 为同步签名。
13
+ *
14
+ * 评估策略:
15
+ * 1. 若提供了 syncRemoteEvaluateFn(远端模式),通过 Worker + SharedArrayBuffer + Atomics.wait
16
+ * 同步执行远端 HTTP 检测并根据结果做阻断/脱敏决策
17
+ * 2. 否则 fallback 到 evaluateService.evaluateSync
18
+ */
19
+ export declare const createBeforeMessageWriteAdapter: (evaluateService: EvaluateService, syncRemoteEvaluateFn?: SyncRemoteEvaluateFn) => PluginHookHandler<"before_message_write">;
20
+ /**
21
+ * 创建工具结果持久化前适配器(tool_result_persist 钩子处理器)
22
+ *
23
+ * OpenClaw 该 hook 为同步签名。
24
+ *
25
+ * 评估策略与 before_message_write 一致:
26
+ * 1. 若提供了 syncRemoteEvaluateFn → 同步远端 HTTP 检测
27
+ * 2. 否则 fallback 到 evaluateService.evaluateSync
28
+ */
29
+ export declare const createToolResultPersistAdapter: (evaluateService: EvaluateService, syncRemoteEvaluateFn?: SyncRemoteEvaluateFn) => PluginHookHandler<"tool_result_persist">;
30
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { PluginHookHandler } from "../../contracts";
2
+ import type { EvaluateService } from "../../runtime-core";
3
+ /**
4
+ * 创建工具调用适配器(before_tool_call 钩子处理器)
5
+ * 在执行工具调用前进行安全评估,并返回 OpenClaw 可消费的 block/blockReason 结果。
6
+ */
7
+ export declare const createToolCallAdapter: (evaluateService: EvaluateService) => PluginHookHandler<"before_tool_call">;
@@ -0,0 +1,4 @@
1
+ export * from "./plugin-runtime";
2
+ export * from "./rpc-handlers";
3
+ export * from "./testing";
4
+ export * from "./hooks";
@@ -0,0 +1,103 @@
1
+ import type { OpenClawApi } from "../contracts";
2
+ import { type SkillsUploadOptions } from "./skills-upload";
3
+ import type { SkillsAvailabilityOptions } from "./skills-availability";
4
+ import { type AuditLogger, type EvaluateService, type PolicyEvaluator, type RemoteGuardConfig, type RemotePolicyEvaluatorConfig, type ShieldRuntimeConfig } from "../runtime-core";
5
+ import { type TelemetryServiceOptions, type TelemetryService } from "../runtime-core/telemetry-service";
6
+ import { MemoryStore } from "../runtime-core/memory-store";
7
+ export type PluginRuntimeRemoteGuardOptions = {
8
+ /** 用户侧自定义远端配置文件路径 */
9
+ configPath?: string;
10
+ /** 直接注入远端配置(优先于 configPath) */
11
+ config?: RemoteGuardConfig;
12
+ };
13
+ export type PluginRuntimeRemoteGuardRefreshResult = {
14
+ ok: true;
15
+ configPath: string;
16
+ transportEnabled: boolean;
17
+ telemetryReloaded: boolean;
18
+ message: string;
19
+ };
20
+ /** 插件运行时的可选配置项 */
21
+ export type PluginRuntimeOptions = {
22
+ /** 运行时配置(阶段、是否模拟、失败模式) */
23
+ config?: Partial<ShieldRuntimeConfig>;
24
+ /** 自定义策略评估器(优先级最高;传入后不可再通过命令切换模式) */
25
+ policyEvaluator?: PolicyEvaluator;
26
+ /** 远端策略评估器配置(用于 remote 模式) */
27
+ remotePolicy?: RemotePolicyEvaluatorConfig;
28
+ /** 远端配置来源(优先使用 config,其次 configPath,再 fallback 到插件随包配置) */
29
+ remoteGuard?: PluginRuntimeRemoteGuardOptions;
30
+ /** 自定义审计日志器,不传则使用空操作(Noop)日志器 */
31
+ auditLogger?: AuditLogger;
32
+ /** 是否注册 RPC/CLI 处理器(默认 true) */
33
+ enableRpcHandlers?: boolean;
34
+ /** 启动后上传 skills 快照到远端接口(可选) */
35
+ skillsUpload?: SkillsUploadOptions;
36
+ /** 启动后执行 skills 可用性治理(可选) */
37
+ skillsAvailability?: SkillsAvailabilityOptions;
38
+ /** 遥测服务配置(心跳上报和数据上报) */
39
+ telemetry?: TelemetryServiceOptions;
40
+ };
41
+ /**
42
+ * OpenClaw 插件运行时主类
43
+ * 负责:初始化运行时配置 → 创建评估服务 → 注册 OpenClaw hooks → 注册 RPC/CLI
44
+ */
45
+ export declare class PluginRuntime {
46
+ /** 宿主平台 API(OpenClaw),提供钩子注册和日志能力 */
47
+ private readonly api;
48
+ /** 可选的运行时配置 */
49
+ private readonly options;
50
+ /** 评估服务实例,在 initialize() 后才可用 */
51
+ private evaluateService?;
52
+ /** 内存存储 */
53
+ private store?;
54
+ /** 运行时配置(可变,支持阶段动态切换) */
55
+ private runtimeConfig?;
56
+ /** 远端策略评估器(仅当未注入自定义 policyEvaluator 时可用,用于 config refresh) */
57
+ private remoteEvaluatorRef?;
58
+ /** 遥测服务实例 */
59
+ private telemetryService?;
60
+ private info;
61
+ constructor(
62
+ /** 宿主平台 API(OpenClaw),提供钩子注册和日志能力 */
63
+ api: OpenClawApi,
64
+ /** 可选的运行时配置 */
65
+ options?: PluginRuntimeOptions);
66
+ /**
67
+ * 初始化插件运行时
68
+ * 1. 合并默认配置(simulated=false, failMode=open)
69
+ * 2. 创建内存存储
70
+ * 3. 创建评估服务实例(EvaluateServiceImpl)+ MemoryStore 包装
71
+ * 4. 向宿主平台注册所有安全钩子(ingress/llm/tool/egress/persist)
72
+ * 5. 注册 RPC 方法和 CLI 命令
73
+ */
74
+ initialize(): void;
75
+ /**
76
+ * 获取评估服务实例
77
+ * 必须在 initialize() 之后调用,否则抛出异常
78
+ */
79
+ getEvaluateService(): EvaluateService;
80
+ /**
81
+ * 获取内存存储实例
82
+ * 必须在 initialize() 之后调用
83
+ */
84
+ getStore(): MemoryStore;
85
+ /**
86
+ * 获取当前运行时配置
87
+ */
88
+ getConfig(): ShieldRuntimeConfig | undefined;
89
+ /** 获取遥测服务实例 */
90
+ getTelemetryService(): TelemetryService | undefined;
91
+ /**
92
+ * 构建远端策略评估器实例
93
+ * 读取本地配置文件 → 创建 HTTP transport → 创建 RemotePolicyEvaluator
94
+ */
95
+ private buildRemoteEvaluator;
96
+ /** 重新读取本地配置并刷新运行时 */
97
+ refreshRemoteGuardConfig(): Promise<PluginRuntimeRemoteGuardRefreshResult>;
98
+ /**
99
+ * 销毁插件运行时,释放所有资源
100
+ * 在插件关闭或 gateway 关闭时调用,确保心跳上报和遥测服务被正确停止
101
+ */
102
+ dispose(): Promise<void>;
103
+ }
@@ -0,0 +1,20 @@
1
+ import type { OpenClawApi } from "../contracts";
2
+ import type { MemoryStore } from "../runtime-core/memory-store";
3
+ import type { ShieldRuntimeConfig } from "../runtime-core/evaluate-service";
4
+ /** RPC 处理器集合的依赖 */
5
+ export type RpcHandlerDeps = {
6
+ config: ShieldRuntimeConfig;
7
+ store: MemoryStore;
8
+ /** 重新读取本地 guardrail 配置并刷新运行时 */
9
+ onRemoteGuardRefresh?: () => Promise<{
10
+ ok: true;
11
+ configPath: string;
12
+ transportEnabled: boolean;
13
+ telemetryReloaded: boolean;
14
+ message: string;
15
+ }>;
16
+ };
17
+ /**
18
+ * 注册所有 RPC 方法和 CLI 命令到宿主 API
19
+ */
20
+ export declare const registerRpcAndCliHandlers: (api: OpenClawApi, deps: RpcHandlerDeps) => void;