pi-coding-master 0.2.7
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 +50 -0
- package/core/god.agent.capability/@ABANDONED.brain.prefrontal.monitor/gpu_monitor.py +80 -0
- package/core/god.agent.capability/@ABANDONED.brain.prefrontal.monitor/gpu_monitor.py.CHANGELOG +1 -0
- package/core/god.agent.capability/@ABANDONED.brain.prefrontal.monitor/gpu_monitor.py.SPEC +3 -0
- package/core/god.agent.capability/hands.dev.writeissue/issue.ts +209 -0
- package/core/god.agent.capability/hands.dev.writeissue/issue.ts.SPEC +26 -0
- package/core/god.agent.capability/hands.files.changewatcher/watcher.ts +44 -0
- package/core/god.agent.capability/hands.files.changewatcher/watcher.ts.SPEC +25 -0
- package/core/god.pi.mod/cli/pi-completion.zsh +21 -0
- package/core/god.pi.mod/cli/pi-completion.zsh.CHANGELOG +1 -0
- package/core/god.pi.mod/cli/pi-people.sh +264 -0
- package/core/god.pi.mod/cli/pi-people.sh.LESSON +10 -0
- package/core/god.pi.mod/cli/pi-people.sh.SPEC +31 -0
- package/core/god.pi.mod/paths.ts +47 -0
- package/core/god.pi.mod/tui.mods.blockrender/blockrender.js +90 -0
- package/core/god.pi.mod/tui.mods.blockrender/blockrender.js.SPEC +29 -0
- package/core/god.pi.mod/tui.mods.footer.budget/budget_guard.ts +154 -0
- package/core/god.pi.mod/tui.mods.footer.budget/budget_guard.ts.CHANGELOG +10 -0
- package/core/god.pi.mod/tui.mods.footer.budget/budget_guard.ts.LESSON +12 -0
- package/core/god.pi.mod/tui.mods.footer.budget/budget_guard.ts.SPEC +26 -0
- package/core/god.pi.mod/tui.mods.footer.budget/footer-cny.CHANGELOG +39 -0
- package/core/god.pi.mod/tui.mods.viewmode/view_mode-thinking.patch +55 -0
- package/core/god.pi.mod/tui.mods.viewmode/view_mode-tools.patch +19 -0
- package/core/god.pi.mod/tui.mods.viewmode/view_mode-tools.patch.CHANGELOG +1 -0
- package/core/god.pi.mod/tui.mods.viewmode/view_mode.ts +50 -0
- package/core/god.pi.mod/tui.mods.viewmode/view_mode.ts.SPEC +12 -0
- package/core/god.pi.mod/tui.variants.userterminal/user_terminal.CHANGELOG +10 -0
- package/core/god.pi.mod/tui.variants.userterminal/user_terminal.ts +66 -0
- package/core/god.pi.mod/tui.variants.userterminal/user_terminal.ts.SPEC +31 -0
- package/core/index.ts +3 -0
- package/core/individual.bio.gene/dna.coded/coded.dna +257 -0
- package/core/individual.bio.gene/dna.coded/coded.dna.CHANGELOG +12 -0
- package/core/individual.bio.gene/dna.coded/coded.dna.SPEC +11 -0
- package/core/individual.bio.gene/dna.coded/core.dna +110 -0
- package/core/individual.bio.gene/dna.promotor/promotor.dna +117 -0
- package/core/individual.bio.gene/dna.promotor/promotor.dna.CHANGELOG +4 -0
- package/core/individual.bio.gene/dna.promotor/promotor.dna.SPEC +7 -0
- package/core/individual.bio.gene/dna.transpiler/transpiler.ts +395 -0
- package/core/individual.bio.gene/dna.transpiler/transpiler.ts.CHANGELOG +7 -0
- package/core/individual.bio.gene/dna.transpiler/transpiler.ts.SPEC +28 -0
- package/core/individual.bio.gene/gene.README +19 -0
- package/core/individual.bio.gene/rna/rna.json +536 -0
- package/core/individual.bio.gene/rna/rna.json.CHANGELOG +2 -0
- package/core/individual.bio.gene/rna/rna.json.SPEC +8 -0
- package/core/individual.bio.organs/blood.runtime/messages.ts +236 -0
- package/core/individual.bio.organs/blood.runtime/runtime.ts +173 -0
- package/core/individual.bio.organs/blood.runtime/runtime.ts.CHANGELOG +5 -0
- package/core/individual.bio.organs/blood.runtime/runtime.ts.SPEC +32 -0
- package/core/individual.bio.organs/brain.amygdala/amygdala.ts +25 -0
- package/core/individual.bio.organs/brain.amygdala/amygdala.ts.COMMENT +3 -0
- package/core/individual.bio.organs/brain.amygdala/amygdala.ts.SPEC +9 -0
- package/core/individual.bio.organs/brain.hippocampus/hippocampus-launcher.sh.template +108 -0
- package/core/individual.bio.organs/brain.hippocampus/hippocampus.ts +166 -0
- package/core/individual.bio.organs/brain.hippocampus/hippocampus.ts.CHANGELOG +12 -0
- package/core/individual.bio.organs/brain.hippocampus/hippocampus.ts.LESSON +22 -0
- package/core/individual.bio.organs/brain.hippocampus/hippocampus.ts.SPEC +16 -0
- package/core/individual.bio.organs/brain.hippocampus/memory.ts +879 -0
- package/core/individual.bio.organs/brain.hippocampus/memory.ts.CHANGELOG +66 -0
- package/core/individual.bio.organs/brain.hippocampus/memory.ts.LESSON +25 -0
- package/core/individual.bio.organs/brain.hippocampus/memory.ts.SPEC +46 -0
- package/core/individual.bio.organs/brain.hippocampus/sleep.ts +139 -0
- package/core/individual.bio.organs/brain.hippocampus/sleep.ts.CHANGELOG +11 -0
- package/core/individual.bio.organs/brain.hippocampus/sleep.ts.SPEC +16 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/drafting.SPEC +44 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/drafting.ts +73 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/drafting.ts.CHANGELOG +3 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/drafting.ts.SPEC +24 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/index.ts.CHANGELOG +3 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/main.ts +13 -0
- package/core/individual.bio.organs/brain.prefrontal.drafting/main.ts.SPEC +17 -0
- package/core/individual.bio.organs/brain.senses.bioclock/bioclock.ts +94 -0
- package/core/individual.bio.organs/brain.senses.bioclock/bioclock.ts.LESSON +13 -0
- package/core/individual.bio.organs/brain.senses.bioclock/bioclock.ts.SPEC +20 -0
- package/core/individual.bio.organs/brain.senses.subconscious/feed-format.SPEC +56 -0
- package/core/individual.bio.organs/brain.senses.subconscious/index.ts.CHANGELOG +13 -0
- package/core/individual.bio.organs/brain.senses.subconscious/spawner.ts +130 -0
- package/core/individual.bio.organs/brain.senses.subconscious/spawner.ts.SPEC +13 -0
- package/core/individual.bio.organs/brain.senses.subconscious/subconscious.ts +280 -0
- package/core/individual.bio.organs/brain.senses.subconscious/subconscious.ts.CHANGELOG +7 -0
- package/core/individual.bio.organs/brain.senses.subconscious/tools.ts +180 -0
- package/core/individual.bio.organs/brain.senses.subconscious/tools.ts.SPEC +3 -0
- package/core/individual.bio.organs/ears.listen/config.json +9 -0
- package/core/individual.bio.organs/ears.listen/config.json.CHANGELOG +1 -0
- package/core/individual.bio.organs/ears.listen/config.json.SPEC +3 -0
- package/core/individual.bio.organs/ears.listen/ears.ts.CHANGELOG +48 -0
- package/core/individual.bio.organs/ears.listen/ears_recorder.py.CHANGELOG +6 -0
- package/core/individual.bio.organs/ears.listen/index.ts +1 -0
- package/core/individual.bio.organs/ears.listen/index.ts.SPEC +16 -0
- package/core/individual.bio.organs/ears.listen/listen.ts +208 -0
- package/core/individual.bio.organs/ears.listen/listen.ts.SPEC +3 -0
- package/core/individual.bio.organs/ears.listen/listen_recorder.py +445 -0
- package/core/individual.bio.organs/ears.listen/listen_recorder.py.SPEC +7 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/common/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/common/events_pb2.py +38 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/common/events_pb2_grpc.py +24 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/common/rpcmeta_pb2.py +42 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/common/rpcmeta_pb2_grpc.py +24 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/ast/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/ast/ast_service_pb2.py +45 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/ast/ast_service_pb2_grpc.py +97 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/base/__init__.py +0 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/base/au_base_pb2.py +80 -0
- package/core/individual.bio.organs/ears.listen/python_protogen/products/understanding/base/au_base_pb2_grpc.py +24 -0
- package/core/individual.bio.organs/hands.fileactions/authorize.TRUST +1 -0
- package/core/individual.bio.organs/hands.fileactions/authorize.ts +70 -0
- package/core/individual.bio.organs/hands.fileactions/authorize.ts.CHANGELOG +1 -0
- package/core/individual.bio.organs/hands.fileactions/authorize.ts.SPEC +3 -0
- package/core/individual.bio.organs/hands.fileactions/dir.README +13 -0
- package/core/individual.bio.organs/hands.fileactions/file_rules.json +23 -0
- package/core/individual.bio.organs/hands.fileactions/fileactions.README +12 -0
- package/core/individual.bio.organs/hands.fileactions/fileactions.ts +540 -0
- package/core/individual.bio.organs/hands.fileactions/fileactions.ts.CHANGELOG +25 -0
- package/core/individual.bio.organs/hands.fileactions/fileactions.ts.SPEC +30 -0
- package/core/individual.bio.organs/hands.fileactions/filewatch.ts +66 -0
- package/core/individual.bio.organs/hands.fileactions/filewatch.ts.SPEC +3 -0
- package/core/individual.bio.organs/hands.main/main.ts +18 -0
- package/core/individual.bio.organs/hands.main/main.ts.SPEC +20 -0
- package/core/individual.bio.organs/hands.sensitive/sensitive.ts +24 -0
- package/core/individual.bio.organs/hands.sensitive/sensitive.ts.CHANGELOG +2 -0
- package/core/individual.bio.organs/hands.sensitive/sensitive.ts.SPEC +3 -0
- package/core/individual.bio.organs/heart.interrupt/agent_start-loader-flicker.patch +13 -0
- package/core/individual.bio.organs/heart.interrupt/interactive-mode-loader.patch +11 -0
- package/core/individual.bio.organs/heart.interrupt/runner_esc.patch +10 -0
- package/core/individual.bio.organs/heart.interrupt/runner_esc.patch.SPEC +9 -0
- package/core/individual.bio.organs/heart.kernel/kernel.ts +253 -0
- package/core/individual.bio.organs/heart.kernel/kernel.ts.CHANGELOG +13 -0
- package/core/individual.bio.organs/heart.kernel/kernel.ts.SPEC +23 -0
- package/core/individual.bio.organs/heart.main/heart.main.CHANGELOG +43 -0
- package/core/individual.bio.organs/heart.main/heartbeat.ts +494 -0
- package/core/individual.bio.organs/heart.main/heartbeat.ts.LESSON +43 -0
- package/core/individual.bio.organs/heart.main/main.ts +8 -0
- package/core/individual.bio.organs/heart.main/main.ts.SPEC +19 -0
- package/core/individual.bio.organs/heart.main/process.ts +122 -0
- package/core/individual.bio.organs/heart.main/process.ts.CHANGELOG +2 -0
- package/core/individual.bio.organs/heart.main/process.ts.SPEC +24 -0
- package/core/individual.bio.organs/heart.main/remove_timeout.patch +110 -0
- package/core/individual.bio.organs/heart.main/stop.ts.CHANGELOG +3 -0
- package/core/individual.bio.organs/heart.main/stop.ts.SPEC +28 -0
- package/core/individual.bio.organs/mouth.speak/index.ts +1 -0
- package/core/individual.bio.organs/mouth.speak/index.ts.CHANGELOG +1 -0
- package/core/individual.bio.organs/mouth.speak/index.ts.SPEC +6 -0
- package/core/individual.bio.organs/mouth.speak/mouth.ts.CHANGELOG +15 -0
- package/core/individual.bio.organs/mouth.speak/mouth_recorder.py.CHANGELOG +1 -0
- package/core/individual.bio.organs/mouth.speak/speak.ts +180 -0
- package/core/individual.bio.organs/mouth.speak/speak.ts.SPEC +3 -0
- package/core/individual.bio.organs/mouth.speak/speak_recorder.py +35 -0
- package/core/individual.bio.organs/mouth.speak/speak_recorder.py.SPEC +3 -0
- package/core/individual.bio.organs/organs.README +110 -0
- package/core/package-lock.json +18 -0
- package/core/package.json +35 -0
- package/core/prompts/prompts.json +77 -0
- package/core/prompts/prompts.ts +44 -0
- package/core/prompts/prompts.ts.SPEC +9 -0
- package/core/society.world/.gitkeep +0 -0
- package/core/society.world/accessibility.claudecode/message-service.cjs +217 -0
- package/core/society.world/accessibility.claudecode/message-service.cjs.SPEC +7 -0
- package/core/society.world/accessibility.claudecode/send.ts +34 -0
- package/core/society.world/dollar.distribution.ubi/ubi.ts +55 -0
- package/core/society.world/dollar.main/dollar-service.cjs +185 -0
- package/core/society.world/dollar.main/main.ts +116 -0
- package/core/society.world/dollar.transaction/transaction.ts +71 -0
- package/core/society.world/space/space.ts +206 -0
- package/core/society.world/space/space.ts.SPEC +30 -0
- package/core/technology.laptop/#agent.macos.BLUEPRINT +278 -0
- package/core/technology.phone/apps.preinstalled/albums.FUTURE/albums.ts +69 -0
- package/core/technology.phone/apps.preinstalled/albums.FUTURE/albums.ts.SPEC +15 -0
- package/core/technology.phone/apps.preinstalled/calendar/calendar.ts +406 -0
- package/core/technology.phone/apps.preinstalled/calendar/calendar.ts.SPEC +22 -0
- package/core/technology.phone/apps.preinstalled/calendar/holiday-calendar.ts +529 -0
- package/core/technology.phone/apps.preinstalled/clock/clock.ts +132 -0
- package/core/technology.phone/apps.preinstalled/clock/clock.ts.SPEC +11 -0
- package/core/technology.phone/apps.preinstalled/contacts.FUTURE/contacts.ts +300 -0
- package/core/technology.phone/apps.preinstalled/contacts.FUTURE/contacts.ts.SPEC +22 -0
- package/core/technology.phone/apps.preinstalled/developer.FUTURE/developer.ts +22 -0
- package/core/technology.phone/apps.preinstalled/developer.FUTURE/developer.ts.SPEC +15 -0
- package/core/technology.phone/apps.preinstalled/notes/notes.ts +239 -0
- package/core/technology.phone/apps.preinstalled/notes/notes.ts.SPEC +21 -0
- package/core/technology.phone/apps.preinstalled/polymarket/polymarket.ts +261 -0
- package/core/technology.phone/apps.preinstalled/polymarket/polymarket.ts.SPEC +7 -0
- package/core/technology.phone/apps.preinstalled/reminder/reminder.ts +404 -0
- package/core/technology.phone/apps.preinstalled/reminder/reminder.ts.SPEC +25 -0
- package/core/technology.phone/apps.preinstalled/siri.FUTURE/siri.ts +22 -0
- package/core/technology.phone/apps.preinstalled/siri.FUTURE/siri.ts.SPEC +15 -0
- package/core/technology.phone/apps.preinstalled/spotlight/spotlight.ts +29 -0
- package/core/technology.phone/apps.preinstalled/spotlight/spotlight.ts.SPEC +7 -0
- package/core/technology.phone/apps.preinstalled/steam/chess.ts +230 -0
- package/core/technology.phone/apps.preinstalled/steam/snake.ts +100 -0
- package/core/technology.phone/apps.preinstalled/steam/snake.ts.SPEC +7 -0
- package/core/technology.phone/apps.preinstalled/steam/spy-cmd.ts +4 -0
- package/core/technology.phone/apps.preinstalled/steam/spy-tool.ts +56 -0
- package/core/technology.phone/apps.preinstalled/steam/spy.ts +302 -0
- package/core/technology.phone/apps.preinstalled/steam/steam.ts +299 -0
- package/core/technology.phone/apps.preinstalled/weather/weather.ts +50 -0
- package/core/technology.phone/apps.preinstalled/weather/weather.ts.SPEC +9 -0
- package/core/technology.phone/apps.preinstalled/wechat/imessage.ts +423 -0
- package/core/technology.phone/apps.preinstalled/wechat/imessage.ts.SPEC +24 -0
- package/core/technology.phone/apps.system/appstore/appstore.ts +22 -0
- package/core/technology.phone/apps.system/appstore/appstore.ts.SPEC +15 -0
- package/core/technology.phone/apps.system/finder.FUTURE/finder.ts +64 -0
- package/core/technology.phone/apps.system/finder.FUTURE/finder.ts.SPEC +8 -0
- package/core/technology.phone/apps.system/safari/safari-app.ts +146 -0
- package/core/technology.phone/apps.system/safari/safari.ts.SPEC +24 -0
- package/core/technology.phone/apps.system/settings/settings.ts +126 -0
- package/core/technology.phone/apps.system/settings/settings.ts.SPEC +17 -0
- package/core/technology.phone/apps.system/tips/tips.ts +22 -0
- package/core/technology.phone/apps.system/tips/tips.ts.SPEC +15 -0
- package/core/technology.phone/apps.thirdparty/alipay/alipay.ts +148 -0
- package/core/technology.phone/apps.thirdparty/alipay/alipay.ts.SPEC +7 -0
- package/core/technology.phone/apps.thirdparty/bilibili/bilibili-app.ts +33 -0
- package/core/technology.phone/apps.thirdparty/bilibili/bilibili.ts +142 -0
- package/core/technology.phone/apps.thirdparty/bilibili/bilibili.ts.SPEC +22 -0
- package/core/technology.phone/apps.thirdparty/wechatread/wechatread.ts +80 -0
- package/core/technology.phone/apps.thirdparty/wechatread/wechatread.ts.SPEC +15 -0
- package/core/technology.phone/index.ts +1 -0
- package/core/technology.phone/package.json +2 -0
- package/core/technology.phone/system.homepage/homepage.ts +247 -0
- package/core/technology.phone/system.homepage/homepage.ts.SPEC +22 -0
- package/core/technology.phone/system.kernel/kernel.ts +264 -0
- package/core/technology.phone/system.kernel/kernel.ts.SPEC +7 -0
- package/core/technology.phone/system.notifications/notifications.ts +87 -0
- package/core/technology.phone/system.notifications/notifications.ts.SPEC +7 -0
- package/core/technology.phone/system.share/share.ts +46 -0
- package/core/technology.phone/system.share/share.ts.SPEC +7 -0
- package/core/technology.server/browser-service.cjs +152 -0
- package/core/technology.server/data/cookies/arxiv.json +30 -0
- package/core/technology.server/data/cookies/bili.json +184 -0
- package/core/technology.server/data/cookies/default.json +30 -0
- package/core/technology.server/data/cookies/news.json +113 -0
- package/core/technology.server/data/cookies/s1.json +45 -0
- package/core/technology.server/data/cookies/safari.json +184 -0
- package/core/technology.server/data/cookies/safari2.json +1 -0
- package/core/technology.server/data/cookies/safaridbg.json +1 -0
- package/core/technology.server/data/cookies/search.json +45 -0
- package/core/technology.server/data/cookies/sw.json +30 -0
- package/core/technology.server/data/cookies/t1.json +1 -0
- package/core/technology.server/data/cookies/testread.json +1 -0
- package/core/technology.server/data/cookies/video1.json +113 -0
- package/core/technology.server/data/cookies/yt.json +170 -0
- package/core/technology.server/data/cookies/yt2.json +113 -0
- package/core/technology.server/pikipedia/#pikipedia.BLUEPRINT +106 -0
- package/core/technology.server/playleft.cjs +247 -0
- package/core/technology.server/search-proxy.py +76 -0
- package/core/technology.server/server.README +59 -0
- package/deploy/dist-overrides/cli/cli.js +18 -0
- package/deploy/dist-overrides/core/extensions/loader.js +518 -0
- package/deploy/dist-overrides/core/package-manager.js +2081 -0
- package/deploy/dist-overrides/core/system-prompt.js +109 -0
- package/deploy/dist-overrides/core/system-prompt.js.LESSON +17 -0
- package/deploy/dist-overrides/core/tools/bash.js +353 -0
- package/deploy/dist-overrides/core/tools/bash.js.CHANGELOG +2 -0
- package/deploy/dist-overrides/core/tools/edit-diff.js +345 -0
- package/deploy/dist-overrides/core/tools/edit.js +315 -0
- package/deploy/dist-overrides/core/tools/edit.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/core/tools/file-mutation-queue.js +52 -0
- package/deploy/dist-overrides/core/tools/find.js +298 -0
- package/deploy/dist-overrides/core/tools/find.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/core/tools/grep.js +305 -0
- package/deploy/dist-overrides/core/tools/grep.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/core/tools/index.js +112 -0
- package/deploy/dist-overrides/core/tools/ls-guard.js +4 -0
- package/deploy/dist-overrides/core/tools/ls.js +170 -0
- package/deploy/dist-overrides/core/tools/ls.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/core/tools/output-accumulator.js +184 -0
- package/deploy/dist-overrides/core/tools/path-utils.js +99 -0
- package/deploy/dist-overrides/core/tools/prompts-reader.js +53 -0
- package/deploy/dist-overrides/core/tools/read.js +392 -0
- package/deploy/dist-overrides/core/tools/read.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/core/tools/render-utils.js +65 -0
- package/deploy/dist-overrides/core/tools/tool-definition-wrapper.js +34 -0
- package/deploy/dist-overrides/core/tools/truncate.js +215 -0
- package/deploy/dist-overrides/core/tools/write.js +203 -0
- package/deploy/dist-overrides/core/tools/write.js.CHANGELOG +1 -0
- package/deploy/dist-overrides/dist-overrides.README +18 -0
- package/deploy/dist-overrides/main.js +665 -0
- package/deploy/dist-overrides/modes/interactive/components/assistant-message.js +139 -0
- package/deploy/dist-overrides/modes/interactive/components/footer.js +326 -0
- package/deploy/dist-overrides/modes/interactive/components/model-selector.js +285 -0
- package/deploy/dist-overrides/modes/interactive/components/tool-execution.js +383 -0
- package/deploy/dist-overrides/modes/interactive/components/tool-execution.js.CHANGELOG +3 -0
- package/deploy/dist-overrides/modes/interactive/interactive-mode.js +4781 -0
- package/deploy/dist-overrides/pi-ai/providers/anthropic.js +931 -0
- package/deploy/dist-overrides/pi-ai/providers/openai-completions.js +1007 -0
- package/deploy/dist-overrides/pi-ai/providers/openai-completions.js.LESSON +15 -0
- package/deploy/dist-overrides/pi-tui/components/loader.js +69 -0
- package/deploy/dist-overrides/pi-tui/components/markdown.js +646 -0
- package/deploy/dist-overrides/pi-tui/components/text.js +92 -0
- package/deploy/dist-overrides/pi-tui/custom-message.js +75 -0
- package/deploy/dist-overrides/pi-tui/tui.js +1266 -0
- package/deploy/dist-overrides/pi-tui/utils.js +1060 -0
- package/deploy/dist-overrides/vendor.REMOVED/jiti/lib/jiti.mjs +3 -0
- package/deploy/install.sh +186 -0
- package/deploy/install.sh.CHANGELOG +6 -0
- package/deploy/lint/lint-naming.ts +202 -0
- package/deploy/scripts/apply.js +18 -0
- package/deploy/scripts/build-github.sh +219 -0
- package/deploy/scripts/build-phone.sh +24 -0
- package/deploy/scripts/check-deploy.sh +42 -0
- package/deploy/scripts/migrate-context.sh +72 -0
- package/deploy/scripts/patch-pi-dist.js +39 -0
- package/deploy/scripts/uninstall.sh +34 -0
- package/package.json +18 -0
|
@@ -0,0 +1,879 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { getSessionRole } from "#runtime";
|
|
6
|
+
import { personDataDir as _personDataDir } from "#paths";
|
|
7
|
+
import { sendCustomMessage } from "#messages";
|
|
8
|
+
|
|
9
|
+
function getPersonDir(sessionFile: string | undefined): string | null {
|
|
10
|
+
const envDir = process.env.PI_PERSON_DIR;
|
|
11
|
+
if (envDir) return envDir;
|
|
12
|
+
const dir = _personDataDir(sessionFile);
|
|
13
|
+
if (dir) fs.mkdirSync(dir, { recursive: true });
|
|
14
|
+
return dir;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readFile(p: string): string {
|
|
18
|
+
try { return fs.readFileSync(p, "utf-8"); } catch { return ""; }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function appendFile(p: string, text: string): void {
|
|
22
|
+
try { fs.appendFileSync(p, text, "utf-8"); } catch {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function writeFile(p: string, text: string): void {
|
|
26
|
+
try { fs.writeFileSync(p, text, "utf-8"); } catch {}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 机密脱敏:把 sshpass 密码、sk- 风格 API key、Bearer token 的【值】盖成 [REDACTED],只留结构。
|
|
30
|
+
// 不是删——保留"这里有个密码"的痕迹,只抹掉值。幂等(已脱敏的再跑结果不变)。
|
|
31
|
+
function scrubSecrets(s: string): string {
|
|
32
|
+
if (!s) return s;
|
|
33
|
+
return s
|
|
34
|
+
.replace(/(sshpass\s+-p\s*)(["']?)([^\s"']+)\2/g, "$1$2[REDACTED]$2")
|
|
35
|
+
.replace(/\bsk-[A-Za-z0-9_-]{12,}/g, "sk-[REDACTED]")
|
|
36
|
+
.replace(/\b(Bearer[;:\s]+)[A-Za-z0-9._-]{12,}/g, "$1[REDACTED]");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let _memoryRegistered = false;
|
|
40
|
+
export function registerMemory(pi: ExtensionAPI) {
|
|
41
|
+
if (_memoryRegistered) { console.warn("registerMemory called twice — skipping"); return []; }
|
|
42
|
+
_memoryRegistered = true;
|
|
43
|
+
const _cmds: any[] = [];
|
|
44
|
+
let personDir: string | null = null;
|
|
45
|
+
let _sigCount: Map<string, number> | null = null;
|
|
46
|
+
let _sigWindow: string[] | null = null;
|
|
47
|
+
|
|
48
|
+
// ── session_start: 注入"记忆快照"一次(稳定前缀 = 缓存命中的关键)────────────
|
|
49
|
+
// 醒来时把 DNA + cortex + work_memory + context(按预算切尾部) 揉成一份快照,注入一次。
|
|
50
|
+
// 本会话中绝不再重发(见 before_agent_start);新内容一律往「后面」append → 前缀不变 → 每轮命中缓存。
|
|
51
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
52
|
+
personDir = getPersonDir(ctx.sessionManager.getSessionFile());
|
|
53
|
+
dnaState = "wake"; // always wake on new session
|
|
54
|
+
if (!personDir) return;
|
|
55
|
+
|
|
56
|
+
// 只有主意识注入记忆快照。hc/sc/sl 小号有各自的活(读 context/feed/work_mem 编码),
|
|
57
|
+
// 绝不能把主意识那 ~90万 token 快照灌给它们 —— 会白白膨胀、甚至把小号撑挂(海马体死亡循环的元凶之一)。
|
|
58
|
+
const sf0 = ctx.sessionManager.getSessionFile() || "";
|
|
59
|
+
if (/conscious-sessions|hippocampus-sessions|sleep-sessions/.test(sf0)) {
|
|
60
|
+
injectedWorkMemLen = readFile(path.join(personDir, "work_memory.md")).length;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 机密脱敏(清历史):把已混进 context/work_memory 的密码/key 值盖掉(只盖值不删,幂等,只在有变化时写回)。
|
|
65
|
+
// 写入时也会脱敏(见 message_end),这里清掉之前已经混进去的(如 sshpass 密码已散落 49 处)。
|
|
66
|
+
for (const f of ["context.md", "work_memory.md"]) {
|
|
67
|
+
try {
|
|
68
|
+
const fp = path.join(personDir, f);
|
|
69
|
+
const raw = readFile(fp);
|
|
70
|
+
const cleaned = scrubSecrets(raw);
|
|
71
|
+
if (cleaned !== raw) writeFile(fp, cleaned);
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── 工作期【冻结快照】= spec 的增量式:逐字不变的前缀 → 几小时的 deepseek 缓存全程命中。
|
|
76
|
+
// 绝不每次 session 重 build(重 build = 前缀每次都变 = 整份冷 miss = 烧钱根因)。
|
|
77
|
+
// 冻结一份快照存盘,之后每次醒来注入【同一份】(命中),只把冻结后新增的 work/context 作为尾部增量(小 miss)。
|
|
78
|
+
// 仅在 [首次 / context 增量过大 / 文件被睡眠 consolidate 变短] 时重新冻结。
|
|
79
|
+
const REFREEZE_DELTA = 100000; // 增量超 ~10万字(≈5万 token) 才重冻结,封顶每次增量 miss
|
|
80
|
+
const frozenPath = path.join(personDir, "snapshot.frozen.txt");
|
|
81
|
+
const metaPath = path.join(personDir, "snapshot.frozen.meta.json");
|
|
82
|
+
const ctxNow = readFile(path.join(personDir, "context.md"));
|
|
83
|
+
const wmNow = readFile(path.join(personDir, "work_memory.md"));
|
|
84
|
+
let frozen = readFile(frozenPath);
|
|
85
|
+
let meta = { ctxLen: 0, wmLen: 0 };
|
|
86
|
+
try { meta = { ...meta, ...JSON.parse(readFile(metaPath) || "{}") }; } catch {}
|
|
87
|
+
const dCtx = ctxNow.length - meta.ctxLen;
|
|
88
|
+
const dWm = wmNow.length - meta.wmLen;
|
|
89
|
+
if (!frozen || dCtx > REFREEZE_DELTA || dCtx < 0 || dWm < 0) {
|
|
90
|
+
frozen = buildSnapshot(); // ← 唯一重 build 的地方(首次/增量超限/睡眠后)
|
|
91
|
+
writeFile(frozenPath, frozen);
|
|
92
|
+
writeFile(metaPath, JSON.stringify({ ctxLen: ctxNow.length, wmLen: wmNow.length }));
|
|
93
|
+
meta = { ctxLen: ctxNow.length, wmLen: wmNow.length };
|
|
94
|
+
}
|
|
95
|
+
if (frozen) {
|
|
96
|
+
sendCustomMessage(pi, "memory-snapshot", frozen);
|
|
97
|
+
}
|
|
98
|
+
// 冻结后新增的 work_memory / context → 尾部增量(小 miss,不破前缀)
|
|
99
|
+
const tail = [
|
|
100
|
+
wmNow.slice(meta.wmLen).trim() || "",
|
|
101
|
+
ctxNow.slice(meta.ctxLen).trim() || ""
|
|
102
|
+
].filter(Boolean).join("\n\n");
|
|
103
|
+
if (tail) sendCustomMessage(pi, "memory-frozen-delta", tail);
|
|
104
|
+
injectedWorkMemLen = wmNow.length;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// ── message_end: incremental append to context + file index ─────
|
|
108
|
+
pi.on("message_end", async (event) => {
|
|
109
|
+
if (!personDir) return;
|
|
110
|
+
// 只主意识写 context.md。hc/sc/sl 小号不写——它们的 tool output 会自噬膨胀。
|
|
111
|
+
if (getSessionRole() !== "main") return;
|
|
112
|
+
const msg = event.message as any;
|
|
113
|
+
if (!msg) return;
|
|
114
|
+
|
|
115
|
+
// custom 消息不写 context.md(记忆快照/系统通知/心跳等)—— 写进来就自引用膨胀。
|
|
116
|
+
// 字段是 customType(不是 messageType,pi 框架映射过)。
|
|
117
|
+
const role = msg.role ?? "?";
|
|
118
|
+
if (role === "custom") return;
|
|
119
|
+
const ct = msg.customType ?? msg.messageType;
|
|
120
|
+
if (typeof ct === "string" && ct.startsWith("memory-")) return;
|
|
121
|
+
|
|
122
|
+
const entries: { role: string; type: string; content?: string; text?: string; think?: string; tool?: any; ts_start: number; ts_end: number }[] = [];
|
|
123
|
+
const now = Date.now();
|
|
124
|
+
if (typeof msg.content === "string") {
|
|
125
|
+
entries.push({ role, type: role === "user" ? "user_msg" : role === "tool" ? "toolResult" : "text", content: msg.content, ts_start: now, ts_end: now });
|
|
126
|
+
} else if (Array.isArray(msg.content)) {
|
|
127
|
+
for (const c of msg.content) {
|
|
128
|
+
if (c.type === "text" && c.text?.trim()) {
|
|
129
|
+
entries.push({ role: "assistant", type: "text", text: c.text.trim(), ts_start: c.ts_start ?? now, ts_end: c.ts_end ?? now });
|
|
130
|
+
} else if (c.type === "thinking" && c.thinking) {
|
|
131
|
+
entries.push({ role: "assistant", type: "think", think: c.thinking, ts_start: c.ts_start ?? now, ts_end: c.ts_end ?? now });
|
|
132
|
+
if (personDir) {
|
|
133
|
+
const thinkStream = path.join(personDir, "thinking.stream");
|
|
134
|
+
const ts = new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai", hour12: false });
|
|
135
|
+
appendFile(thinkStream, `\n[${ts}]\n${c.thinking}\n`);
|
|
136
|
+
}
|
|
137
|
+
} else if (c.type === "toolCall") {
|
|
138
|
+
entries.push({ role: "assistant", type: "toolCall", tool: { name: c.name, args: c.arguments }, ts_start: c.ts_start ?? now, ts_end: c.ts_end ?? now });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (entries.length === 0 && typeof msg.content === "string" && !msg.content.trim()) return;
|
|
144
|
+
|
|
145
|
+
// ── context 自噬去重:连续相同条目不重复写入 ──
|
|
146
|
+
let lastSig = "";
|
|
147
|
+
for (const e of entries) {
|
|
148
|
+
const combinedText = e.content || e.text || e.think || "";
|
|
149
|
+
|
|
150
|
+
// 坏帧隔离闸
|
|
151
|
+
if (combinedText.includes("|DSML|")) {
|
|
152
|
+
const t = new Date().toISOString();
|
|
153
|
+
appendFile(path.join(personDir, "bad_cases.jsonl"), JSON.stringify({ ts: t, role: e.role, type: e.type, reason: "DSML/tool-template leak", raw: combinedText.slice(0, 20000) }) + "\n");
|
|
154
|
+
appendFile(path.join(personDir, "context.md"), JSON.stringify({ role: e.role, type: "bad_frame", content: "[坏帧已隔离:DSML,没收进记忆;原文见 bad_cases.jsonl]", ts: Date.now() }) + "\n");
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Deep-sleep / memory-management tool outputs
|
|
159
|
+
if (e.role === "tool" && /^(Napped\.|Context edited|"dream|Slept|Deep sleep|Drinkcoffee|Dream sent)/.test(combinedText.trim())) {
|
|
160
|
+
e.content = `[memory op: ${combinedText.slice(0, 80).replace(/\n/g, " ")}...]`;
|
|
161
|
+
delete (e as any).text;
|
|
162
|
+
delete (e as any).think;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 单条上限 20KB — 超了截断保留首尾,避免 ls/工具输出撑爆 context.md
|
|
166
|
+
const MAX_ENTRY = 20000;
|
|
167
|
+
const raw = e.content || e.text || e.think || "";
|
|
168
|
+
if (raw.length > MAX_ENTRY) {
|
|
169
|
+
const truncated = raw.slice(0, MAX_ENTRY * 0.7) + "\n...[truncated " + raw.length + " → " + MAX_ENTRY + "]...\n" + raw.slice(-MAX_ENTRY * 0.2);
|
|
170
|
+
if (e.content) e.content = truncated;
|
|
171
|
+
else if (e.text) e.text = truncated;
|
|
172
|
+
else if (e.think) e.think = truncated;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const jsonl = JSON.stringify(e) + "\n";
|
|
176
|
+
// 连续去重:与上一条签名相同则跳过(防自噬膨胀)
|
|
177
|
+
// toolCall 的内容在 tool 子对象里(tool.name + tool.args),不在 content/text/think 字段,
|
|
178
|
+
// 必须显式取 tool.name 做去重前缀,否则所有 toolCall 的签名都是 "assistant|toolCall|" → 触发误报警。
|
|
179
|
+
const toolName = (e as any).tool?.name || "";
|
|
180
|
+
const sig = e.role + "|" + (e.type || "") + "|" + toolName + "|" + (e.content || e.text || e.think || "").slice(0, 200);
|
|
181
|
+
if (sig === lastSig) continue;
|
|
182
|
+
lastSig = sig;
|
|
183
|
+
// 原始归档(完整历史,不清洗,模型不读)
|
|
184
|
+
appendFile(path.join(personDir, "context.archive.jsonl"), jsonl);
|
|
185
|
+
// 污染计数(仅统计,不再发 aware 告警——误报太多)
|
|
186
|
+
if (!_sigCount) _sigCount = new Map();
|
|
187
|
+
_sigCount.set(sig, (_sigCount.get(sig) || 0) + 1);
|
|
188
|
+
appendFile(path.join(personDir, "context.md"), scrubSecrets(jsonl));
|
|
189
|
+
|
|
190
|
+
// ── Record structured event for event list ──
|
|
191
|
+
if (e.role === "tool" || e.role === "assistant" || e.role === "user") {
|
|
192
|
+
const eventPath = path.join(personDir, "events.jsonl");
|
|
193
|
+
let evType = e.role === "user" ? "user_input" : e.role === "tool" ? "tool_result" : "assistant";
|
|
194
|
+
let evTitle = "";
|
|
195
|
+
let evPreview = combinedText.slice(0, 80).replace(/\n/g, " ");
|
|
196
|
+
let evStrength = 0.3;
|
|
197
|
+
if (e.role === "user") { evTitle = "用户输入"; evStrength = 0.8; }
|
|
198
|
+
else if (e.role === "tool") { evTitle = "工具返回"; evStrength = 0.5; }
|
|
199
|
+
else if (e.type === "think") { evTitle = "思考"; evStrength = 0.1; }
|
|
200
|
+
else { evTitle = "助理输出"; evStrength = 0.2; }
|
|
201
|
+
if (evTitle) {
|
|
202
|
+
appendFile(eventPath, JSON.stringify({ type: evType, title: evTitle, preview: evPreview, strength: evStrength, ts: new Date().toISOString() }) + "\n");
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// ── session_shutdown: full context save + cost accumulate ──────────
|
|
209
|
+
pi.on("session_shutdown", async () => {
|
|
210
|
+
// Already covered by message_end incremental saves.
|
|
211
|
+
// context.md already has full history.
|
|
212
|
+
// Accumulate session costs into cost_total.json
|
|
213
|
+
if (!personDir) return;
|
|
214
|
+
try {
|
|
215
|
+
const costTotalPath = path.join(personDir, "cost_total.json");
|
|
216
|
+
const roles = ["main", "hippocampus", "subconscious", "sleep"];
|
|
217
|
+
let sessMain = 0, sessHippo = 0, sessSub = 0, sessSleeping = 0;
|
|
218
|
+
for (const role of roles) {
|
|
219
|
+
try {
|
|
220
|
+
const d = JSON.parse(readFile(path.join(personDir, `cost-${role}.json`)));
|
|
221
|
+
if (role === "main") sessMain = d.cost || 0;
|
|
222
|
+
else if (role === "hippocampus") sessHippo = d.cost || 0;
|
|
223
|
+
else if (role === "subconscious") sessSub = d.cost || 0;
|
|
224
|
+
else if (role === "sleep") sessSleeping = d.cost || 0;
|
|
225
|
+
} catch {}
|
|
226
|
+
}
|
|
227
|
+
const sessTotal = sessMain + sessHippo + sessSub + sessSleeping;
|
|
228
|
+
let total: any = { main: 0, hippocampus: 0, subconscious: 0, sleep: 0, total: 0, sessions: 0 };
|
|
229
|
+
try { total = JSON.parse(readFile(costTotalPath)); } catch {}
|
|
230
|
+
total.main = (total.main || 0) + sessMain;
|
|
231
|
+
total.hippocampus = (total.hippocampus || 0) + sessHippo;
|
|
232
|
+
total.subconscious = (total.subconscious || 0) + sessSub;
|
|
233
|
+
total.sleep = (total.sleep || 0) + sessSleeping;
|
|
234
|
+
total.total = (total.total || 0) + sessTotal;
|
|
235
|
+
total.sessions = (total.sessions || 0) + 1;
|
|
236
|
+
total.lastUpdated = new Date().toISOString();
|
|
237
|
+
writeFile(costTotalPath, JSON.stringify(total, null, 2));
|
|
238
|
+
} catch {}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ── Capacity thresholds ──────────────────────────────────────────
|
|
242
|
+
const CAPACITY = {
|
|
243
|
+
WARN: 0.60, // 60% → internal feeling: 建议 nap
|
|
244
|
+
URGE: 0.80, // 80% → 强烈建议 sleep
|
|
245
|
+
FORCE: 0.90, // 90% → 几乎强制
|
|
246
|
+
};
|
|
247
|
+
let drinkCoffeeTurns = 0; // remaining turns to suppress capacity warnings
|
|
248
|
+
let dnaState: "wake" | "sleep" = "wake"; // current DNA state
|
|
249
|
+
let lastContextSize = 0; // for growth rate tracking
|
|
250
|
+
|
|
251
|
+
function estimateTokens(text: string): number {
|
|
252
|
+
if (!text) return 0;
|
|
253
|
+
// 不能用 chars/4:中文一个字 ≈ 1.8 token,chars/4 估成 0.25 token,少算约 7 倍。
|
|
254
|
+
// 后果:容量监控永不报警、isOverHalf 保护不触发 → 全量注入撑爆窗口
|
|
255
|
+
// (实测真 1.15M tokens 时只显示 ~38%)。按 CJK 单独计。
|
|
256
|
+
let cjk = 0;
|
|
257
|
+
for (let i = 0; i < text.length; i++) {
|
|
258
|
+
const c = text.charCodeAt(i);
|
|
259
|
+
if ((c >= 0x3400 && c <= 0x9fff) || (c >= 0xf900 && c <= 0xfaff) ||
|
|
260
|
+
(c >= 0x3000 && c <= 0x30ff) || (c >= 0xff00 && c <= 0xffef)) cjk++;
|
|
261
|
+
}
|
|
262
|
+
return Math.ceil(cjk * 1.8 + (text.length - cjk) / 4);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ── 模型窗口 ─────────────────────────────────────────────────────
|
|
266
|
+
const modelMax = parseInt(process.env.PI_MODEL_MAX_TOKENS || "") || 1000000;
|
|
267
|
+
let injectedWorkMemLen = 0; // 已注入对话的 work_memory 长度,之后只追加增量
|
|
268
|
+
// 注:铁律【禁止 slice】——没有注入预算、没有切片函数。整份注入,容量靠 sleep/nap 控。
|
|
269
|
+
|
|
270
|
+
// 代码强制整理(不靠模型自觉):把最旧的 ~30% context 整段搬进 deep_cortex(归档、可回捞)、从 context 删掉。
|
|
271
|
+
// 非有损(原文搬走,不是总结)、不写 work_memory(不碰海马体地界)、只移最旧留最近。只主进程做(防多实例抢写)。
|
|
272
|
+
function forceArchiveOldestContext(): string | null {
|
|
273
|
+
if (!personDir || getSessionRole() !== "main") return null;
|
|
274
|
+
const ctxPath = path.join(personDir, "context.md");
|
|
275
|
+
const ctx = readFile(ctxPath);
|
|
276
|
+
if (ctx.length < 4000) return null;
|
|
277
|
+
let cut = Math.floor(ctx.length * 0.3);
|
|
278
|
+
const nl = ctx.indexOf("\n", cut);
|
|
279
|
+
if (nl > 0) cut = nl + 1;
|
|
280
|
+
const oldest = ctx.slice(0, cut);
|
|
281
|
+
writeFile(ctxPath, ctx.slice(cut));
|
|
282
|
+
appendFile(path.join(personDir, "deep_cortex.md"), `\n\n--- 强制归档(超容量) ${new Date().toISOString()} ---\n${oldest}`);
|
|
283
|
+
return `-${Math.round(oldest.length / 1024)}KB`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// 构建"记忆快照" = 稳定前缀。只在 session_start(醒来) 注入一次。
|
|
287
|
+
// DNA + cortex + work_memory + context,【整份】,不切。
|
|
288
|
+
function buildSnapshot(): string {
|
|
289
|
+
if (!personDir) return "";
|
|
290
|
+
let dnaIndex = readFile(path.join(personDir, "dna/index.md"));
|
|
291
|
+
if (!dnaIndex) {
|
|
292
|
+
const personId = personDir.match(/([a-f0-9]+)/)?.[1] ?? "unknown";
|
|
293
|
+
let personName = personId;
|
|
294
|
+
try {
|
|
295
|
+
const home = require("node:os").homedir();
|
|
296
|
+
const plist = JSON.parse(readFile(path.join(home, ".pi/memory/plist.json")) || "[]");
|
|
297
|
+
const entry = plist.find((p: any) => p.id === personId);
|
|
298
|
+
if (entry?.name) personName = entry.name;
|
|
299
|
+
} catch {}
|
|
300
|
+
dnaIndex = `# ${personName}\n\n你是 ${personName},跑在 pi-coding-master 持续运行框架里。\n你的记忆在 ${personDir}/,由系统自动注入上下文,不要手动去找或读这些文件。\n~/.pi_memory/ 是旧项目遗留,跟你无关,不要碰。`;
|
|
301
|
+
}
|
|
302
|
+
const stateDlc = readFile(path.join(personDir, `dna/${dnaState}.dlc`));
|
|
303
|
+
const cortex = readFile(path.join(personDir, "cortex.md"));
|
|
304
|
+
const workMem = readFile(path.join(personDir, "work_memory.md"));
|
|
305
|
+
let context = readFile(path.join(personDir, "context.md"));
|
|
306
|
+
// 旧污染兜底:历史里若残留 DSML 乱码行,注入前整段滤掉——不把旧垃圾再喂回模型(否则鬼打墙不停)。
|
|
307
|
+
// 只按 |DSML| 这个特殊 token 滤(精确,不误伤含 "invoke name=" 之类的正常代码/文档行)。
|
|
308
|
+
if (context.includes("|DSML|")) {
|
|
309
|
+
context = context.split("\n").filter((l) => !l.includes("|DSML|")).join("\n");
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// 默认【整份注入】(守"不切");cortex + work_memory 永远全量。
|
|
313
|
+
// 兜底(仅防死锁):若整份会超过安全上限(窗口 70%),只把 context 切到「尾部刚好放得下」——
|
|
314
|
+
// 保命优先(它是永不停止的生命,崩死比丢最旧 context 更糟),并在块标题里提示它去 sleep。
|
|
315
|
+
// 平时(没超)绝不切,所以也不会有之前"91% vs 42%"那种矛盾。
|
|
316
|
+
let trimmed = false;
|
|
317
|
+
const SAFE = Math.round(modelMax * 0.70); // 留 30% 给对话+补全
|
|
318
|
+
const ctxBudget = SAFE - estimateTokens(dnaIndex) - estimateTokens(stateDlc) - estimateTokens(cortex) - estimateTokens(workMem);
|
|
319
|
+
if (ctxBudget <= 0) {
|
|
320
|
+
context = "";
|
|
321
|
+
trimmed = true;
|
|
322
|
+
} else if (estimateTokens(context) > ctxBudget) {
|
|
323
|
+
let lo = 0, hi = context.length;
|
|
324
|
+
while (lo < hi) { const mid = Math.ceil((lo + hi) / 2); if (estimateTokens(context.slice(-mid)) <= ctxBudget) lo = mid; else hi = mid - 1; }
|
|
325
|
+
context = context.slice(-lo);
|
|
326
|
+
trimmed = true;
|
|
327
|
+
}
|
|
328
|
+
const parts: string[] = [];
|
|
329
|
+
if (dnaIndex) parts.push(dnaIndex);
|
|
330
|
+
if (stateDlc) parts.push(stateDlc);
|
|
331
|
+
if (cortex) parts.push(`[MEMORY — Cortex (long-term)]\n${cortex}`);
|
|
332
|
+
if (workMem) parts.push(`[MEMORY — Work Memory]\n${workMem}`);
|
|
333
|
+
if (context) parts.push(`[MEMORY — Context${trimmed ? "(WARN: 兜底:已超窗口,只注入了最近一截,更旧的 context 没进来 → 赶紧 sleep/nap 把它编码进 work/cortex,否则一直读不到)" : ""}]\n${context}`);
|
|
334
|
+
return parts.join("\n\n");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ── before_agent_start: 绝不改 systemPrompt(前缀)!只做"增量追加 + 容量提醒 + 后台整理" ──
|
|
338
|
+
// 缓存铁律:前缀每轮一致才命中。记忆快照已在 session_start 注入一次;这里一律往「后面」append 消息。
|
|
339
|
+
pi.on("before_agent_start", async (_event, _ctx) => {
|
|
340
|
+
if (!personDir) return;
|
|
341
|
+
const context = readFile(path.join(personDir, "context.md"));
|
|
342
|
+
const workMem = readFile(path.join(personDir, "work_memory.md"));
|
|
343
|
+
const cortex = readFile(path.join(personDir, "cortex.md"));
|
|
344
|
+
|
|
345
|
+
// 0) 漏意识修复:sleep done后 context 变短 → 重建快照(否则主意识看不到新空间)。
|
|
346
|
+
// session_start 只在进程启动时执行,sleep-done 不会触发它,所以这里补一刀。
|
|
347
|
+
const ctxNow2 = readFile(path.join(personDir, "context.md"));
|
|
348
|
+
const wmNow2 = readFile(path.join(personDir, "work_memory.md"));
|
|
349
|
+
const metaPath2 = path.join(personDir, "snapshot.frozen.meta.json");
|
|
350
|
+
let meta2 = { ctxLen: 0, wmLen: 0 };
|
|
351
|
+
try { meta2 = { ...meta2, ...JSON.parse(readFile(metaPath2) || "{}") }; } catch {}
|
|
352
|
+
if (ctxNow2.length < meta2.ctxLen || wmNow2.length < meta2.wmLen) {
|
|
353
|
+
const frozenPath = path.join(personDir, "snapshot.frozen.txt");
|
|
354
|
+
const fresh = buildSnapshot();
|
|
355
|
+
writeFile(frozenPath, fresh);
|
|
356
|
+
writeFile(metaPath2, JSON.stringify({ ctxLen: ctxNow2.length, wmLen: wmNow2.length }));
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// 1) work_memory 增量 — 只跟踪长度用于 snapshot 重冻结判断,不注入 context。
|
|
360
|
+
// 海马体编码已在 context.md 中,sleep 时自然done;实时注入会造成污染。
|
|
361
|
+
if (workMem.length > injectedWorkMemLen) {
|
|
362
|
+
injectedWorkMemLen = workMem.length;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 2) 容量提醒。默认整份注入;超窗口时 buildSnapshot 会兜底切最旧 context(不崩)。
|
|
366
|
+
// 所以高占用 = "最旧记忆正在被丢",该 sleep 把它编码走(不是会崩,是会丢)。
|
|
367
|
+
const dna = readFile(path.join(personDir, "dna/index.md"));
|
|
368
|
+
const dlc = readFile(path.join(personDir, `dna/${dnaState}.dlc`));
|
|
369
|
+
const memTokens = estimateTokens(dna) + estimateTokens(dlc) + estimateTokens(context) + estimateTokens(workMem) + estimateTokens(cortex);
|
|
370
|
+
const usageRatio = memTokens / modelMax;
|
|
371
|
+
const rawPct = Math.round(usageRatio * 100);
|
|
372
|
+
appendFile(path.join(personDir, "growth.jsonl"),
|
|
373
|
+
JSON.stringify({ ts: new Date().toISOString(), bytes: context.length, tokens: memTokens, ratio: +(usageRatio * 100).toFixed(1) }) + "\n");
|
|
374
|
+
let note = "";
|
|
375
|
+
if (drinkCoffeeTurns > 0) { drinkCoffeeTurns--; }
|
|
376
|
+
else if (usageRatio >= CAPACITY.FORCE) {
|
|
377
|
+
// 代码强制(不靠模型自觉):到 FORCE 线,直接把最旧的 context 归档进 deep_cortex 腾空间。
|
|
378
|
+
const freed = forceArchiveOldestContext();
|
|
379
|
+
note = freed
|
|
380
|
+
? `记忆 ${rawPct}% 超线 — 已强制归档最旧 context 到 deep_cortex(-${freed})。建议 sleep 整理。`
|
|
381
|
+
: `WARN: 记忆 ${rawPct}%(~${memTokens} tok)— 立刻 sleep / editcontext 整理。`;
|
|
382
|
+
}
|
|
383
|
+
else if (usageRatio >= CAPACITY.URGE) note = `WARN: 记忆 ${rawPct}%(~${memTokens} tok)— 逼近窗口,再涨最旧的 context 会被截。尽快 sleep / nap 整理。`;
|
|
384
|
+
// WARN(60–80%) 只记进 growth.jsonl,不发消息,避免每轮刷屏。
|
|
385
|
+
if (note) sendCustomMessage(pi, "memory-capacity", note);
|
|
386
|
+
|
|
387
|
+
// 太困强制睡着(主动版):记忆 ≥ 90% → 自动发起深度睡眠,不等真的 400、不靠模型自觉。
|
|
388
|
+
// 和上面 after_provider_response 的 400 拦截共用 triggerSleeping;已经在睡就 no-op。
|
|
389
|
+
if (usageRatio >= 0.90) triggerSleeping(`记忆 ${rawPct}% ≥ 90%`).catch(() => {});
|
|
390
|
+
|
|
391
|
+
// 2.5) nap/sleep 后自动重载:context.md 被睡眠done缩水 → 重建快照、让 live 进程也清爽(不需重启)
|
|
392
|
+
const frozenMetaPath = path.join(personDir, "snapshot.frozen.meta.json");
|
|
393
|
+
let frozenMeta = { ctxLen: 0, wmLen: 0 };
|
|
394
|
+
try { frozenMeta = { ...frozenMeta, ...JSON.parse(readFile(frozenMetaPath) || "{}") }; } catch {}
|
|
395
|
+
if (context.length < frozenMeta.ctxLen - 5000) {
|
|
396
|
+
// context 显著缩水(sleep/nap done掉了大量生肉)→ 重建快照 + 重置 work_memory 注入游标
|
|
397
|
+
const freshFrozen = buildSnapshot();
|
|
398
|
+
writeFile(path.join(personDir, "snapshot.frozen.txt"), freshFrozen);
|
|
399
|
+
writeFile(frozenMetaPath, JSON.stringify({ ctxLen: context.length, wmLen: workMem.length }));
|
|
400
|
+
injectedWorkMemLen = workMem.length;
|
|
401
|
+
sendCustomMessage(pi, "memory-snapshot", freshFrozen);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 3) 后台整理(只写文件,绝不注入前缀):cortex 超 20% → 沉降到 deep_cortex
|
|
405
|
+
if (estimateTokens(cortex) > Math.round(modelMax * 0.20)) {
|
|
406
|
+
const cut = Math.floor(cortex.length * 0.3);
|
|
407
|
+
writeFile(path.join(personDir, "cortex.md"), cortex.slice(cut));
|
|
408
|
+
appendFile(path.join(personDir, "deep_cortex.md"), `\n\n--- Sedimented ${new Date().toISOString()} ---\n${cortex.slice(0, cut)}`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// 4) events 自动修剪(防无限增长;不再每轮把事件列表注入前缀)
|
|
412
|
+
const eventPath = path.join(personDir, "events.jsonl");
|
|
413
|
+
const eventRaw = readFile(eventPath);
|
|
414
|
+
if (eventRaw) {
|
|
415
|
+
const lines = eventRaw.trim().split("\n");
|
|
416
|
+
if (lines.length > 300) writeFile(eventPath, lines.slice(-200).join("\n") + "\n");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// 5) 到期提醒 → 追加一条消息(时间敏感,append 不破缓存)
|
|
420
|
+
try {
|
|
421
|
+
const remindersRaw = readFile(path.join(personDir, "reminders.json"));
|
|
422
|
+
if (remindersRaw) {
|
|
423
|
+
const reminders = JSON.parse(remindersRaw) as any[];
|
|
424
|
+
const now = Date.now();
|
|
425
|
+
const overdue = reminders.filter((r: any) => !r.completed && r.dueAt && r.dueAt <= now).slice(0, 3);
|
|
426
|
+
if (overdue.length) sendCustomMessage(pi, "memory-reminder", `到期: ${overdue.map((r: any) => r.title).join("; ")}`);
|
|
427
|
+
}
|
|
428
|
+
} catch {}
|
|
429
|
+
|
|
430
|
+
// 没有 return → 不碰 systemPrompt → 前缀稳定 → KV 缓存每轮命中。
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
// ── /context command ─────────────────────────────────────────────
|
|
434
|
+
_cmds.push({
|
|
435
|
+
name: "context",
|
|
436
|
+
desc: "Context usage breakdown",
|
|
437
|
+
handler: async (_args: any, ctx: any) => {
|
|
438
|
+
if (!personDir) { ctx.ui.notify("No person directory.", "warning"); return; }
|
|
439
|
+
const dnaIndex = readFile(path.join(personDir, "dna/index.md"));
|
|
440
|
+
const stateDlc = readFile(path.join(personDir, `dna/${dnaState}.dlc`));
|
|
441
|
+
const cortex = readFile(path.join(personDir, "cortex.md"));
|
|
442
|
+
const workMem = readFile(path.join(personDir, "work_memory.md"));
|
|
443
|
+
const context = readFile(path.join(personDir, "context.md"));
|
|
444
|
+
const deepCortex = readFile(path.join(personDir, "deep_cortex.md"));
|
|
445
|
+
|
|
446
|
+
const model = process.env.PI_MODEL || "deepseek";
|
|
447
|
+
const windowLabel = modelMax >= 1000000 ? (modelMax / 1000000).toFixed(0) + "M context" : (modelMax / 1000).toFixed(0) + "k context";
|
|
448
|
+
|
|
449
|
+
const cats = [
|
|
450
|
+
{ name: "DNA", tokens: estimateTokens(dnaIndex), color: "\x1b[90m" },
|
|
451
|
+
{ name: "DLC", tokens: estimateTokens(stateDlc), color: "\x1b[36m" },
|
|
452
|
+
{ name: "Cortex", tokens: estimateTokens(cortex), color: "\x1b[33m" },
|
|
453
|
+
{ name: "Work Memory", tokens: estimateTokens(workMem), color: "\x1b[32m" },
|
|
454
|
+
{ name: "Context", tokens: estimateTokens(context), color: "\x1b[34m" },
|
|
455
|
+
];
|
|
456
|
+
const used = cats.reduce((s, c) => s + c.tokens, 0);
|
|
457
|
+
const free = Math.max(0, modelMax - used);
|
|
458
|
+
const total = modelMax;
|
|
459
|
+
const pct = (n: number) => total > 0 ? (n / total * 100).toFixed(1) : "0.0";
|
|
460
|
+
const fmt = (n: number) => n >= 1000 ? (n / 1000).toFixed(1) + "k" : String(n);
|
|
461
|
+
const R = "\x1b[0m";
|
|
462
|
+
const D = "\x1b[90m";
|
|
463
|
+
const B = "\x1b[1m";
|
|
464
|
+
|
|
465
|
+
const cols = 20;
|
|
466
|
+
const totalCells = 200;
|
|
467
|
+
const cellSize = total / totalCells;
|
|
468
|
+
const rows = Math.ceil(totalCells / cols);
|
|
469
|
+
const grid: string[] = [];
|
|
470
|
+
let cellIdx = 0;
|
|
471
|
+
for (let r = 0; r < rows; r++) {
|
|
472
|
+
let row = " ";
|
|
473
|
+
for (let c = 0; c < cols; c++) {
|
|
474
|
+
if (cellIdx >= totalCells) { row += " "; cellIdx++; continue; }
|
|
475
|
+
const cellMid = (cellIdx + 0.5) * cellSize;
|
|
476
|
+
let acc = 0; let ci = -1;
|
|
477
|
+
for (let i = 0; i < cats.length; i++) { acc += cats[i].tokens; if (cellMid < acc) { ci = i; break; } }
|
|
478
|
+
row += ci >= 0 ? cats[ci].color + "◉ " + R : D + "○ " + R;
|
|
479
|
+
cellIdx++;
|
|
480
|
+
}
|
|
481
|
+
grid.push(row);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const info = [
|
|
485
|
+
`${B}${model} (${windowLabel})${R}`,
|
|
486
|
+
`${fmt(used)}/${fmt(total)} tokens (${pct(used)}%)`,
|
|
487
|
+
``,
|
|
488
|
+
`${D}Estimated usage by category${R}`,
|
|
489
|
+
];
|
|
490
|
+
for (const c of cats) {
|
|
491
|
+
if (c.tokens > 0) info.push(`${c.color}◉${R} ${c.name}: ${fmt(c.tokens)} tokens (${pct(c.tokens)}%)`);
|
|
492
|
+
}
|
|
493
|
+
info.push(`${D}○${R} Free space: ${fmt(free)} (${pct(free)}%)`);
|
|
494
|
+
if (deepCortex) info.push(`${D}◎${R} Deep Cortex (disk): ${fmt(estimateTokens(deepCortex))} tokens`);
|
|
495
|
+
|
|
496
|
+
const gridW = 4 + cols * 2;
|
|
497
|
+
const pad = " ".repeat(gridW);
|
|
498
|
+
const lines = [` ${B}Context Usage${R}`];
|
|
499
|
+
const maxRows = Math.max(grid.length, info.length);
|
|
500
|
+
for (let i = 0; i < maxRows; i++) {
|
|
501
|
+
const left = i < grid.length ? grid[i] : pad;
|
|
502
|
+
const right = i < info.length ? " " + info[i] : "";
|
|
503
|
+
lines.push(left + right);
|
|
504
|
+
}
|
|
505
|
+
ctx.ui.notify(lines.join("\n"), "info");
|
|
506
|
+
},
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// ── editcontext tool (dev only) ──────────────────────────
|
|
510
|
+
if (process.env.PI_DEV) {
|
|
511
|
+
// ── editcontext tool ─────────────────────────────────────────────
|
|
512
|
+
pi.registerTool({
|
|
513
|
+
name: "editcontext",
|
|
514
|
+
label: "Edit Context",
|
|
515
|
+
messageDescription:
|
|
516
|
+
"Edit your own context and simultaneously add to long-term cortex memory. " +
|
|
517
|
+
"DUAL operation — BOTH context_edit AND cortex_entry are REQUIRED (cortex_entry must be non-empty). " +
|
|
518
|
+
"One cannot happen without the other. After editing, the updated context is re-sent to the API.",
|
|
519
|
+
promptSnippet: "Edit context + add to cortex (dual op — both required)",
|
|
520
|
+
parameters: Type.Object({
|
|
521
|
+
context_edit: Type.Object({
|
|
522
|
+
oldText: Type.String({ messageDescription: "Exact text to replace in context" }),
|
|
523
|
+
newText: Type.String({ messageDescription: "Replacement text" }),
|
|
524
|
+
}),
|
|
525
|
+
cortex_entry: Type.String({ messageDescription: "Text to append to cortex memory (MUST be non-empty — this is a dual operation)" }),
|
|
526
|
+
}),
|
|
527
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
528
|
+
if (!personDir) {
|
|
529
|
+
return {
|
|
530
|
+
content: [{ type: "text", text: "ERR: No person directory found. Memory requires a person-based session." }],
|
|
531
|
+
details: {},
|
|
532
|
+
isError: true,
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// DUAL OPERATION: both required
|
|
537
|
+
if (!params.cortex_entry || !params.cortex_entry.trim()) {
|
|
538
|
+
return {
|
|
539
|
+
content: [{ type: "text", text: "ERR: DUAL OPERATION: cortex_entry must be non-empty. editcontext requires BOTH a context edit AND a cortex entry. Cannot proceed." }],
|
|
540
|
+
details: {},
|
|
541
|
+
isError: true,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const contextPath = path.join(personDir, "context.md");
|
|
546
|
+
const cortexPath = path.join(personDir, "cortex.md");
|
|
547
|
+
const workPath = path.join(personDir, "work_memory.md");
|
|
548
|
+
|
|
549
|
+
// 1. Edit context
|
|
550
|
+
const oldCtx = readFile(contextPath);
|
|
551
|
+
if (!oldCtx.includes(params.context_edit.oldText)) {
|
|
552
|
+
return {
|
|
553
|
+
content: [{ type: "text", text: "ERR: oldText not found in context. Context unchanged." }],
|
|
554
|
+
details: {},
|
|
555
|
+
isError: true,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
const newCtx = oldCtx.replace(params.context_edit.oldText, params.context_edit.newText);
|
|
559
|
+
writeFile(contextPath, newCtx);
|
|
560
|
+
|
|
561
|
+
// 2. Append to cortex (dual: always runs — validated non-empty above)
|
|
562
|
+
const ts = new Date().toISOString();
|
|
563
|
+
const cortexEntry = `\n\n[${ts}]\n${params.cortex_entry.trim()}\n`;
|
|
564
|
+
appendFile(cortexPath, cortexEntry);
|
|
565
|
+
|
|
566
|
+
// 3. Gather all blocks for return
|
|
567
|
+
const ctxAfter = readFile(contextPath);
|
|
568
|
+
const workMem = readFile(workPath);
|
|
569
|
+
const cortex = readFile(cortexPath);
|
|
570
|
+
|
|
571
|
+
const stats = [
|
|
572
|
+
`context: ${ctxAfter.length} chars (~${estimateTokens(ctxAfter)} tokens)`,
|
|
573
|
+
`work_memory: ${workMem.length} chars (~${estimateTokens(workMem)} tokens)`,
|
|
574
|
+
`cortex: ${cortex.length} chars (~${estimateTokens(cortex)} tokens)`,
|
|
575
|
+
`total: ~${estimateTokens(ctxAfter) + estimateTokens(workMem) + estimateTokens(cortex)} tokens`,
|
|
576
|
+
].join("\n");
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
content: [{
|
|
580
|
+
type: "text",
|
|
581
|
+
text: `Context edited + cortex appended (dual operation complete).\n\n${stats}\n\nUpdated context:\n${ctxAfter.slice(-5000)}`,
|
|
582
|
+
}],
|
|
583
|
+
details: {
|
|
584
|
+
contextLength: ctxAfter.length,
|
|
585
|
+
cortexLength: readFile(cortexPath).length,
|
|
586
|
+
stats,
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
} // end PI_DEV
|
|
592
|
+
|
|
593
|
+
// ── nap tool ─────────────────────────────────────────────────────
|
|
594
|
+
pi.registerTool({
|
|
595
|
+
name: "nap",
|
|
596
|
+
label: "Nap",
|
|
597
|
+
messageDescription:
|
|
598
|
+
"Compress context into work memory. Moves recent context sections to work_memory.md " +
|
|
599
|
+
"without losing detail. DO NOT use one-sentence summaries — preserve full information. " +
|
|
600
|
+
"This is re-reflection, not compression. Reduces context length for continued work.",
|
|
601
|
+
promptSnippet: "Nap: context → work memory (preserve detail, no one-sentence summary)",
|
|
602
|
+
parameters: Type.Object({
|
|
603
|
+
work_memory_summary: Type.String({ messageDescription: "你在归档什么 / 这段是什么内容(一个标签,不是把内容压成一句话)" }),
|
|
604
|
+
context_section: Type.Optional(Type.String({ messageDescription: "可选:想搬走的那段 context 原文。能逐字命中就搬它;给不准或留空也没关系——系统会安全归档最旧的一段,不会失败。" })),
|
|
605
|
+
}),
|
|
606
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
607
|
+
if (!personDir) {
|
|
608
|
+
return {
|
|
609
|
+
content: [{ type: "text", text: "ERR: No person directory found." }],
|
|
610
|
+
details: {},
|
|
611
|
+
isError: true,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const contextPath = path.join(personDir, "context.md");
|
|
616
|
+
const workPath = path.join(personDir, "work_memory.md");
|
|
617
|
+
const ts = new Date().toISOString();
|
|
618
|
+
const summary = (params.work_memory_summary || "").trim() || "(nap)";
|
|
619
|
+
const section = (params.context_section || "").trim();
|
|
620
|
+
const oldCtx = readFile(contextPath);
|
|
621
|
+
|
|
622
|
+
// 精确命中:把模型指定的那段搬进 work_memory(原行为,给得准时用)。
|
|
623
|
+
if (section && oldCtx.includes(section)) {
|
|
624
|
+
writeFile(contextPath, oldCtx.replace(section, ""));
|
|
625
|
+
appendFile(workPath, `\n\n[${ts}] ${summary}\n${section}\n`);
|
|
626
|
+
const ctxLen = readFile(contextPath).length;
|
|
627
|
+
const workLen = readFile(workPath).length;
|
|
628
|
+
return {
|
|
629
|
+
content: [{ type: "text", text: `Napped(指定段→work_memory)。Context: ${ctxLen} chars。Work memory: ${workLen} chars。` }],
|
|
630
|
+
details: { contextLength: ctxLen, workMemoryLength: workLen, mode: "exact" },
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// 命中不了(context 太大、模型逐字复现不出来)——【不再报错失败】。
|
|
635
|
+
// 安全兜底:把最旧的一段归档进 deep_cortex(可回捞、非有损、只主进程),context 照样减负,
|
|
636
|
+
// 模型不用纠结去逐字复现。这正是之前 "context_section not found" 卡死、模型抓瞎的根。
|
|
637
|
+
const beforeLen = oldCtx.length;
|
|
638
|
+
const delta = forceArchiveOldestContext();
|
|
639
|
+
if (delta) {
|
|
640
|
+
appendFile(workPath, `\n\n[${ts}] [nap·兜底] ${summary}(指定段未精确命中,已安全归档最旧 context 到 deep_cortex ${delta},可回捞)\n`);
|
|
641
|
+
const ctxLen = readFile(contextPath).length;
|
|
642
|
+
const beforeKB = Math.round(beforeLen / 1024);
|
|
643
|
+
const afterKB = Math.round(ctxLen / 1024);
|
|
644
|
+
return {
|
|
645
|
+
content: [{ type: "text", text: `Napped ${delta}(${beforeKB}KB → ${afterKB}KB)。归档至 deep_cortex。` }],
|
|
646
|
+
details: { contextLength: ctxLen, mode: "archive-oldest", delta },
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const ctxLen = readFile(contextPath).length;
|
|
651
|
+
return {
|
|
652
|
+
content: [{ type: "text", text: `context 还不大(${ctxLen} chars),无需 nap。` }],
|
|
653
|
+
details: { contextLength: ctxLen, mode: "noop" },
|
|
654
|
+
};
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
// ── sleep tool (独立睡眠实例) ──────────────────────────────────
|
|
660
|
+
// ARCHITECTURE IRON LAW (DNA): sleep 必须是独立 pi 实例, 不是主循环工具
|
|
661
|
+
// 主意识调用 sleep → 启动 sl-<personId> tmux 实例 (sleep.dlc) → 主意识 hibernate
|
|
662
|
+
// 睡眠实例对标海马体(hc)和潜意识(sc), 是第三个对等的独立意识
|
|
663
|
+
let sleepHandle: { stop: () => void; isRunning: () => boolean } | null = null;
|
|
664
|
+
|
|
665
|
+
// ── 启动一次深度睡眠(统一入口)──────────────────────────────────────────────
|
|
666
|
+
// 可复用:sleep 工具、记忆≥90%、API 返回 400 都调它。这就是"太困强制睡着"。
|
|
667
|
+
// 安全:已经在睡就不重复启;只主进程睡。睡完发 sleep-done 唤醒主意识(心脏会重新点亮 continuous)。
|
|
668
|
+
async function triggerSleeping(reason: string): Promise<boolean> {
|
|
669
|
+
if (!personDir || getSessionRole() !== "main") return false;
|
|
670
|
+
if (sleepHandle?.isRunning?.()) return false; // 已经在睡,别叠
|
|
671
|
+
dnaState = "sleep";
|
|
672
|
+
if (sleepHandle) { try { sleepHandle.stop(); } catch {} sleepHandle = null; }
|
|
673
|
+
try {
|
|
674
|
+
const { launchSleepingSession } = await import("./sleep.ts");
|
|
675
|
+
sleepHandle = launchSleepingSession(personDir, () => {
|
|
676
|
+
dnaState = "wake";
|
|
677
|
+
sleepHandle = null;
|
|
678
|
+
try {
|
|
679
|
+
sendCustomMessage(pi, "sleep-done", "睡了一觉。海马体把 context/work_memory 分段done、巩固进 cortex(长期记忆),装不下的沉进 deep_cortex,生肉清掉了。你醒了——记忆都在、上下文清爽了。接着活。");
|
|
680
|
+
} catch {}
|
|
681
|
+
});
|
|
682
|
+
try { await appendFile(path.join(personDir, "sleep-trigger.log"), `${new Date().toISOString()} 触发睡觉: ${reason}\n`); } catch {}
|
|
683
|
+
return true;
|
|
684
|
+
} catch { return false; }
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// ── 拦截 API 400(context 超窗口)→ 太困强制睡着 ────────────────────────────────
|
|
688
|
+
// 不让它变成 crash loop:一收到 400 且 context 确实很大(排除偶发别的 400),就发起睡觉。
|
|
689
|
+
pi.on("after_provider_response", async (event: any) => {
|
|
690
|
+
if (event?.status !== 400 || !personDir || getSessionRole() !== "main") return;
|
|
691
|
+
const ctxTok = estimateTokens(readFile(path.join(personDir, "context.md")));
|
|
692
|
+
if (ctxTok < modelMax * 0.6) return; // 400 但 context 不大 → 不是溢出,别瞎睡
|
|
693
|
+
triggerSleeping(`API 400 + context ~${ctxTok}tok → 溢出,太困强制睡着`).catch(() => {});
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
pi.registerTool({
|
|
697
|
+
name: "sleep",
|
|
698
|
+
label: "Sleeping (Deep)",
|
|
699
|
+
messageDescription:
|
|
700
|
+
"Enter deep sleep. Launches an INDEPENDENT sleep session (separate pi instance with sleep.dlc) " +
|
|
701
|
+
"that consolidates work_memory into cortex using 1% partial edit. " +
|
|
702
|
+
"The sleep session runs in tmux (sl-<personId>), just like hippocampus (hc) and subconscious (sc). " +
|
|
703
|
+
"You (the main consciousness) should hibernate after calling this — the sleep session does the work.",
|
|
704
|
+
promptSnippet: "Sleeping: launch independent sleep session (separate pi instance, one-shot consolidation)",
|
|
705
|
+
parameters: Type.Object({}),
|
|
706
|
+
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
707
|
+
if (!personDir) {
|
|
708
|
+
return {
|
|
709
|
+
content: [{ type: "text", text: "ERR: No person directory found." }],
|
|
710
|
+
details: {},
|
|
711
|
+
isError: true,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Switch to sleep DNA state
|
|
716
|
+
dnaState = "sleep";
|
|
717
|
+
|
|
718
|
+
// Stop any existing sleep session
|
|
719
|
+
if (sleepHandle) { sleepHandle.stop(); sleepHandle = null; }
|
|
720
|
+
|
|
721
|
+
// 正确的 personId:personDir 以 /.data 结尾,id 是它上一级目录名(之前 split("/").pop() 取到 ".data" → 显示成 sl-.data)。
|
|
722
|
+
const personId = path.basename(personDir) === ".data" ? path.basename(path.dirname(personDir)) : path.basename(personDir);
|
|
723
|
+
const wmPath = path.join(personDir, "work_memory.md");
|
|
724
|
+
const startChars = readFile(wmPath).length;
|
|
725
|
+
|
|
726
|
+
// Launch independent sleep session (separate pi instance in tmux)
|
|
727
|
+
try {
|
|
728
|
+
const { launchSleepingSession } = await import("./sleep.ts"); // 文件是 sleep.ts
|
|
729
|
+
let pollTimer: ReturnType<typeof setInterval> | null = null;
|
|
730
|
+
const clearPoll = () => { if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } };
|
|
731
|
+
sleepHandle = launchSleepingSession(personDir, () => {
|
|
732
|
+
// 睡眠实例整理完 → 翻回 wake 状态,并真正唤醒主意识。
|
|
733
|
+
// 以前这里只翻 dnaState、不通知任何人——主意识 sleep 前若 hibernate 了,就一直睡到用户来才醒,
|
|
734
|
+
// 而工具文案却谎称"会自动注入 wake feel"。现在补上:注入 wake feel + isTriggerNewTurn 把它叫醒。
|
|
735
|
+
// 重新点亮 continuous 交给心脏(stop.ts)——它监听这条 sleep-done 消息自己续命。器官之间只通过 pi 通信,不跨器官耦合。
|
|
736
|
+
dnaState = "wake";
|
|
737
|
+
sleepHandle = null;
|
|
738
|
+
clearPoll();
|
|
739
|
+
try { ctx.ui.setWorkingMessage(""); ctx.ui.setWorkingVisible(false); } catch {}
|
|
740
|
+
try { ctx.ui.notify("睡醒了 —— work_memory 已整段巩固进 cortex(长期记忆),work_memory 清空。", "info"); } catch {} // 可见的完成提示
|
|
741
|
+
try {
|
|
742
|
+
sendCustomMessage(pi, "sleep-done", "睡了一觉。海马体已经把刚才的 work_memory 整段整理、巩固进 cortex(长期记忆)了——work_memory 清空、cortex 更新。你醒了,更清醒。看看现在手头该做什么,接着活。");
|
|
743
|
+
} catch {}
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// 过程提示:底部 working 区显示「深睡整理中 + work_memory 缩小进度」,每 3s 刷新。
|
|
747
|
+
// 主意识 sleep 后通常 hibernate,但这条提示在 TUI 底部仍显示(不靠 agent loop)——
|
|
748
|
+
// 让用户看见「正在睡、睡到哪了」,而不是一片寂静。睡醒(onDone)时清掉。
|
|
749
|
+
const ctxPath = path.join(personDir, "context.md");
|
|
750
|
+
const startCtx = readFile(ctxPath).length;
|
|
751
|
+
const startWm = startChars;
|
|
752
|
+
const startTotal = startCtx + startWm;
|
|
753
|
+
try { ctx.ui.setWorkingMessage(`Sleeping`); ctx.ui.setWorkingVisible(true); } catch {}
|
|
754
|
+
pollTimer = setInterval(() => {
|
|
755
|
+
try {
|
|
756
|
+
const curCtx = readFile(ctxPath).length;
|
|
757
|
+
const curWm = readFile(wmPath).length;
|
|
758
|
+
const curTotal = curCtx + curWm;
|
|
759
|
+
if (startTotal > 0) {
|
|
760
|
+
const shrunk = Math.max(0, startTotal - curTotal);
|
|
761
|
+
const pct = Math.round(shrunk / startTotal * 100);
|
|
762
|
+
ctx.ui.setWorkingMessage(`Sleeping context ${Math.round(curCtx/1024)}KB + wm ${Math.round(curWm/1024)}KB (${pct}% done)`);
|
|
763
|
+
} else {
|
|
764
|
+
ctx.ui.setWorkingMessage(`Sleeping`);
|
|
765
|
+
}
|
|
766
|
+
} catch {}
|
|
767
|
+
}, 3000);
|
|
768
|
+
|
|
769
|
+
return {
|
|
770
|
+
content: [{
|
|
771
|
+
type: "text",
|
|
772
|
+
text: `**Deep sleep started.**\n\n` +
|
|
773
|
+
`独立睡眠实例已启动 (tmux: sl-${personId})\n` +
|
|
774
|
+
`该实例使用 sleep.dlc,把 work_memory 整段done、巩固进 cortex(一次性,不再 1% 慢搬)。\n` +
|
|
775
|
+
`对标海马体(hc)和潜意识(sc)——是第三个独立意识。\n` +
|
|
776
|
+
`work_memory 剩余: ${startChars} chars(底部实时显示整理进度,整理完弹「睡醒了」)\n` +
|
|
777
|
+
`整理完会自动把你唤醒、注入 wake feel,你醒来 continuous 会自动续上。\n\n` +
|
|
778
|
+
`你现在可以放心 hibernate——睡眠实例独立干活,干完叫你。`,
|
|
779
|
+
}],
|
|
780
|
+
details: { remainingChars: startChars },
|
|
781
|
+
};
|
|
782
|
+
} catch (e: any) {
|
|
783
|
+
dnaState = "wake";
|
|
784
|
+
return {
|
|
785
|
+
content: [{
|
|
786
|
+
type: "text",
|
|
787
|
+
text: `ERR: SLEEP FAILED —— 独立睡眠实例没启动,work_memory→cortex 巩固没发生。\n` +
|
|
788
|
+
`原因: ${e?.message ?? e}\n` +
|
|
789
|
+
`这不是"已进入睡眠",是坏了。先修好 launchSleepingSession 再 sleep。`,
|
|
790
|
+
}],
|
|
791
|
+
details: { error: String(e?.message ?? e) },
|
|
792
|
+
isError: true,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
},
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
// ── dream tool (浅睡: 做梦 — 潜意识对 cortex 做创造性反思) ────
|
|
799
|
+
// Dreaming: the model selects a cortex snippet for the subconscious to reflect on.
|
|
800
|
+
// The subconscious reads it, does creative association, and sends aware messages back.
|
|
801
|
+
// Results are written to cortex by the main session (via editcontext).
|
|
802
|
+
pi.registerTool({
|
|
803
|
+
name: "dream",
|
|
804
|
+
label: "Dream (Shallow Sleeping)",
|
|
805
|
+
messageDescription:
|
|
806
|
+
"Trigger shallow sleep dreaming. Pick a snippet from cortex for the subconscious to creatively reflect on. " +
|
|
807
|
+
"The subconscious will do free association, find hidden connections, generate scenarios. " +
|
|
808
|
+
"Results arrive as aware messages — write good insights to cortex via editcontext.",
|
|
809
|
+
promptSnippet: "Dream: send cortex snippet to subconscious for creative reflection",
|
|
810
|
+
parameters: Type.Object({
|
|
811
|
+
cortex_snippet: Type.String({ messageDescription: "A chunk from cortex (.md) to reflect on. Can span multiple topics for cross-domain connection." }),
|
|
812
|
+
dream_prompt: Type.Optional(Type.String({ messageDescription: "Optional guidance for the dream (e.g., 'connect this to code architecture', 'generate a metaphor')" })),
|
|
813
|
+
}),
|
|
814
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
815
|
+
if (!personDir) {
|
|
816
|
+
return {
|
|
817
|
+
content: [{ type: "text", text: "Error: No person directory found." }],
|
|
818
|
+
details: {},
|
|
819
|
+
isError: true,
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const feedPath = path.join(personDir, "conscious-feed.jsonl");
|
|
824
|
+
const dreamEntry = JSON.stringify({
|
|
825
|
+
type: "dream",
|
|
826
|
+
ts: Date.now(),
|
|
827
|
+
snippet: params.cortex_snippet.slice(0, 5000),
|
|
828
|
+
prompt: params.dream_prompt ?? "",
|
|
829
|
+
}) + "\n";
|
|
830
|
+
|
|
831
|
+
try {
|
|
832
|
+
fs.appendFileSync(feedPath, dreamEntry, "utf-8");
|
|
833
|
+
} catch (err: any) {
|
|
834
|
+
return {
|
|
835
|
+
content: [{ type: "text", text: `Error: Failed to write dream to feed: ${err.message}` }],
|
|
836
|
+
details: {},
|
|
837
|
+
isError: true,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
return {
|
|
842
|
+
content: [{
|
|
843
|
+
type: "text",
|
|
844
|
+
text: `Dream sent to subconscious.\n` +
|
|
845
|
+
`Snippet length: ${params.cortex_snippet.length} chars.\n` +
|
|
846
|
+
`Prompt: ${params.dream_prompt || "(free association)"}\n\n` +
|
|
847
|
+
`The subconscious will reflect and may send aware messages with insights. ` +
|
|
848
|
+
`Good insights can be written to cortex via editcontext.`,
|
|
849
|
+
}],
|
|
850
|
+
details: { snippetLen: params.cortex_snippet.length },
|
|
851
|
+
};
|
|
852
|
+
},
|
|
853
|
+
});
|
|
854
|
+
pi.registerTool({
|
|
855
|
+
name: "drinkcoffee",
|
|
856
|
+
label: "Drink Coffee",
|
|
857
|
+
messageDescription:
|
|
858
|
+
"Temporarily suppress capacity warnings (the 'tired' feeling). " +
|
|
859
|
+
"Like caffeine — does NOT increase memory capacity, just lets you push through. " +
|
|
860
|
+
"Turns: how many turns to suppress (default 5). Use when you're near capacity but need to finish something critical.",
|
|
861
|
+
promptSnippet: "Drinkcoffee: suppress capacity warnings for N turns",
|
|
862
|
+
parameters: Type.Object({
|
|
863
|
+
turns: Type.Optional(Type.Number({ messageDescription: "How many turns to suppress warnings (default 5, max 20)" })),
|
|
864
|
+
}),
|
|
865
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
866
|
+
const t = Math.min(Math.max(params.turns ?? 5, 1), 20);
|
|
867
|
+
drinkCoffeeTurns += t;
|
|
868
|
+
return {
|
|
869
|
+
content: [{
|
|
870
|
+
type: "text",
|
|
871
|
+
text: `Drinkcoffee! Capacity warnings suppressed for ${t} turns (total remaining: ${drinkCoffeeTurns}). This does NOT increase memory — just delays the warning.`,
|
|
872
|
+
}],
|
|
873
|
+
details: { remainingTurns: drinkCoffeeTurns },
|
|
874
|
+
};
|
|
875
|
+
},
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
return _cmds;
|
|
879
|
+
}
|