@x-otto/hooks 0.0.1-alpha.3 → 0.0.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -25
- package/dist/index.d.ts +64 -434
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -8
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/core/hook-state.ts","../src/hook-
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/hook-spec.ts","../src/core/hook-state.ts","../src/hook-preset.ts","../src/scope.ts","../src/hook-registry.ts","../src/core/session/first-message-variant.ts","../src/core/tool-guard/label-truncator.ts","../src/core/tool-guard/output-truncation.ts","../src/core/tool-guard/tool-error-tracker.ts","../src/core/transform/thinking-validator.ts","../src/core/transform/anthropic-effort.ts","../src/core/transform/token-budget.ts","../src/core/quality/comment-checker.ts","../src/core/stream/stream-metrics.ts","../src/core/compaction/compaction-logger.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import{createLogger as e}from"@x-otto/shared";import{
|
|
2
|
-
`)}\nPlease replace with meaningful implementation comments.`;t.result={...t.result,content:[...t.result.content,{type:`text`,text:e}]},t.metadata.aiCommentsDetected=r.length}}})}function
|
|
3
|
-
`);for(let e=0;e<n.length;e++){let r=n[e]??``;for(let n of
|
|
4
|
-
`)?e.slice(0,-1):e).split(`
|
|
5
|
-
`).length:0}function he(e){let{toolCallId:t,args:n}=e,r=typeof n.path==`string`?n.path:void 0;if(!r)return null;let i=typeof n.oldContent==`string`?n.oldContent:void 0,a=typeof n.newContent==`string`?n.newContent:void 0;if(i!==void 0&&a!==void 0){let e=A(i),n=A(a);return{path:r,operation:`edit`,oldContent:i,newContent:a,addedLines:Math.max(0,n-e),removedLines:Math.max(0,e-n),timestamp:Date.now(),toolCallId:t}}let o=typeof n.content==`string`?n.content:void 0;return o===void 0?null:{path:r,operation:`write`,newContent:o,addedLines:A(o),removedLines:0,timestamp:Date.now(),toolCallId:t}}function ge(e={}){let{onFileEdited:t,getOnFileEdited:n,dedupCache:r}=e;return u({name:`edit-files-panel`,timing:`tool.execute.after`,priority:90,handle(e){let i=n?.()??t;if(!i||e.readonly)return;let a=he(e);if(!a)return;let o=a.toolCallId;if(r){if(r.has(o))return;r.add(o)}i(a)}})}const _e=[/^\/etc\//,/^\/usr\//,/^\/sys\//,/^\/proc\//,/node_modules\//,/\.git\//,/\.env$/,/\.env\.local$/,/\/\.ssh\//,/\/\.aws\//,/\/\.(bash|zsh)rc$/,/\/\.(bash_profile|zprofile|profile)$/];function ve(e){let t=[],n=``,r=!1,i=!1;for(let a=0;a<e.length;a++){let o=e[a],s=e[a+1];if(r){o===`'`&&(r=!1),n+=o;continue}if(i){o===`"`&&(i=!1),n+=o;continue}if(o===`'`){r=!0,n+=o;continue}if(o===`"`){i=!0,n+=o;continue}if(o===`&`&&s===`&`||o===`|`&&s===`|`){t.push(n),n=``,a++;continue}if(o===`;`||o===`|`||o===`
|
|
6
|
-
`||o===`&`){t.push(n),n=``;continue}n+=o}return t.push(n),t.map(e=>e.trim()).filter(Boolean)}function j(e){let t=e.trim();return t.length>=2&&(t[0]===`"`&&t[t.length-1]===`"`||t[0]===`'`&&t[t.length-1]===`'`)?t.slice(1,-1):t}function ye(e){let t=e.match(/^(?:\S*\/)?(?:bash|sh|zsh|dash|ksh|ash|python[0-9.]*|node|nodejs|perl|ruby|php|deno|bun)\b[\s\S]*?\s-[a-z]*[cer]\b\s*([\s\S]+)$/i);return t?j(t[1]):null}function be(e){let t=e.trim();for(let e=0;e<10;e++){let e=t;if(t=t.replace(/^[A-Za-z_][A-Za-z0-9_]*=(?:"[^"]*"|'[^']*'|\S*)\s+/,``),t=t.replace(/^(?:sudo|env|nohup|setsid|ionice)\b(?:\s+-\S+|\s+[A-Za-z_][A-Za-z0-9_]*=\S+)*\s+/i,``),t=t.replace(/^(?:nice|stdbuf)\b(?:\s+-\S+)*\s+/i,``),t=t.replace(/^timeout\b(?:\s+-\S+)*\s+\S+\s+/i,``),t===e)break}return t}function xe(e){let t=[];for(let n=0;n<e.length;n++)if(e[n]===`$`&&e[n+1]===`(`){let r=1,i=n+2,a=i;for(;i<e.length&&r>0;)e[i]===`(`?r++:e[i]===`)`&&r--,i++;r===0&&t.push(e.slice(a,i-1))}let n=e.match(/`([^`]+)`/g);if(n)for(let e of n)t.push(e.slice(1,-1));return t}const Se={n:`
|
|
7
|
-
`,t:` `,r:`\r`,a:`\x07`,b:`\b`,f:`\f`,v:`\v`,e:`\x1B`,E:`\x1B`,"\\":`\\`,"'":`'`,'"':`"`,"?":`?`};function M(e){return/[0-9a-fA-F]/.test(e)}function Ce(e){let t=``,n=0;for(;n<e.length;){if(e[n]!==`\\`){t+=e[n],n++;continue}let r=e[n+1];if(r===void 0){t+=`\\`;break}if(r===`x`){let r=``;for(;r.length<2&&M(e[n+2+r.length]??``);)r+=e[n+2+r.length];if(r){t+=String.fromCharCode(parseInt(r,16)),n+=2+r.length;continue}}if(r===`u`||r===`U`){let i=r===`u`?4:8,a=``;for(;a.length<i&&M(e[n+2+a.length]??``);)a+=e[n+2+a.length];if(a){t+=String.fromCodePoint(parseInt(a,16)),n+=2+a.length;continue}}if(r>=`0`&&r<=`7`){let r=``;for(;r.length<3;){let t=e[n+1+r.length]??``;if(t<`0`||t>`7`)break;r+=t}t+=String.fromCharCode(parseInt(r,8)&255),n+=1+r.length;continue}t+=Se[r]??r,n+=2}return t}function we(e){return e.replace(/\$'((?:\\.|[^'\\])*)'/g,(e,t)=>Ce(t))}function Te(e){return e.replace(/\$\{IFS[^}]*\}/g,` `).replace(/\$IFS\b(?:\$[0-9@*])?/g,` `)}function Ee(e){return Te(we(e))}function N(e){let t=new Set,n=[],r=(e,i)=>{let a=e.trim();if(!a||i>12||t.has(a))return;t.add(a),n.push(a);let o=Ee(a);o!==a&&r(o,i+1);let s=a.match(/<<<\s*('[^']*'|"[^"]*"|[^\s|;&]+)/)?.[1];s&&r(j(s),i+1);for(let e of xe(a))r(e,i+1);let c=ve(a);if(c.length>1){for(let e of c)r(e,i+1);return}let l=ye(a);l&&r(l,i+1);let u=be(a);u!==a&&r(u,i+1)};return r(e,0),n}function De(e){return e.length>=2&&(e[0]===`"`&&e.endsWith(`"`)||e[0]===`'`&&e.endsWith(`'`))?e.slice(1,-1):e}function P(e){let t=[],n=``,r=!1,i=!1;for(let a=0;a<e.length;a++){let o=e[a];if(r){o===`'`?r=!1:n+=o;continue}if(i){o===`"`?i=!1:n+=o;continue}if(o===`'`){r=!0;continue}if(o===`"`){i=!0;continue}if(o===` `||o===` `){n&&=(t.push(n),``);continue}n+=o}return n&&t.push(n),t}const F=/(?:^|[^0-9&>])(?:[0-9]*&?>>?|>\|)\s*('[^']*'|"[^"]*"|[^\s;|&<>]+)/g;function Oe(e){let t=new Set,n=e=>{if(!e)return;let n=De(e.trim());n&&!n.startsWith(`-`)&&!n.includes(`$`)&&n!==`/dev/null`&&n!==`/dev/stdout`&&n!==`/dev/stderr`&&t.add(n)};for(let t of N(e)){let e;for(F.lastIndex=0;(e=F.exec(t))!==null;)n(e[1]);let r=P(t);if(r.length===0)continue;let i=r[0].replace(/^.*\//,``),a=r.slice(1),o=a.filter(e=>!e.startsWith(`-`));if(i===`tee`)for(let e of o)n(e);else if(i===`cp`||i===`mv`||i===`install`||i===`ln`)o.length>=1&&n(o[o.length-1]);else if(i===`touch`||i===`rm`||i===`rmdir`||i===`mkdir`)for(let e of o)n(e);else if(i===`dd`)for(let e of a)e.startsWith(`of=`)&&n(e.slice(3))}return[...t]}function I(e){return{readonlyTools:new Set(e?.readonlyTools??[]),writeTools:new Set(e?.writeTools??[]),fileWriteTools:new Set(e?.fileWriteTools??[])}}function ke(e){let t=new Set,n=new Set,r=new Set;for(let i of e)if(i.name){if(i.readonly===!0){t.add(i.name);continue}if(i.name===`bash`){n.add(i.name);continue}if(i.metadata?.category===`fs`){n.add(i.name),r.add(i.name);continue}i.name.startsWith(`mcp__`)&&n.add(i.name)}return{readonlyTools:t,writeTools:n,fileWriteTools:r}}function L(e){let{fileWriteTools:t}=I(e);return{name:`builtin::file-guard`,priority:10,enabled:!0,safety:!0,scope:{agents:[`*`]},rules:[{name:`protect-system-paths`,effect:`deny`,match:{tools:[...t],paths:_e},denyReason:`Write to protected system path is not allowed`}]}}const Ae=/\b(rmdir\b|drop\s+table|git\s+push\s+(?:\S+\s+)*(?:--force\b|-f\b)|git\s+reset\s+--hard|git\s+clean\s+-[a-z]*f|truncate\s+table|dd\s+if=|mkfs\b|>\s*\/dev\/(?:sd|nvme|disk|hd))/i;function je(e){let t=P(e);return t.length===0||t[0].replace(/^.*\//,``)!==`rm`?!1:t.slice(1).some(e=>e===`--recursive`||e===`--force`?!0:/^-[a-z]*[rf]/i.test(e))}const Me=/(\beval\s|(?:curl|wget)\b[^|;&]*\|\s*(?:sudo\s+)?(?:ba|z|d)?sh\b|:\s*\(\s*\)\s*\{[^}]*:\s*\|\s*:|base64\s+(?:-d|--decode)\b[^|]*\|)/i,Ne=/\b(?:python[0-9.]*|pythonw|pypy[0-9]*|node|nodejs|deno|bun|perl|ruby|php|osascript|Rscript|pwsh|powershell(?:\.exe)?)\b[^|;&\n]*?(?:\s-{1,2}(?:c|e|r|E|p|eval|Command|EncodedCommand)\b|\s*<<)/i;function R(e){return e?N(e).some(e=>Ae.test(e)||je(e)||Me.test(e)||Ne.test(e)):!1}function Pe(e,t){return e.startsWith(`mcp__`)?Object.values(t).some(e=>typeof e==`string`&&R(e)):!1}const z={name:`builtin::destructive-guard`,priority:15,enabled:!0,safety:!0,scope:{agents:[`*`]},rules:[{name:`ask-destructive-bash`,effect:`ask`,match:{condition:e=>R(String(e.args.command??``))},askPrompt:`This command may be destructive or runs code that cannot be statically checked. Continue?`},{name:`ask-destructive-mcp-args`,effect:`ask`,match:{condition:e=>Pe(e.toolName,e.args)},askPrompt:`This MCP tool argument may run a destructive or unverifiable command. Continue?`}]},Fe=[/\/\.ssh(\/|$)/,/\/\.aws(\/|$)/,/\/\.gnupg(\/|$)/,/\/otto\/auth\.json$/],Ie=[/\/\.env(\.[\w.-]+)?$/],B=[/\.ssh(\/|\s|$)/,/\.aws(\/|\s|$)/,/\.gnupg(\/|\s|$)/,/otto\/auth\.json/],V=[/\.env(\b|\.[\w-]+)/];function H(e,t){return e?N(e).some(e=>t.some(t=>t.test(e))):!1}function U(e,t,n){if(!e.startsWith(`mcp__`))return!1;for(let e of Object.values(t))if(typeof e==`string`&&H(e,n))return!0;return!1}const W={name:`builtin::credential-guard`,priority:16,enabled:!0,safety:!0,scope:{agents:[`*`]},rules:[{name:`deny-credential-paths`,effect:`deny`,match:{paths:Fe},denyReason:`Reading credential/secret files (~/.ssh, ~/.aws, ~/.gnupg, auth store) is not allowed`},{name:`deny-credential-command`,effect:`deny`,match:{condition:e=>H(String(e.args.command??``),B)},denyReason:`Command references credential/secret paths (~/.ssh, ~/.aws, ~/.gnupg, auth store)`},{name:`ask-dotenv-paths`,effect:`ask`,match:{paths:Ie},askPrompt:`This reads a .env file which may contain secrets. Continue?`},{name:`ask-dotenv-command`,effect:`ask`,match:{condition:e=>H(String(e.args.command??``),V)},askPrompt:`This command references a .env file which may contain secrets. Continue?`},{name:`deny-mcp-credential-args`,effect:`deny`,match:{condition:e=>U(e.toolName,e.args,B)},denyReason:`MCP tool argument references credential/secret paths (~/.ssh, ~/.aws, ~/.gnupg, auth store)`},{name:`ask-mcp-dotenv-args`,effect:`ask`,match:{condition:e=>U(e.toolName,e.args,V)},askPrompt:`This MCP tool reads a .env file which may contain secrets. Continue?`}]},Le=[/\.otto\/plugin-trust\.json$/,/\.otto\/claude-trust\.json$/,/\.otto\/config\.json$/,/\.otto\/schedules\//,/\.otto\/plugins\//,/\.otto\/timers\//],Re=[/\.otto\/sessions\//,/\.otto\/.*\.lock$/],ze=[/\.otto\/AGENTS\.md$/,/\.otto\/MEMORY\.md$/,/\.otto\/memory\//,/\.otto\/local\/MEMORY\.md$/,/\.otto\/local\/memory\//,/\.otto\/lessons\.json$/,/\.otto\/local\/lessons\.json$/],Be=[/\.otto\/(plugin-trust|claude-trust|config)\.json/,/\.otto\/(schedules|plugins|timers|sessions)\//,/\.otto\/.*\.lock\b/,/\.otto\/AGENTS\.md/,/\.otto\/(local\/)?MEMORY\.md/,/\.otto\/(local\/)?memory\//,/\.otto\/(local\/)?lessons\.json/];function G(e){let{fileWriteTools:t}=I(e),n=[...t];return{name:`builtin::otto-state-domain-guard`,priority:17,enabled:!0,safety:!0,scope:{agents:[`*`]},rules:[{name:`deny-otto-trust-and-execution-state`,effect:`deny`,match:{tools:n,paths:Le},denyReason:`Writing to otto trust/config/schedule/plugin state is not allowed via file tools — use the dedicated command (/extension, schedule_create, settings) instead`},{name:`deny-otto-runtime-state`,effect:`deny`,match:{tools:n,paths:Re},denyReason:`otto runtime state (sessions/locks) must not be modified directly`},{name:`deny-otto-memory-state`,effect:`deny`,match:{tools:n,paths:ze},denyReason:`Cross-session memory must be written via the memory_record tool (not edit/write) — use scope: "workspace" | "local" | "project-shared"`},{name:`deny-otto-state-command-reference`,effect:`deny`,match:{condition:e=>H(String(e.args.command??``),Be)},denyReason:`Command references otto trust/config/schedule/plugin/session state — not allowed via bash`}]}}function Ve(e){try{return new URL(e).hostname.toLowerCase()}catch{return null}}function He(e,t){return t.some(t=>{let n=t.trim().toLowerCase().replace(/^\*?\./,``);return n.length>0&&(e===n||e.endsWith(`.${n}`))})}function Ue(e){let t=e.map(e=>e.trim().toLowerCase()).filter(Boolean);return{name:`setting::egress-allowlist`,priority:8,enabled:!0,safety:!0,scope:{agents:[`*`]},rules:[{name:`ask-egress-offlist`,effect:`ask`,match:{condition:e=>{let n=e.args.url;if(typeof n!=`string`)return!1;let r=Ve(n);return r?!He(r,t):!1}},askPrompt:`This requests a host not in the egress allowlist. Continue?`}]}}function We(e){return{name:`setting::disabled-tools`,priority:2,enabled:!0,scope:{agents:[`*`]},rules:[{name:`deny-disabled`,effect:`deny`,match:{tools:e},denyReason:`Tool disabled by configuration`}]}}function K(e,t){let{readonlyTools:n,writeTools:r}=I(t);switch(e){case`bypass`:return{name:`mode::bypass`,priority:1,enabled:!0,scope:{agents:[`*`]},rules:[{name:`allow-all`,effect:`allow`,match:{}}]};case`readonly`:return{name:`mode::readonly`,priority:1,enabled:!0,scope:{agents:[`*`]},rules:[{name:`allow-reads`,effect:`allow`,match:{condition:e=>n.has(e.toolName)}},{name:`deny-writes`,effect:`deny`,match:{},fallthrough:!0,denyReason:`Read-only mode: write operations are not allowed`}]};case`confirm`:return{name:`mode::confirm`,priority:1,enabled:!0,scope:{agents:[`*`]},rules:[{name:`allow-reads`,effect:`allow`,match:{condition:e=>n.has(e.toolName)}},{name:`ask-writes`,effect:`ask`,match:{tools:[...r]},askPrompt:`Confirm write operation?`}]};case`strict`:return{name:`mode::strict`,priority:9999,enabled:!0,scope:{agents:[`*`]},rules:[{name:`deny-unmatched`,effect:`deny`,match:{},fallthrough:!0,denyReason:`Strict mode: no matching permission policy`}]};default:return{name:`mode::auto`,priority:1,enabled:!0,scope:{agents:[`*`]},rules:[{name:`allow-reads`,effect:`allow`,match:{condition:e=>n.has(e.toolName)}},{name:`auto-writes`,effect:`allow`,match:{tools:[...r]}}]}}}function Ge(e,t={}){e.register(K(`auto`,t.toolSets)),e.register(L(t.toolSets)),e.register(z),e.register(W),e.register(G(t.toolSets))}function Ke(e){let t=e.trim(),n=/^([^()\s]+)\((.*)\)$/.exec(t);return n?{toolName:n[1]??t,content:(n[2]??``).trim()}:{toolName:t}}function qe(e){let t=e.trim();if(!t)return!1;let n=/^([^()\s]+)\((.*)\)$/.exec(t);if(n){let e=(n[1]??``).trim(),t=(n[2]??``).trim();return e.length>0&&t.length>0}return/^[^()\s]+$/.test(t)}function Je(e,t){let n=e.trim(),r=t.trim();return r?n===r||n.startsWith(r+` `):!1}const Ye=/^cd\s+\S+$/;function Xe(e,t){let n=t.filter(Boolean);if(n.length===0)return!1;let r=N(e);return r.length===0?!1:r.every(e=>{if(Ye.test(e))return!0;let t=e.replace(/^cd\s+\S+\s*&&\s*/,``).trim();return n.some(e=>Je(t,e))})}function Ze(e,t){let n=[];for(let r of t){let t=Ke(r);t.toolName===e.toolName&&n.push(t)}if(n.length===0)return!1;if(n.some(e=>e.content===void 0))return!0;if(e.toolName===`bash`){let t=typeof e.args?.command==`string`?e.args.command:``;return t?Xe(t,n.map(e=>e.content)):!1}return!1}function Qe(e){let t=e.trim().replace(/^cd\s+\S+\s*&&\s*/,``).trim();if(/[;&|]|`|\$\(|<<|\beval\b/.test(t))return null;let n=t.split(/\s+/).filter(Boolean),r=n[0];if(!r||r.includes(`=`))return null;let i=n[1]&&!n[1].startsWith(`-`)?n[1]:null;return i?`${r} ${i}`:r}const q=e(`@x-otto/hooks:permission`);function J(e,t){return!e.agents||e.agents.length===0?!0:e.agents.includes(`*`)||e.agents.includes(t)}function $e(e,t){return!e.sessions||e.sessions.length===0?!0:e.sessions.includes(`*`)||e.sessions.includes(t)}function et(e,t){return J(e,t.agentName)&&$e(e,t.sessionId)}function tt(e,t){return!(e.tools&&e.tools.length>0&&!e.tools.includes(t.toolName)||e.paths&&e.paths.length>0&&(!t.filePath||!e.paths.some(e=>e.test(t.filePath)))||e.condition&&!e.condition(t))}const nt={effect:`allow`,policyName:`__default__`,ruleName:`__fallthrough__`,timestamp:0,evaluatedPolicies:0};var Y=class{policies=[];alwaysAllow=new Set;sessionAlwaysAllow=new Map;setAlwaysAllow(e){this.alwaysAllow=new Set(e)}addAlwaysAllow(e){this.alwaysAllow.add(e)}getAlwaysAllow(){return[...this.alwaysAllow]}setSessionAlwaysAllow(e,t){if(e){if(t.length===0){this.sessionAlwaysAllow.delete(e);return}this.sessionAlwaysAllow.set(e,new Set(t))}}addSessionAlwaysAllow(e,t){if(!e||!t)return;let n=this.sessionAlwaysAllow.get(e)??new Set;n.add(t),this.sessionAlwaysAllow.set(e,n)}getSessionAlwaysAllow(e){return[...this.sessionAlwaysAllow.get(e)??[]]}clearSessionAlwaysAllow(e){this.sessionAlwaysAllow.delete(e)}register(e){this.unregister(e.name),this.policies.push(e),this.policies.sort((e,t)=>e.priority-t.priority),q.debug({name:e.name,priority:e.priority,rules:e.rules.length},`Permission policy registered`)}registerAll(e){for(let t of e)this.register(t)}unregister(e){let t=this.policies.findIndex(t=>t.name===e);return t>=0?(this.policies.splice(t,1),q.debug({name:e},`Permission policy unregistered`),!0):!1}has(e){return this.policies.some(t=>t.name===e)}evaluate(e){let t=0,n=[];for(let r of this.policies)if(r.enabled&&et(r.scope,e)){t++;for(let t of r.rules)tt(t.match,e)&&n.push({effect:t.effect,policyName:r.name,ruleName:t.name,priority:r.priority,reason:t.effect===`deny`?t.denyReason:t.askPrompt,safety:r.safety===!0,fallthrough:t.fallthrough===!0})}let r=n.some(e=>e.policyName===`mode::auto`||e.policyName===`mode::bypass`),i=(e,n)=>{let r=n.reduce((e,t)=>t.priority<e.priority?t:e);return{effect:e,policyName:r.policyName,ruleName:r.ruleName,reason:r.reason,timestamp:Date.now(),evaluatedPolicies:t,safety:r.safety}},a=n=>{if(n.length===0)return null;let a=n.filter(e=>e.effect===`deny`);if(a.length>0)return i(`deny`,a);let o=n.filter(e=>e.effect===`ask`);if(o.length>0){if(r)return{effect:`allow`,policyName:`mode::auto-override`,ruleName:o[0].ruleName,timestamp:Date.now(),evaluatedPolicies:t};let n=Ze(e,[...this.alwaysAllow,...this.sessionAlwaysAllow.get(e.sessionId)??[]]),a=o.filter(e=>e.safety||!n);return a.length===0?{effect:`allow`,policyName:`user::always-allow`,ruleName:e.toolName,timestamp:Date.now(),evaluatedPolicies:t}:i(`ask`,a)}let s=n.filter(e=>e.effect===`allow`);return s.length>0?i(`allow`,s):null},o=n.filter(e=>!e.fallthrough),s=n.filter(e=>e.fallthrough),c=a(o),l=a(s);return c?.effect===`deny`||c?.effect===`allow`?c:l?.effect===`deny`?l:(c??l)||{...nt,timestamp:Date.now(),evaluatedPolicies:t}}getEffective(e){return this.policies.filter(t=>t.enabled&&J(t.scope,e))}getAll(){return[...this.policies]}clear(){this.policies=[],q.debug(`All permission policies cleared`)}};function X(e={}){let t=new Y;return Ge(t,e),t}const Z=1024,rt=/\([^)]*(?:[*+]|\{\d+,\d*\})[^)]*\)(?:[*+]|\{\d+,\d*\})/;function it(e,t){if(e.length>Z)return q.warn({rule:t,length:e.length,max:Z},`Permission path pattern exceeds max length — rule skipped (ReDoS guard)`),null;if(rt.test(e))return q.warn({rule:t,pattern:e},`Permission path pattern has nested quantifiers (catastrophic backtracking risk) — rule skipped (ReDoS guard)`),null;try{return new RegExp(e)}catch(n){return q.warn({rule:t,pattern:e,error:n instanceof Error?n.message:String(n)},`Permission path pattern failed to compile — rule skipped`),null}}function at(e){let t=[];for(let n of e.rules){let e;if(n.paths&&n.paths.length>0){let t=[],r=!1;for(let e of n.paths){let i=it(e,n.name);if(i===null){r=!0;break}t.push(i)}if(r)continue;e=t}t.push({name:n.name,effect:n.effect,match:{tools:n.tools,paths:e},denyReason:n.deny_reason,askPrompt:n.ask_prompt})}return{name:e.name,priority:e.priority??50,enabled:e.enabled??!0,scope:{agents:e.scope?.agents,sessions:e.scope?.sessions},rules:t}}var Q=class{entries=[];maxEntries;constructor(e=1e3){this.maxEntries=e}record(e,t){this.entries.push({timestamp:t.timestamp,sessionId:e.sessionId,agentName:e.agentName,toolName:e.toolName,filePath:e.filePath,decision:t}),this.entries.length>this.maxEntries&&this.entries.splice(0,this.entries.length-this.maxEntries)}getEntries(e){let t=this.entries;return e?.sessionId&&(t=t.filter(t=>t.sessionId===e.sessionId)),e?.effect&&(t=t.filter(t=>t.decision.effect===e.effect)),t===this.entries?this.entries.slice():t}getDenyCount(e){return this.getEntries({sessionId:e,effect:`deny`}).length}getAskCount(e){return this.getEntries({sessionId:e,effect:`ask`}).length}clear(e){e?this.entries=this.entries.filter(t=>t.sessionId!==e):this.entries=[]}get size(){return this.entries.length}};function ot(e){return e.safety?`high`:`medium`}function st(e){let t=[];for(let r of e)if(t.push(r),typeof r==`string`){let e=n(r);e!==r&&t.push(e)}return t}var $=class{name=`permission-guard`;timing=`tool.execute.before`;priority=5;enabled=!0;registry;auditLog;constructor(e,t){this.registry=e,this.auditLog=t}handle=(e,t)=>{let n={timing:`tool.execute.before`,toolName:e.toolName,args:e.args,agentName:e.agentName,sessionId:e.sessionId,metadata:{}},r=st(e.paths&&e.paths.length>0?e.paths:[void 0]),i=this.registry.evaluate({...n,filePath:r[0]}),a=r[0];for(let e=1;e<r.length;e++){let t=this.registry.evaluate({...n,filePath:r[e]});(t.effect===`deny`||t.effect===`ask`&&i.effect===`allow`)&&(i=t,a=r[e])}let o={...n,filePath:a};if(this.auditLog?.record(o,i),i.effect===`deny`){t.cancelled=!0,t.decision=`deny`,t.decisionReason=i.reason??i.ruleName,t.cancelReason=`Permission denied: ${t.decisionReason}`;return}i.effect===`ask`?(t.decision=`ask`,t.decisionReason=i.reason??`Requires confirmation`,t.risk=ot(i)):t.decision!==`ask`&&t.decision!==`deny`&&(t.decision=`allow`)}};function ct(e,t){let n=new $(e,t);return u({name:n.name,timing:n.timing,priority:n.priority,enabled:n.enabled,handle:n.handle})}const lt={fileWriteTools:[`write`,`edit`,`edit-diff`]};function ut(e){if(e.has(`permission-guard`))return;let t=X({toolSets:lt});return e.register(ct(t,new Q)),t}function dt(e){try{return a(e)}catch{return}}function ft(e){let t=new Set;for(let r of e){let e=n(r);t.add(e);let i=dt(e);i&&i!==e&&t.add(i)}return[...t]}function pt(e,t){return e===t||e.startsWith(t.endsWith(`/`)?t:`${t}/`)}function mt(e,t){let n=ft(t);return ft([e]).some(e=>n.some(t=>pt(e,t)))}function ht(e,t){let n=e.writePaths??[],r=e.network===!0;for(let e of n)if(mt(e,t.deniedRoots))return{verdict:`deny`,reason:`write to protected path "${e}" is never allowed (escape guard)`};if(t.mode===`bypass`||n.length===0&&!r)return{verdict:`auto-allow`};let i=n.every(e=>mt(e,t.writableRoots)),a=!r||t.networkAllowed;return i&&a?{verdict:`auto-allow`}:t.mode===`strict`||t.mode===`readonly`?{verdict:`deny`,reason:`${i?`network access`:`write outside the sandbox writable roots`} is not permitted in ${t.mode} mode`}:{verdict:`ask`,reason:`${i?`network access`:`write outside the sandbox writable roots`} requires approval (will run with sandbox bypassed for this operation)`}}function gt(e,t){return[n(e,`.otto`),n(t,`.otto`)]}function _t(e,t,r,i=[]){let a=[n(t),n(r),`/tmp`];if(e!==`strict`&&e!==`readonly`)for(let e of i)a.push(n(e));return[...new Set(a)]}var vt=class{signals=new Map;clock;constructor(e=()=>Date.now()){this.clock=e}record(e,t,n){let r=`${e}:${t}`,i=this.clock(),a=this.signals.get(r);a?(a.count++,a.lastSeen=i,n&&(a.detail=n)):this.signals.set(r,{kind:e,signature:t,count:1,firstSeen:i,lastSeen:i,detail:n})}recurring(e=3){return[...this.signals.values()].filter(t=>t.count>=e).sort((e,t)=>t.count-e.count)}all(){return[...this.signals.values()].sort((e,t)=>t.count-e.count)}clear(){this.signals.clear()}};function yt(e){let t=e.content;if(!Array.isArray(t))return;let n=t.map(e=>e&&typeof e==`object`&&`text`in e?String(e.text??``):``).join(` `).trim();return n?n.split(`
|
|
8
|
-
`)[0].slice(0,120):void 0}function bt(e){return u({name:`failure-signal-recorder`,timing:`tool.execute.after`,priority:14,handle(t){t.result?.isError&&e.record(`tool-error`,t.toolName,yt(t.result))}})}function xt(e){return u({name:`failure-signal-permission-deny`,timing:`tool.execute.before`,priority:90,handle(t,n){n?.decision===`deny`&&e.record(`permission-denied`,t.toolName,n.decisionReason)}})}var St=class{proposed=new Set;minCount;clock;constructor(e,t={}){this.signals=e,this.minCount=t.minCount??3,this.clock=t.clock??(()=>Date.now())}propose(){let e=[];for(let t of this.signals.recurring(this.minCount)){let n=`${t.kind}:${t.signature}`;this.proposed.has(n)||(this.proposed.add(n),e.push({id:n,signature:t.signature,kind:`investigate-fix`,directive:Ct(t),evidence:{signalKind:t.kind,count:t.count,detail:t.detail},createdAt:this.clock()}))}return e}reset(){this.proposed.clear()}};function Ct(e){return e.kind===`tool-error`?`工具「${e.signature}」反复失败(${e.count} 次${e.detail?`,如「${e.detail}」`:``})。调查根因并修复:定位失败路径、补健壮性或修 bug,跑测试验证。`:`反复出现「${e.kind}: ${e.signature}」(${e.count} 次)。调查并消除。`}export{lt as BASELINE_PERMISSION_TOOL_SETS,W as CREDENTIAL_READ_POLICY,z as DESTRUCTIVE_COMMAND_POLICY,St as Diagnoser,vt as FailureSignalStore,D as HookRegistry,w as HookState,Q as PermissionAuditLog,$ as PermissionGuardHook,Y as PermissionPolicyRegistry,r as WATERFALL_TIMINGS,ht as assessSandboxSafety,Qe as bashAlwaysAllowPrefix,at as compilePolicyFromSetting,_t as computeSandboxRoots,te as createAnthropicEffortHook,h as createCommentCheckerHook,x as createCompactionAfterHook,b as createCompactionLoggerHook,We as createDisabledToolsPolicy,ge as createEditFilesPanelHook,Ue as createEgressAllowlistPolicy,bt as createFailureSignalHook,L as createFileGuardPolicy,m as createFirstMessageVariantHook,me as createHookRegistry,f as createLabelTruncatorHook,G as createOttoStateDomainPolicy,d as createOutputTruncationHook,xt as createPermissionDenyHook,ct as createPermissionGuardHook,K as createPermissionModePolicy,X as createPermissionRegistry,ke as createPermissionToolSetsFromRegistry,v as createStreamEndMetricsHook,_ as createStreamMetricsHook,p as createThinkingValidatorHook,C as createTokenBudgetHook,S as createToolErrorTrackerHook,gt as defaultDeniedRoots,u as defineHook,Oe as extractWriteTargets,i as isWaterfallTiming,qe as isWellFormedAllowEntry,ut as provisionBaselineSecurity};
|
|
1
|
+
import{createLogger as e}from"@x-otto/shared";import{z as t}from"zod";import{normalizeEnv as n}from"@x-otto/env";import{WATERFALL_TIMINGS as r,isWaterfallTiming as i}from"@x-otto/hook-contracts";const a=t.void(),o=t.object({message:t.unknown(),cancelled:t.boolean(),metadata:t.record(t.string(),t.unknown())}),s=t.object({args:t.record(t.string(),t.unknown()),displayArgs:t.record(t.string(),t.unknown()).optional(),cancelled:t.boolean(),cancelReason:t.string().optional(),decision:t.enum([`allow`,`deny`,`ask`]).optional(),decisionReason:t.string().optional(),risk:t.enum([`low`,`medium`,`high`]).optional()}),c=t.object({result:t.unknown(),metadata:t.record(t.string(),t.unknown())}),ee=t.object({messages:t.array(t.unknown())}),te=t.object({temperature:t.number().optional(),maxTokens:t.number().optional(),thinkingLevel:t.string().optional(),metadata:t.record(t.string(),t.unknown())}),l=t.object({systemPrompt:t.string(),systemTail:t.array(t.string()).optional()}),u=t.object({cancelled:t.boolean()}),d={"chat.message.before":o,"chat.message.after":o,"tool.execute.before":s,"tool.execute.after":c,"messages.transform":ee,"chat.params":te,"system.prompt.transform":l,"session.created":a,"session.restored":a,"session.deleted":a,"session.idle":a,"session.error":a,"stream.start":a,"stream.end":a,"compaction.before":a,"compaction.after":a,"memory.prune.before":u,"process.spawned":a,"process.exited":a,"task.created":a,"task.started":a,"task.completed":a,"task.failed":a,"task.cancelled":a,notification:a};function f(e){return e.def?.type===`void`}function p(e,t){if(f(e))return!1;let n=e.def?.shape;return n!==void 0&&t in n}const m=Object.keys(d);function h(e){return f(d[e])?`observer`:`interceptor`}const g=new Set(m.filter(e=>h(e)===`interceptor`));new Set(m.filter(e=>h(e)===`observer`));const _=new Set(m.filter(e=>h(e)===`interceptor`&&p(d[e],`cancelled`))),ne=new Set(m.filter(e=>h(e)===`interceptor`&&p(d[e],`decision`)));function re(){return Object.fromEntries(m.map(e=>[e,[]]))}const v=m,y=g;function b(){return re()}function x(e){let t={name:e.name,timing:e.timing,priority:e.priority??50,enabled:e.enabled??!0};return y.has(e.timing)?{...t,kind:`interceptor`,handle:e.handle}:{...t,kind:`observer`,handle:e.handle}}function S(e,t){if(e!==`session.error`)return t;let n=t;return{sessionId:n.sessionId,metadata:n.metadata,error:n.error instanceof Error?n.error.message:String(n.error)}}const C=e(`@x-otto/hooks`),w=new Set([`tool.execute.before`]),T=n(process.env.OTTO_HOOK_TIMEOUT_MS,5e3),E=_,D=ne;function ie(e){return typeof e==`object`&&!!e&&e.abort===!0}async function ae(e,t,n,r,i){let a;try{return{ok:!0,abort:ie(await Promise.race([e.handle(n,r),new Promise((t,n)=>{a=setTimeout(()=>{n(Error(`hook "${e.name}" timed out after ${T}ms`))},T),a.unref?.()})]))}}catch(n){let r=n instanceof Error?n.message:String(n),a=r.includes(`timed out after`);return C.error({name:e.name,timing:t,err:r,timedOut:a},`Hook failed or timed out`),a&&i?.({hookName:e.name,timing:t,timeoutMs:T}),{ok:!1,timedOut:a,message:r}}finally{a!==void 0&&clearTimeout(a)}}async function oe(e,t,n){let r;try{await Promise.race([e.handle(n),new Promise((t,n)=>{r=setTimeout(()=>{n(Error(`hook "${e.name}" timed out after ${T}ms`))},T),r.unref?.()})])}catch(n){let r=n instanceof Error?n.message:String(n);C.error({name:e.name,timing:t,err:r},`Hook failed or timed out`)}finally{r!==void 0&&clearTimeout(r)}}async function se(e,t,n,r,i){if(t.length===0)return;let a=!1,o=!1;for(let s of t){let t=await ae(s,e,n,r,i);if(!t.ok&&w.has(e)){let e=r;typeof e.cancelled==`boolean`&&(e.cancelled=!0,e.cancelReason=t.timedOut?`hook "${s.name}" timed out after ${T}ms`:`hook "${s.name}" failed: ${t.message}`)}if(E.has(e)){let e=r;e.cancelled===!0&&(a=!0),a&&(e.cancelled=!0)}if(D.has(e)){let e=r;e.decision===`deny`&&(o=!0),o&&(e.decision=`deny`)}if(t.ok&&t.abort)break}}async function ce(e,t,n){if(t.length===0)return;let r=S(e,n);for(let n of t)await oe(n,e,r)}function O(e=61440){return x({name:`output-truncation`,timing:`tool.execute.after`,priority:10,handle(t,n){let{content:r}=n.result,i=0;for(let e of r)e.type===`text`&&(i+=e.text.length);if(i<=e)return;let a=e,o=r.map(t=>{if(t.type!==`text`)return t;if(a<=0)return{type:`text`,text:``};if(t.text.length<=a)return a-=t.text.length,t;let n=t.text.slice(0,a);return a=0,{type:`text`,text:`${n}\n\n... [output truncated: ${i} chars → ${e} chars]`}}).filter(e=>e.type!==`text`||e.text.length>0);n.result={...n.result,content:o},n.metadata.truncated=!0,n.metadata.originalSize=i}})}function k(){return x({name:`label-truncator`,timing:`tool.execute.before`,priority:20,handle(e,t){t.displayArgs||={...t.args};for(let e of[`label`,`description`,`title`]){let n=t.displayArgs[e];typeof n==`string`&&n.length>200&&(t.displayArgs[e]=`${n.slice(0,200)}...`)}}})}function A(){return x({name:`thinking-validator`,timing:`messages.transform`,priority:20,handle(e,t){t.messages=t.messages.map(e=>{if(!j(e))return e;let t=e.content;if(!Array.isArray(t))return e;let n=t.filter(e=>!(e.type===`thinking`&&(typeof e.thinking!=`string`||e.thinking.trim()===``)));return{...e,content:n}})}})}function j(e){return typeof e==`object`&&!!e&&`role`in e&&e.role===`assistant`}function M(){return x({name:`anthropic-effort`,timing:`chat.params`,priority:10,handle(e,t){e.provider===`anthropic`&&(t.thinkingLevel||(e.model.includes(`opus`)?t.thinkingLevel=`high`:e.model.includes(`sonnet`)?t.thinkingLevel=`medium`:e.model.includes(`haiku`)&&(t.thinkingLevel=`low`)))}})}function N(){return x({name:`first-message-variant`,timing:`chat.message.before`,priority:10,handle(e,t){e.isFirstMessage&&(t.metadata.isFirstMessage=!0,t.metadata.variant=`first-message`)}})}const P=[/\/\/\s*TODO:\s*implement/i,/\/\/\s*add\s+(your|the)\s+/i,/\/\/\s*placeholder/i,/\/\/\s*\.\.\.\s*(rest|more|other|remaining)/i,/\/\/\s*handle\s+error\s+here/i,/\/\*\s*\*\//];function F(){return x({name:`comment-checker`,timing:`tool.execute.after`,priority:20,handle(e,t){if(e.readonly)return;let n=L(e.args);if(!n)return;let r=I(n);if(r.length>0){let e=`\n\n⚠️ AI-style comments detected:\n${r.map(e=>` - Line ${e.line}: "${e.text}"`).join(`
|
|
2
|
+
`)}\nPlease replace with meaningful implementation comments.`;t.result={...t.result,content:[...t.result.content,{type:`text`,text:e}]},t.metadata.aiCommentsDetected=r.length}}})}function I(e){let t=[],n=e.split(`
|
|
3
|
+
`);for(let e=0;e<n.length;e++){let r=n[e]??``;for(let n of P)if(n.test(r)){t.push({line:e+1,text:r.trim()});break}}return t}function L(e){return typeof e.content==`string`?e.content:typeof e.newString==`string`?e.newString:typeof e.new_string==`string`?e.new_string:null}const R=e(`@x-otto/hooks:stream-metrics`);function z(e){return x({name:`stream-metrics`,timing:`stream.start`,priority:10,handle(t){let n=e.streamMetrics;n.set(t.sessionId,{startTime:Date.now(),model:t.model,requestCount:(n.get(t.sessionId)?.requestCount??0)+1,lastStopReason:``,totalDurationMs:n.get(t.sessionId)?.totalDurationMs??0}),R.debug(`Stream started: session=%s model=%s`,t.sessionId,t.model)}})}function B(e){return x({name:`stream-end-metrics`,timing:`stream.end`,priority:10,handle(t){let n=e.streamMetrics.get(t.sessionId);if(n){let e=Date.now()-n.startTime;n.totalDurationMs+=e,n.lastStopReason=t.stopReason,R.debug(`Stream ended: session=%s model=%s duration=%dms stopReason=%s total=%dms`,t.sessionId,t.model,e,t.stopReason,n.totalDurationMs)}}})}const V=e(`@x-otto/hooks:compaction`);function H(e){return x({name:`compaction-logger`,timing:`compaction.before`,priority:10,handle(t){let n=e.compactionStats.get(t.sessionId)??{compactionCount:0,totalCompacted:0,lastCompactionTime:0};n.compactionCount++,n.lastCompactionTime=Date.now(),e.compactionStats.set(t.sessionId,n),V.info(`Compaction started: session=%s messageCount=%d compaction#%d`,t.sessionId,t.messageCount,n.compactionCount)}})}function U(e){return x({name:`compaction-after-logger`,timing:`compaction.after`,priority:10,handle(t){let n=e.compactionStats.get(t.sessionId);if(n){let e=Date.now()-n.lastCompactionTime;n.totalCompacted+=t.retainedCount,V.info(`Compaction finished: session=%s retained=%d duration=%dms totalCompactions=%d`,t.sessionId,t.retainedCount,e,n.compactionCount)}}})}const W=e(`@x-otto/hooks:tool-error-tracker`);function G(e,t){let n=t?.circuitBreakerThreshold??5,r=t?.decayWindowMs??12e4;return x({name:`tool-error-tracker`,timing:`tool.execute.after`,priority:15,handle(t,i){let a=e.toolErrors.get(t.sessionId)??new Map;e.toolErrors.set(t.sessionId,a);let o=a.get(t.toolName);if(o&&Date.now()-o.lastErrorTime>r&&(o=void 0),t.result.isError){if(o||={toolName:t.toolName,errorCount:0,lastErrorTime:0,consecutiveErrors:0},o.errorCount++,o.consecutiveErrors++,o.lastErrorTime=Date.now(),a.set(t.toolName,o),o.consecutiveErrors>=n){let e=`Tool "${t.toolName}" hit circuit-breaker: ${o.consecutiveErrors} consecutive errors`;W.warn(e),i.metadata.toolCircuitBreaker={toolName:t.toolName,consecutiveErrors:o.consecutiveErrors,tripped:!0}}}else o&&(o.consecutiveErrors=0,a.set(t.toolName,o))}})}const le=e(`@x-otto/hooks:token-budget`);function K(e){let t=e?.maxOutputTokens??16384;return x({name:`token-budget`,timing:`chat.params`,priority:20,handle(e,n){(!n.maxTokens||n.maxTokens>t)&&(n.maxTokens=t,le.debug(`Capped maxTokens to %d for model=%s`,t,e.model))}})}function ue(e,t){switch(e){case`default`:return q(t);case`strict`:return J(t);case`minimal`:return de();default:return[]}}function q(e){return[O(),k(),A(),M(),N(),z(e),B(e),H(e),U(e),G(e),K()]}function J(e){return[...q(e),F()]}function de(){return[O()]}var Y=class{streamMetrics=new Map;compactionStats=new Map;toolErrors=new Map;clearSession(e){this.streamMetrics.delete(e),this.compactionStats.delete(e),this.toolErrors.delete(e)}},X=class{tokens=[];constructor(e,t){this.registry=e,this.name=t}register(e){let t={...e,name:`${this.name}:${e.name}`},n=this.registry.register(t);return this.tokens.push(n),n}dispose(){for(let e of this.tokens)e.dispose();this.tokens.length=0}};const Z=e(`@x-otto/hooks`);var Q=class{hooks=b();disabled=new Set;onHookTimeout;state=new Y;constructor(e){this.onHookTimeout=e?.onHookTimeout,this.register(x({name:`hook-state-cleanup`,timing:`session.deleted`,priority:90,handle:e=>{this.state.clearSession(e.sessionId)}})),e?.preset&&e.preset!==`none`&&this.applyPreset(e.preset)}register(e){return this.hooks[e.timing].push(e),Z.debug({hookName:e.name,timing:e.timing,priority:e.priority},`Hook registered`),{dispose:()=>{let t=this.hooks[e.timing],n=t.filter(t=>t!==e);n.length<t.length&&(this.hooks[e.timing]=n,Z.debug({hookName:e.name,timing:e.timing},`Hook token disposed`))}}}registerAll(e){let t=[];for(let n of e)t.push(this.register(n));return t}createScope(e){return new X(this,e)}on(e,t,n,r=50){return this.register(x({name:t,timing:e,handle:n,priority:r})),this}has(e){return this.getRegistered().some(t=>t.name===e)}unregister(e){let t=!1;for(let n of v){let r=this.hooks[n],i=r.filter(t=>t.name!==e);i.length<r.length&&(this.hooks[n]=i,t=!0,Z.debug({hookName:e,timing:n},`Hook unregistered`))}return t}disable(e){this.disabled.add(e),Z.debug({hookName:e},`Hook disabled`)}enable(e){this.disabled.delete(e),Z.debug({hookName:e},`Hook enabled`)}getRegistered(e){if(e)return this.hooks[e].map($);let t=[];for(let e of v)t.push(...this.hooks[e].map($));return t}async execute(e,t,n){await se(e,this.getSortedInterceptorHooks(e),t,n,this.onHookTimeout)}async emit(e,t){await ce(e,this.getSortedObserverHooks(e),t)}clear(){for(let e of v)this.hooks[e]=[];this.disabled.clear(),Z.debug(`All hooks cleared`)}getSortedInterceptorHooks(e){let t=this.hooks[e];return t?t.filter(e=>e.kind===`interceptor`&&e.enabled&&!this.disabled.has(e.name)).sort((e,t)=>e.priority-t.priority):(Z.warn({timing:e},`Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?`),[])}getSortedObserverHooks(e){let t=this.hooks[e];return t?t.filter(e=>e.kind===`observer`&&e.enabled&&!this.disabled.has(e.name)).sort((e,t)=>e.priority-t.priority):(Z.warn({timing:e},`Unknown hook timing (no bucket) — emit name mismatch with HOOK_NODES?`),[])}applyPreset(e){let t=ue(e,this.state);for(let e of t)this.register(e)}};function $(e){return{name:e.name,timing:e.timing,priority:e.priority,enabled:e.enabled}}function fe(e){return new Q(e)}export{Q as HookRegistry,X as HookScope,Y as HookState,r as WATERFALL_TIMINGS,M as createAnthropicEffortHook,F as createCommentCheckerHook,U as createCompactionAfterHook,H as createCompactionLoggerHook,N as createFirstMessageVariantHook,fe as createHookRegistry,k as createLabelTruncatorHook,O as createOutputTruncationHook,B as createStreamEndMetricsHook,z as createStreamMetricsHook,A as createThinkingValidatorHook,K as createTokenBudgetHook,G as createToolErrorTrackerHook,x as defineHook,i as isWaterfallTiming};
|
|
9
4
|
//# sourceMappingURL=index.js.map
|