cuelo 0.9.6 → 0.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +12 -11
- package/.next/build-manifest.json +3 -3
- 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 +6 -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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- 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 +1 -1
- 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 +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 +3 -3
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/github/projects/route.js +1 -0
- package/.next/server/app/api/github/projects/route.js.nft.json +1 -0
- package/.next/server/app/api/github/projects/route_client-reference-manifest.js +1 -0
- 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/character-voice/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 +2 -2
- package/.next/server/app/api/lounge/route.js +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-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 +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 +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- 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 +2 -2
- 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 +2 -2
- package/.next/server/app/office-preview.segments/_full.segment.rsc +2 -2
- package/.next/server/app/office-preview.segments/_head.segment.rsc +1 -1
- package/.next/server/app/office-preview.segments/_index.segment.rsc +2 -2
- package/.next/server/app/office-preview.segments/_tree.segment.rsc +1 -1
- 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 +2 -2
- package/.next/server/app/office.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/office.segments/office/__PAGE__.segment.rsc +4 -4
- 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 +2 -2
- package/.next/server/app/recover.segments/_full.segment.rsc +2 -2
- package/.next/server/app/recover.segments/_head.segment.rsc +1 -1
- package/.next/server/app/recover.segments/_index.segment.rsc +2 -2
- package/.next/server/app/recover.segments/_tree.segment.rsc +1 -1
- 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 +12 -11
- package/.next/server/chunks/1008.js +2 -2
- package/.next/server/chunks/2651.js +1 -1
- package/.next/server/chunks/3379.js +1 -1
- package/.next/server/chunks/5489.js +12 -12
- package/.next/server/chunks/6429.js +2 -2
- package/.next/server/chunks/7362.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware.js +2 -2
- 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/6110-9f1e951aafcdd72e.js +1 -0
- package/.next/static/chunks/9575-fe25836fd76b4655.js +22 -0
- package/.next/static/chunks/app/{api/agent/[id]/bash-output/route-6b1496af498bb4fd.js → _global-error/page-318ea6a550b5034e.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/{events/route-6b1496af498bb4fd.js → account/route-318ea6a550b5034e.js} +1 -1
- package/.next/static/chunks/app/{_global-error/page-6b1496af498bb4fd.js → api/agent/[id]/bash-output/route-318ea6a550b5034e.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/{account/route-6b1496af498bb4fd.js → events/route-318ea6a550b5034e.js} +1 -1
- package/.next/static/chunks/app/api/agent/[id]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/attachment-settings/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/attachments/[id]/transcription/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/attachments/draft/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/attachments/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/display-settings/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/document-text/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/experiments/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/github/projects/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/gpt6/handles/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/gpt6/reply/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/gpt6/token/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/gpt6/web6-session/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/home/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/live/call/[callId]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/live/character-voice/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/live/events/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/live/offer/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/live/voices/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/lounge/events/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/lounge/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/model-roles/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/models/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/office-avatars/[id]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/office-avatars/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/projects/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/runs/[runId]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/runs/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sessions/search/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/settings/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/skills/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/theme/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/tool-image/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/update-maintenance/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/updates/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/web-access/recovery/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/web-access/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/web6/consults/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/{layout-15c56b62f49b1c1d.js → layout-2bf5721b24426873.js} +1 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/app/office/page-8dc604f9b5e6f220.js +1 -0
- package/.next/static/chunks/app/page-952b1ef4df5ac896.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-318ea6a550b5034e.js +1 -0
- package/.next/static/chunks/{webpack-fe8f1f81562a2b61.js → webpack-68986b211ad38cf1.js} +1 -1
- package/.next/static/css/4fae42750604bc1a.css +1 -0
- package/.next/static/vrwvD7kuXEjDJZtO0Q3iO/_buildManifest.js +1 -0
- package/.next/trace +108 -108
- package/.next/trace-build +1 -1
- package/.next/types/app/api/github/projects/route.ts +351 -0
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/README.md +6 -3
- package/Tools/CUELO_Setup/files/native-runtime-patch.js +3 -3
- package/Tools/OMP_Global_Config/agent/RULES.md +1 -0
- package/Tools/OMP_Global_Config/agent/TITLE_SYSTEM.md +17 -0
- package/Tools/OMP_Global_Config/agent/extensions/jev-runtime.ts +11 -4
- package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +2 -1
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/finalizer.ps1 +68 -31
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/index.ts +8 -6
- package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +1119 -129
- package/bin/docker-entrypoint.mjs +19 -0
- package/install.mjs +11 -7
- package/package.json +8 -7
- package/.next/static/chunks/6110-004be57c825af4d9.js +0 -1
- package/.next/static/chunks/9575-4a84cf066c2f1257.js +0 -22
- package/.next/static/chunks/app/api/agent/[id]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/attachment-settings/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/attachments/[id]/transcription/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/attachments/draft/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/attachments/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/display-settings/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/document-text/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/experiments/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/handles/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/reply/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/token/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/gpt6/web6-session/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/home/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/live/call/[callId]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/live/character-voice/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/live/events/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/live/offer/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/live/voices/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/lounge/events/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/lounge/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/mcp/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/model-roles/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/models/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/models-config/catalog/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/office-avatars/[id]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/office-avatars/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/project-trust/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/projects/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/runs/[runId]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/runs/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sessions/search/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/settings/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/skills/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/theme/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/tool-image/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/update-maintenance/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/updates/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/web-access/recovery/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/web-access/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/web6/consults/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/app/office/page-424e244f1c7fcdc6.js +0 -1
- package/.next/static/chunks/app/page-b43b2b8679ed66e2.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-6b1496af498bb4fd.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-6b1496af498bb4fd.js +0 -1
- package/.next/static/css/00f20b26266e86ed.css +0 -1
- package/.next/static/kRJ4zgHxJf680foSBrfpf/_buildManifest.js +0 -1
- /package/.next/static/{kRJ4zgHxJf680foSBrfpf → vrwvD7kuXEjDJZtO0Q3iO}/_ssgManifest.js +0 -0
|
@@ -344,6 +344,131 @@ const TASK_AGENT_OPTIONAL_EDITS = [
|
|
|
344
344
|
},
|
|
345
345
|
];
|
|
346
346
|
|
|
347
|
+
/**
|
|
348
|
+
* 부모 steer bracket 장부(#ircTurnBracketOpen·adopted steer)와 입양·continuation 관찰 메서드. setIrcWakeTurnObserver
|
|
349
|
+
* 항목의 18.6.1 후보와 18.6.3 후보가 같은 본문을 붙이므로 한 곳에 둔다(값은 18.6.1 후보 patched 의 해당 구간 그대로).
|
|
350
|
+
*/
|
|
351
|
+
const IRC_STEER_BRACKET_MEMBERS = `
|
|
352
|
+
|
|
353
|
+
/** True while some bracket already owes the steering parent this turn's
|
|
354
|
+
* result: the wake monitor, a continuation that started owing a steer, or an
|
|
355
|
+
* adoption below. One owner per turn - never two relays for one turn. */
|
|
356
|
+
#ircTurnBracketOpen = false;
|
|
357
|
+
|
|
358
|
+
/** Parent steers that landed after this turn's bracket was already open - a
|
|
359
|
+
* sibling peer's wake turn, or an earlier adoption. The open bracket owns
|
|
360
|
+
* them: a turn finalizes exactly once, so they ride that finalization
|
|
361
|
+
* instead of opening a second monitor for the same turn. */
|
|
362
|
+
#adoptedParentSteerEntries: ParentSteerRelay[] = [];
|
|
363
|
+
|
|
364
|
+
/** Hands the open bracket the adopted steers this turn actually consumed, so
|
|
365
|
+
* its single finalization answers their sender too. Stranded ones stay parked
|
|
366
|
+
* for the continuation that consumes them. */
|
|
367
|
+
#takeAdoptedParentSteerRecords(): AgentMessage[] {
|
|
368
|
+
const entries = this.#adoptedParentSteerEntries;
|
|
369
|
+
if (entries.length === 0) return [];
|
|
370
|
+
this.#adoptedParentSteerEntries = [];
|
|
371
|
+
const queued = this.agent.peekSteeringQueue();
|
|
372
|
+
return entries.filter(entry => !queued.some(message => message === entry.steered)).map(entry => entry.record);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* A parent steer landed inside a turn that is ALREADY running, so no bracket
|
|
377
|
+
* could have been opened for it at turn start. Ownership follows consumption:
|
|
378
|
+
* if this running turn polls the steer out of the agent-core queue, this
|
|
379
|
+
* turn's result is the answer, and nothing else will ever send one - the
|
|
380
|
+
* spawn job settled long ago and #scheduleAgentContinue only brackets turns
|
|
381
|
+
* that START owing a steer. So bracket the running turn now, while the
|
|
382
|
+
* monitor can still watch it, and decide at settle from the queue itself:
|
|
383
|
+
* - steer gone -> this turn consumed it -> relay its result, exactly once;
|
|
384
|
+
* - steer queued -> genuinely stranded -> stay silent and leave the
|
|
385
|
+
* parked record to the continuation that will consume it.
|
|
386
|
+
* No-ops while the spawn job still owns the run (no observer installed).
|
|
387
|
+
*/
|
|
388
|
+
#adoptParentSteerForRunningTurn(): void {
|
|
389
|
+
if (this.#promptInFlightCount === 0 || !this.isStreaming) return;
|
|
390
|
+
const observer = this.#ircWakeTurnObserver;
|
|
391
|
+
if (observer === undefined) return;
|
|
392
|
+
const entries = this.#irc.peekParentSteerRelays();
|
|
393
|
+
if (entries.length === 0) return;
|
|
394
|
+
if (this.#ircTurnBracketOpen) {
|
|
395
|
+
// Another bracket already owes this turn's one finalization (a sibling's
|
|
396
|
+
// wake turn, or an earlier adoption). Opening a second monitor would
|
|
397
|
+
// finalize the same turn twice - two lifecycle frames, two artifacts -
|
|
398
|
+
// while dropping the steer would leave the parent unanswered whenever
|
|
399
|
+
// this turn consumes it. Ride the open bracket instead.
|
|
400
|
+
for (const entry of entries) {
|
|
401
|
+
if (!this.#adoptedParentSteerEntries.includes(entry)) this.#adoptedParentSteerEntries.push(entry);
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
let finish:
|
|
406
|
+
| ((error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>)
|
|
407
|
+
| undefined;
|
|
408
|
+
try {
|
|
409
|
+
// The parent's steer did not wake this turn, and it may still strand:
|
|
410
|
+
// the monitor must not claim the parent's answer up front.
|
|
411
|
+
finish = observer(entries.map(entry => entry.record), { runningTurn: true });
|
|
412
|
+
} catch (error) {
|
|
413
|
+
logger.warn("IRC steer continuation observer failed to start", { error: String(error) });
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (finish === undefined) return;
|
|
417
|
+
this.#ircTurnBracketOpen = true;
|
|
418
|
+
// Same settle hook every other bracket uses; it runs before the queue
|
|
419
|
+
// reconciliation in #drainStrandedQueuedMessages, so the entries this turn
|
|
420
|
+
// consumed are still parked here and retire immediately afterwards.
|
|
421
|
+
this.#inFlightSettledCallbacks.push(async () => {
|
|
422
|
+
this.#ircTurnBracketOpen = false;
|
|
423
|
+
const queued = this.agent.peekSteeringQueue();
|
|
424
|
+
const stranded = entries.some(entry => queued.some(message => message === entry.steered));
|
|
425
|
+
try {
|
|
426
|
+
await finish?.(undefined, stranded, this.#takeAdoptedParentSteerRecords());
|
|
427
|
+
} catch (error) {
|
|
428
|
+
logger.warn("IRC steer continuation observer failed to finish", { error: String(error) });
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Brackets an autonomous continuation with the wake-turn monitor when a parent
|
|
435
|
+
* IRC steer is what it resumes. \`#ircWakeTurnObserver\` exists only after the
|
|
436
|
+
* kept-alive subagent's spawn run settled (task executor
|
|
437
|
+
* \`installIrcWakeTurnMonitor\`), so its presence is exactly the condition "this
|
|
438
|
+
* agent's job can no longer deliver its results". That flag is handed to the
|
|
439
|
+
* bridge rather than short-circuiting here: while the job still owns the run
|
|
440
|
+
* the record must stay parked, and the queue-backed reconciliation in
|
|
441
|
+
* #drainStrandedQueuedMessages - not this call - decides when it retires.
|
|
442
|
+
* Returns the consumed entries so a continuation that never ran can re-arm.
|
|
443
|
+
*/
|
|
444
|
+
#beginIrcSteerContinuationObservation():
|
|
445
|
+
| {
|
|
446
|
+
entries: ParentSteerRelay[];
|
|
447
|
+
finish:
|
|
448
|
+
| ((error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>)
|
|
449
|
+
| undefined;
|
|
450
|
+
}
|
|
451
|
+
| undefined {
|
|
452
|
+
const observer = this.#ircWakeTurnObserver;
|
|
453
|
+
// A bracket is already open for this turn: #adoptParentSteerForRunningTurn
|
|
454
|
+
// took the running turn and settles the same obligation from the queue.
|
|
455
|
+
// Taking the entries here would open a second monitor for one obligation -
|
|
456
|
+
// two "started" lifecycle frames, two finalized run artifacts, and a parent
|
|
457
|
+
// notice that stays single only because the bus drops the duplicate send.
|
|
458
|
+
if (this.#ircTurnBracketOpen) return undefined;
|
|
459
|
+
const entries = this.#irc.takeParentSteerRelays(observer !== undefined);
|
|
460
|
+
if (observer === undefined || entries.length === 0) return undefined;
|
|
461
|
+
const records = entries.map(entry => entry.record);
|
|
462
|
+
try {
|
|
463
|
+
const finish = observer(records);
|
|
464
|
+
if (finish !== undefined) this.#ircTurnBracketOpen = true;
|
|
465
|
+
return { entries, finish };
|
|
466
|
+
} catch (error) {
|
|
467
|
+
logger.warn("IRC steer continuation observer failed to start", { error: String(error) });
|
|
468
|
+
return { entries, finish: undefined };
|
|
469
|
+
}
|
|
470
|
+
}`;
|
|
471
|
+
|
|
347
472
|
/** 각 항목: 원본 앵커를 찾아 patched 로 바꾼다. marker 가 있으면 이미 적용된 것으로 본다. */
|
|
348
473
|
const EDITS = [
|
|
349
474
|
{
|
|
@@ -1379,6 +1504,9 @@ export interface IrcBridgeHost {
|
|
|
1379
1504
|
// 더 이상 결과를 전달할 수 없다"는 조건이다.
|
|
1380
1505
|
file: "src/session/agent-session.ts",
|
|
1381
1506
|
marker: "#beginIrcSteerContinuationObservation",
|
|
1507
|
+
// 18.6.3(#14428)은 이 setter 의 주석을 바꾸고 관찰 수명을 #openTurnObservation 으로 관리한다. 두 판의 결과가 같은
|
|
1508
|
+
// marker 를 가지므로 그 필드 유무로 후보를 가른다. 붙이는 장부·메서드 본문은 같다(IRC_STEER_BRACKET_MEMBERS).
|
|
1509
|
+
excludes: "#openTurnObservation",
|
|
1382
1510
|
anchor: ` /** Installs task-executor monitoring around autonomous IRC wake turns. */
|
|
1383
1511
|
setIrcWakeTurnObserver(
|
|
1384
1512
|
observer: ((records: AgentMessage[]) => ((error?: unknown) => void | Promise<void>) | undefined) | undefined,
|
|
@@ -1405,126 +1533,45 @@ export interface IrcBridgeHost {
|
|
|
1405
1533
|
| undefined,
|
|
1406
1534
|
): void {
|
|
1407
1535
|
this.#ircWakeTurnObserver = observer;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
* sibling peer's wake turn, or an earlier adoption. The open bracket owns
|
|
1417
|
-
* them: a turn finalizes exactly once, so they ride that finalization
|
|
1418
|
-
* instead of opening a second monitor for the same turn. */
|
|
1419
|
-
#adoptedParentSteerEntries: ParentSteerRelay[] = [];
|
|
1420
|
-
|
|
1421
|
-
/** Hands the open bracket the adopted steers this turn actually consumed, so
|
|
1422
|
-
* its single finalization answers their sender too. Stranded ones stay parked
|
|
1423
|
-
* for the continuation that consumes them. */
|
|
1424
|
-
#takeAdoptedParentSteerRecords(): AgentMessage[] {
|
|
1425
|
-
const entries = this.#adoptedParentSteerEntries;
|
|
1426
|
-
if (entries.length === 0) return [];
|
|
1427
|
-
this.#adoptedParentSteerEntries = [];
|
|
1428
|
-
const queued = this.agent.peekSteeringQueue();
|
|
1429
|
-
return entries.filter(entry => !queued.some(message => message === entry.steered)).map(entry => entry.record);
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
/**
|
|
1433
|
-
* A parent steer landed inside a turn that is ALREADY running, so no bracket
|
|
1434
|
-
* could have been opened for it at turn start. Ownership follows consumption:
|
|
1435
|
-
* if this running turn polls the steer out of the agent-core queue, this
|
|
1436
|
-
* turn's result is the answer, and nothing else will ever send one - the
|
|
1437
|
-
* spawn job settled long ago and #scheduleAgentContinue only brackets turns
|
|
1438
|
-
* that START owing a steer. So bracket the running turn now, while the
|
|
1439
|
-
* monitor can still watch it, and decide at settle from the queue itself:
|
|
1440
|
-
* - steer gone -> this turn consumed it -> relay its result, exactly once;
|
|
1441
|
-
* - steer queued -> genuinely stranded -> stay silent and leave the
|
|
1442
|
-
* parked record to the continuation that will consume it.
|
|
1443
|
-
* No-ops while the spawn job still owns the run (no observer installed).
|
|
1536
|
+
}` + IRC_STEER_BRACKET_MEMBERS,
|
|
1537
|
+
alternates: [{
|
|
1538
|
+
file: "src/session/agent-session.ts",
|
|
1539
|
+
requires: "#openTurnObservation",
|
|
1540
|
+
marker: "#beginIrcSteerContinuationObservation",
|
|
1541
|
+
anchor: ` /**
|
|
1542
|
+
* Installs task-executor monitoring around turns no executor drives: autonomous IRC
|
|
1543
|
+
* wake turns and user prompts (focused-session steering of a kept-alive subagent).
|
|
1444
1544
|
*/
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
}
|
|
1462
|
-
let finish:
|
|
1463
|
-
| ((error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>)
|
|
1464
|
-
| undefined;
|
|
1465
|
-
try {
|
|
1466
|
-
// The parent's steer did not wake this turn, and it may still strand:
|
|
1467
|
-
// the monitor must not claim the parent's answer up front.
|
|
1468
|
-
finish = observer(entries.map(entry => entry.record), { runningTurn: true });
|
|
1469
|
-
} catch (error) {
|
|
1470
|
-
logger.warn("IRC steer continuation observer failed to start", { error: String(error) });
|
|
1471
|
-
return;
|
|
1472
|
-
}
|
|
1473
|
-
if (finish === undefined) return;
|
|
1474
|
-
this.#ircTurnBracketOpen = true;
|
|
1475
|
-
// Same settle hook every other bracket uses; it runs before the queue
|
|
1476
|
-
// reconciliation in #drainStrandedQueuedMessages, so the entries this turn
|
|
1477
|
-
// consumed are still parked here and retire immediately afterwards.
|
|
1478
|
-
this.#inFlightSettledCallbacks.push(async () => {
|
|
1479
|
-
this.#ircTurnBracketOpen = false;
|
|
1480
|
-
const queued = this.agent.peekSteeringQueue();
|
|
1481
|
-
const stranded = entries.some(entry => queued.some(message => message === entry.steered));
|
|
1482
|
-
try {
|
|
1483
|
-
await finish?.(undefined, stranded, this.#takeAdoptedParentSteerRecords());
|
|
1484
|
-
} catch (error) {
|
|
1485
|
-
logger.warn("IRC steer continuation observer failed to finish", { error: String(error) });
|
|
1486
|
-
}
|
|
1487
|
-
});
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
/**
|
|
1491
|
-
* Brackets an autonomous continuation with the wake-turn monitor when a parent
|
|
1492
|
-
* IRC steer is what it resumes. \`#ircWakeTurnObserver\` exists only after the
|
|
1493
|
-
* kept-alive subagent's spawn run settled (task executor
|
|
1494
|
-
* \`installIrcWakeTurnMonitor\`), so its presence is exactly the condition "this
|
|
1495
|
-
* agent's job can no longer deliver its results". That flag is handed to the
|
|
1496
|
-
* bridge rather than short-circuiting here: while the job still owns the run
|
|
1497
|
-
* the record must stay parked, and the queue-backed reconciliation in
|
|
1498
|
-
* #drainStrandedQueuedMessages - not this call - decides when it retires.
|
|
1499
|
-
* Returns the consumed entries so a continuation that never ran can re-arm.
|
|
1545
|
+
setIrcWakeTurnObserver(
|
|
1546
|
+
observer: ((records: AgentMessage[]) => ((error?: unknown) => void | Promise<void>) | undefined) | undefined,
|
|
1547
|
+
): void {
|
|
1548
|
+
this.#ircWakeTurnObserver = observer;
|
|
1549
|
+
}`,
|
|
1550
|
+
patched: ` /**
|
|
1551
|
+
* Installs task-executor monitoring around turns no executor drives: autonomous IRC
|
|
1552
|
+
* wake turns and user prompts (focused-session steering of a kept-alive subagent).
|
|
1553
|
+
* \`suppressRelay\` lets the session finalize a bracketed turn without
|
|
1554
|
+
* claiming it answered the parent (see #adoptParentSteerForRunningTurn).
|
|
1555
|
+
* \`adoptedRecords\` carries steers that landed after the bracket opened and
|
|
1556
|
+
* that this same turn consumed: one finalization answers every source.
|
|
1557
|
+
* \`bracket.runningTurn\` marks a bracket opened over a turn that was ALREADY
|
|
1558
|
+
* running (the parent's steer did not wake it), so the monitor must not
|
|
1559
|
+
* treat it as parent-woken work: whether it owns the answer is only known
|
|
1560
|
+
* at settle.
|
|
1500
1561
|
*/
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1562
|
+
setIrcWakeTurnObserver(
|
|
1563
|
+
observer:
|
|
1564
|
+
| ((
|
|
1565
|
+
records: AgentMessage[],
|
|
1566
|
+
bracket?: { runningTurn?: boolean },
|
|
1567
|
+
) =>
|
|
1505
1568
|
| ((error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>)
|
|
1506
|
-
| undefined
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
// Taking the entries here would open a second monitor for one obligation -
|
|
1513
|
-
// two "started" lifecycle frames, two finalized run artifacts, and a parent
|
|
1514
|
-
// notice that stays single only because the bus drops the duplicate send.
|
|
1515
|
-
if (this.#ircTurnBracketOpen) return undefined;
|
|
1516
|
-
const entries = this.#irc.takeParentSteerRelays(observer !== undefined);
|
|
1517
|
-
if (observer === undefined || entries.length === 0) return undefined;
|
|
1518
|
-
const records = entries.map(entry => entry.record);
|
|
1519
|
-
try {
|
|
1520
|
-
const finish = observer(records);
|
|
1521
|
-
if (finish !== undefined) this.#ircTurnBracketOpen = true;
|
|
1522
|
-
return { entries, finish };
|
|
1523
|
-
} catch (error) {
|
|
1524
|
-
logger.warn("IRC steer continuation observer failed to start", { error: String(error) });
|
|
1525
|
-
return { entries, finish: undefined };
|
|
1526
|
-
}
|
|
1527
|
-
}`,
|
|
1569
|
+
| undefined)
|
|
1570
|
+
| undefined,
|
|
1571
|
+
): void {
|
|
1572
|
+
this.#ircWakeTurnObserver = observer;
|
|
1573
|
+
}` + IRC_STEER_BRACKET_MEMBERS,
|
|
1574
|
+
}],
|
|
1528
1575
|
},
|
|
1529
1576
|
{
|
|
1530
1577
|
// 실제 bracket. #wakeForIrc 가 wake turn 을 감싸는 방식과 같은 계약이다:
|
|
@@ -1765,6 +1812,50 @@ export interface IrcBridgeHost {
|
|
|
1765
1812
|
} catch (error) {
|
|
1766
1813
|
logger.warn("IRC wake turn observer failed to start", { error: String(error) });
|
|
1767
1814
|
}`,
|
|
1815
|
+
// 18.6.3(#14428)은 wake 와 사용자 prompt 의 관찰을 #startTurnObservation 으로 모으고 "세션당 열린 관찰 하나"를
|
|
1816
|
+
// #openTurnObservation 으로 지킨다. 우리 장부도 그 관찰을 이 turn 의 bracket 으로 본다. 반대로 우리 bracket(입양·
|
|
1817
|
+
// stranded steer continuation)이 돌고 있는 turn 을 잡고 있으면, 상류가 자기 관찰의 turn 이 아직 돌 때 하듯 시작하지
|
|
1818
|
+
// 않는다. 두 장부가 서로를 모르면 같은 turn 을 두 monitor 가 정산한다(ADAPT).
|
|
1819
|
+
alternates: [{
|
|
1820
|
+
file: "src/session/agent-session.ts",
|
|
1821
|
+
marker: "this.#openTurnObservation = observation;\n\t\t// The observation is this turn's bracket",
|
|
1822
|
+
anchor: ` const open = this.#openTurnObservation;
|
|
1823
|
+
if (open) {
|
|
1824
|
+
if (!open.turnEnded) return undefined;
|
|
1825
|
+
void this.#closeTurnObservation(open);
|
|
1826
|
+
}
|
|
1827
|
+
let finish: TurnObservation["finish"] | undefined;
|
|
1828
|
+
try {
|
|
1829
|
+
finish = this.#ircWakeTurnObserver?.(records);
|
|
1830
|
+
} catch (error) {
|
|
1831
|
+
logger.warn("Turn observer failed to start", { error: String(error) });
|
|
1832
|
+
}
|
|
1833
|
+
if (!finish) return undefined;
|
|
1834
|
+
const observation: TurnObservation = { finish, turnEnded: false, closed: false, turnError: undefined };
|
|
1835
|
+
this.#openTurnObservation = observation;`,
|
|
1836
|
+
patched: ` const open = this.#openTurnObservation;
|
|
1837
|
+
if (open) {
|
|
1838
|
+
if (!open.turnEnded) return undefined;
|
|
1839
|
+
void this.#closeTurnObservation(open);
|
|
1840
|
+
} else if (this.#ircTurnBracketOpen) {
|
|
1841
|
+
// A parent-steer bracket (#adoptParentSteerForRunningTurn, or the
|
|
1842
|
+
// continuation that resumes a stranded steer) already owns the running
|
|
1843
|
+
// turn. One monitor per turn, as for an open observation whose turn runs.
|
|
1844
|
+
return undefined;
|
|
1845
|
+
}
|
|
1846
|
+
let finish: TurnObservation["finish"] | undefined;
|
|
1847
|
+
try {
|
|
1848
|
+
finish = this.#ircWakeTurnObserver?.(records);
|
|
1849
|
+
} catch (error) {
|
|
1850
|
+
logger.warn("Turn observer failed to start", { error: String(error) });
|
|
1851
|
+
}
|
|
1852
|
+
if (!finish) return undefined;
|
|
1853
|
+
const observation: TurnObservation = { finish, turnEnded: false, closed: false, turnError: undefined };
|
|
1854
|
+
this.#openTurnObservation = observation;
|
|
1855
|
+
// The observation is this turn's bracket: a parent steer landing in it rides
|
|
1856
|
+
// its single finalization instead of opening a second monitor.
|
|
1857
|
+
this.#ircTurnBracketOpen = true;`,
|
|
1858
|
+
}],
|
|
1768
1859
|
},
|
|
1769
1860
|
{
|
|
1770
1861
|
// wake turn 의 지역 선언도 같은 계약을 실어야 한다. 이 turn 도중에 도착해
|
|
@@ -1775,6 +1866,15 @@ export interface IrcBridgeHost {
|
|
|
1775
1866
|
patched: ` let finishObservation:
|
|
1776
1867
|
| ((error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>)
|
|
1777
1868
|
| undefined;`,
|
|
1869
|
+
// 18.6.3: wake 지역 변수 대신 TurnObservation.finish 가 같은 계약을 싣는다(ADAPT).
|
|
1870
|
+
alternates: [{
|
|
1871
|
+
file: "src/session/agent-session.ts",
|
|
1872
|
+
marker: "type TurnObservation = {\n\tfinish: (error?: unknown, suppressRelay?: boolean",
|
|
1873
|
+
anchor: `type TurnObservation = {
|
|
1874
|
+
finish: (error?: unknown) => void | Promise<void>;`,
|
|
1875
|
+
patched: `type TurnObservation = {
|
|
1876
|
+
finish: (error?: unknown, suppressRelay?: boolean, adoptedRecords?: AgentMessage[]) => void | Promise<void>;`,
|
|
1877
|
+
}],
|
|
1778
1878
|
},
|
|
1779
1879
|
{
|
|
1780
1880
|
// wake bracket 의 닫는 쪽. 같은 장부를 되돌린다.
|
|
@@ -1828,6 +1928,54 @@ export interface IrcBridgeHost {
|
|
|
1828
1928
|
};
|
|
1829
1929
|
if (this.#promptInFlightCount > 0) this.#inFlightSettledCallbacks.push(finishWakeObservation);
|
|
1830
1930
|
else await finishWakeObservation();`,
|
|
1931
|
+
}, {
|
|
1932
|
+
// 18.6.3: 같은 정산이 #finishTurnObservation(settle 대기)과 #closeTurnObservation(한 번만 finish)으로 옮겨졌다.
|
|
1933
|
+
// settle 뒤에도 turn 이 in-flight 면 그 settle 콜백에서 닫고, 닫을 때 장부를 내리고 adopted steer 를 넘긴다(ADAPT).
|
|
1934
|
+
// 새 wake·사용자 prompt 가 먼저 닫으면(상류 supersede) 미뤄 둔 닫기는 closed 로 끝난다.
|
|
1935
|
+
file: "src/session/agent-session.ts",
|
|
1936
|
+
marker: "await observation.finish(observation.turnError, undefined, adoptedRecords);",
|
|
1937
|
+
anchor: ` try {
|
|
1938
|
+
await this.settleAsyncWork();
|
|
1939
|
+
} catch (error) {
|
|
1940
|
+
logger.warn("Turn observation async-work settle failed", { error: String(error) });
|
|
1941
|
+
}
|
|
1942
|
+
await this.#closeTurnObservation(observation);
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
/** Runs an observation's finish exactly once; the observer detaches synchronously before the first await. */
|
|
1946
|
+
async #closeTurnObservation(observation: TurnObservation): Promise<void> {
|
|
1947
|
+
if (observation.closed) return;
|
|
1948
|
+
observation.closed = true;
|
|
1949
|
+
if (this.#openTurnObservation === observation) this.#openTurnObservation = undefined;
|
|
1950
|
+
try {
|
|
1951
|
+
await observation.finish(observation.turnError);`,
|
|
1952
|
+
patched: ` try {
|
|
1953
|
+
await this.settleAsyncWork();
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
logger.warn("Turn observation async-work settle failed", { error: String(error) });
|
|
1956
|
+
}
|
|
1957
|
+
// The settle waits for agent streaming only. A turn still in flight past it
|
|
1958
|
+
// (a continuation between #beginInFlight and agent.continue that resumes a
|
|
1959
|
+
// stranded parent steer) runs inside this bracket, so close at its own
|
|
1960
|
+
// settle: the adopted steers it consumes then ride this one finalization.
|
|
1961
|
+
if (this.#promptInFlightCount > 0) {
|
|
1962
|
+
this.#inFlightSettledCallbacks.push(() => this.#closeTurnObservation(observation));
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
await this.#closeTurnObservation(observation);
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/** Runs an observation's finish exactly once; the observer detaches synchronously before the first await. */
|
|
1969
|
+
async #closeTurnObservation(observation: TurnObservation): Promise<void> {
|
|
1970
|
+
if (observation.closed) return;
|
|
1971
|
+
observation.closed = true;
|
|
1972
|
+
if (this.#openTurnObservation === observation) this.#openTurnObservation = undefined;
|
|
1973
|
+
// The bracket ledger closes with the observation; parent steers that landed
|
|
1974
|
+
// after it opened and that its turn consumed ride this finalization.
|
|
1975
|
+
this.#ircTurnBracketOpen = false;
|
|
1976
|
+
const adoptedRecords = this.#takeAdoptedParentSteerRecords();
|
|
1977
|
+
try {
|
|
1978
|
+
await observation.finish(observation.turnError, undefined, adoptedRecords);`,
|
|
1831
1979
|
}],
|
|
1832
1980
|
},
|
|
1833
1981
|
{
|
|
@@ -5676,6 +5824,39 @@ export function hostNextServerEnvUnsets(
|
|
|
5676
5824
|
: hostNextServerEnvUnsets(process.env, shellEnv).filter(name => !(options?.env && name in options.env));
|
|
5677
5825
|
if (hostEnvUnsets.length > 0) preflight.command = \`unset -v \${hostEnvUnsets.join(" ")}; \${preflight.command}\`;
|
|
5678
5826
|
const commandEnv = buildNonInteractiveEnv(preflight.env);
|
|
5827
|
+
`,
|
|
5828
|
+
},
|
|
5829
|
+
{
|
|
5830
|
+
// 2026-10-07 Linux: 위 항목이 건너뛰는 PTY 경로(zsh/fish 사용자 셸의 `!` 핫키)는 map 에서 지운 서버 변수가 되살아났다.
|
|
5831
|
+
// native PTY 가 넘긴 env map 을 OS environ 위에 덧씌우기 때문이다(실측: zsh 이름 셸 + usePty 에서 NODE_ENV=production).
|
|
5832
|
+
// 셸과 무관하게 argv 앞에 `env -u NAME…` 를 붙여 뺀다. 이름 목록은 위 `unset -v` 와 같은 규칙이고, 비면 argv 는 그대로다.
|
|
5833
|
+
// Windows 는 사용자 셸 PTY 를 쓰지 않는다(resolveUserShellConfig 가 기본 셸로 돌아간다).
|
|
5834
|
+
file: "src/exec/bash-executor.ts",
|
|
5835
|
+
marker: "\tenvUnsets: string[]; // HANSE: CUELO next server env",
|
|
5836
|
+
anchor: "\tcwd: string | undefined;\n\tenv: Record<string, string>;\n\tpty: BashPtyOptions;\n",
|
|
5837
|
+
patched: "\tcwd: string | undefined;\n\tenv: Record<string, string>;\n\tenvUnsets: string[]; // HANSE: CUELO next server env\n\tpty: BashPtyOptions;\n",
|
|
5838
|
+
},
|
|
5839
|
+
{
|
|
5840
|
+
file: "src/exec/bash-executor.ts",
|
|
5841
|
+
marker: '...(run.envUnsets.length > 0 ? [...run.envUnsets.flatMap(name => ["-u", name]), run.shell] : []),',
|
|
5842
|
+
anchor: "\t\t\tapplication: run.shell,\n\t\t\targs: [...ensureInteractiveShellArgs(run.shell, run.args), run.command],\n",
|
|
5843
|
+
patched: ` // HANSE: CUELO next server env stays out of the user-shell PTY. The native PTY layers the env map
|
|
5844
|
+
// over the OS environ, so names dropped from the map come back unless env -u removes them at exec.
|
|
5845
|
+
application: run.envUnsets.length > 0 ? "env" : run.shell,
|
|
5846
|
+
args: [
|
|
5847
|
+
...(run.envUnsets.length > 0 ? [...run.envUnsets.flatMap(name => ["-u", name]), run.shell] : []),
|
|
5848
|
+
...ensureInteractiveShellArgs(run.shell, run.args),
|
|
5849
|
+
run.command,
|
|
5850
|
+
],
|
|
5851
|
+
`,
|
|
5852
|
+
},
|
|
5853
|
+
{
|
|
5854
|
+
file: "src/exec/bash-executor.ts",
|
|
5855
|
+
marker: "envUnsets: hostNextServerEnvUnsets(process.env, shellEnv).filter(",
|
|
5856
|
+
anchor: "\t\t\t\tenv: buildUserShellPtyEnv(shellEnv, commandEnv),\n\t\t\t\tpty: ptyRequest,\n",
|
|
5857
|
+
patched: ` env: buildUserShellPtyEnv(shellEnv, commandEnv),
|
|
5858
|
+
envUnsets: hostNextServerEnvUnsets(process.env, shellEnv).filter(name => !(options?.env && name in options.env)),
|
|
5859
|
+
pty: ptyRequest,
|
|
5679
5860
|
`,
|
|
5680
5861
|
},
|
|
5681
5862
|
{
|
|
@@ -5694,14 +5875,53 @@ export function hostNextServerEnvUnsets(
|
|
|
5694
5875
|
patched: 'import { workerEnvFromParent } from "../subprocess/worker-client";\nimport { hostNextServerEnvUnsets } from "@oh-my-pi/pi-utils/env";\n',
|
|
5695
5876
|
},
|
|
5696
5877
|
{
|
|
5878
|
+
// 2026-10-07 Linux: 아래 startup 의 `delete process.env` 는 Windows 에서는 OS env 에도 반영되지만, Linux Bun 에서는 OS
|
|
5879
|
+
// environ 까지 내려가지 않는다. pi-natives PtySession 은 OS environ 위에 넘긴 env map 을 덧씌우므로 pty service 에
|
|
5880
|
+
// 서버 변수가 되살아났다(실측: map·pipe 는 unset, pty 는 OS 값). 지운 이름을 기억해 POSIX PTY 실행에서 뺀다(아래 항목).
|
|
5697
5881
|
file: "src/launch/broker.ts",
|
|
5698
|
-
marker: "
|
|
5882
|
+
marker: "const brokerDroppedHostEnv = new Set<string>();",
|
|
5883
|
+
anchor: 'const LOG_FILE = "output.log";\n',
|
|
5884
|
+
patched: `/** HANSE: CUELO Next server env names this broker dropped from its own env at startup. */
|
|
5885
|
+
const brokerDroppedHostEnv = new Set<string>();
|
|
5886
|
+
const LOG_FILE = "output.log";
|
|
5887
|
+
`,
|
|
5888
|
+
},
|
|
5889
|
+
{
|
|
5890
|
+
file: "src/launch/broker.ts",
|
|
5891
|
+
marker: "brokerDroppedHostEnv.add(name);",
|
|
5699
5892
|
anchor: "\tdelete process.env[DAEMON_IDLE_GRACE_ENV];\n",
|
|
5700
5893
|
patched: ` delete process.env[DAEMON_IDLE_GRACE_ENV];
|
|
5701
5894
|
// HANSE: broker env drops inherited CUELO next server env
|
|
5895
|
+
for (const name of hostNextServerEnvUnsets(process.env, {})) {
|
|
5896
|
+
delete process.env[name];
|
|
5897
|
+
brokerDroppedHostEnv.add(name);
|
|
5898
|
+
}
|
|
5899
|
+
`,
|
|
5900
|
+
// 2026-10-07 이전 적용본(이름을 기억하지 않는다). 그 위에 다시 적용하면 이 본문으로 바뀐다.
|
|
5901
|
+
legacyPatched: ` delete process.env[DAEMON_IDLE_GRACE_ENV];
|
|
5902
|
+
// HANSE: broker env drops inherited CUELO next server env
|
|
5702
5903
|
for (const name of hostNextServerEnvUnsets(process.env, {})) delete process.env[name];
|
|
5703
5904
|
`,
|
|
5704
5905
|
},
|
|
5906
|
+
{
|
|
5907
|
+
// POSIX PTY service 는 사용자 셸(bash·zsh·fish)로 `exec <argv>` 를 띄운다. 셸과 무관하게 argv 앞에 `env -u NAME…` 를
|
|
5908
|
+
// 붙여 OS environ 에서 온 서버 변수를 뺀다(`exec env` 가 같은 pid 로 app 을 exec 한다). 호출자가 준 이름은 map 에 있으니
|
|
5909
|
+
// 남긴다. Windows(startArgv)·pipe 경로와 서버 변수가 없던 broker 는 그대로다.
|
|
5910
|
+
file: "src/launch/broker.ts",
|
|
5911
|
+
marker: "const dropped = [...brokerDroppedHostEnv]",
|
|
5912
|
+
anchor: ` const argv = [record.spec.application, ...record.spec.args];
|
|
5913
|
+
const command = \`exec \${argv.map(quoteShellArg).join(" ")}\`;`,
|
|
5914
|
+
patched: ` // HANSE: CUELO next server env stays out of POSIX PTY services. The native PTY layers its env
|
|
5915
|
+
// map over the OS environ, and the startup delete of process.env does not reach the OS environ
|
|
5916
|
+
// on Linux, so drop those names at exec. Names the caller set are in the map and stay.
|
|
5917
|
+
const dropped = [...brokerDroppedHostEnv].filter(name => !(name in options.env));
|
|
5918
|
+
const argv = [
|
|
5919
|
+
...(dropped.length > 0 ? ["env", ...dropped.flatMap(name => ["-u", name])] : []),
|
|
5920
|
+
record.spec.application,
|
|
5921
|
+
...record.spec.args,
|
|
5922
|
+
];
|
|
5923
|
+
const command = \`exec \${argv.map(quoteShellArg).join(" ")}\`;`,
|
|
5924
|
+
},
|
|
5705
5925
|
{
|
|
5706
5926
|
// 2026-09-29 jevgrep 비교(.omp/jevgrep-comparison/SUMMARY.md §4): jfind 의 비밀 파일 제외는 이름을
|
|
5707
5927
|
// 대소문자 구분으로 비교해, Windows(NTFS 대소문자 무시)에서 같은 파일인 `Credentials.json`·`ID_RSA`
|
|
@@ -7363,6 +7583,10 @@ class El {
|
|
|
7363
7583
|
// 모든 ref에 지문을 남기고, find()/ref()는 발급하는 요소에 지문을 남긴다.
|
|
7364
7584
|
file: "src/tools/computer/worker.ts",
|
|
7365
7585
|
marker: "return windowInputResult(action, this.id, options);",
|
|
7586
|
+
// 18.6.3 은 같은 클래스에 holdKeys·holdMouse·observe·menu·bringToCurrentSpace 를 넣고 move 에 pointerOptions() 를
|
|
7587
|
+
// 넘긴다. 결과가 같은 marker 를 가지므로 holdKeys 유무로 후보를 가른다. 새 메서드는 상류 그대로 두고, observe 만
|
|
7588
|
+
// ax() 와 같은 규칙(텍스트의 모든 ref 에 지문, goal probe 안에서는 출력 없음)을 따른다(ADAPT).
|
|
7589
|
+
excludes: "async holdKeys(",
|
|
7366
7590
|
anchor: ` async click(x: number, y: number, options?: ClickOptions): Promise<void> {
|
|
7367
7591
|
const context = this.#getContext();
|
|
7368
7592
|
guardRun(context, "click");
|
|
@@ -7403,18 +7627,230 @@ class El {
|
|
|
7403
7627
|
);
|
|
7404
7628
|
}
|
|
7405
7629
|
|
|
7406
|
-
async type(text: string, options?: InputOptions): Promise<void> {
|
|
7407
|
-
const context = this.#getContext();
|
|
7408
|
-
guardRun(context, "type");
|
|
7409
|
-
await nativeCall(context.signal, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
7630
|
+
async type(text: string, options?: InputOptions): Promise<void> {
|
|
7631
|
+
const context = this.#getContext();
|
|
7632
|
+
guardRun(context, "type");
|
|
7633
|
+
await nativeCall(context.signal, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
7634
|
+
}
|
|
7635
|
+
|
|
7636
|
+
async press(chord: string | string[], options?: InputOptions): Promise<void> {
|
|
7637
|
+
const context = this.#getContext();
|
|
7638
|
+
guardRun(context, "press");
|
|
7639
|
+
await nativeCall(context.signal, () =>
|
|
7640
|
+
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
7641
|
+
);
|
|
7642
|
+
}
|
|
7643
|
+
|
|
7644
|
+
async raise(): Promise<void> {
|
|
7645
|
+
const context = this.#getContext();
|
|
7646
|
+
guardRun(context, "raise");
|
|
7647
|
+
await nativeCall(context.signal, () => this.#session.raiseWindow(this.id));
|
|
7648
|
+
}
|
|
7649
|
+
|
|
7650
|
+
async ax(options?: AxOptions): Promise<string> {
|
|
7651
|
+
const { signal } = this.#getContext();
|
|
7652
|
+
return (await nativeCall(signal, () => this.#session.axSnapshot(this.id, options))).text;
|
|
7653
|
+
}
|
|
7654
|
+
|
|
7655
|
+
async find(query: AxQuery): Promise<El[]> {
|
|
7656
|
+
const { signal } = this.#getContext();
|
|
7657
|
+
return (await nativeCall(signal, () => this.#session.axQuery(this.id, query))).map(
|
|
7658
|
+
node => new El(this.#session, this.#getContext, node),
|
|
7659
|
+
);
|
|
7660
|
+
}
|
|
7661
|
+
|
|
7662
|
+
async ref(ref: string): Promise<El> {
|
|
7663
|
+
const { signal } = this.#getContext();
|
|
7664
|
+
return new El(this.#session, this.#getContext, await nativeCall(signal, () => this.#session.axNode(ref)));
|
|
7665
|
+
}`,
|
|
7666
|
+
patched: ` async #input(action: string, options: InputOptions | undefined, call: () => Promise<void>): Promise<ComputerActionResult> {
|
|
7667
|
+
const context = this.#getContext();
|
|
7668
|
+
guardRun(context, action);
|
|
7669
|
+
try {
|
|
7670
|
+
await nativeCall(context.signal, call);
|
|
7671
|
+
} catch (error) {
|
|
7672
|
+
throw refuseAction(error, action, undefined, options?.takeover);
|
|
7673
|
+
}
|
|
7674
|
+
return windowInputResult(action, this.id, options);
|
|
7675
|
+
}
|
|
7676
|
+
|
|
7677
|
+
click(x: number, y: number, options?: ClickOptions): Promise<ComputerActionResult> {
|
|
7678
|
+
return this.#input("click", options, () => this.#session.click(this.id, x, y, pointerOptions(options)));
|
|
7679
|
+
}
|
|
7680
|
+
|
|
7681
|
+
doubleClick(x: number, y: number, options?: Omit<ClickOptions, "count">): Promise<ComputerActionResult> {
|
|
7682
|
+
return this.#input("doubleClick", options, () =>
|
|
7683
|
+
this.#session.click(this.id, x, y, pointerOptions({ ...options, count: 2 })),
|
|
7684
|
+
);
|
|
7685
|
+
}
|
|
7686
|
+
|
|
7687
|
+
move(x: number, y: number): Promise<ComputerActionResult> {
|
|
7688
|
+
return this.#input("move", undefined, () => this.#session.moveMouse(this.id, x, y));
|
|
7689
|
+
}
|
|
7690
|
+
|
|
7691
|
+
drag(points: Array<[number, number]>, options?: DragOptions): Promise<ComputerActionResult> {
|
|
7692
|
+
return this.#input("drag", options, () =>
|
|
7693
|
+
this.#session.drag(
|
|
7694
|
+
this.id,
|
|
7695
|
+
points.map(([x, y]) => ({ x, y })),
|
|
7696
|
+
pointerOptions(options),
|
|
7697
|
+
),
|
|
7698
|
+
);
|
|
7699
|
+
}
|
|
7700
|
+
|
|
7701
|
+
scroll(x: number, y: number, options: ScrollOptions = {}): Promise<ComputerActionResult> {
|
|
7702
|
+
return this.#input("scroll", options, () =>
|
|
7703
|
+
this.#session.scroll(this.id, x, y, options.dx ?? 0, options.dy ?? 0, pointerOptions(options)),
|
|
7704
|
+
);
|
|
7705
|
+
}
|
|
7706
|
+
|
|
7707
|
+
type(text: string, options?: InputOptions): Promise<ComputerActionResult> {
|
|
7708
|
+
return this.#input("type", options, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
7709
|
+
}
|
|
7710
|
+
|
|
7711
|
+
press(chord: string | string[], options?: InputOptions): Promise<ComputerActionResult> {
|
|
7712
|
+
return this.#input("press", options, () =>
|
|
7713
|
+
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
7714
|
+
);
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
raise(): Promise<ComputerActionResult> {
|
|
7718
|
+
return this.#input("raise", undefined, () => this.#session.raiseWindow(this.id));
|
|
7719
|
+
}
|
|
7720
|
+
|
|
7721
|
+
async ax(options?: AxOptions): Promise<string> {
|
|
7722
|
+
const { signal } = this.#getContext();
|
|
7723
|
+
const text = (await nativeCall(signal, () => this.#session.axSnapshot(this.id, options))).text;
|
|
7724
|
+
refBook(this.#session).noteSnapshot(this.id, text);
|
|
7725
|
+
return text;
|
|
7726
|
+
}
|
|
7727
|
+
|
|
7728
|
+
async find(query: AxQuery): Promise<El[]> {
|
|
7729
|
+
const { signal } = this.#getContext();
|
|
7730
|
+
const nodes = await nativeCall(signal, () => this.#session.axQuery(this.id, query));
|
|
7731
|
+
return issueElements(this.#session, this.#getContext, nodes, this.id);
|
|
7732
|
+
}
|
|
7733
|
+
|
|
7734
|
+
ref(ref: string): Promise<El> {
|
|
7735
|
+
return resolveElement(this.#session, this.#getContext, ref, this.id);
|
|
7736
|
+
}`,
|
|
7737
|
+
alternates: [{
|
|
7738
|
+
file: "src/tools/computer/worker.ts",
|
|
7739
|
+
requires: "async holdKeys(",
|
|
7740
|
+
marker: "return windowInputResult(action, this.id, options);",
|
|
7741
|
+
anchor: ` async click(x: number, y: number, options?: ClickOptions): Promise<void> {
|
|
7742
|
+
const context = this.#getContext();
|
|
7743
|
+
guardRun(context, "click");
|
|
7744
|
+
await nativeCall(context.signal, () => this.#session.click(this.id, x, y, pointerOptions(options)));
|
|
7745
|
+
}
|
|
7746
|
+
|
|
7747
|
+
async doubleClick(x: number, y: number, options?: Omit<ClickOptions, "count">): Promise<void> {
|
|
7748
|
+
const context = this.#getContext();
|
|
7749
|
+
guardRun(context, "doubleClick");
|
|
7750
|
+
await nativeCall(context.signal, () =>
|
|
7751
|
+
this.#session.click(this.id, x, y, pointerOptions({ ...options, count: 2 })),
|
|
7752
|
+
);
|
|
7753
|
+
}
|
|
7754
|
+
|
|
7755
|
+
async move(x: number, y: number): Promise<void> {
|
|
7756
|
+
const context = this.#getContext();
|
|
7757
|
+
guardRun(context, "move");
|
|
7758
|
+
await nativeCall(context.signal, () => this.#session.moveMouse(this.id, x, y, pointerOptions()));
|
|
7759
|
+
}
|
|
7760
|
+
|
|
7761
|
+
async drag(points: Array<[number, number]>, options?: DragOptions): Promise<void> {
|
|
7762
|
+
const context = this.#getContext();
|
|
7763
|
+
guardRun(context, "drag");
|
|
7764
|
+
await nativeCall(context.signal, () =>
|
|
7765
|
+
this.#session.drag(
|
|
7766
|
+
this.id,
|
|
7767
|
+
points.map(([x, y]) => ({ x, y })),
|
|
7768
|
+
pointerOptions(options),
|
|
7769
|
+
),
|
|
7770
|
+
);
|
|
7771
|
+
}
|
|
7772
|
+
|
|
7773
|
+
async scroll(x: number, y: number, options: ScrollOptions = {}): Promise<void> {
|
|
7774
|
+
const context = this.#getContext();
|
|
7775
|
+
guardRun(context, "scroll");
|
|
7776
|
+
await nativeCall(context.signal, () =>
|
|
7777
|
+
this.#session.scroll(this.id, x, y, options.dx ?? 0, options.dy ?? 0, pointerOptions(options)),
|
|
7778
|
+
);
|
|
7779
|
+
}
|
|
7780
|
+
|
|
7781
|
+
async type(text: string, options?: InputOptions): Promise<void> {
|
|
7782
|
+
const context = this.#getContext();
|
|
7783
|
+
guardRun(context, "type");
|
|
7784
|
+
await nativeCall(context.signal, () => this.#session.typeText(this.id, text, pointerOptions(options)));
|
|
7785
|
+
}
|
|
7786
|
+
|
|
7787
|
+
async press(chord: string | string[], options?: InputOptions): Promise<void> {
|
|
7788
|
+
const context = this.#getContext();
|
|
7789
|
+
guardRun(context, "press");
|
|
7790
|
+
await nativeCall(context.signal, () =>
|
|
7791
|
+
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
7792
|
+
);
|
|
7793
|
+
}
|
|
7794
|
+
|
|
7795
|
+
async holdKeys(keys: string[], options: HoldOptions): Promise<void> {
|
|
7796
|
+
const context = this.#getContext();
|
|
7797
|
+
guardRun(context, "holdKeys");
|
|
7798
|
+
validateHold(options);
|
|
7799
|
+
validateKeys(keys, "keys");
|
|
7800
|
+
await nativeCall(context.signal, () => this.#session.holdKeys(this.id, keys, options));
|
|
7801
|
+
}
|
|
7802
|
+
|
|
7803
|
+
async holdMouse(x: number, y: number, options: HoldMouseOptions): Promise<void> {
|
|
7804
|
+
const context = this.#getContext();
|
|
7805
|
+
guardRun(context, "holdMouse");
|
|
7806
|
+
validateHold(options);
|
|
7807
|
+
if (options.keys !== undefined) validateKeys(options.keys, "keys");
|
|
7808
|
+
await nativeCall(context.signal, () => this.#session.holdMouse(this.id, x, y, options));
|
|
7809
|
+
}
|
|
7810
|
+
|
|
7811
|
+
async observe(options?: ScreenshotOptions & AxOptions): Promise<ObservationResult> {
|
|
7812
|
+
const context = this.#getContext();
|
|
7813
|
+
const result = await nativeCall(context.signal, () =>
|
|
7814
|
+
this.#session.observe(
|
|
7815
|
+
this.id,
|
|
7816
|
+
{
|
|
7817
|
+
maxWidth: context.snapshot.captureMaxWidth,
|
|
7818
|
+
maxHeight: context.snapshot.captureMaxHeight,
|
|
7819
|
+
},
|
|
7820
|
+
options && { all: options.all, maxDepth: options.maxDepth },
|
|
7821
|
+
),
|
|
7822
|
+
);
|
|
7823
|
+
const screenshot = await emitScreenshot(context, result.capture, options);
|
|
7824
|
+
if (!options?.silent) context.output.push({ type: "text", text: result.accessibility.text });
|
|
7825
|
+
return {
|
|
7826
|
+
...screenshot,
|
|
7827
|
+
ax: result.accessibility.text,
|
|
7828
|
+
nodeCount: result.accessibility.nodeCount,
|
|
7829
|
+
truncated: result.accessibility.truncated,
|
|
7830
|
+
};
|
|
7831
|
+
}
|
|
7832
|
+
|
|
7833
|
+
get menu() {
|
|
7834
|
+
return {
|
|
7835
|
+
items: async (path?: string | string[]): Promise<MenuItem[]> => {
|
|
7836
|
+
const context = this.#getContext();
|
|
7837
|
+
const segments = path === undefined ? undefined : typeof path === "string" ? [path] : path;
|
|
7838
|
+
if (segments !== undefined) validateKeys(segments, "menu path", { allowEmpty: true });
|
|
7839
|
+
return await nativeCall(context.signal, () => this.#session.menuItems(this.id, segments));
|
|
7840
|
+
},
|
|
7841
|
+
select: async (path: string[]): Promise<void> => {
|
|
7842
|
+
const context = this.#getContext();
|
|
7843
|
+
guardRun(context, "menu.select");
|
|
7844
|
+
validateKeys(path, "menu path");
|
|
7845
|
+
await nativeCall(context.signal, () => this.#session.menuSelect(this.id, path));
|
|
7846
|
+
},
|
|
7847
|
+
};
|
|
7410
7848
|
}
|
|
7411
7849
|
|
|
7412
|
-
async
|
|
7850
|
+
async bringToCurrentSpace(): Promise<void> {
|
|
7413
7851
|
const context = this.#getContext();
|
|
7414
|
-
guardRun(context, "
|
|
7415
|
-
await nativeCall(context.signal, () =>
|
|
7416
|
-
this.#session.keyChord(this.id, chordKeys(chord), pointerOptions(options)),
|
|
7417
|
-
);
|
|
7852
|
+
guardRun(context, "bringToCurrentSpace");
|
|
7853
|
+
await nativeCall(context.signal, () => this.#session.bringToCurrentSpace(this.id));
|
|
7418
7854
|
}
|
|
7419
7855
|
|
|
7420
7856
|
async raise(): Promise<void> {
|
|
@@ -7439,7 +7875,7 @@ class El {
|
|
|
7439
7875
|
const { signal } = this.#getContext();
|
|
7440
7876
|
return new El(this.#session, this.#getContext, await nativeCall(signal, () => this.#session.axNode(ref)));
|
|
7441
7877
|
}`,
|
|
7442
|
-
|
|
7878
|
+
patched: ` async #input(action: string, options: InputOptions | undefined, call: () => Promise<void>): Promise<ComputerActionResult> {
|
|
7443
7879
|
const context = this.#getContext();
|
|
7444
7880
|
guardRun(context, action);
|
|
7445
7881
|
try {
|
|
@@ -7461,7 +7897,7 @@ class El {
|
|
|
7461
7897
|
}
|
|
7462
7898
|
|
|
7463
7899
|
move(x: number, y: number): Promise<ComputerActionResult> {
|
|
7464
|
-
return this.#input("move", undefined, () => this.#session.moveMouse(this.id, x, y));
|
|
7900
|
+
return this.#input("move", undefined, () => this.#session.moveMouse(this.id, x, y, pointerOptions()));
|
|
7465
7901
|
}
|
|
7466
7902
|
|
|
7467
7903
|
drag(points: Array<[number, number]>, options?: DragOptions): Promise<ComputerActionResult> {
|
|
@@ -7490,6 +7926,69 @@ class El {
|
|
|
7490
7926
|
);
|
|
7491
7927
|
}
|
|
7492
7928
|
|
|
7929
|
+
async holdKeys(keys: string[], options: HoldOptions): Promise<void> {
|
|
7930
|
+
const context = this.#getContext();
|
|
7931
|
+
guardRun(context, "holdKeys");
|
|
7932
|
+
validateHold(options);
|
|
7933
|
+
validateKeys(keys, "keys");
|
|
7934
|
+
await nativeCall(context.signal, () => this.#session.holdKeys(this.id, keys, options));
|
|
7935
|
+
}
|
|
7936
|
+
|
|
7937
|
+
async holdMouse(x: number, y: number, options: HoldMouseOptions): Promise<void> {
|
|
7938
|
+
const context = this.#getContext();
|
|
7939
|
+
guardRun(context, "holdMouse");
|
|
7940
|
+
validateHold(options);
|
|
7941
|
+
if (options.keys !== undefined) validateKeys(options.keys, "keys");
|
|
7942
|
+
await nativeCall(context.signal, () => this.#session.holdMouse(this.id, x, y, options));
|
|
7943
|
+
}
|
|
7944
|
+
|
|
7945
|
+
async observe(options?: ScreenshotOptions & AxOptions): Promise<ObservationResult> {
|
|
7946
|
+
const context = this.#getContext();
|
|
7947
|
+
const result = await nativeCall(context.signal, () =>
|
|
7948
|
+
this.#session.observe(
|
|
7949
|
+
this.id,
|
|
7950
|
+
{
|
|
7951
|
+
maxWidth: context.snapshot.captureMaxWidth,
|
|
7952
|
+
maxHeight: context.snapshot.captureMaxHeight,
|
|
7953
|
+
},
|
|
7954
|
+
options && { all: options.all, maxDepth: options.maxDepth },
|
|
7955
|
+
),
|
|
7956
|
+
);
|
|
7957
|
+
const screenshot = await emitScreenshot(context, result.capture, options);
|
|
7958
|
+
// Same tree text as ax(): every ref it shows gets a fingerprint, and a goal probe stays silent.
|
|
7959
|
+
refBook(this.#session).noteSnapshot(this.id, result.accessibility.text);
|
|
7960
|
+
if (!options?.silent && !context.goalProbe) context.output.push({ type: "text", text: result.accessibility.text });
|
|
7961
|
+
return {
|
|
7962
|
+
...screenshot,
|
|
7963
|
+
ax: result.accessibility.text,
|
|
7964
|
+
nodeCount: result.accessibility.nodeCount,
|
|
7965
|
+
truncated: result.accessibility.truncated,
|
|
7966
|
+
};
|
|
7967
|
+
}
|
|
7968
|
+
|
|
7969
|
+
get menu() {
|
|
7970
|
+
return {
|
|
7971
|
+
items: async (path?: string | string[]): Promise<MenuItem[]> => {
|
|
7972
|
+
const context = this.#getContext();
|
|
7973
|
+
const segments = path === undefined ? undefined : typeof path === "string" ? [path] : path;
|
|
7974
|
+
if (segments !== undefined) validateKeys(segments, "menu path", { allowEmpty: true });
|
|
7975
|
+
return await nativeCall(context.signal, () => this.#session.menuItems(this.id, segments));
|
|
7976
|
+
},
|
|
7977
|
+
select: async (path: string[]): Promise<void> => {
|
|
7978
|
+
const context = this.#getContext();
|
|
7979
|
+
guardRun(context, "menu.select");
|
|
7980
|
+
validateKeys(path, "menu path");
|
|
7981
|
+
await nativeCall(context.signal, () => this.#session.menuSelect(this.id, path));
|
|
7982
|
+
},
|
|
7983
|
+
};
|
|
7984
|
+
}
|
|
7985
|
+
|
|
7986
|
+
async bringToCurrentSpace(): Promise<void> {
|
|
7987
|
+
const context = this.#getContext();
|
|
7988
|
+
guardRun(context, "bringToCurrentSpace");
|
|
7989
|
+
await nativeCall(context.signal, () => this.#session.bringToCurrentSpace(this.id));
|
|
7990
|
+
}
|
|
7991
|
+
|
|
7493
7992
|
raise(): Promise<ComputerActionResult> {
|
|
7494
7993
|
return this.#input("raise", undefined, () => this.#session.raiseWindow(this.id));
|
|
7495
7994
|
}
|
|
@@ -7510,6 +8009,7 @@ class El {
|
|
|
7510
8009
|
ref(ref: string): Promise<El> {
|
|
7511
8010
|
return resolveElement(this.#session, this.#getContext, ref, this.id);
|
|
7512
8011
|
}`,
|
|
8012
|
+
}],
|
|
7513
8013
|
},
|
|
7514
8014
|
{
|
|
7515
8015
|
// Python·JS 직접 헬퍼는 win.ref(ref)도 매번 desktop.ref(ref)로 푼다. 재획득은 이 경로에서도 일어나야 한다.
|
|
@@ -7525,6 +8025,9 @@ class El {
|
|
|
7525
8025
|
// 모델이 보는 타입: 행동 결과·거절 필드와 재획득 규칙을 적는다.
|
|
7526
8026
|
file: "src/tools/computer/declarations.d.ts",
|
|
7527
8027
|
marker: "interface ComputerActionResult {",
|
|
8028
|
+
// 18.6.3 은 ComputerInputTarget 에 zoom·holdKeys·holdMouse 를 넣고 좌표 설명을 "full screenshot" 기준으로 바꿨다.
|
|
8029
|
+
// 새 멤버와 설명은 상류 그대로 두고, 우리 결과 타입이 걸린 멤버만 바꾼다. holdKeys 유무로 후보를 가른다(ADAPT).
|
|
8030
|
+
excludes: "holdKeys(keys: string[]",
|
|
7528
8031
|
anchor: `/** Live accessibility element resolved from a snapshot ref; expired refs throw \`StaleRef\`. */
|
|
7529
8032
|
interface ComputerElement {
|
|
7530
8033
|
/** Snapshot ref tag, e.g. \`e5\`. */
|
|
@@ -7634,6 +8137,122 @@ interface ComputerInputTarget {
|
|
|
7634
8137
|
/** Key chord such as \`"cmd+shift+p"\` or \`["cmd", "shift", "p"]\`. */
|
|
7635
8138
|
press(chord: string | string[], options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
7636
8139
|
}`,
|
|
8140
|
+
alternates: [{
|
|
8141
|
+
file: "src/tools/computer/declarations.d.ts",
|
|
8142
|
+
requires: "holdKeys(keys: string[]",
|
|
8143
|
+
marker: "interface ComputerActionResult {",
|
|
8144
|
+
anchor: `/** Live accessibility element resolved from a snapshot ref; expired refs throw \`StaleRef\`. */
|
|
8145
|
+
interface ComputerElement {
|
|
8146
|
+
/** Snapshot ref tag, e.g. \`e5\`. */
|
|
8147
|
+
readonly ref: string;
|
|
8148
|
+
readonly role: string;
|
|
8149
|
+
readonly nativeRole: string;
|
|
8150
|
+
readonly title?: string;
|
|
8151
|
+
readonly description?: string;
|
|
8152
|
+
readonly enabled: boolean;
|
|
8153
|
+
readonly focused: boolean;
|
|
8154
|
+
readonly childCount: number;
|
|
8155
|
+
value(): Promise<string | undefined>;
|
|
8156
|
+
setValue(value: string): Promise<void>;
|
|
8157
|
+
/** Bounds in global desktop coordinates, or null when the element has none. */
|
|
8158
|
+
bounds(): Promise<ComputerBounds | null>;
|
|
8159
|
+
attributes(): Promise<Record<string, string>>;
|
|
8160
|
+
actions(): Promise<string[]>;
|
|
8161
|
+
perform(action: string): Promise<void>;
|
|
8162
|
+
/** Perform the element's native press action; needs no screenshot. */
|
|
8163
|
+
press(): Promise<void>;
|
|
8164
|
+
/** Click the element's center with native input. */
|
|
8165
|
+
click(options?: ComputerInputOptions): Promise<void>;
|
|
8166
|
+
focus(): Promise<void>;
|
|
8167
|
+
parent(): Promise<ComputerElement | null>;
|
|
8168
|
+
children(): Promise<ComputerElement[]>;
|
|
8169
|
+
}
|
|
8170
|
+
|
|
8171
|
+
/** Native input helpers shared by the desktop root and window handles; \`x\`/\`y\` are pixels in the most recent full screenshot of the same target, never zoom pixels. */
|
|
8172
|
+
interface ComputerInputTarget {
|
|
8173
|
+
screenshot(options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
8174
|
+
/** Display a detailed region without replacing the input frame. Use base full screenshot coordinates for subsequent input, not zoom pixels. */
|
|
8175
|
+
zoom(region: CaptureRegion, options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
8176
|
+
click(x: number, y: number, options?: ComputerClickOptions): Promise<void>;
|
|
8177
|
+
doubleClick(x: number, y: number, options?: Omit<ComputerClickOptions, "count">): Promise<void>;
|
|
8178
|
+
move(x: number, y: number): Promise<void>;
|
|
8179
|
+
drag(points: Array<[number, number]>, options?: ComputerDragOptions): Promise<void>;
|
|
8180
|
+
scroll(x: number, y: number, options?: ComputerScrollOptions): Promise<void>;
|
|
8181
|
+
type(text: string, options?: ComputerInputOptions): Promise<void>;
|
|
8182
|
+
/** Key chord such as \`"cmd+shift+p"\` or \`["cmd", "shift", "p"]\`. */
|
|
8183
|
+
press(chord: string | string[], options?: ComputerInputOptions): Promise<void>;`,
|
|
8184
|
+
patched: `/** Result of an input or element action. Delivery is not proof of effect. */
|
|
8185
|
+
interface ComputerActionResult {
|
|
8186
|
+
action: string;
|
|
8187
|
+
/** \`verified\`: an AX readback shows the change. \`unverified\`: delivered, effect not observed. \`suspected_noop\`: the readback contradicts the intended change. */
|
|
8188
|
+
status: "verified" | "unverified" | "suspected_noop";
|
|
8189
|
+
/** \`reobserve\`: screenshot or AX before relying on the effect. \`escalate\`: follow \`escalation\`. */
|
|
8190
|
+
suggestedNext: "continue" | "reobserve" | "escalate";
|
|
8191
|
+
escalation?: { target: "pixel" | "takeover"; reason: string };
|
|
8192
|
+
/** Readback the status rests on. */
|
|
8193
|
+
evidence?: string;
|
|
8194
|
+
ref?: string;
|
|
8195
|
+
/** Present when the action ran on an element reacquired from a stale ref. */
|
|
8196
|
+
reacquired?: { from: string; to: string; matchedBy: string };
|
|
8197
|
+
}
|
|
8198
|
+
|
|
8199
|
+
/** Attached to a failed action's error as \`error.computerAction\` (visible inside \`computer.run\`); the error message is unchanged. */
|
|
8200
|
+
interface ComputerActionRefusal {
|
|
8201
|
+
action: string;
|
|
8202
|
+
status: "refused";
|
|
8203
|
+
suggestedNext: "reobserve" | "escalate";
|
|
8204
|
+
escalation?: { target: "takeover"; reason: string };
|
|
8205
|
+
ref?: string;
|
|
8206
|
+
/** Why a stale ref was not reacquired, with up to five candidates. */
|
|
8207
|
+
reacquire?: {
|
|
8208
|
+
outcome: "no-fingerprint" | "no-window" | "no-candidate" | "parent-mismatch" | "ambiguous";
|
|
8209
|
+
candidates: Array<{ ref: string; role: string; title?: string; bounds?: ComputerBounds }>;
|
|
8210
|
+
};
|
|
8211
|
+
}
|
|
8212
|
+
|
|
8213
|
+
/** 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\`. */
|
|
8214
|
+
interface ComputerElement {
|
|
8215
|
+
/** Snapshot ref tag, e.g. \`e5\`; the new ref after a reacquisition. */
|
|
8216
|
+
readonly ref: string;
|
|
8217
|
+
/** Stale ref this element was reacquired from. */
|
|
8218
|
+
readonly reacquiredFrom?: string;
|
|
8219
|
+
readonly role: string;
|
|
8220
|
+
readonly nativeRole: string;
|
|
8221
|
+
readonly title?: string;
|
|
8222
|
+
readonly description?: string;
|
|
8223
|
+
readonly enabled: boolean;
|
|
8224
|
+
readonly focused: boolean;
|
|
8225
|
+
readonly childCount: number;
|
|
8226
|
+
value(): Promise<string | undefined>;
|
|
8227
|
+
setValue(value: string): Promise<ComputerActionResult>;
|
|
8228
|
+
/** Bounds in global desktop coordinates, or null when the element has none. */
|
|
8229
|
+
bounds(): Promise<ComputerBounds | null>;
|
|
8230
|
+
attributes(): Promise<Record<string, string>>;
|
|
8231
|
+
actions(): Promise<string[]>;
|
|
8232
|
+
perform(action: string): Promise<ComputerActionResult>;
|
|
8233
|
+
/** Perform the element's native press action; needs no screenshot. */
|
|
8234
|
+
press(): Promise<ComputerActionResult>;
|
|
8235
|
+
/** Click the element's center with native input. */
|
|
8236
|
+
click(options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
8237
|
+
focus(): Promise<ComputerActionResult>;
|
|
8238
|
+
parent(): Promise<ComputerElement | null>;
|
|
8239
|
+
children(): Promise<ComputerElement[]>;
|
|
8240
|
+
}
|
|
8241
|
+
|
|
8242
|
+
/** Native input helpers shared by the desktop root and window handles; \`x\`/\`y\` are pixels in the most recent full screenshot of the same target, never zoom pixels. Pointer and keyboard action results are always \`unverified\`. */
|
|
8243
|
+
interface ComputerInputTarget {
|
|
8244
|
+
screenshot(options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
8245
|
+
/** Display a detailed region without replacing the input frame. Use base full screenshot coordinates for subsequent input, not zoom pixels. */
|
|
8246
|
+
zoom(region: CaptureRegion, options?: ComputerScreenshotOptions): Promise<ComputerScreenshotResult>;
|
|
8247
|
+
click(x: number, y: number, options?: ComputerClickOptions): Promise<ComputerActionResult>;
|
|
8248
|
+
doubleClick(x: number, y: number, options?: Omit<ComputerClickOptions, "count">): Promise<ComputerActionResult>;
|
|
8249
|
+
move(x: number, y: number): Promise<ComputerActionResult>;
|
|
8250
|
+
drag(points: Array<[number, number]>, options?: ComputerDragOptions): Promise<ComputerActionResult>;
|
|
8251
|
+
scroll(x: number, y: number, options?: ComputerScrollOptions): Promise<ComputerActionResult>;
|
|
8252
|
+
type(text: string, options?: ComputerInputOptions): Promise<ComputerActionResult>;
|
|
8253
|
+
/** Key chord such as \`"cmd+shift+p"\` or \`["cmd", "shift", "p"]\`. */
|
|
8254
|
+
press(chord: string | string[], options?: ComputerInputOptions): Promise<ComputerActionResult>;`,
|
|
8255
|
+
}],
|
|
7637
8256
|
},
|
|
7638
8257
|
{
|
|
7639
8258
|
file: "src/tools/computer/declarations.d.ts",
|
|
@@ -7647,6 +8266,15 @@ interface ComputerInputTarget {
|
|
|
7647
8266
|
marker: "Actions return `{ action, status, suggestedNext",
|
|
7648
8267
|
anchor: "- Each window `.ax()` starts a ref generation. Current/previous snapshot refs remain valid; older refs throw `StaleRef`. Re-snapshot; NEVER guess.\n",
|
|
7649
8268
|
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",
|
|
8269
|
+
// 18.6.3(#14485)은 ref 가 role·label 이 바뀔 때까지 유지되고 마지막 두 `.ax()` 에 없는 요소의 ref 만 StaleRef 라고
|
|
8270
|
+
// 고쳐 적었다. 그 규칙은 상류 그대로 두고, StaleRef 가 날 때의 지문 재획득과 결과 필드 읽는 법을 잇는다. 결과를
|
|
8271
|
+
// 돌려주는 helper 는 상류가 새로 넣은 hold·menu 와 구별해 이름으로 적는다(ADAPT).
|
|
8272
|
+
alternates: [{
|
|
8273
|
+
file: "src/prompts/tools/computer.md",
|
|
8274
|
+
marker: "snapshots is reacquired only when its saved fingerprint",
|
|
8275
|
+
anchor: "- An element keeps its `[ref=eN]` across `.ax()`/`find()` reads until its role or label changes; a ref whose element is missing from the window's last two `.ax()` snapshots throws `StaleRef`. Re-snapshot; NEVER guess.\n",
|
|
8276
|
+
patched: "- An element keeps its `[ref=eN]` across `.ax()`/`find()` reads until its role or label changes. A ref whose element is missing from the window's last two `.ax()` snapshots 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- Element actions and window/display `click`, `doubleClick`, `move`, `drag`, `scroll`, `type`, `press`, `raise` 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",
|
|
8277
|
+
}],
|
|
7650
8278
|
},
|
|
7651
8279
|
{
|
|
7652
8280
|
// 2026-09-30 computer 목표 대기(사용자 요청 0.6.6): 행동은 사용자 코드에서 한 번, 목표는 wait(predicate)가 로컬 읽기로만 확인한다.
|
|
@@ -7661,6 +8289,9 @@ import * as postmortem from "@oh-my-pi/pi-utils/postmortem";`,
|
|
|
7661
8289
|
{
|
|
7662
8290
|
file: "src/tools/computer/worker.ts",
|
|
7663
8291
|
marker: " goalProbe?: boolean;",
|
|
8292
|
+
// 18.6.3 은 run context 에 confirmControl(전경 제어 승인 요청)을 넣었다. probe 는 context 를 펼쳐 복제하므로 그 필드도
|
|
8293
|
+
// 따라가고, control.acquire 는 guardRun 에서 probe 거절된다. 두 판 결과가 같은 marker 를 가지므로 그 필드로 가른다(ADAPT).
|
|
8294
|
+
excludes: "confirmControl(reason: string): Promise<boolean>;",
|
|
7664
8295
|
anchor: `interface ComputerRunContext {
|
|
7665
8296
|
signal: AbortSignal;
|
|
7666
8297
|
readOnly: boolean;
|
|
@@ -7687,6 +8318,39 @@ function goalProbeRefusal(method: string): ToolError {
|
|
|
7687
8318
|
\`wait(predicate) probe cannot run '\${method}': probes only read desktop state (windows, ax, find, value); act once before wait()\`,
|
|
7688
8319
|
);
|
|
7689
8320
|
}`,
|
|
8321
|
+
alternates: [{
|
|
8322
|
+
file: "src/tools/computer/worker.ts",
|
|
8323
|
+
requires: "confirmControl(reason: string): Promise<boolean>;",
|
|
8324
|
+
marker: " goalProbe?: boolean;",
|
|
8325
|
+
anchor: `interface ComputerRunContext {
|
|
8326
|
+
signal: AbortSignal;
|
|
8327
|
+
readOnly: boolean;
|
|
8328
|
+
snapshot: ComputerSessionSnapshot;
|
|
8329
|
+
output: RunOutput;
|
|
8330
|
+
confirmControl(reason: string): Promise<boolean>;
|
|
8331
|
+
screenshots: ComputerScreenshot[];
|
|
8332
|
+
}`,
|
|
8333
|
+
patched: `interface ComputerRunContext {
|
|
8334
|
+
signal: AbortSignal;
|
|
8335
|
+
readOnly: boolean;
|
|
8336
|
+
/** Set while a \`wait(predicate)\` probe runs; \`signal\` then also ends at the wait's deadline and when it settles. */
|
|
8337
|
+
goalProbe?: boolean;
|
|
8338
|
+
snapshot: ComputerSessionSnapshot;
|
|
8339
|
+
output: RunOutput;
|
|
8340
|
+
confirmControl(reason: string): Promise<boolean>;
|
|
8341
|
+
screenshots: ComputerScreenshot[];
|
|
8342
|
+
}
|
|
8343
|
+
|
|
8344
|
+
/** Pauses between goal probes when \`interval\` is not given: Playwright's \`pollAgainstDeadline\` default; the last one repeats. */
|
|
8345
|
+
const GOAL_POLL_INTERVALS = [100, 250, 500, 1000] as const;
|
|
8346
|
+
|
|
8347
|
+
/** A goal probe only reads: desktop input, clipboard writes, and tool-bridge calls would repeat on every probe. */
|
|
8348
|
+
function goalProbeRefusal(method: string): ToolError {
|
|
8349
|
+
return new ToolError(
|
|
8350
|
+
\`wait(predicate) probe cannot run '\${method}': probes only read desktop state (windows, ax, find, value); act once before wait()\`,
|
|
8351
|
+
);
|
|
8352
|
+
}`,
|
|
8353
|
+
}],
|
|
7690
8354
|
},
|
|
7691
8355
|
{
|
|
7692
8356
|
file: "src/tools/computer/worker.ts",
|
|
@@ -7826,8 +8490,18 @@ function goalProbeRefusal(method: string): ToolError {
|
|
|
7826
8490
|
// 모델 지시: 행동은 한 번, 목표는 wait(predicate)로 읽고, 미달이면 다시 누르지 않는다.
|
|
7827
8491
|
file: "src/prompts/tools/computer.md",
|
|
7828
8492
|
marker: "- Goal after an action: act ONCE",
|
|
8493
|
+
// 18.6.3 은 같은 줄 끝에 "Group predictable actions …" 문장을 붙였다. 그 문장 뒤에 같은 지시를 잇는다. 두 판 결과가
|
|
8494
|
+
// 같은 marker 를 가지므로 그 문장 유무로 후보를 가른다(ADAPT).
|
|
8495
|
+
excludes: "Group predictable actions and their verification in one run",
|
|
7829
8496
|
anchor: "Plain data, functions, and `RegExp` values are supported in `args`.\n",
|
|
7830
8497
|
patched: "Plain data, functions, and `RegExp` values are supported in `args`.\n- Goal after an action: act ONCE, then `await wait(async () => …read…, { timeout })` for the row, text, or value the user asked for; it resolves with that value. Inside the predicate, desktop input, `clipboard.write`, and tool calls throw and screenshots stay silent; probes back off 100→1000 ms, and a late read cannot continue past the deadline. The predicate is still unsandboxed Bun/Node code. On `wait(predicate) timed out` NEVER re-click, take over, or switch windows: reobserve and report. A button state change alone does not prove a save.\n",
|
|
8498
|
+
alternates: [{
|
|
8499
|
+
file: "src/prompts/tools/computer.md",
|
|
8500
|
+
requires: "Group predictable actions and their verification in one run",
|
|
8501
|
+
marker: "- Goal after an action: act ONCE",
|
|
8502
|
+
anchor: "Plain data, functions, and `RegExp` values are supported in `args`. Group predictable actions and their verification in one run; stop and inspect when the outcome is uncertain.\n",
|
|
8503
|
+
patched: "Plain data, functions, and `RegExp` values are supported in `args`. Group predictable actions and their verification in one run; stop and inspect when the outcome is uncertain.\n- Goal after an action: act ONCE, then `await wait(async () => …read…, { timeout })` for the row, text, or value the user asked for; it resolves with that value. Inside the predicate, desktop input, `clipboard.write`, and tool calls throw and screenshots stay silent; probes back off 100→1000 ms, and a late read cannot continue past the deadline. The predicate is still unsandboxed Bun/Node code. On `wait(predicate) timed out` NEVER re-click, take over, or switch windows: reobserve and report. A button state change alone does not prove a save.\n",
|
|
8504
|
+
}],
|
|
7831
8505
|
},
|
|
7832
8506
|
{
|
|
7833
8507
|
file: "src/prompts/tools/computer.md",
|
|
@@ -7835,6 +8509,287 @@ function goalProbeRefusal(method: string): ToolError {
|
|
|
7835
8509
|
anchor: "\treturn await target.ax();\n}, { timeout: 30 });\n",
|
|
7836
8510
|
patched: "\treturn await target.ax();\n}, { timeout: 30 });\nawait computer.run(async ({ desktop, wait }) => {\n\tconst orders = await desktop.window({ title: \"Orders\" });\n\tconst [save] = await orders.find({ role: \"button\", title: \"Save\" });\n\tawait save.press();\n\treturn await wait(async () => (await orders.find({ title: \"Saved row 1\" }))[0]?.title, { timeout: 5000 });\n}, { timeout: 30 });\n",
|
|
7837
8511
|
},
|
|
8512
|
+
{
|
|
8513
|
+
// 2026-10-06 WSL 단일 CUELO의 Windows 화면 조작. upstream computer worker는 같은 프로세스의 Bun Worker라
|
|
8514
|
+
// Linux CUELO는 linux-x64 addon(WSLg)만 본다. Windows helper exe(Tools/CUELO_Setup/wsl/windows-computer)를
|
|
8515
|
+
// interop 자식으로 띄우고 stdin/stdout으로 아래 frame을 주고받는다. 포트·리스너 없음.
|
|
8516
|
+
file: "src/tools/computer/protocol.ts",
|
|
8517
|
+
marker: `import { deserialize, serialize } from "bun:jsc";`,
|
|
8518
|
+
excludes: "CaptureRegion, DesktopCapabilities",
|
|
8519
|
+
anchor: `import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
8520
|
+
import type { DesktopCapabilities } from "@oh-my-pi/pi-natives";`,
|
|
8521
|
+
patched: `import { deserialize, serialize } from "bun:jsc";
|
|
8522
|
+
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
8523
|
+
import type { DesktopCapabilities } from "@oh-my-pi/pi-natives";`,
|
|
8524
|
+
// 18.6.3 은 같은 import 에 CaptureRegion 을 더했다(zoom). 프레임 직렬화 import 는 그대로 앞에 둔다. 두 판 결과가 같은
|
|
8525
|
+
// marker 를 가지므로 그 import 이름으로 후보를 가른다(ADAPT).
|
|
8526
|
+
alternates: [{
|
|
8527
|
+
file: "src/tools/computer/protocol.ts",
|
|
8528
|
+
requires: "CaptureRegion, DesktopCapabilities",
|
|
8529
|
+
marker: `import { deserialize, serialize } from "bun:jsc";`,
|
|
8530
|
+
anchor: `import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
8531
|
+
import type { CaptureRegion, DesktopCapabilities } from "@oh-my-pi/pi-natives";`,
|
|
8532
|
+
patched: `import { deserialize, serialize } from "bun:jsc";
|
|
8533
|
+
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
8534
|
+
import type { CaptureRegion, DesktopCapabilities } from "@oh-my-pi/pi-natives";`,
|
|
8535
|
+
}],
|
|
8536
|
+
},
|
|
8537
|
+
{
|
|
8538
|
+
// frame: "CMP1" magic + big-endian u32 길이 + Bun structured clone 본문. Worker postMessage와 같은 값 의미.
|
|
8539
|
+
// 잘못된 magic·읽을 수 없는 본문은 throw해 읽는 쪽이 fail closed 한다. 조각난 frame은 완성될 때 한 번만 복사한다.
|
|
8540
|
+
file: "src/tools/computer/protocol.ts",
|
|
8541
|
+
marker: "export class ComputerFrameReader {",
|
|
8542
|
+
anchor: `/** Transport used by the worker core in Bun workers and tests. */
|
|
8543
|
+
export interface ComputerWorkerTransport {
|
|
8544
|
+
send(message: ComputerWorkerOutbound, transfer?: Bun.Transferable[]): void;
|
|
8545
|
+
onMessage(handler: (message: ComputerWorkerInbound) => void): () => void;
|
|
8546
|
+
close(): void;
|
|
8547
|
+
}`,
|
|
8548
|
+
patched: `/** Transport used by the worker core in Bun workers and tests. */
|
|
8549
|
+
export interface ComputerWorkerTransport {
|
|
8550
|
+
send(message: ComputerWorkerOutbound, transfer?: Bun.Transferable[]): void;
|
|
8551
|
+
onMessage(handler: (message: ComputerWorkerInbound) => void): () => void;
|
|
8552
|
+
close(): void;
|
|
8553
|
+
}
|
|
8554
|
+
|
|
8555
|
+
/**
|
|
8556
|
+
* CUELO: stdio framing for a computer worker hosted in another process (a WSL CUELO driving the
|
|
8557
|
+
* Windows host executable through interop). Each frame is the "CMP1" magic, a big-endian u32 body
|
|
8558
|
+
* length, and a Bun structured-clone body, so values cross exactly as through Worker postMessage.
|
|
8559
|
+
* A wrong magic or an unreadable body throws: the stream is corrupt and the reader fails closed
|
|
8560
|
+
* instead of guessing where the next frame starts.
|
|
8561
|
+
*/
|
|
8562
|
+
const COMPUTER_FRAME_MAGIC = 0x434d5031;
|
|
8563
|
+
const COMPUTER_FRAME_HEADER_BYTES = 8;
|
|
8564
|
+
|
|
8565
|
+
/** Encodes one worker message as a stdio frame; a non-cloneable message throws like postMessage. */
|
|
8566
|
+
export function encodeComputerFrame(message: ComputerWorkerInbound | ComputerWorkerOutbound): Uint8Array {
|
|
8567
|
+
const body = new Uint8Array(serialize(message));
|
|
8568
|
+
const frame = new Uint8Array(COMPUTER_FRAME_HEADER_BYTES + body.byteLength);
|
|
8569
|
+
const view = new DataView(frame.buffer);
|
|
8570
|
+
view.setUint32(0, COMPUTER_FRAME_MAGIC);
|
|
8571
|
+
view.setUint32(4, body.byteLength);
|
|
8572
|
+
frame.set(body, COMPUTER_FRAME_HEADER_BYTES);
|
|
8573
|
+
return frame;
|
|
8574
|
+
}
|
|
8575
|
+
|
|
8576
|
+
/** Reassembles frames from stdio chunks. After a throw the stream is corrupt; drop the reader. */
|
|
8577
|
+
export class ComputerFrameReader {
|
|
8578
|
+
#chunks: Uint8Array[] = [];
|
|
8579
|
+
#buffered = 0;
|
|
8580
|
+
|
|
8581
|
+
/** Adds one chunk and returns every message it completes, in order. */
|
|
8582
|
+
push(chunk: Uint8Array): unknown[] {
|
|
8583
|
+
this.#chunks.push(chunk);
|
|
8584
|
+
this.#buffered += chunk.byteLength;
|
|
8585
|
+
const messages: unknown[] = [];
|
|
8586
|
+
while (this.#buffered >= COMPUTER_FRAME_HEADER_BYTES) {
|
|
8587
|
+
const header = this.#read(COMPUTER_FRAME_HEADER_BYTES);
|
|
8588
|
+
const view = new DataView(header.buffer, header.byteOffset, COMPUTER_FRAME_HEADER_BYTES);
|
|
8589
|
+
if (view.getUint32(0) !== COMPUTER_FRAME_MAGIC) throw new Error("computer frame stream is corrupt: bad magic");
|
|
8590
|
+
const length = view.getUint32(4);
|
|
8591
|
+
if (this.#buffered < COMPUTER_FRAME_HEADER_BYTES + length) break;
|
|
8592
|
+
const frame = this.#read(COMPUTER_FRAME_HEADER_BYTES + length);
|
|
8593
|
+
this.#drop(COMPUTER_FRAME_HEADER_BYTES + length);
|
|
8594
|
+
messages.push(deserialize(frame.subarray(COMPUTER_FRAME_HEADER_BYTES)));
|
|
8595
|
+
}
|
|
8596
|
+
return messages;
|
|
8597
|
+
}
|
|
8598
|
+
|
|
8599
|
+
/** The first \`size\` buffered bytes: a view when one chunk holds them, else one copy. */
|
|
8600
|
+
#read(size: number): Uint8Array {
|
|
8601
|
+
const first = this.#chunks[0]!;
|
|
8602
|
+
if (first.byteLength >= size) return first.subarray(0, size);
|
|
8603
|
+
const bytes = new Uint8Array(size);
|
|
8604
|
+
let filled = 0;
|
|
8605
|
+
for (const chunk of this.#chunks) {
|
|
8606
|
+
const take = Math.min(chunk.byteLength, size - filled);
|
|
8607
|
+
bytes.set(chunk.subarray(0, take), filled);
|
|
8608
|
+
filled += take;
|
|
8609
|
+
if (filled === size) break;
|
|
8610
|
+
}
|
|
8611
|
+
return bytes;
|
|
8612
|
+
}
|
|
8613
|
+
|
|
8614
|
+
#drop(size: number): void {
|
|
8615
|
+
this.#buffered -= size;
|
|
8616
|
+
let left = size;
|
|
8617
|
+
while (left > 0) {
|
|
8618
|
+
const chunk = this.#chunks[0]!;
|
|
8619
|
+
if (chunk.byteLength <= left) {
|
|
8620
|
+
this.#chunks.shift();
|
|
8621
|
+
left -= chunk.byteLength;
|
|
8622
|
+
} else {
|
|
8623
|
+
this.#chunks[0] = chunk.subarray(left);
|
|
8624
|
+
left = 0;
|
|
8625
|
+
}
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
}`,
|
|
8629
|
+
},
|
|
8630
|
+
{
|
|
8631
|
+
file: "src/tools/computer/supervisor.ts",
|
|
8632
|
+
marker: "\tComputerFrameReader,\n\ttype ComputerRunOk,",
|
|
8633
|
+
anchor: `import {
|
|
8634
|
+
COMPUTER_WORKER_ARG,
|
|
8635
|
+
type ComputerRunOk,
|
|
8636
|
+
type ComputerSessionSnapshot,
|
|
8637
|
+
type ComputerWorkerInbound,
|
|
8638
|
+
type ComputerWorkerOutbound,
|
|
8639
|
+
type RunErrorPayload,
|
|
8640
|
+
} from "./protocol";`,
|
|
8641
|
+
patched: `import {
|
|
8642
|
+
COMPUTER_WORKER_ARG,
|
|
8643
|
+
ComputerFrameReader,
|
|
8644
|
+
type ComputerRunOk,
|
|
8645
|
+
type ComputerSessionSnapshot,
|
|
8646
|
+
type ComputerWorkerInbound,
|
|
8647
|
+
type ComputerWorkerOutbound,
|
|
8648
|
+
encodeComputerFrame,
|
|
8649
|
+
type RunErrorPayload,
|
|
8650
|
+
} from "./protocol";`,
|
|
8651
|
+
},
|
|
8652
|
+
{
|
|
8653
|
+
// linux + CUELO_WINDOWS_COMPUTER_HOST 일 때만 helper를 쓴다. 다른 OS·변수 없음은 upstream Worker 경로 그대로이고,
|
|
8654
|
+
// helper 실패는 오류로 보고할 뿐 WSLg desktop으로 바꾸지 않는다. spawn 실패·host 종료·끊긴 pipe·손상 frame은
|
|
8655
|
+
// onError로 한 번 알려 supervisor가 대기 중인 호출을 바로 실패시킨다. terminate는 stdin EOF → 2초 → kill.
|
|
8656
|
+
file: "src/tools/computer/supervisor.ts",
|
|
8657
|
+
marker: "export function spawnInteropComputerWorker(command: string[]): ComputerWorkerHandle {",
|
|
8658
|
+
anchor: `/** Spawns the computer worker through the active CLI host when available. */
|
|
8659
|
+
export function spawnComputerWorker(): ComputerWorkerHandle {
|
|
8660
|
+
const hostEntry = workerHostEntry();`,
|
|
8661
|
+
patched: `/** CUELO: names the Windows computer host executable a WSL CUELO runs through interop. */
|
|
8662
|
+
const WINDOWS_COMPUTER_HOST_ENV = "CUELO_WINDOWS_COMPUTER_HOST";
|
|
8663
|
+
/** Wait after closing the host's stdin before killing it; the host exits on EOF. */
|
|
8664
|
+
const INTEROP_TERMINATE_MS = 2_000;
|
|
8665
|
+
/** Wait for the exit status after the host closes its output, so the error names it. */
|
|
8666
|
+
const INTEROP_EXIT_AFTER_EOF_MS = 1_000;
|
|
8667
|
+
const INTEROP_STDERR_TAIL_CHARS = 2_000;
|
|
8668
|
+
|
|
8669
|
+
function errorText(error: unknown): string {
|
|
8670
|
+
return error instanceof Error ? error.message : String(error);
|
|
8671
|
+
}
|
|
8672
|
+
|
|
8673
|
+
/**
|
|
8674
|
+
* CUELO: runs the computer worker in a separate process speaking {@link encodeComputerFrame} frames on
|
|
8675
|
+
* stdin/stdout. A WSL CUELO starts the Windows host executable this way through interop: only the
|
|
8676
|
+
* parent's pipes connect the two (no port, no listener), and the host exits when its stdin closes. A
|
|
8677
|
+
* spawn failure, an exited host, a closed pipe, or a corrupt frame surfaces once through \`onError\`, so
|
|
8678
|
+
* the supervisor fails pending calls instead of waiting for them.
|
|
8679
|
+
*/
|
|
8680
|
+
export function spawnInteropComputerWorker(command: string[]): ComputerWorkerHandle {
|
|
8681
|
+
let child: Bun.Subprocess<"pipe", "pipe", "pipe">;
|
|
8682
|
+
try {
|
|
8683
|
+
child = Bun.spawn(command, { stdin: "pipe", stdout: "pipe", stderr: "pipe" });
|
|
8684
|
+
} catch (error) {
|
|
8685
|
+
throw new ToolError(\`Cannot start the computer host \${command[0]}: \${errorText(error)}\`);
|
|
8686
|
+
}
|
|
8687
|
+
const messageHandlers = new Set<(message: ComputerWorkerOutbound) => void>();
|
|
8688
|
+
const errorHandlers = new Set<(error: Error) => void>();
|
|
8689
|
+
let failure: Error | undefined;
|
|
8690
|
+
let stderrTail = "";
|
|
8691
|
+
const describe = (text: string): Error => {
|
|
8692
|
+
const tail = stderrTail.trim();
|
|
8693
|
+
return new Error(tail ? \`\${text}: \${tail}\` : text);
|
|
8694
|
+
};
|
|
8695
|
+
const fail = (error: Error): void => {
|
|
8696
|
+
if (failure) return;
|
|
8697
|
+
failure = error;
|
|
8698
|
+
for (const handler of [...errorHandlers]) handler(error);
|
|
8699
|
+
};
|
|
8700
|
+
|
|
8701
|
+
void (async () => {
|
|
8702
|
+
const decoder = new TextDecoder();
|
|
8703
|
+
try {
|
|
8704
|
+
for await (const chunk of child.stderr) {
|
|
8705
|
+
const text = decoder.decode(chunk, { stream: true });
|
|
8706
|
+
stderrTail = (stderrTail + text).slice(-INTEROP_STDERR_TAIL_CHARS);
|
|
8707
|
+
logger.debug("Computer host stderr", { text });
|
|
8708
|
+
}
|
|
8709
|
+
} catch {
|
|
8710
|
+
// stderr only feeds diagnostics; the exit and stdout paths report the failure.
|
|
8711
|
+
}
|
|
8712
|
+
})();
|
|
8713
|
+
void (async () => {
|
|
8714
|
+
const reader = new ComputerFrameReader();
|
|
8715
|
+
try {
|
|
8716
|
+
for await (const chunk of child.stdout) {
|
|
8717
|
+
let messages: unknown[];
|
|
8718
|
+
try {
|
|
8719
|
+
messages = reader.push(chunk);
|
|
8720
|
+
} catch (error) {
|
|
8721
|
+
child.kill(9);
|
|
8722
|
+
fail(describe(\`Computer host sent an unreadable frame (\${errorText(error)}); rebuild the host for this CUELO build\`));
|
|
8723
|
+
return;
|
|
8724
|
+
}
|
|
8725
|
+
for (const message of messages) {
|
|
8726
|
+
// The host is built from this core's protocol, exactly like a Worker entry.
|
|
8727
|
+
const outbound = message as ComputerWorkerOutbound;
|
|
8728
|
+
for (const handler of [...messageHandlers]) handler(outbound);
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8731
|
+
} catch (error) {
|
|
8732
|
+
fail(describe(\`Computer host output failed: \${errorText(error)}\`));
|
|
8733
|
+
return;
|
|
8734
|
+
}
|
|
8735
|
+
const exited = await Promise.race([child.exited.then(() => true), Bun.sleep(INTEROP_EXIT_AFTER_EOF_MS).then(() => false)]);
|
|
8736
|
+
if (!exited) fail(describe("Computer host closed its output"));
|
|
8737
|
+
})();
|
|
8738
|
+
void child.exited.then(code => fail(describe(\`Computer host exited (\${code ?? child.signalCode})\`)));
|
|
8739
|
+
|
|
8740
|
+
return {
|
|
8741
|
+
send(message) {
|
|
8742
|
+
if (failure) throw failure;
|
|
8743
|
+
// A non-cloneable message throws here, before any byte is written, exactly like postMessage.
|
|
8744
|
+
const frame = encodeComputerFrame(message);
|
|
8745
|
+
try {
|
|
8746
|
+
child.stdin.write(frame);
|
|
8747
|
+
const flushed = child.stdin.flush();
|
|
8748
|
+
if (flushed instanceof Promise) flushed.catch(error => fail(describe(\`Computer host input closed: \${errorText(error)}\`)));
|
|
8749
|
+
} catch (error) {
|
|
8750
|
+
const closed = describe(\`Computer host input closed: \${errorText(error)}\`);
|
|
8751
|
+
fail(closed);
|
|
8752
|
+
throw closed;
|
|
8753
|
+
}
|
|
8754
|
+
},
|
|
8755
|
+
onMessage(handler) {
|
|
8756
|
+
messageHandlers.add(handler);
|
|
8757
|
+
return () => {
|
|
8758
|
+
messageHandlers.delete(handler);
|
|
8759
|
+
};
|
|
8760
|
+
},
|
|
8761
|
+
onError(handler) {
|
|
8762
|
+
errorHandlers.add(handler);
|
|
8763
|
+
const early = failure;
|
|
8764
|
+
if (early) queueMicrotask(() => handler(early));
|
|
8765
|
+
return () => {
|
|
8766
|
+
errorHandlers.delete(handler);
|
|
8767
|
+
};
|
|
8768
|
+
},
|
|
8769
|
+
async terminate() {
|
|
8770
|
+
try {
|
|
8771
|
+
await child.stdin.end();
|
|
8772
|
+
} catch {
|
|
8773
|
+
// Already closed: the host has its EOF.
|
|
8774
|
+
}
|
|
8775
|
+
const deadline = Promise.withResolvers<boolean>();
|
|
8776
|
+
const timer = setTimeout(() => deadline.resolve(false), INTEROP_TERMINATE_MS);
|
|
8777
|
+
const exited = await Promise.race([child.exited.then(() => true), deadline.promise]);
|
|
8778
|
+
clearTimeout(timer);
|
|
8779
|
+
if (!exited) child.kill(9);
|
|
8780
|
+
},
|
|
8781
|
+
};
|
|
8782
|
+
}
|
|
8783
|
+
|
|
8784
|
+
/** Spawns the computer worker through the active CLI host when available. */
|
|
8785
|
+
export function spawnComputerWorker(): ComputerWorkerHandle {
|
|
8786
|
+
// CUELO: a WSL CUELO drives the Windows desktop through the host executable named by
|
|
8787
|
+
// CUELO_WINDOWS_COMPUTER_HOST. Linux only; without the variable nothing changes, and a host
|
|
8788
|
+
// failure is reported, never replaced by the Linux (WSLg) desktop.
|
|
8789
|
+
const windowsHost = process.platform === "linux" ? process.env[WINDOWS_COMPUTER_HOST_ENV]?.trim() : undefined;
|
|
8790
|
+
if (windowsHost) return spawnInteropComputerWorker([windowsHost]);
|
|
8791
|
+
const hostEntry = workerHostEntry();`,
|
|
8792
|
+
},
|
|
7838
8793
|
{
|
|
7839
8794
|
// MCP 선택 연결: 요청별 MCP 선택(opt-in). all이 기본이며 per-request면 시작 시 연결하지 않고 요청마다 확장이 고른다.
|
|
7840
8795
|
file: "src/mcp/settings.ts",
|
|
@@ -8844,6 +9799,13 @@ export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from:
|
|
|
8844
9799
|
marker: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
8845
9800
|
anchor: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
8846
9801
|
patched: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
9802
|
+
}, {
|
|
9803
|
+
// 18.6.3: upstream 이 상수를 AIError.CODEX_NATIVE_LANE_STEER_REJECTED_CODE 로 옮겼다(#14242). 같은 upstream 줄의 no-op.
|
|
9804
|
+
file: "../pi-ai/src/providers/openai-codex-responses.ts",
|
|
9805
|
+
requires: "AIError.CODEX_NATIVE_LANE_STEER_REJECTED_CODE",
|
|
9806
|
+
marker: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === AIError.CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
9807
|
+
anchor: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === AIError.CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
9808
|
+
patched: "\t\t\t\tif (parsed.type === \"error\" && parsed.code === AIError.CODEX_NATIVE_LANE_STEER_REJECTED_CODE) {\n\t\t\t\t\tthis.#refuseSteerWaiters(",
|
|
8847
9809
|
}],
|
|
8848
9810
|
},
|
|
8849
9811
|
{
|
|
@@ -8897,9 +9859,19 @@ export function summarizeAutoLearnSaved(messages: readonly AgentMessage[], from:
|
|
|
8897
9859
|
},
|
|
8898
9860
|
{
|
|
8899
9861
|
file: "src/tools/ask.ts",
|
|
9862
|
+
// 18.6.3(#14369)은 formatQuestionResult 를 다시 짜 timeout 접미사를 `timeoutSuffix` 한 줄로 옮겼다. 그 줄에 같은 문구를 넣는다(ADAPT).
|
|
9863
|
+
// 두 판 결과가 같은 marker 를 가지므로 `timeoutSuffix` 유무로 후보를 가른다.
|
|
9864
|
+
excludes: "const timeoutSuffix = ",
|
|
8900
9865
|
marker: "no user response, not user approval",
|
|
8901
9866
|
anchor: ` const suffix = \`\${result.timedOut ? " (auto-selected after timeout)" : ""}\${noteSuffix}\`;`,
|
|
8902
9867
|
patched: ` const suffix = \`\${result.timedOut ? " (auto-selected recommended option after timeout; no user response, not user approval)" : ""}\${noteSuffix}\`;`,
|
|
9868
|
+
alternates: [{
|
|
9869
|
+
file: "src/tools/ask.ts",
|
|
9870
|
+
requires: "const timeoutSuffix = ",
|
|
9871
|
+
marker: "no user response, not user approval",
|
|
9872
|
+
anchor: ` const timeoutSuffix = result.timedOut ? " (auto-selected after timeout)" : "";`,
|
|
9873
|
+
patched: ` const timeoutSuffix = result.timedOut ? " (auto-selected recommended option after timeout; no user response, not user approval)" : "";`,
|
|
9874
|
+
}],
|
|
8903
9875
|
},
|
|
8904
9876
|
{
|
|
8905
9877
|
file: "src/tools/ask.ts",
|
|
@@ -9018,6 +9990,12 @@ function parentSubagentServiceTiers(
|
|
|
9018
9990
|
marker: '{"source":"classes/anthropic.kdl:85","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:101",',
|
|
9019
9991
|
anchor: '{"source":"classes/anthropic.kdl:85","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:101",',
|
|
9020
9992
|
patched: '{"source":"classes/anthropic.kdl:85","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:101",',
|
|
9993
|
+
}, {
|
|
9994
|
+
// 18.6.3: 같은 upstream 규칙이 kdl:150(뒤 Sonnet 규칙 kdl:166)로 옮겨졌다. 본문은 그대로다. 그 줄의 no-op.
|
|
9995
|
+
file: "../pi-catalog/src/compat/rules.json",
|
|
9996
|
+
marker: '{"source":"classes/anthropic.kdl:150","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:166",',
|
|
9997
|
+
anchor: '{"source":"classes/anthropic.kdl:150","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:166",',
|
|
9998
|
+
patched: '{"source":"classes/anthropic.kdl:150","class":"anthropic","family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsForcedToolChoice":false},"thinking":{"prefixBinding":true}},{"source":"classes/anthropic.kdl:166",',
|
|
9021
9999
|
}],
|
|
9022
10000
|
},
|
|
9023
10001
|
{
|
|
@@ -9055,6 +10033,12 @@ function parentSubagentServiceTiers(
|
|
|
9055
10033
|
marker: '{"source":"classes/anthropic.kdl:175","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
9056
10034
|
anchor: '{"source":"classes/anthropic.kdl:175","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
9057
10035
|
patched: '{"source":"classes/anthropic.kdl:175","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
10036
|
+
}, {
|
|
10037
|
+
// 18.6.3: 같은 upstream 규칙이 kdl:240 으로 옮겨졌다. 본문·provider 범위는 그대로다. 그 줄의 no-op.
|
|
10038
|
+
file: "../pi-catalog/src/compat/rules.json",
|
|
10039
|
+
marker: '{"source":"classes/anthropic.kdl:240","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
10040
|
+
anchor: '{"source":"classes/anthropic.kdl:240","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
10041
|
+
patched: '{"source":"classes/anthropic.kdl:240","class":"anthropic","providers":["anthropic","cloudflare-ai-gateway"],"family":"opus","revision":[{"op":">=","revision":"5.5.0"},{"op":"<","revision":"6.0.0"}],"wire":{"supportsThinkingBindingControls":true}}',
|
|
9058
10042
|
}],
|
|
9059
10043
|
},
|
|
9060
10044
|
// 위 세 항목과 짝: modelOverrides의 thinking은 buildModel이 규칙으로 채운 thinking을 통째로 덮어써서, override가
|
|
@@ -9256,6 +10240,12 @@ function parentSubagentServiceTiers(
|
|
|
9256
10240
|
marker: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||NRe(i))continue;let a=i.slice(0,-6),',
|
|
9257
10241
|
anchor: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||NRe(i))continue;let a=i.slice(0,-6),',
|
|
9258
10242
|
patched: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||NRe(i))continue;let a=i.slice(0,-6),',
|
|
10243
|
+
}, {
|
|
10244
|
+
// 18.6.3 번들의 같은 upstream 조건(ZRe = isAdvisorTranscriptName) no-op. 이름만 바뀌었다.
|
|
10245
|
+
file: "dist/cli.js",
|
|
10246
|
+
marker: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||ZRe(i))continue;let a=i.slice(0,-6),',
|
|
10247
|
+
anchor: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||ZRe(i))continue;let a=i.slice(0,-6),',
|
|
10248
|
+
patched: 'if(!i.endsWith(".jsonl")||i.includes(".bak")||ZRe(i))continue;let a=i.slice(0,-6),',
|
|
9259
10249
|
}],
|
|
9260
10250
|
},
|
|
9261
10251
|
// 18.4.5 는 /ratchet 을 새로 넣으면서 `src/ratchet/prelude.ts`(코드)와 `prelude.js`(eval 텍스트 자산)를 같은 stem 으로
|