commandmate 0.25.0 → 0.26.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 -1
- package/.next/app-build-manifest.json +117 -117
- package/.next/app-path-routes-manifest.json +22 -22
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- 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/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/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/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.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/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/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/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/todos/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_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]/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.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]/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]/interrupt/route_client-reference-manifest.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]/prompt-response/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_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]/start-polling/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/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/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +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 +22 -22
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/4455.js +1 -1
- package/.next/server/chunks/5959.js +4 -4
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6274.js +12 -9
- package/.next/server/chunks/7458.js +6 -6
- package/.next/server/chunks/9373.js +4 -3
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{6381-51f9c4a128aadc0b.js → 6381-9303430933084b56.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-207c69540fd856ef.js → page-fbdf4621a2911ac1.js} +1 -1
- package/.next/trace +12 -12
- package/dist/cli/commands/task.d.ts.map +1 -1
- package/dist/cli/commands/task.js +2 -1
- package/dist/cli/commands/verify.d.ts +14 -0
- package/dist/cli/commands/verify.d.ts.map +1 -1
- package/dist/cli/commands/verify.js +109 -3
- package/dist/cli/commands/wait.d.ts +2 -0
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +184 -4
- package/dist/cli/types/api-responses.d.ts +172 -0
- package/dist/cli/types/api-responses.d.ts.map +1 -1
- package/dist/cli/types/index.d.ts +31 -0
- package/dist/cli/types/index.d.ts.map +1 -1
- package/dist/cli/types/index.js +26 -0
- package/dist/cli/utils/verify-runner.d.ts +36 -1
- package/dist/cli/utils/verify-runner.d.ts.map +1 -1
- package/dist/cli/utils/verify-runner.js +114 -6
- package/dist/server/src/lib/auto-yes-state.js +108 -3
- package/dist/server/src/lib/cli-tools/antigravity.js +8 -4
- package/dist/server/src/lib/cli-tools/codex.js +5 -1
- package/dist/server/src/lib/cli-tools/copilot.js +5 -1
- package/dist/server/src/lib/cli-tools/gemini.js +15 -15
- package/dist/server/src/lib/cli-tools/opencode.js +9 -3
- package/dist/server/src/lib/detection/upstream-faults.js +120 -0
- package/dist/server/src/lib/hooks/permission-decision-service.js +13 -5
- package/dist/server/src/lib/hooks/sources/antigravity/hooks-config.js +18 -11
- package/dist/server/src/lib/hooks/sources/antigravity/source.js +10 -9
- package/dist/server/src/lib/hooks/sources/claude/source.js +5 -2
- package/dist/server/src/lib/hooks/sources/codex/hooks-config.js +26 -18
- package/dist/server/src/lib/hooks/sources/codex/source.js +6 -9
- package/dist/server/src/lib/hooks/sources/copilot/hook-settings.js +22 -12
- package/dist/server/src/lib/hooks/sources/copilot/source.js +1 -1
- package/dist/server/src/lib/hooks/sources/define-source.js +6 -4
- package/dist/server/src/lib/hooks/sources/gemini/settings-generator.js +14 -8
- package/dist/server/src/lib/hooks/sources/gemini/source.js +29 -29
- package/dist/server/src/lib/hooks/sources/index.js +4 -1
- package/dist/server/src/lib/hooks/sources/launch-command.js +61 -0
- package/dist/server/src/lib/hooks/sources/legacy-relay.js +2 -1
- package/dist/server/src/lib/hooks/sources/opencode/source.js +10 -7
- package/dist/server/src/lib/polling/prompt-dedup-state.js +95 -0
- package/dist/server/src/lib/polling/response-checker.js +12 -0
- package/dist/server/src/lib/polling/response-poller-core.js +11 -1
- package/dist/server/src/lib/session/agent-session-lifecycle.js +28 -7
- package/dist/server/src/lib/session/claude-session.js +5 -1
- package/dist/server/src/lib/session/current-output-builder.js +34 -0
- package/dist/server/src/lib/verification/env-snapshot.js +21 -3
- package/dist/server/src/lib/verification/gate-runner.js +258 -7
- package/dist/server/src/lib/verification/machine-lock.js +204 -0
- package/dist/server/src/lib/verification/scope-gate.js +126 -13
- package/dist/server/src/lib/verification/verify-config.js +85 -3
- package/dist/server/src/lib/verification/worktree-index.js +140 -0
- package/package.json +1 -1
- /package/.next/static/{ehkNc5xDMDkXlua08Bwq0 → hNdNiFes2xLls68SWhAU9}/_buildManifest.js +0 -0
- /package/.next/static/{ehkNc5xDMDkXlua08Bwq0 → hNdNiFes2xLls68SWhAU9}/_ssgManifest.js +0 -0
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* auto-yes-poller.ts -> auto-yes-state.ts -> path-validator.ts
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.THINKING_CHECK_LINE_COUNT = exports.MAX_CONCURRENT_POLLERS = exports.MAX_CONSECUTIVE_ERRORS = exports.MAX_BACKOFF_MS = exports.DUPLICATE_RETRY_EXPIRY_MS = exports.COOLDOWN_INTERVAL_MS = exports.POLLING_INTERVAL_MS = exports.executeRegexWithTimeout = exports.COMPOSITE_KEY_SEPARATOR = void 0;
|
|
14
|
+
exports.THINKING_CHECK_LINE_COUNT = exports.MAX_CONCURRENT_POLLERS = exports.MAX_CONSECUTIVE_ERRORS = exports.MAX_BACKOFF_MS = exports.DUPLICATE_RETRY_EXPIRY_MS = exports.COOLDOWN_INTERVAL_MS = exports.POLLING_INTERVAL_MS = exports.STOP_MATCH_EXCERPT_CONTEXT_LINES = exports.STOP_MATCH_EXCERPT_TRUNCATION_MARKER = exports.STOP_MATCH_EXCERPT_MAX_BYTES = exports.executeRegexWithTimeout = exports.COMPOSITE_KEY_SEPARATOR = void 0;
|
|
15
15
|
exports.buildCompositeKey = buildCompositeKey;
|
|
16
16
|
exports.extractWorktreeId = extractWorktreeId;
|
|
17
17
|
exports.extractCliToolId = extractCliToolId;
|
|
@@ -21,6 +21,7 @@ exports.getAutoYesState = getAutoYesState;
|
|
|
21
21
|
exports.setAutoYesEnabled = setAutoYesEnabled;
|
|
22
22
|
exports.disableAutoYes = disableAutoYes;
|
|
23
23
|
exports.clearAllAutoYesStates = clearAllAutoYesStates;
|
|
24
|
+
exports.truncateToUtf8Bytes = truncateToUtf8Bytes;
|
|
24
25
|
exports.checkStopCondition = checkStopCondition;
|
|
25
26
|
exports.deleteAutoYesState = deleteAutoYesState;
|
|
26
27
|
exports.getAutoYesStateCompositeKeys = getAutoYesStateCompositeKeys;
|
|
@@ -204,9 +205,12 @@ function setAutoYesEnabled(worktreeId, cliToolId, enabled, duration, stopPattern
|
|
|
204
205
|
* @param cliToolId - CLI tool type (default: 'claude')
|
|
205
206
|
* @param reason - Optional reason for disabling ('expired' | 'stop_pattern_matched')
|
|
206
207
|
* @param instanceId - Optional instance identifier (Issue #896; defaults to primary)
|
|
208
|
+
* @param stopMatchedText - Optional excerpt of what the stop pattern matched
|
|
209
|
+
* (Issue #1694). Deliberately *not* carried over from the existing state: it
|
|
210
|
+
* belongs to `reason`, so a disable for any other reason stores none.
|
|
207
211
|
* @returns Updated auto-yes state
|
|
208
212
|
*/
|
|
209
|
-
function disableAutoYes(worktreeId, cliToolId = 'claude', reason, instanceId) {
|
|
213
|
+
function disableAutoYes(worktreeId, cliToolId = 'claude', reason, instanceId, stopMatchedText) {
|
|
210
214
|
const key = buildCompositeKey(worktreeId, cliToolId, instanceId);
|
|
211
215
|
const existing = autoYesStates.get(key);
|
|
212
216
|
const state = {
|
|
@@ -215,6 +219,7 @@ function disableAutoYes(worktreeId, cliToolId = 'claude', reason, instanceId) {
|
|
|
215
219
|
expiresAt: existing?.expiresAt ?? 0,
|
|
216
220
|
stopPattern: existing?.stopPattern,
|
|
217
221
|
stopReason: reason,
|
|
222
|
+
stopMatchedText,
|
|
218
223
|
};
|
|
219
224
|
autoYesStates.set(key, state);
|
|
220
225
|
return state;
|
|
@@ -235,6 +240,103 @@ function clearAllAutoYesStates() {
|
|
|
235
240
|
*/
|
|
236
241
|
var auto_yes_config_2 = require("../config/auto-yes-config");
|
|
237
242
|
Object.defineProperty(exports, "executeRegexWithTimeout", { enumerable: true, get: function () { return auto_yes_config_2.executeRegexWithTimeout; } });
|
|
243
|
+
/**
|
|
244
|
+
* Maximum size of the stop-pattern excerpt, in UTF-8 bytes, marker included
|
|
245
|
+
* (Issue #1694).
|
|
246
|
+
*
|
|
247
|
+
* The excerpt answers one question — "is this a real stop or did a build log
|
|
248
|
+
* happen to contain the pattern?" (#1678 A-5) — and a couple of lines answer
|
|
249
|
+
* it. The cap is a hard bound rather than a hint because `cleanOutput` is a
|
|
250
|
+
* whole terminal frame and a greedy pattern can match all of it, and because
|
|
251
|
+
* the value rides in every `capture --json` poll for the rest of the session.
|
|
252
|
+
*/
|
|
253
|
+
exports.STOP_MATCH_EXCERPT_MAX_BYTES = 400;
|
|
254
|
+
/**
|
|
255
|
+
* Appended when the excerpt was cut to fit {@link STOP_MATCH_EXCERPT_MAX_BYTES}.
|
|
256
|
+
*
|
|
257
|
+
* Cutting silently would make a fragment read as the whole match, which is the
|
|
258
|
+
* wrong conclusion in exactly the triage this field exists for.
|
|
259
|
+
*/
|
|
260
|
+
exports.STOP_MATCH_EXCERPT_TRUNCATION_MARKER = '…[truncated]';
|
|
261
|
+
/** Lines of context kept on either side of the match (Issue #1694). */
|
|
262
|
+
exports.STOP_MATCH_EXCERPT_CONTEXT_LINES = 1;
|
|
263
|
+
/** Shared encoder for byte-length math (UTF-8 by spec). */
|
|
264
|
+
const utf8Encoder = new TextEncoder();
|
|
265
|
+
/** UTF-8 byte length of a string. */
|
|
266
|
+
function utf8ByteLength(text) {
|
|
267
|
+
return utf8Encoder.encode(text).length;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Cut `text` down to `maxBytes` UTF-8 bytes, marker included, never splitting a
|
|
271
|
+
* character (Issue #1694).
|
|
272
|
+
*
|
|
273
|
+
* The budget is measured in bytes rather than characters because the excerpt is
|
|
274
|
+
* copied verbatim into a JSON payload, and a Japanese terminal frame costs 3
|
|
275
|
+
* bytes per character — a character cap would let one frame publish three times
|
|
276
|
+
* the intended payload.
|
|
277
|
+
*
|
|
278
|
+
* @internal Exported for testing purposes only.
|
|
279
|
+
* @param text - Text to bound
|
|
280
|
+
* @param maxBytes - Byte ceiling for the returned string (marker included)
|
|
281
|
+
* @returns `text` unchanged when it already fits, otherwise a prefix of it with
|
|
282
|
+
* {@link STOP_MATCH_EXCERPT_TRUNCATION_MARKER} appended
|
|
283
|
+
*/
|
|
284
|
+
function truncateToUtf8Bytes(text, maxBytes = exports.STOP_MATCH_EXCERPT_MAX_BYTES) {
|
|
285
|
+
if (utf8ByteLength(text) <= maxBytes)
|
|
286
|
+
return text;
|
|
287
|
+
const budget = maxBytes - utf8ByteLength(exports.STOP_MATCH_EXCERPT_TRUNCATION_MARKER);
|
|
288
|
+
if (budget <= 0)
|
|
289
|
+
return exports.STOP_MATCH_EXCERPT_TRUNCATION_MARKER;
|
|
290
|
+
let used = 0;
|
|
291
|
+
let kept = '';
|
|
292
|
+
// for..of iterates code points, so a surrogate pair is never halved.
|
|
293
|
+
for (const char of text) {
|
|
294
|
+
const size = utf8ByteLength(char);
|
|
295
|
+
if (used + size > budget)
|
|
296
|
+
break;
|
|
297
|
+
kept += char;
|
|
298
|
+
used += size;
|
|
299
|
+
}
|
|
300
|
+
return kept + exports.STOP_MATCH_EXCERPT_TRUNCATION_MARKER;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Build the excerpt shown for a stop-pattern hit (Issue #1694).
|
|
304
|
+
*
|
|
305
|
+
* Whole lines, not the raw match: a pattern like `error` matched against a
|
|
306
|
+
* terminal frame yields the six characters `error`, which tells the operator
|
|
307
|
+
* nothing about whether it came from the agent asking a question or from a
|
|
308
|
+
* dependency's build log scrolling past. The surrounding line is the smallest
|
|
309
|
+
* unit that carries that.
|
|
310
|
+
*
|
|
311
|
+
* @param regex - The already-constructed stop pattern (no `g` flag, so `exec`
|
|
312
|
+
* is position-independent and does not disturb the caller's `test`)
|
|
313
|
+
* @param output - ANSI-stripped terminal output the pattern matched
|
|
314
|
+
* @returns Bounded excerpt, or null when the match cannot be located
|
|
315
|
+
*/
|
|
316
|
+
function buildStopMatchExcerpt(regex, output) {
|
|
317
|
+
let match;
|
|
318
|
+
try {
|
|
319
|
+
match = regex.exec(output);
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (!match)
|
|
325
|
+
return null;
|
|
326
|
+
const matchStart = match.index;
|
|
327
|
+
const matchEnd = matchStart + match[0].length;
|
|
328
|
+
const lines = output.split('\n');
|
|
329
|
+
const firstLine = output.slice(0, matchStart).split('\n').length - 1;
|
|
330
|
+
const lastLine = output.slice(0, matchEnd).split('\n').length - 1;
|
|
331
|
+
const from = Math.max(0, firstLine - exports.STOP_MATCH_EXCERPT_CONTEXT_LINES);
|
|
332
|
+
const to = Math.min(lines.length - 1, lastLine + exports.STOP_MATCH_EXCERPT_CONTEXT_LINES);
|
|
333
|
+
const raw = lines.slice(from, to + 1).join('\n');
|
|
334
|
+
// Terminal frames are padded with blank lines and trailing spaces; those are
|
|
335
|
+
// pure noise here and eat the byte budget. A whitespace-only match keeps its
|
|
336
|
+
// raw form rather than becoming an empty, unexplained field.
|
|
337
|
+
const trimmed = raw.trim();
|
|
338
|
+
return truncateToUtf8Bytes(trimmed.length > 0 ? trimmed : raw);
|
|
339
|
+
}
|
|
238
340
|
/**
|
|
239
341
|
* Check if the terminal output matches the stop condition pattern.
|
|
240
342
|
* If matched, disables auto-yes for the worktree/agent.
|
|
@@ -275,7 +377,10 @@ function checkStopCondition(compositeKey, cleanOutput, onStopMatched) {
|
|
|
275
377
|
return false;
|
|
276
378
|
}
|
|
277
379
|
if (matched) {
|
|
278
|
-
|
|
380
|
+
// Issue #1694: what matched, not just that something did. Built here
|
|
381
|
+
// because this is the only frame that still holds the output.
|
|
382
|
+
const stopMatchedText = buildStopMatchExcerpt(regex, cleanOutput) ?? undefined;
|
|
383
|
+
disableAutoYes(worktreeId, cliToolId, 'stop_pattern_matched', instanceId, stopMatchedText);
|
|
279
384
|
if (onStopMatched) {
|
|
280
385
|
onStopMatched(compositeKey);
|
|
281
386
|
}
|
|
@@ -136,15 +136,19 @@ class AntigravityTool extends base_1.BaseCLITool {
|
|
|
136
136
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
137
137
|
// Start agy in interactive mode, optionally pinned to a model.
|
|
138
138
|
//
|
|
139
|
-
// Issue #1762:
|
|
139
|
+
// Issue #1762: the launch merges CommandMate's named hook into
|
|
140
140
|
// `~/.gemini/config/hooks.json` — agy's single global config, shared with
|
|
141
141
|
// gemini's tree and with whatever the user has in it — and prefixes
|
|
142
142
|
// `CM_HOOK_URL`. That variable is the *only* correlation channel agy has:
|
|
143
143
|
// its payloads carry no `cwd`, its hooks run in `~/.gemini/config`, and
|
|
144
144
|
// one file serves every worktree on the machine. `--model` is appended
|
|
145
|
-
// after, so the env
|
|
146
|
-
// `CM_AGENT_HOOKS_INJECT=0` returns bare `agy`, unchanged
|
|
147
|
-
const base = (0, agent_session_lifecycle_1.
|
|
145
|
+
// after the rendered line, so the env assignments stay in front of the
|
|
146
|
+
// command. `CM_AGENT_HOOKS_INJECT=0` returns bare `agy`, unchanged.
|
|
147
|
+
const base = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
148
|
+
target: { worktreeId, cliToolId: sources_1.ANTIGRAVITY_CLI_TOOL_ID, instanceId },
|
|
149
|
+
executablePath: this.command,
|
|
150
|
+
worktreePath,
|
|
151
|
+
});
|
|
148
152
|
const launchCommand = model ? `${base} --model ${shellSingleQuote(model)}` : base;
|
|
149
153
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
150
154
|
// Wait for agy to initialize
|
|
@@ -166,7 +166,11 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
166
166
|
// Falls back to the bare command on any failure and when
|
|
167
167
|
// `CM_AGENT_HOOKS_INJECT=0`; a session that starts without hooks is the
|
|
168
168
|
// pre-#1760 status quo, and a session that fails to start is not.
|
|
169
|
-
const launchCommand = (0, agent_session_lifecycle_1.
|
|
169
|
+
const launchCommand = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
170
|
+
target: { worktreeId, cliToolId: sources_1.CODEX_CLI_TOOL_ID, instanceId },
|
|
171
|
+
executablePath: this.command,
|
|
172
|
+
worktreePath,
|
|
173
|
+
});
|
|
170
174
|
// Start Codex CLI in interactive mode
|
|
171
175
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
172
176
|
// Wait for Codex to initialize
|
|
@@ -149,7 +149,11 @@ class CopilotTool extends base_1.BaseCLITool {
|
|
|
149
149
|
// Fails open in every branch: with `CM_AGENT_HOOKS_INJECT=0`, or with a
|
|
150
150
|
// settings file that cannot be read or written, this is byte-for-byte the
|
|
151
151
|
// `gh copilot` this line has always sent.
|
|
152
|
-
const launchCommand = (0, agent_session_lifecycle_1.
|
|
152
|
+
const launchCommand = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
153
|
+
target: { worktreeId, cliToolId: tool_id_1.COPILOT_CLI_TOOL_ID, instanceId },
|
|
154
|
+
executablePath: hook_settings_1.COPILOT_LAUNCH_COMMAND,
|
|
155
|
+
worktreePath,
|
|
156
|
+
});
|
|
153
157
|
// Start Copilot CLI in interactive mode
|
|
154
158
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
155
159
|
// Wait for Copilot to initialize
|
|
@@ -16,7 +16,6 @@ const submit_verified_sender_1 = require("./submit-verified-sender");
|
|
|
16
16
|
const tmux_capture_cache_1 = require("../tmux/tmux-capture-cache");
|
|
17
17
|
const cli_patterns_1 = require("../detection/cli-patterns");
|
|
18
18
|
const sources_1 = require("../../lib/hooks/sources");
|
|
19
|
-
const source_1 = require("../../lib/hooks/sources/gemini/source");
|
|
20
19
|
const agent_session_lifecycle_1 = require("../../lib/session/agent-session-lifecycle");
|
|
21
20
|
const logger_1 = require("../../lib/logger");
|
|
22
21
|
const cli_tool_timing_config_1 = require("../../config/cli-tool-timing-config");
|
|
@@ -92,23 +91,24 @@ class GeminiTool extends base_1.BaseCLITool {
|
|
|
92
91
|
});
|
|
93
92
|
// Wait a moment for the session to be created
|
|
94
93
|
await new Promise((resolve) => setTimeout(resolve, cli_tool_timing_config_1.TUI_SESSION_CREATE_WAIT_MS));
|
|
95
|
-
// Issue #1762: hand this worktree its own hooks config, merged into any
|
|
96
|
-
// `.gemini/settings.json` that is already there. Written from here rather
|
|
97
|
-
// than from the source's `prepareLaunch` because the worktree path is the
|
|
98
|
-
// one input `AgentInstanceRef` does not carry, and gemini is the only tool
|
|
99
|
-
// whose config is scoped to a worktree. Fail-open: a config that cannot be
|
|
100
|
-
// written costs the events and nothing else.
|
|
101
|
-
(0, source_1.injectGeminiHookSettings)(worktreePath, {
|
|
102
|
-
worktreeId,
|
|
103
|
-
cliToolId: sources_1.GEMINI_CLI_TOOL_ID,
|
|
104
|
-
instanceId,
|
|
105
|
-
});
|
|
106
94
|
// Start Gemini CLI in interactive mode (no flags = interactive REPL).
|
|
107
|
-
//
|
|
95
|
+
//
|
|
96
|
+
// Issue #1762 hands this worktree its own hooks config, merged into any
|
|
97
|
+
// `.gemini/settings.json` that is already there; Issue #1846 moved that
|
|
98
|
+
// write inside `prepareLaunch`, where it belongs — the worktree path is
|
|
99
|
+
// now part of the launch context, so the second entry point this file
|
|
100
|
+
// used to call (`injectGeminiHookSettings`) is gone.
|
|
101
|
+
//
|
|
102
|
+
// The rendered line prefixes `CM_HOOK_URL`, which is what tells the
|
|
108
103
|
// receiver which instance an event came from — `.gemini/settings.json` is
|
|
109
104
|
// per worktree and cannot. `CM_AGENT_HOOKS_INJECT=0` returns the bare
|
|
110
|
-
// command, unchanged from before
|
|
111
|
-
|
|
105
|
+
// command, unchanged from before these Issues. Fail-open throughout: a
|
|
106
|
+
// config that cannot be written costs the events and nothing else.
|
|
107
|
+
const launchCommand = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
108
|
+
target: { worktreeId, cliToolId: sources_1.GEMINI_CLI_TOOL_ID, instanceId },
|
|
109
|
+
executablePath: this.command,
|
|
110
|
+
worktreePath,
|
|
111
|
+
});
|
|
112
112
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
113
113
|
// Wait for Gemini to initialize (minimum wait for banner/auth)
|
|
114
114
|
await new Promise((resolve) => setTimeout(resolve, GEMINI_INIT_WAIT_MS));
|
|
@@ -148,9 +148,15 @@ class OpenCodeTool extends base_1.BaseCLITool {
|
|
|
148
148
|
// that nothing can read back (§5.9). Null means "no structured events",
|
|
149
149
|
// and the launch below is then the pre-#1763 bare command.
|
|
150
150
|
await (0, runtime_1.reserveOpencodeServerPort)(target, worktreePath);
|
|
151
|
-
// Start OpenCode TUI. `
|
|
152
|
-
// `AgentEventSource` for the
|
|
153
|
-
|
|
151
|
+
// Start OpenCode TUI. `buildAgentLaunchCommandLine` asks the tool's own
|
|
152
|
+
// `AgentEventSource` for the plan (S3/S4/S5) and never throws. opencode's
|
|
153
|
+
// environment is empty — it is the one source with no correlation
|
|
154
|
+
// variable, because CommandMate holds the connection (#1846).
|
|
155
|
+
const launchCommand = (0, agent_session_lifecycle_1.buildAgentLaunchCommandLine)({
|
|
156
|
+
target,
|
|
157
|
+
executablePath: this.command,
|
|
158
|
+
worktreePath,
|
|
159
|
+
});
|
|
154
160
|
await (0, tmux_1.sendKeys)(sessionName, launchCommand, true);
|
|
155
161
|
// Wait for OpenCode to initialize (GPU model loading via Ollama)
|
|
156
162
|
await new Promise((resolve) => setTimeout(resolve, exports.OPENCODE_INIT_WAIT_MS));
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Signatures of an upstream (model API) fault, as they appear on an agent's
|
|
4
|
+
* terminal frame (Issue #1839).
|
|
5
|
+
*
|
|
6
|
+
* Moved here from `scripts/canary/expectations.ts`, where Issue #1727 first
|
|
7
|
+
* wrote them, because the canary is no longer the only reader: `capture --json`
|
|
8
|
+
* publishes the match as `upstreamFault` and `wait --fail-on-upstream-fault`
|
|
9
|
+
* branches on it. Two copies of a pattern list whose whole job is *not* to be
|
|
10
|
+
* over-eager would drift, and the drift would be silent on both sides.
|
|
11
|
+
*
|
|
12
|
+
* A leaf module by design — it imports nothing but {@link stripAnsi}, so the
|
|
13
|
+
* canary (which runs outside Next) and the server payload builder can share it
|
|
14
|
+
* without either pulling in the other's dependencies.
|
|
15
|
+
*
|
|
16
|
+
* ## What a match means, and what it does not
|
|
17
|
+
*
|
|
18
|
+
* A match means the signature was on the frame. A **non**-match means nothing at
|
|
19
|
+
* all about upstream health: the pane may have scrolled, the agent may render a
|
|
20
|
+
* failure with wording nobody has seen yet, and Issue #1834 measured a case
|
|
21
|
+
* where the pane went blank instead of carrying a banner. Nothing in this
|
|
22
|
+
* module — and nothing reading it — may report "no fault" as "upstream is
|
|
23
|
+
* healthy".
|
|
24
|
+
*
|
|
25
|
+
* @module lib/detection/upstream-faults
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.UPSTREAM_FAULT_TRUNCATION_MARKER = exports.UPSTREAM_FAULT_EXCERPT_MAX_BYTES = exports.UPSTREAM_FAULTS = void 0;
|
|
29
|
+
exports.matchUpstreamFault = matchUpstreamFault;
|
|
30
|
+
exports.findUpstreamFault = findUpstreamFault;
|
|
31
|
+
const ansi_1 = require("./ansi");
|
|
32
|
+
/**
|
|
33
|
+
* Patterns are anchored on ERROR wording, not on the words "usage limit".
|
|
34
|
+
* Claude's own banner advertises "up to 50% of your weekly usage limit on
|
|
35
|
+
* Fable 5" on a perfectly healthy frame — a looser pattern marked every
|
|
36
|
+
* scenario of a run as `blocked` (measured 2026-08-06), which would have hidden
|
|
37
|
+
* real regressions behind a fake infrastructure excuse.
|
|
38
|
+
*
|
|
39
|
+
* Order is precedence: the first match wins, so the specific overload wording
|
|
40
|
+
* is listed ahead of the generic `API Error` catch-all. The line Claude 2.1.236
|
|
41
|
+
* prints on a 529 storm contains both (`API Error: Repeated 529 Overloaded
|
|
42
|
+
* errors …`, measured for Issue #1839), and `overloaded` is the more useful of
|
|
43
|
+
* the two answers because it carries `selfRetrying: true`.
|
|
44
|
+
*/
|
|
45
|
+
exports.UPSTREAM_FAULTS = [
|
|
46
|
+
{ id: 'overloaded', pattern: /\b5\d{2}\s+Overloaded\b/i, selfRetrying: true },
|
|
47
|
+
{ id: 'retrying', pattern: /Retrying in \d+s\s*[·•]\s*attempt \d+\/\d+/i, selfRetrying: true },
|
|
48
|
+
{ id: 'limit-reached', pattern: /\blimit reached\b/i, selfRetrying: false },
|
|
49
|
+
{ id: 'api-error', pattern: /\bAPI Error(?::|\s+\d{3})/i, selfRetrying: false },
|
|
50
|
+
];
|
|
51
|
+
/**
|
|
52
|
+
* Bound on {@link UpstreamFaultMatch.matchedText}, in UTF-8 **bytes**.
|
|
53
|
+
*
|
|
54
|
+
* Bytes rather than characters for the reason Issue #1694 gives for its own
|
|
55
|
+
* excerpt: a Japanese frame runs three bytes to the character, so a
|
|
56
|
+
* character-counted bound publishes three times the payload it promises. The
|
|
57
|
+
* excerpt rides every `current-output` response of a faulted session, and those
|
|
58
|
+
* responses are polled every few seconds.
|
|
59
|
+
*/
|
|
60
|
+
exports.UPSTREAM_FAULT_EXCERPT_MAX_BYTES = 200;
|
|
61
|
+
/** Appended when {@link UPSTREAM_FAULT_EXCERPT_MAX_BYTES} cut the excerpt. */
|
|
62
|
+
exports.UPSTREAM_FAULT_TRUNCATION_MARKER = '…[truncated]';
|
|
63
|
+
/**
|
|
64
|
+
* Truncate to a UTF-8 byte budget on a code-point boundary.
|
|
65
|
+
*
|
|
66
|
+
* A local copy rather than `truncateToUtf8Bytes` from `lib/auto-yes-state`: that
|
|
67
|
+
* module reaches the database and the Auto-Yes state machine, and this one is a
|
|
68
|
+
* leaf the canary imports from outside Next. Accumulating code points (never
|
|
69
|
+
* UTF-16 units) is what keeps a surrogate pair whole.
|
|
70
|
+
*/
|
|
71
|
+
function truncateExcerpt(text) {
|
|
72
|
+
const encoder = new TextEncoder();
|
|
73
|
+
if (encoder.encode(text).length <= exports.UPSTREAM_FAULT_EXCERPT_MAX_BYTES)
|
|
74
|
+
return text;
|
|
75
|
+
// The marker is inside the budget, not on top of it: the bound is a promise
|
|
76
|
+
// about the published field, and a caller sizing a log line by it must not be
|
|
77
|
+
// handed twelve more characters than it asked for.
|
|
78
|
+
const budget = exports.UPSTREAM_FAULT_EXCERPT_MAX_BYTES - encoder.encode(exports.UPSTREAM_FAULT_TRUNCATION_MARKER).length;
|
|
79
|
+
let bytes = 0;
|
|
80
|
+
let out = '';
|
|
81
|
+
for (const char of text) {
|
|
82
|
+
const size = encoder.encode(char).length;
|
|
83
|
+
if (bytes + size > budget)
|
|
84
|
+
break;
|
|
85
|
+
bytes += size;
|
|
86
|
+
out += char;
|
|
87
|
+
}
|
|
88
|
+
return `${out}${exports.UPSTREAM_FAULT_TRUNCATION_MARKER}`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The first upstream fault visible in the frame, with its line.
|
|
92
|
+
*
|
|
93
|
+
* ANSI is stripped first: the signature words are routinely split by colour
|
|
94
|
+
* sequences on a live pane.
|
|
95
|
+
*/
|
|
96
|
+
function matchUpstreamFault(frame) {
|
|
97
|
+
const clean = (0, ansi_1.stripAnsi)(frame);
|
|
98
|
+
for (const fault of exports.UPSTREAM_FAULTS) {
|
|
99
|
+
const match = fault.pattern.exec(clean);
|
|
100
|
+
// The patterns are unanchored and stateless (no /g), so `exec` cannot carry
|
|
101
|
+
// a lastIndex between calls — but reading `index` requires exec regardless.
|
|
102
|
+
if (!match)
|
|
103
|
+
continue;
|
|
104
|
+
const lineStart = clean.lastIndexOf('\n', match.index) + 1;
|
|
105
|
+
const lineEnd = clean.indexOf('\n', match.index);
|
|
106
|
+
const line = clean.slice(lineStart, lineEnd === -1 ? undefined : lineEnd).trim();
|
|
107
|
+
return { fault, matchedText: truncateExcerpt(line || match[0]) };
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* First upstream fault visible in the frame, if any.
|
|
113
|
+
*
|
|
114
|
+
* Kept alongside {@link matchUpstreamFault} because the canary only ever wanted
|
|
115
|
+
* the classification (`id` / `selfRetrying`), and a caller that does not need
|
|
116
|
+
* the excerpt should not have to reach through a wrapper for it.
|
|
117
|
+
*/
|
|
118
|
+
function findUpstreamFault(frame) {
|
|
119
|
+
return matchUpstreamFault(frame)?.fault ?? null;
|
|
120
|
+
}
|
|
@@ -63,6 +63,10 @@ const NO_DECISION = (reason) => ({
|
|
|
63
63
|
behavior: null,
|
|
64
64
|
reason,
|
|
65
65
|
});
|
|
66
|
+
/** Production policy source: the active task's contract, TTL-cached. */
|
|
67
|
+
function readContractPolicy(session) {
|
|
68
|
+
return (0, auto_yes_policy_1.getSessionAutoYesPolicy)(session.worktreeId, session.cliToolId, session.instanceId);
|
|
69
|
+
}
|
|
66
70
|
/**
|
|
67
71
|
* The verdict for one permission request. Pure apart from reading Auto-Yes
|
|
68
72
|
* state and the (TTL-cached) contract policy; the recording side effects live
|
|
@@ -80,8 +84,10 @@ const NO_DECISION = (reason) => ({
|
|
|
80
84
|
*
|
|
81
85
|
* @param session - Worktree / tool / instance the request came from
|
|
82
86
|
* @param payload - Parsed payload, or null when it could not be parsed
|
|
87
|
+
* @param deps - Substitutions for the database-backed lookups; see
|
|
88
|
+
* {@link PermissionDecisionDeps}. Omit in the server.
|
|
83
89
|
*/
|
|
84
|
-
function decidePermissionRequest(session, payload) {
|
|
90
|
+
function decidePermissionRequest(session, payload, deps = {}) {
|
|
85
91
|
if (!payload) {
|
|
86
92
|
return NO_DECISION('unknown-payload');
|
|
87
93
|
}
|
|
@@ -98,7 +104,7 @@ function decidePermissionRequest(session, payload) {
|
|
|
98
104
|
if (!state?.enabled) {
|
|
99
105
|
return NO_DECISION('auto-yes-disabled');
|
|
100
106
|
}
|
|
101
|
-
const policy = (
|
|
107
|
+
const policy = (deps.readPolicy ?? readContractPolicy)({ worktreeId, cliToolId, instanceId });
|
|
102
108
|
const denial = (0, auto_yes_resolver_1.evaluatePolicyAgainstTexts)(exports.PERMISSION_REQUEST_PROMPT_TYPE, (0, permission_request_payload_1.collectToolInputMatchTexts)(payload.toolName, payload.toolInput), policy);
|
|
103
109
|
if (denial) {
|
|
104
110
|
return {
|
|
@@ -214,10 +220,12 @@ function reportPendingDialog(session, payload) {
|
|
|
214
220
|
*
|
|
215
221
|
* @param session - Worktree / tool / instance the request came from
|
|
216
222
|
* @param payload - Parsed payload, or null when it could not be parsed
|
|
223
|
+
* @param deps - Substitutions for the database-backed lookups; see
|
|
224
|
+
* {@link PermissionDecisionDeps}. Omit in the server.
|
|
217
225
|
* @returns The verdict the route serialises
|
|
218
226
|
*/
|
|
219
|
-
function resolvePermissionRequest(session, payload) {
|
|
220
|
-
const decision = decidePermissionRequest(session, payload);
|
|
227
|
+
function resolvePermissionRequest(session, payload, deps = {}) {
|
|
228
|
+
const decision = decidePermissionRequest(session, payload, deps);
|
|
221
229
|
// Issue #1726: `AskUserQuestion` raises a `PermissionRequest` carrying the
|
|
222
230
|
// same `tool_input` its `PreToolUse` does (§5.6), so the questions are
|
|
223
231
|
// recorded from here too. Redundant on a session this server started — both
|
|
@@ -263,7 +271,7 @@ function resolvePermissionRequest(session, payload) {
|
|
|
263
271
|
});
|
|
264
272
|
}
|
|
265
273
|
if (decision.behavior === 'allow' && payload) {
|
|
266
|
-
recordAllowedPermission(session, payload);
|
|
274
|
+
(deps.recordAllow ?? recordAllowedPermission)(session, payload);
|
|
267
275
|
}
|
|
268
276
|
return decision;
|
|
269
277
|
}
|
|
@@ -87,6 +87,7 @@ const logger_1 = require("../../../../lib/logger");
|
|
|
87
87
|
// The `~/.gemini` tree belongs to gemini; agy lives inside it. The merge that
|
|
88
88
|
// keeps the two from erasing each other is written once, there.
|
|
89
89
|
const shared_config_tree_1 = require("../gemini/shared-config-tree");
|
|
90
|
+
const launch_command_1 = require("../launch-command");
|
|
90
91
|
const tool_id_1 = require("./tool-id");
|
|
91
92
|
const logger = (0, logger_1.createLogger)('lib/hooks/sources/antigravity/hooks-config');
|
|
92
93
|
/**
|
|
@@ -347,26 +348,32 @@ function writeAntigravityHooksConfig(options = {}) {
|
|
|
347
348
|
* terminal, so the variable is set here and nowhere else.
|
|
348
349
|
*
|
|
349
350
|
* The executable is quoted but not otherwise touched, so a caller may append its
|
|
350
|
-
* own flags — `AntigravityTool.startSession` adds `--model` — after the
|
|
351
|
-
*
|
|
351
|
+
* own flags — `AntigravityTool.startSession` adds `--model` — after the rendered
|
|
352
|
+
* line. Since Issue #1846 the two variables come back as `env` rather than as a
|
|
353
|
+
* prefix on the command, which is also what keeps `--model` appendable: the
|
|
354
|
+
* caller appends to the *rendered* line and the assignments stay in front of it
|
|
355
|
+
* by construction rather than by the caller remembering to.
|
|
352
356
|
*
|
|
353
|
-
* Never throws, and returns the executable
|
|
357
|
+
* Never throws, and returns the executable with an empty environment when
|
|
358
|
+
* injection is off.
|
|
354
359
|
*
|
|
355
360
|
* @param executablePath - `agy`, or a resolved path to it
|
|
356
361
|
* @param target - The instance being started
|
|
357
|
-
* @returns The command
|
|
362
|
+
* @returns The command to type into the pane and the environment it needs
|
|
358
363
|
*/
|
|
359
364
|
function buildAntigravityLaunchCommand(executablePath, target, options = {}) {
|
|
360
365
|
if (!(0, hook_settings_generator_1.isHookInjectionEnabled)())
|
|
361
|
-
return executablePath;
|
|
366
|
+
return { command: executablePath, env: {} };
|
|
362
367
|
if (!(0, types_1.isValidInstanceId)((0, hook_settings_generator_1.resolveTargetInstanceId)(target))) {
|
|
363
368
|
logger.warn('antigravity-hook-invalid-instance-id', { worktreeId: target.worktreeId });
|
|
364
|
-
return executablePath;
|
|
369
|
+
return { command: executablePath, env: {} };
|
|
365
370
|
}
|
|
366
371
|
const scoped = { ...target, cliToolId: tool_id_1.ANTIGRAVITY_CLI_TOOL_ID };
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
+
return {
|
|
373
|
+
command: (0, hook_settings_generator_1.shellQuote)(executablePath),
|
|
374
|
+
env: {
|
|
375
|
+
[launch_command_1.AGENT_EVENT_URL_ENV_VAR]: (0, hook_settings_generator_1.buildAgentEventUrl)(scoped, options),
|
|
376
|
+
[exports.ANTIGRAVITY_PERMISSION_URL_ENV_VAR]: (0, hook_settings_generator_1.buildPermissionRequestUrl)(scoped, options),
|
|
377
|
+
},
|
|
378
|
+
};
|
|
372
379
|
}
|
|
@@ -215,15 +215,16 @@ exports.antigravityAgentEventSource = (0, define_source_1.definePushHookSource)(
|
|
|
215
215
|
encodeVerdict: encodeAntigravityVerdict,
|
|
216
216
|
// Unlike gemini, the whole config is reachable from here: the file is global,
|
|
217
217
|
// so no worktree path is needed and `prepareLaunch` really does write it.
|
|
218
|
-
prepareLaunch: (target, executablePath) => {
|
|
218
|
+
prepareLaunch: ({ target, executablePath }) => {
|
|
219
219
|
const settingsPath = (0, hooks_config_1.writeAntigravityHooksConfig)();
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
};
|
|
220
|
+
// #1846: the two correlation URLs are the plan's `env`. `worktreePath` is
|
|
221
|
+
// in the context now and deliberately unused here — agy's config is one
|
|
222
|
+
// file for the machine, so there is nothing per-worktree to write.
|
|
223
|
+
const { command, env } = (0, hooks_config_1.buildAntigravityLaunchCommand)(executablePath, {
|
|
224
|
+
worktreeId: target.worktreeId,
|
|
225
|
+
instanceId: target.instanceId,
|
|
226
|
+
cliToolId: tool_id_1.ANTIGRAVITY_CLI_TOOL_ID,
|
|
227
|
+
});
|
|
228
|
+
return { command, settingsPath, env };
|
|
228
229
|
},
|
|
229
230
|
});
|
|
@@ -128,7 +128,7 @@ exports.claudeAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
128
128
|
// S3 / S4 / S5. Delegated rather than reimplemented: `buildClaudeLaunchCommand`
|
|
129
129
|
// writes the settings file, falls back to the bare path on any failure, and is
|
|
130
130
|
// covered byte-for-byte by `tests/unit/hooks/hook-settings-generator.test.ts`.
|
|
131
|
-
prepareLaunch: (target, executablePath) => {
|
|
131
|
+
prepareLaunch: ({ target, executablePath }) => {
|
|
132
132
|
const command = (0, hook_settings_generator_1.buildClaudeLaunchCommand)(executablePath, {
|
|
133
133
|
worktreeId: target.worktreeId,
|
|
134
134
|
instanceId: target.instanceId,
|
|
@@ -145,6 +145,9 @@ exports.claudeAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
145
145
|
cliToolId: target.cliToolId,
|
|
146
146
|
})
|
|
147
147
|
: null;
|
|
148
|
-
|
|
148
|
+
// Empty, and the only source for which that is uninteresting: `--settings`
|
|
149
|
+
// carries the correlation keys inside the file, so Claude never needed the
|
|
150
|
+
// environment prefix the other four sources reached for (#1846).
|
|
151
|
+
return { command, settingsPath, env: {} };
|
|
149
152
|
},
|
|
150
153
|
});
|