cuelo 0.5.0
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 -0
- package/.next/app-path-routes-manifest.json +72 -0
- package/.next/build-manifest.json +20 -0
- package/.next/diagnostics/build-diagnostics.json +6 -0
- package/.next/diagnostics/framework.json +1 -0
- package/.next/export-marker.json +6 -0
- package/.next/images-manifest.json +68 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +138 -0
- package/.next/react-loadable-manifest.json +2370 -0
- package/.next/required-server-files.js +350 -0
- package/.next/required-server-files.json +350 -0
- package/.next/routes-manifest.json +537 -0
- package/.next/server/app/_global-error/page.js +32 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +16 -0
- package/.next/server/app/_global-error.rsc +14 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +14 -0
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_global-error.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page.js +2 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +16 -0
- package/.next/server/app/_not-found.rsc +18 -0
- package/.next/server/app/_not-found.segments/_full.segment.rsc +18 -0
- package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_index.segment.rsc +7 -0
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -0
- package/.next/server/app/api/agent/[id]/account/route.js +1 -0
- package/.next/server/app/api/agent/[id]/account/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/[id]/account/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/agent/[id]/bash-output/route.js +1 -0
- package/.next/server/app/api/agent/[id]/bash-output/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/agent/[id]/events/route.js +3 -0
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/agent/[id]/route.js +1 -0
- package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/agent/new/route.js +1 -0
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/agent/running/route.js +1 -0
- package/.next/server/app/api/agent/running/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/all-providers/route.js +1 -0
- package/.next/server/app/api/auth/all-providers/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -0
- package/.next/server/app/api/auth/api-key/[provider]/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/login/[provider]/route.js +3 -0
- package/.next/server/app/api/auth/login/[provider]/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -0
- package/.next/server/app/api/auth/logout/[provider]/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/auth/providers/route.js +1 -0
- package/.next/server/app/api/auth/providers/route.js.nft.json +1 -0
- package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/cwd/browse/route.js +1 -0
- package/.next/server/app/api/cwd/browse/route.js.nft.json +1 -0
- package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/cwd/validate/route.js +1 -0
- package/.next/server/app/api/cwd/validate/route.js.nft.json +1 -0
- package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/default-cwd/route.js +1 -0
- package/.next/server/app/api/default-cwd/route.js.nft.json +1 -0
- package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/display-settings/route.js +1 -0
- package/.next/server/app/api/display-settings/route.js.nft.json +1 -0
- package/.next/server/app/api/display-settings/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/document-text/route.js +4 -0
- package/.next/server/app/api/document-text/route.js.nft.json +1 -0
- package/.next/server/app/api/document-text/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/experiments/route.js +1 -0
- package/.next/server/app/api/experiments/route.js.nft.json +1 -0
- package/.next/server/app/api/experiments/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/file-index/route.js +1 -0
- package/.next/server/app/api/file-index/route.js.nft.json +1 -0
- package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/files/[...path]/route.js +49 -0
- package/.next/server/app/api/files/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/git/diff/route.js +6 -0
- package/.next/server/app/api/git/diff/route.js.nft.json +1 -0
- package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/git/status/route.js +6 -0
- package/.next/server/app/api/git/status/route.js.nft.json +1 -0
- package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/gpt6/handles/route.js +1 -0
- package/.next/server/app/api/gpt6/handles/route.js.nft.json +1 -0
- package/.next/server/app/api/gpt6/handles/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/gpt6/mcp/route.js +2 -0
- package/.next/server/app/api/gpt6/mcp/route.js.nft.json +1 -0
- package/.next/server/app/api/gpt6/mcp/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/gpt6/reply/route.js +1 -0
- package/.next/server/app/api/gpt6/reply/route.js.nft.json +1 -0
- package/.next/server/app/api/gpt6/reply/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/gpt6/token/route.js +1 -0
- package/.next/server/app/api/gpt6/token/route.js.nft.json +1 -0
- package/.next/server/app/api/gpt6/token/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/gpt6/web6-session/route.js +1 -0
- package/.next/server/app/api/gpt6/web6-session/route.js.nft.json +1 -0
- package/.next/server/app/api/gpt6/web6-session/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/home/route.js +1 -0
- package/.next/server/app/api/home/route.js.nft.json +1 -0
- package/.next/server/app/api/home/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/live/call/[callId]/route.js +1 -0
- package/.next/server/app/api/live/call/[callId]/route.js.nft.json +1 -0
- package/.next/server/app/api/live/call/[callId]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/live/events/route.js +3 -0
- package/.next/server/app/api/live/events/route.js.nft.json +1 -0
- package/.next/server/app/api/live/events/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/live/offer/route.js +1 -0
- package/.next/server/app/api/live/offer/route.js.nft.json +1 -0
- package/.next/server/app/api/live/offer/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/live/voices/route.js +1 -0
- package/.next/server/app/api/live/voices/route.js.nft.json +1 -0
- package/.next/server/app/api/live/voices/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/mcp/route.js +2 -0
- package/.next/server/app/api/mcp/route.js.nft.json +1 -0
- package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/model-roles/route.js +1 -0
- package/.next/server/app/api/model-roles/route.js.nft.json +1 -0
- package/.next/server/app/api/model-roles/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models/route.js +1 -0
- package/.next/server/app/api/models/route.js.nft.json +1 -0
- package/.next/server/app/api/models/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models-config/catalog/route.js +1 -0
- package/.next/server/app/api/models-config/catalog/route.js.nft.json +1 -0
- package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models-config/discover/route.js +1 -0
- package/.next/server/app/api/models-config/discover/route.js.nft.json +1 -0
- package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models-config/route.js +25 -0
- package/.next/server/app/api/models-config/route.js.nft.json +1 -0
- package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/models-config/test/route.js +1 -0
- package/.next/server/app/api/models-config/test/route.js.nft.json +1 -0
- package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/plugins/route.js +1 -0
- package/.next/server/app/api/plugins/route.js.nft.json +1 -0
- package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/project-trust/route.js +1 -0
- package/.next/server/app/api/project-trust/route.js.nft.json +1 -0
- package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/projects/route.js +2 -0
- package/.next/server/app/api/projects/route.js.nft.json +1 -0
- package/.next/server/app/api/projects/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/runs/[runId]/route.js +1 -0
- package/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/runs/route.js +1 -0
- package/.next/server/app/api/runs/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/auto-name/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/context/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/[id]/state/route.js +1 -0
- package/.next/server/app/api/sessions/[id]/state/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sessions/route.js +1 -0
- package/.next/server/app/api/sessions/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/settings/route.js +1 -0
- package/.next/server/app/api/settings/route.js.nft.json +1 -0
- package/.next/server/app/api/settings/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/sidecars/[service]/[...path]/route.js +1 -0
- package/.next/server/app/api/sidecars/[service]/[...path]/route.js.nft.json +1 -0
- package/.next/server/app/api/sidecars/[service]/[...path]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/check/route.js +1 -0
- package/.next/server/app/api/skills/check/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/install/route.js +2 -0
- package/.next/server/app/api/skills/install/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/route.js +6 -0
- package/.next/server/app/api/skills/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/search/route.js +1 -0
- package/.next/server/app/api/skills/search/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/skills/update/route.js +1 -0
- package/.next/server/app/api/skills/update/route.js.nft.json +1 -0
- package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/theme/route.js +1 -0
- package/.next/server/app/api/theme/route.js.nft.json +1 -0
- package/.next/server/app/api/theme/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/update-maintenance/route.js +556 -0
- package/.next/server/app/api/update-maintenance/route.js.nft.json +1 -0
- package/.next/server/app/api/update-maintenance/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/updates/route.js +1 -0
- package/.next/server/app/api/updates/route.js.nft.json +1 -0
- package/.next/server/app/api/updates/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/web-access/recovery/route.js +1 -0
- package/.next/server/app/api/web-access/recovery/route.js.nft.json +1 -0
- package/.next/server/app/api/web-access/recovery/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/web-access/route.js +1 -0
- package/.next/server/app/api/web-access/route.js.nft.json +1 -0
- package/.next/server/app/api/web-access/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/web6/consults/route.js +1 -0
- package/.next/server/app/api/web6/consults/route.js.nft.json +1 -0
- package/.next/server/app/api/web6/consults/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/worktrees/route.js +1 -0
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -0
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -0
- package/.next/server/app/favicon.ico/route.js +1 -0
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -0
- package/.next/server/app/favicon.ico.body +0 -0
- package/.next/server/app/favicon.ico.meta +1 -0
- package/.next/server/app/index.html +1 -0
- package/.next/server/app/index.meta +14 -0
- package/.next/server/app/index.rsc +20 -0
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
- package/.next/server/app/index.segments/_full.segment.rsc +20 -0
- package/.next/server/app/index.segments/_head.segment.rsc +6 -0
- package/.next/server/app/index.segments/_index.segment.rsc +7 -0
- package/.next/server/app/index.segments/_tree.segment.rsc +5 -0
- package/.next/server/app/manifest.webmanifest/route.js +16 -0
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -0
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -0
- package/.next/server/app/page.js +22 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app/recover/page.js +2 -0
- package/.next/server/app/recover/page.js.nft.json +1 -0
- package/.next/server/app/recover/page_client-reference-manifest.js +1 -0
- package/.next/server/app/recover.html +1 -0
- package/.next/server/app/recover.meta +15 -0
- package/.next/server/app/recover.rsc +23 -0
- package/.next/server/app/recover.segments/_full.segment.rsc +23 -0
- package/.next/server/app/recover.segments/_head.segment.rsc +6 -0
- package/.next/server/app/recover.segments/_index.segment.rsc +7 -0
- package/.next/server/app/recover.segments/_tree.segment.rsc +4 -0
- package/.next/server/app/recover.segments/recover/__PAGE__.segment.rsc +10 -0
- package/.next/server/app/recover.segments/recover.segment.rsc +5 -0
- package/.next/server/app-paths-manifest.json +72 -0
- package/.next/server/chunks/11.js +1 -0
- package/.next/server/chunks/1286.js +1 -0
- package/.next/server/chunks/1412.js +38 -0
- package/.next/server/chunks/1517.js +19 -0
- package/.next/server/chunks/1605.js +1 -0
- package/.next/server/chunks/169.js +1 -0
- package/.next/server/chunks/18.js +86 -0
- package/.next/server/chunks/1813.js +1 -0
- package/.next/server/chunks/2022.js +1 -0
- package/.next/server/chunks/2156.js +1 -0
- package/.next/server/chunks/2157.js +1 -0
- package/.next/server/chunks/2325.js +82 -0
- package/.next/server/chunks/2651.js +2 -0
- package/.next/server/chunks/2838.js +1 -0
- package/.next/server/chunks/2897.js +79 -0
- package/.next/server/chunks/2958.js +90 -0
- package/.next/server/chunks/319.js +45 -0
- package/.next/server/chunks/3227.js +1 -0
- package/.next/server/chunks/3229.js +2 -0
- package/.next/server/chunks/3266.js +1 -0
- package/.next/server/chunks/3379.js +1 -0
- package/.next/server/chunks/3428.js +32 -0
- package/.next/server/chunks/3479.js +1 -0
- package/.next/server/chunks/367.js +115 -0
- package/.next/server/chunks/3698.js +93 -0
- package/.next/server/chunks/3709.js +148 -0
- package/.next/server/chunks/3759.js +34 -0
- package/.next/server/chunks/4107.js +62 -0
- package/.next/server/chunks/4741.js +20 -0
- package/.next/server/chunks/4755.js +153 -0
- package/.next/server/chunks/5002.js +1 -0
- package/.next/server/chunks/5112.js +1 -0
- package/.next/server/chunks/5289.js +8 -0
- package/.next/server/chunks/5318.js +1 -0
- package/.next/server/chunks/5452.js +23 -0
- package/.next/server/chunks/5453.js +1 -0
- package/.next/server/chunks/5582.js +20 -0
- package/.next/server/chunks/5749.js +27 -0
- package/.next/server/chunks/5782.js +36 -0
- package/.next/server/chunks/6163.js +104 -0
- package/.next/server/chunks/6169.js +64 -0
- package/.next/server/chunks/6208.js +172 -0
- package/.next/server/chunks/6399.js +24 -0
- package/.next/server/chunks/6429.js +10 -0
- package/.next/server/chunks/6496.js +1 -0
- package/.next/server/chunks/6775.js +6 -0
- package/.next/server/chunks/6823.js +215 -0
- package/.next/server/chunks/6888.js +1 -0
- package/.next/server/chunks/7245.js +77 -0
- package/.next/server/chunks/7307.js +1 -0
- package/.next/server/chunks/7372.js +287 -0
- package/.next/server/chunks/7384.js +226 -0
- package/.next/server/chunks/7516.js +1 -0
- package/.next/server/chunks/7531.js +24 -0
- package/.next/server/chunks/7591.js +1 -0
- package/.next/server/chunks/7755.js +138 -0
- package/.next/server/chunks/7916.js +1 -0
- package/.next/server/chunks/8078.js +94 -0
- package/.next/server/chunks/8253.js +41 -0
- package/.next/server/chunks/8635.js +1 -0
- package/.next/server/chunks/8688.js +1 -0
- package/.next/server/chunks/877.js +1 -0
- package/.next/server/chunks/8872.js +1 -0
- package/.next/server/chunks/9006.js +1 -0
- package/.next/server/chunks/9018.js +1 -0
- package/.next/server/chunks/9047.js +1 -0
- package/.next/server/chunks/9108.js +1 -0
- package/.next/server/chunks/9280.js +256 -0
- package/.next/server/chunks/9419.js +1 -0
- package/.next/server/chunks/9430.js +1 -0
- package/.next/server/chunks/9586.js +1 -0
- package/.next/server/chunks/9775.js +1 -0
- package/.next/server/chunks/9910.js +1 -0
- package/.next/server/chunks/992.js +1 -0
- package/.next/server/chunks/9926.js +1 -0
- package/.next/server/functions-config-manifest.json +25 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/middleware.js +45 -0
- package/.next/server/middleware.js.nft.json +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages-manifest.json +4 -0
- package/.next/server/prefetch-hints.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/MvC-CM1U-UMEzpaGIsCuQ/_buildManifest.js +1 -0
- package/.next/static/MvC-CM1U-UMEzpaGIsCuQ/_ssgManifest.js +1 -0
- package/.next/static/chunks/1213.e5c6e70a79b5f7e6.js +24 -0
- package/.next/static/chunks/1519.2f596e5f5af9ef57.js +62 -0
- package/.next/static/chunks/1966.b8f83f7ba376c962.js +1 -0
- package/.next/static/chunks/1974.885c9a133b86d8a4.js +32 -0
- package/.next/static/chunks/2280.6a7f540787704698.js +1 -0
- package/.next/static/chunks/2521.7f745f50d30076e7.js +77 -0
- package/.next/static/chunks/2585.4d6aa847c780c666.js +1 -0
- package/.next/static/chunks/2589.1aad68c09021b1ac.js +1 -0
- package/.next/static/chunks/2600.45ba17a0f756fc31.js +1 -0
- package/.next/static/chunks/2619.9aac8983f30c7c8a.js +1 -0
- package/.next/static/chunks/26d302b2.b77b53a34ca944aa.js +122 -0
- package/.next/static/chunks/2768.305914f7900745b6.js +1 -0
- package/.next/static/chunks/3016.406e568aa95ff867.js +1 -0
- package/.next/static/chunks/3022.972047558a20553a.js +172 -0
- package/.next/static/chunks/3064.3a2a1e7cd4fdca5f.js +1 -0
- package/.next/static/chunks/3174.03fbed7b7d7a8256.js +1 -0
- package/.next/static/chunks/3189.b048866768087750.js +1 -0
- package/.next/static/chunks/3217.1a053843c58b70b5.js +24 -0
- package/.next/static/chunks/3308.34ea31e24088c85f.js +74 -0
- package/.next/static/chunks/331.fc5d78e1e91daf43.js +1 -0
- package/.next/static/chunks/3510.5d4d1c79533e080c.js +1 -0
- package/.next/static/chunks/364.ce5400a9dd70b9dc.js +1 -0
- package/.next/static/chunks/370.57cfa27da8567918.js +1 -0
- package/.next/static/chunks/3789.c418b69121761983.js +1 -0
- package/.next/static/chunks/3794-1b03b52e3112452e.js +32 -0
- package/.next/static/chunks/3831.25ae62d10dccc27a.js +1 -0
- package/.next/static/chunks/3899.3f761f7e3a944e8c.js +1 -0
- package/.next/static/chunks/3a7aa434.93dbd20997cfd24f.js +82 -0
- package/.next/static/chunks/4020.3cc19c463c49f969.js +23 -0
- package/.next/static/chunks/4035.0acc428926d5c8dd.js +1 -0
- package/.next/static/chunks/405.0902e4f6eb7bd9a2.js +215 -0
- package/.next/static/chunks/4159.49ae537ff687bdc6.js +138 -0
- package/.next/static/chunks/4208.384b381ca2757643.js +287 -0
- package/.next/static/chunks/4263.ffa8d9bd8a0faaae.js +1 -0
- package/.next/static/chunks/4592.d0f37bc216b9d988.js +93 -0
- package/.next/static/chunks/4799.0dc7aa21bff2fd2a.js +1 -0
- package/.next/static/chunks/4bd1b696-e356ca5ba0218e27.js +1 -0
- package/.next/static/chunks/514.b804c51b26f5e126.js +1 -0
- package/.next/static/chunks/5270.28592864d38e881c.js +1 -0
- package/.next/static/chunks/52733464.3870f99cc446f9ff.js +1 -0
- package/.next/static/chunks/5312.4828d88ccc66f057.js +41 -0
- package/.next/static/chunks/5354.b559c717a607dc5c.js +1 -0
- package/.next/static/chunks/5413.1f8eeba1c502f3e9.js +34 -0
- package/.next/static/chunks/5448.d3972d9fb464b54f.js +38 -0
- package/.next/static/chunks/587.d52737d7288c11b4.js +1 -0
- package/.next/static/chunks/5909.9e37f7b495b11995.js +115 -0
- package/.next/static/chunks/5949.5504b3545fcc3d34.js +104 -0
- package/.next/static/chunks/6361.893d04ac34fa1b49.js +148 -0
- package/.next/static/chunks/6379-7da431a165ac5d41.js +7 -0
- package/.next/static/chunks/6774.8cd53427dbf32cac.js +86 -0
- package/.next/static/chunks/6968.b80277c724dbc96e.js +1 -0
- package/.next/static/chunks/7113.4b9e7ea870aa300a.js +8 -0
- package/.next/static/chunks/7236.55a9728d35f5b8a6.js +1 -0
- package/.next/static/chunks/7390.dde9b5bc2f6e24b8.js +1 -0
- package/.next/static/chunks/7806.a903780fcfa3a30e.js +1 -0
- package/.next/static/chunks/7997.be57e92e583fe635.js +1 -0
- package/.next/static/chunks/8216.64a72d4d35ea7be0.js +1 -0
- package/.next/static/chunks/830.5d5274d7ad1cb58e.js +1 -0
- package/.next/static/chunks/8378.fa91de3406f554c9.js +226 -0
- package/.next/static/chunks/8501.cd06aa1b0c7e1c6a.js +79 -0
- package/.next/static/chunks/8663.6ec5792fb4952e45.js +27 -0
- package/.next/static/chunks/872.96937e758ddce860.js +1 -0
- package/.next/static/chunks/8803.75bde095fe3613f5.js +1 -0
- package/.next/static/chunks/8863.72f5dbb820a0e4c3.js +1 -0
- package/.next/static/chunks/889.ff9934ce4324e448.js +90 -0
- package/.next/static/chunks/90542734.b88b1467ff8248c7.js +1 -0
- package/.next/static/chunks/9379.5bc9c9343ec5a16c.js +64 -0
- package/.next/static/chunks/9389.906fe7aa9cd6a170.js +1 -0
- package/.next/static/chunks/9472.8818ee52cfa931a4.js +94 -0
- package/.next/static/chunks/966.d6babbf8119162cc.js +1 -0
- package/.next/static/chunks/9884.839b23ae397c150e.js +36 -0
- package/.next/static/chunks/app/_global-error/page-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/_not-found/page-d3f2ac234e6fd3b6.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/account/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/display-settings/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/document-text/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/experiments/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/gpt6/handles/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/gpt6/mcp/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/gpt6/reply/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/gpt6/token/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/gpt6/web6-session/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/home/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/live/call/[callId]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/live/events/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/live/offer/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/live/voices/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/model-roles/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/models/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/projects/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/runs/[runId]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/runs/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/settings/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/sidecars/[service]/[...path]/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/skills/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/theme/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/update-maintenance/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/updates/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/web-access/recovery/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/web-access/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/web6/consults/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/layout-95725ad1abf3cb75.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/app/page-26a4df181c1d562c.js +15 -0
- package/.next/static/chunks/app/recover/page-770a1de1ff4ded56.js +1 -0
- package/.next/static/chunks/c16f53c3-7916593d74747bde.js +1 -0
- package/.next/static/chunks/d3ac728e-7964f816a1ca64e5.js +1 -0
- package/.next/static/chunks/e413bedd.3a12a8cb02e28fdf.js +153 -0
- package/.next/static/chunks/ea233666.f00b64a7d99b4950.js +62 -0
- package/.next/static/chunks/framework-32a7466d5cae4a81.js +1 -0
- package/.next/static/chunks/main-50999e6ee5708e08.js +5 -0
- package/.next/static/chunks/main-app-0dc38ffe862cd588.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-070571d2fa580e7d.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-a9c51a9353807009.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-4094d78ef0b4c382.js +1 -0
- package/.next/static/css/2f46e0db0d4a3aed.css +1 -0
- package/.next/static/css/792a9d55c2fd2c0c.css +1 -0
- package/.next/static/css/92023589aa455863.css +1 -0
- package/.next/static/css/bb6b220f400250d8.css +1 -0
- package/.next/static/css/bc8f2da9192ea16d.css +1 -0
- package/.next/static/media/KaTeX_AMS-Regular.1608a09b.woff +0 -0
- package/.next/static/media/KaTeX_AMS-Regular.4aafdb68.ttf +0 -0
- package/.next/static/media/KaTeX_AMS-Regular.a79f1c31.woff2 +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Bold.b6770918.woff +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Bold.cce5b8ec.ttf +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Bold.ec17d132.woff2 +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Regular.07ef19e7.ttf +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Regular.55fac258.woff2 +0 -0
- package/.next/static/media/KaTeX_Caligraphic-Regular.dad44a7f.woff +0 -0
- package/.next/static/media/KaTeX_Fraktur-Bold.9f256b85.woff +0 -0
- package/.next/static/media/KaTeX_Fraktur-Bold.b18f59e1.ttf +0 -0
- package/.next/static/media/KaTeX_Fraktur-Bold.d42a5579.woff2 +0 -0
- package/.next/static/media/KaTeX_Fraktur-Regular.7c187121.woff +0 -0
- package/.next/static/media/KaTeX_Fraktur-Regular.d3c882a6.woff2 +0 -0
- package/.next/static/media/KaTeX_Fraktur-Regular.ed38e79f.ttf +0 -0
- package/.next/static/media/KaTeX_Main-Bold.b74a1a8b.ttf +0 -0
- package/.next/static/media/KaTeX_Main-Bold.c3fb5ac2.woff2 +0 -0
- package/.next/static/media/KaTeX_Main-Bold.d181c465.woff +0 -0
- package/.next/static/media/KaTeX_Main-BoldItalic.6f2bb1df.woff2 +0 -0
- package/.next/static/media/KaTeX_Main-BoldItalic.70d8b0a5.ttf +0 -0
- package/.next/static/media/KaTeX_Main-BoldItalic.e3f82f9d.woff +0 -0
- package/.next/static/media/KaTeX_Main-Italic.47373d1e.ttf +0 -0
- package/.next/static/media/KaTeX_Main-Italic.8916142b.woff2 +0 -0
- package/.next/static/media/KaTeX_Main-Italic.9024d815.woff +0 -0
- package/.next/static/media/KaTeX_Main-Regular.0462f03b.woff2 +0 -0
- package/.next/static/media/KaTeX_Main-Regular.7f51fe03.woff +0 -0
- package/.next/static/media/KaTeX_Main-Regular.b7f8fe9b.ttf +0 -0
- package/.next/static/media/KaTeX_Math-BoldItalic.572d331f.woff2 +0 -0
- package/.next/static/media/KaTeX_Math-BoldItalic.a879cf83.ttf +0 -0
- package/.next/static/media/KaTeX_Math-BoldItalic.f1035d8d.woff +0 -0
- package/.next/static/media/KaTeX_Math-Italic.5295ba48.woff +0 -0
- package/.next/static/media/KaTeX_Math-Italic.939bc644.ttf +0 -0
- package/.next/static/media/KaTeX_Math-Italic.f28c23ac.woff2 +0 -0
- package/.next/static/media/KaTeX_SansSerif-Bold.8c5b5494.woff2 +0 -0
- package/.next/static/media/KaTeX_SansSerif-Bold.94e1e8dc.ttf +0 -0
- package/.next/static/media/KaTeX_SansSerif-Bold.bf59d231.woff +0 -0
- package/.next/static/media/KaTeX_SansSerif-Italic.3b1e59b3.woff2 +0 -0
- package/.next/static/media/KaTeX_SansSerif-Italic.7c9bc82b.woff +0 -0
- package/.next/static/media/KaTeX_SansSerif-Italic.b4c20c84.ttf +0 -0
- package/.next/static/media/KaTeX_SansSerif-Regular.74048478.woff +0 -0
- package/.next/static/media/KaTeX_SansSerif-Regular.ba21ed5f.woff2 +0 -0
- package/.next/static/media/KaTeX_SansSerif-Regular.d4d7ba48.ttf +0 -0
- package/.next/static/media/KaTeX_Script-Regular.03e9641d.woff2 +0 -0
- package/.next/static/media/KaTeX_Script-Regular.07505710.woff +0 -0
- package/.next/static/media/KaTeX_Script-Regular.fe9cbbe1.ttf +0 -0
- package/.next/static/media/KaTeX_Size1-Regular.e1e279cb.woff +0 -0
- package/.next/static/media/KaTeX_Size1-Regular.eae34984.woff2 +0 -0
- package/.next/static/media/KaTeX_Size1-Regular.fabc004a.ttf +0 -0
- package/.next/static/media/KaTeX_Size2-Regular.57727022.woff +0 -0
- package/.next/static/media/KaTeX_Size2-Regular.5916a24f.woff2 +0 -0
- package/.next/static/media/KaTeX_Size2-Regular.d6b476ec.ttf +0 -0
- package/.next/static/media/KaTeX_Size3-Regular.9acaf01c.woff +0 -0
- package/.next/static/media/KaTeX_Size3-Regular.a144ef58.ttf +0 -0
- package/.next/static/media/KaTeX_Size3-Regular.b4230e7e.woff2 +0 -0
- package/.next/static/media/KaTeX_Size4-Regular.10d95fd3.woff2 +0 -0
- package/.next/static/media/KaTeX_Size4-Regular.7a996c9d.woff +0 -0
- package/.next/static/media/KaTeX_Size4-Regular.fbccdabe.ttf +0 -0
- package/.next/static/media/KaTeX_Typewriter-Regular.6258592b.woff +0 -0
- package/.next/static/media/KaTeX_Typewriter-Regular.a8709e36.woff2 +0 -0
- package/.next/static/media/KaTeX_Typewriter-Regular.d97aaf4a.ttf +0 -0
- package/.next/trace +8 -0
- package/.next/trace-build +1 -0
- package/.next/types/app/api/agent/[id]/account/route.ts +351 -0
- package/.next/types/app/api/agent/[id]/bash-output/route.ts +351 -0
- package/.next/types/app/api/agent/[id]/events/route.ts +351 -0
- package/.next/types/app/api/agent/[id]/route.ts +351 -0
- package/.next/types/app/api/agent/new/route.ts +351 -0
- package/.next/types/app/api/agent/running/route.ts +351 -0
- package/.next/types/app/api/auth/all-providers/route.ts +351 -0
- package/.next/types/app/api/auth/api-key/[provider]/route.ts +351 -0
- package/.next/types/app/api/auth/login/[provider]/route.ts +351 -0
- package/.next/types/app/api/auth/logout/[provider]/route.ts +351 -0
- package/.next/types/app/api/auth/providers/route.ts +351 -0
- package/.next/types/app/api/cwd/browse/route.ts +351 -0
- package/.next/types/app/api/cwd/validate/route.ts +351 -0
- package/.next/types/app/api/default-cwd/route.ts +351 -0
- package/.next/types/app/api/display-settings/route.ts +351 -0
- package/.next/types/app/api/document-text/route.ts +351 -0
- package/.next/types/app/api/experiments/route.ts +351 -0
- package/.next/types/app/api/file-index/route.ts +351 -0
- package/.next/types/app/api/files/[...path]/route.ts +351 -0
- package/.next/types/app/api/git/diff/route.ts +351 -0
- package/.next/types/app/api/git/status/route.ts +351 -0
- package/.next/types/app/api/gpt6/handles/route.ts +351 -0
- package/.next/types/app/api/gpt6/mcp/route.ts +351 -0
- package/.next/types/app/api/gpt6/reply/route.ts +351 -0
- package/.next/types/app/api/gpt6/token/route.ts +351 -0
- package/.next/types/app/api/gpt6/web6-session/route.ts +351 -0
- package/.next/types/app/api/home/route.ts +351 -0
- package/.next/types/app/api/live/call/[callId]/route.ts +351 -0
- package/.next/types/app/api/live/events/route.ts +351 -0
- package/.next/types/app/api/live/offer/route.ts +351 -0
- package/.next/types/app/api/live/voices/route.ts +351 -0
- package/.next/types/app/api/mcp/route.ts +351 -0
- package/.next/types/app/api/model-roles/route.ts +351 -0
- package/.next/types/app/api/models/route.ts +351 -0
- package/.next/types/app/api/models-config/catalog/route.ts +351 -0
- package/.next/types/app/api/models-config/discover/route.ts +351 -0
- package/.next/types/app/api/models-config/route.ts +351 -0
- package/.next/types/app/api/models-config/test/route.ts +351 -0
- package/.next/types/app/api/plugins/route.ts +351 -0
- package/.next/types/app/api/project-trust/route.ts +351 -0
- package/.next/types/app/api/projects/route.ts +351 -0
- package/.next/types/app/api/runs/[runId]/route.ts +351 -0
- package/.next/types/app/api/runs/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/auto-name/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/context/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/export/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/route.ts +351 -0
- package/.next/types/app/api/sessions/[id]/state/route.ts +351 -0
- package/.next/types/app/api/sessions/route.ts +351 -0
- package/.next/types/app/api/settings/route.ts +351 -0
- package/.next/types/app/api/sidecars/[service]/[...path]/route.ts +351 -0
- package/.next/types/app/api/skills/check/route.ts +351 -0
- package/.next/types/app/api/skills/install/route.ts +351 -0
- package/.next/types/app/api/skills/route.ts +351 -0
- package/.next/types/app/api/skills/search/route.ts +351 -0
- package/.next/types/app/api/skills/update/route.ts +351 -0
- package/.next/types/app/api/theme/route.ts +351 -0
- package/.next/types/app/api/update-maintenance/route.ts +351 -0
- package/.next/types/app/api/updates/route.ts +351 -0
- package/.next/types/app/api/web-access/recovery/route.ts +351 -0
- package/.next/types/app/api/web-access/route.ts +351 -0
- package/.next/types/app/api/web6/consults/route.ts +351 -0
- package/.next/types/app/api/worktrees/route.ts +351 -0
- package/.next/types/app/layout.ts +87 -0
- package/.next/types/app/page.ts +87 -0
- package/.next/types/app/recover/page.ts +87 -0
- package/.next/types/cache-life.d.ts +145 -0
- package/.next/types/package.json +1 -0
- package/.next/types/routes.d.ts +137 -0
- package/.next/types/validator.ts +655 -0
- package/LICENSE +22 -0
- package/README.md +174 -0
- package/Tools/CUELO_Setup/files/btw-server.js +343 -0
- package/Tools/CUELO_Setup/files/native-runtime-patch.js +349 -0
- package/Tools/CUELO_Setup/files/subagent-server.js +587 -0
- package/Tools/CUELO_Setup/files/usage-server.js +896 -0
- package/Tools/OMP_Global_Config/agent/AGENTS.md +122 -0
- package/Tools/OMP_Global_Config/agent/RULES.md +75 -0
- package/Tools/OMP_Global_Config/agent/WATCHDOG.md +55 -0
- package/Tools/OMP_Global_Config/agent/agents/maker.md +390 -0
- package/Tools/OMP_Global_Config/agent/extensions/character-voice.ts +718 -0
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/index.ts +899 -0
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/matcher.ts +1227 -0
- package/Tools/OMP_Global_Config/agent/extensions/command-guard/task-guard.ts +890 -0
- package/Tools/OMP_Global_Config/agent/extensions/jev-runtime.ts +1567 -0
- package/Tools/OMP_Global_Config/agent/extensions/lib/maker-routing.ts +1020 -0
- package/Tools/OMP_Global_Config/agent/extensions/lib/prepared-task.ts +204 -0
- package/Tools/OMP_Global_Config/agent/extensions/lib/routing-ledger.ts +289 -0
- package/Tools/OMP_Global_Config/agent/extensions/lib/task-progress.ts +492 -0
- package/Tools/OMP_Global_Config/agent/extensions/prompt-compact.ts +67 -0
- package/Tools/OMP_Global_Config/agent/extensions/steering-reply-gate.ts +92 -0
- package/Tools/OMP_Global_Config/agent/extensions/todo-nudge.ts +56 -0
- package/Tools/OMP_Global_Config/agent/extensions/turn-end-guard.ts +154 -0
- package/Tools/OMP_Global_Config/agent/prompt-copies/AGENTS.en.md +125 -0
- package/Tools/OMP_Global_Config/agent/prompt-copies/RULES.en.md +28 -0
- package/Tools/OMP_Global_Config/agent/rules/docs-handoff.md +36 -0
- package/Tools/OMP_Global_Config/agent/rules/frontend.md +63 -0
- package/Tools/OMP_Global_Config/agent/rules/harness-policy.json +1080 -0
- package/Tools/OMP_Global_Config/agent/rules/legacy-encoding.md +21 -0
- package/Tools/OMP_Global_Config/agent/rules/meta-harness.md +45 -0
- package/Tools/OMP_Global_Config/agent/rules/omp-harness.md +143 -0
- package/Tools/OMP_Global_Config/agent/rules/subagent.md +543 -0
- package/Tools/OMP_Global_Config/agent/rules/task-guard.md +54 -0
- package/Tools/OMP_Global_Config/agent/rules/verdict.md +183 -0
- package/Tools/OMP_Global_Config/agent/rules/web6-consult.md +84 -0
- package/Tools/OMP_Global_Config/agent/sop/_common.md +130 -0
- package/Tools/OMP_Global_Config/agent/sop/_writer.md +242 -0
- package/Tools/OMP_Global_Config/agent/sop/maker.md +9 -0
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/finalizer.ps1 +340 -0
- package/Tools/OMP_Global_Config/agent/tools/git-finalizer/index.ts +83 -0
- package/Tools/OMP_Global_Config/notices/orchestrate.md +26 -0
- package/Tools/OMP_Global_Config/notices/ultrathink.md +9 -0
- package/Tools/OMP_Global_Config/notices/workflow.md +16 -0
- package/Tools/OMP_Global_Config/patches/apply-core-patch.mjs +5005 -0
- package/Tools/OMP_Global_Config/patches/apply-notices.mjs +135 -0
- package/bin/cuelo-options.js +38 -0
- package/bin/cuelo.js +268 -0
- package/bin/password-prompt.js +100 -0
- package/bin/prepare-runtime.js +81 -0
- package/bin/runtime.js +134 -0
- package/bin/web-auth-store.d.ts +104 -0
- package/bin/web-auth-store.js +593 -0
- package/install.mjs +393 -0
- package/next.config.ts +96 -0
- package/package.json +134 -0
- package/public/audio/completion/isana/01.wav +0 -0
- package/public/audio/completion/isana/02.wav +0 -0
- package/public/audio/completion/isana/03.wav +0 -0
- package/public/audio/completion/isana/04.wav +0 -0
- package/public/audio/completion/isana/05.wav +0 -0
- package/public/audio/completion/isana/06.wav +0 -0
- package/public/audio/completion/isana/approval/01.wav +0 -0
- package/public/audio/completion/isana/approval/02.wav +0 -0
- package/public/audio/completion/isana/approval/03.wav +0 -0
- package/public/audio/completion/isana/choice/01.wav +0 -0
- package/public/audio/completion/isana/choice/02.wav +0 -0
- package/public/audio/completion/isana/choice/03.wav +0 -0
- package/public/audio/completion/isana/failed/01.wav +0 -0
- package/public/audio/completion/isana/failed/02.wav +0 -0
- package/public/audio/completion/isana/failed/03.wav +0 -0
- package/public/audio/completion/isana/working/01.wav +0 -0
- package/public/audio/completion/isana/working/02.wav +0 -0
- package/public/audio/completion/isana/working/03.wav +0 -0
- package/public/audio/completion/mio/01.wav +0 -0
- package/public/audio/completion/mio/02.wav +0 -0
- package/public/audio/completion/mio/03.wav +0 -0
- package/public/audio/completion/mio/04.wav +0 -0
- package/public/audio/completion/mio/05.wav +0 -0
- package/public/audio/completion/mio/06.wav +0 -0
- package/public/audio/completion/mio/approval/01.wav +0 -0
- package/public/audio/completion/mio/approval/02.wav +0 -0
- package/public/audio/completion/mio/approval/03.wav +0 -0
- package/public/audio/completion/mio/choice/01.wav +0 -0
- package/public/audio/completion/mio/choice/02.wav +0 -0
- package/public/audio/completion/mio/choice/03.wav +0 -0
- package/public/audio/completion/mio/failed/01.wav +0 -0
- package/public/audio/completion/mio/failed/02.wav +0 -0
- package/public/audio/completion/mio/failed/03.wav +0 -0
- package/public/audio/completion/mio/working/01.wav +0 -0
- package/public/audio/completion/mio/working/02.wav +0 -0
- package/public/audio/completion/mio/working/03.wav +0 -0
- package/public/audio/completion/nova/01.wav +0 -0
- package/public/audio/completion/nova/02.wav +0 -0
- package/public/audio/completion/nova/03.wav +0 -0
- package/public/audio/completion/nova/04.wav +0 -0
- package/public/audio/completion/nova/05.wav +0 -0
- package/public/audio/completion/nova/06.wav +0 -0
- package/public/audio/completion/nova/approval/01.wav +0 -0
- package/public/audio/completion/nova/approval/02.wav +0 -0
- package/public/audio/completion/nova/approval/03.wav +0 -0
- package/public/audio/completion/nova/choice/01.wav +0 -0
- package/public/audio/completion/nova/choice/02.wav +0 -0
- package/public/audio/completion/nova/choice/03.wav +0 -0
- package/public/audio/completion/nova/failed/01.wav +0 -0
- package/public/audio/completion/nova/failed/02.wav +0 -0
- package/public/audio/completion/nova/failed/03.wav +0 -0
- package/public/audio/completion/nova/working/01.wav +0 -0
- package/public/audio/completion/nova/working/02.wav +0 -0
- package/public/audio/completion/nova/working/03.wav +0 -0
- package/public/audio/completion/rin/01.wav +0 -0
- package/public/audio/completion/rin/02.wav +0 -0
- package/public/audio/completion/rin/03.wav +0 -0
- package/public/audio/completion/rin/04.wav +0 -0
- package/public/audio/completion/rin/05.wav +0 -0
- package/public/audio/completion/rin/06.wav +0 -0
- package/public/audio/completion/rin/approval/01.wav +0 -0
- package/public/audio/completion/rin/approval/02.wav +0 -0
- package/public/audio/completion/rin/approval/03.wav +0 -0
- package/public/audio/completion/rin/choice/01.wav +0 -0
- package/public/audio/completion/rin/choice/02.wav +0 -0
- package/public/audio/completion/rin/choice/03.wav +0 -0
- package/public/audio/completion/rin/failed/01.wav +0 -0
- package/public/audio/completion/rin/failed/02.wav +0 -0
- package/public/audio/completion/rin/failed/03.wav +0 -0
- package/public/audio/completion/rin/working/01.wav +0 -0
- package/public/audio/completion/rin/working/02.wav +0 -0
- package/public/audio/completion/rin/working/03.wav +0 -0
- package/public/audio/completion/shion/01.wav +0 -0
- package/public/audio/completion/shion/02.wav +0 -0
- package/public/audio/completion/shion/03.wav +0 -0
- package/public/audio/completion/shion/04.wav +0 -0
- package/public/audio/completion/shion/05.wav +0 -0
- package/public/audio/completion/shion/06.wav +0 -0
- package/public/audio/completion/shion/approval/01.wav +0 -0
- package/public/audio/completion/shion/approval/02.wav +0 -0
- package/public/audio/completion/shion/approval/03.wav +0 -0
- package/public/audio/completion/shion/choice/01.wav +0 -0
- package/public/audio/completion/shion/choice/02.wav +0 -0
- package/public/audio/completion/shion/choice/03.wav +0 -0
- package/public/audio/completion/shion/failed/01.wav +0 -0
- package/public/audio/completion/shion/failed/02.wav +0 -0
- package/public/audio/completion/shion/failed/03.wav +0 -0
- package/public/audio/completion/shion/working/01.wav +0 -0
- package/public/audio/completion/shion/working/02.wav +0 -0
- package/public/audio/completion/shion/working/03.wav +0 -0
- package/public/audio/completion/voices.json +1195 -0
- package/public/audio/completion/yuki/01.wav +0 -0
- package/public/audio/completion/yuki/02.wav +0 -0
- package/public/audio/completion/yuki/03.wav +0 -0
- package/public/audio/completion/yuki/04.wav +0 -0
- package/public/audio/completion/yuki/05.wav +0 -0
- package/public/audio/completion/yuki/06.wav +0 -0
- package/public/audio/completion/yuki/approval/01.wav +0 -0
- package/public/audio/completion/yuki/approval/02.wav +0 -0
- package/public/audio/completion/yuki/approval/03.wav +0 -0
- package/public/audio/completion/yuki/choice/01.wav +0 -0
- package/public/audio/completion/yuki/choice/02.wav +0 -0
- package/public/audio/completion/yuki/choice/03.wav +0 -0
- package/public/audio/completion/yuki/failed/01.wav +0 -0
- package/public/audio/completion/yuki/failed/02.wav +0 -0
- package/public/audio/completion/yuki/failed/03.wav +0 -0
- package/public/audio/completion/yuki/working/01.wav +0 -0
- package/public/audio/completion/yuki/working/02.wav +0 -0
- package/public/audio/completion/yuki/working/03.wav +0 -0
- package/public/avatars/isana.webp +0 -0
- package/public/avatars/mio.webp +0 -0
- package/public/avatars/nova.webp +0 -0
- package/public/avatars/rin.webp +0 -0
- package/public/avatars/shion.webp +0 -0
- package/public/avatars/yuki.webp +0 -0
- package/public/icons/apple-touch-icon.png +0 -0
- package/public/icons/catppuccin/LICENSE +22 -0
- package/public/icons/catppuccin/README.md +8 -0
- package/public/icons/catppuccin/latte/_file.svg +3 -0
- package/public/icons/catppuccin/latte/_folder.svg +3 -0
- package/public/icons/catppuccin/latte/_folder_open.svg +3 -0
- package/public/icons/catppuccin/latte/bash.svg +7 -0
- package/public/icons/catppuccin/latte/bun-lock.svg +8 -0
- package/public/icons/catppuccin/latte/config.svg +3 -0
- package/public/icons/catppuccin/latte/css.svg +6 -0
- package/public/icons/catppuccin/latte/database.svg +3 -0
- package/public/icons/catppuccin/latte/docker.svg +3 -0
- package/public/icons/catppuccin/latte/env.svg +3 -0
- package/public/icons/catppuccin/latte/eslint.svg +6 -0
- package/public/icons/catppuccin/latte/git.svg +6 -0
- package/public/icons/catppuccin/latte/go.svg +3 -0
- package/public/icons/catppuccin/latte/graphql.svg +3 -0
- package/public/icons/catppuccin/latte/html.svg +6 -0
- package/public/icons/catppuccin/latte/javascript-react.svg +8 -0
- package/public/icons/catppuccin/latte/javascript.svg +6 -0
- package/public/icons/catppuccin/latte/json.svg +3 -0
- package/public/icons/catppuccin/latte/lock.svg +3 -0
- package/public/icons/catppuccin/latte/markdown.svg +3 -0
- package/public/icons/catppuccin/latte/ms-word.svg +6 -0
- package/public/icons/catppuccin/latte/next.svg +6 -0
- package/public/icons/catppuccin/latte/npm-lock.svg +7 -0
- package/public/icons/catppuccin/latte/pdf.svg +3 -0
- package/public/icons/catppuccin/latte/python.svg +8 -0
- package/public/icons/catppuccin/latte/rust.svg +6 -0
- package/public/icons/catppuccin/latte/sass.svg +3 -0
- package/public/icons/catppuccin/latte/terraform.svg +3 -0
- package/public/icons/catppuccin/latte/toml.svg +3 -0
- package/public/icons/catppuccin/latte/typescript-react.svg +8 -0
- package/public/icons/catppuccin/latte/typescript.svg +6 -0
- package/public/icons/catppuccin/latte/yaml.svg +3 -0
- package/public/icons/catppuccin/mocha/_file.svg +3 -0
- package/public/icons/catppuccin/mocha/_folder.svg +3 -0
- package/public/icons/catppuccin/mocha/_folder_open.svg +3 -0
- package/public/icons/catppuccin/mocha/bash.svg +7 -0
- package/public/icons/catppuccin/mocha/bun-lock.svg +8 -0
- package/public/icons/catppuccin/mocha/config.svg +3 -0
- package/public/icons/catppuccin/mocha/css.svg +6 -0
- package/public/icons/catppuccin/mocha/database.svg +3 -0
- package/public/icons/catppuccin/mocha/docker.svg +3 -0
- package/public/icons/catppuccin/mocha/env.svg +3 -0
- package/public/icons/catppuccin/mocha/eslint.svg +6 -0
- package/public/icons/catppuccin/mocha/git.svg +6 -0
- package/public/icons/catppuccin/mocha/go.svg +3 -0
- package/public/icons/catppuccin/mocha/graphql.svg +3 -0
- package/public/icons/catppuccin/mocha/html.svg +6 -0
- package/public/icons/catppuccin/mocha/javascript-react.svg +8 -0
- package/public/icons/catppuccin/mocha/javascript.svg +6 -0
- package/public/icons/catppuccin/mocha/json.svg +3 -0
- package/public/icons/catppuccin/mocha/lock.svg +3 -0
- package/public/icons/catppuccin/mocha/markdown.svg +3 -0
- package/public/icons/catppuccin/mocha/ms-word.svg +6 -0
- package/public/icons/catppuccin/mocha/next.svg +6 -0
- package/public/icons/catppuccin/mocha/npm-lock.svg +7 -0
- package/public/icons/catppuccin/mocha/pdf.svg +3 -0
- package/public/icons/catppuccin/mocha/python.svg +8 -0
- package/public/icons/catppuccin/mocha/rust.svg +6 -0
- package/public/icons/catppuccin/mocha/sass.svg +3 -0
- package/public/icons/catppuccin/mocha/terraform.svg +3 -0
- package/public/icons/catppuccin/mocha/toml.svg +3 -0
- package/public/icons/catppuccin/mocha/typescript-react.svg +8 -0
- package/public/icons/catppuccin/mocha/typescript.svg +6 -0
- package/public/icons/catppuccin/mocha/yaml.svg +3 -0
- package/public/icons/icon-192.png +0 -0
- package/public/icons/icon-512.png +0 -0
- package/public/offline.html +145 -0
- package/public/stickers/isana/approval/01.webp +0 -0
- package/public/stickers/isana/approval/02.webp +0 -0
- package/public/stickers/isana/approval/03.webp +0 -0
- package/public/stickers/isana/choice/01.webp +0 -0
- package/public/stickers/isana/choice/02.webp +0 -0
- package/public/stickers/isana/choice/03.webp +0 -0
- package/public/stickers/isana/done/01.webp +0 -0
- package/public/stickers/isana/done/02.webp +0 -0
- package/public/stickers/isana/done/03.webp +0 -0
- package/public/stickers/isana/failed/01.webp +0 -0
- package/public/stickers/isana/failed/02.webp +0 -0
- package/public/stickers/isana/failed/03.webp +0 -0
- package/public/stickers/isana/working/01.webp +0 -0
- package/public/stickers/mio/approval/01.webp +0 -0
- package/public/stickers/mio/approval/02.webp +0 -0
- package/public/stickers/mio/approval/03.webp +0 -0
- package/public/stickers/mio/choice/01.webp +0 -0
- package/public/stickers/mio/choice/02.webp +0 -0
- package/public/stickers/mio/choice/03.webp +0 -0
- package/public/stickers/mio/done/01.webp +0 -0
- package/public/stickers/mio/done/02.webp +0 -0
- package/public/stickers/mio/done/03.webp +0 -0
- package/public/stickers/mio/failed/01.webp +0 -0
- package/public/stickers/mio/failed/02.webp +0 -0
- package/public/stickers/mio/failed/03.webp +0 -0
- package/public/stickers/mio/working/01.webp +0 -0
- package/public/stickers/nova/approval/01.webp +0 -0
- package/public/stickers/nova/approval/02.webp +0 -0
- package/public/stickers/nova/approval/03.webp +0 -0
- package/public/stickers/nova/choice/01.webp +0 -0
- package/public/stickers/nova/choice/02.webp +0 -0
- package/public/stickers/nova/choice/03.webp +0 -0
- package/public/stickers/nova/done/01.webp +0 -0
- package/public/stickers/nova/done/02.webp +0 -0
- package/public/stickers/nova/done/03.webp +0 -0
- package/public/stickers/nova/failed/01.webp +0 -0
- package/public/stickers/nova/failed/02.webp +0 -0
- package/public/stickers/nova/failed/03.webp +0 -0
- package/public/stickers/nova/working/01.webp +0 -0
- package/public/stickers/rin/approval/01.webp +0 -0
- package/public/stickers/rin/approval/02.webp +0 -0
- package/public/stickers/rin/approval/03.webp +0 -0
- package/public/stickers/rin/choice/01.webp +0 -0
- package/public/stickers/rin/choice/02.webp +0 -0
- package/public/stickers/rin/choice/03.webp +0 -0
- package/public/stickers/rin/done/01.webp +0 -0
- package/public/stickers/rin/done/02.webp +0 -0
- package/public/stickers/rin/done/03.webp +0 -0
- package/public/stickers/rin/failed/01.webp +0 -0
- package/public/stickers/rin/failed/02.webp +0 -0
- package/public/stickers/rin/failed/03.webp +0 -0
- package/public/stickers/rin/working/01.webp +0 -0
- package/public/stickers/shion/approval/01.webp +0 -0
- package/public/stickers/shion/approval/02.webp +0 -0
- package/public/stickers/shion/approval/03.webp +0 -0
- package/public/stickers/shion/choice/01.webp +0 -0
- package/public/stickers/shion/choice/02.webp +0 -0
- package/public/stickers/shion/choice/03.webp +0 -0
- package/public/stickers/shion/done/01.webp +0 -0
- package/public/stickers/shion/done/02.webp +0 -0
- package/public/stickers/shion/done/03.webp +0 -0
- package/public/stickers/shion/failed/01.webp +0 -0
- package/public/stickers/shion/failed/02.webp +0 -0
- package/public/stickers/shion/failed/03.webp +0 -0
- package/public/stickers/shion/working/01.webp +0 -0
- package/public/stickers/yuki/approval/01.webp +0 -0
- package/public/stickers/yuki/approval/02.webp +0 -0
- package/public/stickers/yuki/approval/03.webp +0 -0
- package/public/stickers/yuki/choice/01.webp +0 -0
- package/public/stickers/yuki/choice/02.webp +0 -0
- package/public/stickers/yuki/choice/03.webp +0 -0
- package/public/stickers/yuki/done/01.webp +0 -0
- package/public/stickers/yuki/done/02.webp +0 -0
- package/public/stickers/yuki/done/03.webp +0 -0
- package/public/stickers/yuki/failed/01.webp +0 -0
- package/public/stickers/yuki/failed/02.webp +0 -0
- package/public/stickers/yuki/failed/03.webp +0 -0
- package/public/stickers/yuki/working/01.webp +0 -0
- package/public/sw.js +124 -0
|
@@ -0,0 +1,1567 @@
|
|
|
1
|
+
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent";
|
|
3
|
+
import { registerMakerRouting } from "./lib/maker-routing";
|
|
4
|
+
import { clearPreparedTaskSession } from "./lib/prepared-task";
|
|
5
|
+
import { createRoutingLedger, DEFAULT_LEDGER_PATH, scopedAttempts, type AttemptIdentity, type OutcomeRecord, type VerdictRecord } from "./lib/routing-ledger";
|
|
6
|
+
import {
|
|
7
|
+
advanceTodoProgressState,
|
|
8
|
+
assessTodoProgress,
|
|
9
|
+
captureTodoBindings,
|
|
10
|
+
createTodoProgressState,
|
|
11
|
+
readPersistedTodoProgress,
|
|
12
|
+
readTaskProgressMetadata,
|
|
13
|
+
readTerminalValidation,
|
|
14
|
+
readTodoSnapshot,
|
|
15
|
+
type BoundTodoSnapshotItem,
|
|
16
|
+
type TaskProgressMetadata,
|
|
17
|
+
type TerminalValidationItem,
|
|
18
|
+
type TodoProgressAssessment,
|
|
19
|
+
type TodoProgressState,
|
|
20
|
+
} from "./lib/task-progress";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Jev 실행 경계. 발주 전에는 maker_route가 사실 요약을 한 번 판정하고
|
|
24
|
+
* Main이 결과를 읽은 뒤 선택한 selector를 task에 전달한다. task hook은
|
|
25
|
+
* 준비된 판단의 유효성만 확인하며 모델 선택·spawn·추가 Jev 호출을 하지 않는다.
|
|
26
|
+
* 재시도와 결과 수신의 기존 advisory는 의미를 관측할 수 없는 항목을 명시한다.
|
|
27
|
+
* 권한·TaskBudget·최종 수용은 기존 owner와 guard가 그대로 책임진다.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// 최소 타입 — runtime 값은 지연 import로 해석하고, 타입은 로컬에 둔다.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
interface JudgeQuestionBool {
|
|
35
|
+
type: "noul";
|
|
36
|
+
instructions: string;
|
|
37
|
+
criteria?: { true?: string; false?: string };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface JudgeQuestionChoice {
|
|
41
|
+
type: "choice";
|
|
42
|
+
instructions: string;
|
|
43
|
+
criteria: Record<string, string | null>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type JudgeQuestion = JudgeQuestionBool | JudgeQuestionChoice;
|
|
47
|
+
|
|
48
|
+
type JudgeAnswer =
|
|
49
|
+
| { type: "noul"; noul: number }
|
|
50
|
+
| { type: "choice"; choice: string; probabilities: Record<string, number>; confidence: number }
|
|
51
|
+
| { type: "score"; score: number; probabilities: Record<string, number>; confidence: number };
|
|
52
|
+
|
|
53
|
+
interface JudgeLike {
|
|
54
|
+
readonly label: string;
|
|
55
|
+
judge(
|
|
56
|
+
request: { state: unknown; questions: Record<string, JudgeQuestion> },
|
|
57
|
+
options?: { signal?: AbortSignal },
|
|
58
|
+
): Promise<{ answers: Record<string, JudgeAnswer>; provider: string; model: string }>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface JudgeSettingsLike {
|
|
62
|
+
getModelRoles(): Readonly<Record<string, string>>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface JudgeDepsLike {
|
|
66
|
+
settings: JudgeSettingsLike;
|
|
67
|
+
registry: unknown;
|
|
68
|
+
backend: string;
|
|
69
|
+
sessionModel?: unknown;
|
|
70
|
+
sessionId?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type ResolveJudgeFn = (deps: JudgeDepsLike) => JudgeLike;
|
|
74
|
+
type FindScopedSettingsFn = (cwd?: string) => JudgeSettingsLike | undefined;
|
|
75
|
+
|
|
76
|
+
/** 테스트가 실제 SDK 없이 judge를 주입할 수 있게 하는 seam. */
|
|
77
|
+
export interface JevRuntimeDeps {
|
|
78
|
+
resolveJudge?: ResolveJudgeFn;
|
|
79
|
+
findScopedSettings?: FindScopedSettingsFn;
|
|
80
|
+
/** 발주 이력 JSON Lines 경로. 테스트는 임시 경로를 주입한다. 기본은 실행 프로필 agent 루트다. */
|
|
81
|
+
ledgerPath?: string;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// 정본 설정 키 — 값은 Settings에서 읽고 상수로 복제하지 않는다.
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
|
|
88
|
+
/** resolveJudge의 backend 계약값. SDK의 ONLINE_MEMORY_MODEL_KEY("online")와 동일하다. */
|
|
89
|
+
const ONLINE_JUDGE_BACKEND = "online";
|
|
90
|
+
const ADVISORY_CUSTOM_TYPE = "jev-runtime-advisory";
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// TASK_GUARD 메타데이터 추출 — 본문은 judge에 보내지 않고 필드만 읽는다.
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
interface TaskGuardMeta {
|
|
99
|
+
workClass?: string;
|
|
100
|
+
purpose?: string;
|
|
101
|
+
/** Main이 적은 1줄 완료물 — duplicate/effort 판정의 최소 의미 근거. */
|
|
102
|
+
primaryDeliverable?: string;
|
|
103
|
+
/** 소유 경로 목록 — 종류와 개수가 중복 판정의 관측 근거다. */
|
|
104
|
+
ownedPaths: string[];
|
|
105
|
+
hasFindingId: boolean;
|
|
106
|
+
hasExplicitCheck: boolean;
|
|
107
|
+
progress: TaskProgressMetadata;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function readGuardField(block: string, name: string): string | undefined {
|
|
111
|
+
const match = block.match(new RegExp(`^\\s*${name}\\s*:\\s*(.+?)\\s*$`, "im"));
|
|
112
|
+
const value = match?.[1]?.trim();
|
|
113
|
+
return value || undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractTaskGuardMeta(task: string): TaskGuardMeta {
|
|
117
|
+
const marker = /^\s*TASK_GUARD\s*:\s*$/im.exec(task);
|
|
118
|
+
const progress = readTaskProgressMetadata(task);
|
|
119
|
+
const meta: TaskGuardMeta = {
|
|
120
|
+
ownedPaths: [],
|
|
121
|
+
hasFindingId: false,
|
|
122
|
+
hasExplicitCheck: false,
|
|
123
|
+
progress,
|
|
124
|
+
};
|
|
125
|
+
if (marker) {
|
|
126
|
+
const tailStart = marker.index + marker[0].length;
|
|
127
|
+
const tail = task.slice(tailStart);
|
|
128
|
+
const separator = /\r?\n\s*\r?\n/.exec(tail);
|
|
129
|
+
const blockEnd = separator ? tailStart + separator.index : task.length;
|
|
130
|
+
const block = task.slice(tailStart, blockEnd);
|
|
131
|
+
const workClass = readGuardField(block, "WORK_CLASS");
|
|
132
|
+
const purpose = readGuardField(block, "PURPOSE");
|
|
133
|
+
if (workClass) meta.workClass = workClass.toLowerCase();
|
|
134
|
+
if (purpose) meta.purpose = purpose.toLowerCase();
|
|
135
|
+
const primaryDeliverable = readGuardField(block, "PRIMARY_DELIVERABLE");
|
|
136
|
+
if (primaryDeliverable) meta.primaryDeliverable = primaryDeliverable;
|
|
137
|
+
const ownedPaths = readGuardField(block, "OWNED_PATHS");
|
|
138
|
+
if (ownedPaths) {
|
|
139
|
+
meta.ownedPaths = ownedPaths
|
|
140
|
+
.split(",")
|
|
141
|
+
.map((entry) => entry.trim())
|
|
142
|
+
.filter((entry) => entry.length > 0);
|
|
143
|
+
}
|
|
144
|
+
meta.hasFindingId = readGuardField(block, "FINDING_ID") !== undefined;
|
|
145
|
+
}
|
|
146
|
+
// 명시 검증 명령 플래그: brief 본문의 검사 지시 존재 여부만 본다.
|
|
147
|
+
meta.hasExplicitCheck = /^\s*(검사\s*명령|CHECK(?:\s+COMMAND)?)\s*:/im.test(task);
|
|
148
|
+
return meta;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
interface SpawnedTaskMeta {
|
|
152
|
+
/** tasks[] 배열 위치 — tool_result progress row의 index와 대응한다. */
|
|
153
|
+
index: number;
|
|
154
|
+
name?: string;
|
|
155
|
+
agent?: string;
|
|
156
|
+
guard: TaskGuardMeta;
|
|
157
|
+
/** Spawn 시점의 exact TodoTracker incarnation. 늦은 결과는 이 identity로 구별한다. */
|
|
158
|
+
todoBindings: ReadonlyMap<string, number>;
|
|
159
|
+
/** Spawn 시점에 캡처한 발주 identity. settle outcome은 name 조회가 아니라 이 값으로 잇는다. */
|
|
160
|
+
identity?: AttemptIdentity;
|
|
161
|
+
/** spawn progress row의 canonical child id(= agent:// target). */
|
|
162
|
+
agentId?: string;
|
|
163
|
+
/** 그 spawn이 등록된 async jobId. details가 단일 spawn의 jobId를 줄 때만 채운다. */
|
|
164
|
+
jobId?: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function extractSpawnedTasks(
|
|
168
|
+
input: Record<string, unknown>,
|
|
169
|
+
currentTodos: readonly BoundTodoSnapshotItem[],
|
|
170
|
+
): SpawnedTaskMeta[] {
|
|
171
|
+
const items: unknown[] = Array.isArray(input.tasks)
|
|
172
|
+
? input.tasks
|
|
173
|
+
: typeof input.task === "string"
|
|
174
|
+
? [input]
|
|
175
|
+
: [];
|
|
176
|
+
const tasks: SpawnedTaskMeta[] = [];
|
|
177
|
+
for (const [index, raw] of items.entries()) {
|
|
178
|
+
if (!raw || typeof raw !== "object") continue;
|
|
179
|
+
const item = raw as Record<string, unknown>;
|
|
180
|
+
const taskText = typeof item.task === "string" ? item.task : "";
|
|
181
|
+
const guard = extractTaskGuardMeta(taskText);
|
|
182
|
+
tasks.push({
|
|
183
|
+
index,
|
|
184
|
+
...(typeof item.name === "string" && item.name.trim() ? { name: item.name.trim() } : {}),
|
|
185
|
+
...(typeof item.agent === "string" && item.agent.trim()
|
|
186
|
+
? { agent: item.agent.trim().toLowerCase() }
|
|
187
|
+
: {}),
|
|
188
|
+
// 현재/이전 effort는 블라인드 판정을 깨므로 state에 싣지 않는다.
|
|
189
|
+
guard,
|
|
190
|
+
todoBindings: captureTodoBindings(guard.progress, currentTodos),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return tasks;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// 재시도 입력 비교 — 내용은 judge에 보내지 않고 로컬 직렬화 동일성만 본다.
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
function serializeInput(value: unknown): string {
|
|
201
|
+
try {
|
|
202
|
+
return JSON.stringify(value) ?? "";
|
|
203
|
+
} catch {
|
|
204
|
+
return "";
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// 오류 카테고리 — toolResult 원문은 보내지 않고 분류 라벨만 만든다.
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
|
|
212
|
+
const ERROR_CATEGORY_PATTERNS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
213
|
+
[/ENOENT|no such file|cannot find|not found|존재하지 않/iu, "missing-path"],
|
|
214
|
+
[/EACCES|EPERM|permission denied|access is denied|권한/iu, "permission"],
|
|
215
|
+
[/401|403|unauthorized|forbidden|invalid[_ ]?grant|api[_ ]?key|authentication|인증/iu, "auth"],
|
|
216
|
+
[/command (?:aborted|cancelled)|작업 취소|명령 취소/iu, "cancelled"],
|
|
217
|
+
[/ETIMEDOUT|ECONNREFUSED|ECONNRESET|ENOTFOUND|EAI_AGAIN|network|socket|fetch failed|timed? ?out/iu, "network"],
|
|
218
|
+
[/exit code|exit status|command failed|failed with|exit1|exit 1/iu, "exit-status"],
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
function textContent(content: unknown): string {
|
|
222
|
+
return Array.isArray(content)
|
|
223
|
+
? content
|
|
224
|
+
.flatMap((part) =>
|
|
225
|
+
part && typeof part === "object" && "type" in part && part.type === "text" &&
|
|
226
|
+
"text" in part && typeof part.text === "string"
|
|
227
|
+
? [part.text]
|
|
228
|
+
: [],
|
|
229
|
+
)
|
|
230
|
+
.join("\n")
|
|
231
|
+
: typeof content === "string"
|
|
232
|
+
? content
|
|
233
|
+
: "";
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function classifyError(content: unknown): string {
|
|
237
|
+
const text = textContent(content);
|
|
238
|
+
for (const [pattern, category] of ERROR_CATEGORY_PATTERNS) {
|
|
239
|
+
if (pattern.test(text)) return category;
|
|
240
|
+
}
|
|
241
|
+
return "other";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface FailureObservation {
|
|
245
|
+
deterministicExitObserved: boolean;
|
|
246
|
+
executionObservationPresent: boolean;
|
|
247
|
+
cancelled: boolean;
|
|
248
|
+
timedOut: boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function readFailureObservation(details: unknown, content: unknown): FailureObservation {
|
|
252
|
+
const record = details && typeof details === "object" && !Array.isArray(details)
|
|
253
|
+
? details as Record<string, unknown>
|
|
254
|
+
: {};
|
|
255
|
+
const text = textContent(content);
|
|
256
|
+
return {
|
|
257
|
+
deterministicExitObserved: typeof record.exitCode === "number",
|
|
258
|
+
executionObservationPresent: typeof record.executionObservation === "string",
|
|
259
|
+
cancelled: /command (?:aborted|cancelled)|작업 취소|명령 취소/iu.test(text),
|
|
260
|
+
timedOut: record.timedOut === true,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// settled task job 추출 — async-result와 `wait`·`read proc://`의 details jobs[] 공통.
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
interface SettledJobMeta {
|
|
270
|
+
jobId: string;
|
|
271
|
+
/** canonical child id(= agent:// target). settled job이 싣지 않으면 undefined이며 추측으로 채우지 않는다. */
|
|
272
|
+
agentId?: string;
|
|
273
|
+
label?: string;
|
|
274
|
+
durationMs?: number;
|
|
275
|
+
/** async job 상태(completed|failed|cancelled). */
|
|
276
|
+
status?: string;
|
|
277
|
+
schemaStatus?: string;
|
|
278
|
+
hasData: boolean;
|
|
279
|
+
/** data.validation 맵에서 state가 unverified/pending 계열인 항목 수. */
|
|
280
|
+
unverifiedCount?: number;
|
|
281
|
+
/** data.unresolved 배열 길이. */
|
|
282
|
+
unresolvedCount?: number;
|
|
283
|
+
hasError: boolean;
|
|
284
|
+
terminalRevision?: string;
|
|
285
|
+
validation: Record<string, TerminalValidationItem>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
interface TerminalReportMeta {
|
|
289
|
+
revision?: string;
|
|
290
|
+
validation: Record<string, TerminalValidationItem>;
|
|
291
|
+
}
|
|
292
|
+
interface ReviewStructuralReport {
|
|
293
|
+
jobId: string;
|
|
294
|
+
evidenceLocators: string[];
|
|
295
|
+
schemaStatus: string | null;
|
|
296
|
+
hasData: boolean;
|
|
297
|
+
unverifiedCount: number | null;
|
|
298
|
+
unresolvedCount: number | null;
|
|
299
|
+
hasError: boolean;
|
|
300
|
+
terminalRevisionPresent: boolean;
|
|
301
|
+
todoCoverageObserved: boolean;
|
|
302
|
+
todoMetadataValid: boolean | null;
|
|
303
|
+
todoTaskTitleValid: boolean | null;
|
|
304
|
+
todoExpectedCount: number | null;
|
|
305
|
+
todoCurrentMissingCount: number | null;
|
|
306
|
+
todoDuplicateCurrentCount: number | null;
|
|
307
|
+
todoStaleBindingCount: number | null;
|
|
308
|
+
todoUnboundBindingCount: number | null;
|
|
309
|
+
todoValidationCoveredCount: number | null;
|
|
310
|
+
todoValidationMetCount: number | null;
|
|
311
|
+
todoValidationEvidenceMissingCount: number | null;
|
|
312
|
+
todoValidationWaitingCount: number | null;
|
|
313
|
+
todoReadyForMainAcceptanceCount: number | null;
|
|
314
|
+
todoMainAcceptedCount: number | null;
|
|
315
|
+
todoUnattributedCompletedCount: number | null;
|
|
316
|
+
todoPartialCompletion: boolean | null;
|
|
317
|
+
todoReworkLinked: boolean | null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
interface StructuralCountObservation {
|
|
321
|
+
locator: string;
|
|
322
|
+
value: number | null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
function readTerminalReport(data: unknown, resultText: string): TerminalReportMeta {
|
|
327
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
328
|
+
const record = data as Record<string, unknown>;
|
|
329
|
+
return {
|
|
330
|
+
...(typeof record.revision === "string" && record.revision.trim()
|
|
331
|
+
? { revision: record.revision.trim() }
|
|
332
|
+
: {}),
|
|
333
|
+
validation: readTerminalValidation(record),
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const envelope =
|
|
337
|
+
/<output>\s*([\s\S]*?)\s*<\/output>/i.exec(resultText)?.[1] ??
|
|
338
|
+
/<preview\b[^>]*>\s*([\s\S]*?)\s*<\/preview>/i.exec(resultText)?.[1] ??
|
|
339
|
+
resultText;
|
|
340
|
+
const trimmed = envelope.trim().replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
|
|
341
|
+
try {
|
|
342
|
+
const parsed = JSON.parse(trimmed);
|
|
343
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
344
|
+
const record = parsed as Record<string, unknown>;
|
|
345
|
+
return {
|
|
346
|
+
...(typeof record.revision === "string" && record.revision.trim()
|
|
347
|
+
? { revision: record.revision.trim() }
|
|
348
|
+
: {}),
|
|
349
|
+
validation: readTerminalValidation(record),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
// 5KB 초과 task preview는 JSON 뒷부분이 잘릴 수 있다. 완결된 revision만 복구한다.
|
|
354
|
+
}
|
|
355
|
+
const revisionMatch = /"revision"\s*:\s*("(?:\\.|[^"\\])*")/.exec(trimmed);
|
|
356
|
+
let revision: string | undefined;
|
|
357
|
+
if (revisionMatch?.[1]) {
|
|
358
|
+
try {
|
|
359
|
+
const parsed = JSON.parse(revisionMatch[1]);
|
|
360
|
+
if (typeof parsed === "string" && parsed.trim()) revision = parsed.trim();
|
|
361
|
+
} catch {
|
|
362
|
+
// 잘린 JSON string은 revision 증거로 쓰지 않는다.
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
...(revision ? { revision } : {}),
|
|
367
|
+
validation: {},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function readSettledTaskJobs(details: unknown, content?: unknown): SettledJobMeta[] {
|
|
372
|
+
const jobs = (details as { jobs?: unknown } | undefined)?.jobs;
|
|
373
|
+
if (!Array.isArray(jobs)) return [];
|
|
374
|
+
const resultBlocks = [...textContent(content).matchAll(/<task-result\b[\s\S]*?<\/task-result>/gi)]
|
|
375
|
+
.map((match) => match[0]);
|
|
376
|
+
const settled: SettledJobMeta[] = [];
|
|
377
|
+
let resultBlockIndex = 0;
|
|
378
|
+
for (const job of jobs) {
|
|
379
|
+
if (!job || typeof job !== "object") continue;
|
|
380
|
+
const record = job as Record<string, unknown>;
|
|
381
|
+
if (record.type !== undefined && record.type !== "task") continue;
|
|
382
|
+
if (record.status === "running" || record.status === "pending") continue;
|
|
383
|
+
const jobId =
|
|
384
|
+
typeof record.jobId === "string"
|
|
385
|
+
? record.jobId
|
|
386
|
+
: typeof record.id === "string"
|
|
387
|
+
? record.id
|
|
388
|
+
: undefined;
|
|
389
|
+
if (!jobId) continue;
|
|
390
|
+
const schema = (record.schema ?? record.structured) as Record<string, unknown> | undefined;
|
|
391
|
+
const data = schema?.data;
|
|
392
|
+
const resultText =
|
|
393
|
+
typeof record.resultText === "string"
|
|
394
|
+
? record.resultText
|
|
395
|
+
: resultBlocks[resultBlockIndex] ?? (jobs.length === 1 ? textContent(content) : "");
|
|
396
|
+
resultBlockIndex += 1;
|
|
397
|
+
const terminal = readTerminalReport(data, resultText);
|
|
398
|
+
const meta: SettledJobMeta = {
|
|
399
|
+
jobId,
|
|
400
|
+
...(typeof record.agentId === "string" && record.agentId ? { agentId: record.agentId } : {}),
|
|
401
|
+
...(typeof record.label === "string" ? { label: record.label } : {}),
|
|
402
|
+
...(typeof record.durationMs === "number" ? { durationMs: record.durationMs } : {}),
|
|
403
|
+
...(typeof record.status === "string" ? { status: record.status } : {}),
|
|
404
|
+
...(typeof schema?.status === "string" ? { schemaStatus: schema.status } : {}),
|
|
405
|
+
hasData: data !== undefined,
|
|
406
|
+
hasError: typeof schema?.error === "string" || record.status === "failed",
|
|
407
|
+
...(terminal.revision ? { terminalRevision: terminal.revision } : {}),
|
|
408
|
+
validation: terminal.validation,
|
|
409
|
+
};
|
|
410
|
+
const validationEntries = Object.values(terminal.validation);
|
|
411
|
+
if (validationEntries.length > 0) {
|
|
412
|
+
meta.unverifiedCount = validationEntries.filter(
|
|
413
|
+
(entry) => entry.state !== "met" || !entry.evidencePresent,
|
|
414
|
+
).length;
|
|
415
|
+
}
|
|
416
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
417
|
+
const unresolved = (data as Record<string, unknown>).unresolved;
|
|
418
|
+
if (Array.isArray(unresolved)) meta.unresolvedCount = unresolved.length;
|
|
419
|
+
}
|
|
420
|
+
settled.push(meta);
|
|
421
|
+
}
|
|
422
|
+
return settled;
|
|
423
|
+
}
|
|
424
|
+
function renderStructuralCount(
|
|
425
|
+
label: string,
|
|
426
|
+
observations: StructuralCountObservation[],
|
|
427
|
+
): string {
|
|
428
|
+
if (observations.length === 0) return `${label}=해당 없음`;
|
|
429
|
+
const observed = observations.filter((entry) => entry.value !== null);
|
|
430
|
+
const unobserved = observations.filter((entry) => entry.value === null);
|
|
431
|
+
if (observed.length === 0) {
|
|
432
|
+
return `${label}=미관측(${unobserved.length}건:${unobserved.map((entry) => entry.locator).join(",")})`;
|
|
433
|
+
}
|
|
434
|
+
const total = observed.reduce((sum, entry) => sum + (entry.value ?? 0), 0);
|
|
435
|
+
const unobservedSuffix = unobserved.length > 0
|
|
436
|
+
? `+미관측(${unobserved.length}건:${unobserved.map((entry) => entry.locator).join(",")})`
|
|
437
|
+
: "";
|
|
438
|
+
return `${label}=${total}${unobservedSuffix}`;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function summarizeStructuralReports(reports: ReviewStructuralReport[]) {
|
|
442
|
+
return {
|
|
443
|
+
details: reports.map((report) => {
|
|
444
|
+
let normalCount = 0;
|
|
445
|
+
const problem: Record<string, unknown> = {};
|
|
446
|
+
const unobserved: string[] = [];
|
|
447
|
+
const recordCount = (key: string, value: number | null, positiveIsProblem: boolean): void => {
|
|
448
|
+
if (value === null) {
|
|
449
|
+
unobserved.push(key);
|
|
450
|
+
} else if (positiveIsProblem && value > 0) {
|
|
451
|
+
problem[key] = value;
|
|
452
|
+
} else {
|
|
453
|
+
normalCount += 1;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
if (report.schemaStatus === null) unobserved.push("schemaStatus");
|
|
458
|
+
else if (report.schemaStatus === "valid") normalCount += 1;
|
|
459
|
+
else problem.schemaStatus = report.schemaStatus;
|
|
460
|
+
if (report.hasData) normalCount += 1;
|
|
461
|
+
else problem.hasData = false;
|
|
462
|
+
if (report.hasError) problem.hasError = true;
|
|
463
|
+
else normalCount += 1;
|
|
464
|
+
if (report.terminalRevisionPresent) normalCount += 1;
|
|
465
|
+
else problem.terminalRevisionPresent = false;
|
|
466
|
+
recordCount("unverifiedCount", report.unverifiedCount, true);
|
|
467
|
+
recordCount("unresolvedCount", report.unresolvedCount, true);
|
|
468
|
+
normalCount += 1;
|
|
469
|
+
|
|
470
|
+
if (report.todoCoverageObserved) {
|
|
471
|
+
if (report.todoMetadataValid === null) unobserved.push("todoMetadataValid");
|
|
472
|
+
else if (report.todoMetadataValid) normalCount += 1;
|
|
473
|
+
else problem.todoMetadataValid = false;
|
|
474
|
+
if (report.todoTaskTitleValid === null) unobserved.push("todoTaskTitleValid");
|
|
475
|
+
else if (report.todoTaskTitleValid) normalCount += 1;
|
|
476
|
+
else problem.todoTaskTitleValid = false;
|
|
477
|
+
const todoIssueCountKeys = [
|
|
478
|
+
"todoCurrentMissingCount",
|
|
479
|
+
"todoDuplicateCurrentCount",
|
|
480
|
+
"todoStaleBindingCount",
|
|
481
|
+
"todoUnboundBindingCount",
|
|
482
|
+
"todoValidationEvidenceMissingCount",
|
|
483
|
+
"todoValidationWaitingCount",
|
|
484
|
+
"todoUnattributedCompletedCount",
|
|
485
|
+
] as const;
|
|
486
|
+
for (const key of todoIssueCountKeys) recordCount(key, report[key], true);
|
|
487
|
+
const todoInformationalCountKeys = [
|
|
488
|
+
"todoExpectedCount",
|
|
489
|
+
"todoValidationCoveredCount",
|
|
490
|
+
"todoValidationMetCount",
|
|
491
|
+
"todoReadyForMainAcceptanceCount",
|
|
492
|
+
"todoMainAcceptedCount",
|
|
493
|
+
] as const;
|
|
494
|
+
for (const key of todoInformationalCountKeys) recordCount(key, report[key], false);
|
|
495
|
+
if (report.todoPartialCompletion === null) unobserved.push("todoPartialCompletion");
|
|
496
|
+
else if (report.todoPartialCompletion) problem.todoPartialCompletion = true;
|
|
497
|
+
else normalCount += 1;
|
|
498
|
+
if (report.todoReworkLinked === null) unobserved.push("todoReworkLinked");
|
|
499
|
+
else normalCount += 1;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
locator: report.jobId,
|
|
504
|
+
normalCount,
|
|
505
|
+
problem,
|
|
506
|
+
unobserved,
|
|
507
|
+
evidenceLocators: report.evidenceLocators,
|
|
508
|
+
};
|
|
509
|
+
}),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// ---------------------------------------------------------------------------
|
|
514
|
+
// advisory 렌더링
|
|
515
|
+
// ---------------------------------------------------------------------------
|
|
516
|
+
|
|
517
|
+
function formatAnswer(id: string, answer: JudgeAnswer | undefined): string {
|
|
518
|
+
if (!answer) return `${id}=?`;
|
|
519
|
+
if (answer.type === "noul") return `${id}=${answer.noul >= 0.5 ? "true" : "false"}(${answer.noul.toFixed(2)})`;
|
|
520
|
+
if (answer.type === "choice") return `${id}=${answer.choice}(${answer.confidence.toFixed(2)})`;
|
|
521
|
+
return `${id}=${answer.score.toFixed(2)}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function renderAdvisory(
|
|
525
|
+
placement: string,
|
|
526
|
+
answers: Record<string, JudgeAnswer> | undefined,
|
|
527
|
+
unobservable: readonly string[],
|
|
528
|
+
note: string,
|
|
529
|
+
): string {
|
|
530
|
+
const lines = [`[JevRuntime:${placement}] ${note}`];
|
|
531
|
+
if (answers) {
|
|
532
|
+
const rendered = Object.keys(answers).map((id) => formatAnswer(id, answers[id]));
|
|
533
|
+
lines.push(`판정: ${rendered.join(" ") || "없음"}`);
|
|
534
|
+
}
|
|
535
|
+
if (unobservable.length > 0) {
|
|
536
|
+
lines.push(`관측 불가(판정 생략): ${unobservable.join(", ")}`);
|
|
537
|
+
}
|
|
538
|
+
lines.push(
|
|
539
|
+
"이 advisory는 참고용이다. 권한·TaskBudget·최종 수용·guard를 대체하지 않으며, " +
|
|
540
|
+
"모델이 이 메시지를 읽는 시점은 다음 continuation이다.",
|
|
541
|
+
);
|
|
542
|
+
return lines.join("\n");
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ---------------------------------------------------------------------------
|
|
546
|
+
// 미회신 조향 체크포인트 — known maker의 편집 전 체크포인트(`wait` 결과 details.waited 또는
|
|
547
|
+
// `irc:incoming` 주입)를 받은 뒤 Main이 그 Maker에게 `write agent://<id>`로 답하지 않은 채 다시
|
|
548
|
+
// wait하면 알린다. Maker는 답이 올 때까지 트리거 편집만 보류하고 독립 작업을 계속한다.
|
|
549
|
+
// 18.3.0 `write agent://`는 replyTo를 싣지 못하므로 수신 뒤 같은 Maker로 가는 첫 DM을 답으로 본다.
|
|
550
|
+
// 2026-09-24 실측: 대규모 위임에서 미회신 체크포인트 2통이 Maker를 합계 510초 멈췄다.
|
|
551
|
+
// ---------------------------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
const CHECKPOINT_LOCATION = /(?:^|\n)\s*(?:위치|LOCATION)\s*:/;
|
|
554
|
+
const CHECKPOINT_INVARIANTS = /(?:^|\n)\s*(?:불변식|INVARIANTS)\s*:/;
|
|
555
|
+
const AGENT_URL = /^agent:\/\/([^/?#]+)$/;
|
|
556
|
+
const PROC_KILL_URL = /^proc:\/\/([^/?#]+)\/kill$/;
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* IRC 메시지 레코드(`wait` details.waited는 body, `irc:incoming` details는 message)가 체크포인트
|
|
560
|
+
* 형식이면 {id, from}을 돌려준다. wake relay는 Maker의 DM이 아니라 제외한다.
|
|
561
|
+
*/
|
|
562
|
+
function readIncomingCheckpoint(record: unknown): { id: string; from: string } | undefined {
|
|
563
|
+
if (!record || typeof record !== "object") return undefined;
|
|
564
|
+
const { id, from, body, message, wakeRelay } = record as Record<string, unknown>;
|
|
565
|
+
if (wakeRelay === true || typeof id !== "string" || typeof from !== "string") return undefined;
|
|
566
|
+
const text = typeof body === "string" ? body : typeof message === "string" ? message : "";
|
|
567
|
+
return CHECKPOINT_LOCATION.test(text) && CHECKPOINT_INVARIANTS.test(text) ? { id, from } : undefined;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/** 모양을 모르는 레코드의 한 필드. 값 검증은 호출부가 한다. */
|
|
571
|
+
function fieldOf(value: unknown, key: string): unknown {
|
|
572
|
+
return value !== null && typeof value === "object" ? Reflect.get(value, key) : undefined;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// ---------------------------------------------------------------------------
|
|
576
|
+
// pre-retry 대상 도구 — 탐색 도구의 실패는 일상적 probing이라 판정하지 않는다.
|
|
577
|
+
// ---------------------------------------------------------------------------
|
|
578
|
+
|
|
579
|
+
const EXPLORATION_TOOLS: Record<string, true> = {
|
|
580
|
+
read: true,
|
|
581
|
+
grep: true,
|
|
582
|
+
glob: true,
|
|
583
|
+
web_search: true,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
interface OwnerMessageSummary {
|
|
587
|
+
localIdentity: string;
|
|
588
|
+
detail: {
|
|
589
|
+
actionSignal: boolean;
|
|
590
|
+
scopeSignal: boolean;
|
|
591
|
+
acceptanceSignal: boolean;
|
|
592
|
+
hasTaskGuard: boolean;
|
|
593
|
+
ownerPathCount: number;
|
|
594
|
+
mentionedPathCount: number;
|
|
595
|
+
ownedPathOverlap: boolean;
|
|
596
|
+
excludedSensitiveDetail: boolean;
|
|
597
|
+
};
|
|
598
|
+
shouldAdvise: boolean;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function summarizeOwnerMessage(message: string, answersCheckpoint: boolean, owner: SpawnedTaskMeta): OwnerMessageSummary {
|
|
602
|
+
const hasCodeFence = /```[\s\S]*?```/.test(message) || /`[^`\r\n]+`/.test(message);
|
|
603
|
+
const hasQuote = /^\s*>/m.test(message);
|
|
604
|
+
const hasSecretCandidate = /(?:api[_-]?key|access[_-]?token|authorization|bearer|password|secret)\s*[:=]\s*\S+/i.test(message);
|
|
605
|
+
const pathMatches = message.match(/(?:[A-Za-z]:[\\/]|(?:^|\s)(?:\.{0,2}[\\/]))[^\s"'`]+|(?:^|\s)[\w.-]+(?:[\\/][\w.@() -]+)+/gm) ?? [];
|
|
606
|
+
const ownerPaths = owner.guard.ownedPaths.map((path) => path.replace(/\\/g, "/").toLowerCase());
|
|
607
|
+
const ownedPathOverlap = pathMatches.some((candidate) => {
|
|
608
|
+
const normalized = candidate.trim().replace(/\\/g, "/").toLowerCase();
|
|
609
|
+
return ownerPaths.some((path) => normalized.includes(path) || path.includes(normalized));
|
|
610
|
+
});
|
|
611
|
+
const scrubbed = message
|
|
612
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
613
|
+
.replace(/`[^`\r\n]+`/g, " ")
|
|
614
|
+
.replace(/^\s*>.*$/gm, " ")
|
|
615
|
+
.replace(/(?:api[_-]?key|access[_-]?token|authorization|bearer|password|secret)\s*[:=]\s*\S+/gi, " ")
|
|
616
|
+
.replace(/(?:[A-Za-z]:[\\/]|(?:^|\s)(?:\.{0,2}[\\/]))[^\s"'`]+|(?:^|\s)[\w.-]+(?:[\\/][\w.@() -]+)+/gm, " ");
|
|
617
|
+
const normalized = scrubbed
|
|
618
|
+
.normalize("NFKC")
|
|
619
|
+
.toLowerCase()
|
|
620
|
+
.replace(/[^\p{L}\p{N}]+/gu, " ")
|
|
621
|
+
.trim();
|
|
622
|
+
// judge에는 싣지 않는 session-local dedupe 값이다. 경로·코드가 다른 업무를 합치지 않되
|
|
623
|
+
// 대소문자·공백 차이만 정규화한다.
|
|
624
|
+
const localIdentity = message.normalize("NFKC").toLowerCase().replace(/\s+/g, " ").trim();
|
|
625
|
+
const actionSignal = /(?:추가(?:해|하|해줘|하세요)|고쳐|수정(?:해|하|하세요)|구현(?:해|하|하세요)|조사(?:해|하|하세요)|검증(?:해|하|하세요)|테스트(?:해|하|하세요)|바꿔|변경(?:해|하|하세요)|확장(?:해|하|하세요)|제거(?:해|하|하세요)|재작업|다시\s*(?:해|하|고쳐)|\b(?:add|fix|change|implement|investigate|verify|test|expand|remove|rework|retarget)\b)/iu.test(normalized);
|
|
626
|
+
const scopeSignal = /(?:새\s*(?:과제|작업|범위)|범위\s*(?:확대|변경|추가)|목적\s*(?:변경|추가)|다른\s*(?:파일|모듈|경로)|\b(?:new task|new work|scope change|scope expansion|additional scope)\b)/iu.test(normalized);
|
|
627
|
+
const acceptanceSignal = /(?:완료\s*조건|수용\s*조건|합격\s*기준|인수\s*조건|검사\s*기준|\b(?:acceptance|done criteria|completion criteria)\b)/iu.test(normalized);
|
|
628
|
+
const statusSignal = /(?:상태|진행|결과|끝났|완료됐|어디까지|문제\s*있|막혔|\b(?:status|progress|result|done|blocked)\b)/iu.test(normalized);
|
|
629
|
+
const approvalSignal = /^(?:승인(?:함)?|좋아|확인|그대로\s*진행|approved?|ok(?:ay)?|lgtm)(?:\s|$)/iu.test(normalized);
|
|
630
|
+
const questionSignal = /[??]\s*$/.test(scrubbed) || /(?:인가|했어|됐어|있어|어때|알려\s*줘)\s*[.!]?\s*$/u.test(normalized);
|
|
631
|
+
const hasTaskGuard = /^\s*TASK_GUARD\s*:\s*$/im.test(message);
|
|
632
|
+
const clearStatusOrApproval = !actionSignal && !scopeSignal && !acceptanceSignal
|
|
633
|
+
&& (approvalSignal || (statusSignal && (questionSignal || answersCheckpoint)));
|
|
634
|
+
const shouldAdvise = !clearStatusOrApproval
|
|
635
|
+
&& (actionSignal || scopeSignal || acceptanceSignal || hasTaskGuard || (!questionSignal && normalized.length > 0));
|
|
636
|
+
return {
|
|
637
|
+
localIdentity,
|
|
638
|
+
shouldAdvise,
|
|
639
|
+
detail: {
|
|
640
|
+
actionSignal,
|
|
641
|
+
scopeSignal,
|
|
642
|
+
acceptanceSignal,
|
|
643
|
+
hasTaskGuard,
|
|
644
|
+
ownerPathCount: owner.guard.ownedPaths.length,
|
|
645
|
+
mentionedPathCount: pathMatches.length,
|
|
646
|
+
ownedPathOverlap,
|
|
647
|
+
excludedSensitiveDetail: hasCodeFence || hasQuote || pathMatches.length > 0 || hasSecretCandidate,
|
|
648
|
+
},
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function renderTodoProgressAdvice(assessment: TodoProgressAssessment): string {
|
|
653
|
+
if (!assessment.metadataValid) {
|
|
654
|
+
return "TODO 메타데이터 형식 오류(자동 변경 없음): TASK_GUARD 밖에 TASK_TITLE 한 줄과 비어 있지 않은 TODO_TASKS exact JSON 문자열 배열을 각각 하나만 둔다.";
|
|
655
|
+
}
|
|
656
|
+
if (assessment.expectedCount === 0) return "";
|
|
657
|
+
const lines = [
|
|
658
|
+
"TODO 연결(자동 변경 없음): child completed는 Main 수용이 아니다. 이 Main 세션의 성공한 exact `todo` `done` tool_result만 수용으로 관측한다.",
|
|
659
|
+
];
|
|
660
|
+
if (!assessment.revisionPresent) {
|
|
661
|
+
lines.push("- frozen revision이 없어 완료 후보를 만들지 않는다. owner에게 revision이 연결된 terminal validation을 요청한다.");
|
|
662
|
+
}
|
|
663
|
+
for (const item of assessment.items) {
|
|
664
|
+
const task = JSON.stringify(item.content);
|
|
665
|
+
if (item.bindingStatus !== "current") {
|
|
666
|
+
const reason = item.bindingStatus === "stale"
|
|
667
|
+
? "spawn 당시 todo identity와 현재 정본이 다르다"
|
|
668
|
+
: "spawn 당시 exact todo identity가 없었다";
|
|
669
|
+
lines.push(`- ${task}: ${reason}. 사용자 삭제·계획 교체·재개를 되돌리거나 항목을 자동 추가하지 않고 늦은 child 결과로 둔다.`);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
if (item.exactCurrentMatches > 1) {
|
|
673
|
+
lines.push(`- ${task}: 현재 todo 정본에 exact 항목이 중복됐다. Main이 한 항목으로 정리하기 전 상태를 바꾸지 않는다.`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
if (item.mainAccepted) {
|
|
677
|
+
lines.push(`- ${task}: Main의 explicit todo done 수용과 completed 정본을 모두 관측했다.`);
|
|
678
|
+
} else if (item.currentStatus === "completed") {
|
|
679
|
+
lines.push(`- ${task}: completed 정본은 있으나 이 Main 세션의 explicit todo done receipt가 없다. child lifecycle이나 자연어 완료로 수용하지 않는다.`);
|
|
680
|
+
} else if (item.currentStatus === "blocked" || item.currentStatus === "abandoned") {
|
|
681
|
+
lines.push(`- ${task}: ${item.currentStatus} 상태는 완료 후보가 아니다. 현재 TodoTracker 상태를 유지한다.`);
|
|
682
|
+
} else if (item.readyForMainAcceptance) {
|
|
683
|
+
lines.push(`- ${task}: terminal 검증 근거 충족. Main 검수 수용 뒤 기존 todo 도구로 exact done을 적용한다.`);
|
|
684
|
+
} else if (item.validation === "met-without-evidence") {
|
|
685
|
+
lines.push(`- ${task}: state=met만 있고 항목별 raw evidence locator가 없다. 근거를 연결하기 전 완료하지 않는다.`);
|
|
686
|
+
} else {
|
|
687
|
+
lines.push(`- ${task}: exact terminal validation이 없거나 미검증이다. focused 결과를 회수해 같은 key에 연결한다.`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (assessment.unattributedCompletedCount > 0) {
|
|
691
|
+
lines.push("- completed 스냅샷은 있으나 이 세션의 explicit todo done receipt가 없어 자연어 '끝남'이나 child lifecycle을 Main 수용으로 추정하지 않는다.");
|
|
692
|
+
}
|
|
693
|
+
return lines.join("\n");
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
// ---------------------------------------------------------------------------
|
|
698
|
+
// extension 본체
|
|
699
|
+
// ---------------------------------------------------------------------------
|
|
700
|
+
|
|
701
|
+
export function createJevRuntime(deps: JevRuntimeDeps = {}) {
|
|
702
|
+
return function jevRuntime(pi: ExtensionAPI): void {
|
|
703
|
+
/** toolCallId → spawn된 task 메타. pre-dispatch 중복 판정의 기존 maker 목록. */
|
|
704
|
+
const liveMakers = new Map<string, SpawnedTaskMeta[]>();
|
|
705
|
+
/** jobId → {callId, taskIndex}. settle 시 해당 child만 liveMakers에서 닫는다. */
|
|
706
|
+
const jobIndex = new Map<string, { callId: string; taskIndex: number }>();
|
|
707
|
+
/** 완료/parked 뒤에도 같은 session에서 `write agent://` 대상으로 식별할 수 있었던 maker. */
|
|
708
|
+
const knownMakers = new Map<string, SpawnedTaskMeta>();
|
|
709
|
+
/** 이미 판정한 경계. */
|
|
710
|
+
// 발주 판정은 maker_route 한 곳에서만 수행한다.
|
|
711
|
+
const judgedReviews = new Set<string>();
|
|
712
|
+
/** 같은 known owner에게 같은 정규화 지시를 반복할 때 local advisory를 한 번만 만든다. */
|
|
713
|
+
const advisedOwnerMessages = new Set<string>();
|
|
714
|
+
/** 완료된 동일 attempt의 미기록 Main 판정은 owner 후속 지시에서 한 번만 알린다. */
|
|
715
|
+
const advisedMissingVerdicts = new Set<string>();
|
|
716
|
+
/** 수신했지만 그 뒤 같은 Maker에게 아직 답하지 않은 체크포인트: sender → messageId. */
|
|
717
|
+
const pendingCheckpoints = new Map<string, string>();
|
|
718
|
+
/** 미회신 advisory를 이미 낸 체크포인트 id. */
|
|
719
|
+
const advisedCheckpoints = new Set<string>();
|
|
720
|
+
|
|
721
|
+
function knownMakerForTarget(target: string): SpawnedTaskMeta | undefined {
|
|
722
|
+
for (const owner of knownMakers.values()) {
|
|
723
|
+
if (owner.agentId === target) return owner;
|
|
724
|
+
}
|
|
725
|
+
return knownMakers.get(target);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function noteCheckpoint(record: unknown): void {
|
|
729
|
+
const checkpoint = readIncomingCheckpoint(record);
|
|
730
|
+
if (checkpoint && knownMakerForTarget(checkpoint.from)) {
|
|
731
|
+
pendingCheckpoints.set(checkpoint.from, checkpoint.id);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* 도구별 직전 실패. 같은 도구의 다음 호출이 재시도 경계다.
|
|
736
|
+
* genuine 새 입력·해당 도구 성공·경계 소비만 해당 항목을 지운다.
|
|
737
|
+
*/
|
|
738
|
+
const pendingFailures = new Map<
|
|
739
|
+
string,
|
|
740
|
+
{ inputSerialized: string; category: string; interveningTools: string[]; observation: FailureObservation }
|
|
741
|
+
>();
|
|
742
|
+
/** TASK_TITLE/TODO_TASKS consumer state: TodoTracker incarnation과 explicit Main `done` receipt. */
|
|
743
|
+
let todoProgressState: TodoProgressState = createTodoProgressState();
|
|
744
|
+
/** 같은 owner+frozen revision의 TODO coverage는 한 번만 판정한다. */
|
|
745
|
+
const reviewedTodoRevisions = new Set<string>();
|
|
746
|
+
/** running job 취소 직전의 근거 공백 advisory 중복 방지. */
|
|
747
|
+
const advisedRunningCancellations = new Set<string>();
|
|
748
|
+
|
|
749
|
+
interface ResolvedJudgeEnv {
|
|
750
|
+
judge: JudgeLike;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
let cachedEnv: Promise<ResolvedJudgeEnv | undefined> | undefined;
|
|
754
|
+
function judgeEnvFor(ctx: ExtensionContext): Promise<ResolvedJudgeEnv | undefined> {
|
|
755
|
+
if (cachedEnv) return cachedEnv;
|
|
756
|
+
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
757
|
+
cachedEnv = (async () => {
|
|
758
|
+
try {
|
|
759
|
+
// 지연 import가 필수다: bun test는 node_modules 없는 미러에서 이 파일을
|
|
760
|
+
// 직접 로드하고, 실제 런타임에서는 legacy-pi shim이 @oh-my-pi/* specifier를
|
|
761
|
+
// host 번들로 재작성한다. 로드 시점이 아니라 첫 판정 시점에 해석한다.
|
|
762
|
+
const resolveJudge: ResolveJudgeFn =
|
|
763
|
+
deps.resolveJudge ??
|
|
764
|
+
((await import("@oh-my-pi/pi-coding-agent/judgment"))
|
|
765
|
+
.resolveJudge as unknown as ResolveJudgeFn);
|
|
766
|
+
const settings =
|
|
767
|
+
deps.findScopedSettings?.(ctx.cwd) ??
|
|
768
|
+
((
|
|
769
|
+
await import("@oh-my-pi/pi-coding-agent/config/settings")
|
|
770
|
+
).findScopedSettings(ctx.cwd) as JudgeSettingsLike | undefined);
|
|
771
|
+
if (!settings) return undefined;
|
|
772
|
+
const judge = resolveJudge({
|
|
773
|
+
settings,
|
|
774
|
+
registry: ctx.modelRegistry,
|
|
775
|
+
backend: ONLINE_JUDGE_BACKEND,
|
|
776
|
+
sessionModel: ctx.model,
|
|
777
|
+
sessionId,
|
|
778
|
+
});
|
|
779
|
+
return { judge };
|
|
780
|
+
} catch (error) {
|
|
781
|
+
pi.logger.warn("jev-runtime: judge resolution failed", {
|
|
782
|
+
error: error instanceof Error ? error.message : String(error),
|
|
783
|
+
});
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
786
|
+
})();
|
|
787
|
+
return cachedEnv;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const baseLedger = createRoutingLedger(deps.ledgerPath ?? DEFAULT_LEDGER_PATH, (error) => {
|
|
791
|
+
pi.logger.warn("jev-runtime: routing ledger 기록 실패(발주는 계속)", {
|
|
792
|
+
error: error instanceof Error ? error.message : String(error),
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
// spawn으로 실제 관측된 attempt. routing_verdict는 여기 있는 identity만 받는다.
|
|
796
|
+
// 상태를 함께 두어 실행 중 attempt를 완료로 오인하지 않게 하고, session_start에서 원장 scoped 기록으로 복원한다.
|
|
797
|
+
type ObservedAttempt = AttemptIdentity & { name: string; status: "running" | OutcomeRecord["status"] };
|
|
798
|
+
const observedAttempts = new Map<string, ObservedAttempt>();
|
|
799
|
+
/** outcome을 이미 기록한 attempt. 같은 attempt를 두 번 소비하지 않는다. */
|
|
800
|
+
const settledAttempts = new Set<string>();
|
|
801
|
+
/** settle로 이미 소비한 async jobId. 채널(via)과 무관하게 중복 소비하지 않는다. */
|
|
802
|
+
const consumedJobs = new Set<string>();
|
|
803
|
+
/** 명시 REWORK 전송이 성공해 열린 재개 attempt. agentId → 그 전송 직후 관측한 정확한 새 jobId. */
|
|
804
|
+
const resumeByAgent = new Map<string, { attemptId: string; jobId: string }>();
|
|
805
|
+
/** 전송별 snapshot을 보존해 같은 actor로 겹친 DM도 서로의 기준선을 덮지 않는다. */
|
|
806
|
+
const preWriteJobs = new Map<string, Set<string>>();
|
|
807
|
+
/** 성공한 REWORK receipt. 새 job이 실제 snapshot에 나타나기 전에는 attempt를 만들지 않는다. */
|
|
808
|
+
const pendingResume = new Map<string, { priorJobs: ReadonlySet<string>; sourceAttemptId: string }>();
|
|
809
|
+
const VERDICTS = ["accepted", "rework", "held"] as const;
|
|
810
|
+
type VerdictInput = {
|
|
811
|
+
sessionId: string; assignmentId: string; attemptId: string; verdict: string;
|
|
812
|
+
reason: string; revision?: string; evidenceLocators?: string[];
|
|
813
|
+
};
|
|
814
|
+
/**
|
|
815
|
+
* Main 명시 수용 판정 기록. advisory 원장에 한 줄 append할 뿐 gate도 LLM 호출도 아니다.
|
|
816
|
+
* 잘못된 identity나 근거 부족, 저장 실패는 조용히 성공으로 넘기지 않고 오류로 돌려준다.
|
|
817
|
+
*/
|
|
818
|
+
function recordVerdict(request: VerdictInput, ctxSessionId: string):
|
|
819
|
+
| { ok: true; record: VerdictRecord }
|
|
820
|
+
| { ok: false; error: string; knownAttemptIds: string[] } {
|
|
821
|
+
const sessionId = request.sessionId.trim();
|
|
822
|
+
const assignmentId = request.assignmentId.trim();
|
|
823
|
+
const attemptId = request.attemptId.trim();
|
|
824
|
+
const reason = request.reason.trim();
|
|
825
|
+
const revision = (request.revision ?? "").trim();
|
|
826
|
+
const evidenceLocators = (request.evidenceLocators ?? []).map((value) => value.trim()).filter(Boolean);
|
|
827
|
+
if (!sessionId || !assignmentId || !attemptId) {
|
|
828
|
+
return { ok: false, error: "sessionId·assignmentId·attemptId가 모두 필요합니다. maker_route 결과가 아니라 spawn advisory의 실제 triple을 쓰세요.", knownAttemptIds: [] };
|
|
829
|
+
}
|
|
830
|
+
// 요청 값만 믿지 않고 실제 현재 session과 대조한다.
|
|
831
|
+
if (sessionId !== ctxSessionId.trim()) {
|
|
832
|
+
return { ok: false, error: "요청 sessionId가 현재 session과 다릅니다. 이 session에서 관측된 attempt만 판정할 수 있습니다.", knownAttemptIds: [] };
|
|
833
|
+
}
|
|
834
|
+
if (!(VERDICTS as readonly string[]).includes(request.verdict)) {
|
|
835
|
+
return { ok: false, error: `verdict는 accepted|rework|held 중 하나여야 합니다. 받은 값: ${request.verdict || "<없음>"}`, knownAttemptIds: [] };
|
|
836
|
+
}
|
|
837
|
+
const observed = observedAttempts.get(attemptId);
|
|
838
|
+
if (!observed || observed.sessionId !== sessionId || observed.assignmentId !== assignmentId) {
|
|
839
|
+
return {
|
|
840
|
+
ok: false,
|
|
841
|
+
error: "이 session에서 spawn으로 관측된 attempt가 아닙니다(stale identity이거나 아직 spawn되지 않은 prepared attempt). spawn/pre-review advisory의 실제 triple을 쓰세요.",
|
|
842
|
+
knownAttemptIds: [...observedAttempts.values()].map((entry) => entry.attemptId),
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
if (!reason) return { ok: false, error: "판정 근거 reason이 필요합니다.", knownAttemptIds: [] };
|
|
846
|
+
if (request.verdict !== "held" && (!revision || evidenceLocators.length === 0)) {
|
|
847
|
+
return { ok: false, error: `${request.verdict} 판정에는 비어 있지 않은 revision과 evidenceLocators 최소 1개가 필요합니다.`, knownAttemptIds: [] };
|
|
848
|
+
}
|
|
849
|
+
// 수용은 완료를 실제로 관측한 attempt에만 유효하다.
|
|
850
|
+
if (request.verdict === "accepted" && observed.status !== "completed") {
|
|
851
|
+
return { ok: false, error: `accepted는 완료가 관측된 attempt에만 쓸 수 있습니다. 현재 실행 상태: ${observed.status}`, knownAttemptIds: [] };
|
|
852
|
+
}
|
|
853
|
+
const record: VerdictRecord = {
|
|
854
|
+
type: "verdict",
|
|
855
|
+
ts: new Date().toISOString(),
|
|
856
|
+
sessionId: observed.sessionId,
|
|
857
|
+
assignmentId: observed.assignmentId,
|
|
858
|
+
attempt: observed.attempt,
|
|
859
|
+
attemptId: observed.attemptId,
|
|
860
|
+
agentId: observed.agentId,
|
|
861
|
+
jobId: observed.jobId,
|
|
862
|
+
verdict: request.verdict as VerdictRecord["verdict"],
|
|
863
|
+
revision: revision || null,
|
|
864
|
+
evidenceLocators,
|
|
865
|
+
reason,
|
|
866
|
+
};
|
|
867
|
+
if (!baseLedger.append(record)) {
|
|
868
|
+
return { ok: false, error: "원장 기록에 실패했습니다(저장 오류). 판정은 남지 않았습니다.", knownAttemptIds: [] };
|
|
869
|
+
}
|
|
870
|
+
return { ok: true, record };
|
|
871
|
+
}
|
|
872
|
+
const routing = registerMakerRouting(pi, {
|
|
873
|
+
ledger: baseLedger,
|
|
874
|
+
owners: () => {
|
|
875
|
+
const activeTasks = [...liveMakers.values()].flat();
|
|
876
|
+
const activeNames = new Set(
|
|
877
|
+
activeTasks.flatMap((task) => task.name ? [task.name] : []),
|
|
878
|
+
);
|
|
879
|
+
const owners = [...knownMakers.values()].map((task) => ({
|
|
880
|
+
name: task.name ?? null,
|
|
881
|
+
primaryDeliverable: task.guard.primaryDeliverable ?? null,
|
|
882
|
+
ownedPaths: task.guard.ownedPaths,
|
|
883
|
+
active: Boolean(task.name && activeNames.has(task.name)),
|
|
884
|
+
}));
|
|
885
|
+
for (const task of activeTasks) {
|
|
886
|
+
if (task.name) continue;
|
|
887
|
+
owners.push({
|
|
888
|
+
name: null,
|
|
889
|
+
primaryDeliverable: task.guard.primaryDeliverable ?? null,
|
|
890
|
+
ownedPaths: task.guard.ownedPaths,
|
|
891
|
+
active: true,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
return owners;
|
|
895
|
+
},
|
|
896
|
+
// legacy-pi loader가 host SDK specifier를 재작성하므로 미러의 static runtime import는 불가하다.
|
|
897
|
+
settings: async (ctx) => deps.findScopedSettings?.(ctx.cwd) ??
|
|
898
|
+
(await import("@oh-my-pi/pi-coding-agent/config/settings")).findScopedSettings(ctx.cwd),
|
|
899
|
+
judge: async (ctx, request, signal) => {
|
|
900
|
+
const env = await judgeEnvFor(ctx);
|
|
901
|
+
if (!env) throw new Error("Jev를 해석하지 못했습니다. 다른 모델로 대체하지 않습니다.");
|
|
902
|
+
return env.judge.judge(request as Parameters<JudgeLike["judge"]>[0], { signal });
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// Main 전용 명시 수용 판정 입력경로. maker_route와 같은 registerTool 경계를 쓰고 Maker tools 목록에 없으므로
|
|
907
|
+
// Maker child는 이 도구를 받지 않는다. 원장에 advisory 한 줄을 남길 뿐 gate도 LLM 호출도 아니다.
|
|
908
|
+
if (pi.registerTool) {
|
|
909
|
+
const z = pi.zod;
|
|
910
|
+
pi.registerTool({
|
|
911
|
+
name: "routing_verdict", label: "Routing Verdict", loadMode: "essential", approval: "read",
|
|
912
|
+
description: "Main 전용 발주 수용 판정 기록(advisory). 실행 상태와 분리해 accepted·rework·held만 남기며 gate도 LLM 호출도 아니다. identity는 spawn/pre-review advisory와 maker_route 결과의 plan이 아니라 실제 spawn triple(sessionId·assignmentId·attemptId)을 쓴다. accepted·rework는 비어 있지 않은 revision과 evidenceLocators 최소 1개, reason이 필요하고 held는 revision·evidence를 생략할 수 있다. accepted는 완료가 관측된 attempt에만 쓸 수 있다. spawn으로 관측되지 않은 attempt, stale identity, 현재 session이 아닌 identity는 기록하지 않고 오류와 알려진 attemptId 목록으로 알린다.",
|
|
913
|
+
parameters: z.object({
|
|
914
|
+
sessionId: z.string(),
|
|
915
|
+
assignmentId: z.string(),
|
|
916
|
+
attemptId: z.string(),
|
|
917
|
+
verdict: z.string(),
|
|
918
|
+
reason: z.string(),
|
|
919
|
+
revision: z.string().optional(),
|
|
920
|
+
evidenceLocators: z.array(z.string()).optional(),
|
|
921
|
+
}) as never,
|
|
922
|
+
async execute(_id: unknown, params: unknown, _signal: unknown, _onUpdate: unknown, ctx: ExtensionContext) {
|
|
923
|
+
const sessionId = ctx.sessionManager?.getSessionId?.() ?? "";
|
|
924
|
+
const result = recordVerdict(params as VerdictInput, typeof sessionId === "string" ? sessionId : "");
|
|
925
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }], details: result };
|
|
926
|
+
},
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
function sendAdvisory(placement: string, content: string): void {
|
|
931
|
+
pi.sendMessage(
|
|
932
|
+
{
|
|
933
|
+
customType: ADVISORY_CUSTOM_TYPE,
|
|
934
|
+
content,
|
|
935
|
+
display: false,
|
|
936
|
+
attribution: "agent",
|
|
937
|
+
},
|
|
938
|
+
{ deliverAs: "aside" },
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
/** settled job의 child만 live 목록에서 닫는다. 같은 call의 sibling은 유지한다. */
|
|
944
|
+
function closeSettledMakers(jobIds: readonly string[]): void {
|
|
945
|
+
for (const jobId of jobIds) {
|
|
946
|
+
const ref = jobIndex.get(jobId);
|
|
947
|
+
if (!ref) continue;
|
|
948
|
+
jobIndex.delete(jobId);
|
|
949
|
+
const tasks = liveMakers.get(ref.callId);
|
|
950
|
+
if (!tasks) continue;
|
|
951
|
+
const remaining = tasks.filter((task) => task.index !== ref.taskIndex);
|
|
952
|
+
if (remaining.length > 0) liveMakers.set(ref.callId, remaining);
|
|
953
|
+
else liveMakers.delete(ref.callId);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
async function runPreReview(
|
|
957
|
+
ctx: ExtensionContext,
|
|
958
|
+
via: "async-result" | "wait" | "read proc://",
|
|
959
|
+
settled: SettledJobMeta[],
|
|
960
|
+
note: string,
|
|
961
|
+
): Promise<void> {
|
|
962
|
+
const todoAdvice: string[] = [];
|
|
963
|
+
const identityNotes: string[] = [];
|
|
964
|
+
const reports: ReviewStructuralReport[] = settled.map((job) => {
|
|
965
|
+
const ref = jobIndex.get(job.jobId);
|
|
966
|
+
const spawned = ref
|
|
967
|
+
? liveMakers.get(ref.callId)?.find((task) => task.index === ref.taskIndex)
|
|
968
|
+
: undefined;
|
|
969
|
+
const todoMetadata = spawned?.guard.progress;
|
|
970
|
+
// 재개 바인딩은 그 전송 직후 관측한 정확한 새 jobId에만 소비된다. 다른 job(늦은 원 실행·stale)은 소비하지 않는다.
|
|
971
|
+
const binding = job.agentId ? resumeByAgent.get(job.agentId) : undefined;
|
|
972
|
+
let resumeAttemptId = binding && binding.jobId === job.jobId ? binding.attemptId : undefined;
|
|
973
|
+
// 늦은 이벤트 자체를 새 실행으로 간주하지 않는다. 현재 snapshot에서 확인한 유일한 새 job만 잇는다.
|
|
974
|
+
if (!resumeAttemptId && job.agentId && !spawned?.identity && !consumedJobs.has(job.jobId)) {
|
|
975
|
+
const pending = pendingResume.get(job.agentId);
|
|
976
|
+
const source = pending ? observedAttempts.get(pending.sourceAttemptId) : undefined;
|
|
977
|
+
const snapshot = pending ? ctx.getAsyncJobSnapshot?.() : undefined;
|
|
978
|
+
const candidates = new Set([...(snapshot?.running ?? []), ...(snapshot?.recent ?? [])]
|
|
979
|
+
.filter((entry) => entry.agentId === job.agentId && !pending!.priorJobs.has(entry.id) && !consumedJobs.has(entry.id))
|
|
980
|
+
.map((entry) => entry.id));
|
|
981
|
+
if (pending && source && candidates.size === 1 && candidates.has(job.jobId)) {
|
|
982
|
+
const attempt = source.attempt + 1;
|
|
983
|
+
const identity: AttemptIdentity = {
|
|
984
|
+
sessionId: source.sessionId,
|
|
985
|
+
assignmentId: source.assignmentId,
|
|
986
|
+
attempt,
|
|
987
|
+
attemptId: `${source.assignmentId}#a${attempt}`,
|
|
988
|
+
agentId: job.agentId,
|
|
989
|
+
jobId: job.jobId,
|
|
990
|
+
};
|
|
991
|
+
const sourceDispatch = baseLedger.read()
|
|
992
|
+
.filter((record) => record.type === "dispatch" && record.attemptId === source.attemptId)
|
|
993
|
+
.at(-1);
|
|
994
|
+
baseLedger.append({
|
|
995
|
+
type: "dispatch",
|
|
996
|
+
ts: new Date().toISOString(),
|
|
997
|
+
...identity,
|
|
998
|
+
name: source.name,
|
|
999
|
+
workClass: sourceDispatch?.workClass ?? null,
|
|
1000
|
+
focus: sourceDispatch?.focus ?? null,
|
|
1001
|
+
recommendedProfile: sourceDispatch?.recommendedProfile ?? null,
|
|
1002
|
+
recommendedModel: sourceDispatch?.recommendedModel ?? null,
|
|
1003
|
+
recommendedEffort: sourceDispatch?.recommendedEffort ?? null,
|
|
1004
|
+
chosenModel: sourceDispatch?.chosenModel ?? "",
|
|
1005
|
+
chosenEffort: sourceDispatch?.chosenEffort ?? "",
|
|
1006
|
+
routingReason: sourceDispatch?.routingReason ?? false,
|
|
1007
|
+
purpose: sourceDispatch?.purpose ?? null,
|
|
1008
|
+
});
|
|
1009
|
+
observedAttempts.set(identity.attemptId, { ...identity, name: source.name, status: "running" });
|
|
1010
|
+
pendingResume.delete(job.agentId);
|
|
1011
|
+
resumeAttemptId = identity.attemptId;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
// session_start에서 복원한 in-flight dispatch도 실제 jobId·agentId로 정산한다.
|
|
1015
|
+
const identity = resumeAttemptId ? observedAttempts.get(resumeAttemptId)
|
|
1016
|
+
: spawned?.identity ?? [...observedAttempts.values()].find((entry) =>
|
|
1017
|
+
entry.status === "running" && entry.jobId === job.jobId && entry.agentId === job.agentId);
|
|
1018
|
+
// 채널과 무관하게 이미 소비한 jobId는 다시 처리하지 않고, 같은 attempt도 한 번만 소비한다.
|
|
1019
|
+
if (identity && !consumedJobs.has(job.jobId) && !settledAttempts.has(identity.attemptId)
|
|
1020
|
+
&& (spawned?.agent === undefined || spawned.agent === "maker")) {
|
|
1021
|
+
consumedJobs.add(job.jobId);
|
|
1022
|
+
settledAttempts.add(identity.attemptId);
|
|
1023
|
+
if (resumeAttemptId && job.agentId) resumeByAgent.delete(job.agentId);
|
|
1024
|
+
const status: OutcomeRecord["status"] = job.status === "cancelled" ? "cancelled" : job.hasError ? "failed" : "completed";
|
|
1025
|
+
baseLedger.append({
|
|
1026
|
+
type: "outcome",
|
|
1027
|
+
ts: new Date().toISOString(),
|
|
1028
|
+
sessionId: identity.sessionId,
|
|
1029
|
+
assignmentId: identity.assignmentId,
|
|
1030
|
+
attempt: identity.attempt,
|
|
1031
|
+
attemptId: identity.attemptId,
|
|
1032
|
+
agentId: identity.agentId,
|
|
1033
|
+
// durable outcome에 실제 정산 jobId를 남겨 reload 뒤에도 중복 소비를 막는다.
|
|
1034
|
+
jobId: job.jobId,
|
|
1035
|
+
status,
|
|
1036
|
+
durationSec: job.durationMs === undefined ? null : Math.round(job.durationMs / 1000),
|
|
1037
|
+
});
|
|
1038
|
+
const observed = observedAttempts.get(identity.attemptId);
|
|
1039
|
+
if (observed) observed.status = status;
|
|
1040
|
+
identityNotes.push(`${spawned?.name ?? observed?.name ?? job.label ?? job.jobId} ${identity.sessionId}|${identity.assignmentId}|${identity.attemptId}`);
|
|
1041
|
+
}
|
|
1042
|
+
const todoRevisionKey = spawned && job.terminalRevision
|
|
1043
|
+
? `${spawned.name ?? job.jobId}:${job.terminalRevision}`
|
|
1044
|
+
: undefined;
|
|
1045
|
+
const todoCoverageObserved = Boolean(
|
|
1046
|
+
todoMetadata &&
|
|
1047
|
+
(todoMetadata.taskTitleFieldPresent || todoMetadata.todoTasksFieldPresent) &&
|
|
1048
|
+
(!todoRevisionKey || !reviewedTodoRevisions.has(todoRevisionKey))
|
|
1049
|
+
);
|
|
1050
|
+
if (todoCoverageObserved && todoRevisionKey) reviewedTodoRevisions.add(todoRevisionKey);
|
|
1051
|
+
const todoAssessment = todoCoverageObserved && todoMetadata
|
|
1052
|
+
? assessTodoProgress({
|
|
1053
|
+
metadata: todoMetadata,
|
|
1054
|
+
currentTodos: todoProgressState.currentTodos,
|
|
1055
|
+
todoBindings: spawned?.todoBindings ?? new Map<string, number>(),
|
|
1056
|
+
validation: job.validation,
|
|
1057
|
+
acceptedTodoBindingIds: todoProgressState.acceptedTodoBindingIds,
|
|
1058
|
+
revision: job.terminalRevision,
|
|
1059
|
+
terminalError: job.hasError,
|
|
1060
|
+
unresolvedCount: job.unresolvedCount,
|
|
1061
|
+
purpose: spawned?.guard.purpose,
|
|
1062
|
+
})
|
|
1063
|
+
: undefined;
|
|
1064
|
+
if (todoAssessment) todoAdvice.push(renderTodoProgressAdvice(todoAssessment));
|
|
1065
|
+
return {
|
|
1066
|
+
jobId: job.jobId,
|
|
1067
|
+
evidenceLocators: [...new Set(
|
|
1068
|
+
Object.values(job.validation).flatMap((item) => item.evidenceLocators),
|
|
1069
|
+
)],
|
|
1070
|
+
schemaStatus: job.schemaStatus ?? null,
|
|
1071
|
+
hasData: job.hasData,
|
|
1072
|
+
unverifiedCount: job.unverifiedCount ?? null,
|
|
1073
|
+
unresolvedCount: job.unresolvedCount ?? null,
|
|
1074
|
+
hasError: job.hasError,
|
|
1075
|
+
terminalRevisionPresent: Boolean(job.terminalRevision),
|
|
1076
|
+
todoCoverageObserved,
|
|
1077
|
+
todoMetadataValid: todoAssessment?.metadataValid ?? null,
|
|
1078
|
+
todoTaskTitleValid: todoMetadata?.taskTitleValid ?? null,
|
|
1079
|
+
todoExpectedCount: todoAssessment?.expectedCount ?? null,
|
|
1080
|
+
todoCurrentMissingCount: todoAssessment?.currentMissingCount ?? null,
|
|
1081
|
+
todoDuplicateCurrentCount: todoAssessment?.duplicateCurrentCount ?? null,
|
|
1082
|
+
todoStaleBindingCount: todoAssessment?.staleBindingCount ?? null,
|
|
1083
|
+
todoUnboundBindingCount: todoAssessment?.unboundBindingCount ?? null,
|
|
1084
|
+
todoValidationCoveredCount: todoAssessment?.validationCoveredCount ?? null,
|
|
1085
|
+
todoValidationMetCount: todoAssessment?.validationMetCount ?? null,
|
|
1086
|
+
todoValidationEvidenceMissingCount: todoAssessment?.validationEvidenceMissingCount ?? null,
|
|
1087
|
+
todoValidationWaitingCount: todoAssessment?.validationWaitingCount ?? null,
|
|
1088
|
+
todoReadyForMainAcceptanceCount: todoAssessment?.readyForMainAcceptanceCount ?? null,
|
|
1089
|
+
todoMainAcceptedCount: todoAssessment?.mainAcceptedCount ?? null,
|
|
1090
|
+
todoUnattributedCompletedCount: todoAssessment?.unattributedCompletedCount ?? null,
|
|
1091
|
+
todoPartialCompletion: todoAssessment?.partialCompletion ?? null,
|
|
1092
|
+
todoReworkLinked: todoAssessment?.reworkLinked ?? null,
|
|
1093
|
+
};
|
|
1094
|
+
});
|
|
1095
|
+
const todoGapObservations = reports.flatMap((report): StructuralCountObservation[] => {
|
|
1096
|
+
if (!report.todoCoverageObserved) return [];
|
|
1097
|
+
const issueCounts = [
|
|
1098
|
+
report.todoCurrentMissingCount,
|
|
1099
|
+
report.todoDuplicateCurrentCount,
|
|
1100
|
+
report.todoStaleBindingCount,
|
|
1101
|
+
report.todoUnboundBindingCount,
|
|
1102
|
+
report.todoValidationEvidenceMissingCount,
|
|
1103
|
+
report.todoValidationWaitingCount,
|
|
1104
|
+
report.todoUnattributedCompletedCount,
|
|
1105
|
+
];
|
|
1106
|
+
const value = report.todoMetadataValid === null || issueCounts.some((count) => count === null)
|
|
1107
|
+
? null
|
|
1108
|
+
: (report.todoMetadataValid ? 0 : 1) +
|
|
1109
|
+
issueCounts.reduce((sum, count) => sum + (count as number), 0);
|
|
1110
|
+
return [{ locator: report.jobId, value }];
|
|
1111
|
+
});
|
|
1112
|
+
const todoReadyObservations = reports.flatMap((report): StructuralCountObservation[] =>
|
|
1113
|
+
report.todoCoverageObserved
|
|
1114
|
+
? [{ locator: report.jobId, value: report.todoReadyForMainAcceptanceCount }]
|
|
1115
|
+
: []
|
|
1116
|
+
);
|
|
1117
|
+
const unverifiedObservations = reports.map((report) => ({
|
|
1118
|
+
locator: report.jobId,
|
|
1119
|
+
value: report.unverifiedCount,
|
|
1120
|
+
}));
|
|
1121
|
+
const unresolvedObservations = reports.map((report) => ({
|
|
1122
|
+
locator: report.jobId,
|
|
1123
|
+
value: report.unresolvedCount,
|
|
1124
|
+
}));
|
|
1125
|
+
const structuralSummary = summarizeStructuralReports(reports);
|
|
1126
|
+
closeSettledMakers(settled.map((job) => job.jobId));
|
|
1127
|
+
sendAdvisory(
|
|
1128
|
+
"pre-review",
|
|
1129
|
+
renderAdvisory(
|
|
1130
|
+
"pre-review",
|
|
1131
|
+
undefined,
|
|
1132
|
+
["requirementsAndEvidenceAligned", "claimsExceedEvidence", "validationWeakened"],
|
|
1133
|
+
`${note} 구조 관측은 로컬에서 확정했다: via=${via}, reports=${reports.length}, ${renderStructuralCount("unverified", unverifiedObservations)}, ${renderStructuralCount("unresolved", unresolvedObservations)}, ${renderStructuralCount("TODO gap", todoGapObservations)}, ${renderStructuralCount("Main 수용 후보", todoReadyObservations)}, structuralSummary=${JSON.stringify(structuralSummary)}. 구조 count만으로 요구 의미 충족을 판정하지 않는다.${identityNotes.length > 0 ? ` route identity: ${identityNotes.join("; ")}.` : ""}${todoAdvice.length > 0 ? `\n${todoAdvice.join("\n")}` : ""} 관측 가능한 JEV 질문이 없어 호출을 생략한다.`,
|
|
1134
|
+
),
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
pi.on("session_start", (_event, ctx) => {
|
|
1140
|
+
const sessionId = ctx.sessionManager?.getSessionId?.();
|
|
1141
|
+
if (typeof sessionId === "string") clearPreparedTaskSession(sessionId);
|
|
1142
|
+
// 이 session의 durable scoped dispatch·outcome·verdict로 관측 목록을 복원한다.
|
|
1143
|
+
// reload 뒤에도 held→입력→명시수용과 재개 후 수용이 이어진다. name-only 기록은 identity가 없어 빠진다.
|
|
1144
|
+
observedAttempts.clear();
|
|
1145
|
+
settledAttempts.clear();
|
|
1146
|
+
consumedJobs.clear();
|
|
1147
|
+
resumeByAgent.clear();
|
|
1148
|
+
preWriteJobs.clear();
|
|
1149
|
+
pendingResume.clear();
|
|
1150
|
+
for (const entry of scopedAttempts(baseLedger.read(), typeof sessionId === "string" ? sessionId : "")) {
|
|
1151
|
+
observedAttempts.set(entry.identity.attemptId, { ...entry.identity, name: entry.name, status: entry.status });
|
|
1152
|
+
if (entry.status !== "running") settledAttempts.add(entry.identity.attemptId);
|
|
1153
|
+
}
|
|
1154
|
+
// durable outcome이 남긴 jobId는 reload 뒤에도 중복 소비를 막는다.
|
|
1155
|
+
for (const record of baseLedger.read()) {
|
|
1156
|
+
if (record.type === "outcome" && record.sessionId === sessionId && record.jobId) consumedJobs.add(record.jobId);
|
|
1157
|
+
}
|
|
1158
|
+
liveMakers.clear();
|
|
1159
|
+
jobIndex.clear();
|
|
1160
|
+
knownMakers.clear();
|
|
1161
|
+
routing.reset();
|
|
1162
|
+
judgedReviews.clear();
|
|
1163
|
+
advisedOwnerMessages.clear();
|
|
1164
|
+
advisedMissingVerdicts.clear();
|
|
1165
|
+
pendingCheckpoints.clear();
|
|
1166
|
+
advisedCheckpoints.clear();
|
|
1167
|
+
pendingFailures.clear();
|
|
1168
|
+
todoProgressState = readPersistedTodoProgress(ctx.sessionManager.getBranch());
|
|
1169
|
+
reviewedTodoRevisions.clear();
|
|
1170
|
+
advisedRunningCancellations.clear();
|
|
1171
|
+
cachedEnv = undefined;
|
|
1172
|
+
});
|
|
1173
|
+
|
|
1174
|
+
// 새 genuine 입력은 실패 문맥과 routing의 TaskGuard 상속 lock만 끊는다.
|
|
1175
|
+
// 성공 판단 cache·prepared ref·known owner의 local advisory dedupe는 보존한다.
|
|
1176
|
+
pi.on("input", (event) => {
|
|
1177
|
+
if (event.source === "extension") return;
|
|
1178
|
+
pendingFailures.clear();
|
|
1179
|
+
// 수용 상태는 Main 명시 routing_verdict로만 바뀐다. 중간 입력은 verdict를 종결하지 않는다.
|
|
1180
|
+
if (event.source === "interactive" || event.source === "rpc") {
|
|
1181
|
+
routing.releaseContractLock();
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
pi.on("message_start", async (event, ctx) => {
|
|
1186
|
+
const message = event.message as {
|
|
1187
|
+
role?: string;
|
|
1188
|
+
customType?: string;
|
|
1189
|
+
content?: unknown;
|
|
1190
|
+
details?: unknown;
|
|
1191
|
+
synthetic?: boolean;
|
|
1192
|
+
attribution?: string;
|
|
1193
|
+
steering?: boolean;
|
|
1194
|
+
};
|
|
1195
|
+
if (message.synthetic === true) return;
|
|
1196
|
+
// 자기 advisory는 재귀하지 않는다.
|
|
1197
|
+
if (message.customType === ADVISORY_CUSTOM_TYPE) return;
|
|
1198
|
+
// 사람의 user 메시지는 재시도 문맥을 끊는다. genuine steering은 TaskGuard와 같은
|
|
1199
|
+
// deliverable 상속 lock만 풀고, 성공 판단 cache와 prepared ref는 유지한다.
|
|
1200
|
+
if (message.role === "user") {
|
|
1201
|
+
if (message.attribution === "agent") return;
|
|
1202
|
+
pendingFailures.clear();
|
|
1203
|
+
if (message.steering === true) routing.releaseContractLock();
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
// Maker가 `write agent://Main`으로 보낸 DM은 `irc:incoming` custom 메시지로 주입된다.
|
|
1207
|
+
if (message.role === "custom" && message.customType === "irc:incoming") {
|
|
1208
|
+
noteCheckpoint(message.details);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
// pre-review: async-result 자동 전달이 Main에 도착하는 가장 이른 경계.
|
|
1212
|
+
// async-result는 attribution:"agent"로 오므로 attribution 필터보다 먼저 본다.
|
|
1213
|
+
if (message.role !== "custom" || message.customType !== "async-result") return;
|
|
1214
|
+
const settled = readSettledTaskJobs(message.details, message.content).filter(
|
|
1215
|
+
(job) => !judgedReviews.has(job.jobId),
|
|
1216
|
+
);
|
|
1217
|
+
if (settled.length === 0) return;
|
|
1218
|
+
for (const job of settled) judgedReviews.add(job.jobId);
|
|
1219
|
+
await runPreReview(
|
|
1220
|
+
ctx,
|
|
1221
|
+
"async-result",
|
|
1222
|
+
settled,
|
|
1223
|
+
"maker 보고가 도착했다. Main 검수 전 구조 플래그와 TODO 진행 판단이다.",
|
|
1224
|
+
);
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
1228
|
+
if (event.toolName === "task") return routing.beforeTask(event.input, ctx);
|
|
1229
|
+
|
|
1230
|
+
if (event.toolName === "wait") {
|
|
1231
|
+
const unadvised = [...pendingCheckpoints].filter(([, id]) => !advisedCheckpoints.has(id));
|
|
1232
|
+
if (unadvised.length > 0) {
|
|
1233
|
+
for (const [, id] of unadvised) advisedCheckpoints.add(id);
|
|
1234
|
+
sendAdvisory(
|
|
1235
|
+
"pre-wait-unanswered-checkpoint",
|
|
1236
|
+
renderAdvisory(
|
|
1237
|
+
"pre-wait-unanswered-checkpoint",
|
|
1238
|
+
undefined,
|
|
1239
|
+
[],
|
|
1240
|
+
`미회신 편집 전 체크포인트 ${unadvised.length}건: ${unadvised.map(([from, id]) => `${from}(id=${id})`).join(", ")}. 해당 Maker는 답이 올 때까지 트리거 편집을 보류한다. 다시 기다리기 전에 각 Maker에게 write agent://<Maker>로 한 줄(approved·retarget·scope)을 답한다.`,
|
|
1241
|
+
),
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
if (event.toolName === "write") {
|
|
1247
|
+
const input = event.input as Record<string, unknown>;
|
|
1248
|
+
const path = typeof input.path === "string" ? input.path.trim() : "";
|
|
1249
|
+
const cancelId = PROC_KILL_URL.exec(path)?.[1];
|
|
1250
|
+
if (cancelId) {
|
|
1251
|
+
const running = (ctx.getAsyncJobSnapshot?.()?.running ?? []).some((job) => job.id === cancelId);
|
|
1252
|
+
if (running && !advisedRunningCancellations.has(cancelId)) {
|
|
1253
|
+
advisedRunningCancellations.add(cancelId);
|
|
1254
|
+
sendAdvisory(
|
|
1255
|
+
"pre-retry",
|
|
1256
|
+
renderAdvisory(
|
|
1257
|
+
"pre-retry",
|
|
1258
|
+
undefined,
|
|
1259
|
+
["sameCause", "newEvidence", "stallOrHang"],
|
|
1260
|
+
`running job 1건의 취소 직전 구조 event에 terminal exit·실패 결과가 없다. 취소 근거 없음: 실행 결과 회수 또는 다음 한 변수 확인. 산출물·로그·프로세스 생존 중 하나를 새로 확인한 뒤 결정한다. stdout 침묵·낮은 CPU·elapsed만으로 stall을 확정하지 않는다.`,
|
|
1261
|
+
),
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
const target = AGENT_URL.exec(path)?.[1] ?? "";
|
|
1266
|
+
if (target && /(?:^|\n)\s*REWORK\s+task_id=\S+/i.test(String(input.content ?? ""))) {
|
|
1267
|
+
// 전송 전에 그 actor의 running/recent jobId를 캡처한다. 재개 새 job은 이 집합과의 차집합에서만 고른다.
|
|
1268
|
+
const snapshot = ctx.getAsyncJobSnapshot?.();
|
|
1269
|
+
const known = new Set<string>();
|
|
1270
|
+
for (const job of [...(snapshot?.running ?? []), ...(snapshot?.recent ?? [])]) {
|
|
1271
|
+
const record = job as { id?: unknown; agentId?: unknown };
|
|
1272
|
+
if (typeof record.id !== "string" || !record.id) continue;
|
|
1273
|
+
if (record.agentId === target) known.add(record.id);
|
|
1274
|
+
}
|
|
1275
|
+
for (const entry of observedAttempts.values()) {
|
|
1276
|
+
if (entry.agentId === target && entry.jobId) known.add(entry.jobId);
|
|
1277
|
+
}
|
|
1278
|
+
preWriteJobs.set(event.toolCallId, known);
|
|
1279
|
+
}
|
|
1280
|
+
// canonical child id가 agent:// 대상이다. 표시용 task name으로 판정 대상을 추측하지 않는다.
|
|
1281
|
+
const owner = target ? knownMakerForTarget(target) : undefined;
|
|
1282
|
+
// 체크포인트 수신 뒤 같은 Maker로 가는 첫 DM이 그 체크포인트의 답이다. agent://all은 치지 않는다.
|
|
1283
|
+
const answersCheckpoint = target !== "" && pendingCheckpoints.delete(target);
|
|
1284
|
+
const message = typeof input.content === "string" ? input.content : "";
|
|
1285
|
+
const currentSessionId = ctx.sessionManager?.getSessionId?.();
|
|
1286
|
+
let attempt: ObservedAttempt | undefined;
|
|
1287
|
+
if (target && typeof currentSessionId === "string") {
|
|
1288
|
+
for (const entry of observedAttempts.values()) {
|
|
1289
|
+
if (entry.agentId === target && entry.sessionId === currentSessionId) attempt = entry;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
// reload 후 knownMakers의 원문 guard는 복원할 수 없어 경로 중복 구조는 알리지 않는다.
|
|
1293
|
+
// 다만 durable dispatch/outcome으로 확인한 canonical owner의 누락 판정은 여전히 확인할 수 있다.
|
|
1294
|
+
const summary = message && (owner || attempt)
|
|
1295
|
+
? summarizeOwnerMessage(message, answersCheckpoint, owner ?? { guard: { ownedPaths: [] } } as SpawnedTaskMeta)
|
|
1296
|
+
: undefined;
|
|
1297
|
+
const missingVerdict = summary?.shouldAdvise && attempt?.status === "completed"
|
|
1298
|
+
&& !advisedMissingVerdicts.has(attempt.attemptId)
|
|
1299
|
+
&& !baseLedger.read().some((record) => record.type === "verdict"
|
|
1300
|
+
&& record.sessionId === attempt.sessionId
|
|
1301
|
+
&& record.assignmentId === attempt.assignmentId
|
|
1302
|
+
&& record.attemptId === attempt.attemptId);
|
|
1303
|
+
const reminder = missingVerdict
|
|
1304
|
+
? ` 명시 판정 미기록(읽은 원장 기준): ${attempt.sessionId}|${attempt.assignmentId}|${attempt.attemptId}. 완료 보고와 해당 수용 조건·증거를 Main이 대조한 뒤 routing_verdict로 accepted/rework/held를 직접 기록한다. 증거 보충·추가 요구만으로 rework를 추정하지 않는다.`
|
|
1305
|
+
: "";
|
|
1306
|
+
if (missingVerdict) advisedMissingVerdicts.add(attempt.attemptId);
|
|
1307
|
+
if (owner && summary?.shouldAdvise) {
|
|
1308
|
+
const dedupeKey = JSON.stringify([
|
|
1309
|
+
target,
|
|
1310
|
+
owner.guard.workClass ?? null,
|
|
1311
|
+
owner.guard.primaryDeliverable ?? null,
|
|
1312
|
+
owner.guard.ownedPaths,
|
|
1313
|
+
summary.localIdentity,
|
|
1314
|
+
]);
|
|
1315
|
+
if (!advisedOwnerMessages.has(dedupeKey)) {
|
|
1316
|
+
advisedOwnerMessages.add(dedupeKey);
|
|
1317
|
+
const detail = summary.detail;
|
|
1318
|
+
sendAdvisory(
|
|
1319
|
+
"pre-dispatch-existing-owner-message",
|
|
1320
|
+
renderAdvisory(
|
|
1321
|
+
"pre-dispatch-existing-owner-message",
|
|
1322
|
+
undefined,
|
|
1323
|
+
["requestedMeaning", "exactScopeDelta", "acceptanceSemantics", "workClass", "candidateEfforts"],
|
|
1324
|
+
`known owner에게 보내는 지시의 로컬 구조만 관측했다: detailLocator=tool_call:${event.toolCallId}, targetOwner=${target}, actionSignal=${detail.actionSignal}, scopeSignal=${detail.scopeSignal}, acceptanceSignal=${detail.acceptanceSignal}, hasTaskGuard=${detail.hasTaskGuard}, ownerPathCount=${detail.ownerPathCount}, mentionedPathCount=${detail.mentionedPathCount}, ownedPathOverlap=${detail.ownedPathOverlap}, excludedSensitiveDetail=${detail.excludedSensitiveDetail}. 원문 의미·정확한 범위 delta·수용 의미는 unknown이며 원문·코드·경로·secret은 advisory에 싣지 않았다. Main이 실제 지시와 원 수용 조건을 대조해 실질 목적·범위·수용 조건이 변경됐으면 기존 owner를 보존하며 변경된 사실로 maker_route를 다시 판단한다. 실제 재작업이라 판단했다면 rule://subagent 「검수와 수용」의 REWORK task_id=... role=maker previous_revision=... next_revision=... finding_id=... source=... 계약을 적용한다. 구조 문자열 일치만으로 변경을 확정하거나 unknown만으로 호출하지 않으며 별도 JEV 판단을 호출하지 않는다.${reminder}`,
|
|
1325
|
+
),
|
|
1326
|
+
);
|
|
1327
|
+
} else if (missingVerdict) {
|
|
1328
|
+
sendAdvisory("pre-dispatch-existing-owner-message", renderAdvisory(
|
|
1329
|
+
"pre-dispatch-existing-owner-message", undefined, [], reminder.trim(),
|
|
1330
|
+
));
|
|
1331
|
+
}
|
|
1332
|
+
} else if (missingVerdict) {
|
|
1333
|
+
sendAdvisory("pre-dispatch-existing-owner-message", renderAdvisory(
|
|
1334
|
+
"pre-dispatch-existing-owner-message", undefined, [], reminder.trim(),
|
|
1335
|
+
));
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// pre-retry: 직전 실패 뒤 같은 도구의 다음 호출이 재시도 경계다.
|
|
1340
|
+
// 다른 도구 호출은 문맥을 소비하지 않는다.
|
|
1341
|
+
const failure = pendingFailures.get(event.toolName);
|
|
1342
|
+
if (!failure) return;
|
|
1343
|
+
pendingFailures.delete(event.toolName);
|
|
1344
|
+
const inputChanged = serializeInput(event.input) !== failure.inputSerialized;
|
|
1345
|
+
const observation = failure.observation;
|
|
1346
|
+
const nextAction = observation.cancelled && !observation.deterministicExitObserved
|
|
1347
|
+
? "취소 근거 없음: 실행 결과 회수 또는 다음 한 변수 확인. 산출물·로그·프로세스 생존 중 하나를 새로 확인한 뒤 결정한다. stdout 침묵·낮은 CPU·elapsed만으로 stall을 확정하지 않는다."
|
|
1348
|
+
: "sameCause/newEvidence를 inputChanged·interveningTools로 추정하지 않는다. 실제 오류·exit를 근거로 다음 한 변수를 확인한 뒤 조치를 바꾼다.";
|
|
1349
|
+
sendAdvisory(
|
|
1350
|
+
"pre-retry",
|
|
1351
|
+
renderAdvisory(
|
|
1352
|
+
"pre-retry",
|
|
1353
|
+
undefined,
|
|
1354
|
+
["sameCause", "newEvidence", "authOrProviderCause", "environmentOrSourceCause", "weakensValidation", "skillConflict", "skillBlocked"],
|
|
1355
|
+
`첫 실패 뒤 재시도 경계의 구조 관측은 로컬에서 확정했다: tool=${event.toolName}, errorCategory=${failure.category}, inputChanged=${inputChanged}, interveningTools=${failure.interveningTools.join(",") || "none"}, deterministicExitObserved=${observation.deterministicExitObserved}, executionObservationPresent=${observation.executionObservationPresent}, cancelled=${observation.cancelled}, timedOut=${observation.timedOut}. ${nextAction} errorCategory만으로 원인 의미를 재판단하지 않아 judge 호출을 생략한다.`,
|
|
1356
|
+
),
|
|
1357
|
+
);
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
1362
|
+
// 명시 REWORK의 write 전송이 실제로 성공했고 그 agentId의 이전 attempt가 끝났을 때만 같은 assignment의 새 재개를 연다.
|
|
1363
|
+
// 실행 중 attempt에 대한 DM은 그 attempt의 조향이므로 새 attempt를 만들지 않는다. 두 번째 DM도 바인딩을 덮지 않는다.
|
|
1364
|
+
const beforeWrite = event.toolName === "write" ? preWriteJobs.get(event.toolCallId) : undefined;
|
|
1365
|
+
if (event.toolName === "write") preWriteJobs.delete(event.toolCallId);
|
|
1366
|
+
if (event.toolName === "write" && !event.isError) {
|
|
1367
|
+
const input = event.input as Record<string, unknown>;
|
|
1368
|
+
const targetAgentId = AGENT_URL.exec(typeof input.path === "string" ? input.path.trim() : "")?.[1] ?? "";
|
|
1369
|
+
const message = typeof input.content === "string" ? input.content : "";
|
|
1370
|
+
const previous = targetAgentId
|
|
1371
|
+
? [...observedAttempts.values()].filter((entry) => entry.agentId === targetAgentId).at(-1)
|
|
1372
|
+
: undefined;
|
|
1373
|
+
if (targetAgentId && previous && /(?:^|\n)\s*REWORK\s+task_id=\S+/i.test(message)
|
|
1374
|
+
&& previous.status !== "running" && !resumeByAgent.has(targetAgentId) && !pendingResume.has(targetAgentId) && beforeWrite) {
|
|
1375
|
+
// 성공 receipt와 전송 전 snapshot이 있는 경우에만 실제 새 job을 바인딩한다.
|
|
1376
|
+
const prior = beforeWrite;
|
|
1377
|
+
const snapshot = ctx.getAsyncJobSnapshot?.();
|
|
1378
|
+
const fresh = [...new Set([...(snapshot?.running ?? []), ...(snapshot?.recent ?? [])]
|
|
1379
|
+
.flatMap((job) => {
|
|
1380
|
+
const record = job as { id?: unknown; agentId?: unknown };
|
|
1381
|
+
if (typeof record.id !== "string" || !record.id || prior.has(record.id)) return [];
|
|
1382
|
+
// 관측된 canonical agentId가 정확히 같은 job만 후보다. agentId 없는 낯선 job은 승격하지 않는다.
|
|
1383
|
+
if (record.agentId !== targetAgentId) return [];
|
|
1384
|
+
return [record.id];
|
|
1385
|
+
}))];
|
|
1386
|
+
if (fresh.length === 1) {
|
|
1387
|
+
const attempt = previous.attempt + 1;
|
|
1388
|
+
const identity: AttemptIdentity = {
|
|
1389
|
+
sessionId: previous.sessionId,
|
|
1390
|
+
assignmentId: previous.assignmentId,
|
|
1391
|
+
attempt,
|
|
1392
|
+
attemptId: `${previous.assignmentId}#a${attempt}`,
|
|
1393
|
+
agentId: targetAgentId,
|
|
1394
|
+
jobId: fresh[0]!,
|
|
1395
|
+
};
|
|
1396
|
+
// 원 attempt의 dispatch metadata를 정확한 identity로 복사해 재작업 attempt가 같은 모델·effort 관측을 유지한다.
|
|
1397
|
+
const source = baseLedger.read()
|
|
1398
|
+
.filter((record) => record.type === "dispatch" && record.attemptId === previous.attemptId)
|
|
1399
|
+
.at(-1);
|
|
1400
|
+
baseLedger.append({
|
|
1401
|
+
type: "dispatch",
|
|
1402
|
+
ts: new Date().toISOString(),
|
|
1403
|
+
...identity,
|
|
1404
|
+
name: previous.name,
|
|
1405
|
+
workClass: source?.workClass ?? null,
|
|
1406
|
+
focus: source?.focus ?? null,
|
|
1407
|
+
recommendedProfile: source?.recommendedProfile ?? null,
|
|
1408
|
+
recommendedModel: source?.recommendedModel ?? null,
|
|
1409
|
+
recommendedEffort: source?.recommendedEffort ?? null,
|
|
1410
|
+
chosenModel: source?.chosenModel ?? "",
|
|
1411
|
+
chosenEffort: source?.chosenEffort ?? "",
|
|
1412
|
+
routingReason: source?.routingReason ?? false,
|
|
1413
|
+
purpose: source?.purpose ?? null,
|
|
1414
|
+
});
|
|
1415
|
+
observedAttempts.set(identity.attemptId, { ...identity, name: previous.name, status: "running" });
|
|
1416
|
+
// 바인딩에 전송 직후 관측한 정확한 새 jobId를 둔다. 다른 job이 먼저 정산돼도 이 attempt를 소비하지 않는다.
|
|
1417
|
+
resumeByAgent.set(targetAgentId, { attemptId: identity.attemptId, jobId: fresh[0]! });
|
|
1418
|
+
sendAdvisory(
|
|
1419
|
+
"rework-attempt",
|
|
1420
|
+
renderAdvisory(
|
|
1421
|
+
"rework-attempt",
|
|
1422
|
+
undefined,
|
|
1423
|
+
[],
|
|
1424
|
+
`REWORK write 전송 성공으로 같은 assignment의 새 attempt를 열었다: agentId=${targetAgentId} jobId=${fresh[0]!} ${identity.sessionId}|${identity.assignmentId}|${identity.attemptId}. 그 실행의 settle이 이 attempt에 연결된다.`,
|
|
1425
|
+
),
|
|
1426
|
+
);
|
|
1427
|
+
} else if (fresh.length === 0) {
|
|
1428
|
+
// 새 job이 아직 스냅샷에 없다. 성공 receipt를 보관하고 다음 기존 관측(settle)에서 실제 새 job을 묶는다.
|
|
1429
|
+
pendingResume.set(targetAgentId, { priorJobs: prior, sourceAttemptId: previous.attemptId });
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// task 호출은 pre-dispatch 경계가 따로 있으므로 재시도 문맥을 세우지 않는다.
|
|
1435
|
+
if (event.toolName === "task") {
|
|
1436
|
+
if (event.isError) return;
|
|
1437
|
+
// task spawn 성공: 여기서 live maker로 등록한다. tool_call 시점에 등록하면
|
|
1438
|
+
// 다른 guard의 block으로 spawn이 없는데도 phantom maker가 남는다.
|
|
1439
|
+
// 실제 attempt identity는 이 spawn의 task 호출 id·index·session으로, canonical child id는 progress row에서 캡처한다.
|
|
1440
|
+
const sessionId = (ctx.sessionManager?.getSessionId?.() ?? "").trim();
|
|
1441
|
+
const details = event.details as { progress?: unknown; async?: { jobId?: unknown } } | undefined;
|
|
1442
|
+
// progress row의 index가 tasks[] 위치와 대응한다(AgentProgress.index). row.id가 canonical child id(= agent:// target)다.
|
|
1443
|
+
const progressAgentIds = new Map<number, string>();
|
|
1444
|
+
if (Array.isArray(details?.progress)) {
|
|
1445
|
+
for (const [position, row] of details.progress.entries()) {
|
|
1446
|
+
if (!row || typeof row !== "object") continue;
|
|
1447
|
+
const record = row as { id?: unknown; index?: unknown };
|
|
1448
|
+
if (typeof record.id !== "string" || !record.id) continue;
|
|
1449
|
+
progressAgentIds.set(typeof record.index === "number" ? record.index : position, record.id);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
const spawned = extractSpawnedTasks(event.input, todoProgressState.currentTodos);
|
|
1453
|
+
// 실제 wire: 각 spawn의 jobId는 ctx snapshot의 exact agentId 대응에서만 얻는다(없으면 미관측).
|
|
1454
|
+
const snapshot = ctx.getAsyncJobSnapshot?.();
|
|
1455
|
+
const jobByAgent = new Map<string, string>();
|
|
1456
|
+
for (const job of [...(snapshot?.running ?? []), ...(snapshot?.recent ?? [])]) {
|
|
1457
|
+
const record = job as { id?: unknown; agentId?: unknown };
|
|
1458
|
+
if (typeof record.id === "string" && record.id && typeof record.agentId === "string" && record.agentId) {
|
|
1459
|
+
jobByAgent.set(record.agentId, record.id);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
// details.async.jobId는 batch primary 하나뿐이며 그 actor로 snapshot에서 확인될 때만, 단일 spawn일 때만 쓴다.
|
|
1463
|
+
const primaryJobId = typeof details?.async?.jobId === "string" ? details.async.jobId : "";
|
|
1464
|
+
const ids = new Map<number, { agentId: string; jobId: string }>();
|
|
1465
|
+
for (const task of spawned) {
|
|
1466
|
+
const agentId = progressAgentIds.get(task.index) ?? "";
|
|
1467
|
+
const jobId = (agentId ? jobByAgent.get(agentId) : undefined) ?? (spawned.length === 1 ? primaryJobId : "");
|
|
1468
|
+
ids.set(task.index, { agentId, jobId });
|
|
1469
|
+
// 관측된 모든 spawn jobId를 settle 연결 대상으로 등록한다(첫 child만 등록하지 않는다).
|
|
1470
|
+
if (jobId) jobIndex.set(jobId, { callId: event.toolCallId, taskIndex: task.index });
|
|
1471
|
+
}
|
|
1472
|
+
const dispatched = routing.noteSpawned(event.input, sessionId, event.toolCallId, ids);
|
|
1473
|
+
const identities: string[] = [];
|
|
1474
|
+
for (const task of spawned) {
|
|
1475
|
+
const identity = dispatched.get(task.index);
|
|
1476
|
+
if (!identity || !identity.sessionId) continue;
|
|
1477
|
+
task.identity = identity;
|
|
1478
|
+
if (identity.agentId) task.agentId = identity.agentId;
|
|
1479
|
+
if (identity.jobId) task.jobId = identity.jobId;
|
|
1480
|
+
observedAttempts.set(identity.attemptId, { ...identity, name: task.name ?? "", status: "running" });
|
|
1481
|
+
identities.push(`${task.name ?? "<unnamed>"} agentId=${identity.agentId || "<unobserved>"} jobId=${identity.jobId || "<unobserved>"} ${identity.sessionId}|${identity.assignmentId}|${identity.attemptId}`);
|
|
1482
|
+
}
|
|
1483
|
+
if (identities.length > 0) {
|
|
1484
|
+
sendAdvisory(
|
|
1485
|
+
"spawn-identity",
|
|
1486
|
+
renderAdvisory(
|
|
1487
|
+
"spawn-identity",
|
|
1488
|
+
undefined,
|
|
1489
|
+
[],
|
|
1490
|
+
`실제 발주 identity를 spawn에서 캡처했다: ${identities.join("; ")}. routing_verdict는 이 triple과 이 session으로만 기록하며 준비 plan 값으로는 기록하지 않는다.`,
|
|
1491
|
+
),
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
liveMakers.set(event.toolCallId, spawned);
|
|
1495
|
+
for (const task of spawned) {
|
|
1496
|
+
if (task.name && (task.agent === undefined || task.agent === "maker")) knownMakers.set(task.name, task);
|
|
1497
|
+
}
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
if (event.toolName === "todo" && !event.isError) {
|
|
1502
|
+
const snapshot = readTodoSnapshot(event.details);
|
|
1503
|
+
if (snapshot) {
|
|
1504
|
+
todoProgressState = advanceTodoProgressState(
|
|
1505
|
+
todoProgressState,
|
|
1506
|
+
snapshot,
|
|
1507
|
+
event.input,
|
|
1508
|
+
event.details,
|
|
1509
|
+
);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// 실패 기록 — 탐색 도구의 실패는 일상 probing이라 재시도 판정을 세우지 않는다.
|
|
1514
|
+
// 다른 도구의 완료는 각 pending 실패의 interveningTools에만 남는다.
|
|
1515
|
+
if (event.isError) {
|
|
1516
|
+
if (!EXPLORATION_TOOLS[event.toolName]) {
|
|
1517
|
+
pendingFailures.set(event.toolName, {
|
|
1518
|
+
inputSerialized: serializeInput(event.input),
|
|
1519
|
+
category: classifyError(event.content),
|
|
1520
|
+
interveningTools: [],
|
|
1521
|
+
observation: readFailureObservation(event.details, event.content),
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
} else if (pendingFailures.has(event.toolName)) {
|
|
1525
|
+
// 같은 도구의 성공은 그 도구의 실패 문맥만 닫는다.
|
|
1526
|
+
pendingFailures.delete(event.toolName);
|
|
1527
|
+
}
|
|
1528
|
+
// 이 호출 자체를 제외한 다른 도구의 pending 실패에 intervening으로 기록한다.
|
|
1529
|
+
for (const [toolName, failure] of pendingFailures) {
|
|
1530
|
+
if (toolName === event.toolName) continue;
|
|
1531
|
+
if (failure.interveningTools.length < 8 && !failure.interveningTools.includes(event.toolName)) {
|
|
1532
|
+
failure.interveningTools.push(event.toolName);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// pre-review: `wait`·`read proc://`가 auto-delivery보다 먼저 settled 결과를 보여 준 경계.
|
|
1537
|
+
if (event.isError) return;
|
|
1538
|
+
let via: "wait" | "read proc://";
|
|
1539
|
+
let jobDetails: unknown;
|
|
1540
|
+
if (event.toolName === "wait") {
|
|
1541
|
+
noteCheckpoint(fieldOf(event.details, "waited"));
|
|
1542
|
+
via = "wait";
|
|
1543
|
+
jobDetails = event.details;
|
|
1544
|
+
} else if (event.toolName === "read" && /^proc:\/\//.test(String(fieldOf(event.input, "path") ?? ""))) {
|
|
1545
|
+
const proc = fieldOf(event.details, "proc");
|
|
1546
|
+
const job = fieldOf(proc, "job");
|
|
1547
|
+
via = "read proc://";
|
|
1548
|
+
jobDetails = job ? { jobs: [job] } : proc;
|
|
1549
|
+
} else {
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
const settled = readSettledTaskJobs(jobDetails, event.content).filter(
|
|
1553
|
+
(job) => !judgedReviews.has(job.jobId),
|
|
1554
|
+
);
|
|
1555
|
+
if (settled.length === 0) return;
|
|
1556
|
+
for (const job of settled) judgedReviews.add(job.jobId);
|
|
1557
|
+
await runPreReview(
|
|
1558
|
+
ctx,
|
|
1559
|
+
via,
|
|
1560
|
+
settled,
|
|
1561
|
+
`${via}가 settled maker 결과를 먼저 보여 줬다. Main 검수 전 구조 플래그와 TODO 진행 판단이다.`,
|
|
1562
|
+
);
|
|
1563
|
+
});
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
export default createJevRuntime();
|