{"_id":"@alexzhaosheng/huko-engine","_rev":"6-f009ea998f4fd7da1cdde2182ea0f076","name":"@alexzhaosheng/huko-engine","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alexzhaosheng/huko-engine","version":"0.1.0","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"license":"MIT","_id":"@alexzhaosheng/huko-engine@0.1.0","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"homepage":"https://github.com/alexzhaosheng/huko-engine","bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"dist":{"shasum":"1947663e94b69a77227a0c21c4621d0982d0e240","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.1.0.tgz","fileCount":300,"integrity":"sha512-/qgDqCRpX6ZIboHo7t2bAqamPH5/Gyjc5oKZ7zCdP5hYR9WiWoHccny1W+DYWsBJPPOrFucCuuZGLMzOXEn01g==","signatures":[{"sig":"MEYCIQD9E8Ob535Xl4EYl9mp7Kb/JkGlUQN0pA4nnc1HIAYokwIhAIXg2UFRPcdRnBlUOY7U8QSOvr0CfhIoqOdboaQgaHBV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1118410},"main":"./src/index.ts","type":"module","types":"./src/index.ts","//main":"Dev mode (this repo's tests, IDE) reads TS source directly via the exports map. publishConfig.exports below swaps to dist/ on `npm publish` — see https://docs.npmjs.com/cli/v10/configuring-npm/package-json#publishconfig.","engines":{"node":">=20"},"exports":{".":"./src/index.ts","./*.js":"./src/*.ts","./package.json":"./package.json"},"gitHead":"2c930ce4ae53f3a3d3a221e6694eab1df7d45624","scripts":{"test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","prepublishOnly":"npm run check && npm run test && npm run build"},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"//exports":"Dev mode keeps the wildcard so tests + scripts can reach every subpath (including src/internal/*). publishConfig.exports enumerates ONLY the public surface — internal kernel modules become unreachable from npm consumers.","repository":{"url":"git+https://github.com/alexzhaosheng/huko-engine.git","type":"git"},"_npmVersion":"11.12.1","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"nanoid":"^5.0.0","iconv-lite":"^0.6.3","better-sqlite3":"^12.0.0"},"publishConfig":{"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json","./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"}}},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@types/better-sqlite3":"^7.6.0"},"_npmOperationalInternal":{"tmp":"tmp/huko-engine_0.1.0_1779788256550_0.30391767111512125","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@alexzhaosheng/huko-engine","version":"0.1.2","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"license":"MIT","_id":"@alexzhaosheng/huko-engine@0.1.2","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"homepage":"https://github.com/alexzhaosheng/huko-engine","bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"dist":{"shasum":"df000361168ed46d76739f071d5eed23fa3640bb","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.1.2.tgz","fileCount":300,"integrity":"sha512-9676XGHQKLY7srumcLIs+dysJugMVT3KUQMVPBO584LunrBCUkgabYYzpGYiGrKUv/e5Bj3EkB2FRJxK0SyoAA==","signatures":[{"sig":"MEUCIQDLZTRN8smRtYlFROp2oSQUrWYY3iXAi6tvlV0qmPWGtQIgXIWbDmmzwWdAQ6uqEtALUgo1LQC9y/hMqWLKGI4o7Kg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1118064},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","//main":"main/types/exports point at dist/ unconditionally. The repo's own tests + scripts use relative paths (../src/X.js) so they don't need a dev-mode self-resolution rule. Run `npm run build` before any consumer-style import here. The exports map enumerates ONLY the public subpaths — internal kernel modules under src/internal/ are absent, so consumers get ERR_PACKAGE_PATH_NOT_EXPORTED for any internal/* import.","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json","./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"}},"gitHead":"517a8f9c50ba58799b1e432c8a9ff02fff92ba3f","scripts":{"test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","prepublishOnly":"npm run check && npm run test && npm run build"},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"repository":{"url":"git+https://github.com/alexzhaosheng/huko-engine.git","type":"git"},"_npmVersion":"11.12.1","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"nanoid":"^5.0.0","iconv-lite":"^0.6.3","better-sqlite3":"^12.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@types/better-sqlite3":"^7.6.0"},"_npmOperationalInternal":{"tmp":"tmp/huko-engine_0.1.2_1779789092496_0.9557665138040128","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@alexzhaosheng/huko-engine","version":"0.1.3","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"license":"MIT","_id":"@alexzhaosheng/huko-engine@0.1.3","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"homepage":"https://github.com/alexzhaosheng/huko-engine","bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"dist":{"shasum":"daecbbacc963928a6ceb2520dfa70b89c85fd60d","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.1.3.tgz","fileCount":300,"integrity":"sha512-5mj67uGyMdZ/3y/hjmY7FIq+dos4FJNplmc7O5GYiJfEFr00ygmZG5TReL2TiW3q7uc6h6/KuYkvU9lg98/erw==","signatures":[{"sig":"MEUCIQCyvubO8L7LAVb1sLTbgs7KXZt59u58R68zqdlHQjTYawIgPTy2TYdgAUET60PeJCngec4P84B4Oik36hejpWP7lOo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1131245},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","//main":"main/types/exports point at dist/ unconditionally. The repo's own tests + scripts use relative paths (../src/X.js) so they don't need a dev-mode self-resolution rule. Run `npm run build` before any consumer-style import here. The exports map enumerates ONLY the public subpaths — internal kernel modules under src/internal/ are absent, so consumers get ERR_PACKAGE_PATH_NOT_EXPORTED for any internal/* import.","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json","./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"}},"gitHead":"dfd676231ca13c9a145b5f9dbb7d7a4fd3cc2936","scripts":{"test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","prepublishOnly":"npm run check && npm run test && npm run build"},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"repository":{"url":"git+https://github.com/alexzhaosheng/huko-engine.git","type":"git"},"_npmVersion":"11.12.1","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","directories":{},"_nodeVersion":"24.15.0","dependencies":{"nanoid":"^5.0.0","iconv-lite":"^0.6.3","better-sqlite3":"^12.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@types/better-sqlite3":"^7.6.0"},"_npmOperationalInternal":{"tmp":"tmp/huko-engine_0.1.3_1779791501912_0.7120921623356218","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@alexzhaosheng/huko-engine","version":"0.1.4","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"license":"MIT","_id":"@alexzhaosheng/huko-engine@0.1.4","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"homepage":"https://github.com/alexzhaosheng/huko-engine","bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"dist":{"shasum":"a048626a4dadfa1ea299d28222d955c99f8f7a94","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.1.4.tgz","fileCount":300,"integrity":"sha512-EkAlGQ6rJ0FEgZeEm7s4TLuOuu1JJbGPQ0uYjv+AeiZB5IRoURLNvYn3nAYcaGF2xfIKDy22g9K+7/qOqPSVIA==","signatures":[{"sig":"MEUCIGtcW2aqYcbWk8iLbNriAWT06aF9ybW6EECeQBIcdGeLAiEA5D8VpcPkYmCd7RDN+0YMr8gfQ/Kw96FxLvKDqXzYZ/g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.1.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1137255},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","//main":"main/types/exports point at dist/ unconditionally. The repo's own tests + scripts use relative paths (../src/X.js) so they don't need a dev-mode self-resolution rule. Run `npm run build` before any consumer-style import here. The exports map enumerates ONLY the public subpaths — internal kernel modules under src/internal/ are absent, so consumers get ERR_PACKAGE_PATH_NOT_EXPORTED for any internal/* import.","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json","./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"}},"gitHead":"5975e52d94d999804ab8be84e4a34bf593030c64","scripts":{"test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","prepublishOnly":"npm run check && npm run test && npm run build","example:cli-chat":"tsx example/cli-chat/main.ts","example:web-server":"tsx example/web-server/main.ts","example:custom-tool":"tsx example/custom-tool/main.ts","example:with-sqlite":"tsx example/with-sqlite/main.ts"},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"repository":{"url":"git+https://github.com/alexzhaosheng/huko-engine.git","type":"git"},"_npmVersion":"11.13.0","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"nanoid":"^5.0.0","iconv-lite":"^0.6.3","better-sqlite3":"^12.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@types/better-sqlite3":"^7.6.0"},"_npmOperationalInternal":{"tmp":"tmp/huko-engine_0.1.4_1779946530983_0.39360950264989336","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@alexzhaosheng/huko-engine","version":"0.1.5","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"license":"MIT","_id":"@alexzhaosheng/huko-engine@0.1.5","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"homepage":"https://github.com/alexzhaosheng/huko-engine","bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"dist":{"shasum":"a428c16a425d8e07ffb5782c101a5f7b2fadad31","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.1.5.tgz","fileCount":300,"integrity":"sha512-myiaGrVwbKiNjte12/9dRTTuowhAMxinzCzgca7r4+tJUb1pSYpaRZ1AHScv2qdOaL1f5aLzm/aVmTx4A1oyKQ==","signatures":[{"sig":"MEYCIQCed7DS2q8UstmSEDXkwN8gV6jcH02kjYPMT5KOpp7tAAIhAMko9PQWlMo2Q6vfCBor58yayLRiNzZJbmWv48cT1SAq","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.1.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1140668},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","//main":"main/types/exports point at dist/ unconditionally. The repo's own tests + scripts use relative paths (../src/X.js) so they don't need a dev-mode self-resolution rule. Run `npm run build` before any consumer-style import here. The exports map enumerates ONLY the public subpaths — internal kernel modules under src/internal/ are absent, so consumers get ERR_PACKAGE_PATH_NOT_EXPORTED for any internal/* import.","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json","./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"}},"gitHead":"c30f6eeac5946085aed7bdca35327805c8b8a13e","scripts":{"test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","check":"tsc --noEmit","prepublishOnly":"npm run check && npm run test && npm run build","example:cli-chat":"tsx example/cli-chat/main.ts","example:web-server":"tsx example/web-server/main.ts","example:custom-tool":"tsx example/custom-tool/main.ts","example:with-sqlite":"tsx example/with-sqlite/main.ts"},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"repository":{"url":"git+https://github.com/alexzhaosheng/huko-engine.git","type":"git"},"_npmVersion":"11.13.0","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","directories":{},"_nodeVersion":"24.16.0","dependencies":{"nanoid":"^5.0.0","iconv-lite":"^0.6.3","better-sqlite3":"^12.0.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@types/better-sqlite3":"^7.6.0"},"_npmOperationalInternal":{"tmp":"tmp/huko-engine_0.1.5_1780006316642_0.6800596316327221","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alexzhaosheng/huko-engine","version":"0.2.0","description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","type":"module","license":"MIT","homepage":"https://github.com/alexzhaosheng/huko-engine","repository":{"type":"git","url":"git+https://github.com/alexzhaosheng/huko-engine.git"},"bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"engines":{"node":">=20"},"//main":"main/types/exports point at dist/ unconditionally. The repo's own tests + scripts use relative paths (../src/X.js) so they don't need a dev-mode self-resolution rule. Run `npm run build` before any consumer-style import here. The exports map enumerates ONLY the public subpaths — internal kernel modules under src/internal/ are absent, so consumers get ERR_PACKAGE_PATH_NOT_EXPORTED for any internal/* import.","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./persistence/index.js":{"types":"./dist/persistence/index.d.ts","default":"./dist/persistence/index.js"},"./persistence/agent-persistence.js":{"types":"./dist/persistence/agent-persistence.d.ts","default":"./dist/persistence/agent-persistence.js"},"./persistence/types.js":{"types":"./dist/persistence/types.d.ts","default":"./dist/persistence/types.js"},"./prompt/overlay.js":{"types":"./dist/prompt/overlay.d.ts","default":"./dist/prompt/overlay.js"},"./task/tools/registry.js":{"types":"./dist/task/tools/registry.d.ts","default":"./dist/task/tools/registry.js"},"./task/tools/best-practices-built-in.js":{"types":"./dist/task/tools/best-practices-built-in.d.ts","default":"./dist/task/tools/best-practices-built-in.js"},"./task/tools/foundational.js":{"types":"./dist/task/tools/foundational.d.ts","default":"./dist/task/tools/foundational.js"},"./shared/events.js":{"types":"./dist/shared/events.d.ts","default":"./dist/shared/events.js"},"./shared/types.js":{"types":"./dist/shared/types.d.ts","default":"./dist/shared/types.js"},"./llm/types.js":{"types":"./dist/llm/types.d.ts","default":"./dist/llm/types.js"},"./package.json":"./package.json"},"scripts":{"check":"tsc --noEmit","test":"node --import tsx --test \"tests/*.test.ts\"","build":"tsc -p tsconfig.build.json","example:cli-chat":"tsx example/cli-chat/main.ts","example:with-sqlite":"tsx example/with-sqlite/main.ts","example:custom-tool":"tsx example/custom-tool/main.ts","example:web-server":"tsx example/web-server/main.ts","prepublishOnly":"npm run check && npm run test && npm run build"},"keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"dependencies":{"better-sqlite3":"^12.0.0","iconv-lite":"^0.6.3","nanoid":"^5.0.0"},"devDependencies":{"@types/better-sqlite3":"^7.6.0","@types/node":"^22.0.0","tsx":"^4.0.0","typescript":"^5.9.3"},"gitHead":"28566608729bbaff03c231de000110d19df7e512","_id":"@alexzhaosheng/huko-engine@0.2.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-Mded30BZnrjdgPriB/g9eIK6o4xADATgw9wVAotEgMny0rQECfdKuYYwfNzQ0smw7wEYzhdIK4Hvp92939Bb9Q==","shasum":"991c0b5949361bb37a0e14d13b83dca5ef0ba6f9","tarball":"https://registry.npmjs.org/@alexzhaosheng/huko-engine/-/huko-engine-0.2.0.tgz","fileCount":304,"unpackedSize":1151370,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alexzhaosheng%2fhuko-engine@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDfChuDw0w5bNLwEgP9acZ2AHMWQ7CvRZlseAZcDcmdiAIhAPQ3k4wtY5/yzF8Qo7BlbxnC3575mwhEVJ87iO5pWqo/"}]},"_npmUser":{"name":"alexzhaosheng","email":"woodheadz@gmail.com"},"directories":{},"maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/huko-engine_0.2.0_1780529159713_0.27891131059269214"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-26T09:37:36.435Z","modified":"2026-06-03T23:26:00.134Z","0.1.0":"2026-05-26T09:37:36.706Z","0.1.2":"2026-05-26T09:51:32.652Z","0.1.3":"2026-05-26T10:31:42.109Z","0.1.4":"2026-05-28T05:35:31.159Z","0.1.5":"2026-05-28T22:11:56.897Z","0.2.0":"2026-06-03T23:25:59.851Z"},"bugs":{"url":"https://github.com/alexzhaosheng/huko-engine/issues"},"license":"MIT","homepage":"https://github.com/alexzhaosheng/huko-engine","keywords":["agent","ai","llm","tool-use","anthropic","openai","task-loop","huko"],"repository":{"type":"git","url":"git+https://github.com/alexzhaosheng/huko-engine.git"},"description":"Agent runtime — facade + LLM protocol + task loop + tool framework + safety policy + skill loader + prompt assembler + persistence. Embeddable in any host process.","maintainers":[{"name":"alexzhaosheng","email":"woodheadz@gmail.com"}],"readme":"# @alexzhaosheng/huko-engine\n\nEmbeddable agent runtime — LLM protocol adapters, task loop, tool\nframework, safety policy evaluator, skill loader, prompt assembler,\npersistence. Drop it into any Node host process to run capable\nagents through a small, opinionated facade.\n\nA reference host implementation lives in the\n[**huko-cli**](https://github.com/alexzhaosheng/huko) repo — a full\ndaemon + CLI built on this engine (orchestrator, scheduler, daemon\ntransport, web UI, browser tool, file-share, etc.). Reach for it when\nyou want a worked example of how each engine seam gets wired into a\nreal host. For the smallest possible demo see\n[`example/cli-chat/`](example/cli-chat/) in this repo.\n\n---\n\n## Quick start\n\n```sh\nnpm install @alexzhaosheng/huko-engine\n```\n\n> Prefer reading runnable code? [`example/cli-chat/`](example/cli-chat/)\n> is the same shape as the snippet below, wired into a stdin/stdout\n> chat loop with every foundational tool turned on. About 75 lines.\n\n```ts\nimport {\n  createHukoEngine,\n  MemoryAgentPersistence,\n} from \"@alexzhaosheng/huko-engine\";\n\nconst engine = await createHukoEngine({\n  persistence: new MemoryAgentPersistence(),\n});\n\nconst sessionId = await engine.createSession({ title: \"demo\" });\n\nconst agent = engine.createAgent({\n  name: \"demo-agent\",\n  sessionId,\n  defaultProvider: {\n    protocol: \"openai\",\n    baseUrl: \"https://api.openai.com/v1\",\n    apiKey: process.env.OPENAI_API_KEY!,\n    modelId: \"gpt-4o\",\n    toolCallMode: \"native\",\n    thinkLevel: \"off\",\n    contextWindow: 128_000,\n  },\n  // 13 foundational tools (bash, glob, grep, plan, message, ...)\n  // are auto-registered. Allow-list whichever ones this agent\n  // should see — omit `tools` to expose none.\n  tools: { allow: [\"plan\", \"message\", \"bash\", \"read_file\"] },\n});\n\nconst result = await agent.runTurn({ message: \"Hello, who are you?\" });\nconsole.log(result.finalResult);\n\nawait engine.close();\n```\n\nThat's it — three things the host has to supply (`persistence`,\n`defaultProvider`, `tools.allow`); everything else is defaults the\nengine ships:\n\n| Default | What it gets you |\n|--------|-----------------|\n| Foundational tools auto-registered | bash / glob / grep / plan / message / read_file / write_file / edit_file / delete_file / move_file / list_dir / web_fetch / web_search are all resolvable by name. Allow-list to expose. Opt out with `foundationalTools: false`. |\n| `defaultBestPracticesProvider` | The plan tool's `tool_result` grows an \"Expert Checklist\" block for the 4 bundled capabilities (`coding`, `writing`, `research`, `analysis`) when an agent's plan phase tags one. Opt out with `hostHooks: { bestPracticesProvider: null }`. |\n| Automatic orphan-recovery scan | At construction, engine scans persistence for tasks left in non-terminal state from a crashed previous run; marks them failed; injects synthetic `tool_result` rows for any dangling tool_calls so the next conversation continuation on the same session doesn't 400 on strict providers. Silent unless host passes `onOrphanRecovered`. `MemoryAgentPersistence` skips the scan. |\n\nDaemons / orchestrators with live streaming, mid-flight stop, and\noperator response routing reach for `startTurn` instead of\n`runTurn` — see [Daemon patterns](#daemon-patterns).\n\n`createHukoEngine` is async because of the orphan-recovery scan\nabove. For tests or scripts that don't need recovery,\n`createHukoEngineSync(options)` constructs the engine without\nawaiting the scan (defaults still apply).\n\n---\n\n## Install requirements\n\nESM-only; Node 20+. Native `better-sqlite3` is bundled — `npm install`\nfetches a prebuilt binary for common platforms (linux/macos/windows\n× x64/arm64).\n\nThe package ships as compiled JS + `.d.ts` declaration files under\n`dist/`. The `publishConfig.exports` map enumerates the public\nsurface only — kernel primitives under `src/internal/` are NOT\nreachable from the published package (npm consumers get\n`ERR_PACKAGE_PATH_NOT_EXPORTED` for any `internal/*` import). The\nfacade barrel + a small set of curated subpaths (persistence types,\nprompt overlay, registry, foundational tools, event types) are the\nentire public surface.\n\n---\n\n## Core concepts\n\n### `HukoEngine` (one per process)\n\nOwns the per-instance tool registry, the default `AgentPersistence`,\nand the host integration hooks (engine config, safety rule persister,\nbest-practices provider, default cwd). Constructed once at boot:\n\n```ts\nconst engine = await createHukoEngine({\n  persistence,                 // AgentPersistence\n  hostHooks: {                 // optional — see Host hooks\n    config: engineConfig,\n    defaultCwd: process.cwd(),\n    safetyRulePersister: (scope, cwd, tool, bucket, pattern) => { ... },\n    bestPracticesProvider: async (phaseId, title, capabilities) => null,\n  },\n});\n```\n\n### `HukoAgent` (one per chat session)\n\nSession-pinned — each agent represents one ongoing chat. The agent\ncaches its `SessionContext` for its lifetime so successive turns\nshare llmContext without replaying from persistence. Construct one\nper session and cache it on the host (the huko CLI keeps a\n`Map<sessionKey, HukoAgent>`):\n\n```ts\nconst sessionId = await engine.createSession({ title: \"chat 1\" });\nconst agent = engine.createAgent({\n  name: \"chat-1\",              // for debugging\n  sessionId,                   // required — pinned for the agent's life\n  defaultProvider,             // can be overridden per-turn\n  cwd: \"/path/to/project\",     // for engine tools\n  tools: { allow: [\"bash\", \"edit_file\"] },\n  overlays: [...],             // host-supplied prompt extensions\n  skills: [...],               // pre-loaded operator skills\n  projectContext: \"...\",       // AGENTS.md / CLAUDE.md contents\n});\n```\n\n### `AgentPersistence` (narrow contract)\n\nSeven methods (six required + an optional atomic-create hook). Two\nbuilt-ins ship in the box; hosts can implement their own (remote\nstorage, multi-tenant sharded DB, custom audit). See\n[Persistence](#persistence).\n\n---\n\n## Two entry shapes\n\n### `runTurn(input) → Promise<AgentTurnResult>`\n\nConvenience: starts the turn, awaits completion, collects events into\nan array, returns the summary + events. Useful when a single HTTP\nrequest maps to a single turn that returns a single JSON response:\n\n```ts\nconst result = await agent.runTurn({ message: \"...\" });\n// result.{sessionId, taskId, status, finalResult, errorMessage,\n//        promptTokens, completionTokens, totalTokens,\n//        toolCallCount, iterationCount, events}\n```\n\n### `startTurn(input) → Promise<TaskHandle>`\n\nFire-and-track: returns immediately with `{taskId, interjected,\ncompletion}`. The host awaits `completion` when it wants the final\nsummary and uses the live agent for `stop()` / `interject` /\n`respondToAsk` in the meantime. The shape daemon orchestrators use:\n\n```ts\nconst handle = await agent.startTurn({ message: \"...\" });\n// kick off other work, listen for events, etc.\nconst summary = await handle.completion;\n```\n\nBoth methods share the same `StartTurnInput`; `runTurn` is literally\n`startTurn` + `await completion` + event-collection.\n\n---\n\n## Providers\n\nLLM endpoint + model config. The engine takes `Provider` objects as\ndata — the host constructs them however its config layer wants\n(keys.json, vault, environment variables, whatever). The engine\ndoes NOT resolve API key references.\n\n```ts\nconst provider: Provider = {\n  protocol: \"openai\",          // | \"anthropic\" (engine handles both)\n  baseUrl: \"https://api.openai.com/v1\",\n  apiKey: \"sk-...\",\n  modelId: \"gpt-4o\",\n  toolCallMode: \"native\",      // | \"tool-call-emulation\"\n  thinkLevel: \"off\",           // | \"low\" | \"medium\" | \"high\"\n  contextWindow: 128_000,\n  headers: { \"Custom-Header\": \"...\" }, // optional\n};\n```\n\nPer-turn override beats agent default:\n\n```ts\nconst agent = engine.createAgent({\n  name: \"...\",\n  sessionId,\n  defaultProvider: gpt4o,\n});\n\nawait agent.runTurn({ message: \"...\", provider: gpt4oMini }); // one-off\n```\n\n---\n\n## Persistence\n\n### Built-ins\n\n```ts\nimport {\n  SqliteAgentPersistence,   // better-sqlite3, WAL pragma, 3-table schema\n  MemoryAgentPersistence,   // Map-backed, for tests + short-lived agents\n} from \"@alexzhaosheng/huko-engine\";\n\nconst sqlite = new SqliteAgentPersistence(\"/path/to/agent.db\");\nconst memory = new MemoryAgentPersistence();\n```\n\n`SqliteAgentPersistence` exposes its underlying `db: Database.Database`\nfor hosts that need to run their own listing/admin queries without\nwidening the engine's contract.\n\n### Custom\n\nImplement the seven-method `AgentPersistence` interface:\n\n```ts\ninterface AgentPersistence {\n  persist: PersistFn;      // insert one entry, return its id\n  update: UpdateFn;        // patch an existing entry's content/metadata\n  loadInitialContext(sessionId, sessionType): Promise<LLMMessage[]>;\n  createSession(input): Promise<number>;\n  createTask(input): Promise<number>;\n  updateTask(id, patch): Promise<void>;\n  createTaskWithInitialEntry?(input): Promise<{taskId, entryId}>; // optional atomic\n  close(): Promise<void> | void;\n}\n```\n\nThe optional `createTaskWithInitialEntry` lets long-running hosts\nguarantee the \"task row + initial entry\" pair is written\ntransactionally — a crash between the two leaves no orphan task\nwithout its first message. The facade uses it when available, falls\nback to two-step writes otherwise.\n\nA conformance test suite lives in\n`tests/agent-persistence.test.ts` (in this repo) and runs the\nsame 8-test battery against any implementation — parametrise yours\ninto it when adding a new backend.\n\n### Per-agent override\n\n```ts\nconst customPersistence = new MyRemotePersistence(...);\nconst agent = engine.createAgent({\n  name: \"...\",\n  sessionId,\n  persistence: customPersistence,   // overrides engine default\n});\n```\n\n---\n\n## Tools\n\n### Registering host-defined tools\n\n```ts\nengine.registerTool({\n  name: \"write_definition_file\",\n  description: \"Write the app's spec.yaml. Re-renders the build.\",\n  parameters: {\n    type: \"object\",\n    properties: {\n      file: { type: \"string\" },\n      content: { type: \"string\" },\n    },\n    required: [\"file\", \"content\"],\n  },\n  dangerLevel: \"moderate\",\n  promptHint:\n    \"Use write_definition_file to commit changes — never edit files inline.\",\n  handler: async (args, ctx) => {\n    await applyWriteDefinitionFile(ctx.cwd, args.file, args.content);\n    return \"wrote \" + args.file;\n  },\n});\n```\n\n`promptHint` is rendered into the system prompt's `<tool_use>` block\nalongside the tool description. The host can't accidentally desync\ndescription + hint — they're attached to the same record. When a\ntool gets filtered out (not in `agent.tools.allow`), its hint goes\nwith it.\n\n### Foundational tools (auto-registered by default)\n\nThe engine ships 13 foundational tools — bash, glob, grep, list_dir,\nread_file, write_file, edit_file, delete_file, move_file, plan,\nmessage, web_fetch, web_search — and registers them on the engine\ninstance automatically at construction. No imports, no wiring; just\nallow-list whichever ones each agent should see:\n\n```ts\nconst engine = await createHukoEngine({ persistence });\n// All 13 are now resolvable by name; pick what to expose:\nconst agent = engine.createAgent({\n  name: \"...\", sessionId, defaultProvider,\n  tools: { allow: [\"bash\", \"grep\", \"read_file\"] },\n});\n```\n\nOpt out when the host wants to replace a foundational tool with\nits own (e.g. a sandboxed `bash`):\n\n```ts\nconst engine = await createHukoEngine({\n  persistence,\n  foundationalTools: false,\n});\nengine.registerTool({ name: \"bash\", ..., handler: sandboxedBash });\n// Optionally register the rest manually:\nimport {\n  registerFoundationalTools,\n  FOUNDATIONAL_TOOL_REGISTRATIONS,\n} from \"@alexzhaosheng/huko-engine\";\n// either register all of them, or filter the array:\nfor (const reg of FOUNDATIONAL_TOOL_REGISTRATIONS) {\n  if (reg.name !== \"bash\") engine.registerTool(reg);\n}\n```\n\nA tool being registered on the engine doesn't expose it to any LLM —\nexposure is controlled per-agent via `tools.allow`. So the \"all\nfoundational tools registered by default\" stance is safe by default\neven though `bash` is in there.\n\n### Rich tool surface materialization\n\nFor hosts that need dynamic per-tool descriptions (platform notes,\nlean materialization, interactive-mode parameter shaping — the huko\nCLI does all three), compute the LLM-visible tool list off the engine\nand pass it through. `engine.getToolsForLLM` / `engine.getToolPromptHints`\nwalk engine-instance tools merged with the process-global registry\n(engine wins on conflicts), so host-registered tools show up alongside\nfoundational ones:\n\n```ts\nconst filter = { interactive, lean, allowedTools };\nconst toolsMaterialized = engine.getToolsForLLM(filter);\nconst toolPromptHints = engine.getToolPromptHints(filter);\n\nawait agent.startTurn({\n  message: \"...\",\n  toolsMaterialized,\n  toolPromptHints,\n});\n```\n\nWhen `toolsMaterialized` is set, the facade uses it directly instead\nof running its own allow-list materialization.\n\nThe bare `getToolsForLLM(filter)` / `getToolPromptHints(filter)` from\n`@alexzhaosheng/huko-engine/task/tools/registry.js` walk ONLY the\nprocess-global registry. Reach for them when a test or admin path\ngenuinely wants the global-only view; for an agent's LLM surface\nalways go through the engine method.\n\n---\n\n## Prompts\n\n### Profile (full vs lean)\n\n```ts\nconst agent = engine.createAgent({\n  name: \"...\",\n  sessionId,\n  profile: \"lean\",   // ~300-token shell-only prompt; tool filter narrows to [\"bash\"]\n});\n\n// Per-turn toggle:\nawait agent.runTurn({ message: \"...\", lean: true });\n```\n\n### Overlays — extending the canonical prompt\n\nHosts cannot replace base blocks (identity, scope, principles,\nagent_loop, tool_use, error_handling, local, safety, disclosure).\nThey insert at three named positions inside the cache-stable prefix:\n\n```ts\nconst agent = engine.createAgent({\n  name: \"...\",\n  sessionId,\n  overlays: [\n    {\n      name: \"build-context\",\n      content: \"<build_context>app: my-app, ...</build_context>\",\n      position: \"after-project-context\",\n    },\n    {\n      name: \"setup-assistant\",\n      content: \"<setup_assistant>...</setup_assistant>\",\n      position: \"tail\",  // default — same slot as legacy extraOverlays\n    },\n  ],\n});\n```\n\nPositions:\n- `\"after-skills\"` — right after operator skills, before project context\n- `\"after-project-context\"` — right after AGENTS.md / CLAUDE.md / HUKO.md\n- `\"tail\"` — at the cache-stable tail (default; matches legacy\n  `extraOverlays: string[]`)\n\nAll three sit INSIDE the prompt-cache-covered prefix — overlays\ndon't go before `<agent_loop>` because that would invalidate prompt\ncache across hosts sharing the same base.\n\n### Per-turn prompt inputs\n\n`StartTurnInput` accepts per-turn overrides for everything the\nprompt depends on:\n\n```ts\nawait agent.startTurn({\n  message: \"...\",\n  skills: [...],                   // override agent.skills for this turn\n  projectContext: \"...\",           // override agent.projectContext\n  cwd: \"/different/path\",          // override agent.cwd\n  workingLanguage: \"中文\",          // pin language for this turn\n  scheduledTask: {                 // adds <scheduled_task> block\n    cron: \"0 9 * * *\",\n    timezone: \"America/Los_Angeles\",\n    instructions: \"Daily standup brief.\",\n  },\n  extraOverlays: [...],            // merged on top of agent.overlays\n});\n```\n\n### Attachments\n\n```ts\nawait agent.runTurn({\n  message: \"What's in this image?\",\n  attachments: [\n    { kind: \"image\", url: \"https://...\", mimeType: \"image/png\" },\n  ],\n});\n```\n\n---\n\n## Daemon patterns\n\nFor hosts running many sessions concurrently (cli daemon, multi-app\nservers), use `startTurn` for live control:\n\n### Mid-flight stop\n\n```ts\nconst handle = await agent.startTurn({ message: \"...\" });\n// later, from a SIGINT handler or UI button:\nagent.stop();   // aborts pending asks/decisions + tells the loop to wind down\n```\n\n### Interject (operator sends a new message while the agent is still working)\n\n```ts\nif (agent.liveTaskId() !== null) {\n  await agent.startTurn({\n    message: \"actually never mind, do X instead\",\n    interject: true,    // appends to the live task; doesn't start a new one\n  });\n}\n```\n\nIf `interject` is omitted and a live task exists, `startTurn`\nthrows — opt-in semantics prevent accidental clobbering.\n\n### Subscribing to events\n\n```ts\nconst unsubscribe = agent.onEvent((event) => {\n  // event.type: \"assistant_streaming_delta\" | \"task_started\" |\n  //             \"tool_call_started\" | \"ask_user\" | ...\n  socket.emit(\"event\", event);\n});\n// ...\nunsubscribe();\n```\n\nConvenience subscribers for the two operator-facing event types:\n\n```ts\nagent.onAskUser((event) => {\n  // event.toolCallId, event.question, event.options, event.selectionType\n  showAskBanner(event);\n});\n\nagent.onDecision((event) => {\n  // event.toolCallId, event.toolName, event.args, event.reason\n  showDecisionPrompt(event);\n});\n```\n\n### Responding to asks + decisions\n\n```ts\n// The operator's free-text reply to `message(type=ask)`:\nagent.respondToAsk(toolCallId, {\n  content: \"yes, the second option\",\n  attachments: [],\n});\n\n// The operator's y/n/a verdict on a safety-policy decision:\nagent.respondToDecision(toolCallId, {\n  kind: \"allow\",   // | \"deny\" | \"allow_and_remember\"\n});\n```\n\nIf a frontend reconnects mid-conversation (page refresh during an\nask), it can pull the live registry to restore the UI:\n\n```ts\nconst asks = agent.pendingAsks();\n// [{toolCallId, taskId, question, options?, selectionType?, ts}, ...]\n\nconst decisions = agent.pendingDecisions();\n// [{toolCallId, taskId}, ...]\n```\n\n### Scrubber / expander (redacting secrets in transit)\n\nFor hosts with secret-redaction needs (the huko CLI scrubs outbound\ncontent, expands placeholders before tool execution):\n\n```ts\nconst agent = engine.createAgent({\n  name: \"...\",\n  sessionId,\n  scrubText: async (text) => scrubAndRecord(text, { ... }),\n  expandArgs: async (value) => expandPlaceholdersDeep(value, { ... }),\n});\n```\n\nThe agent threads both into its cached `SessionContext` so every\npersisted entry is scrubbed on write and every tool-arg value is\nexpanded before the handler runs.\n\n---\n\n## Host hooks\n\nCross-cutting concerns the engine consults — install through the\nconstructor instead of monkey-patching. **All four are optional**:\nomit any of them to take the default (or the no-op equivalent).\n\n```ts\nconst engine = await createHukoEngine({\n  persistence,\n  hostHooks: {\n    // Engine-eligible config slice (safety rules, llm timeouts,\n    // compaction thresholds, ...). Pipeline + tool code read it\n    // via `ctx.engine.config` per-instance. Omit → DEFAULT_ENGINE_CONFIG.\n    config: projectEngineConfig(hostConfig),\n\n    // Working-directory fallback for tools (bash/glob/grep/...) when\n    // neither call args nor TaskContext.cwd supplies one. Engine code\n    // never reads `process.cwd()` itself. Omit → defaults to \".\".\n    defaultCwd: process.cwd(),\n\n    // Safety policy invokes this when the operator picks \"always\n    // allow\" on a tool decision — typically writes back to the host's\n    // config files. Persistence failures are non-fatal. Omit → no\n    // persistence (the tool still runs, the rule just isn't durable).\n    safetyRulePersister: (scope, cwd, toolName, bucket, pattern) => {\n      appendRule(scope, cwd, toolName, bucket, pattern);\n    },\n\n    // Plan tool invokes this when an agent's phase tags a capability.\n    // Omit → `defaultBestPracticesProvider` (auto-installed; built-in\n    // 4 capabilities). Pass `null` to opt out entirely. Pass your\n    // own function to override (see \"Built-in best practices\" below\n    // for the building blocks).\n    // bestPracticesProvider: defaultBestPracticesProvider,  // implicit\n  },\n});\n```\n\nThe four `hostHooks` fields live as **per-engine state** —\n`ctx.engine.{config,defaultCwd,safetyRulePersister,bestPracticesProvider}`\ninside pipeline / tool code. Two engines in one process can have\ndifferent config / safety persister / best-practices provider without\noverwriting each other.\n\n(The engine constructor also installs the same values into\nmodule-level globals for back-compat with transitional callsites\nthat build a `TaskContext` without an engine handle. New code paths\nalways read the per-instance state; the globals will go away once\nevery transitional callsite migrates.)\n\n### Built-in best practices (auto-installed by default)\n\nEngine bundles four foundational capabilities — `coding`, `writing`,\n`research`, `analysis` — as the in-memory `BUILT_IN_BEST_PRACTICES`\nmap and installs the matching `defaultBestPracticesProvider`\nautomatically.\n\nWhen the LLM tags a plan phase with `capabilities: [\"coding\"]`, the\nplan tool's `tool_result` grows a per-phase Expert Checklist block\npulled from the bundled markdown. No wiring required:\n\n```ts\nconst engine = await createHukoEngine({ persistence });\n// plan(phases=[{ ..., capabilities: [\"coding\"] }]) → checklist auto-attached\n```\n\nOverride or opt out:\n\n```ts\n// Override with your own provider (e.g. filesystem layers on top):\nconst engine = await createHukoEngine({\n  persistence,\n  hostHooks: { bestPracticesProvider: myProvider },\n});\n\n// Opt out entirely:\nconst engine = await createHukoEngine({\n  persistence,\n  hostHooks: { bestPracticesProvider: null },\n});\n```\n\nFor hosts that want richer behaviour (project-local override files,\nremote registries, multi-tenant rules), compose with the building\nblocks:\n\n| Export | What it does |\n|--------|-------------|\n| `BUILT_IN_BEST_PRACTICES` | Read-only `Record<name, rawMarkdown>` — the four bundled blobs |\n| `extractBestPracticesSection(body)` | Pure section extractor — pulls `## Best Practices` block from a body |\n| `resolveBestPracticeBody(raw, max?)` | Strip frontmatter → prefer section → cap → return body or null |\n| `resolveBuiltInBestPractice(name, max?)` | Same pipeline, sourced from the bundled map |\n| `formatBestPracticesInjection(phaseId, title, entries)` | Canonical header + per-capability blocks; returns the final string |\n| `defaultBestPracticesProvider` | Ready-to-use `BestPracticesProvider` walking the bundled map only (the one installed by default) |\n\nThe [huko-cli](https://github.com/alexzhaosheng/huko) repo's\n`src/task/best-practices.ts` is a worked example of wrapping the\nengine helpers with project + user filesystem override layers\n(~50 lines total).\n\n---\n\n## Multiple agents in one process\n\nTwo genuinely different agents, sharing the same engine:\n\n```ts\nconst engine = await createHukoEngine({ persistence });\n\nconst systemChatId = await engine.createSession({ title: \"System chat\" });\nconst systemAgent = engine.createAgent({\n  name: \"system-chat\",\n  sessionId: systemChatId,\n  defaultProvider,\n  overlays: [{ name: \"system-role\", content: \"...\", position: \"tail\" }],\n  tools: { allow: [\"bash\"] },\n});\n\nconst buildAgentId = await engine.createSession({ title: \"Build agent\" });\nconst buildAgent = engine.createAgent({\n  name: \"build-agent\",\n  sessionId: buildAgentId,\n  defaultProvider,\n  cwd: \"/path/to/app\",\n  overlays: [{ name: \"build-context\", content: \"...\", position: \"tail\" }],\n  tools: { allow: [\"bash\", \"edit_file\", \"write_definition_file\"] },\n});\n```\n\nBoth share the engine's tool registry + persistence + host hooks.\nEach agent's `SessionContext`, live task, ask/decision registries,\nand event subscribers are isolated.\n\n---\n\n## Package boundary\n\nEngine code under `src/` must not depend on a specific host\nenvironment. That means no:\n\n- HTTP, Socket.IO, or other transport (host wires those)\n- Concrete persistence backends — engine sees only the\n  `AgentPersistence` / `SessionPersistence` interfaces, not their\n  implementations\n- `process.cwd()` — host injects `defaultCwd` via hostHooks instead\n- DOM, drizzle, or other dep that ties the engine to one runtime\n\nEnforced two ways:\n\n1. **Package-level**: anything the engine imports must be a `node:*`\n   builtin, a dep declared in this package's `package.json`, or a\n   sibling file inside `src/`. pnpm's per-package install rejects\n   undeclared imports at install time; Node's resolver rejects them\n   at runtime.\n2. **`tests/engine-boundary.test.ts`** in this repo walks engine\n   sources and greps imports as a belt-and-braces check.\n\n---\n\n## Layout\n\n```text\nsrc/\n├── facade.ts                    createHukoEngine + HukoEngine + HukoAgent\n├── SessionContext.ts            @internal — session-scoped data bus\n├── TaskContext.ts               @internal — task-scoped runtime state\n├── config/                      EngineConfig + module-level state\n├── features/                    feature registry + sidecar lifecycle\n├── llm/                         provider abstraction: protocols, openai\n│                                adapter, types, model context window,\n│                                raw-debug-log, cache-boundary sentinel\n├── persistence/\n│   ├── agent-persistence.ts     narrow interface\n│   ├── sqlite.ts                SqliteAgentPersistence\n│   ├── memory.ts                MemoryAgentPersistence\n│   └── types.ts                 wider SessionPersistence (host-side)\n├── prompt/\n│   ├── assemble.ts              @internal — canonical assembler\n│   ├── lean.ts                  @internal — lean profile composer\n│   ├── blocks.ts                named building blocks\n│   └── overlay.ts               PromptOverlay type + position bucketing\n├── safety/                      pure policy evaluator\n├── skills/                      skill parsing primitives (file IO host-side)\n├── task/                        task execution: task-loop.ts, pipeline/,\n│                                behavior-guard, language-reminder,\n│                                plan-state, resume, task-boundary,\n│                                tools/ (foundational implementations +\n│                                registry)\n├── util/yaml-frontmatter.ts     zero-dep YAML subset parser\n├── shared/                      type-only modules (events, llm-protocol,\n│                                plan-types, types)\n└── index.ts                     public barrel — facade + persistence +\n                                 overlay + event/protocol types\n```\n\n---\n\n## Internal kernel\n\nThe engine kernel primitives (`TaskLoop`, `TaskContext`,\n`SessionContext`, `assembleSystemPrompt`, `assembleLeanSystemPrompt`,\n`recoverOrphans`, `registerServerTool`) are tagged `@internal` in\nJSDoc. They remain exported via subpath imports for engine tests +\npre-facade host paths, but new host code should reach for the public\nfacade barrel instead.\n\n`@internal` is a documentation tag, not a runtime check — imports\nstill work. The tag signals that the surface is engine-internal and\nmay shift between releases without a deprecation cycle.\n\n---\n\n## See also\n\n- **[huko-cli](https://github.com/alexzhaosheng/huko)** —\n  reference host implementation. The cli daemon, web UI, CLI\n  formatters, scheduler, scrubber, browser tool, file-share tool,\n  and the orchestrator wiring around `engine.startTurn` /\n  `agent.respondToAsk` are all worth reading if you're embedding\n  the engine into a daemon-style product.\n- **[docs/cookbook.md](docs/cookbook.md)** — copy-pasteable recipes\n  for common patterns (switching providers, custom tools, sqlite\n  persistence, ask/answer flow, skills, scheduled tasks).\n- **[AGENTS.md](AGENTS.md)** — guidance for AI assistants helping\n  developers integrate this engine into their projects.\n- **[example/](example/)** — runnable end-to-end demos: cli-chat,\n  with-sqlite, custom-tool, web-server.\n- **[docs/public-api-facade.md](docs/public-api-facade.md)** —\n  why the facade looks like this, the design tradeoffs, and the\n  migration steps the engine went through to reach this shape.\n- **[docs/RELEASE.md](docs/RELEASE.md)** — release process for\n  this package (tag-triggered npm publish with provenance).\n\n---\n\n## Status\n\nPublished as `@alexzhaosheng/huko-engine` on npm. Versioned per\nsemver:\n\n- `0.x` — public API may still shift. Pinning to an exact patch is\n  reasonable until 1.0.\n- breaking changes within `0.x` get called out explicitly in the\n  CHANGELOG; bumps to the minor version.\n\nRelease process is documented in [`docs/RELEASE.md`](docs/RELEASE.md)\n— tag-triggered, no manual `npm publish` from a laptop.\n\nCI runs on every push and PR across Linux / macOS / Windows × Node\n24 (see `.github/workflows/ci.yml`).\n\n---\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n","readmeFilename":"README.md"}