commandmate 0.45.0 → 0.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +265 -264
- package/.next/app-path-routes-manifest.json +18 -18
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +9 -9
- package/.next/react-loadable-manifest.json +4 -4
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/release-notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/direct-input/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page.js.nft.json +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page.js +2 -2
- package/.next/server/app/skills/[skillId]/page.js.nft.json +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +4 -4
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +18 -18
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2335.js +1 -0
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/2460.js +1 -1
- package/.next/server/chunks/{4802.js → 4243.js} +1 -1
- package/.next/server/chunks/4837.js +3 -3
- package/.next/server/chunks/5209.js +1 -1
- package/.next/server/chunks/5625.js +2 -2
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6401.js +1 -1
- package/.next/server/chunks/6927.js +1 -1
- package/.next/server/chunks/8728.js +1 -1
- package/.next/server/chunks/9355.js +1 -0
- package/.next/server/chunks/9862.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{7485.8a2a9d7c5396aa26.js → 1111.5ab4f0ffb0d944e5.js} +2 -2
- package/.next/static/chunks/{1642-767aed8e7c61ef7f.js → 1642-0f1bf8fb8161e10a.js} +1 -1
- package/.next/static/chunks/{2407-dd4a0f04f1349221.js → 2407-86890b00eeecf391.js} +1 -1
- package/.next/static/chunks/3662-5a27ee39a3ecba49.js +1 -0
- package/.next/static/chunks/3922.0bc2e4e168d2b522.js +1 -0
- package/.next/static/chunks/4541-b0ce0bf829ae79b7.js +1 -0
- package/.next/static/chunks/4727-4c69d1aa4118cc09.js +1 -0
- package/.next/static/chunks/5371-af1c2b68e983da61.js +1 -0
- package/.next/static/chunks/6288-09feba367cc2c65e.js +1 -0
- package/.next/static/chunks/953.31c52a251deda22d.js +1 -0
- package/.next/static/chunks/app/{layout-07d040cb3d3ea657.js → layout-eaa6365f8f942e88.js} +1 -1
- package/.next/static/chunks/app/sessions/{page-2af68c63211c751f.js → page-ebfed2c80e916fb8.js} +1 -1
- package/.next/static/chunks/app/skills/[skillId]/page-e4b14031f946c007.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9cf1ecc59e36a004.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-3372b78fde232b1f.js +1 -0
- package/.next/static/chunks/{webpack-165d8df9acd1c5d4.js → webpack-ee3ccbe378c4aa06.js} +1 -1
- package/.next/static/css/{057f64a65726c439.css → 6faeb04a0b6819d4.css} +1 -1
- package/.next/trace +13 -13
- package/dist/server/locales/en/worktree.json +2 -1
- package/dist/server/locales/ja/worktree.json +2 -1
- package/dist/server/src/lib/auto-yes-poller.js +16 -163
- package/dist/server/src/lib/polling/auto-yes-enter-fallback-state.js +1 -1
- package/dist/server/src/lib/polling/auto-yes-poller-state.js +52 -0
- package/dist/server/src/lib/polling/auto-yes-prompt-suppression.js +125 -0
- package/dist/server/src/lib/polling/prompt-dedup.js +93 -2
- package/dist/server/src/lib/polling/response-checker.js +40 -2
- package/dist/server/src/lib/polling/response-dedup.js +62 -1
- package/package.json +1 -1
- package/release-notes/0.45.1.json +38 -0
- package/.next/server/chunks/364.js +0 -1
- package/.next/server/chunks/488.js +0 -1
- package/.next/server/chunks/5326.js +0 -1
- package/.next/static/chunks/2053-390e77fce43b555c.js +0 -1
- package/.next/static/chunks/3922.f45dcfc1e7da552a.js +0 -1
- package/.next/static/chunks/4727-5d695d71e3fdf97b.js +0 -1
- package/.next/static/chunks/8283-26ad31a4670ea52d.js +0 -1
- package/.next/static/chunks/8566-aeda747542072ace.js +0 -1
- package/.next/static/chunks/8811-29eaa24237f48558.js +0 -1
- package/.next/static/chunks/953.19e4c8178c9eac4c.js +0 -1
- package/.next/static/chunks/app/skills/[skillId]/page-9ee7a381636e60cb.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-5445205c8dd80109.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-35f0e31a725d1c89.js +0 -1
- /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → vYIpFRCU_Mt0wtG8F_3-6}/_buildManifest.js +0 -0
- /package/.next/static/{TPWbZB3zBp3Zv_eFEOlmt → vYIpFRCU_Mt0wtG8F_3-6}/_ssgManifest.js +0 -0
|
@@ -818,7 +818,8 @@
|
|
|
818
818
|
"loading": "Loading diagram...",
|
|
819
819
|
"errorTitle": "Diagram Error",
|
|
820
820
|
"emptyCode": "Diagram code is empty",
|
|
821
|
-
"renderError": "Failed to render diagram"
|
|
821
|
+
"renderError": "Failed to render diagram",
|
|
822
|
+
"source": "Source"
|
|
822
823
|
},
|
|
823
824
|
"imageViewer": {
|
|
824
825
|
"loadError": "Failed to load image"
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Issue #525: Composite key migration (worktreeId:cliToolId) for per-agent auto-yes.
|
|
9
9
|
*
|
|
10
|
-
* Dependencies: auto-yes-state.ts (
|
|
11
|
-
*
|
|
10
|
+
* Dependencies: auto-yes-state.ts (composite keys / state), polling/* (prompt
|
|
11
|
+
* detection, resolver, policy, dialog gate, enter fallback, poller state),
|
|
12
|
+
* cli-tools/*, session/cli-session.ts, tmux/*, detection/*, db/*, tasks/*.
|
|
12
13
|
*/
|
|
13
14
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
15
|
if (k2 === undefined) k2 = k;
|
|
@@ -67,7 +68,6 @@ const auto_yes_resolver_1 = require("./polling/auto-yes-resolver");
|
|
|
67
68
|
const auto_yes_policy_1 = require("./polling/auto-yes-policy");
|
|
68
69
|
const detect_1 = require("./detection/tools/codex/detect");
|
|
69
70
|
const frame_1 = require("./detection/tools/frame");
|
|
70
|
-
const auto_yes_suppression_state_1 = require("./polling/auto-yes-suppression-state");
|
|
71
71
|
const auto_yes_dialog_gate_1 = require("./polling/auto-yes-dialog-gate");
|
|
72
72
|
const auto_yes_enter_fallback_1 = require("./polling/auto-yes-enter-fallback");
|
|
73
73
|
const task_transition_service_1 = require("./tasks/task-transition-service");
|
|
@@ -88,6 +88,8 @@ const logger = (0, logger_1.createLogger)('auto-yes-poller');
|
|
|
88
88
|
const path_validator_1 = require("./security/path-validator");
|
|
89
89
|
const auto_yes_state_1 = require("./auto-yes-state");
|
|
90
90
|
const global_state_1 = require("./global-state");
|
|
91
|
+
const auto_yes_poller_state_1 = require("./polling/auto-yes-poller-state");
|
|
92
|
+
const auto_yes_prompt_suppression_1 = require("./polling/auto-yes-prompt-suppression");
|
|
91
93
|
/** In-memory storage for poller states (globalThis for hot reload persistence) */
|
|
92
94
|
const autoYesPollerStates = (0, global_state_1.getOrInitGlobal)('__autoYesPollerStates', () => new Map());
|
|
93
95
|
/**
|
|
@@ -221,45 +223,6 @@ function isDuplicatePrompt(pollerState, promptKey) {
|
|
|
221
223
|
return false;
|
|
222
224
|
return (Date.now() - pollerState.lastAnsweredAt) < auto_yes_state_1.DUPLICATE_RETRY_EXPIRY_MS;
|
|
223
225
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Issue #2995: print a "did not answer" line as WARN once per frame.
|
|
226
|
-
*
|
|
227
|
-
* The poller re-reads a static pane every 2s, so a frame Auto-Yes leaves alone
|
|
228
|
-
* (a reply quoting `1. Yes / 2. No`, a launch dialog, a policy-withheld prompt,
|
|
229
|
-
* a foreign session) would otherwise print the same WARN on every tick. The
|
|
230
|
-
* first one stays WARN -- it is what tells a silently stalled worker apart --
|
|
231
|
-
* and repeats for the same frame drop to `debug`. The frame is identified by
|
|
232
|
-
* the event, `promptFrameKey()` and the event's own detail; a different
|
|
233
|
-
* prompt, a different reason, or a tick with no prompt / an answer sent (both
|
|
234
|
-
* clear the key) makes the next line WARN again.
|
|
235
|
-
* `recordPolicySuppression()` is not throttled: `capture --json` and `wait`
|
|
236
|
-
* read the latest record, not the log.
|
|
237
|
-
*/
|
|
238
|
-
/**
|
|
239
|
-
* Issue #2995: what makes two ticks "the same prompt" for `warnOncePerFrame`.
|
|
240
|
-
*
|
|
241
|
-
* `generatePromptKey` alone is type + question, and the question the detector
|
|
242
|
-
* extracts from an agent's reply is often a fixed line above the list, so two
|
|
243
|
-
* different lists would share it. The option rows (and where the cursor sits)
|
|
244
|
-
* plus `approvalTarget` -- the current prompt's own panel, which leaves out
|
|
245
|
-
* scrollback and footers that change while the prompt does not -- separate them.
|
|
246
|
-
* Not the whole pane: a status bar that ticks every second would defeat it.
|
|
247
|
-
*/
|
|
248
|
-
function promptFrameKey(promptData) {
|
|
249
|
-
const options = promptData.type === 'multiple_choice'
|
|
250
|
-
? promptData.options.map((o) => `${o.isDefault ? '>' : ''}${o.number}.${o.label}`).join('\n')
|
|
251
|
-
: '';
|
|
252
|
-
return [(0, prompt_key_1.generatePromptKey)(promptData), options, promptData.approvalTarget ?? ''].join('\u0000');
|
|
253
|
-
}
|
|
254
|
-
function warnOncePerFrame(pollerState, frameKey, event, fields) {
|
|
255
|
-
const key = `${event}\u0000${frameKey}`;
|
|
256
|
-
if (pollerState.lastSkipWarnKey === key) {
|
|
257
|
-
logger.debug(event, { ...fields, repeated: true });
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
pollerState.lastSkipWarnKey = key;
|
|
261
|
-
logger.warn(event, fields);
|
|
262
|
-
}
|
|
263
226
|
// =============================================================================
|
|
264
227
|
// Extracted Functions for pollAutoYes (Issue #323: SRP decomposition)
|
|
265
228
|
// =============================================================================
|
|
@@ -395,116 +358,6 @@ function logIfWithheldForWantOfReceipt(worktreeId, instanceId, compositeKey, rea
|
|
|
395
358
|
hint: 'hook の受信記録が直近に無いため Auto-Yes は答えなかった。~/.gemini/config/hooks.json の向き先(#2622)を確認',
|
|
396
359
|
});
|
|
397
360
|
}
|
|
398
|
-
/**
|
|
399
|
-
* Issue #3214: the form the four suppression exits of `detectAndRespondToPrompt`
|
|
400
|
-
* share. Records why Auto-Yes left the prompt alone (`recordPolicySuppression`,
|
|
401
|
-
* not throttled), then prints the WARN once per frame (`warnOncePerFrame`).
|
|
402
|
-
*
|
|
403
|
-
* @param suppression - Handed to `recordPolicySuppression` as is
|
|
404
|
-
* @param warnFrameKey - Handed to `warnOncePerFrame` as is: the prompt's
|
|
405
|
-
* `frameKey` plus the exit's own detail
|
|
406
|
-
* @param event - The WARN's log name
|
|
407
|
-
* @param detail - The WARN's fields after `worktreeId`, `cliToolId` and `instanceId`
|
|
408
|
-
*/
|
|
409
|
-
function suppressAndWarnOnce(prompt, suppression, warnFrameKey, event, detail) {
|
|
410
|
-
const { worktreeId, cliToolId, instanceId } = prompt;
|
|
411
|
-
(0, auto_yes_suppression_state_1.recordPolicySuppression)(worktreeId, cliToolId, instanceId, suppression);
|
|
412
|
-
warnOncePerFrame(prompt.pollerState, warnFrameKey, event, {
|
|
413
|
-
worktreeId,
|
|
414
|
-
cliToolId,
|
|
415
|
-
instanceId,
|
|
416
|
-
...detail,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* Issue #3214: steps 3, 3.2 and 3.5 of `detectAndRespondToPrompt` -- the frames
|
|
421
|
-
* that read as a prompt and are still not Auto-Yes's to answer, judged in the
|
|
422
|
-
* order they always were. "The detection above" in the comments below is that
|
|
423
|
-
* function's step 1.
|
|
424
|
-
*
|
|
425
|
-
* Issue #3397: step 3.5 no longer records its refusal here. It hands the gate's
|
|
426
|
-
* verdict back so `tryEnterFallback` can look at the frame first, and records
|
|
427
|
-
* through {@link suppressUnclassifiedFrame} when that sends nothing. Steps 3 and
|
|
428
|
-
* 3.2 still win: an Enter never reaches codex's launch dialogs or its `/model`
|
|
429
|
-
* picker.
|
|
430
|
-
*
|
|
431
|
-
* @param frame - The tick's one normalised frame (Issue #3183)
|
|
432
|
-
*/
|
|
433
|
-
function suppressIfNotOursToAnswer(prompt, frame) {
|
|
434
|
-
const { cliToolId, promptData, frameKey } = prompt;
|
|
435
|
-
// 3. Issue #1829: codex's own launch dialogs are CodexTool.waitForReady()'s
|
|
436
|
-
// to answer, not the poller's. Every one of them defaults to option 1, and
|
|
437
|
-
// the base rules answer the default:
|
|
438
|
-
//
|
|
439
|
-
// "Hooks need review" -> 1. Review hooks (undoes Issue #1760; the pane
|
|
440
|
-
// then sits two screens deep in a review UI that
|
|
441
|
-
// only `t`/`esc` leave, reported as `running`)
|
|
442
|
-
// "Update available" -> 1. Update now (undoes Issue #890; runs
|
|
443
|
-
// `npm install -g @openai/codex`, killing codex)
|
|
444
|
-
// "Do you trust …" -> 1. Yes, continue
|
|
445
|
-
//
|
|
446
|
-
// waitForReady answers the same screens deliberately and differently ('3',
|
|
447
|
-
// '2', '1' — each without a trailing Enter), but only during startSession,
|
|
448
|
-
// while this poller runs on its own 2s phase for the life of the session.
|
|
449
|
-
// Whichever sees the dialog first decides, so the fix is to leave them all
|
|
450
|
-
// to the tool. Auto-answer layer only: the detection above still reports the
|
|
451
|
-
// prompt, so the human keeps seeing the screen and the response poller
|
|
452
|
-
// still notifies them about it.
|
|
453
|
-
const launchDialog = cliToolId === 'codex' ? (0, cli_patterns_1.getCodexLifecycleDialog)(frame) : null;
|
|
454
|
-
if (launchDialog) {
|
|
455
|
-
// Recorded through the #1684 channel so `capture --json` and `cmate wait`
|
|
456
|
-
// can name the reason instead of showing a worker that went quiet.
|
|
457
|
-
suppressAndWarnOnce(prompt, { reason: 'agent-launch-dialog', mode: null, promptType: promptData.type }, `${frameKey}\u0000${launchDialog}`, 'poller:auto-yes-skipped-launch-dialog', { dialog: launchDialog, promptType: promptData.type });
|
|
458
|
-
return { kind: 'left-alone' };
|
|
459
|
-
}
|
|
460
|
-
// 3.2. Issue #3062: codex's `/model` picker (both stages) is not ours to
|
|
461
|
-
// answer. A digit there is an immediate decision, so the base rules'
|
|
462
|
-
// default would choose the model and effort for the operator. Judged by the
|
|
463
|
-
// picker's own footer, not by `kind: picker`, which the hooks screens share.
|
|
464
|
-
// The tool's `detectPrompt` still reports the prompt, so `/prompt-response`
|
|
465
|
-
// (the human's answer) is unaffected. Reuses `unclassified-frame`: the tool
|
|
466
|
-
// recognised the frame and deliberately declined it.
|
|
467
|
-
if (cliToolId === 'codex' && (0, detect_1.isCodexModelPickerFrame)(frame)) {
|
|
468
|
-
suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000codex-model-picker`, 'poller:auto-yes-skipped-model-picker', { promptType: promptData.type });
|
|
469
|
-
return { kind: 'left-alone' };
|
|
470
|
-
}
|
|
471
|
-
// 3.5. Issue #1928 (§4 D1 decision 4): the generic numbered-list inference is
|
|
472
|
-
// not enough to send an answer. The detection above judges the ROWS, and the
|
|
473
|
-
// rows of an agent's own reply can be indistinguishable from a dialog's --
|
|
474
|
-
// opencode 1.18 answering "list three options and ask which one" is the
|
|
475
|
-
// reported case (#1896), and the `1` this poller sent in reply was not
|
|
476
|
-
// answering anything, it was SENT AS A USER UTTERANCE. What separates the two
|
|
477
|
-
// is position and chrome, which only the tool's own module knows, so the
|
|
478
|
-
// decision is delegated to `detectDialog` (the seam #1927 declared).
|
|
479
|
-
//
|
|
480
|
-
// Per tool, and only for tools whose dialogs were measured from their own
|
|
481
|
-
// live captures -- see AUTO_YES_DIALOG_GATE_DEFAULT_MODE. An ungated tool
|
|
482
|
-
// reaches `allowed: true` without being judged, which is the pre-#1928
|
|
483
|
-
// behaviour and the right one where nobody has measured anything.
|
|
484
|
-
//
|
|
485
|
-
// Recorded through the #1684 channel with the reason code #1924 landed for
|
|
486
|
-
// exactly this position, so `capture --json` and `cmate wait` can name the
|
|
487
|
-
// gap instead of showing a worker that silently went quiet.
|
|
488
|
-
const dialogGate = (0, auto_yes_dialog_gate_1.evaluateAutoYesDialogGate)(cliToolId, promptData.type, frame);
|
|
489
|
-
if (!dialogGate.allowed) {
|
|
490
|
-
return { kind: 'dialog-gate-refused', dialogGate };
|
|
491
|
-
}
|
|
492
|
-
return { kind: 'ours' };
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Step 3.5's record: the tool's dialog detector did not vouch for the frame and
|
|
496
|
-
* nothing was sent (Issue #1928). Split out of `suppressIfNotOursToAnswer` by
|
|
497
|
-
* Issue #3397, unchanged, so it runs after `tryEnterFallback` declined.
|
|
498
|
-
*/
|
|
499
|
-
function suppressUnclassifiedFrame(prompt, dialogGate) {
|
|
500
|
-
const { promptData, frameKey } = prompt;
|
|
501
|
-
suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000${dialogGate.dialog?.kind ?? ''}\u0000${dialogGate.mode}`, 'poller:auto-yes-skipped-unclassified-frame', {
|
|
502
|
-
promptType: promptData.type,
|
|
503
|
-
dialogKind: dialogGate.dialog?.kind ?? null,
|
|
504
|
-
answerMode: dialogGate.dialog?.answerMode ?? null,
|
|
505
|
-
gateMode: dialogGate.mode,
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
361
|
/**
|
|
509
362
|
* The audit row's `answer` for the Enter (Issue #3397): not a digit, so the
|
|
510
363
|
* History reads as "Auto-Yes pressed Enter" rather than as a chosen option.
|
|
@@ -525,7 +378,7 @@ function isStillEnterFallbackScreen(prompt, fresh) {
|
|
|
525
378
|
const detection = (0, response_checker_1.detectPromptOnCleanFrame)(clean, cliToolId, clean.split('\n'), fresh, { worktreeId, instanceId }, frame);
|
|
526
379
|
if (!detection.isPrompt || !detection.promptData)
|
|
527
380
|
return false;
|
|
528
|
-
if (promptFrameKey(detection.promptData) !== frameKey)
|
|
381
|
+
if ((0, auto_yes_poller_state_1.promptFrameKey)(detection.promptData) !== frameKey)
|
|
529
382
|
return false;
|
|
530
383
|
if (cliToolId === 'codex' && ((0, cli_patterns_1.getCodexLifecycleDialog)(frame) || (0, detect_1.isCodexModelPickerFrame)(frame))) {
|
|
531
384
|
return false;
|
|
@@ -563,7 +416,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
563
416
|
const judgement = dialogGate.dialog === null ? (0, auto_yes_enter_fallback_1.judgeEnterFallback)(cliToolId, rawOutput) : null;
|
|
564
417
|
if (judgement === null || !judgement.eligible) {
|
|
565
418
|
pollerState.enterFallbackCandidateKey = null;
|
|
566
|
-
suppressUnclassifiedFrame(prompt, dialogGate);
|
|
419
|
+
(0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
|
|
567
420
|
return false;
|
|
568
421
|
}
|
|
569
422
|
const screenKey = (0, auto_yes_enter_fallback_1.enterFallbackScreenKey)(promptData);
|
|
@@ -571,8 +424,8 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
571
424
|
if (pollerState.enterFallbackSentKey === frameKey) {
|
|
572
425
|
pollerState.enterFallbackCandidateKey = null;
|
|
573
426
|
(0, auto_yes_enter_fallback_1.recordEnterFallbackNoEffect)(worktreeId, cliToolId, instanceId, screenKey);
|
|
574
|
-
suppressUnclassifiedFrame(prompt, dialogGate);
|
|
575
|
-
warnOncePerFrame(pollerState, `${frameKey}\u0000enter-fallback-no-effect`, 'poller:auto-yes-enter-fallback-no-effect', {
|
|
427
|
+
(0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
|
|
428
|
+
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000enter-fallback-no-effect`, 'poller:auto-yes-enter-fallback-no-effect', {
|
|
576
429
|
worktreeId,
|
|
577
430
|
cliToolId,
|
|
578
431
|
instanceId,
|
|
@@ -586,7 +439,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
586
439
|
const resolution = (0, auto_yes_resolver_1.resolveAutoAnswerWithPolicy)(promptData, policy);
|
|
587
440
|
if (resolution.suppressedBy) {
|
|
588
441
|
pollerState.enterFallbackCandidateKey = null;
|
|
589
|
-
suppressAndWarnOnce(prompt, {
|
|
442
|
+
(0, auto_yes_prompt_suppression_1.suppressAndWarnOnce)(prompt, {
|
|
590
443
|
reason: resolution.suppressedBy,
|
|
591
444
|
mode: policy?.mode ?? null,
|
|
592
445
|
promptType: promptData.type,
|
|
@@ -608,7 +461,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
608
461
|
if (resolution.answer === null ||
|
|
609
462
|
(promptData.type === 'multiple_choice' && (0, prompt_answer_semantic_1.isMultiSelectPrompt)(promptData))) {
|
|
610
463
|
pollerState.enterFallbackCandidateKey = null;
|
|
611
|
-
suppressUnclassifiedFrame(prompt, dialogGate);
|
|
464
|
+
(0, auto_yes_prompt_suppression_1.suppressUnclassifiedFrame)(prompt, dialogGate);
|
|
612
465
|
return false;
|
|
613
466
|
}
|
|
614
467
|
// 4. Seen once: wait for the next tick to see the same screen again.
|
|
@@ -629,7 +482,7 @@ async function tryEnterFallback(prompt, promptDetection, dialogGate, rawOutput)
|
|
|
629
482
|
const sessionName = manager_1.CLIToolManager.getInstance().getTool(cliToolId).getSessionName(worktreeId, instanceId);
|
|
630
483
|
const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
|
|
631
484
|
if (ownership === null || ownership.verdict === 'foreign') {
|
|
632
|
-
warnOncePerFrame(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
485
|
+
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
633
486
|
worktreeId,
|
|
634
487
|
cliToolId,
|
|
635
488
|
instanceId,
|
|
@@ -841,7 +694,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
841
694
|
}
|
|
842
695
|
// 2. Check for duplicate prompt (Issue #306)
|
|
843
696
|
const promptKey = (0, prompt_key_1.generatePromptKey)(promptDetection.promptData);
|
|
844
|
-
const frameKey = promptFrameKey(promptDetection.promptData);
|
|
697
|
+
const frameKey = (0, auto_yes_poller_state_1.promptFrameKey)(promptDetection.promptData);
|
|
845
698
|
if (isDuplicatePrompt(pollerState, promptKey)) {
|
|
846
699
|
return 'duplicate';
|
|
847
700
|
}
|
|
@@ -857,7 +710,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
857
710
|
};
|
|
858
711
|
// 3., 3.2., 3.5. Frames that are not Auto-Yes's to answer: see
|
|
859
712
|
// `suppressIfNotOursToAnswer`.
|
|
860
|
-
const notOurs = suppressIfNotOursToAnswer(judged, frame);
|
|
713
|
+
const notOurs = (0, auto_yes_prompt_suppression_1.suppressIfNotOursToAnswer)(judged, frame);
|
|
861
714
|
if (notOurs.kind !== 'dialog-gate-refused') {
|
|
862
715
|
pollerState.enterFallbackCandidateKey = null;
|
|
863
716
|
}
|
|
@@ -883,7 +736,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
883
736
|
// Issue #1684: the log line alone leaves a CLI-driven pipeline blind to
|
|
884
737
|
// why its worker stalled. Record the suppression so buildCurrentOutput
|
|
885
738
|
// can publish it (`autoYes.lastSuppression` in capture --json).
|
|
886
|
-
suppressAndWarnOnce(judged, {
|
|
739
|
+
(0, auto_yes_prompt_suppression_1.suppressAndWarnOnce)(judged, {
|
|
887
740
|
reason: resolution.suppressedBy,
|
|
888
741
|
mode: policy?.mode ?? null,
|
|
889
742
|
promptType: promptDetection.promptData.type,
|
|
@@ -907,7 +760,7 @@ async function detectAndRespondToPrompt(worktreeId, pollerState, cliToolId, clea
|
|
|
907
760
|
// server created. The poller keeps running; it just does not type.
|
|
908
761
|
const ownership = await (0, worktree_session_ownership_1.checkWorktreeSessionOwnership)(worktreeId, sessionName);
|
|
909
762
|
if (ownership === null || ownership.verdict === 'foreign') {
|
|
910
|
-
warnOncePerFrame(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
763
|
+
(0, auto_yes_poller_state_1.warnOncePerFrame)(pollerState, `${frameKey}\u0000${sessionName}\u0000${ownership ? 'foreign' : 'worktree_not_found'}`, 'poller:auto-yes-skipped-foreign-session', {
|
|
911
764
|
worktreeId,
|
|
912
765
|
cliToolId,
|
|
913
766
|
instanceId,
|
|
@@ -30,7 +30,7 @@ const global_state_1 = require("../global-state");
|
|
|
30
30
|
* What tells two prompts apart for the published record: the type, the
|
|
31
31
|
* question and the option labels.
|
|
32
32
|
*
|
|
33
|
-
* Not the poller's `promptFrameKey
|
|
33
|
+
* Not the poller's `promptFrameKey` (in `auto-yes-poller-state.ts`): that one also carries the cursor and
|
|
34
34
|
* `approvalTarget`, and the status API reads a different number of rows than
|
|
35
35
|
* the poller does, so `approvalTarget` can differ between the two readings of
|
|
36
36
|
* one screen. The record is matched against the status API's prompt (to say
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-Yes poller state and its once-per-frame WARN (Issue #3411).
|
|
4
|
+
*
|
|
5
|
+
* Moved out of auto-yes-poller.ts unchanged; the poller re-exports
|
|
6
|
+
* `AutoYesPollerState`. Must not import auto-yes-poller.ts (cycle).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.promptFrameKey = promptFrameKey;
|
|
10
|
+
exports.warnOncePerFrame = warnOncePerFrame;
|
|
11
|
+
const prompt_key_1 = require("../detection/prompt-key");
|
|
12
|
+
const logger_1 = require("../../lib/logger");
|
|
13
|
+
const logger = (0, logger_1.createLogger)('auto-yes-poller');
|
|
14
|
+
/**
|
|
15
|
+
* Issue #2995: what makes two ticks "the same prompt" for `warnOncePerFrame`.
|
|
16
|
+
*
|
|
17
|
+
* `generatePromptKey` alone is type + question, and the question the detector
|
|
18
|
+
* extracts from an agent's reply is often a fixed line above the list, so two
|
|
19
|
+
* different lists would share it. The option rows (and where the cursor sits)
|
|
20
|
+
* plus `approvalTarget` -- the current prompt's own panel, which leaves out
|
|
21
|
+
* scrollback and footers that change while the prompt does not -- separate them.
|
|
22
|
+
* Not the whole pane: a status bar that ticks every second would defeat it.
|
|
23
|
+
*/
|
|
24
|
+
function promptFrameKey(promptData) {
|
|
25
|
+
const options = promptData.type === 'multiple_choice'
|
|
26
|
+
? promptData.options.map((o) => `${o.isDefault ? '>' : ''}${o.number}.${o.label}`).join('\n')
|
|
27
|
+
: '';
|
|
28
|
+
return [(0, prompt_key_1.generatePromptKey)(promptData), options, promptData.approvalTarget ?? ''].join('\u0000');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Issue #2995: print a "did not answer" line as WARN once per frame.
|
|
32
|
+
*
|
|
33
|
+
* The poller re-reads a static pane every 2s, so a frame Auto-Yes leaves alone
|
|
34
|
+
* (a reply quoting `1. Yes / 2. No`, a launch dialog, a policy-withheld prompt,
|
|
35
|
+
* a foreign session) would otherwise print the same WARN on every tick. The
|
|
36
|
+
* first one stays WARN -- it is what tells a silently stalled worker apart --
|
|
37
|
+
* and repeats for the same frame drop to `debug`. The frame is identified by
|
|
38
|
+
* the event, `promptFrameKey()` and the event's own detail; a different
|
|
39
|
+
* prompt, a different reason, or a tick with no prompt / an answer sent (both
|
|
40
|
+
* clear the key) makes the next line WARN again.
|
|
41
|
+
* `recordPolicySuppression()` is not throttled: `capture --json` and `wait`
|
|
42
|
+
* read the latest record, not the log.
|
|
43
|
+
*/
|
|
44
|
+
function warnOncePerFrame(pollerState, frameKey, event, fields) {
|
|
45
|
+
const key = `${event}\u0000${frameKey}`;
|
|
46
|
+
if (pollerState.lastSkipWarnKey === key) {
|
|
47
|
+
logger.debug(event, { ...fields, repeated: true });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
pollerState.lastSkipWarnKey = key;
|
|
51
|
+
logger.warn(event, fields);
|
|
52
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The suppression exits of `detectAndRespondToPrompt` (Issue #3214), moved out
|
|
4
|
+
* of auto-yes-poller.ts unchanged (Issue #3411). Must not import
|
|
5
|
+
* auto-yes-poller.ts (cycle) or `../tmux/tmux` (tmux import allowlist).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.suppressAndWarnOnce = suppressAndWarnOnce;
|
|
9
|
+
exports.suppressIfNotOursToAnswer = suppressIfNotOursToAnswer;
|
|
10
|
+
exports.suppressUnclassifiedFrame = suppressUnclassifiedFrame;
|
|
11
|
+
const detect_1 = require("../detection/tools/codex/detect");
|
|
12
|
+
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
13
|
+
const auto_yes_suppression_state_1 = require("./auto-yes-suppression-state");
|
|
14
|
+
const auto_yes_dialog_gate_1 = require("./auto-yes-dialog-gate");
|
|
15
|
+
const auto_yes_poller_state_1 = require("./auto-yes-poller-state");
|
|
16
|
+
/**
|
|
17
|
+
* Issue #3214: the form the four suppression exits of `detectAndRespondToPrompt`
|
|
18
|
+
* share. Records why Auto-Yes left the prompt alone (`recordPolicySuppression`,
|
|
19
|
+
* not throttled), then prints the WARN once per frame (`warnOncePerFrame`).
|
|
20
|
+
*
|
|
21
|
+
* @param suppression - Handed to `recordPolicySuppression` as is
|
|
22
|
+
* @param warnFrameKey - Handed to `warnOncePerFrame` as is: the prompt's
|
|
23
|
+
* `frameKey` plus the exit's own detail
|
|
24
|
+
* @param event - The WARN's log name
|
|
25
|
+
* @param detail - The WARN's fields after `worktreeId`, `cliToolId` and `instanceId`
|
|
26
|
+
*/
|
|
27
|
+
function suppressAndWarnOnce(prompt, suppression, warnFrameKey, event, detail) {
|
|
28
|
+
const { worktreeId, cliToolId, instanceId } = prompt;
|
|
29
|
+
(0, auto_yes_suppression_state_1.recordPolicySuppression)(worktreeId, cliToolId, instanceId, suppression);
|
|
30
|
+
(0, auto_yes_poller_state_1.warnOncePerFrame)(prompt.pollerState, warnFrameKey, event, {
|
|
31
|
+
worktreeId,
|
|
32
|
+
cliToolId,
|
|
33
|
+
instanceId,
|
|
34
|
+
...detail,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Issue #3214: steps 3, 3.2 and 3.5 of `detectAndRespondToPrompt` -- the frames
|
|
39
|
+
* that read as a prompt and are still not Auto-Yes's to answer, judged in the
|
|
40
|
+
* order they always were. "The detection above" in the comments below is that
|
|
41
|
+
* function's step 1.
|
|
42
|
+
*
|
|
43
|
+
* Issue #3397: step 3.5 no longer records its refusal here. It hands the gate's
|
|
44
|
+
* verdict back so `tryEnterFallback` can look at the frame first, and records
|
|
45
|
+
* through {@link suppressUnclassifiedFrame} when that sends nothing. Steps 3 and
|
|
46
|
+
* 3.2 still win: an Enter never reaches codex's launch dialogs or its `/model`
|
|
47
|
+
* picker.
|
|
48
|
+
*
|
|
49
|
+
* @param frame - The tick's one normalised frame (Issue #3183)
|
|
50
|
+
*/
|
|
51
|
+
function suppressIfNotOursToAnswer(prompt, frame) {
|
|
52
|
+
const { cliToolId, promptData, frameKey } = prompt;
|
|
53
|
+
// 3. Issue #1829: codex's own launch dialogs are CodexTool.waitForReady()'s
|
|
54
|
+
// to answer, not the poller's. Every one of them defaults to option 1, and
|
|
55
|
+
// the base rules answer the default:
|
|
56
|
+
//
|
|
57
|
+
// "Hooks need review" -> 1. Review hooks (undoes Issue #1760; the pane
|
|
58
|
+
// then sits two screens deep in a review UI that
|
|
59
|
+
// only `t`/`esc` leave, reported as `running`)
|
|
60
|
+
// "Update available" -> 1. Update now (undoes Issue #890; runs
|
|
61
|
+
// `npm install -g @openai/codex`, killing codex)
|
|
62
|
+
// "Do you trust …" -> 1. Yes, continue
|
|
63
|
+
//
|
|
64
|
+
// waitForReady answers the same screens deliberately and differently ('3',
|
|
65
|
+
// '2', '1' — each without a trailing Enter), but only during startSession,
|
|
66
|
+
// while this poller runs on its own 2s phase for the life of the session.
|
|
67
|
+
// Whichever sees the dialog first decides, so the fix is to leave them all
|
|
68
|
+
// to the tool. Auto-answer layer only: the detection above still reports the
|
|
69
|
+
// prompt, so the human keeps seeing the screen and the response poller
|
|
70
|
+
// still notifies them about it.
|
|
71
|
+
const launchDialog = cliToolId === 'codex' ? (0, cli_patterns_1.getCodexLifecycleDialog)(frame) : null;
|
|
72
|
+
if (launchDialog) {
|
|
73
|
+
// Recorded through the #1684 channel so `capture --json` and `cmate wait`
|
|
74
|
+
// can name the reason instead of showing a worker that went quiet.
|
|
75
|
+
suppressAndWarnOnce(prompt, { reason: 'agent-launch-dialog', mode: null, promptType: promptData.type }, `${frameKey}\u0000${launchDialog}`, 'poller:auto-yes-skipped-launch-dialog', { dialog: launchDialog, promptType: promptData.type });
|
|
76
|
+
return { kind: 'left-alone' };
|
|
77
|
+
}
|
|
78
|
+
// 3.2. Issue #3062: codex's `/model` picker (both stages) is not ours to
|
|
79
|
+
// answer. A digit there is an immediate decision, so the base rules'
|
|
80
|
+
// default would choose the model and effort for the operator. Judged by the
|
|
81
|
+
// picker's own footer, not by `kind: picker`, which the hooks screens share.
|
|
82
|
+
// The tool's `detectPrompt` still reports the prompt, so `/prompt-response`
|
|
83
|
+
// (the human's answer) is unaffected. Reuses `unclassified-frame`: the tool
|
|
84
|
+
// recognised the frame and deliberately declined it.
|
|
85
|
+
if (cliToolId === 'codex' && (0, detect_1.isCodexModelPickerFrame)(frame)) {
|
|
86
|
+
suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000codex-model-picker`, 'poller:auto-yes-skipped-model-picker', { promptType: promptData.type });
|
|
87
|
+
return { kind: 'left-alone' };
|
|
88
|
+
}
|
|
89
|
+
// 3.5. Issue #1928 (§4 D1 decision 4): the generic numbered-list inference is
|
|
90
|
+
// not enough to send an answer. The detection above judges the ROWS, and the
|
|
91
|
+
// rows of an agent's own reply can be indistinguishable from a dialog's --
|
|
92
|
+
// opencode 1.18 answering "list three options and ask which one" is the
|
|
93
|
+
// reported case (#1896), and the `1` this poller sent in reply was not
|
|
94
|
+
// answering anything, it was SENT AS A USER UTTERANCE. What separates the two
|
|
95
|
+
// is position and chrome, which only the tool's own module knows, so the
|
|
96
|
+
// decision is delegated to `detectDialog` (the seam #1927 declared).
|
|
97
|
+
//
|
|
98
|
+
// Per tool, and only for tools whose dialogs were measured from their own
|
|
99
|
+
// live captures -- see AUTO_YES_DIALOG_GATE_DEFAULT_MODE. An ungated tool
|
|
100
|
+
// reaches `allowed: true` without being judged, which is the pre-#1928
|
|
101
|
+
// behaviour and the right one where nobody has measured anything.
|
|
102
|
+
//
|
|
103
|
+
// Recorded through the #1684 channel with the reason code #1924 landed for
|
|
104
|
+
// exactly this position, so `capture --json` and `cmate wait` can name the
|
|
105
|
+
// gap instead of showing a worker that silently went quiet.
|
|
106
|
+
const dialogGate = (0, auto_yes_dialog_gate_1.evaluateAutoYesDialogGate)(cliToolId, promptData.type, frame);
|
|
107
|
+
if (!dialogGate.allowed) {
|
|
108
|
+
return { kind: 'dialog-gate-refused', dialogGate };
|
|
109
|
+
}
|
|
110
|
+
return { kind: 'ours' };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Step 3.5's record: the tool's dialog detector did not vouch for the frame and
|
|
114
|
+
* nothing was sent (Issue #1928). Split out of `suppressIfNotOursToAnswer` by
|
|
115
|
+
* Issue #3397, unchanged, so it runs after `tryEnterFallback` declined.
|
|
116
|
+
*/
|
|
117
|
+
function suppressUnclassifiedFrame(prompt, dialogGate) {
|
|
118
|
+
const { promptData, frameKey } = prompt;
|
|
119
|
+
suppressAndWarnOnce(prompt, { reason: 'unclassified-frame', mode: null, promptType: promptData.type }, `${frameKey}\u0000${dialogGate.dialog?.kind ?? ''}\u0000${dialogGate.mode}`, 'poller:auto-yes-skipped-unclassified-frame', {
|
|
120
|
+
promptType: promptData.type,
|
|
121
|
+
dialogKind: dialogGate.dialog?.kind ?? null,
|
|
122
|
+
answerMode: dialogGate.dialog?.answerMode ?? null,
|
|
123
|
+
gateMode: dialogGate.mode,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
@@ -6,15 +6,53 @@
|
|
|
6
6
|
* Uses SHA-256 content hash with an in-memory cache (Map<pollerKey, hash>).
|
|
7
7
|
* Design decision [DR1-006]: In-memory only (no DB layer).
|
|
8
8
|
* Process restart may cause a single duplicate, which is acceptable (KISS).
|
|
9
|
+
*
|
|
10
|
+
* ## How often the skip says so (Issue #3538)
|
|
11
|
+
*
|
|
12
|
+
* A full-screen TUI (copilot, opencode) keeps polling while it sits on a
|
|
13
|
+
* prompt, so the same prompt is a duplicate on every 2 s tick until it is
|
|
14
|
+
* answered, and `duplicate-prompt-skipped` used to be logged on each of them.
|
|
15
|
+
* The run of duplicate ticks is counted here, the same way `response-dedup`
|
|
16
|
+
* counts `duplicate-response-skipped` (#3519): the line is written on the first
|
|
17
|
+
* tick of a run and then once per {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}
|
|
18
|
+
* ticks. The count is about one particular cached hash, so it never outlives
|
|
19
|
+
* it — the functions below that drop or move the hash drop or move the count
|
|
20
|
+
* too. The one exception runs the other way: a tick that reads a frame with no
|
|
21
|
+
* live prompt on it ends the run without touching the hash
|
|
22
|
+
* ({@link resetDuplicatePromptSkipStreak}), because the prompt left the screen
|
|
23
|
+
* while the guard still has to keep it from being saved twice. A returning
|
|
24
|
+
* prompt is then still suppressed, but its first duplicate logs as the first
|
|
25
|
+
* of a new run.
|
|
26
|
+
*
|
|
27
|
+
* Only the log line is thinned. `recordPromptDedupSkip` (#1695) is still called
|
|
28
|
+
* on every duplicate tick; its tally is what `capture --json` reports.
|
|
9
29
|
*/
|
|
10
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = void 0;
|
|
11
32
|
exports.isDuplicatePrompt = isDuplicatePrompt;
|
|
33
|
+
exports.claimDuplicatePromptSkipLog = claimDuplicatePromptSkipLog;
|
|
34
|
+
exports.resetDuplicatePromptSkipStreak = resetDuplicatePromptSkipStreak;
|
|
12
35
|
exports.clearPromptHashCache = clearPromptHashCache;
|
|
13
36
|
exports.renamePromptHashCacheKey = renamePromptHashCacheKey;
|
|
14
37
|
exports.normalizePromptForDedup = normalizePromptForDedup;
|
|
15
38
|
const crypto_1 = require("crypto");
|
|
16
39
|
const global_state_1 = require("../global-state");
|
|
40
|
+
const response_dedup_1 = require("./response-dedup");
|
|
17
41
|
const promptHashCache = (0, global_state_1.getOrInitGlobal)('__promptHashCache', () => new Map());
|
|
42
|
+
/**
|
|
43
|
+
* In-memory cache: pollerKey -> how many duplicate ticks in a row the current
|
|
44
|
+
* cached prompt hash has been skipped (Issue #3538). Absence means none yet.
|
|
45
|
+
*
|
|
46
|
+
* On `globalThis` for the same reason `promptHashCache` is, and cleared and
|
|
47
|
+
* moved at exactly the same moments — see the module comment.
|
|
48
|
+
*/
|
|
49
|
+
const duplicatePromptSkipStreak = (0, global_state_1.getOrInitGlobal)('__duplicatePromptSkipStreak', () => new Map());
|
|
50
|
+
/**
|
|
51
|
+
* How many duplicate ticks separate two `duplicate-prompt-skipped` lines
|
|
52
|
+
* (Issue #3538). Shared with `duplicate-response-skipped` (#3519): one line a
|
|
53
|
+
* minute at the poller's 2 s tick.
|
|
54
|
+
*/
|
|
55
|
+
exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL = response_dedup_1.DUPLICATE_RESPONSE_SKIP_LOG_TICK_INTERVAL;
|
|
18
56
|
/**
|
|
19
57
|
* Check if the given prompt content is a duplicate of the last saved prompt
|
|
20
58
|
* for the same pollerKey. If not a duplicate, updates the cache.
|
|
@@ -29,8 +67,46 @@ function isDuplicatePrompt(pollerKey, content) {
|
|
|
29
67
|
return true;
|
|
30
68
|
}
|
|
31
69
|
promptHashCache.set(pollerKey, hash);
|
|
70
|
+
// Issue #3538: a new prompt starts a new run of duplicate ticks.
|
|
71
|
+
duplicatePromptSkipStreak.delete(pollerKey);
|
|
32
72
|
return false;
|
|
33
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Count one duplicate prompt tick for this pollerKey and say whether it should
|
|
76
|
+
* be logged (Issue #3538).
|
|
77
|
+
*
|
|
78
|
+
* A claim: it advances the count, so call it exactly once per duplicate tick.
|
|
79
|
+
* Logs on the first tick of a run and then on every
|
|
80
|
+
* {@link DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL}th tick after it (ticks 1,
|
|
81
|
+
* 1+N, 1+2N, …).
|
|
82
|
+
*
|
|
83
|
+
* @param pollerKey - Poller key ("worktreeId:cliToolId")
|
|
84
|
+
* @returns Whether to log, the run length so far, and the ticks left unlogged
|
|
85
|
+
* since the previous line
|
|
86
|
+
*/
|
|
87
|
+
function claimDuplicatePromptSkipLog(pollerKey) {
|
|
88
|
+
const consecutive = (duplicatePromptSkipStreak.get(pollerKey) ?? 0) + 1;
|
|
89
|
+
duplicatePromptSkipStreak.set(pollerKey, consecutive);
|
|
90
|
+
const log = (consecutive - 1) % exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL === 0;
|
|
91
|
+
const suppressed = consecutive === 1 ? 0 : exports.DUPLICATE_PROMPT_SKIP_LOG_TICK_INTERVAL - 1;
|
|
92
|
+
return { log, consecutive, suppressed };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* End the current run of duplicate prompt ticks for this pollerKey without
|
|
96
|
+
* touching the cached hash (Issue #3538).
|
|
97
|
+
*
|
|
98
|
+
* Called by `response-checker` on every tick that does not reach the prompt
|
|
99
|
+
* guard with a live prompt — a working screen, an unfinished frame, a reply.
|
|
100
|
+
* The prompt is no longer on screen, so if the same prompt comes back the next
|
|
101
|
+
* duplicate is the first of a new run and logs. The hash stays: whether the
|
|
102
|
+
* returning prompt is saved again is the guard's question (#565), and this
|
|
103
|
+
* Issue only thins the log line.
|
|
104
|
+
*
|
|
105
|
+
* @param pollerKey - Poller key ("worktreeId:cliToolId")
|
|
106
|
+
*/
|
|
107
|
+
function resetDuplicatePromptSkipStreak(pollerKey) {
|
|
108
|
+
duplicatePromptSkipStreak.delete(pollerKey);
|
|
109
|
+
}
|
|
34
110
|
/**
|
|
35
111
|
* Clear the prompt hash cache for a specific pollerKey.
|
|
36
112
|
* Called during session cleanup / stopPolling.
|
|
@@ -39,6 +115,8 @@ function isDuplicatePrompt(pollerKey, content) {
|
|
|
39
115
|
*/
|
|
40
116
|
function clearPromptHashCache(pollerKey) {
|
|
41
117
|
promptHashCache.delete(pollerKey);
|
|
118
|
+
// Issue #3538: the skip count is about the same hash.
|
|
119
|
+
duplicatePromptSkipStreak.delete(pollerKey);
|
|
42
120
|
}
|
|
43
121
|
/**
|
|
44
122
|
* Move the cached hash from one pollerKey to another.
|
|
@@ -57,9 +135,22 @@ function renamePromptHashCacheKey(oldKey, newKey) {
|
|
|
57
135
|
if (oldKey === newKey)
|
|
58
136
|
return;
|
|
59
137
|
const hash = promptHashCache.get(oldKey);
|
|
138
|
+
const streak = duplicatePromptSkipStreak.get(oldKey);
|
|
60
139
|
promptHashCache.delete(oldKey);
|
|
61
|
-
|
|
62
|
-
|
|
140
|
+
duplicatePromptSkipStreak.delete(oldKey);
|
|
141
|
+
if (hash === undefined)
|
|
142
|
+
return;
|
|
143
|
+
promptHashCache.set(newKey, hash);
|
|
144
|
+
// Issue #3538: the skip count moves with the hash, so a rename mid-run does
|
|
145
|
+
// not restart the thinning and log as if a new run had begun. Whenever the
|
|
146
|
+
// hash under `newKey` is replaced, so is its count — a count left over from
|
|
147
|
+
// the hash that was there before is about a different prompt.
|
|
148
|
+
if (streak !== undefined) {
|
|
149
|
+
duplicatePromptSkipStreak.set(newKey, streak);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
duplicatePromptSkipStreak.delete(newKey);
|
|
153
|
+
}
|
|
63
154
|
}
|
|
64
155
|
/**
|
|
65
156
|
* Normalize prompt content before deduplication check.
|