cuelo 0.10.19 → 0.10.21
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +3 -4
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.js +2 -2
- package/.next/required-server-files.json +2 -2
- package/.next/routes-manifest.json +0 -6
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +2 -2
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +3 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/agent/[id]/account/route.js +1 -1
- package/.next/server/app/api/agent/[id]/bash-output/route.js +1 -1
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/attachment-settings/route.js +1 -1
- package/.next/server/app/api/attachments/[id]/transcription/route.js +2 -2
- package/.next/server/app/api/attachments/draft/route.js +1 -1
- package/.next/server/app/api/attachments/route.js +2 -2
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +2 -2
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +1 -1
- package/.next/server/app/api/default-cwd/route.js +1 -1
- package/.next/server/app/api/display-settings/route.js +1 -1
- package/.next/server/app/api/document-text/route.js +1 -1
- package/.next/server/app/api/experiments/route.js +1 -1
- package/.next/server/app/api/file-index/route.js +1 -1
- package/.next/server/app/api/files/[...path]/route.js +1 -1
- package/.next/server/app/api/git/diff/route.js +2 -2
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/github/projects/route.js +1 -1
- package/.next/server/app/api/gpt6/handles/route.js +1 -1
- package/.next/server/app/api/gpt6/reply/route.js +1 -1
- package/.next/server/app/api/gpt6/token/route.js +1 -1
- package/.next/server/app/api/gpt6/web6-session/route.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/live/call/[callId]/route.js +3 -3
- package/.next/server/app/api/live/call/[callId]/route.js.nft.json +1 -1
- package/.next/server/app/api/live/events/route.js +4 -4
- package/.next/server/app/api/live/events/route.js.nft.json +1 -1
- package/.next/server/app/api/live/interpret/route.js +3 -3
- package/.next/server/app/api/live/interpret/route.js.nft.json +1 -1
- package/.next/server/app/api/live/offer/route.js +1 -1
- package/.next/server/app/api/live/offer/route.js.nft.json +1 -1
- package/.next/server/app/api/live/voices/route.js +1 -1
- package/.next/server/app/api/live/voices/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/model-roles/route.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/route.js +3 -3
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/office-avatars/[id]/route.js +1 -1
- package/.next/server/app/api/office-avatars/route.js +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/project-docs/route.js +1 -1
- package/.next/server/app/api/project-trust/route.js +1 -1
- package/.next/server/app/api/projects/route.js +1 -1
- package/.next/server/app/api/runs/[runId]/route.js +1 -1
- package/.next/server/app/api/runs/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/search/route.js +1 -1
- package/.next/server/app/api/settings/route.js +1 -1
- package/.next/server/app/api/sidecars/[service]/[...path]/route.js +1 -1
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/route.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/subagents/eta/route.js +1 -1
- package/.next/server/app/api/theme/route.js +1 -1
- package/.next/server/app/api/tool-image/route.js +1 -1
- package/.next/server/app/api/update-maintenance/route.js +3 -3
- package/.next/server/app/api/updates/route.js +1 -1
- package/.next/server/app/api/web-access/recovery/route.js +1 -1
- package/.next/server/app/api/web-access/route.js +1 -1
- package/.next/server/app/api/web6/consults/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/office/page.js +2 -2
- package/.next/server/app/office/page_client-reference-manifest.js +1 -1
- package/.next/server/app/office-preview/page.js +2 -2
- package/.next/server/app/office-preview/page_client-reference-manifest.js +1 -1
- package/.next/server/app/office-preview.html +1 -1
- package/.next/server/app/office-preview.rsc +3 -3
- package/.next/server/app/office-preview.segments/_full.segment.rsc +3 -3
- package/.next/server/app/office-preview.segments/_head.segment.rsc +1 -1
- package/.next/server/app/office-preview.segments/_index.segment.rsc +3 -3
- package/.next/server/app/office-preview.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/office-preview.segments/office-preview/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/office-preview.segments/office-preview.segment.rsc +1 -1
- package/.next/server/app/office.html +1 -1
- package/.next/server/app/office.rsc +5 -5
- package/.next/server/app/office.segments/_full.segment.rsc +5 -5
- package/.next/server/app/office.segments/_head.segment.rsc +1 -1
- package/.next/server/app/office.segments/_index.segment.rsc +3 -3
- package/.next/server/app/office.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/office.segments/office/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/office.segments/office.segment.rsc +1 -1
- package/.next/server/app/page.js +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recover/page.js +2 -2
- package/.next/server/app/recover/page_client-reference-manifest.js +1 -1
- package/.next/server/app/recover.html +1 -1
- package/.next/server/app/recover.rsc +3 -3
- package/.next/server/app/recover.segments/_full.segment.rsc +3 -3
- package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
- package/.next/server/app/recover.segments/_index.segment.rsc +3 -3
- package/.next/server/app/recover.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/recover.segments/recover.segment.rsc +1 -1
- package/.next/server/app-paths-manifest.json +3 -4
- package/.next/server/chunks/1008.js +5 -5
- package/.next/server/chunks/5489.js +4 -4
- package/.next/server/chunks/5582.js +1 -1
- package/.next/server/chunks/6429.js +5 -5
- package/.next/server/chunks/6775.js +7 -0
- package/.next/server/chunks/7362.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/IKi2VjUzAnD5AUcDn7QNi/_buildManifest.js +1 -0
- package/.next/static/chunks/6110-7a2311ab143b31e2.js +1 -0
- package/.next/static/chunks/{9575-fd6b2862fa09bf5a.js → 9575-032510d25828764f.js} +4 -4
- package/.next/static/chunks/app/_global-error/{page-95aaaafc49522a6b.js → page-571d58ede1391fc8.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/account/{route-95aaaafc49522a6b.js → route-571d58ede1391fc8.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/bash-output/{route-95aaaafc49522a6b.js → route-571d58ede1391fc8.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/events/{route-95aaaafc49522a6b.js → route-571d58ede1391fc8.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/attachment-settings/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/attachments/[id]/transcription/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/attachments/draft/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/attachments/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/display-settings/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/document-text/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/experiments/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/github/projects/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/gpt6/handles/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/gpt6/reply/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/gpt6/token/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/gpt6/web6-session/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/home/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/live/call/[callId]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/live/events/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/live/interpret/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/live/offer/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/live/voices/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/model-roles/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/models/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/office-avatars/[id]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/office-avatars/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/project-docs/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/projects/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/runs/[runId]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/runs/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sessions/search/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/settings/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/skills/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/subagents/eta/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/theme/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/tool-image/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/update-maintenance/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/updates/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/web-access/recovery/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/web-access/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/web6/consults/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/app/{layout-1781d82d3faf88af.js → layout-a7fca69a4fa9b2c2.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-571d58ede1391fc8.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-571d58ede1391fc8.js +1 -0
- package/.next/static/css/{949fdc99cee51026.css → cc26725ba00edca3.css} +1 -1
- package/.next/trace +108 -108
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +1 -2
- package/.next/types/validator.ts +0 -9
- package/README.md +8 -4
- package/Tools/CUELO_Setup/files/native-runtime-patch.js +3 -3
- package/Tools/OMP_Global_Config/agent/AGENTS.md +2 -0
- package/Tools/OMP_Global_Config/agent/extensions/docmap-route.ts +34 -0
- package/Tools/OMP_Global_Config/agent/extensions/jev-runtime.ts +12 -1
- package/Tools/OMP_Global_Config/agent/extensions/lib/docmap-search.ts +576 -0
- package/Tools/OMP_Global_Config/agent/extensions/mcp-selection.ts +48 -7
- package/Tools/OMP_Global_Config/agent/prompt-copies/AGENTS.en.md +3 -1
- package/Tools/OMP_Global_Config/agent/rules/docs-handoff.md +1 -1
- package/Tools/OMP_Global_Config/agent/rules/frontend.md +4 -2
- package/Tools/OMP_Global_Config/agent/rules/verdict.md +1 -0
- package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +981 -132
- package/package.json +7 -7
- package/.next/server/app/api/live/character-voice/route.js +0 -1
- package/.next/server/app/api/live/character-voice/route.js.nft.json +0 -1
- package/.next/server/app/api/live/character-voice/route_client-reference-manifest.js +0 -1
- package/.next/server/chunks/205.js +0 -7
- package/.next/server/chunks/7467.js +0 -3
- package/.next/static/chunks/6110-11dcb476b561c51f.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/attachment-settings/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/attachments/[id]/transcription/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/attachments/draft/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/attachments/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/display-settings/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/document-text/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/experiments/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/github/projects/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/gpt6/handles/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/gpt6/reply/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/gpt6/token/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/gpt6/web6-session/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/home/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/call/[callId]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/character-voice/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/events/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/interpret/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/offer/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/live/voices/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/mcp/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/model-roles/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/models/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/models-config/catalog/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/office-avatars/[id]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/office-avatars/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/project-docs/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/project-trust/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/projects/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/runs/[runId]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/runs/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sessions/search/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/settings/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/skills/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/subagents/eta/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/theme/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/tool-image/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/update-maintenance/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/updates/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/web-access/recovery/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/web-access/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/web6/consults/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-95aaaafc49522a6b.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-95aaaafc49522a6b.js +0 -1
- package/.next/static/uKP9o1VFOow0E3p4hZv3O/_buildManifest.js +0 -1
- package/.next/types/app/api/live/character-voice/route.ts +0 -351
- package/public/audio/live/ACML-1.0.txt +0 -85
- package/public/audio/live/hikari-reference.wav +0 -0
- package/public/audio/live/isana-reference.wav +0 -0
- package/public/audio/live/mio-reference.wav +0 -0
- package/public/audio/live/nova-reference.wav +0 -0
- package/public/audio/live/rin-reference.wav +0 -0
- package/public/audio/live/shion-reference.wav +0 -0
- package/public/audio/live/voices.json +0 -275
- package/public/audio/live/yuki-reference.wav +0 -0
- /package/.next/static/{uKP9o1VFOow0E3p4hZv3O → IKi2VjUzAnD5AUcDn7QNi}/_ssgManifest.js +0 -0
|
@@ -116,7 +116,8 @@ const OPUS55_HEAD_1846 = OPUS55_ROW_1846.anchor.slice(0, OPUS55_ROW_1846.anchor.
|
|
|
116
116
|
* (공개 이슈 #30: 같은 본문 재저장·context 만 바뀐 갱신도 벡터를 지워 다음 store open 이 다시 예약했다).
|
|
117
117
|
* 벡터의 모델 일치는 store open 의 reconcileEmbeddingModel 이 계속 책임진다. 별도 projection(embed_text ≠ 본문)은 기존처럼 지운다.
|
|
118
118
|
*/
|
|
119
|
-
|
|
119
|
+
/** 이슈 #32 이전에 배포된 learn topic upsert(HEAD b8f1aad, embedding 보존판) 그대로다. */
|
|
120
|
+
const learnTopicUpsertPreEvidenceBlock = `// HANSE: topic-key learn upsert
|
|
120
121
|
function upsertLearnTopic(
|
|
121
122
|
state: MnemopiSessionState,
|
|
122
123
|
params: LearnParams,
|
|
@@ -160,12 +161,61 @@ function upsertLearnTopic(
|
|
|
160
161
|
|
|
161
162
|
export class LearnTool implements AgentTool<LearnSchema> {`;
|
|
162
163
|
|
|
164
|
+
const learnTopicUpsertBlock = `// HANSE: topic-key learn upsert
|
|
165
|
+
function upsertLearnTopic(
|
|
166
|
+
state: MnemopiSessionState,
|
|
167
|
+
params: LearnParams,
|
|
168
|
+
target: ReturnType<MnemopiSessionState["getScopedRetainTarget"]>,
|
|
169
|
+
evidence: LearnEvidence,
|
|
170
|
+
): { id: string; revision: number } {
|
|
171
|
+
const topic = params.topic!.normalize("NFKC").trim().toLowerCase();
|
|
172
|
+
if (!topic) throw new Error("Learn topic must not be blank.");
|
|
173
|
+
if (redactMemorySecrets(topic) !== topic) throw new Error("Learn topic contains a credential.");
|
|
174
|
+
const db = target.memory.conn;
|
|
175
|
+
const existing = db.query(
|
|
176
|
+
"SELECT id, timestamp, metadata_json FROM working_memory WHERE json_extract(metadata_json, '$.topic_key') = ? ORDER BY timestamp DESC, id DESC LIMIT 1",
|
|
177
|
+
).get(topic) as { id: string; timestamp: string; metadata_json: string } | null;
|
|
178
|
+
// HANSE: learn topic evidence is stored with the topic row
|
|
179
|
+
// 근거(kind·citations)는 본문·임베딩과 같은 쓰기 단위로 저장한다. 갱신은 아래 transaction 한 번, 새로 만들 때는 한 번의 INSERT 다.
|
|
180
|
+
const revision = existing ? (Number(JSON.parse(existing.metadata_json).revision) || 1) + 1 : 1;
|
|
181
|
+
if (existing) return db.transaction(() => {
|
|
182
|
+
const content = redactMemorySecrets(params.memory);
|
|
183
|
+
const stored = JSON.parse(existing.metadata_json);
|
|
184
|
+
delete stored.kind;
|
|
185
|
+
delete stored.citations;
|
|
186
|
+
const now = new Date().toISOString();
|
|
187
|
+
// HANSE: learn topic provenance keeps first and latest source
|
|
188
|
+
const firstSeen = stored.revision === 1 ? { first_context: stored.context ?? null, first_timestamp: existing.timestamp } : {};
|
|
189
|
+
const latest = { updated_session_id: state.sessionId, updated_cwd: state.session.sessionManager.getCwd(), updated_at: now };
|
|
190
|
+
const metadata = JSON.stringify({ ...firstSeen, ...stored, context: params.context == null ? null : redactMemorySecrets(params.context), topic_key: topic, revision, ...latest, ...learnEvidenceMetadata(evidence) });
|
|
191
|
+
// HANSE: learn topic keeps embedding when its input is unchanged
|
|
192
|
+
// 비교할 본문은 쓰기 lock 을 잡은(immediate) 같은 transaction 안에서 다시 읽는다. 바깥에서 읽은 값이면
|
|
193
|
+
// 그 사이 다른 프로세스가 바꾼 본문·벡터를 이 갱신이 그대로 남길 수 있다.
|
|
194
|
+
const current = db.query("SELECT content, embed_text FROM working_memory WHERE id = ?").get(existing.id) as { content: string; embed_text: string | null } | null;
|
|
195
|
+
if (current?.content === content && (current.embed_text == null || current.embed_text === content)) {
|
|
196
|
+
db.query("UPDATE working_memory SET timestamp = ?, metadata_json = ? WHERE id = ?").run(now, metadata, existing.id);
|
|
197
|
+
} else {
|
|
198
|
+
db.query("UPDATE working_memory SET content = ?, embed_text = NULL, timestamp = ?, metadata_json = ? WHERE id = ?").run(content, now, metadata, existing.id);
|
|
199
|
+
db.query("DELETE FROM memory_embeddings WHERE memory_id = ?").run(existing.id);
|
|
200
|
+
}
|
|
201
|
+
return { id: existing.id, revision };
|
|
202
|
+
}).immediate();
|
|
203
|
+
const id = state.rememberScoped(params.memory, {
|
|
204
|
+
source: "coding-agent-learn", importance: 0.8,
|
|
205
|
+
metadata: { session_id: state.sessionId, cwd: state.session.sessionManager.getCwd(), context: params.context ?? null, tool: "learn", topic_key: topic, revision, ...learnEvidenceMetadata(evidence) },
|
|
206
|
+
scope: "bank", extract: true, extractEntities: true, veracity: "tool", memoryType: "fact",
|
|
207
|
+
}, target);
|
|
208
|
+
return { id, revision };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export class LearnTool implements AgentTool<LearnSchema> {`;
|
|
212
|
+
|
|
163
213
|
/**
|
|
164
214
|
* 회상 줄의 범위(global/project)·정정 표시 보조. 순수 표시이며 DB 와 RecallResult 원본은 바꾸지 않는다.
|
|
165
215
|
* 범위는 collectScopedRecallResults 가 고른 결과의 실제 bank 로 붙인 일시 필드(recallScope)에서만 읽는다.
|
|
166
216
|
* 정정 표시는 revision>1 일 때만, 최초 시점은 first_timestamp 가 있을 때만, 정정 시점은 updated_at 이 있을 때만 쓴다.
|
|
167
217
|
*/
|
|
168
|
-
const
|
|
218
|
+
const recallProvenanceLegacyBlock = String.raw`// HANSE: recall provenance note
|
|
169
219
|
type RecallProvenance = { scope?: string; revision: number; first?: string; corrected?: string };
|
|
170
220
|
function recallProvenance(result: RecallResult): RecallProvenance {
|
|
171
221
|
const scope = (result as { recallScope?: string }).recallScope;
|
|
@@ -205,6 +255,452 @@ function recallRevisionFooter(results: readonly RecallResult[]): string {
|
|
|
205
255
|
|
|
206
256
|
`;
|
|
207
257
|
|
|
258
|
+
/**
|
|
259
|
+
* 2026-10-17: 근거형 learn 의 citations 를 회상 줄에 `근거 후보:` 로 싣는다. 확인할 위치이지 근거가 아니라는 안내는 줄이 하나라도
|
|
260
|
+
* 있을 때 한 번만 붙는다. 위 legacy 본문(정정 표시만)을 쓰던 설치는 legacyPatched 로 이 본문으로 바뀐다.
|
|
261
|
+
*/
|
|
262
|
+
const recallProvenanceBlock = String.raw`// HANSE: recall provenance note
|
|
263
|
+
type RecallProvenance = { scope?: string; revision: number; first?: string; corrected?: string; citations: string[] };
|
|
264
|
+
function recallProvenance(result: RecallResult): RecallProvenance {
|
|
265
|
+
const scope = (result as { recallScope?: string }).recallScope;
|
|
266
|
+
let meta: Record<string, unknown> | null = null;
|
|
267
|
+
try {
|
|
268
|
+
const raw: unknown = result.metadata ?? (result.metadata_json ? JSON.parse(result.metadata_json) : null);
|
|
269
|
+
if (raw && typeof raw === "object") meta = raw as Record<string, unknown>;
|
|
270
|
+
} catch {
|
|
271
|
+
meta = null;
|
|
272
|
+
}
|
|
273
|
+
const day = (value: unknown): string | undefined =>
|
|
274
|
+
typeof value === "string" && /^\d{4}-\d{2}-\d{2}/.test(value) ? value.slice(0, 10) : undefined;
|
|
275
|
+
const revision = Number(meta?.revision);
|
|
276
|
+
const cited: unknown[] = Array.isArray(meta?.citations) ? (meta?.citations as unknown[]) : [];
|
|
277
|
+
const citations = cited.flatMap((entry): string[] => {
|
|
278
|
+
const item = entry as { path?: unknown; anchor?: unknown } | null;
|
|
279
|
+
if (!item || typeof item.path !== "string" || item.path === "") return [];
|
|
280
|
+
const text = item.path + (typeof item.anchor === "string" && item.anchor !== "" ? "#" + item.anchor : "");
|
|
281
|
+
return [text.length > 120 ? text.slice(0, 119) + "…" : text];
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
scope,
|
|
285
|
+
revision: Number.isInteger(revision) ? revision : 0,
|
|
286
|
+
first: day(meta?.first_timestamp),
|
|
287
|
+
corrected: day(meta?.updated_at),
|
|
288
|
+
citations,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function recallProvenanceNote(result: RecallResult): string {
|
|
292
|
+
const info = recallProvenance(result);
|
|
293
|
+
const parts: string[] = [];
|
|
294
|
+
if (info.scope) parts.push(info.scope);
|
|
295
|
+
if (info.revision > 1) {
|
|
296
|
+
parts.push("corrected r" + info.revision);
|
|
297
|
+
parts.push("first " + (info.first ?? "unknown"));
|
|
298
|
+
if (info.corrected) parts.push("corrected " + info.corrected);
|
|
299
|
+
}
|
|
300
|
+
if (info.citations.length > 0) {
|
|
301
|
+
const more = info.citations.length > 3 ? " 외 " + (info.citations.length - 3) + "건" : "";
|
|
302
|
+
parts.push("근거 후보: " + info.citations.slice(0, 3).join(", ") + more);
|
|
303
|
+
}
|
|
304
|
+
return parts.length > 0 ? " {" + parts.join("; ") + "}" : "";
|
|
305
|
+
}
|
|
306
|
+
function recallRevisionFooter(results: readonly RecallResult[]): string {
|
|
307
|
+
const notes: string[] = [];
|
|
308
|
+
if (results.some(result => recallProvenance(result).revision > 1))
|
|
309
|
+
notes.push("Corrected rows: read memory://<id> for the full metadata and correction reason.");
|
|
310
|
+
if (results.some(result => recallProvenance(result).citations.length > 0))
|
|
311
|
+
notes.push("근거 후보 paths are where a memory says it was recorded, not proof: open one to verify before relying on the memory.");
|
|
312
|
+
return notes.length > 0 ? "\n\n" + notes.join("\n") : "";
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* A fact extracted from an evidence lesson carries only source_memory_id, so the recall line for it would show no 근거 후보 even when the
|
|
317
|
+
* lesson it came from cites a record. Copy just the citations of that origin row onto the fact result before it is rendered.
|
|
318
|
+
*/
|
|
319
|
+
function attachFactCitations(memory: { conn: { query(sql: string): { get(...params: string[]): unknown } } }, results: RecallResult[]): void {
|
|
320
|
+
for (const result of results) {
|
|
321
|
+
const origin = (result as { source_memory_id?: unknown }).source_memory_id;
|
|
322
|
+
if (result.tier !== "fact" || typeof origin !== "string" || result.metadata) continue;
|
|
323
|
+
try {
|
|
324
|
+
const row = memory.conn.query("SELECT metadata_json FROM working_memory WHERE id = ?").get(origin) as { metadata_json?: string | null } | null;
|
|
325
|
+
const cited: unknown = row?.metadata_json ? (JSON.parse(row.metadata_json) as { citations?: unknown }).citations : undefined;
|
|
326
|
+
if (Array.isArray(cited) && cited.length > 0) result.metadata = { citations: cited };
|
|
327
|
+
} catch {
|
|
328
|
+
// A fact whose origin row cannot be read simply shows no citation.
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
`;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* 2026-10-17 근거형 learn(이슈 #32 배포본). kind 와 citations 를 받아 저장 전에 검증하고 metadata 에 남긴다. 존재(경로·앵커)만 보며
|
|
337
|
+
* 글이 교훈을 뒷받침하는지는 판정하지 않는다. 주석·문자열에 백틱과 달러 중괄호를 쓰지 않는다(이 파일의 String.raw 안이다).
|
|
338
|
+
* 이 본문은 배포된 그대로 두고(아래 learnEvidenceBlock 의 legacyPatched), 오류 문구만 바꾼 새 판을 그 아래에서 만든다.
|
|
339
|
+
*/
|
|
340
|
+
const learnEvidenceIssue32Block = String.raw`// HANSE: learn evidence citations
|
|
341
|
+
type LearnKind = "evidence" | "preference" | "procedure" | "hypothesis";
|
|
342
|
+
type LearnCitation = { path: string; anchor?: string };
|
|
343
|
+
type LearnEvidence = { kind?: LearnKind; citations: LearnCitation[] };
|
|
344
|
+
const LEARN_MAX_CITATIONS = 8;
|
|
345
|
+
const LEARN_MAX_ANCHOR_FILE_BYTES = 8 * 1024 * 1024;
|
|
346
|
+
|
|
347
|
+
const learnKindSchema = type("'evidence' | 'preference' | 'procedure' | 'hypothesis'").describe(
|
|
348
|
+
"what the lesson is: evidence = project fact, design decision or failure cause (needs citations in project scope); preference = how the user wants work done; procedure = how to do a task; hypothesis = unverified guess",
|
|
349
|
+
);
|
|
350
|
+
const learnCitationsSchema = type({
|
|
351
|
+
path: type("string").describe("file path relative to the project root"),
|
|
352
|
+
"anchor?": type("string").describe("a '## ' heading text, a header field name such as RECORD or SCOPE, or a line range such as 120-140"),
|
|
353
|
+
})
|
|
354
|
+
.array()
|
|
355
|
+
.describe("project files that record this lesson; required for kind evidence in project scope; checked for existence only, not for whether the file supports the lesson");
|
|
356
|
+
|
|
357
|
+
function learnProjectRoot(cwd: string): string {
|
|
358
|
+
let real = cwd;
|
|
359
|
+
try {
|
|
360
|
+
real = fs.realpathSync(cwd);
|
|
361
|
+
} catch {
|
|
362
|
+
// An unreadable cwd is reported by the citation path check below.
|
|
363
|
+
}
|
|
364
|
+
try {
|
|
365
|
+
const out = Bun.spawnSync(["git", "-C", real, "rev-parse", "--show-toplevel"], { stdout: "pipe", stderr: "ignore" });
|
|
366
|
+
const top = out.exitCode === 0 ? out.stdout.toString().trim() : "";
|
|
367
|
+
if (top) return fs.realpathSync(top);
|
|
368
|
+
} catch {
|
|
369
|
+
// No git: the session cwd is the project root.
|
|
370
|
+
}
|
|
371
|
+
return real;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function learnPathIgnored(root: string, rel: string): boolean {
|
|
375
|
+
try {
|
|
376
|
+
return Bun.spawnSync(["git", "-C", root, "check-ignore", "-q", "--", rel], { stdout: "ignore", stderr: "ignore" }).exitCode === 0;
|
|
377
|
+
} catch {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function learnHeadings(text: string): string[] {
|
|
383
|
+
const headings: string[] = [];
|
|
384
|
+
for (const line of text.split(/\r?\n/)) {
|
|
385
|
+
const match = /^#{1,6}\s+(.*?)\s*#*\s*$/.exec(line);
|
|
386
|
+
if (match) headings.push(match[1]!.replace(/\s+/g, " "));
|
|
387
|
+
}
|
|
388
|
+
return headings;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function learnAnchorFound(text: string, anchor: string): boolean {
|
|
392
|
+
const lines = text.split(/\r?\n/);
|
|
393
|
+
const range = /^(\d+)(?:-(\d+))?$/.exec(anchor);
|
|
394
|
+
if (range) {
|
|
395
|
+
const start = Number(range[1]);
|
|
396
|
+
const end = Number(range[2] ?? range[1]);
|
|
397
|
+
return start >= 1 && end >= start && end <= lines.length;
|
|
398
|
+
}
|
|
399
|
+
if (/^(RECORD|DATE|SCOPE|PATHS|STATUS)$/.test(anchor)) {
|
|
400
|
+
return lines.slice(0, 40).some(line => line.startsWith(anchor + ":") || line.startsWith("*" + anchor + ":"));
|
|
401
|
+
}
|
|
402
|
+
const want = anchor.replace(/^#{1,6}\s*/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
403
|
+
if (!want) return false;
|
|
404
|
+
return learnHeadings(text).some(heading => {
|
|
405
|
+
const have = heading.toLowerCase();
|
|
406
|
+
return have === want || have.startsWith(want);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function resolveLearnEvidence(session: ToolSession, params: LearnParams): LearnEvidence {
|
|
411
|
+
const given = params.citations ?? [];
|
|
412
|
+
const projectScope = (params.scope ?? "project") === "project";
|
|
413
|
+
const kind = params.kind ?? (given.length > 0 ? "evidence" : undefined);
|
|
414
|
+
if (kind === undefined && projectScope) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
"learn needs a kind. Use kind evidence for a project fact, design decision or failure cause, and add citations (a list of path plus optional anchor) naming the project file that records it. Use kind preference (how the user wants work done), procedure (how to do a task) or hypothesis (unverified) when no project file records it.",
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
if (kind === "evidence" && projectScope && given.length === 0) {
|
|
420
|
+
throw new Error(
|
|
421
|
+
"kind evidence in project scope needs at least one citation: citations = [{ path: <file relative to the project root>, anchor: <optional: a '## ' heading text, a header field such as RECORD or SCOPE, or a line range like 120-140> }]. Cite the file that records it (a doc/history record, HANDOFF.md, source). If no file records it yet, write the record first, or use kind hypothesis.",
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
if (given.length > LEARN_MAX_CITATIONS) {
|
|
425
|
+
throw new Error("learn accepts at most " + LEARN_MAX_CITATIONS + " citations; cite the one or two files that matter most.");
|
|
426
|
+
}
|
|
427
|
+
if (given.length === 0) return { kind, citations: [] };
|
|
428
|
+
const root = learnProjectRoot(session.cwd);
|
|
429
|
+
const citations: LearnCitation[] = [];
|
|
430
|
+
const seen = new Set<string>();
|
|
431
|
+
given.forEach((citation, index) => {
|
|
432
|
+
const label = "citations[" + index + "]";
|
|
433
|
+
const raw = citation.path.trim().replaceAll("\\", "/");
|
|
434
|
+
if (!raw) throw new Error(label + ".path is empty.");
|
|
435
|
+
// A credential-shaped path or anchor is rejected before any error below can echo it and before anything is stored; the value is never quoted.
|
|
436
|
+
const rawAnchor = citation.anchor?.trim();
|
|
437
|
+
if (redactMemorySecrets(raw) !== raw) throw new Error(label + ".path contains a credential and cannot be cited.");
|
|
438
|
+
if (rawAnchor && redactMemorySecrets(rawAnchor) !== rawAnchor) throw new Error(label + ".anchor contains a credential and cannot be cited.");
|
|
439
|
+
let real: string;
|
|
440
|
+
try {
|
|
441
|
+
real = fs.realpathSync(path.resolve(root, raw));
|
|
442
|
+
} catch {
|
|
443
|
+
throw new Error(label + '.path "' + raw + '" does not exist under the project root ' + root + ". Use a path relative to the project root.");
|
|
444
|
+
}
|
|
445
|
+
const rel = path.relative(root, real);
|
|
446
|
+
if (rel === "" || rel === ".." || rel.startsWith(".." + path.sep) || path.isAbsolute(rel)) {
|
|
447
|
+
throw new Error(label + '.path "' + raw + '" is outside the project root ' + root + ".");
|
|
448
|
+
}
|
|
449
|
+
// The normalized path is checked before any filesystem call on it, so a native stat error cannot echo a credential-shaped real path.
|
|
450
|
+
const relPosix = rel.split(path.sep).join("/");
|
|
451
|
+
if (redactMemorySecrets(relPosix) !== relPosix) throw new Error(label + ".path contains a credential and cannot be cited.");
|
|
452
|
+
const stat = fs.statSync(real);
|
|
453
|
+
if (!stat.isFile()) throw new Error(label + '.path "' + raw + '" is not a file; cite a file.');
|
|
454
|
+
if (!eligibleFile(relPosix, stat.size, true) || learnPathIgnored(root, relPosix)) {
|
|
455
|
+
throw new Error(label + '.path "' + raw + '" is a secret, binary, empty, excluded or git-ignored file and cannot be cited.');
|
|
456
|
+
}
|
|
457
|
+
const anchor = citation.anchor?.trim();
|
|
458
|
+
if (anchor) {
|
|
459
|
+
if (stat.size > LEARN_MAX_ANCHOR_FILE_BYTES) throw new Error(label + '.path "' + raw + '" is too large to check an anchor in; cite it without an anchor or use a line range of a smaller file.');
|
|
460
|
+
const text = fs.readFileSync(real, "utf8");
|
|
461
|
+
if (!learnAnchorFound(text, anchor)) {
|
|
462
|
+
const headings = learnHeadings(text).slice(0, 8).map(heading => redactMemorySecrets(heading));
|
|
463
|
+
throw new Error(
|
|
464
|
+
label + '.anchor "' + anchor + '" was not found in ' + relPosix + ". Use the text of a '## ' heading, a header field name (RECORD, DATE, SCOPE, PATHS, STATUS) or a line range inside the file." +
|
|
465
|
+
(headings.length > 0 ? " Headings in the file: " + headings.join(" | ") : ""),
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// Store a heading without its "## " prefix so the recall line reads path#heading.
|
|
470
|
+
const stored = anchor ? anchor.replace(/^#{1,6}\s*/, "") : undefined;
|
|
471
|
+
if (stored && redactMemorySecrets(stored) !== stored) throw new Error(label + ".anchor contains a credential and cannot be cited.");
|
|
472
|
+
const key = relPosix + "#" + (stored ?? "");
|
|
473
|
+
if (seen.has(key)) return;
|
|
474
|
+
seen.add(key);
|
|
475
|
+
citations.push(stored ? { path: relPosix, anchor: stored } : { path: relPosix });
|
|
476
|
+
});
|
|
477
|
+
return { kind, citations };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function learnEvidenceMetadata(evidence: LearnEvidence): Record<string, unknown> {
|
|
481
|
+
return { ...(evidence.kind ? { kind: evidence.kind } : {}), ...(evidence.citations.length > 0 ? { citations: evidence.citations } : {}) };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const learnSchemaBase = type({
|
|
485
|
+
`;
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* 2026-10-17 18.11.3 이식: #32 판의 경로 오류가 절대 프로젝트 루트를 그대로 보였다. 오류는 "the project root" 라고만 말하고,
|
|
489
|
+
* 사용자가 준 상대 경로만 인용한다(절대 경로 입력은 루트를 담을 수 있으므로 "(an absolute path)"). 루트 안으로 확인된 뒤의
|
|
490
|
+
* 오류는 정규화한 상대 경로를 인용한다. 이슈 #34: 안전한 이름의 링크 cwd 뒤 실제 루트가 native fs 오류(EACCES 등)에 그대로
|
|
491
|
+
* 실리므로 stat·read 실패도 상대 경로와 오류 코드만 보인다. 나머지 본문은 #32 판과 바이트가 같다.
|
|
492
|
+
*/
|
|
493
|
+
const learnEvidenceBlock = (() => {
|
|
494
|
+
let text = learnEvidenceIssue32Block;
|
|
495
|
+
const swap = (from, to) => {
|
|
496
|
+
const found = text.split(from).length - 1;
|
|
497
|
+
if (found !== 1) throw new Error(`learn 근거 블록을 만들 수 없다: ${JSON.stringify(from.slice(0, 80))} ${found}/1`);
|
|
498
|
+
text = text.replace(from, () => to);
|
|
499
|
+
};
|
|
500
|
+
swap("\t\tlet real: string;\n", String.raw` // Errors never echo the absolute project root: they say "the project root" and quote only a relative input.
|
|
501
|
+
const shown = path.isAbsolute(raw) || /^[A-Za-z]:\//.test(raw) ? "(an absolute path)" : '"' + raw + '"';
|
|
502
|
+
let real: string;
|
|
503
|
+
`);
|
|
504
|
+
swap(
|
|
505
|
+
String.raw`throw new Error(label + '.path "' + raw + '" does not exist under the project root ' + root + ". Use a path relative to the project root.");`,
|
|
506
|
+
String.raw`throw new Error(label + ".path " + shown + " does not exist under the project root. Use a path relative to the project root.");`,
|
|
507
|
+
);
|
|
508
|
+
swap(
|
|
509
|
+
String.raw`throw new Error(label + '.path "' + raw + '" is outside the project root ' + root + ".");`,
|
|
510
|
+
String.raw`throw new Error(label + ".path " + shown + " is outside the project root.");`,
|
|
511
|
+
);
|
|
512
|
+
swap(String.raw`'.path "' + raw + '" is not a file; cite a file.'`, String.raw`'.path "' + relPosix + '" is not a file; cite a file.'`);
|
|
513
|
+
swap(String.raw`'.path "' + raw + '" is a secret, binary,`, String.raw`'.path "' + relPosix + '" is a secret, binary,`);
|
|
514
|
+
swap(String.raw`'.path "' + raw + '" is too large`, String.raw`'.path "' + relPosix + '" is too large`);
|
|
515
|
+
swap(
|
|
516
|
+
"\t\tconst stat = fs.statSync(real);\n",
|
|
517
|
+
String.raw` // Native fs errors quote the absolute real path; report only the relative path and the error code.
|
|
518
|
+
let stat: fs.Stats;
|
|
519
|
+
try {
|
|
520
|
+
stat = fs.statSync(real);
|
|
521
|
+
} catch (error) {
|
|
522
|
+
throw new Error(label + '.path "' + relPosix + '" could not be read (' + ((error as NodeJS.ErrnoException).code ?? "error") + ").");
|
|
523
|
+
}
|
|
524
|
+
`,
|
|
525
|
+
);
|
|
526
|
+
swap(
|
|
527
|
+
'\t\t\tconst text = fs.readFileSync(real, "utf8");\n',
|
|
528
|
+
String.raw` let text: string;
|
|
529
|
+
try {
|
|
530
|
+
text = fs.readFileSync(real, "utf8");
|
|
531
|
+
} catch (error) {
|
|
532
|
+
throw new Error(label + '.path "' + relPosix + '" could not be read (' + ((error as NodeJS.ErrnoException).code ?? "error") + ").");
|
|
533
|
+
}
|
|
534
|
+
`,
|
|
535
|
+
);
|
|
536
|
+
return text;
|
|
537
|
+
})();
|
|
538
|
+
/** 새 판에만 있는 줄. #32 판·그 이전 판에는 없다. */
|
|
539
|
+
const LEARN_EVIDENCE_MASKED_MARKER = String.raw`+ " is outside the project root.");`;
|
|
540
|
+
/** #32 판과 새 판에만 있는 줄. 그 이전 판(learnEvidenceLegacyBlock)과 순정에는 없다. */
|
|
541
|
+
const LEARN_EVIDENCE_SINCE_32 = "// A credential-shaped path or anchor is rejected";
|
|
542
|
+
|
|
543
|
+
/** 이슈 #32 이전에 배포된 learn 근거 블록(HEAD b8f1aad) 그대로다. 새 판(learnEvidenceBlock)이 이 본문을 대체한다(legacyPatched). */
|
|
544
|
+
const learnEvidenceLegacyBlock = String.raw`// HANSE: learn evidence citations
|
|
545
|
+
type LearnKind = "evidence" | "preference" | "procedure" | "hypothesis";
|
|
546
|
+
type LearnCitation = { path: string; anchor?: string };
|
|
547
|
+
type LearnEvidence = { kind?: LearnKind; citations: LearnCitation[] };
|
|
548
|
+
const LEARN_MAX_CITATIONS = 8;
|
|
549
|
+
const LEARN_MAX_ANCHOR_FILE_BYTES = 8 * 1024 * 1024;
|
|
550
|
+
|
|
551
|
+
const learnKindSchema = type("'evidence' | 'preference' | 'procedure' | 'hypothesis'").describe(
|
|
552
|
+
"what the lesson is: evidence = project fact, design decision or failure cause (needs citations in project scope); preference = how the user wants work done; procedure = how to do a task; hypothesis = unverified guess",
|
|
553
|
+
);
|
|
554
|
+
const learnCitationsSchema = type({
|
|
555
|
+
path: type("string").describe("file path relative to the project root"),
|
|
556
|
+
"anchor?": type("string").describe("a '## ' heading text, a header field name such as RECORD or SCOPE, or a line range such as 120-140"),
|
|
557
|
+
})
|
|
558
|
+
.array()
|
|
559
|
+
.describe("project files that record this lesson; required for kind evidence in project scope; checked for existence only, not for whether the file supports the lesson");
|
|
560
|
+
|
|
561
|
+
function learnProjectRoot(cwd: string): string {
|
|
562
|
+
let real = cwd;
|
|
563
|
+
try {
|
|
564
|
+
real = fs.realpathSync(cwd);
|
|
565
|
+
} catch {
|
|
566
|
+
// An unreadable cwd is reported by the citation path check below.
|
|
567
|
+
}
|
|
568
|
+
try {
|
|
569
|
+
const out = Bun.spawnSync(["git", "-C", real, "rev-parse", "--show-toplevel"], { stdout: "pipe", stderr: "ignore" });
|
|
570
|
+
const top = out.exitCode === 0 ? out.stdout.toString().trim() : "";
|
|
571
|
+
if (top) return fs.realpathSync(top);
|
|
572
|
+
} catch {
|
|
573
|
+
// No git: the session cwd is the project root.
|
|
574
|
+
}
|
|
575
|
+
return real;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function learnPathIgnored(root: string, rel: string): boolean {
|
|
579
|
+
try {
|
|
580
|
+
return Bun.spawnSync(["git", "-C", root, "check-ignore", "-q", "--", rel], { stdout: "ignore", stderr: "ignore" }).exitCode === 0;
|
|
581
|
+
} catch {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function learnHeadings(text: string): string[] {
|
|
587
|
+
const headings: string[] = [];
|
|
588
|
+
for (const line of text.split(/\r?\n/)) {
|
|
589
|
+
const match = /^#{1,6}\s+(.*?)\s*#*\s*$/.exec(line);
|
|
590
|
+
if (match) headings.push(match[1]!.replace(/\s+/g, " "));
|
|
591
|
+
}
|
|
592
|
+
return headings;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function learnAnchorFound(text: string, anchor: string): boolean {
|
|
596
|
+
const lines = text.split(/\r?\n/);
|
|
597
|
+
const range = /^(\d+)(?:-(\d+))?$/.exec(anchor);
|
|
598
|
+
if (range) {
|
|
599
|
+
const start = Number(range[1]);
|
|
600
|
+
const end = Number(range[2] ?? range[1]);
|
|
601
|
+
return start >= 1 && end >= start && end <= lines.length;
|
|
602
|
+
}
|
|
603
|
+
if (/^(RECORD|DATE|SCOPE|PATHS|STATUS)$/.test(anchor)) {
|
|
604
|
+
return lines.slice(0, 40).some(line => line.startsWith(anchor + ":") || line.startsWith("*" + anchor + ":"));
|
|
605
|
+
}
|
|
606
|
+
const want = anchor.replace(/^#{1,6}\s*/, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
607
|
+
if (!want) return false;
|
|
608
|
+
return learnHeadings(text).some(heading => {
|
|
609
|
+
const have = heading.toLowerCase();
|
|
610
|
+
return have === want || have.startsWith(want);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function resolveLearnEvidence(session: ToolSession, params: LearnParams): LearnEvidence {
|
|
615
|
+
const given = params.citations ?? [];
|
|
616
|
+
const projectScope = (params.scope ?? "project") === "project";
|
|
617
|
+
const kind = params.kind ?? (given.length > 0 ? "evidence" : undefined);
|
|
618
|
+
if (kind === undefined && projectScope) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
"learn needs a kind. Use kind evidence for a project fact, design decision or failure cause, and add citations (a list of path plus optional anchor) naming the project file that records it. Use kind preference (how the user wants work done), procedure (how to do a task) or hypothesis (unverified) when no project file records it.",
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
if (kind === "evidence" && projectScope && given.length === 0) {
|
|
624
|
+
throw new Error(
|
|
625
|
+
"kind evidence in project scope needs at least one citation: citations = [{ path: <file relative to the project root>, anchor: <optional: a '## ' heading text, a header field such as RECORD or SCOPE, or a line range like 120-140> }]. Cite the file that records it (a doc/history record, HANDOFF.md, source). If no file records it yet, write the record first, or use kind hypothesis.",
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (given.length > LEARN_MAX_CITATIONS) {
|
|
629
|
+
throw new Error("learn accepts at most " + LEARN_MAX_CITATIONS + " citations; cite the one or two files that matter most.");
|
|
630
|
+
}
|
|
631
|
+
if (given.length === 0) return { kind, citations: [] };
|
|
632
|
+
const root = learnProjectRoot(session.cwd);
|
|
633
|
+
const citations: LearnCitation[] = [];
|
|
634
|
+
const seen = new Set<string>();
|
|
635
|
+
given.forEach((citation, index) => {
|
|
636
|
+
const label = "citations[" + index + "]";
|
|
637
|
+
const raw = citation.path.trim().replaceAll("\\", "/");
|
|
638
|
+
if (!raw) throw new Error(label + ".path is empty.");
|
|
639
|
+
let real: string;
|
|
640
|
+
try {
|
|
641
|
+
real = fs.realpathSync(path.resolve(root, raw));
|
|
642
|
+
} catch {
|
|
643
|
+
throw new Error(label + '.path "' + raw + '" does not exist under the project root ' + root + ". Use a path relative to the project root.");
|
|
644
|
+
}
|
|
645
|
+
const rel = path.relative(root, real);
|
|
646
|
+
if (rel === "" || rel === ".." || rel.startsWith(".." + path.sep) || path.isAbsolute(rel)) {
|
|
647
|
+
throw new Error(label + '.path "' + raw + '" is outside the project root ' + root + ".");
|
|
648
|
+
}
|
|
649
|
+
const stat = fs.statSync(real);
|
|
650
|
+
if (!stat.isFile()) throw new Error(label + '.path "' + raw + '" is not a file; cite a file.');
|
|
651
|
+
const relPosix = rel.split(path.sep).join("/");
|
|
652
|
+
if (!eligibleFile(relPosix, stat.size, true) || learnPathIgnored(root, relPosix)) {
|
|
653
|
+
throw new Error(label + '.path "' + raw + '" is a secret, binary, empty, excluded or git-ignored file and cannot be cited.');
|
|
654
|
+
}
|
|
655
|
+
const anchor = citation.anchor?.trim();
|
|
656
|
+
if (anchor) {
|
|
657
|
+
if (stat.size > LEARN_MAX_ANCHOR_FILE_BYTES) throw new Error(label + '.path "' + raw + '" is too large to check an anchor in; cite it without an anchor or use a line range of a smaller file.');
|
|
658
|
+
const text = fs.readFileSync(real, "utf8");
|
|
659
|
+
if (!learnAnchorFound(text, anchor)) {
|
|
660
|
+
const headings = learnHeadings(text).slice(0, 8);
|
|
661
|
+
throw new Error(
|
|
662
|
+
label + '.anchor "' + anchor + '" was not found in ' + relPosix + ". Use the text of a '## ' heading, a header field name (RECORD, DATE, SCOPE, PATHS, STATUS) or a line range inside the file." +
|
|
663
|
+
(headings.length > 0 ? " Headings in the file: " + headings.join(" | ") : ""),
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
// Store a heading without its "## " prefix so the recall line reads path#heading.
|
|
668
|
+
const stored = anchor ? anchor.replace(/^#{1,6}\s*/, "") : undefined;
|
|
669
|
+
const key = relPosix + "#" + (stored ?? "");
|
|
670
|
+
if (seen.has(key)) return;
|
|
671
|
+
seen.add(key);
|
|
672
|
+
citations.push(stored ? { path: relPosix, anchor: stored } : { path: relPosix });
|
|
673
|
+
});
|
|
674
|
+
return { kind, citations };
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function learnEvidenceMetadata(evidence: LearnEvidence): Record<string, unknown> {
|
|
678
|
+
return { ...(evidence.kind ? { kind: evidence.kind } : {}), ...(evidence.citations.length > 0 ? { citations: evidence.citations } : {}) };
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** A topic lesson is written by upsertLearnTopic; the evidence of the newest revision replaces the previous one afterwards. */
|
|
682
|
+
function stampLearnTopicEvidence(
|
|
683
|
+
target: ReturnType<MnemopiSessionState["getScopedRetainTarget"]>,
|
|
684
|
+
topicInput: string,
|
|
685
|
+
evidence: LearnEvidence,
|
|
686
|
+
): void {
|
|
687
|
+
const topic = topicInput.normalize("NFKC").trim().toLowerCase();
|
|
688
|
+
const db = target.memory.conn;
|
|
689
|
+
db.transaction(() => {
|
|
690
|
+
const row = db
|
|
691
|
+
.query("SELECT id, metadata_json FROM working_memory WHERE json_extract(metadata_json, '$.topic_key') = ? ORDER BY timestamp DESC, id DESC LIMIT 1")
|
|
692
|
+
.get(topic) as { id: string; metadata_json: string } | null;
|
|
693
|
+
if (!row) throw new Error("Lesson stored, but its topic row was not found to attach the evidence.");
|
|
694
|
+
const metadata: Record<string, unknown> = JSON.parse(row.metadata_json);
|
|
695
|
+
delete metadata.kind;
|
|
696
|
+
delete metadata.citations;
|
|
697
|
+
db.query("UPDATE working_memory SET metadata_json = ? WHERE id = ?").run(JSON.stringify({ ...metadata, ...learnEvidenceMetadata(evidence) }), row.id);
|
|
698
|
+
}).immediate();
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const learnSchemaBase = type({
|
|
702
|
+
`;
|
|
703
|
+
|
|
208
704
|
/** embedding 보존 이전(출처 구분) learn topic upsert 적용본. 새 판(learnTopicUpsertBlock)이 이 본문을 대체한다(legacyPatched). */
|
|
209
705
|
const learnTopicUpsertLegacyBlock = `// HANSE: topic-key learn upsert
|
|
210
706
|
function upsertLearnTopic(
|
|
@@ -504,6 +1000,33 @@ const IRC_STEER_BRACKET_MEMBERS = `
|
|
|
504
1000
|
}
|
|
505
1001
|
}`;
|
|
506
1002
|
|
|
1003
|
+
/**
|
|
1004
|
+
* 2026-10-16 Codex realtime v3 목소리. core 18.11.1 의 `LIVE_VOICE_OPTIONS` 는 v1 아홉 개뿐이라 `live.voice` 가
|
|
1005
|
+
* 새 목소리(한국어 haetsal·hanul 등)를 거절한다. CUELO 통화는 Codex CLI 와 같은 frameless-bidi/avas 경로
|
|
1006
|
+
* (`gpt-live-1-codex`)이고, Codex CLI 는 그 경로를 `RealtimeVoicesList::builtin().v3`(openai/codex
|
|
1007
|
+
* codex-rs/protocol/src/protocol.rs, #52363) 로 검증한다. 기존 아홉 개와 기본값 `sol` 은 그대로 두고 v3 에만 있는
|
|
1008
|
+
* 열여섯 개를 같은 순서로 뒤에 붙인다. 한국어 두 목소리만 뜻을 라벨에 덧붙인다.
|
|
1009
|
+
* 회귀: patches/core-patch-test.ts [34].
|
|
1010
|
+
*/
|
|
1011
|
+
const CODEX_V3_LIVE_VOICES = [
|
|
1012
|
+
["aube", "Aube"],
|
|
1013
|
+
["bubbie", "Bubbie"],
|
|
1014
|
+
["haetsal", "Haetsal (햇살)"],
|
|
1015
|
+
["hanul", "Hanul (하늘)"],
|
|
1016
|
+
["himari", "Himari"],
|
|
1017
|
+
["leher", "Leher"],
|
|
1018
|
+
["miko", "Miko"],
|
|
1019
|
+
["neer", "Neer"],
|
|
1020
|
+
["porto", "Porto"],
|
|
1021
|
+
["rindo", "Rindo"],
|
|
1022
|
+
["rio", "Rio"],
|
|
1023
|
+
["rivage", "Rivage"],
|
|
1024
|
+
["selva", "Selva"],
|
|
1025
|
+
["sonna", "Sonna"],
|
|
1026
|
+
["tinta", "Tinta"],
|
|
1027
|
+
["viola", "Viola"],
|
|
1028
|
+
];
|
|
1029
|
+
|
|
507
1030
|
/** 각 항목: 원본 앵커를 찾아 patched 로 바꾼다. marker 가 있으면 이미 적용된 것으로 본다. */
|
|
508
1031
|
const EDITS = [
|
|
509
1032
|
{
|
|
@@ -3692,6 +4215,37 @@ function evalRejectionResult(error: unknown): AgentToolResult<EvalToolDetails |
|
|
|
3692
4215
|
...(params.tools?.length`,
|
|
3693
4216
|
}],
|
|
3694
4217
|
},
|
|
4218
|
+
{
|
|
4219
|
+
// 2026-10-17 18.11.3 공개 이슈 #33: upstream 은 seance 전용 task `model` 을 넣으며 validateSeanceParams 로 다른 agent 의
|
|
4220
|
+
// `model` 을 거절한다. planSpawns 가 이 검사를 speculative launch 와 dispatch 양쪽에서 부르므로 CUELO Maker 의
|
|
4221
|
+
// `model: '<후보 selector>:auto'` 발주가 executor 에 닿기 전에 막힌다. Maker(agent 이름 `maker`, CUELO task-guard 가
|
|
4222
|
+
// 허용하는 유일한 child) 만 문자열 selector 하나를 받는다. selector 자체(후보·`:auto`)는 command-guard maker-routing 이
|
|
4223
|
+
// 검사한다. seance 검사(sourceSession 필수·custom tools 금지·selector 형태)와 비-seance `sourceSession` 거절, 다른
|
|
4224
|
+
// 비-seance agent 의 `model` 거절은 그대로다. seance.ts 가 없는 옛 판(18.11.1 이하)은 index.ts 의 no-op 후보로 둔다.
|
|
4225
|
+
file: "src/task/seance.ts",
|
|
4226
|
+
marker: "// CUELO: the Maker carries its routed candidate",
|
|
4227
|
+
anchor: ` if (agent !== SEANCE_AGENT_NAME) {
|
|
4228
|
+
if (item.sourceSession !== undefined)
|
|
4229
|
+
return \`\\\`sourceSession\\\` is only accepted for the \\\`\${SEANCE_AGENT_NAME}\\\` agent.\`;
|
|
4230
|
+
if (item.model !== undefined) return \`\\\`model\\\` is only accepted for the \\\`\${SEANCE_AGENT_NAME}\\\` agent.\`;`,
|
|
4231
|
+
patched: ` if (agent !== SEANCE_AGENT_NAME) {
|
|
4232
|
+
if (item.sourceSession !== undefined)
|
|
4233
|
+
return \`\\\`sourceSession\\\` is only accepted for the \\\`\${SEANCE_AGENT_NAME}\\\` agent.\`;
|
|
4234
|
+
// CUELO: the Maker carries its routed candidate as one \`model\` selector ('<selector>:auto'); the command-guard
|
|
4235
|
+
// maker-routing hook validates that selector. Every other non-seance agent still rejects \`model\`.
|
|
4236
|
+
if (agent === "maker" && item.model !== undefined) {
|
|
4237
|
+
if (typeof item.model === "string" && item.model.trim().length > 0) return undefined;
|
|
4238
|
+
return "\`model\` for the \`maker\` agent must be one non-empty selector string.";
|
|
4239
|
+
}
|
|
4240
|
+
if (item.model !== undefined) return \`\\\`model\\\` is only accepted for the \\\`\${SEANCE_AGENT_NAME}\\\` agent.\`;`,
|
|
4241
|
+
alternates: [{
|
|
4242
|
+
file: "src/task/index.ts",
|
|
4243
|
+
excludes: "validateSeanceParams",
|
|
4244
|
+
marker: 'import { formatTaskResultSummary } from "./result-summary";',
|
|
4245
|
+
anchor: 'import { formatTaskResultSummary } from "./result-summary";',
|
|
4246
|
+
patched: 'import { formatTaskResultSummary } from "./result-summary";',
|
|
4247
|
+
}],
|
|
4248
|
+
},
|
|
3695
4249
|
{
|
|
3696
4250
|
// 18.3.3 BLOCK: upstream 은 `task`·`bash` 를 가진 SubAgent 정의에 `wait` 를 자동으로 더한다
|
|
3697
4251
|
// (executor.ts:3505-3516). CUELO 계약은 Main 전용 wait 다(harness-policy
|
|
@@ -5702,137 +6256,47 @@ class LocalTextBackend implements TextBackend {`,
|
|
|
5702
6256
|
patched: `import type { Settings } from "../config/settings";
|
|
5703
6257
|
import { cfgJudgmentProvider } from "../config/model-settings";`,
|
|
5704
6258
|
},
|
|
6259
|
+
// 2026-10-17 RETIRE(사용자 결정: Experiential 탈퇴): typesafe 경로를 Experiential Labs gateway 로 보내던 설정을 지웠으므로
|
|
6260
|
+
// 그 gateway decoder 의 32문항 한도(MAX_DECISION_QUESTIONS) 때문에 judge chain 이 typesafe 요청을 32개씩 나누던
|
|
6261
|
+
// 2026-10-09 항목 두 개(18.9.1 typesafe.ts·18.9.2 이후 judgment/index.ts import·#createJudge)를 없앤다. judge 는 이제
|
|
6262
|
+
// upstream createNativeJudge(catalog 상한) 그대로다. 이미 그 패치를 실은 설치(18.11.1 라이브)를 --apply·--revert 가
|
|
6263
|
+
// upstream 줄로 되돌리도록 옛 적용본을 legacyPatched 로 둔 no-op 후보로 남긴다. 회귀 시험 core-typesafe-split-test.ts 도 지웠다.
|
|
5705
6264
|
{
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
file: "../pi-ai/src/judgment/typesafe.ts",
|
|
5716
|
-
marker: "const TYPESAFE_MAX_QUESTIONS = 32;",
|
|
5717
|
-
anchor: "\treturn Math.min(BACKOFF_BASE_MS * 2 ** attempt, BACKOFF_MAX_MS);\n}\n\nexport class TypeSafeJudge implements Judge {\n",
|
|
5718
|
-
patched: [
|
|
5719
|
-
"\treturn Math.min(BACKOFF_BASE_MS * 2 ** attempt, BACKOFF_MAX_MS);",
|
|
5720
|
-
"}",
|
|
5721
|
-
"",
|
|
5722
|
-
"/**",
|
|
5723
|
-
" * CUELO: questions per request on the `typesafe` route. CUELO points that route at the Experiential Labs",
|
|
5724
|
-
" * gateway, whose decoder admits at most 32 questions (`MAX_DECISION_QUESTIONS`) and answers a larger set",
|
|
5725
|
-
" * with 400 \"Invalid decision request\". Questions are judged independently against the shared state, so a",
|
|
5726
|
-
" * larger set goes out as parts that each repeat the state.",
|
|
5727
|
-
" */",
|
|
5728
|
-
"const TYPESAFE_MAX_QUESTIONS = 32;",
|
|
5729
|
-
"",
|
|
5730
|
-
"/** `questions` itself when it has at most `max` entries, else parts of at most `max` in insertion order. */",
|
|
5731
|
-
"function questionParts(questions: Questions, max: number): Questions[] {",
|
|
5732
|
-
"\tconst ids = Object.keys(questions);",
|
|
5733
|
-
"\tif (ids.length <= max) return [questions];",
|
|
5734
|
-
"\tconst parts: Questions[] = [];",
|
|
5735
|
-
"\tfor (let start = 0; start < ids.length; start += max) {",
|
|
5736
|
-
"\t\tconst part: Questions = {};",
|
|
5737
|
-
"\t\tfor (const id of ids.slice(start, start + max)) part[id] = questions[id]!;",
|
|
5738
|
-
"\t\tparts.push(part);",
|
|
5739
|
-
"\t}",
|
|
5740
|
-
"\treturn parts;",
|
|
5741
|
-
"}",
|
|
5742
|
-
"",
|
|
5743
|
-
"/** The parts' responses as one: answers merged, token counts and billed cost summed where reported. */",
|
|
5744
|
-
"function mergeResponses(responses: SystemOneResponse[]): SystemOneResponse {",
|
|
5745
|
-
"\tif (responses.length === 1) return responses[0]!;",
|
|
5746
|
-
"\tconst total = (pick: (usage: SystemOneResponse[\"usage\"]) => number | undefined): number | undefined => {",
|
|
5747
|
-
"\t\tlet sum: number | undefined;",
|
|
5748
|
-
"\t\tfor (const response of responses) {",
|
|
5749
|
-
"\t\t\tconst value = pick(response.usage);",
|
|
5750
|
-
"\t\t\tif (value !== undefined) sum = (sum ?? 0) + value;",
|
|
5751
|
-
"\t\t}",
|
|
5752
|
-
"\t\treturn sum;",
|
|
5753
|
-
"\t};",
|
|
5754
|
-
"\treturn {",
|
|
5755
|
-
"\t\tmodel: responses[0]!.model,",
|
|
5756
|
-
"\t\tanswers: Object.assign({}, ...responses.map(response => response.answers)),",
|
|
5757
|
-
"\t\tusage: {",
|
|
5758
|
-
"\t\t\tinput_tokens: total(usage => usage.input_tokens),",
|
|
5759
|
-
"\t\t\toutput_tokens: total(usage => usage.output_tokens),",
|
|
5760
|
-
"\t\t\tcost: total(usage => usage.cost),",
|
|
5761
|
-
"\t\t},",
|
|
5762
|
-
"\t};",
|
|
5763
|
-
"}",
|
|
5764
|
-
"",
|
|
5765
|
-
"export class TypeSafeJudge implements Judge {",
|
|
6265
|
+
file: "src/judgment/index.ts",
|
|
6266
|
+
marker: 'import * as AIError from "@oh-my-pi/pi-ai/error";\n',
|
|
6267
|
+
anchor: 'import * as AIError from "@oh-my-pi/pi-ai/error";\n',
|
|
6268
|
+
patched: 'import * as AIError from "@oh-my-pi/pi-ai/error";\n',
|
|
6269
|
+
legacyPatched: [
|
|
6270
|
+
'import * as AIError from "@oh-my-pi/pi-ai/error";',
|
|
6271
|
+
"// CUELO: the typesafe route's per-request question cap (see #createJudge).",
|
|
6272
|
+
'import { NativeJudge as TypesafeGatewayJudge } from "@oh-my-pi/pi-ai";',
|
|
6273
|
+
'import { resolveJudgmentQuestionsMax } from "@oh-my-pi/pi-catalog/compat/judgment-questions";',
|
|
5766
6274
|
"",
|
|
5767
6275
|
].join("\n"),
|
|
5768
|
-
// 2026-10-13 18.9.2(ADAPT): upstream 이 TypeSafeJudge·typesafe.ts 를 지우고 NativeJudge 로 바꿨다. NativeJudge 는
|
|
5769
|
-
// catalog `judgment-questions-max` 로 요청을 나누지만(최대 4개 병렬, 한 조각 실패 시 나머지 중단) TypeSafe jev 는
|
|
5770
|
-
// 한도 없음으로 둔다. CUELO 는 typesafe 경로를 Experiential gateway(32문항)로 보내므로 judge chain 호출부
|
|
5771
|
-
// (judgment/index.ts)에서만 그 경로의 상한을 32로 넘긴다. 이 항목은 그 호출부가 쓰는 import 두 줄이다.
|
|
5772
|
-
alternates: [{
|
|
5773
|
-
file: "src/judgment/index.ts",
|
|
5774
|
-
requires: "\tcreateNativeJudge,\n",
|
|
5775
|
-
marker: 'import { resolveJudgmentQuestionsMax } from "@oh-my-pi/pi-catalog/compat/judgment-questions";',
|
|
5776
|
-
anchor: 'import * as AIError from "@oh-my-pi/pi-ai/error";\n',
|
|
5777
|
-
patched: [
|
|
5778
|
-
'import * as AIError from "@oh-my-pi/pi-ai/error";',
|
|
5779
|
-
"// CUELO: the typesafe route's per-request question cap (see #createJudge).",
|
|
5780
|
-
'import { NativeJudge as TypesafeGatewayJudge } from "@oh-my-pi/pi-ai";',
|
|
5781
|
-
'import { resolveJudgmentQuestionsMax } from "@oh-my-pi/pi-catalog/compat/judgment-questions";',
|
|
5782
|
-
"",
|
|
5783
|
-
].join("\n"),
|
|
5784
|
-
}],
|
|
5785
6276
|
},
|
|
5786
6277
|
{
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
"\t\
|
|
5794
|
-
"\t\t//
|
|
5795
|
-
"\t\tconst
|
|
5796
|
-
"\t\
|
|
5797
|
-
"\t\t\
|
|
5798
|
-
"\t\t\t\
|
|
5799
|
-
"\t\t\t\t\
|
|
5800
|
-
"\t\t\t\t\
|
|
5801
|
-
"\t\t\t\t\t\
|
|
5802
|
-
"\t\t\t\t\t\
|
|
5803
|
-
"\t\t\t\t\t\t
|
|
5804
|
-
"\t\t\t\t\t)
|
|
5805
|
-
"\t\t\t\t})
|
|
5806
|
-
"\t\t\t)
|
|
5807
|
-
"\t\t);",
|
|
6278
|
+
file: "src/judgment/index.ts",
|
|
6279
|
+
marker: "\t\t\tconst judge = createNativeJudge(model, { apiKey, headers });\n",
|
|
6280
|
+
anchor: "\t\t\tconst judge = createNativeJudge(model, { apiKey, headers });\n",
|
|
6281
|
+
patched: "\t\t\tconst judge = createNativeJudge(model, { apiKey, headers });\n",
|
|
6282
|
+
legacyPatched: [
|
|
6283
|
+
"\t\t\t// CUELO: past the typesafe question bound the judge splits the request. CUELO points the `typesafe` route at the",
|
|
6284
|
+
"\t\t\t// Experiential Labs gateway, whose decoder admits at most 32 questions (`MAX_DECISION_QUESTIONS`) and answers a",
|
|
6285
|
+
"\t\t\t// larger set with 400 \"Invalid decision request\"; the catalog leaves TypeSafe jev uncapped.",
|
|
6286
|
+
"\t\t\tconst judge =",
|
|
6287
|
+
"\t\t\t\tmodel.api === TYPESAFE_PROVIDER",
|
|
6288
|
+
"\t\t\t\t\t? new TypesafeGatewayJudge({",
|
|
6289
|
+
"\t\t\t\t\t\t\tapiKey,",
|
|
6290
|
+
"\t\t\t\t\t\t\theaders,",
|
|
6291
|
+
"\t\t\t\t\t\t\tapi: model.api,",
|
|
6292
|
+
"\t\t\t\t\t\t\tprovider: model.provider,",
|
|
6293
|
+
"\t\t\t\t\t\t\tmodel: model.requestModelId ?? model.id,",
|
|
6294
|
+
"\t\t\t\t\t\t\tbaseUrl: model.baseUrl,",
|
|
6295
|
+
"\t\t\t\t\t\t\tmaxQuestions: Math.min(resolveJudgmentQuestionsMax(model) ?? 32, 32),",
|
|
6296
|
+
"\t\t\t\t\t\t})",
|
|
6297
|
+
"\t\t\t\t\t: createNativeJudge(model, { apiKey, headers });",
|
|
5808
6298
|
"",
|
|
5809
6299
|
].join("\n"),
|
|
5810
|
-
// 2026-10-13 18.9.2(ADAPT): 위 항목과 짝. typesafe API 후보만 catalog 상한과 32 중 작은 값으로 NativeJudge 를 직접
|
|
5811
|
-
// 만들고(createNativeJudge 와 같은 wire id·baseUrl), 다른 judgment API 와 auth-gateway 는 upstream createNativeJudge 그대로다.
|
|
5812
|
-
alternates: [{
|
|
5813
|
-
file: "src/judgment/index.ts",
|
|
5814
|
-
requires: "\tcreateNativeJudge,\n",
|
|
5815
|
-
marker: "// CUELO: past the typesafe question bound",
|
|
5816
|
-
anchor: "\t\t\tconst judge = createNativeJudge(model, { apiKey, headers });\n",
|
|
5817
|
-
patched: [
|
|
5818
|
-
"\t\t\t// CUELO: past the typesafe question bound the judge splits the request. CUELO points the `typesafe` route at the",
|
|
5819
|
-
"\t\t\t// Experiential Labs gateway, whose decoder admits at most 32 questions (`MAX_DECISION_QUESTIONS`) and answers a",
|
|
5820
|
-
"\t\t\t// larger set with 400 \"Invalid decision request\"; the catalog leaves TypeSafe jev uncapped.",
|
|
5821
|
-
"\t\t\tconst judge =",
|
|
5822
|
-
"\t\t\t\tmodel.api === TYPESAFE_PROVIDER",
|
|
5823
|
-
"\t\t\t\t\t? new TypesafeGatewayJudge({",
|
|
5824
|
-
"\t\t\t\t\t\t\tapiKey,",
|
|
5825
|
-
"\t\t\t\t\t\t\theaders,",
|
|
5826
|
-
"\t\t\t\t\t\t\tapi: model.api,",
|
|
5827
|
-
"\t\t\t\t\t\t\tprovider: model.provider,",
|
|
5828
|
-
"\t\t\t\t\t\t\tmodel: model.requestModelId ?? model.id,",
|
|
5829
|
-
"\t\t\t\t\t\t\tbaseUrl: model.baseUrl,",
|
|
5830
|
-
"\t\t\t\t\t\t\tmaxQuestions: Math.min(resolveJudgmentQuestionsMax(model) ?? 32, 32),",
|
|
5831
|
-
"\t\t\t\t\t\t})",
|
|
5832
|
-
"\t\t\t\t\t: createNativeJudge(model, { apiKey, headers });",
|
|
5833
|
-
"",
|
|
5834
|
-
].join("\n"),
|
|
5835
|
-
}],
|
|
5836
6300
|
},
|
|
5837
6301
|
{
|
|
5838
6302
|
// 18.2.7 에는 `providers.judgmentProvider` schema 항목 자체가 없다(legacy key 목록에만
|
|
@@ -7488,12 +7952,14 @@ function operationFromNative(op: string): Operation | undefined {
|
|
|
7488
7952
|
marker: "// HANSE: recall provenance note",
|
|
7489
7953
|
anchor: "function formatRecallBlock(results: RecallResult[]): string {\n",
|
|
7490
7954
|
patched: recallProvenanceBlock + "function formatRecallBlock(results: RecallResult[]): string {\n",
|
|
7955
|
+
legacyPatched: recallProvenanceLegacyBlock + "function formatRecallBlock(results: RecallResult[]): string {\n",
|
|
7491
7956
|
alternates: [{
|
|
7492
7957
|
file: "src/mnemopi/state.ts",
|
|
7493
7958
|
requires: "findPersistedRecall",
|
|
7494
7959
|
marker: "// HANSE: recall provenance note",
|
|
7495
7960
|
anchor: "/** Renders a recall block and the offset where each result's text starts in it. */\nfunction formatRecallBlock(results: RecallResult[]): { text: string; offsets: number[] } {\n",
|
|
7496
7961
|
patched: recallProvenanceBlock + "/** Renders a recall block and the offset where each result's text starts in it. */\nfunction formatRecallBlock(results: RecallResult[]): { text: string; offsets: number[] } {\n",
|
|
7962
|
+
legacyPatched: recallProvenanceLegacyBlock + "/** Renders a recall block and the offset where each result's text starts in it. */\nfunction formatRecallBlock(results: RecallResult[]): { text: string; offsets: number[] } {\n",
|
|
7497
7963
|
}],
|
|
7498
7964
|
},
|
|
7499
7965
|
{
|
|
@@ -7541,6 +8007,73 @@ function operationFromNative(op: string): Operation | undefined {
|
|
|
7541
8007
|
patched: "\t\t\t\t\t\tfor (const result of results) {\n\t\t\t\t\t\t\t// HANSE: recall result carries its bank scope\n\t\t\t\t\t\t\tconst recallScope = this.scoped.global ? (target.bank === this.scoped.global.bank ? \"global\" : \"project\") : undefined;\n\t\t\t\t\t\t\tmergeRecallResult(merged, byId, byContent, { ...result, recallScope } as RecallResult);\n",
|
|
7542
8008
|
}],
|
|
7543
8009
|
},
|
|
8010
|
+
{
|
|
8011
|
+
// 2026-10-17: recall 은 facts 를 함께 싣는데 fact 줄은 metadata 가 없다. 출처 기억(source_memory_id)의 citations 만 줄에 옮겨 근거 후보가 사라지지 않게 한다.
|
|
8012
|
+
file: "src/mnemopi/state.ts",
|
|
8013
|
+
marker: "// HANSE: facts carry the citations of the memory they were taken from",
|
|
8014
|
+
anchor: "targetSucceeded = true;\n",
|
|
8015
|
+
patched: "targetSucceeded = true;\n\t\t\t\t\t// HANSE: facts carry the citations of the memory they were taken from\n\t\t\t\t\tattachFactCitations(target.memory, results);\n",
|
|
8016
|
+
},
|
|
8017
|
+
{
|
|
8018
|
+
// 2026-10-17 턴 단위 주입 게이트(doc/history/2026/10/17-memory-gate-citations): 자동 첫 턴 회상에서 상위 후보의 원시 dense
|
|
8019
|
+
// 코사인 최댓값이 기준 미만이면 그 턴에는 기억을 싣지 않는다. 기준은 설정이고 0 이면 끈다(기본 0.80, multilingual-e5 전용).
|
|
8020
|
+
file: "src/mnemopi/settings.ts",
|
|
8021
|
+
marker: "cfgMnemopiRecallDenseGate",
|
|
8022
|
+
anchor: "export const cfgMnemopiRecallContextTurns = register({ id: \"mnemopi.recallContextTurns\", type: \"number\", default: 3 });\n",
|
|
8023
|
+
patched: "export const cfgMnemopiRecallContextTurns = register({ id: \"mnemopi.recallContextTurns\", type: \"number\", default: 3 });\n\n// HANSE: turn-level dense gate setting (0 disables)\nexport const cfgMnemopiRecallDenseGate = register({ id: \"mnemopi.recallDenseGate\", type: \"number\", default: 0.8 });\n",
|
|
8024
|
+
},
|
|
8025
|
+
{
|
|
8026
|
+
file: "src/mnemopi/config.ts",
|
|
8027
|
+
marker: "\tcfgMnemopiRecallDenseGate,\n",
|
|
8028
|
+
anchor: "\tcfgMnemopiRecallContextTurns,\n",
|
|
8029
|
+
patched: "\tcfgMnemopiRecallContextTurns,\n\tcfgMnemopiRecallDenseGate,\n",
|
|
8030
|
+
},
|
|
8031
|
+
{
|
|
8032
|
+
file: "src/mnemopi/config.ts",
|
|
8033
|
+
marker: "\trecallDenseGate: number;\n",
|
|
8034
|
+
anchor: "\trecallContextTurns: number;\n",
|
|
8035
|
+
patched: "\trecallContextTurns: number;\n\t/** HANSE: turn-level dense gate; best raw dense cosine below this skips the first-turn injection. 0 disables. */\n\trecallDenseGate: number;\n",
|
|
8036
|
+
},
|
|
8037
|
+
{
|
|
8038
|
+
file: "src/mnemopi/config.ts",
|
|
8039
|
+
marker: "\t\trecallDenseGate: Math.min(1, Math.max(0, Number(cfgMnemopiRecallDenseGate.get(settings)) || 0)),\n",
|
|
8040
|
+
anchor: "\t\trecallContextTurns: Math.max(1, Math.floor(cfgMnemopiRecallContextTurns.get(settings))),\n",
|
|
8041
|
+
patched: "\t\trecallContextTurns: Math.max(1, Math.floor(cfgMnemopiRecallContextTurns.get(settings))),\n\t\trecallDenseGate: Math.min(1, Math.max(0, Number(cfgMnemopiRecallDenseGate.get(settings)) || 0)),\n",
|
|
8042
|
+
},
|
|
8043
|
+
{
|
|
8044
|
+
// recallForContext 는 압축 회상도 쓰므로 게이트는 세 번째 인자로 자동 첫 턴 주입 경로만 켠다.
|
|
8045
|
+
file: "src/mnemopi/state.ts",
|
|
8046
|
+
marker: "// HANSE: turn-level dense gate",
|
|
8047
|
+
anchor: "\tasync recallForContext(query: string, signal?: AbortSignal): Promise<string | undefined> {\n\t\treturn (await this.#recallBlock(query, signal))?.text || undefined;\n\t}\n\n\t/** Recalls `query` into a prompt block plus the memories it holds; undefined when aborted. */\n\tasync #recallBlock(query: string, signal?: AbortSignal): Promise<RecallBlock | undefined> {\n\t\tconst results = await this.collectScopedRecallResults(query);\n\t\tif (signal?.aborted) return undefined;\n\t\tif (results.length === 0) return { text: \"\", memories: [] };\n",
|
|
8048
|
+
patched: "\t/** `autoInjection` is passed only by the first-turn prompt injection; compaction recall and explicit recall never gate. */\n\tasync recallForContext(query: string, signal?: AbortSignal, autoInjection = false): Promise<string | undefined> {\n\t\treturn (await this.#recallBlock(query, signal, autoInjection))?.text || undefined;\n\t}\n\n"
|
|
8049
|
+
+ "\t// HANSE: turn-level dense gate\n"
|
|
8050
|
+
+ "\t/**\n\t * Whole-turn skip for the automatic first-turn recall: when even the best raw dense cosine among the recalled candidates\n\t * is below `mnemopi.recallDenseGate`, the request matches nothing stored and no memory is injected this turn.\n\t * Calibrated for multilingual-e5 (other models have another cosine range), so it is off for them. A turn with no dense\n\t * signal at all (embeddings off, rows not embedded yet) never gates.\n\t */\n"
|
|
8051
|
+
+ "\t#recallGated(results: readonly RecallResult[]): boolean {\n"
|
|
8052
|
+
+ "\t\tconst gate = this.config.recallDenseGate;\n"
|
|
8053
|
+
+ "\t\tconst provider = this.config.providerOptions;\n"
|
|
8054
|
+
+ "\t\tif (!(typeof gate === \"number\" && gate > 0)) return false;\n"
|
|
8055
|
+
+ "\t\tif (!provider || provider.noEmbeddings || provider.embeddingApiUrl || !/(^|[/_-])e5([-_]|$)/i.test(provider.embeddingModel ?? \"\")) return false;\n"
|
|
8056
|
+
+ "\t\tlet best = 0;\n"
|
|
8057
|
+
+ "\t\tfor (const result of results) if (typeof result.dense_score === \"number\" && result.dense_score > best) best = result.dense_score;\n"
|
|
8058
|
+
+ "\t\tif (best <= 0 || best >= gate) return false;\n"
|
|
8059
|
+
+ "\t\tlogger.debug(\"Mnemopi: first-turn recall skipped by dense gate\", { best, gate });\n"
|
|
8060
|
+
+ "\t\tthis.session.sessionManager.appendCustomEntry?.(\"mnemopi-recall-gated\", { dense: best, gate, candidates: results.length });\n"
|
|
8061
|
+
+ "\t\treturn true;\n"
|
|
8062
|
+
+ "\t}\n\n"
|
|
8063
|
+
+ "\t/** Recalls `query` into a prompt block plus the memories it holds; undefined when aborted. */\n\tasync #recallBlock(query: string, signal?: AbortSignal, autoInjection = false): Promise<RecallBlock | undefined> {\n\t\tconst results = await this.collectScopedRecallResults(query);\n\t\tif (signal?.aborted) return undefined;\n\t\tif (results.length === 0) return { text: \"\", memories: [] };\n\t\tif (autoInjection && this.#recallGated(results)) return { text: \"\", memories: [] };\n",
|
|
8064
|
+
},
|
|
8065
|
+
{
|
|
8066
|
+
file: "src/mnemopi/state.ts",
|
|
8067
|
+
marker: "await this.#recallBlock(truncated, signal, true);",
|
|
8068
|
+
anchor: "\t\tconst recall = await this.#recallBlock(truncated, signal);\n",
|
|
8069
|
+
patched: "\t\tconst recall = await this.#recallBlock(truncated, signal, true);\n",
|
|
8070
|
+
},
|
|
8071
|
+
{
|
|
8072
|
+
file: "src/mnemopi/state.ts",
|
|
8073
|
+
marker: "await this.#recallBlock(truncated, undefined, true);",
|
|
8074
|
+
anchor: "\t\t\t\tconst recall = await this.#recallBlock(truncated);\n",
|
|
8075
|
+
patched: "\t\t\t\tconst recall = await this.#recallBlock(truncated, undefined, true);\n",
|
|
8076
|
+
},
|
|
7544
8077
|
{
|
|
7545
8078
|
file: "src/tools/learn.ts",
|
|
7546
8079
|
marker: 'import { redactMemorySecrets } from "../memory-backend/redact";',
|
|
@@ -7561,12 +8094,24 @@ function operationFromNative(op: string): Operation | undefined {
|
|
|
7561
8094
|
},
|
|
7562
8095
|
{
|
|
7563
8096
|
// 출처 구분: 갱신해도 최초 session/cwd 는 남고 updated_* 가 최신 정정자다. 실효 embedding 입력이 같으면 벡터를 둔다(#30).
|
|
7564
|
-
//
|
|
8097
|
+
// 이슈 #32: topic 근거(kind·citations)는 이 함수가 본문·임베딩과 같은 쓰기 단위로 저장한다.
|
|
8098
|
+
// 본 후보의 legacyPatched 는 #32 이전에 배포된 embedding 보존판(HEAD b8f1aad)이고, alternates 는 embedding 보존 이전(출처 구분) 적용본을 옮긴다.
|
|
7565
8099
|
file: "src/tools/learn.ts",
|
|
7566
|
-
marker: "// HANSE: learn topic
|
|
8100
|
+
marker: "// HANSE: learn topic evidence is stored with the topic row",
|
|
7567
8101
|
anchor: "export class LearnTool implements AgentTool<LearnSchema> {",
|
|
7568
|
-
|
|
8102
|
+
excludes: learnTopicUpsertLegacyBlock,
|
|
8103
|
+
legacyPatched: learnTopicUpsertPreEvidenceBlock,
|
|
7569
8104
|
patched: learnTopicUpsertBlock,
|
|
8105
|
+
alternates: [
|
|
8106
|
+
{
|
|
8107
|
+
file: "src/tools/learn.ts",
|
|
8108
|
+
marker: "// HANSE: learn topic evidence is stored with the topic row",
|
|
8109
|
+
anchor: "export class LearnTool implements AgentTool<LearnSchema> {",
|
|
8110
|
+
requires: learnTopicUpsertLegacyBlock,
|
|
8111
|
+
legacyPatched: learnTopicUpsertLegacyBlock,
|
|
8112
|
+
patched: learnTopicUpsertBlock,
|
|
8113
|
+
},
|
|
8114
|
+
],
|
|
7570
8115
|
},
|
|
7571
8116
|
{
|
|
7572
8117
|
// learn 은 rememberScoped 가 돌려준 기억 id 를 버리고 "Lesson stored." 만 알렸다. 교훈을 뒤에서
|
|
@@ -7574,10 +8119,22 @@ function operationFromNative(op: string): Operation | undefined {
|
|
|
7574
8119
|
// 18.3.3은 호출을 `rememberScoped(memory, {...}, target)` 3인자로 펼쳤다(learn.ts:110-134,
|
|
7575
8120
|
// global scope 지원). 반환값(기억 id)은 그대로 string 이다.
|
|
7576
8121
|
file: "src/tools/learn.ts",
|
|
8122
|
+
// 이슈 #32: topic 호출이 근거(evidence)를 함께 넘긴다. 본 후보의 legacyPatched 는 근거를 넘기지 않던 배포본(HEAD b8f1aad)이다.
|
|
7577
8123
|
marker: "// HANSE: learn reports memory id",
|
|
7578
8124
|
anchor: "\t\t\ttry {\n\t\t\t\tstate.rememberScoped(\n",
|
|
7579
|
-
|
|
7580
|
-
|
|
8125
|
+
excludes: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst memoryId = state.rememberScoped(\n",
|
|
8126
|
+
legacyPatched: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst topicResult = params.topic ? upsertLearnTopic(state, params, target ?? state.getScopedRetainTarget()) : undefined;\n\t\t\t\tconst memoryId = topicResult?.id ?? state.rememberScoped(\n",
|
|
8127
|
+
patched: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst topicResult = params.topic ? upsertLearnTopic(state, params, target ?? state.getScopedRetainTarget(), evidence) : undefined;\n\t\t\t\tconst memoryId = topicResult?.id ?? state.rememberScoped(\n",
|
|
8128
|
+
alternates: [
|
|
8129
|
+
{
|
|
8130
|
+
file: "src/tools/learn.ts",
|
|
8131
|
+
marker: "// HANSE: learn reports memory id",
|
|
8132
|
+
anchor: "\t\t\ttry {\n\t\t\t\tstate.rememberScoped(\n",
|
|
8133
|
+
requires: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst memoryId = state.rememberScoped(\n",
|
|
8134
|
+
legacyPatched: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst memoryId = state.rememberScoped(\n",
|
|
8135
|
+
patched: "\t\t\ttry {\n\t\t\t\t// HANSE: learn reports memory id\n\t\t\t\tconst topicResult = params.topic ? upsertLearnTopic(state, params, target ?? state.getScopedRetainTarget(), evidence) : undefined;\n\t\t\t\tconst memoryId = topicResult?.id ?? state.rememberScoped(\n",
|
|
8136
|
+
},
|
|
8137
|
+
],
|
|
7581
8138
|
},
|
|
7582
8139
|
{
|
|
7583
8140
|
file: "src/tools/learn.ts",
|
|
@@ -7586,6 +8143,72 @@ function operationFromNative(op: string): Operation | undefined {
|
|
|
7586
8143
|
legacyPatched: "\t\t\t\t\ttarget,\n\t\t\t\t);\n\t\t\t\tif (memoryId) memoryMessage = `Lesson stored (id: ${memoryId})`;\n\t\t\t} catch (error) {\n",
|
|
7587
8144
|
patched: "\t\t\t\t\ttarget,\n\t\t\t\t);\n\t\t\t\tif (memoryId) memoryMessage = `Lesson stored (id: ${memoryId}${topicResult ? `, revision: ${topicResult.revision}` : \"\"})`;\n\t\t\t} catch (error) {\n",
|
|
7588
8145
|
},
|
|
8146
|
+
{
|
|
8147
|
+
// 2026-10-17 근거형 learn: 검증·metadata 보조 함수와 kind·citations schema 는 learnSchemaBase 앞에 한 덩어리로 둔다.
|
|
8148
|
+
file: "src/tools/learn.ts",
|
|
8149
|
+
// legacyPatched 는 citation 의 비밀 형태 거절 이전 적용본이다(#32).
|
|
8150
|
+
// 2026-10-17 18.11.3 이식: 경로 오류가 루트를 가린 새 판. #32 배포본(learnEvidenceIssue32Block)은 아래 alternate 의
|
|
8151
|
+
// legacyPatched 로 받는다. 두 후보는 #32 이후에만 있는 비밀 거절 주석으로 서로 배타적이다(순정·옛 판 = 본 후보).
|
|
8152
|
+
marker: LEARN_EVIDENCE_MASKED_MARKER,
|
|
8153
|
+
excludes: LEARN_EVIDENCE_SINCE_32,
|
|
8154
|
+
anchor: "const learnSchemaBase = type({\n",
|
|
8155
|
+
legacyPatched: learnEvidenceLegacyBlock,
|
|
8156
|
+
patched: learnEvidenceBlock,
|
|
8157
|
+
alternates: [{
|
|
8158
|
+
file: "src/tools/learn.ts",
|
|
8159
|
+
requires: LEARN_EVIDENCE_SINCE_32,
|
|
8160
|
+
marker: LEARN_EVIDENCE_MASKED_MARKER,
|
|
8161
|
+
anchor: "const learnSchemaBase = type({\n",
|
|
8162
|
+
legacyPatched: learnEvidenceIssue32Block,
|
|
8163
|
+
patched: learnEvidenceBlock,
|
|
8164
|
+
}],
|
|
8165
|
+
},
|
|
8166
|
+
{
|
|
8167
|
+
file: "src/tools/learn.ts",
|
|
8168
|
+
marker: 'import { eligibleFile } from "./jfind/tree";',
|
|
8169
|
+
anchor: 'import type { ToolSession } from ".";\n',
|
|
8170
|
+
patched: 'import type { ToolSession } from ".";\nimport * as fs from "node:fs";\nimport * as path from "node:path";\nimport { eligibleFile } from "./jfind/tree";\n',
|
|
8171
|
+
},
|
|
8172
|
+
{
|
|
8173
|
+
file: "src/tools/learn.ts",
|
|
8174
|
+
marker: '\t"kind?": learnKindSchema,\n\t"citations?": learnCitationsSchema,\n\t"skill?": learnSkillSchema,\n});\n\n/** Offered only',
|
|
8175
|
+
anchor: '\t"skill?": learnSkillSchema,\n});\n\n/** Offered only',
|
|
8176
|
+
patched: '\t"kind?": learnKindSchema,\n\t"citations?": learnCitationsSchema,\n\t"skill?": learnSkillSchema,\n});\n\n/** Offered only',
|
|
8177
|
+
},
|
|
8178
|
+
{
|
|
8179
|
+
file: "src/tools/learn.ts",
|
|
8180
|
+
marker: '\t"kind?": learnKindSchema,\n\t"citations?": learnCitationsSchema,\n\t"skill?": learnSkillSchema,\n});\n\ntype LearnSchema',
|
|
8181
|
+
anchor: '\t"skill?": learnSkillSchema,\n});\n\ntype LearnSchema',
|
|
8182
|
+
patched: '\t"kind?": learnKindSchema,\n\t"citations?": learnCitationsSchema,\n\t"skill?": learnSkillSchema,\n});\n\ntype LearnSchema',
|
|
8183
|
+
},
|
|
8184
|
+
{
|
|
8185
|
+
// 저장 전에 검증한다. mnemopi 만 metadata 를 남길 수 있으므로 다른 백엔드는 kind·citations 를 요구하지 않는다.
|
|
8186
|
+
file: "src/tools/learn.ts",
|
|
8187
|
+
marker: "// HANSE: learn evidence is validated before anything is written",
|
|
8188
|
+
anchor: '\t\tconst backend = cfgMemoryBackend.get(this.session.settings);\n\t\tif (params.scope === "global" && backend !== "mnemopi") {\n',
|
|
8189
|
+
patched: '\t\tconst backend = cfgMemoryBackend.get(this.session.settings);\n\t\t// HANSE: learn evidence is validated before anything is written\n\t\tconst evidence: LearnEvidence = backend === "mnemopi" ? resolveLearnEvidence(this.session, params) : { citations: [] };\n\t\tif (params.scope === "global" && backend !== "mnemopi") {\n',
|
|
8190
|
+
},
|
|
8191
|
+
{
|
|
8192
|
+
file: "src/tools/learn.ts",
|
|
8193
|
+
marker: "...learnEvidenceMetadata(evidence),",
|
|
8194
|
+
anchor: '\t\t\t\t\t\t\ttool: "learn",\n\t\t\t\t\t\t},\n',
|
|
8195
|
+
patched: '\t\t\t\t\t\t\ttool: "learn",\n\t\t\t\t\t\t\t...learnEvidenceMetadata(evidence),\n\t\t\t\t\t\t},\n',
|
|
8196
|
+
},
|
|
8197
|
+
{
|
|
8198
|
+
// 이슈 #32: 별도 stamp 호출(저장 뒤 두 번째 쓰기)을 없앴다. 근거는 upsertLearnTopic 이 저장한다.
|
|
8199
|
+
// legacyPatched 는 그 stamp 호출을 넣었던 배포본(HEAD b8f1aad)이다. 원본·#32 이후 상태에서는 앵커를 그대로 둔다.
|
|
8200
|
+
file: "src/tools/learn.ts",
|
|
8201
|
+
marker: "// HANSE: learn topic evidence has no separate stamp write",
|
|
8202
|
+
anchor: '\t\t} else if (backend === "local") {\n',
|
|
8203
|
+
legacyPatched: '\t\t\t// HANSE: learn evidence of a topic lesson follows its newest revision\n\t\t\tif (params.topic) stampLearnTopicEvidence(target ?? state.getScopedRetainTarget(), params.topic, evidence);\n\t\t} else if (backend === "local") {\n',
|
|
8204
|
+
patched: '\t\t\t// HANSE: learn topic evidence has no separate stamp write\n\t\t} else if (backend === "local") {\n',
|
|
8205
|
+
},
|
|
8206
|
+
{
|
|
8207
|
+
file: "src/prompts/tools/learn.md",
|
|
8208
|
+
marker: "`kind` is required in project scope",
|
|
8209
|
+
anchor: "Capture sparingly, specifically: one strong reusable lesson > several vague ones.",
|
|
8210
|
+
patched: "`kind` is required in project scope. `evidence` = a project fact, design decision, or failure cause: add `citations`, each a `path` relative to the project root plus an optional `anchor` (a `## ` heading text, a header field such as `RECORD`/`SCOPE`, or a line range), naming the file that records it. The save is rejected when a path or anchor does not exist; the check does not judge whether the file supports the lesson, so cite only what you read. Recall shows citations as places to verify, not as proof. `preference` (how the user wants work done), `procedure` (how to do a task) and `hypothesis` (unverified) need no citation.\n\nCapture sparingly, specifically: one strong reusable lesson > several vague ones.",
|
|
8211
|
+
},
|
|
7589
8212
|
{
|
|
7590
8213
|
// 2026-09-27: 자동 주입 <memories> 는 세션 파일에 남지 않아, Main 단독 세션에서 어떤 교훈이 전달됐는지
|
|
7591
8214
|
// 셀 수 없었다(위임 attempt 만 routing_verdict appliedLessons 로 연결됨). 첫 턴 회상이 확정될 때 전달한
|
|
@@ -13954,6 +14577,20 @@ function createSubagentRunMonitor(args: RunMonitorArgs): SubagentRunMonitor {`,
|
|
|
13954
14577
|
anchor: '\t\t\t...(latest.type === "task" ? { agentUrlId: current?.agentId ?? latest.id } : {}),',
|
|
13955
14578
|
patched: '\t\t\t...(latest.type === "task" ? { agentUrlId: j.completion?.agentId ?? current?.agentId ?? latest.id } : {}),\n\t\t\t...(options.includeResults !== false && j.completion ? { completion: j.completion } : {}),',
|
|
13956
14579
|
},
|
|
14580
|
+
{
|
|
14581
|
+
// Codex realtime v3 목소리(위 CODEX_V3_LIVE_VOICES 주석). CUELO 앱이 읽는 소스 판.
|
|
14582
|
+
file: "src/live/voices.ts",
|
|
14583
|
+
marker: "\t// CUELO: Codex realtime v3 voices",
|
|
14584
|
+
anchor: '\t{ value: "vale", label: "Vale" },\n] as const;',
|
|
14585
|
+
patched: `\t{ value: "vale", label: "Vale" },\n\t// CUELO: Codex realtime v3 voices (gpt-live-1-codex, openai/codex RealtimeVoicesList::builtin().v3).\n${CODEX_V3_LIVE_VOICES.map(([value, label]) => `\t{ value: "${value}", label: "${label}" },\n`).join("")}] as const;`,
|
|
14586
|
+
},
|
|
14587
|
+
{
|
|
14588
|
+
// 같은 목록의 CLI 번들(dist/cli.js, Linux `omp`) 판. 같은 config.yml 을 읽으므로 CLI 도 새 값을 거절하지 않게 한다.
|
|
14589
|
+
file: "dist/cli.js",
|
|
14590
|
+
marker: '{value:"haetsal",label:"Haetsal (햇살)"}',
|
|
14591
|
+
anchor: '{value:"spruce",label:"Spruce"},{value:"vale",label:"Vale"}]',
|
|
14592
|
+
patched: `{value:"spruce",label:"Spruce"},{value:"vale",label:"Vale"}${CODEX_V3_LIVE_VOICES.map(([value, label]) => `,{value:"${value}",label:"${label}"}`).join("")}]`,
|
|
14593
|
+
},
|
|
13957
14594
|
];
|
|
13958
14595
|
// EDITS 문자열의 줄 끝을 LF로 통일한다. 이 파일의 작업 사본이 CRLF여도 core 파일(LF)과
|
|
13959
14596
|
// 비교·치환이 어긋나지 않는다. core 파일 자체의 줄 끝은 건드리지 않는다.
|
|
@@ -14317,6 +14954,216 @@ interface ComputerElementRef
|
|
|
14317
14954
|
patched: swap(agentOptional.patched, ...options),
|
|
14318
14955
|
}];
|
|
14319
14956
|
}
|
|
14957
|
+
// 2026-10-17 18.11.3 이식(공개 이슈 #31·#33 포함). 18.11.1 후보는 한 바이트도 바꾸지 않고, 18.11.3 순정에서 앵커를 잃은 항목마다
|
|
14958
|
+
// 그 판에만 성립하는 후보를 더한다. marker 를 공유하는 후보는 18.11.3 에만 있는 문자열로 requires(새 후보)·excludes(옛 후보)를
|
|
14959
|
+
// 짝지어 서로 배타적으로 둔다.
|
|
14960
|
+
// - ADAPT: IRC steer bracket(in-flight epoch), wake-turn 판정·완료 기록(publishMessageResult·messageResult), Vercel judge
|
|
14961
|
+
// (#31: #withCandidate 의 셋째 인자가 images 배열), task schema 의 seance 필드, recall 의 tool 가중(env 로 바뀜).
|
|
14962
|
+
// - RETIRE: upstream 이 seance 와 함께 넣은 task model 타입·전달 줄(같은 줄을 두 번 넣지 않는다. 특히 TaskItem·TaskParams 의
|
|
14963
|
+
// `model?: string` 은 upstream 의 `string | string[]` 과 겹쳐 TS2717 이 된다), 번들의 advisor transcript filter 새 이름.
|
|
14964
|
+
{
|
|
14965
|
+
const one = (predicate, label) => {
|
|
14966
|
+
const found = EDITS.filter(predicate);
|
|
14967
|
+
if (found.length !== 1) throw new Error(`18.11.3 후보를 만들 수 없다: ${label} ${found.length}/1`);
|
|
14968
|
+
return found[0];
|
|
14969
|
+
};
|
|
14970
|
+
const swap = (text, from, to) => {
|
|
14971
|
+
const found = text.split(from).length - 1;
|
|
14972
|
+
if (found !== 1) throw new Error(`18.11.3 후보를 만들 수 없다: ${JSON.stringify(from.slice(0, 80))} ${found}/1`);
|
|
14973
|
+
return text.replace(from, () => to);
|
|
14974
|
+
};
|
|
14975
|
+
const noop = (file, text, extra = {}) => ({ file, marker: text, anchor: text, patched: text, ...extra });
|
|
14976
|
+
|
|
14977
|
+
// IRC steer continuation bracket: #beginInFlight 가 epoch 를 돌려주고 #endInFlight(epoch, onSettled) 가 abort 뒤(#resetInFlight 가
|
|
14978
|
+
// epoch 를 올린 뒤) 늦게 온 settle 콜백을 버린다. 18.11.1 에서는 그 늦은 콜백이 flush 되어 관찰자가 닫혔으므로, epoch 가
|
|
14979
|
+
// 지났으면 관찰자를 직접 닫아 부모가 받을 답 하나를 그대로 지킨다.
|
|
14980
|
+
const EPOCH_18113 = "#endInFlight(epoch: number";
|
|
14981
|
+
const steerOpen = one(entry => entry.marker === "const steerObservation = this.#beginIrcSteerContinuationObservation()", "steer bracket open");
|
|
14982
|
+
const steerOpenTurnEnded = steerOpen.alternates.find(candidate => candidate.requires === "turnEnded: boolean;");
|
|
14983
|
+
steerOpenTurnEnded.excludes = EPOCH_18113;
|
|
14984
|
+
const epochBegin = ["\t\t\t\tthis.#beginInFlight();\n", "\t\t\t\tconst inFlightEpoch = this.#beginInFlight();\n"];
|
|
14985
|
+
steerOpen.alternates.push({
|
|
14986
|
+
file: steerOpen.file,
|
|
14987
|
+
requires: EPOCH_18113,
|
|
14988
|
+
marker: steerOpen.marker,
|
|
14989
|
+
anchor: swap(steerOpenTurnEnded.anchor, ...epochBegin),
|
|
14990
|
+
patched: swap(steerOpenTurnEnded.patched, ...epochBegin),
|
|
14991
|
+
});
|
|
14992
|
+
const steerClose = one(entry => entry.marker === "IRC steer continuation observer failed to finish", "steer bracket close");
|
|
14993
|
+
steerClose.excludes = EPOCH_18113;
|
|
14994
|
+
(steerClose.alternates ??= []).push({
|
|
14995
|
+
file: steerClose.file,
|
|
14996
|
+
requires: EPOCH_18113,
|
|
14997
|
+
marker: steerClose.marker,
|
|
14998
|
+
anchor: swap(steerClose.anchor, "this.#endInFlight();", "this.#endInFlight(inFlightEpoch);"),
|
|
14999
|
+
patched: ` this.#usagePreflightReadyForNextModelCall = false;
|
|
15000
|
+
const finishSteerObservation = steerObservation?.finish
|
|
15001
|
+
? async () => {
|
|
15002
|
+
this.#ircTurnBracketOpen = false;
|
|
15003
|
+
try {
|
|
15004
|
+
await steerObservation.finish?.(
|
|
15005
|
+
steerObservationError,
|
|
15006
|
+
undefined,
|
|
15007
|
+
this.#takeAdoptedParentSteerRecords(),
|
|
15008
|
+
);
|
|
15009
|
+
} catch (error) {
|
|
15010
|
+
logger.warn("IRC steer continuation observer failed to finish", {
|
|
15011
|
+
error: String(error),
|
|
15012
|
+
});
|
|
15013
|
+
}
|
|
15014
|
+
}
|
|
15015
|
+
: undefined;
|
|
15016
|
+
// An abort (#resetInFlight) already released this bracket and #endInFlight drops a stale settle
|
|
15017
|
+
// callback, yet the steering parent is still owed its one answer: close the observer here then.
|
|
15018
|
+
if (finishSteerObservation && inFlightEpoch !== this.#inFlightEpoch) void finishSteerObservation();
|
|
15019
|
+
else this.#endInFlight(inFlightEpoch, finishSteerObservation);
|
|
15020
|
+
}
|
|
15021
|
+
},
|
|
15022
|
+
{
|
|
15023
|
+
delayMs: options.delayMs,`,
|
|
15024
|
+
});
|
|
15025
|
+
|
|
15026
|
+
// Wake turn: upstream 이 owner 판정을 ownerWoken 변수로 빼고(publishMessageResult 에도 쓴다), 완료 인자에 publishMessageResult·
|
|
15027
|
+
// signal 을, 결과 분기에 messageResult 를 더했다. CUELO 의 runningTurn 예외는 wake job 선등록에만 걸고, ircWakeTurn 표시와
|
|
15028
|
+
// 완료 기록은 같은 자리에 그대로 둔다.
|
|
15029
|
+
const wakeRegister = one(entry => entry.marker.startsWith("if (bracket?.runningTurn !== true && wakeSources(records, id)"), "wake register");
|
|
15030
|
+
const ownerWoken = "\t\tconst ownerWoken = wakeSources(records, id).some(source => source.from === ownerId);\n";
|
|
15031
|
+
wakeRegister.alternates.push({
|
|
15032
|
+
file: wakeRegister.file,
|
|
15033
|
+
marker: "if (bracket?.runningTurn !== true && ownerWoken) {",
|
|
15034
|
+
anchor: `${ownerWoken}\t\tif (ownerWoken) registerWakeJob();`,
|
|
15035
|
+
patched: `${ownerWoken}\t\t// A bracket opened over a turn that was already running (a parent steer
|
|
15036
|
+
// adopted mid-turn) is not parent-woken work: that turn may leave the steer
|
|
15037
|
+
// queued, and the continuation that consumes it owns the answer. Keep the
|
|
15038
|
+
// yield-time registration for it so a stranded turn stays silent.
|
|
15039
|
+
if (bracket?.runningTurn !== true && ownerWoken) {
|
|
15040
|
+
registerWakeJob();
|
|
15041
|
+
}`,
|
|
15042
|
+
});
|
|
15043
|
+
const PUBLISH_FIELD_18113 = "publishMessageResult?: boolean;";
|
|
15044
|
+
const wakeField = one(entry => entry.marker === "ircWakeTurn?: boolean;", "ircWakeTurn field");
|
|
15045
|
+
const publishField = ["\tfollowUpTurn?: boolean;\n", `\tfollowUpTurn?: boolean;\n\t${PUBLISH_FIELD_18113}\n`];
|
|
15046
|
+
wakeField.excludes = PUBLISH_FIELD_18113;
|
|
15047
|
+
(wakeField.alternates ??= []).push({
|
|
15048
|
+
file: wakeField.file,
|
|
15049
|
+
requires: PUBLISH_FIELD_18113,
|
|
15050
|
+
marker: wakeField.marker,
|
|
15051
|
+
anchor: swap(wakeField.anchor, ...publishField),
|
|
15052
|
+
patched: swap(wakeField.patched, ...publishField),
|
|
15053
|
+
});
|
|
15054
|
+
const PUBLISH_ARG_18113 = "\t\t\t\t\tpublishMessageResult: ownerWoken || userPrompted,";
|
|
15055
|
+
const wakeArg = one(entry => entry.marker === "\t\t\t\t\tircWakeTurn: true,", "ircWakeTurn argument");
|
|
15056
|
+
wakeArg.excludes = PUBLISH_ARG_18113;
|
|
15057
|
+
(wakeArg.alternates ??= []).push({
|
|
15058
|
+
file: wakeArg.file,
|
|
15059
|
+
requires: PUBLISH_ARG_18113,
|
|
15060
|
+
marker: wakeArg.marker,
|
|
15061
|
+
anchor: `\t\t\t\t\tfollowUpTurn: true,\n${PUBLISH_ARG_18113}`,
|
|
15062
|
+
patched: `\t\t\t\t\tfollowUpTurn: true,\n\t\t\t\t\tircWakeTurn: true,\n${PUBLISH_ARG_18113}`,
|
|
15063
|
+
});
|
|
15064
|
+
const MESSAGE_RESULT_18113 = "if (wakeJob && (yielded || messageResult) && result) {";
|
|
15065
|
+
const completion = one(entry => entry.marker === "\t\t\t\t\tconst completionJob = session.asyncJobManager?.getJob(wakeJob.jobId);", "wake completion record");
|
|
15066
|
+
const resultBranch = ["if (wakeJob && yielded && result) {", MESSAGE_RESULT_18113];
|
|
15067
|
+
completion.excludes = MESSAGE_RESULT_18113;
|
|
15068
|
+
(completion.alternates ??= []).push({
|
|
15069
|
+
file: completion.file,
|
|
15070
|
+
requires: MESSAGE_RESULT_18113,
|
|
15071
|
+
marker: completion.marker,
|
|
15072
|
+
anchor: swap(completion.anchor, ...resultBranch),
|
|
15073
|
+
patched: swap(completion.patched, ...resultBranch),
|
|
15074
|
+
});
|
|
15075
|
+
|
|
15076
|
+
// #31 Vercel judge: 18.11.2 부터 #withCandidate 의 셋째 인자가 images 배열이고 첫 줄에서 requiresImages 를 계산하며 후보는
|
|
15077
|
+
// #resolveCandidates(images) 로 고른다. upstream 세 줄은 그대로 두고 명시 Vercel 분기를 requiresImages 계산 바로 뒤에 둔다.
|
|
15078
|
+
// 고정 평가 요청은 이미지를 싣지 않으므로 이미지 요청은 명시적으로 거절하고, 텍스트 요청은 한 번만 보내 usage 를 한 번 남기며
|
|
15079
|
+
// chat/local 후보로 넘어가지 않는다(18.11.1 후보와 같은 본문).
|
|
15080
|
+
const IMAGES_18112 = "const requiresImages = images.length > 0;";
|
|
15081
|
+
const vercel = one(entry => entry.file === "src/judgment/index.ts" && entry.marker.startsWith("VERCEL_JUDGMENT_PROVIDER) {\n\t\t\tconst vercel = new VercelJudge("), "Vercel judge");
|
|
15082
|
+
const vercelPrivate = vercel.alternates.find(candidate => candidate.requires === "\tasync #withCandidate<T>(");
|
|
15083
|
+
const imagesArg = ["\t\trequiresImages: boolean,\n\t): Promise<T> {\n", `\t\timages: readonly ImageContent[],\n\t): Promise<T> {\n\t\t${IMAGES_18112}\n`];
|
|
15084
|
+
vercelPrivate.excludes = IMAGES_18112;
|
|
15085
|
+
vercel.alternates.push({
|
|
15086
|
+
file: vercel.file,
|
|
15087
|
+
requires: IMAGES_18112,
|
|
15088
|
+
marker: vercelPrivate.marker,
|
|
15089
|
+
anchor: swap(vercelPrivate.anchor, ...imagesArg),
|
|
15090
|
+
patched: swap(vercelPrivate.patched, ...imagesArg),
|
|
15091
|
+
});
|
|
15092
|
+
|
|
15093
|
+
// recall 가중: upstream 이 상수표를 env 로 읽는 resolveTrustWeights 로 바꿨다(MNEMOPI_TOOL_WEIGHT, 기본 0.5). learn/retain 의
|
|
15094
|
+
// 의도적 기록 가중 1.0 은 기본값으로만 지키고, 사용자가 env 로 정한 값은 그대로 따른다.
|
|
15095
|
+
const TRUST_18113 = "function resolveTrustWeights(";
|
|
15096
|
+
const recallWeight = one(entry => entry.marker === "// HANSE: deliberate learn/retain weight", "recall tool weight");
|
|
15097
|
+
recallWeight.excludes = TRUST_18113;
|
|
15098
|
+
(recallWeight.alternates ??= []).push({
|
|
15099
|
+
file: recallWeight.file,
|
|
15100
|
+
requires: TRUST_18113,
|
|
15101
|
+
marker: recallWeight.marker,
|
|
15102
|
+
anchor: "\t\t\ttool: Math.max(0, toolWeight()),\n",
|
|
15103
|
+
patched: '\t\t\t// HANSE: deliberate learn/retain weight (default 1.0; MNEMOPI_TOOL_WEIGHT still overrides)\n\t\t\ttool: Math.max(0, toolWeight({ MNEMOPI_TOOL_WEIGHT: "1.0", ...process.env })),\n',
|
|
15104
|
+
});
|
|
15105
|
+
|
|
15106
|
+
// task schema: 동적 schema 가 ...seanceFields 를 펼치고 createTaskSchema 앞에 SeanceTaskSchemaFields 타입이 생겼다. batch 항목의
|
|
15107
|
+
// `model?` 추가와 agent optional 정리는 같은 의미로 새 모양에 둔다. seance 가 노출되면 upstream 의 seance 필드가 뒤에서
|
|
15108
|
+
// `model?` 을 string | string[] 로 덮는다(Maker 는 아래 validator 와 maker-routing 이 문자열 하나만 받는다).
|
|
15109
|
+
const SEANCE_FIELDS_18113 = "const seanceFields = options.seanceFields;";
|
|
15110
|
+
for (const indent of ["\t\t\t\t", "\t\t\t"]) {
|
|
15111
|
+
const entry = one(candidate => candidate.file === "src/task/types.ts" && candidate.marker.startsWith(`"model?": "string",\n${indent}...effortField,\n${indent}"outputSchema?"`), `task schema ${indent.length}`);
|
|
15112
|
+
const structured = entry.alternates.find(candidate => candidate.requires === "const schemaFields = options.structured");
|
|
15113
|
+
const spread = [`${indent}...toolsField,\n`, `${indent}...toolsField,\n${indent}...seanceFields,\n`];
|
|
15114
|
+
structured.excludes = SEANCE_FIELDS_18113;
|
|
15115
|
+
entry.alternates.push({
|
|
15116
|
+
file: entry.file,
|
|
15117
|
+
requires: SEANCE_FIELDS_18113,
|
|
15118
|
+
marker: swap(structured.marker, ...spread),
|
|
15119
|
+
anchor: swap(structured.anchor, ...spread),
|
|
15120
|
+
patched: swap(structured.patched, ...spread),
|
|
15121
|
+
});
|
|
15122
|
+
}
|
|
15123
|
+
const agentOptional = one(candidate => candidate.marker === "// CUELO: task schema agent is optional on the wire.", "agent optional");
|
|
15124
|
+
const agentOptionalStructured = agentOptional.alternates.find(candidate => candidate.requires === "const schemaFields = options.structured");
|
|
15125
|
+
const seanceType = 'type SeanceTaskSchemaFields = {\n\t"sourceSession?"?: FluentType<string>;\n\t"model?"?: FluentType<string | string[]>;\n};\n\n';
|
|
15126
|
+
const seanceOption = ["\tstructured: boolean;\n}): BaseType {", "\tstructured: boolean;\n\tseanceFields: SeanceTaskSchemaFields;\n}): BaseType {"];
|
|
15127
|
+
agentOptionalStructured.excludes = SEANCE_FIELDS_18113;
|
|
15128
|
+
agentOptional.alternates.push({
|
|
15129
|
+
file: agentOptional.file,
|
|
15130
|
+
requires: SEANCE_FIELDS_18113,
|
|
15131
|
+
marker: agentOptional.marker,
|
|
15132
|
+
anchor: swap(swap(agentOptionalStructured.anchor, "}\n\nfunction createTaskSchema(", `}\n\n${seanceType}function createTaskSchema(`), ...seanceOption),
|
|
15133
|
+
patched: swap(swap(agentOptionalStructured.patched, "BaseType>();\n\nfunction createTaskSchema(", `BaseType>();\n\n${seanceType}function createTaskSchema(`), ...seanceOption),
|
|
15134
|
+
});
|
|
15135
|
+
|
|
15136
|
+
// RETIRE: 18.11.3 upstream 의 TaskItem·TaskParams `model?: string | string[]`, 단일 형 item 과 spawn 의 model 복사,
|
|
15137
|
+
// preflight·실행 요청의 `model: params.model` 은 CUELO 가 넣던 줄과 같은 일을 한다. 그 판에서는 upstream 줄을 no-op 후보로 둔다.
|
|
15138
|
+
const SEANCE_TYPES_18113 = "\t/** Historical source session selected by the bundled consult agent. */\n\tsourceSession?: string;\n\t/** Optional model selector override for the historical source session. */\n\tmodel?: string | string[];";
|
|
15139
|
+
for (const [marker, agentLine] of [
|
|
15140
|
+
["Standard model selector for this spawn (item)", "\t/** Agent type to run this item (e.g. \"scout\"). Defaults to the spawn policy's default agent. */\n\tagent?: string;\n"],
|
|
15141
|
+
["Standard model selector for this spawn (flat form)", "\t/** Agent type to spawn (flat form); omitted values resolve from the session spawn policy. */\n\tagent?: string;\n"],
|
|
15142
|
+
]) {
|
|
15143
|
+
const entry = one(candidate => candidate.marker === marker, marker);
|
|
15144
|
+
entry.excludes = SEANCE_TYPES_18113;
|
|
15145
|
+
entry.alternates.push(noop(entry.file, agentLine + SEANCE_TYPES_18113, { requires: SEANCE_TYPES_18113 }));
|
|
15146
|
+
}
|
|
15147
|
+
const itemSource = 'if ("sourceSession" in params) item.sourceSession = params.sourceSession;';
|
|
15148
|
+
const flatItem = one(candidate => candidate.file === "src/task/index.ts" && candidate.marker.startsWith("\tconst item: TaskItem = {"), "flat item model");
|
|
15149
|
+
flatItem.excludes = itemSource;
|
|
15150
|
+
flatItem.alternates.push(noop(flatItem.file, `\t${itemSource}\n\tif ("model" in params) item.model = params.model;`, { requires: itemSource }));
|
|
15151
|
+
const spawnSource = 'if ("sourceSession" in item) spawn.sourceSession = item.sourceSession;';
|
|
15152
|
+
const spawnModel = one(candidate => candidate.file === "src/task/index.ts" && candidate.marker.startsWith("\tconst spawn: TaskParams = {"), "spawn model");
|
|
15153
|
+
spawnModel.excludes = spawnSource;
|
|
15154
|
+
spawnModel.alternates.push(noop(spawnModel.file, `\t${spawnSource}\n\tif ("model" in item) spawn.model = item.model;`, { requires: spawnSource }));
|
|
15155
|
+
for (const [next, indent] of [['...("is', "\t\t\t"], ["...(params.tools", "\t\t\t\t"]]) {
|
|
15156
|
+
const entry = one(candidate => candidate.file === "src/task/index.ts" && candidate.marker.startsWith(`...(params.model !== undefined ? { model: params.model } : {}),\n${indent}${next}`), `request model ${indent.length}`);
|
|
15157
|
+
const forwarded = `\n${indent}sourceSession: params.sourceSession,\n${indent}model: params.model,\n`;
|
|
15158
|
+
entry.excludes = [entry.excludes, forwarded];
|
|
15159
|
+
entry.alternates.push(noop(entry.file, forwarded, { requires: forwarded }));
|
|
15160
|
+
}
|
|
15161
|
+
|
|
15162
|
+
// RETIRE(17번째): 번들의 advisor transcript filter 이름이 b_e → X_e 로 바뀌었다(본문 같음:
|
|
15163
|
+
// e===J_e||e.startsWith(`${Y_e}.`)&&e.endsWith(cue), Y_e="__advisor", cue=".jsonl").
|
|
15164
|
+
const advisorFilter = one(candidate => candidate.file === "dist/cli.js" && candidate.marker.startsWith('if(!o.endsWith(".jsonl")||o.includes(".bak")||o==="__advisor.jsonl"'), "advisor filter");
|
|
15165
|
+
advisorFilter.alternates.push(noop(advisorFilter.file, 'if(!a.endsWith(".jsonl")||a.includes(".bak")||X_e(a))continue;let l=a.slice(0,-6),'));
|
|
15166
|
+
}
|
|
14320
15167
|
// 18.9.1 MCPManager 는 deferred 전역 로더에 `static #deferred` 를 새로 둔다. JS 는 같은 class 안에서 static 과 instance
|
|
14321
15168
|
// private 이름을 겹쳐 둘 수 없어("#deferred" has already been declared) 모듈 적재가 실패한다. 요청별 MCP 선택이 쓰는
|
|
14322
15169
|
// instance 표(나중에 띄울 서버)의 이름만 #deferredServers 로 바꾼 18.9.1 후보를 만든다. 의미·위치는 같다(ADAPT).
|
|
@@ -14380,6 +15227,8 @@ for (const update of EDITS.splice(watchFollowupStart, watchFollowupEnd - watchFo
|
|
|
14380
15227
|
* 새 판의 순정이 국소 문맥까지 바이트가 같을 때만 쓴다(18.5.0 task/index.ts 의 model 전달: upstream 이 우리
|
|
14381
15228
|
* 패치와 같은 줄을 넣어 marker 만으로는 "옛 판 적용본"과 "새 판 순정"을 가를 수 없고, 잘못 가르면 --revert 가
|
|
14382
15229
|
* upstream 줄을 지운다). 조건은 marker·anchor 판별 전에 적용하고, 남은 후보 수 규칙은 그대로다.
|
|
15230
|
+
* `excludes` 는 문자열 하나 또는 목록이며, 목록이면 그중 하나라도 있으면 후보가 아니다(18.11.3: 18.5.0 과 18.11.3 이
|
|
15231
|
+
* 같은 model 전달 줄을 서로 다른 모양으로 upstream 에 넣었다).
|
|
14383
15232
|
*/
|
|
14384
15233
|
function resolveEdit(entry, target) {
|
|
14385
15234
|
const candidates = [entry, ...(entry.alternates ?? [])];
|
|
@@ -14389,7 +15238,7 @@ function resolveEdit(entry, target) {
|
|
|
14389
15238
|
.map(candidate => {
|
|
14390
15239
|
const text = readFileSync(join(target, candidate.file), "utf8");
|
|
14391
15240
|
if (candidate.requires !== undefined && !text.includes(candidate.requires)) return undefined;
|
|
14392
|
-
if (candidate.excludes !== undefined && text.includes(
|
|
15241
|
+
if (candidate.excludes !== undefined && [candidate.excludes].flat().some(excluded => text.includes(excluded))) return undefined;
|
|
14393
15242
|
if (candidate.legacyPatched && text.includes(candidate.legacyPatched)) return { ...candidate, path: join(target, candidate.file), text, status: "legacy" };
|
|
14394
15243
|
if (text.includes(candidate.marker)) return { ...candidate, path: join(target, candidate.file), text, status: "applied" };
|
|
14395
15244
|
if (text.includes(candidate.anchor)) return { ...candidate, path: join(target, candidate.file), text, status: "appliable" };
|