cuelo 0.6.1 → 0.6.3
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 +13 -11
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/required-server-files.js +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +12 -0
- package/.next/server/app/_global-error/page.js +3 -3
- 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 +1 -1
- 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/attachments/route.js +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +1 -1
- package/.next/server/app/api/live/events/route.js +2 -2
- package/.next/server/app/api/live/offer/route.js +1 -1
- package/.next/server/app/api/live/voices/route.js +1 -1
- package/.next/server/app/api/lounge/events/route.js +4 -0
- package/.next/server/app/api/lounge/events/route.js.nft.json +1 -0
- package/.next/server/app/api/lounge/events/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/lounge/route.js +1 -0
- package/.next/server/app/api/lounge/route.js.nft.json +1 -0
- package/.next/server/app/api/lounge/route_client-reference-manifest.js +1 -0
- 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/plugins/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/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 +2 -2
- 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/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 +2 -2
- 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 +6 -6
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +6 -6
- 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 +3 -3
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/page.js +17 -17
- 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 +13 -11
- package/.next/server/chunks/2651.js +2 -2
- package/.next/server/chunks/3379.js +1 -1
- package/.next/server/chunks/3707.js +5 -0
- package/.next/server/chunks/5582.js +1 -1
- package/.next/server/chunks/6429.js +5 -5
- package/.next/server/functions-config-manifest.json +2 -0
- 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/chunks/8579-73b103d96227d1b8.js +7 -0
- package/.next/static/chunks/app/_global-error/page-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/account/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/attachments/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/display-settings/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/document-text/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/experiments/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/gpt6/handles/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/gpt6/reply/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/gpt6/token/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/gpt6/web6-session/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/home/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/live/call/[callId]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/live/events/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/live/offer/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/live/voices/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/lounge/events/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/lounge/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/model-roles/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/models/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/projects/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/runs/[runId]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/runs/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/settings/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/skills/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/theme/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/tool-image/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/update-maintenance/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/updates/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/web-access/recovery/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/web-access/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/web6/consults/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/{layout-43a56002b0fb773b.js → layout-483ef2fa8ff4fe98.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/app/page-bac6b66a3725ef8b.js +22 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d1e510cc33aec52f.js +1 -0
- package/.next/static/chunks/{webpack-4094d78ef0b4c382.js → webpack-13c58b226bf07c7e.js} +1 -1
- package/.next/static/css/{792a9d55c2fd2c0c.css → 078ee14f375802c0.css} +1 -1
- package/.next/static/css/1f257674fb782a57.css +1 -0
- package/.next/static/oBwJ3c4Q_HKgoiBl0dxdQ/_buildManifest.js +1 -0
- package/.next/trace +105 -104
- package/.next/trace-build +1 -1
- package/.next/types/app/api/lounge/events/route.ts +351 -0
- package/.next/types/app/api/lounge/route.ts +351 -0
- package/.next/types/routes.d.ts +3 -1
- package/.next/types/validator.ts +18 -0
- package/README.md +10 -1
- package/Tools/CUELO_Setup/files/native-runtime-patch.js +1 -1
- package/Tools/OMP_Global_Config/agent/RULES.md +1 -1
- package/Tools/OMP_Global_Config/agent/extensions/character-voice.ts +16 -0
- package/Tools/OMP_Global_Config/agent/extensions/lib/mcp-selection.ts +11 -9
- package/Tools/OMP_Global_Config/agent/extensions/steering-reply-gate.ts +12 -3
- package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +2 -2
- package/Tools/OMP_Global_Config/agent/rules/verdict.md +4 -0
- package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +1059 -0
- package/package.json +2 -1
- package/.next/static/9IHPWkHderLIQA0fYAPmt/_buildManifest.js +0 -1
- package/.next/static/chunks/6379-7da431a165ac5d41.js +0 -7
- package/.next/static/chunks/app/_global-error/page-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/account/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/attachments/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/display-settings/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/document-text/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/experiments/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/handles/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/reply/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/token/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/web6-session/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/home/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/live/call/[callId]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/live/events/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/live/offer/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/live/voices/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/mcp/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/model-roles/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/models/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/models-config/catalog/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/project-trust/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/projects/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/runs/[runId]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/runs/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/settings/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/skills/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/theme/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/tool-image/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/update-maintenance/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/updates/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/web-access/recovery/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/web-access/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/web6/consults/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/app/page-952f237df95e7ee4.js +0 -22
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-0c0ee2cbde2b8cfd.js +0 -1
- package/.next/static/css/bbe00993de567f95.css +0 -1
- /package/.next/static/{9IHPWkHderLIQA0fYAPmt → oBwJ3c4Q_HKgoiBl0dxdQ}/_ssgManifest.js +0 -0
|
@@ -5636,6 +5636,988 @@ function matchesFilter(window: DesktopWindow, filter?: WindowFilter): boolean {
|
|
|
5636
5636
|
assertWindowFilter(filter);
|
|
5637
5637
|
const { signal } = getContext();`,
|
|
5638
5638
|
},
|
|
5639
|
+
{
|
|
5640
|
+
// 2026-09-29 computer 행동 결과 표준화와 stale ref 재획득. 행동은 void 대신 표준 결과
|
|
5641
|
+
// {action, status, suggestedNext, escalation?, evidence?, ref, reacquired?}를 돌려준다. 참고 설계:
|
|
5642
|
+
// Cua action-result 계약(https://github.com/trycua/cua/blob/main/libs/cua-driver/docs/action-result-contract.md
|
|
5643
|
+
// — 전달은 효과의 증거가 아니다, readback만 verified 근거)과 browser-use의 단계별 요소 재식별
|
|
5644
|
+
// (https://github.com/browser-use/browser-use/blob/main/browser_use/agent/service.py
|
|
5645
|
+
// — EXACT→STABLE→XPATH→AX_NAME→ATTRIBUTE). 발급한 ref마다 지문(role·nativeRole·title·description·
|
|
5646
|
+
// bounds·AutomationId·RuntimeId·창 안 이름 있는 조상 경로)을 세션 등록부에 두고, stale이면 axQuery
|
|
5647
|
+
// (ref 세대를 올리지 않는다, 실측)로 같은 요소를 찾는다. 후보 0개·경로 불일치·동점·지문 없음이면 다른
|
|
5648
|
+
// 요소를 건드리지 않고 원래 StaleRef로 실패하며 사유를 error.computerAction에 남긴다.
|
|
5649
|
+
// readback은 행동 전·후 1회씩이고, readback 실패는 행동 실패가 아니라 unverified다.
|
|
5650
|
+
file: "src/tools/computer/worker.ts",
|
|
5651
|
+
marker: "class RefBook {",
|
|
5652
|
+
anchor: `class El {
|
|
5653
|
+
readonly ref: string;
|
|
5654
|
+
readonly role: string;
|
|
5655
|
+
readonly nativeRole: string;
|
|
5656
|
+
readonly title?: string;
|
|
5657
|
+
readonly description?: string;
|
|
5658
|
+
readonly enabled: boolean;
|
|
5659
|
+
readonly focused: boolean;
|
|
5660
|
+
readonly childCount: number;
|
|
5661
|
+
readonly #session: NativeDesktopSession;
|
|
5662
|
+
readonly #getContext: RunContextAccessor;
|
|
5663
|
+
|
|
5664
|
+
constructor(session: NativeDesktopSession, getContext: RunContextAccessor, node: AxNode) {
|
|
5665
|
+
this.#session = session;
|
|
5666
|
+
this.#getContext = getContext;
|
|
5667
|
+
this.ref = node.ref;
|
|
5668
|
+
this.role = node.role;
|
|
5669
|
+
this.nativeRole = node.nativeRole;
|
|
5670
|
+
this.title = node.title;
|
|
5671
|
+
this.description = node.description;
|
|
5672
|
+
this.enabled = node.enabled;
|
|
5673
|
+
this.focused = node.focused;
|
|
5674
|
+
this.childCount = node.childCount;
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
async value(): Promise<string | undefined> {
|
|
5678
|
+
const { signal } = this.#getContext();
|
|
5679
|
+
return (await nativeCall(signal, () => this.#session.axNode(this.ref))).value;
|
|
5680
|
+
}
|
|
5681
|
+
|
|
5682
|
+
async setValue(value: string): Promise<void> {
|
|
5683
|
+
const context = this.#getContext();
|
|
5684
|
+
guardRun(context, "setValue");
|
|
5685
|
+
await nativeCall(context.signal, () => this.#session.axSetValue(this.ref, value));
|
|
5686
|
+
}
|
|
5687
|
+
|
|
5688
|
+
async bounds(): Promise<{ x: number; y: number; width: number; height: number } | null> {
|
|
5689
|
+
const { signal } = this.#getContext();
|
|
5690
|
+
const node = await nativeCall(signal, () => this.#session.axNode(this.ref));
|
|
5691
|
+
if (node.x === undefined || node.y === undefined || node.width === undefined || node.height === undefined)
|
|
5692
|
+
return null;
|
|
5693
|
+
return { x: node.x, y: node.y, width: node.width, height: node.height };
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
async attributes(): Promise<Record<string, string>> {
|
|
5697
|
+
const { signal } = this.#getContext();
|
|
5698
|
+
return Object.fromEntries(await nativeCall(signal, () => this.#session.axAttributes(this.ref)));
|
|
5699
|
+
}
|
|
5700
|
+
|
|
5701
|
+
async actions(): Promise<string[]> {
|
|
5702
|
+
const { signal } = this.#getContext();
|
|
5703
|
+
return (await nativeCall(signal, () => this.#session.axNode(this.ref))).actions ?? [];
|
|
5704
|
+
}
|
|
5705
|
+
|
|
5706
|
+
async perform(action: string): Promise<void> {
|
|
5707
|
+
const context = this.#getContext();
|
|
5708
|
+
guardRun(context, "perform");
|
|
5709
|
+
await nativeCall(context.signal, () => this.#session.axPerform(this.ref, action));
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5712
|
+
async press(): Promise<void> {
|
|
5713
|
+
const context = this.#getContext();
|
|
5714
|
+
guardRun(context, "press");
|
|
5715
|
+
await nativeCall(context.signal, () => this.#session.axPerform(this.ref, "press"));
|
|
5716
|
+
}
|
|
5717
|
+
|
|
5718
|
+
async click(options?: InputOptions): Promise<void> {
|
|
5719
|
+
const context = this.#getContext();
|
|
5720
|
+
guardRun(context, "click");
|
|
5721
|
+
await nativeCall(context.signal, () => this.#session.axClick(this.ref, pointerOptions(options)));
|
|
5722
|
+
}
|
|
5723
|
+
|
|
5724
|
+
async focus(): Promise<void> {
|
|
5725
|
+
const context = this.#getContext();
|
|
5726
|
+
guardRun(context, "focus");
|
|
5727
|
+
await nativeCall(context.signal, () => this.#session.axFocus(this.ref));
|
|
5728
|
+
}
|
|
5729
|
+
|
|
5730
|
+
async parent(): Promise<El | null> {
|
|
5731
|
+
const { signal } = this.#getContext();
|
|
5732
|
+
const node = await nativeCall(signal, () => this.#session.axParent(this.ref));
|
|
5733
|
+
return node ? new El(this.#session, this.#getContext, node) : null;
|
|
5734
|
+
}
|
|
5735
|
+
|
|
5736
|
+
async children(): Promise<El[]> {
|
|
5737
|
+
const { signal } = this.#getContext();
|
|
5738
|
+
return (await nativeCall(signal, () => this.#session.axChildren(this.ref))).map(
|
|
5739
|
+
node => new El(this.#session, this.#getContext, node),
|
|
5740
|
+
);
|
|
5741
|
+
}
|
|
5742
|
+
}`,
|
|
5743
|
+
patched: `type AxBounds = { x: number; y: number; width: number; height: number };
|
|
5744
|
+
|
|
5745
|
+
/** Outcome of one computer action. Delivery is not proof of effect: only an AX readback makes it \`verified\`. */
|
|
5746
|
+
interface ComputerActionResult {
|
|
5747
|
+
action: string;
|
|
5748
|
+
/** verified: a readback shows the change. unverified: delivered, effect not observed. suspected_noop: the readback contradicts the intended change. */
|
|
5749
|
+
status: "verified" | "unverified" | "suspected_noop";
|
|
5750
|
+
suggestedNext: "continue" | "reobserve" | "escalate";
|
|
5751
|
+
escalation?: { target: "pixel" | "takeover"; reason: string };
|
|
5752
|
+
evidence?: string;
|
|
5753
|
+
ref?: string;
|
|
5754
|
+
reacquired?: ComputerReacquired;
|
|
5755
|
+
}
|
|
5756
|
+
|
|
5757
|
+
interface ComputerReacquired {
|
|
5758
|
+
from: string;
|
|
5759
|
+
to: string;
|
|
5760
|
+
matchedBy: string;
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
type ReacquireRefusal = "no-fingerprint" | "no-window" | "no-candidate" | "parent-mismatch" | "ambiguous";
|
|
5764
|
+
|
|
5765
|
+
interface ComputerRefCandidate {
|
|
5766
|
+
ref: string;
|
|
5767
|
+
role: string;
|
|
5768
|
+
title?: string;
|
|
5769
|
+
bounds?: AxBounds;
|
|
5770
|
+
}
|
|
5771
|
+
|
|
5772
|
+
/** Facts attached to a failed action as \`error.computerAction\`; the error type and first message line stay unchanged. */
|
|
5773
|
+
interface ComputerActionRefusal {
|
|
5774
|
+
action: string;
|
|
5775
|
+
status: "refused";
|
|
5776
|
+
suggestedNext: "reobserve" | "escalate";
|
|
5777
|
+
escalation?: { target: "takeover"; reason: string };
|
|
5778
|
+
ref?: string;
|
|
5779
|
+
reacquire?: { outcome: ReacquireRefusal; candidates: ComputerRefCandidate[] };
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
/** Identity kept for an issued ref so a stale ref can be found again with a fresh AX query. */
|
|
5783
|
+
interface AxFingerprint {
|
|
5784
|
+
windowId?: string;
|
|
5785
|
+
role: string;
|
|
5786
|
+
/** Undefined when the source (an ax() text line) does not carry the field. */
|
|
5787
|
+
nativeRole?: string;
|
|
5788
|
+
title: string;
|
|
5789
|
+
description?: string;
|
|
5790
|
+
bounds?: AxBounds;
|
|
5791
|
+
automationId?: string;
|
|
5792
|
+
runtimeId?: string;
|
|
5793
|
+
/** Named ancestors inside the window, nearest first, ending with "window" when the window is reached. */
|
|
5794
|
+
parentPath?: string[];
|
|
5795
|
+
}
|
|
5796
|
+
|
|
5797
|
+
type IdentityAttributes = Pick<AxFingerprint, "automationId" | "runtimeId">;
|
|
5798
|
+
type Judged = Pick<ComputerActionResult, "status" | "suggestedNext" | "escalation" | "evidence">;
|
|
5799
|
+
type ReacquireOutcome =
|
|
5800
|
+
| { node: AxNode; matchedBy: string; print: AxFingerprint }
|
|
5801
|
+
| { node?: undefined; outcome: ReacquireRefusal; candidates: ComputerRefCandidate[] };
|
|
5802
|
+
|
|
5803
|
+
const REF_BOOK_LIMIT = 4000;
|
|
5804
|
+
const PARENT_WALK_LIMIT = 12;
|
|
5805
|
+
const PARENT_NAMED_LIMIT = 3;
|
|
5806
|
+
/** Walking parents costs a native call per level, so bulk issues (large find results) skip it. */
|
|
5807
|
+
const PARENT_WALK_BATCH = 8;
|
|
5808
|
+
const REACQUIRE_QUERY_LIMIT = 500;
|
|
5809
|
+
const CANDIDATE_REPORT_LIMIT = 5;
|
|
5810
|
+
/** Element fields whose change after press/click/perform counts as readback evidence; focus alone does not. */
|
|
5811
|
+
const ELEMENT_STATE_KEYS = ["value", "title", "enabled", "childCount", "x", "y", "width", "height"] as const;
|
|
5812
|
+
/** One ax() text line: indentation, role, optional quoted title, ref. */
|
|
5813
|
+
const SNAPSHOT_LINE = /^( *)- ([^ ]+)(?: "(.*?)")? [[]ref=(e[0-9]+)/;
|
|
5814
|
+
|
|
5815
|
+
const refBooks = new WeakMap<NativeDesktopSession, RefBook>();
|
|
5816
|
+
|
|
5817
|
+
function refBook(session: NativeDesktopSession): RefBook {
|
|
5818
|
+
let book = refBooks.get(session);
|
|
5819
|
+
if (!book) {
|
|
5820
|
+
book = new RefBook();
|
|
5821
|
+
refBooks.set(session, book);
|
|
5822
|
+
}
|
|
5823
|
+
return book;
|
|
5824
|
+
}
|
|
5825
|
+
|
|
5826
|
+
function trimOldest(map: Map<string, unknown>): void {
|
|
5827
|
+
if (map.size > REF_BOOK_LIMIT) map.delete(map.keys().next().value as string);
|
|
5828
|
+
}
|
|
5829
|
+
|
|
5830
|
+
function boundsOf(node: AxNode): AxBounds | undefined {
|
|
5831
|
+
if (node.x === undefined || node.y === undefined || node.width === undefined || node.height === undefined)
|
|
5832
|
+
return undefined;
|
|
5833
|
+
return { x: node.x, y: node.y, width: node.width, height: node.height };
|
|
5834
|
+
}
|
|
5835
|
+
|
|
5836
|
+
function nodePrint(node: AxNode): AxFingerprint {
|
|
5837
|
+
return {
|
|
5838
|
+
role: node.role,
|
|
5839
|
+
nativeRole: node.nativeRole,
|
|
5840
|
+
title: node.title ?? "",
|
|
5841
|
+
description: node.description ?? "",
|
|
5842
|
+
bounds: boundsOf(node),
|
|
5843
|
+
};
|
|
5844
|
+
}
|
|
5845
|
+
|
|
5846
|
+
function decodeTitle(raw: string | undefined): string {
|
|
5847
|
+
if (raw === undefined) return "";
|
|
5848
|
+
try {
|
|
5849
|
+
const decoded: unknown = JSON.parse('"' + raw + '"');
|
|
5850
|
+
return typeof decoded === "string" ? decoded : raw;
|
|
5851
|
+
} catch {
|
|
5852
|
+
return raw;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
/** Nearest named ancestors, stopping at the enclosing window; unnamed containers are skipped because ax() text may hide them. */
|
|
5857
|
+
function namedPath(ancestors: Iterable<{ role: string; title?: string }>): string[] {
|
|
5858
|
+
const path: string[] = [];
|
|
5859
|
+
for (const ancestor of ancestors) {
|
|
5860
|
+
if (ancestor.role === "window") {
|
|
5861
|
+
path.push("window");
|
|
5862
|
+
break;
|
|
5863
|
+
}
|
|
5864
|
+
if (ancestor.title) path.push(ancestor.role + ":" + ancestor.title);
|
|
5865
|
+
if (path.length === PARENT_NAMED_LIMIT) break;
|
|
5866
|
+
}
|
|
5867
|
+
return path;
|
|
5868
|
+
}
|
|
5869
|
+
|
|
5870
|
+
function samePath(left: string[], right: string[]): boolean {
|
|
5871
|
+
return left.length === right.length && left.every((entry, index) => entry === right[index]);
|
|
5872
|
+
}
|
|
5873
|
+
|
|
5874
|
+
function attributeText(raw: string | undefined): string | undefined {
|
|
5875
|
+
if (raw === undefined) return undefined;
|
|
5876
|
+
const value = /^[A-Z0-9_]+[(](.*)[)]$/s.exec(raw)?.[1] ?? raw;
|
|
5877
|
+
return value === "" ? undefined : value;
|
|
5878
|
+
}
|
|
5879
|
+
|
|
5880
|
+
function isStaleRef(error: unknown): error is ToolError {
|
|
5881
|
+
return error instanceof ToolError && error.message.startsWith("StaleRef:");
|
|
5882
|
+
}
|
|
5883
|
+
|
|
5884
|
+
function candidateOf(node: AxNode): ComputerRefCandidate {
|
|
5885
|
+
return { ref: node.ref, role: node.role, title: node.title, bounds: boundsOf(node) };
|
|
5886
|
+
}
|
|
5887
|
+
|
|
5888
|
+
function nearBounds(node: AxNode, bounds: AxBounds): boolean {
|
|
5889
|
+
const now = boundsOf(node);
|
|
5890
|
+
if (!now) return false;
|
|
5891
|
+
const dx = Math.abs(now.x + now.width / 2 - (bounds.x + bounds.width / 2));
|
|
5892
|
+
const dy = Math.abs(now.y + now.height / 2 - (bounds.y + bounds.height / 2));
|
|
5893
|
+
return dx <= Math.max(8, bounds.width / 2) && dy <= Math.max(8, bounds.height / 2);
|
|
5894
|
+
}
|
|
5895
|
+
|
|
5896
|
+
/** Session-scoped fingerprints of issued refs and where reacquired stale refs now point. Bounded; oldest entries drop first. */
|
|
5897
|
+
class RefBook {
|
|
5898
|
+
readonly #prints = new Map<string, AxFingerprint>();
|
|
5899
|
+
readonly #moved = new Map<string, ComputerReacquired>();
|
|
5900
|
+
|
|
5901
|
+
get(ref: string): AxFingerprint | undefined {
|
|
5902
|
+
return this.#prints.get(ref);
|
|
5903
|
+
}
|
|
5904
|
+
|
|
5905
|
+
remember(ref: string, print: AxFingerprint): void {
|
|
5906
|
+
const merged: Record<string, unknown> = { ...this.#prints.get(ref) };
|
|
5907
|
+
for (const [key, value] of Object.entries(print)) {
|
|
5908
|
+
if (value !== undefined) merged[key] = value;
|
|
5909
|
+
}
|
|
5910
|
+
this.#prints.delete(ref);
|
|
5911
|
+
this.#prints.set(ref, merged as unknown as AxFingerprint);
|
|
5912
|
+
trimOldest(this.#prints);
|
|
5913
|
+
}
|
|
5914
|
+
|
|
5915
|
+
movedTo(ref: string): ComputerReacquired | undefined {
|
|
5916
|
+
return this.#moved.get(ref);
|
|
5917
|
+
}
|
|
5918
|
+
|
|
5919
|
+
move(reacquired: ComputerReacquired): void {
|
|
5920
|
+
this.#moved.delete(reacquired.from);
|
|
5921
|
+
this.#moved.set(reacquired.from, reacquired);
|
|
5922
|
+
trimOldest(this.#moved);
|
|
5923
|
+
}
|
|
5924
|
+
|
|
5925
|
+
/** Every ax() line carries role, title, and ancestry; refs picked from the text keep a fingerprint without extra native calls. */
|
|
5926
|
+
noteSnapshot(windowId: string, text: string): void {
|
|
5927
|
+
const ancestors: Array<{ depth: number; role: string; title: string }> = [];
|
|
5928
|
+
for (const line of text.split("\\n")) {
|
|
5929
|
+
const match = SNAPSHOT_LINE.exec(line);
|
|
5930
|
+
if (!match) continue;
|
|
5931
|
+
const depth = match[1]!.length;
|
|
5932
|
+
while (ancestors.length > 0 && ancestors[ancestors.length - 1]!.depth >= depth) ancestors.pop();
|
|
5933
|
+
const role = match[2]!;
|
|
5934
|
+
const title = decodeTitle(match[3]);
|
|
5935
|
+
this.remember(match[4]!, { windowId, role, title, parentPath: namedPath([...ancestors].reverse()) });
|
|
5936
|
+
ancestors.push({ depth, role, title });
|
|
5937
|
+
}
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
|
|
5941
|
+
async function identityAttributes(
|
|
5942
|
+
session: NativeDesktopSession,
|
|
5943
|
+
signal: AbortSignal,
|
|
5944
|
+
ref: string,
|
|
5945
|
+
): Promise<IdentityAttributes> {
|
|
5946
|
+
try {
|
|
5947
|
+
const attributes = new Map(await nativeCall(signal, () => session.axAttributes(ref)));
|
|
5948
|
+
return {
|
|
5949
|
+
automationId: attributeText(attributes.get("AutomationId") ?? attributes.get("AXIdentifier")),
|
|
5950
|
+
runtimeId: attributeText(attributes.get("RuntimeId")),
|
|
5951
|
+
};
|
|
5952
|
+
} catch (error) {
|
|
5953
|
+
if (error instanceof ToolAbortError) throw error;
|
|
5954
|
+
return {};
|
|
5955
|
+
}
|
|
5956
|
+
}
|
|
5957
|
+
|
|
5958
|
+
async function walkParentPath(
|
|
5959
|
+
session: NativeDesktopSession,
|
|
5960
|
+
signal: AbortSignal,
|
|
5961
|
+
ref: string,
|
|
5962
|
+
): Promise<string[] | undefined> {
|
|
5963
|
+
const ancestors: AxNode[] = [];
|
|
5964
|
+
let current = ref;
|
|
5965
|
+
for (let level = 0; level < PARENT_WALK_LIMIT; level++) {
|
|
5966
|
+
let parent: AxNode | null | undefined;
|
|
5967
|
+
try {
|
|
5968
|
+
parent = await nativeCall(signal, () => session.axParent(current));
|
|
5969
|
+
} catch (error) {
|
|
5970
|
+
if (error instanceof ToolAbortError) throw error;
|
|
5971
|
+
return undefined;
|
|
5972
|
+
}
|
|
5973
|
+
if (!parent) return namedPath(ancestors);
|
|
5974
|
+
ancestors.push(parent);
|
|
5975
|
+
const path = namedPath(ancestors);
|
|
5976
|
+
if (parent.role === "window" || path.length === PARENT_NAMED_LIMIT) return path;
|
|
5977
|
+
current = parent.ref;
|
|
5978
|
+
}
|
|
5979
|
+
return undefined;
|
|
5980
|
+
}
|
|
5981
|
+
|
|
5982
|
+
/**
|
|
5983
|
+
* Finds the element a stale fingerprint names, narrowing like browser-use's re-identification:
|
|
5984
|
+
* exact instance (RuntimeId), then role+name, AutomationId, named parent path, and position only to
|
|
5985
|
+
* break ties. Anything short of exactly one candidate is refused, never guessed.
|
|
5986
|
+
*/
|
|
5987
|
+
async function reacquire(
|
|
5988
|
+
session: NativeDesktopSession,
|
|
5989
|
+
signal: AbortSignal,
|
|
5990
|
+
print: AxFingerprint | undefined,
|
|
5991
|
+
): Promise<ReacquireOutcome> {
|
|
5992
|
+
if (!print) return { outcome: "no-fingerprint", candidates: [] };
|
|
5993
|
+
const windowId = print.windowId;
|
|
5994
|
+
if (!windowId) return { outcome: "no-window", candidates: [] };
|
|
5995
|
+
const report = (nodes: AxNode[]): ComputerRefCandidate[] => nodes.slice(0, CANDIDATE_REPORT_LIMIT).map(candidateOf);
|
|
5996
|
+
const sameRole = (
|
|
5997
|
+
await nativeCall(signal, () => session.axQuery(windowId, { role: print.role, limit: REACQUIRE_QUERY_LIMIT }))
|
|
5998
|
+
).filter(node => node.role === print.role && (print.nativeRole === undefined || node.nativeRole === print.nativeRole));
|
|
5999
|
+
let pool = sameRole.filter(
|
|
6000
|
+
node =>
|
|
6001
|
+
(node.title ?? "") === print.title &&
|
|
6002
|
+
(print.description === undefined || (node.description ?? "") === print.description),
|
|
6003
|
+
);
|
|
6004
|
+
if (pool.length === 0) return { outcome: "no-candidate", candidates: report(sameRole) };
|
|
6005
|
+
const attributes = new Map<string, IdentityAttributes>();
|
|
6006
|
+
for (const node of pool) attributes.set(node.ref, await identityAttributes(session, signal, node.ref));
|
|
6007
|
+
const matched = (node: AxNode, matchedBy: string[]): ReacquireOutcome => ({
|
|
6008
|
+
node,
|
|
6009
|
+
matchedBy: matchedBy.join("+"),
|
|
6010
|
+
print: { ...print, ...nodePrint(node), ...attributes.get(node.ref), parentPath: print.parentPath },
|
|
6011
|
+
});
|
|
6012
|
+
if (print.runtimeId) {
|
|
6013
|
+
const exact = pool.filter(node => attributes.get(node.ref)?.runtimeId === print.runtimeId);
|
|
6014
|
+
if (exact.length === 1) return matched(exact[0]!, ["runtimeId"]);
|
|
6015
|
+
}
|
|
6016
|
+
const matchedBy = ["role", "name"];
|
|
6017
|
+
if (print.automationId) {
|
|
6018
|
+
const named = pool;
|
|
6019
|
+
pool = pool.filter(node => attributes.get(node.ref)?.automationId === print.automationId);
|
|
6020
|
+
if (pool.length === 0) return { outcome: "no-candidate", candidates: report(named) };
|
|
6021
|
+
matchedBy.push("automationId");
|
|
6022
|
+
}
|
|
6023
|
+
if (print.parentPath && pool.length <= PARENT_WALK_BATCH) {
|
|
6024
|
+
const expected = print.parentPath;
|
|
6025
|
+
const kept: AxNode[] = [];
|
|
6026
|
+
for (const node of pool) {
|
|
6027
|
+
const path = await walkParentPath(session, signal, node.ref);
|
|
6028
|
+
if (path && samePath(path, expected)) kept.push(node);
|
|
6029
|
+
}
|
|
6030
|
+
if (kept.length === 0) return { outcome: "parent-mismatch", candidates: report(pool) };
|
|
6031
|
+
pool = kept;
|
|
6032
|
+
matchedBy.push("parent");
|
|
6033
|
+
}
|
|
6034
|
+
const weak = !print.title && !print.description && !print.automationId && !matchedBy.includes("parent");
|
|
6035
|
+
if (pool.length === 1 && !weak) return matched(pool[0]!, matchedBy);
|
|
6036
|
+
if (print.bounds) {
|
|
6037
|
+
const bounds = print.bounds;
|
|
6038
|
+
const near = pool.filter(node => nearBounds(node, bounds));
|
|
6039
|
+
if (near.length === 1) return matched(near[0]!, [...matchedBy, "position"]);
|
|
6040
|
+
}
|
|
6041
|
+
return { outcome: "ambiguous", candidates: report(pool) };
|
|
6042
|
+
}
|
|
6043
|
+
|
|
6044
|
+
function attachRefusal(error: ToolError, refusal: ComputerActionRefusal): ToolError {
|
|
6045
|
+
(error as ToolError & { computerAction?: ComputerActionRefusal }).computerAction = refusal;
|
|
6046
|
+
return error;
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
function refuseStale(
|
|
6050
|
+
error: ToolError,
|
|
6051
|
+
ref: string,
|
|
6052
|
+
refusal: { outcome: ReacquireRefusal; candidates: ComputerRefCandidate[] },
|
|
6053
|
+
): ToolError {
|
|
6054
|
+
const detail = refusal.candidates
|
|
6055
|
+
.map(candidate => candidate.ref + " " + candidate.role + (candidate.title ? " " + JSON.stringify(candidate.title) : ""))
|
|
6056
|
+
.join(", ");
|
|
6057
|
+
const refused = new ToolError(error.message + "\\nreacquire refused (" + refusal.outcome + ")" + (detail ? ": " + detail : ""));
|
|
6058
|
+
return attachRefusal(refused, {
|
|
6059
|
+
action: "ref",
|
|
6060
|
+
status: "refused",
|
|
6061
|
+
suggestedNext: "reobserve",
|
|
6062
|
+
ref,
|
|
6063
|
+
reacquire: { outcome: refusal.outcome, candidates: refusal.candidates },
|
|
6064
|
+
});
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6067
|
+
/** Keeps the thrown error and adds structured facts; aborts and non-tool errors pass through untouched. */
|
|
6068
|
+
function refuseAction(error: unknown, action: string, ref?: string, takeover?: boolean): unknown {
|
|
6069
|
+
if (!(error instanceof ToolError)) return error;
|
|
6070
|
+
const previous = (error as ToolError & { computerAction?: ComputerActionRefusal }).computerAction;
|
|
6071
|
+
const routeUnavailable = !takeover && error.message.startsWith("BackgroundUnavailable:");
|
|
6072
|
+
return attachRefusal(error, {
|
|
6073
|
+
...previous,
|
|
6074
|
+
action,
|
|
6075
|
+
status: "refused",
|
|
6076
|
+
suggestedNext: routeUnavailable ? "escalate" : "reobserve",
|
|
6077
|
+
...(routeUnavailable ? { escalation: { target: "takeover" as const, reason: "route_unavailable" } } : {}),
|
|
6078
|
+
...(ref ? { ref } : {}),
|
|
6079
|
+
});
|
|
6080
|
+
}
|
|
6081
|
+
|
|
6082
|
+
/** Resolves a ref, following an earlier reacquisition or reacquiring a stale ref from its fingerprint. */
|
|
6083
|
+
async function resolveNode(
|
|
6084
|
+
session: NativeDesktopSession,
|
|
6085
|
+
signal: AbortSignal,
|
|
6086
|
+
ref: string,
|
|
6087
|
+
windowHint?: string,
|
|
6088
|
+
): Promise<{ node: AxNode; reacquired?: ComputerReacquired }> {
|
|
6089
|
+
const book = refBook(session);
|
|
6090
|
+
const moved = book.movedTo(ref);
|
|
6091
|
+
const live = moved?.to ?? ref;
|
|
6092
|
+
try {
|
|
6093
|
+
const node = await nativeCall(signal, () => session.axNode(live));
|
|
6094
|
+
return moved ? { node, reacquired: moved } : { node };
|
|
6095
|
+
} catch (error) {
|
|
6096
|
+
if (!isStaleRef(error)) throw error;
|
|
6097
|
+
const known = book.get(live) ?? book.get(ref);
|
|
6098
|
+
const print = known && !known.windowId && windowHint ? { ...known, windowId: windowHint } : known;
|
|
6099
|
+
const outcome = await reacquire(session, signal, print);
|
|
6100
|
+
if (outcome.node === undefined) throw refuseStale(error, live, outcome);
|
|
6101
|
+
const reacquired = { from: ref, to: outcome.node.ref, matchedBy: outcome.matchedBy };
|
|
6102
|
+
book.move(reacquired);
|
|
6103
|
+
book.remember(outcome.node.ref, outcome.print);
|
|
6104
|
+
return { node: outcome.node, reacquired };
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
|
|
6108
|
+
/** Wraps nodes as elements and records their fingerprints; attributes are cheap, parent walks only for small batches. */
|
|
6109
|
+
async function issueElements(
|
|
6110
|
+
session: NativeDesktopSession,
|
|
6111
|
+
getContext: RunContextAccessor,
|
|
6112
|
+
nodes: AxNode[],
|
|
6113
|
+
windowId?: string,
|
|
6114
|
+
reacquired?: ComputerReacquired,
|
|
6115
|
+
): Promise<El[]> {
|
|
6116
|
+
const { signal } = getContext();
|
|
6117
|
+
const book = refBook(session);
|
|
6118
|
+
for (const node of nodes) {
|
|
6119
|
+
const known = book.get(node.ref);
|
|
6120
|
+
const parentPath =
|
|
6121
|
+
known?.parentPath === undefined && nodes.length <= PARENT_WALK_BATCH
|
|
6122
|
+
? await walkParentPath(session, signal, node.ref)
|
|
6123
|
+
: undefined;
|
|
6124
|
+
book.remember(node.ref, {
|
|
6125
|
+
...nodePrint(node),
|
|
6126
|
+
windowId: known?.windowId ?? windowId,
|
|
6127
|
+
...(await identityAttributes(session, signal, node.ref)),
|
|
6128
|
+
parentPath,
|
|
6129
|
+
});
|
|
6130
|
+
}
|
|
6131
|
+
return nodes.map(node => new El(session, getContext, node, reacquired));
|
|
6132
|
+
}
|
|
6133
|
+
|
|
6134
|
+
async function resolveElement(
|
|
6135
|
+
session: NativeDesktopSession,
|
|
6136
|
+
getContext: RunContextAccessor,
|
|
6137
|
+
ref: string,
|
|
6138
|
+
windowHint?: string,
|
|
6139
|
+
): Promise<El> {
|
|
6140
|
+
const { signal } = getContext();
|
|
6141
|
+
const { node, reacquired } = await resolveNode(session, signal, ref, windowHint);
|
|
6142
|
+
const [element] = await issueElements(session, getContext, [node], windowHint, reacquired);
|
|
6143
|
+
return element!;
|
|
6144
|
+
}
|
|
6145
|
+
|
|
6146
|
+
function verified(evidence: string): Judged {
|
|
6147
|
+
return { status: "verified", suggestedNext: "continue", evidence };
|
|
6148
|
+
}
|
|
6149
|
+
|
|
6150
|
+
function unverified(evidence: string): Judged {
|
|
6151
|
+
return { status: "unverified", suggestedNext: "reobserve", evidence };
|
|
6152
|
+
}
|
|
6153
|
+
|
|
6154
|
+
function suspectedNoop(evidence: string, reason: string): Judged {
|
|
6155
|
+
return { status: "suspected_noop", suggestedNext: "escalate", escalation: { target: "pixel", reason }, evidence };
|
|
6156
|
+
}
|
|
6157
|
+
|
|
6158
|
+
function judgeStateChange(before: AxNode | undefined, after: AxNode | undefined): Judged {
|
|
6159
|
+
if (!before || !after) return unverified("element could not be read back around the action");
|
|
6160
|
+
const changed = ELEMENT_STATE_KEYS.filter(key => before[key] !== after[key]);
|
|
6161
|
+
return changed.length > 0
|
|
6162
|
+
? verified("element " + changed.join(", ") + " changed")
|
|
6163
|
+
: unverified("element state unchanged; the effect may be elsewhere");
|
|
6164
|
+
}
|
|
6165
|
+
|
|
6166
|
+
function windowInputResult(action: string, target: string, options?: InputOptions): ComputerActionResult {
|
|
6167
|
+
const route = target === "desktop" ? "desktop" : options?.takeover ? "takeover" : "background";
|
|
6168
|
+
return { action, ...unverified("input accepted via the " + route + " route; application effect not observed") };
|
|
6169
|
+
}
|
|
6170
|
+
|
|
6171
|
+
class El {
|
|
6172
|
+
/** Updated when an action reacquires this element after its ref went stale. */
|
|
6173
|
+
ref: string;
|
|
6174
|
+
readonly role: string;
|
|
6175
|
+
readonly nativeRole: string;
|
|
6176
|
+
readonly title?: string;
|
|
6177
|
+
readonly description?: string;
|
|
6178
|
+
readonly enabled: boolean;
|
|
6179
|
+
readonly focused: boolean;
|
|
6180
|
+
readonly childCount: number;
|
|
6181
|
+
/** Stale ref this element was reacquired from; present only after a reacquisition. */
|
|
6182
|
+
declare reacquiredFrom?: string;
|
|
6183
|
+
readonly #session: NativeDesktopSession;
|
|
6184
|
+
readonly #getContext: RunContextAccessor;
|
|
6185
|
+
#reacquired?: ComputerReacquired;
|
|
6186
|
+
|
|
6187
|
+
constructor(session: NativeDesktopSession, getContext: RunContextAccessor, node: AxNode, reacquired?: ComputerReacquired) {
|
|
6188
|
+
this.#session = session;
|
|
6189
|
+
this.#getContext = getContext;
|
|
6190
|
+
this.ref = node.ref;
|
|
6191
|
+
this.role = node.role;
|
|
6192
|
+
this.nativeRole = node.nativeRole;
|
|
6193
|
+
this.title = node.title;
|
|
6194
|
+
this.description = node.description;
|
|
6195
|
+
this.enabled = node.enabled;
|
|
6196
|
+
this.focused = node.focused;
|
|
6197
|
+
this.childCount = node.childCount;
|
|
6198
|
+
if (reacquired) this.#noteReacquired(reacquired);
|
|
6199
|
+
}
|
|
6200
|
+
|
|
6201
|
+
#noteReacquired(reacquired: ComputerReacquired): void {
|
|
6202
|
+
this.#reacquired = this.#reacquired ? { ...reacquired, from: this.#reacquired.from } : reacquired;
|
|
6203
|
+
this.reacquiredFrom = this.#reacquired.from;
|
|
6204
|
+
}
|
|
6205
|
+
|
|
6206
|
+
/**
|
|
6207
|
+
* One readback before and one after the delivery, no polling. The before read doubles as the stale
|
|
6208
|
+
* check that may reacquire; once input reaches the native layer nothing is retried. A failed
|
|
6209
|
+
* readback never turns a delivered action into an error; it leaves the result unverified.
|
|
6210
|
+
*/
|
|
6211
|
+
async #act(
|
|
6212
|
+
action: string,
|
|
6213
|
+
deliver: (ref: string) => Promise<void>,
|
|
6214
|
+
judge: (before: AxNode | undefined, after: AxNode | undefined) => Judged,
|
|
6215
|
+
takeover?: boolean,
|
|
6216
|
+
): Promise<ComputerActionResult> {
|
|
6217
|
+
const context = this.#getContext();
|
|
6218
|
+
guardRun(context, action);
|
|
6219
|
+
let before: AxNode | undefined;
|
|
6220
|
+
try {
|
|
6221
|
+
const live = await resolveNode(this.#session, context.signal, this.ref);
|
|
6222
|
+
before = live.node;
|
|
6223
|
+
if (live.reacquired) {
|
|
6224
|
+
this.ref = live.node.ref;
|
|
6225
|
+
this.#noteReacquired(live.reacquired);
|
|
6226
|
+
}
|
|
6227
|
+
} catch (error) {
|
|
6228
|
+
if (isStaleRef(error)) throw refuseAction(error, action, this.ref, takeover);
|
|
6229
|
+
if (error instanceof ToolAbortError) throw error;
|
|
6230
|
+
}
|
|
6231
|
+
try {
|
|
6232
|
+
await nativeCall(context.signal, () => deliver(this.ref));
|
|
6233
|
+
} catch (error) {
|
|
6234
|
+
throw refuseAction(error, action, this.ref, takeover);
|
|
6235
|
+
}
|
|
6236
|
+
let after: AxNode | undefined;
|
|
6237
|
+
try {
|
|
6238
|
+
after = await nativeCall(context.signal, () => this.#session.axNode(this.ref));
|
|
6239
|
+
} catch (error) {
|
|
6240
|
+
if (error instanceof ToolAbortError) throw error;
|
|
6241
|
+
}
|
|
6242
|
+
return {
|
|
6243
|
+
action,
|
|
6244
|
+
...judge(before, after),
|
|
6245
|
+
ref: this.ref,
|
|
6246
|
+
...(this.#reacquired ? { reacquired: this.#reacquired } : {}),
|
|
6247
|
+
};
|
|
6248
|
+
}
|
|
6249
|
+
|
|
6250
|
+
async value(): Promise<string | undefined> {
|
|
6251
|
+
const { signal } = this.#getContext();
|
|
6252
|
+
return (await nativeCall(signal, () => this.#session.axNode(this.ref))).value;
|
|
6253
|
+
}
|
|
6254
|
+
|
|
6255
|
+
setValue(value: string): Promise<ComputerActionResult> {
|
|
6256
|
+
return this.#act(
|
|
6257
|
+
"setValue",
|
|
6258
|
+
ref => this.#session.axSetValue(ref, value),
|
|
6259
|
+
(_before, after) => {
|
|
6260
|
+
if (after?.value === undefined) return unverified("value could not be read back");
|
|
6261
|
+
return after.value === value
|
|
6262
|
+
? verified("value read back as requested")
|
|
6263
|
+
: suspectedNoop("value read back as " + JSON.stringify(after.value), "value_mismatch");
|
|
6264
|
+
},
|
|
6265
|
+
);
|
|
6266
|
+
}
|
|
6267
|
+
|
|
6268
|
+
async bounds(): Promise<{ x: number; y: number; width: number; height: number } | null> {
|
|
6269
|
+
const { signal } = this.#getContext();
|
|
6270
|
+
const node = await nativeCall(signal, () => this.#session.axNode(this.ref));
|
|
6271
|
+
if (node.x === undefined || node.y === undefined || node.width === undefined || node.height === undefined)
|
|
6272
|
+
return null;
|
|
6273
|
+
return { x: node.x, y: node.y, width: node.width, height: node.height };
|
|
6274
|
+
}
|
|
6275
|
+
|
|
6276
|
+
async attributes(): Promise<Record<string, string>> {
|
|
6277
|
+
const { signal } = this.#getContext();
|
|
6278
|
+
return Object.fromEntries(await nativeCall(signal, () => this.#session.axAttributes(this.ref)));
|
|
6279
|
+
}
|
|
6280
|
+
|
|
6281
|
+
async actions(): Promise<string[]> {
|
|
6282
|
+
const { signal } = this.#getContext();
|
|
6283
|
+
return (await nativeCall(signal, () => this.#session.axNode(this.ref))).actions ?? [];
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6286
|
+
perform(action: string): Promise<ComputerActionResult> {
|
|
6287
|
+
return this.#act("perform", ref => this.#session.axPerform(ref, action), judgeStateChange);
|
|
6288
|
+
}
|
|
6289
|
+
|
|
6290
|
+
press(): Promise<ComputerActionResult> {
|
|
6291
|
+
return this.#act("press", ref => this.#session.axPerform(ref, "press"), judgeStateChange);
|
|
6292
|
+
}
|
|
6293
|
+
|
|
6294
|
+
click(options?: InputOptions): Promise<ComputerActionResult> {
|
|
6295
|
+
return this.#act(
|
|
6296
|
+
"click",
|
|
6297
|
+
ref => this.#session.axClick(ref, pointerOptions(options)),
|
|
6298
|
+
judgeStateChange,
|
|
6299
|
+
options?.takeover,
|
|
6300
|
+
);
|
|
6301
|
+
}
|
|
6302
|
+
|
|
6303
|
+
focus(): Promise<ComputerActionResult> {
|
|
6304
|
+
return this.#act(
|
|
6305
|
+
"focus",
|
|
6306
|
+
ref => this.#session.axFocus(ref),
|
|
6307
|
+
(_before, after) => {
|
|
6308
|
+
if (!after) return unverified("focus could not be read back");
|
|
6309
|
+
return after.focused
|
|
6310
|
+
? verified("element reports focus")
|
|
6311
|
+
: suspectedNoop("element does not report focus", "focus_not_observed");
|
|
6312
|
+
},
|
|
6313
|
+
);
|
|
6314
|
+
}
|
|
6315
|
+
|
|
6316
|
+
async parent(): Promise<El | null> {
|
|
6317
|
+
const { signal } = this.#getContext();
|
|
6318
|
+
const node = await nativeCall(signal, () => this.#session.axParent(this.ref));
|
|
6319
|
+
if (!node) return null;
|
|
6320
|
+
const [parent] = await issueElements(this.#session, this.#getContext, [node], refBook(this.#session).get(this.ref)?.windowId);
|
|
6321
|
+
return parent!;
|
|
6322
|
+
}
|
|
6323
|
+
|
|
6324
|
+
async children(): Promise<El[]> {
|
|
6325
|
+
const { signal } = this.#getContext();
|
|
6326
|
+
const nodes = await nativeCall(signal, () => this.#session.axChildren(this.ref));
|
|
6327
|
+
return issueElements(this.#session, this.#getContext, nodes, refBook(this.#session).get(this.ref)?.windowId);
|
|
6328
|
+
}
|
|
6329
|
+
}`,
|
|
6330
|
+
},
|
|
6331
|
+
{
|
|
6332
|
+
// 창 좌표·키 입력은 앱 쪽 효과를 읽어 올 방법이 없다: 결과는 늘 unverified/reobserve다. ax()는 텍스트의
|
|
6333
|
+
// 모든 ref에 지문을 남기고, find()/ref()는 발급하는 요소에 지문을 남긴다.
|
|
6334
|
+
file: "src/tools/computer/worker.ts",
|
|
6335
|
+
marker: "return windowInputResult(action, this.id, options);",
|
|
6336
|
+
anchor: ` async click(x: number, y: number, options?: ClickOptions): Promise<void> {
|
|
6337
|
+
const context = this.#getContext();
|
|
6338
|
+
guardRun(context, "click");
|
|
6339
|
+
await nativeCall(context.signal, () => this.#session.click(this.id, x, y, pointerOptions(options)));
|
|
6340
|
+
}
|
|
6341
|
+
|
|
6342
|
+
async doubleClick(x: number, y: number, options?: Omit<ClickOptions, "count">): Promise<void> {
|
|
6343
|
+
const context = this.#getContext();
|
|
6344
|
+
guardRun(context, "doubleClick");
|
|
6345
|
+
await nativeCall(context.signal, () =>
|
|
6346
|
+
this.#session.click(this.id, x, y, pointerOptions({ ...options, count: 2 })),
|
|
6347
|
+
);
|
|
6348
|
+
}
|
|
6349
|
+
|
|
6350
|
+
async move(x: number, y: number): Promise<void> {
|
|
6351
|
+
const context = this.#getContext();
|
|
6352
|
+
guardRun(context, "move");
|
|
6353
|
+
await nativeCall(context.signal, () => this.#session.moveMouse(this.id, x, y));
|
|
6354
|
+
}
|
|
6355
|
+
|
|
6356
|
+
async drag(points: Array<[number, number]>, options?: DragOptions): Promise<void> {
|
|
6357
|
+
const context = this.#getContext();
|
|
6358
|
+
guardRun(context, "drag");
|
|
6359
|
+
await nativeCall(context.signal, () =>
|
|
6360
|
+
this.#session.drag(
|
|
6361
|
+
this.id,
|
|
6362
|
+
points.map(([x, y]) => ({ x, y })),
|
|
6363
|
+
pointerOptions(options),
|
|
6364
|
+
),
|
|
6365
|
+
);
|
|
6366
|
+
}
|
|
6367
|
+
|
|
6368
|
+
async scroll(x: number, y: number, options: ScrollOptions = {}): Promise<void> {
|
|
6369
|
+
const context = this.#getContext();
|
|
6370
|
+
guardRun(context, "scroll");
|
|
6371
|
+
await nativeCall(context.signal, () =>
|
|
6372
|
+
this.#session.scroll(this.id, x, y, options.dx ?? 0, options.dy ?? 0, pointerOptions(options)),
|
|
6373
|
+
);
|
|
6374
|
+
}
|
|
6375
|
+
|
|
6376
|
+
async type(text: string, options?: InputOptions): Promise<void> {
|
|
6377
|
+
const context = this.#getContext();
|
|
6378
|
+
guardRun(context, "type");
|
|
6379
|
+
await nativeCall(context.signal, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6382
|
+
async press(chord: string | string[], options?: InputOptions): Promise<void> {
|
|
6383
|
+
const context = this.#getContext();
|
|
6384
|
+
guardRun(context, "press");
|
|
6385
|
+
await nativeCall(context.signal, () =>
|
|
6386
|
+
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
6387
|
+
);
|
|
6388
|
+
}
|
|
6389
|
+
|
|
6390
|
+
async raise(): Promise<void> {
|
|
6391
|
+
const context = this.#getContext();
|
|
6392
|
+
guardRun(context, "raise");
|
|
6393
|
+
await nativeCall(context.signal, () => this.#session.raiseWindow(this.id));
|
|
6394
|
+
}
|
|
6395
|
+
|
|
6396
|
+
async ax(options?: AxOptions): Promise<string> {
|
|
6397
|
+
const { signal } = this.#getContext();
|
|
6398
|
+
return (await nativeCall(signal, () => this.#session.axSnapshot(this.id, options))).text;
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6401
|
+
async find(query: AxQuery): Promise<El[]> {
|
|
6402
|
+
const { signal } = this.#getContext();
|
|
6403
|
+
return (await nativeCall(signal, () => this.#session.axQuery(this.id, query))).map(
|
|
6404
|
+
node => new El(this.#session, this.#getContext, node),
|
|
6405
|
+
);
|
|
6406
|
+
}
|
|
6407
|
+
|
|
6408
|
+
async ref(ref: string): Promise<El> {
|
|
6409
|
+
const { signal } = this.#getContext();
|
|
6410
|
+
return new El(this.#session, this.#getContext, await nativeCall(signal, () => this.#session.axNode(ref)));
|
|
6411
|
+
}`,
|
|
6412
|
+
patched: ` async #input(action: string, options: InputOptions | undefined, call: () => Promise<void>): Promise<ComputerActionResult> {
|
|
6413
|
+
const context = this.#getContext();
|
|
6414
|
+
guardRun(context, action);
|
|
6415
|
+
try {
|
|
6416
|
+
await nativeCall(context.signal, call);
|
|
6417
|
+
} catch (error) {
|
|
6418
|
+
throw refuseAction(error, action, undefined, options?.takeover);
|
|
6419
|
+
}
|
|
6420
|
+
return windowInputResult(action, this.id, options);
|
|
6421
|
+
}
|
|
6422
|
+
|
|
6423
|
+
click(x: number, y: number, options?: ClickOptions): Promise<ComputerActionResult> {
|
|
6424
|
+
return this.#input("click", options, () => this.#session.click(this.id, x, y, pointerOptions(options)));
|
|
6425
|
+
}
|
|
6426
|
+
|
|
6427
|
+
doubleClick(x: number, y: number, options?: Omit<ClickOptions, "count">): Promise<ComputerActionResult> {
|
|
6428
|
+
return this.#input("doubleClick", options, () =>
|
|
6429
|
+
this.#session.click(this.id, x, y, pointerOptions({ ...options, count: 2 })),
|
|
6430
|
+
);
|
|
6431
|
+
}
|
|
6432
|
+
|
|
6433
|
+
move(x: number, y: number): Promise<ComputerActionResult> {
|
|
6434
|
+
return this.#input("move", undefined, () => this.#session.moveMouse(this.id, x, y));
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6437
|
+
drag(points: Array<[number, number]>, options?: DragOptions): Promise<ComputerActionResult> {
|
|
6438
|
+
return this.#input("drag", options, () =>
|
|
6439
|
+
this.#session.drag(
|
|
6440
|
+
this.id,
|
|
6441
|
+
points.map(([x, y]) => ({ x, y })),
|
|
6442
|
+
pointerOptions(options),
|
|
6443
|
+
),
|
|
6444
|
+
);
|
|
6445
|
+
}
|
|
6446
|
+
|
|
6447
|
+
scroll(x: number, y: number, options: ScrollOptions = {}): Promise<ComputerActionResult> {
|
|
6448
|
+
return this.#input("scroll", options, () =>
|
|
6449
|
+
this.#session.scroll(this.id, x, y, options.dx ?? 0, options.dy ?? 0, pointerOptions(options)),
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
|
|
6453
|
+
type(text: string, options?: InputOptions): Promise<ComputerActionResult> {
|
|
6454
|
+
return this.#input("type", options, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
6455
|
+
}
|
|
6456
|
+
|
|
6457
|
+
press(chord: string | string[], options?: InputOptions): Promise<ComputerActionResult> {
|
|
6458
|
+
return this.#input("press", options, () =>
|
|
6459
|
+
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
6460
|
+
);
|
|
6461
|
+
}
|
|
6462
|
+
|
|
6463
|
+
raise(): Promise<ComputerActionResult> {
|
|
6464
|
+
return this.#input("raise", undefined, () => this.#session.raiseWindow(this.id));
|
|
6465
|
+
}
|
|
6466
|
+
|
|
6467
|
+
async ax(options?: AxOptions): Promise<string> {
|
|
6468
|
+
const { signal } = this.#getContext();
|
|
6469
|
+
const text = (await nativeCall(signal, () => this.#session.axSnapshot(this.id, options))).text;
|
|
6470
|
+
refBook(this.#session).noteSnapshot(this.id, text);
|
|
6471
|
+
return text;
|
|
6472
|
+
}
|
|
6473
|
+
|
|
6474
|
+
async find(query: AxQuery): Promise<El[]> {
|
|
6475
|
+
const { signal } = this.#getContext();
|
|
6476
|
+
const nodes = await nativeCall(signal, () => this.#session.axQuery(this.id, query));
|
|
6477
|
+
return issueElements(this.#session, this.#getContext, nodes, this.id);
|
|
6478
|
+
}
|
|
6479
|
+
|
|
6480
|
+
ref(ref: string): Promise<El> {
|
|
6481
|
+
return resolveElement(this.#session, this.#getContext, ref, this.id);
|
|
6482
|
+
}`,
|
|
6483
|
+
},
|
|
6484
|
+
{
|
|
6485
|
+
// Python·JS 직접 헬퍼는 win.ref(ref)도 매번 desktop.ref(ref)로 푼다. 재획득은 이 경로에서도 일어나야 한다.
|
|
6486
|
+
file: "src/tools/computer/worker.ts",
|
|
6487
|
+
marker: "ref: (ref: string): Promise<El> => resolveElement(session, getContext, ref),",
|
|
6488
|
+
anchor: ` ref: async (ref: string): Promise<El> => {
|
|
6489
|
+
const { signal } = getContext();
|
|
6490
|
+
return el(await nativeCall(signal, () => session.axNode(ref)));
|
|
6491
|
+
},`,
|
|
6492
|
+
patched: ` ref: (ref: string): Promise<El> => resolveElement(session, getContext, ref),`,
|
|
6493
|
+
},
|
|
6494
|
+
{
|
|
6495
|
+
// 모델이 보는 타입: 행동 결과·거절 필드와 재획득 규칙을 적는다.
|
|
6496
|
+
file: "src/tools/computer/declarations.d.ts",
|
|
6497
|
+
marker: "interface ComputerActionResult {",
|
|
6498
|
+
anchor: `/** Live accessibility element resolved from a snapshot ref; expired refs throw \`StaleRef\`. */
|
|
6499
|
+
interface ComputerElement {
|
|
6500
|
+
/** Snapshot ref tag, e.g. \`e5\`. */
|
|
6501
|
+
readonly ref: string;
|
|
6502
|
+
readonly role: string;
|
|
6503
|
+
readonly nativeRole: string;
|
|
6504
|
+
readonly title?: string;
|
|
6505
|
+
readonly description?: string;
|
|
6506
|
+
readonly enabled: boolean;
|
|
6507
|
+
readonly focused: boolean;
|
|
6508
|
+
readonly childCount: number;
|
|
6509
|
+
value(): Promise<string | undefined>;
|
|
6510
|
+
setValue(value: string): Promise<void>;
|
|
6511
|
+
/** Bounds in global desktop coordinates, or null when the element has none. */
|
|
6512
|
+
bounds(): Promise<ComputerBounds | null>;
|
|
6513
|
+
attributes(): Promise<Record<string, string>>;
|
|
6514
|
+
actions(): Promise<string[]>;
|
|
6515
|
+
perform(action: string): Promise<void>;
|
|
6516
|
+
/** Perform the element's native press action; needs no screenshot. */
|
|
6517
|
+
press(): Promise<void>;
|
|
6518
|
+
/** Click the element's center with native input. */
|
|
6519
|
+
click(options?: ComputerInputOptions): Promise<void>;
|
|
6520
|
+
focus(): Promise<void>;
|
|
6521
|
+
parent(): Promise<ComputerElement | null>;
|
|
6522
|
+
children(): Promise<ComputerElement[]>;
|
|
6523
|
+
}
|
|
6524
|
+
|
|
6525
|
+
/** Native input helpers shared by the desktop root and window handles; \`x\`/\`y\` are pixels in the most recent screenshot of the same target. */
|
|
6526
|
+
interface ComputerInputTarget {
|
|
6527
|
+
screenshot(options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
6528
|
+
click(x: number, y: number, options?: ComputerClickOptions): Promise<void>;
|
|
6529
|
+
doubleClick(x: number, y: number, options?: Omit<ComputerClickOptions, "count">): Promise<void>;
|
|
6530
|
+
move(x: number, y: number): Promise<void>;
|
|
6531
|
+
drag(points: Array<[number, number]>, options?: ComputerDragOptions): Promise<void>;
|
|
6532
|
+
scroll(x: number, y: number, options?: ComputerScrollOptions): Promise<void>;
|
|
6533
|
+
type(text: string, options?: ComputerInputOptions): Promise<void>;
|
|
6534
|
+
/** Key chord such as \`"cmd+shift+p"\` or \`["cmd", "shift", "p"]\`. */
|
|
6535
|
+
press(chord: string | string[], options?: ComputerInputOptions): Promise<void>;
|
|
6536
|
+
}`,
|
|
6537
|
+
patched: `/** Result of an input or element action. Delivery is not proof of effect. */
|
|
6538
|
+
interface ComputerActionResult {
|
|
6539
|
+
action: string;
|
|
6540
|
+
/** \`verified\`: an AX readback shows the change. \`unverified\`: delivered, effect not observed. \`suspected_noop\`: the readback contradicts the intended change. */
|
|
6541
|
+
status: "verified" | "unverified" | "suspected_noop";
|
|
6542
|
+
/** \`reobserve\`: screenshot or AX before relying on the effect. \`escalate\`: follow \`escalation\`. */
|
|
6543
|
+
suggestedNext: "continue" | "reobserve" | "escalate";
|
|
6544
|
+
escalation?: { target: "pixel" | "takeover"; reason: string };
|
|
6545
|
+
/** Readback the status rests on. */
|
|
6546
|
+
evidence?: string;
|
|
6547
|
+
ref?: string;
|
|
6548
|
+
/** Present when the action ran on an element reacquired from a stale ref. */
|
|
6549
|
+
reacquired?: { from: string; to: string; matchedBy: string };
|
|
6550
|
+
}
|
|
6551
|
+
|
|
6552
|
+
/** Attached to a failed action's error as \`error.computerAction\` (visible inside \`computer.run\`); the error message is unchanged. */
|
|
6553
|
+
interface ComputerActionRefusal {
|
|
6554
|
+
action: string;
|
|
6555
|
+
status: "refused";
|
|
6556
|
+
suggestedNext: "reobserve" | "escalate";
|
|
6557
|
+
escalation?: { target: "takeover"; reason: string };
|
|
6558
|
+
ref?: string;
|
|
6559
|
+
/** Why a stale ref was not reacquired, with up to five candidates. */
|
|
6560
|
+
reacquire?: {
|
|
6561
|
+
outcome: "no-fingerprint" | "no-window" | "no-candidate" | "parent-mismatch" | "ambiguous";
|
|
6562
|
+
candidates: Array<{ ref: string; role: string; title?: string; bounds?: ComputerBounds }>;
|
|
6563
|
+
};
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6566
|
+
/** Live accessibility element resolved from a snapshot ref. A stale ref is reacquired only when its fingerprint matches exactly one element; otherwise it throws \`StaleRef\`. */
|
|
6567
|
+
interface ComputerElement {
|
|
6568
|
+
/** Snapshot ref tag, e.g. \`e5\`; the new ref after a reacquisition. */
|
|
6569
|
+
readonly ref: string;
|
|
6570
|
+
/** Stale ref this element was reacquired from. */
|
|
6571
|
+
readonly reacquiredFrom?: string;
|
|
6572
|
+
readonly role: string;
|
|
6573
|
+
readonly nativeRole: string;
|
|
6574
|
+
readonly title?: string;
|
|
6575
|
+
readonly description?: string;
|
|
6576
|
+
readonly enabled: boolean;
|
|
6577
|
+
readonly focused: boolean;
|
|
6578
|
+
readonly childCount: number;
|
|
6579
|
+
value(): Promise<string | undefined>;
|
|
6580
|
+
setValue(value: string): Promise<ComputerActionResult>;
|
|
6581
|
+
/** Bounds in global desktop coordinates, or null when the element has none. */
|
|
6582
|
+
bounds(): Promise<ComputerBounds | null>;
|
|
6583
|
+
attributes(): Promise<Record<string, string>>;
|
|
6584
|
+
actions(): Promise<string[]>;
|
|
6585
|
+
perform(action: string): Promise<ComputerActionResult>;
|
|
6586
|
+
/** Perform the element's native press action; needs no screenshot. */
|
|
6587
|
+
press(): Promise<ComputerActionResult>;
|
|
6588
|
+
/** Click the element's center with native input. */
|
|
6589
|
+
click(options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
6590
|
+
focus(): Promise<ComputerActionResult>;
|
|
6591
|
+
parent(): Promise<ComputerElement | null>;
|
|
6592
|
+
children(): Promise<ComputerElement[]>;
|
|
6593
|
+
}
|
|
6594
|
+
|
|
6595
|
+
/** Native input helpers shared by the desktop root and window handles; \`x\`/\`y\` are pixels in the most recent screenshot of the same target. Input results are always \`unverified\`. */
|
|
6596
|
+
interface ComputerInputTarget {
|
|
6597
|
+
screenshot(options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
6598
|
+
click(x: number, y: number, options?: ComputerClickOptions): Promise<ComputerActionResult>;
|
|
6599
|
+
doubleClick(x: number, y: number, options?: Omit<ComputerClickOptions, "count">): Promise<ComputerActionResult>;
|
|
6600
|
+
move(x: number, y: number): Promise<ComputerActionResult>;
|
|
6601
|
+
drag(points: Array<[number, number]>, options?: ComputerDragOptions): Promise<ComputerActionResult>;
|
|
6602
|
+
scroll(x: number, y: number, options?: ComputerScrollOptions): Promise<ComputerActionResult>;
|
|
6603
|
+
type(text: string, options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
6604
|
+
/** Key chord such as \`"cmd+shift+p"\` or \`["cmd", "shift", "p"]\`. */
|
|
6605
|
+
press(chord: string | string[], options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
6606
|
+
}`,
|
|
6607
|
+
},
|
|
6608
|
+
{
|
|
6609
|
+
file: "src/tools/computer/declarations.d.ts",
|
|
6610
|
+
marker: "\traise(): Promise<ComputerActionResult>;",
|
|
6611
|
+
anchor: "\traise(): Promise<void>;",
|
|
6612
|
+
patched: "\traise(): Promise<ComputerActionResult>;",
|
|
6613
|
+
},
|
|
6614
|
+
{
|
|
6615
|
+
// 모델 지시: 재획득 규칙과 결과 필드를 읽는 법. 기존 takeover 규칙(다음 줄)은 그대로 둔다.
|
|
6616
|
+
file: "src/prompts/tools/computer.md",
|
|
6617
|
+
marker: "Actions return `{ action, status, suggestedNext",
|
|
6618
|
+
anchor: "- Each window `.ax()` starts a ref generation. Current/previous snapshot refs remain valid; older refs throw `StaleRef`. Re-snapshot; NEVER guess.\n",
|
|
6619
|
+
patched: "- Each window `.ax()` starts a ref generation. Current/previous snapshot refs remain valid. An older ref is reacquired only when its saved fingerprint (role, name, AutomationId, named parents, position) matches exactly one element; the element's `ref` changes and the action result carries `reacquired`. Otherwise it throws `StaleRef` naming why and the candidates: re-snapshot; NEVER guess.\n- Actions return `{ action, status, suggestedNext, evidence?, escalation?, reacquired? }`. `verified` rests on an AX readback; `unverified` (all pointer/keyboard input) means delivered but unobserved: `reobserve` before relying on it; `suspected_noop` → follow `escalation`. Inside `computer.run`, a thrown action error carries `error.computerAction`.\n",
|
|
6620
|
+
},
|
|
5639
6621
|
{
|
|
5640
6622
|
// MCP 선택 연결: 요청별 MCP 선택(opt-in). all이 기본이며 per-request면 시작 시 연결하지 않고 요청마다 확장이 고른다.
|
|
5641
6623
|
file: "src/mcp/settings.ts",
|
|
@@ -6421,6 +7403,83 @@ export interface McpSelectEventResult {
|
|
|
6421
7403
|
}
|
|
6422
7404
|
`,
|
|
6423
7405
|
},
|
|
7406
|
+
{
|
|
7407
|
+
// 교훈 자동 저장 표시(2026-09-29 사용자 지적 "교훈 저장이 안 뜬다"): autolearn capture Agent는 본 대화와
|
|
7408
|
+
// 분리돼 저장해도 화면·다음 턴 문맥에 아무것도 남지 않았다. 성공한 learn·manage_skill만 요약해 돌려준다.
|
|
7409
|
+
file: "src/sdk.ts",
|
|
7410
|
+
marker: `export function summarizeAutoLearnSaved(`,
|
|
7411
|
+
anchor: ` createSessionId?: () => string;
|
|
7412
|
+
}
|
|
7413
|
+
|
|
7414
|
+
/** Build a private capture runner over a detached message snapshot and provider session. */`,
|
|
7415
|
+
patched: ` createSessionId?: () => string;
|
|
7416
|
+
/** Receives the capture run's successful \`learn\`/\`manage_skill\` saves so the session can show them. */
|
|
7417
|
+
onCaptured?: (saved: string[]) => void;
|
|
7418
|
+
}
|
|
7419
|
+
|
|
7420
|
+
/**
|
|
7421
|
+
* Summarize a capture run's successful \`learn\`/\`manage_skill\` calls. Each call is paired with its
|
|
7422
|
+
* result, so a rejected or failed save is never reported as stored.
|
|
7423
|
+
*/
|
|
7424
|
+
export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from: number): string[] {
|
|
7425
|
+
const text = (value: unknown): string => (typeof value === "string" ? value.replace(/\\s+/g, " ").trim() : "");
|
|
7426
|
+
const calls = new Map<string, { name: string; args: Record<string, unknown> }>();
|
|
7427
|
+
const saved: string[] = [];
|
|
7428
|
+
for (const message of messages.slice(from)) {
|
|
7429
|
+
if (message.role === "assistant") {
|
|
7430
|
+
for (const block of message.content) {
|
|
7431
|
+
if (block.type === "toolCall") {
|
|
7432
|
+
calls.set(block.id, { name: block.name, args: (block.arguments ?? {}) as Record<string, unknown> });
|
|
7433
|
+
}
|
|
7434
|
+
}
|
|
7435
|
+
continue;
|
|
7436
|
+
}
|
|
7437
|
+
if (message.role !== "toolResult" || message.isError) continue;
|
|
7438
|
+
const call = calls.get(message.toolCallId);
|
|
7439
|
+
if (!call) continue;
|
|
7440
|
+
if (call.name === "learn") {
|
|
7441
|
+
const memory = text(call.args.memory);
|
|
7442
|
+
const skill = call.args.skill as { action?: unknown; name?: unknown } | undefined;
|
|
7443
|
+
const skillNote = skill ? " (스킬 " + text(skill.action) + " " + text(skill.name) + ")" : "";
|
|
7444
|
+
saved.push("교훈: " + (memory.length > 160 ? memory.slice(0, 160) + "…" : memory) + skillNote);
|
|
7445
|
+
} else if (call.name === "manage_skill") {
|
|
7446
|
+
saved.push("스킬 " + text(call.args.action) + ": " + text(call.args.name));
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
return saved;
|
|
7450
|
+
}
|
|
7451
|
+
|
|
7452
|
+
/** Build a private capture runner over a detached message snapshot and provider session. */`,
|
|
7453
|
+
},
|
|
7454
|
+
{
|
|
7455
|
+
// capture가 중간에 실패·중단돼도 이미 저장된 항목은 알린다(finally).
|
|
7456
|
+
file: "src/sdk.ts",
|
|
7457
|
+
marker: ` const saved = summarizeAutoLearnSaved(captureAgent.state.messages, captureMessages.length);`,
|
|
7458
|
+
anchor: ` } finally {
|
|
7459
|
+
signal?.removeEventListener("abort", abortCapture);`,
|
|
7460
|
+
patched: ` } finally {
|
|
7461
|
+
signal?.removeEventListener("abort", abortCapture);
|
|
7462
|
+
const saved = summarizeAutoLearnSaved(captureAgent.state.messages, captureMessages.length);
|
|
7463
|
+
if (saved.length > 0) options.onCaptured?.(saved);`,
|
|
7464
|
+
},
|
|
7465
|
+
{
|
|
7466
|
+
// 저장 결과를 보이는 custom 메시지로 남긴다. idle이면 턴을 시작하지 않고 붙이고, 진행 중이면 aside로 끼운다.
|
|
7467
|
+
file: "src/sdk.ts",
|
|
7468
|
+
marker: ` { customType: "autolearn-saved", content, display: true, attribution: "agent" },`,
|
|
7469
|
+
anchor: ` const runAutoLearnCapture = createAutoLearnCaptureRunner({
|
|
7470
|
+
sourceAgent: agent,`,
|
|
7471
|
+
patched: ` const runAutoLearnCapture = createAutoLearnCaptureRunner({
|
|
7472
|
+
sourceAgent: agent,
|
|
7473
|
+
onCaptured: saved => {
|
|
7474
|
+
const content = ["[교훈 자동 저장] 이번 턴이 끝난 뒤 별도 capture가 저장했다.", ...saved.map(line => "- " + line)].join("\\n");
|
|
7475
|
+
void session
|
|
7476
|
+
.sendCustomMessage(
|
|
7477
|
+
{ customType: "autolearn-saved", content, display: true, attribution: "agent" },
|
|
7478
|
+
session.isStreaming ? { deliverAs: "aside" } : undefined,
|
|
7479
|
+
)
|
|
7480
|
+
.catch(error => logger.warn("Failed to show auto-learn capture result", { error: String(error) }));
|
|
7481
|
+
},`,
|
|
7482
|
+
},
|
|
6424
7483
|
];
|
|
6425
7484
|
// EDITS 문자열의 줄 끝을 LF로 통일한다. 이 파일의 작업 사본이 CRLF여도 core 파일(LF)과
|
|
6426
7485
|
// 비교·치환이 어긋나지 않는다. core 파일 자체의 줄 끝은 건드리지 않는다.
|