commandmate 0.25.0 → 0.26.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 +141 -141
- package/.next/app-path-routes-manifest.json +28 -28
- 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 +28 -28
- 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-0e097c0286155883.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/{page-207c69540fd856ef.js → page-fdfca039b7f5837e.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 → wyFLOpmKpr45BHZ4svYpE}/_buildManifest.js +0 -0
- /package/.next/static/{ehkNc5xDMDkXlua08Bwq0 → wyFLOpmKpr45BHZ4svYpE}/_ssgManifest.js +0 -0
|
@@ -63,7 +63,7 @@ exports.buildCodexPermissionHookCommand = buildCodexPermissionHookCommand;
|
|
|
63
63
|
exports.buildCodexHookSettings = buildCodexHookSettings;
|
|
64
64
|
exports.mergeCodexHookSettings = mergeCodexHookSettings;
|
|
65
65
|
exports.writeCodexHookSettings = writeCodexHookSettings;
|
|
66
|
-
exports.
|
|
66
|
+
exports.buildCodexLaunchPlan = buildCodexLaunchPlan;
|
|
67
67
|
const fs_1 = require("fs");
|
|
68
68
|
const os_1 = require("os");
|
|
69
69
|
const path_1 = require("path");
|
|
@@ -396,7 +396,7 @@ function writeCodexHookSettings(options = {}) {
|
|
|
396
396
|
return settingsPath;
|
|
397
397
|
}
|
|
398
398
|
/**
|
|
399
|
-
* The
|
|
399
|
+
* The plan that starts codex for one instance.
|
|
400
400
|
*
|
|
401
401
|
* Never throws: hooks are an enhancement to a session that has to start anyway,
|
|
402
402
|
* so anything that goes wrong here returns the bare executable — which is
|
|
@@ -409,32 +409,40 @@ function writeCodexHookSettings(options = {}) {
|
|
|
409
409
|
* own (#1757 §5.1.7 found no `CLAUDE_PROJECT_DIR` equivalent). What is left is
|
|
410
410
|
* the environment of the process CommandMate starts, which a hook inherits.
|
|
411
411
|
*
|
|
412
|
+
* Since Issue #1846 they are returned as {@link AgentLaunchPlan.env} rather than
|
|
413
|
+
* written onto the front of the command. The bytes a pane receives are
|
|
414
|
+
* unchanged — `renderAgentLaunchCommand` re-emits them in declaration order —
|
|
415
|
+
* but the assignments are now data the launcher applies, which is what let the
|
|
416
|
+
* other three sources that had written the same prefix by hand stop writing it.
|
|
417
|
+
*
|
|
412
418
|
* @param executablePath - Resolved codex CLI path or bare command name
|
|
413
419
|
* @param target - The instance being started
|
|
414
|
-
* @returns
|
|
415
|
-
* injection is off, the ids are unusable, or the file
|
|
420
|
+
* @returns The command and its environment, or the bare executable with an
|
|
421
|
+
* empty environment when injection is off, the ids are unusable, or the file
|
|
422
|
+
* could not be written
|
|
416
423
|
*/
|
|
417
|
-
function
|
|
424
|
+
function buildCodexLaunchPlan(executablePath, target, options = {}) {
|
|
425
|
+
const bare = { command: executablePath, settingsPath: null, env: {} };
|
|
418
426
|
if (!(0, hook_settings_generator_1.isHookInjectionEnabled)())
|
|
419
|
-
return
|
|
427
|
+
return bare;
|
|
420
428
|
const instanceId = target.instanceId ?? tool_id_1.CODEX_CLI_TOOL_ID;
|
|
421
429
|
if (!(0, path_validator_1.isValidWorktreeId)(target.worktreeId) || !(0, types_1.isValidInstanceId)(instanceId)) {
|
|
422
430
|
// Both become URL parameters the receiver re-validates; a value that would
|
|
423
431
|
// be rejected there is not worth injecting.
|
|
424
432
|
logger.warn('codex-hooks-invalid-correlation-key', { worktreeId: target.worktreeId });
|
|
425
|
-
return
|
|
433
|
+
return bare;
|
|
426
434
|
}
|
|
427
435
|
try {
|
|
428
436
|
const settingsPath = writeCodexHookSettings(options);
|
|
429
437
|
if (!settingsPath)
|
|
430
|
-
return
|
|
438
|
+
return bare;
|
|
431
439
|
const port = options.port ?? (0, env_1.getServerPort)();
|
|
432
440
|
const query = new URLSearchParams({
|
|
433
441
|
tool: tool_id_1.CODEX_CLI_TOOL_ID,
|
|
434
442
|
worktreeId: target.worktreeId,
|
|
435
443
|
instanceId,
|
|
436
444
|
});
|
|
437
|
-
const
|
|
445
|
+
const env = {
|
|
438
446
|
// Pin the home the file was just written to, rather than trusting the
|
|
439
447
|
// agent to resolve the same one. Measured on 2026-08-13: a tmux session
|
|
440
448
|
// inherits the environment of the *tmux server*, which was started long
|
|
@@ -445,21 +453,21 @@ function buildCodexLaunchCommand(executablePath, target, options = {}) {
|
|
|
445
453
|
// wrote" and "the file codex reads" the same file by construction; in the
|
|
446
454
|
// ordinary case where nothing sets it, this is the default codex would
|
|
447
455
|
// have picked anyway.
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
456
|
+
[exports.CODEX_HOME_ENV_VAR]: getCodexHome(options),
|
|
457
|
+
[exports.CODEX_TOOL_ENV_VAR]: tool_id_1.CODEX_CLI_TOOL_ID,
|
|
458
|
+
[exports.CODEX_WORKTREE_ID_ENV_VAR]: target.worktreeId,
|
|
459
|
+
[exports.CODEX_INSTANCE_ID_ENV_VAR]: instanceId,
|
|
460
|
+
[exports.CODEX_EVENT_URL_ENV_VAR]: `http://${CODEX_HOOK_HOST}:${port}${hook_settings_generator_1.AGENT_EVENT_PATH}`,
|
|
461
|
+
[exports.CODEX_PERMISSION_URL_ENV_VAR]: `http://${CODEX_HOOK_HOST}:${port}${hook_settings_generator_1.PERMISSION_REQUEST_PATH}?${query.toString()}`,
|
|
462
|
+
};
|
|
455
463
|
const trust = isCodexHookTrustBypassEnabled() ? ` ${exports.CODEX_HOOK_TRUST_BYPASS_FLAG}` : '';
|
|
456
|
-
return
|
|
464
|
+
return { command: `${(0, hook_settings_generator_1.shellQuote)(executablePath)}${trust}`, settingsPath, env };
|
|
457
465
|
}
|
|
458
466
|
catch (error) {
|
|
459
467
|
logger.warn('codex-hooks-config-write-failed', {
|
|
460
468
|
worktreeId: target.worktreeId,
|
|
461
469
|
error: error instanceof Error ? error.message : String(error),
|
|
462
470
|
});
|
|
463
|
-
return
|
|
471
|
+
return bare;
|
|
464
472
|
}
|
|
465
473
|
}
|
|
@@ -131,13 +131,10 @@ exports.codexAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
131
131
|
// S3 / S4 / S5. The whole of the config generation lives in `./hooks-config`,
|
|
132
132
|
// which never throws — a session that starts without hooks is the status quo,
|
|
133
133
|
// a session that fails to start is not.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const settingsPath = command === executablePath ? null : (0, hooks_config_1.getCodexHooksPath)();
|
|
141
|
-
return { command, settingsPath };
|
|
142
|
-
},
|
|
134
|
+
//
|
|
135
|
+
// Issue #1846: the six correlation variables are the plan's `env` rather than
|
|
136
|
+
// a `NAME=value ` prefix written onto `command` here. `settingsPath` now comes
|
|
137
|
+
// straight out of the builder — it used to be re-derived by comparing the
|
|
138
|
+
// command against the bare executable, which was the same fact inferred twice.
|
|
139
|
+
prepareLaunch: ({ target, executablePath }) => (0, hooks_config_1.buildCodexLaunchPlan)(executablePath, target),
|
|
143
140
|
});
|
|
@@ -80,7 +80,7 @@ exports.buildCopilotHookSettings = buildCopilotHookSettings;
|
|
|
80
80
|
exports.mergeCopilotHookSettings = mergeCopilotHookSettings;
|
|
81
81
|
exports.readCopilotSettings = readCopilotSettings;
|
|
82
82
|
exports.writeCopilotHookSettings = writeCopilotHookSettings;
|
|
83
|
-
exports.
|
|
83
|
+
exports.buildCopilotHookEnvironment = buildCopilotHookEnvironment;
|
|
84
84
|
exports.isCopilotHookInjectionEnabled = isCopilotHookInjectionEnabled;
|
|
85
85
|
exports.buildCopilotLaunchCommand = buildCopilotLaunchCommand;
|
|
86
86
|
const fs_1 = require("fs");
|
|
@@ -411,16 +411,25 @@ function writeCopilotHookSettings(options = {}) {
|
|
|
411
411
|
return settingsPath;
|
|
412
412
|
}
|
|
413
413
|
/**
|
|
414
|
-
* The environment
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* created before the agent starts and the assignment then travels
|
|
418
|
-
* process that matters, out of reach of
|
|
419
|
-
* anything else that edits the session's
|
|
414
|
+
* The environment that tells a hook which instance fired it.
|
|
415
|
+
*
|
|
416
|
+
* Applied to the launched process rather than through `tmux set-environment`:
|
|
417
|
+
* the pane is created before the agent starts and the assignment then travels
|
|
418
|
+
* with the one process that matters, out of reach of
|
|
419
|
+
* `sanitizeSessionEnvironment` and of anything else that edits the session's
|
|
420
|
+
* environment later.
|
|
421
|
+
*
|
|
422
|
+
* Returned as a map since Issue #1846. It used to be a pre-quoted
|
|
423
|
+
* `NAME=value NAME=value` string that the caller concatenated in front of the
|
|
424
|
+
* command — the same workaround codex, gemini and antigravity had each written
|
|
425
|
+
* separately, all four assuming a shell nobody had declared. The rendering now
|
|
426
|
+
* happens once, in `../launch-command`, and the bytes are identical.
|
|
420
427
|
*/
|
|
421
|
-
function
|
|
422
|
-
return
|
|
423
|
-
|
|
428
|
+
function buildCopilotHookEnvironment(worktreeId, instanceId) {
|
|
429
|
+
return {
|
|
430
|
+
[exports.COPILOT_WORKTREE_ID_ENV]: worktreeId,
|
|
431
|
+
[exports.COPILOT_INSTANCE_ID_ENV]: instanceId,
|
|
432
|
+
};
|
|
424
433
|
}
|
|
425
434
|
/**
|
|
426
435
|
* Whether to inject at all.
|
|
@@ -445,7 +454,7 @@ function isCopilotHookInjectionEnabled() {
|
|
|
445
454
|
* @param target - The instance being started
|
|
446
455
|
*/
|
|
447
456
|
function buildCopilotLaunchCommand(executablePath, target, options = {}) {
|
|
448
|
-
const bare = { command: executablePath, settingsPath: null };
|
|
457
|
+
const bare = { command: executablePath, settingsPath: null, env: {} };
|
|
449
458
|
if (!isCopilotHookInjectionEnabled())
|
|
450
459
|
return bare;
|
|
451
460
|
const instanceId = target.instanceId ?? tool_id_1.COPILOT_CLI_TOOL_ID;
|
|
@@ -459,8 +468,9 @@ function buildCopilotLaunchCommand(executablePath, target, options = {}) {
|
|
|
459
468
|
try {
|
|
460
469
|
const settingsPath = writeCopilotHookSettings(options);
|
|
461
470
|
return {
|
|
462
|
-
command:
|
|
471
|
+
command: executablePath,
|
|
463
472
|
settingsPath,
|
|
473
|
+
env: buildCopilotHookEnvironment(target.worktreeId, instanceId),
|
|
464
474
|
};
|
|
465
475
|
}
|
|
466
476
|
catch (error) {
|
|
@@ -206,5 +206,5 @@ exports.copilotAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
206
206
|
// machine without hooks rather than to a guess.
|
|
207
207
|
parseQuestion: () => null,
|
|
208
208
|
encodeVerdict: encodeCopilotVerdict,
|
|
209
|
-
prepareLaunch: (target, executablePath) => (0, hook_settings_1.buildCopilotLaunchCommand)(executablePath, target),
|
|
209
|
+
prepareLaunch: ({ target, executablePath }) => (0, hook_settings_1.buildCopilotLaunchCommand)(executablePath, target),
|
|
210
210
|
});
|
|
@@ -164,10 +164,12 @@ function definePullEventSource(spec) {
|
|
|
164
164
|
// has been open for hours and carries no reply channel.
|
|
165
165
|
return { kind: 'outOfBand' };
|
|
166
166
|
},
|
|
167
|
-
prepareLaunch: spec.prepareLaunch ??
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
prepareLaunch: spec.prepareLaunch ??
|
|
168
|
+
((context) => ({
|
|
169
|
+
command: context.executablePath,
|
|
170
|
+
settingsPath: null,
|
|
171
|
+
env: {},
|
|
172
|
+
})),
|
|
171
173
|
subscribe: spec.subscribe,
|
|
172
174
|
decide: spec.decide,
|
|
173
175
|
listPending: spec.listPending,
|
|
@@ -60,6 +60,7 @@ const types_1 = require("../../../../lib/cli-tools/types");
|
|
|
60
60
|
const hook_settings_generator_1 = require("../../../../lib/hooks/hook-settings-generator");
|
|
61
61
|
const logger_1 = require("../../../../lib/logger");
|
|
62
62
|
const event_mapper_1 = require("../event-mapper");
|
|
63
|
+
const launch_command_1 = require("../launch-command");
|
|
63
64
|
const shared_config_tree_1 = require("./shared-config-tree");
|
|
64
65
|
const tool_id_1 = require("./tool-id");
|
|
65
66
|
const logger = (0, logger_1.createLogger)('lib/hooks/sources/gemini/settings-generator');
|
|
@@ -278,7 +279,7 @@ function writeGeminiHookSettings(worktreePath, target) {
|
|
|
278
279
|
}
|
|
279
280
|
}
|
|
280
281
|
/**
|
|
281
|
-
* The command that
|
|
282
|
+
* The command and environment that launch gemini for one instance.
|
|
282
283
|
*
|
|
283
284
|
* `CM_HOOK_URL` is the whole of the instance correlation, and it is an
|
|
284
285
|
* environment variable rather than a flag in the settings file for a reason the
|
|
@@ -293,23 +294,28 @@ function writeGeminiHookSettings(worktreePath, target) {
|
|
|
293
294
|
* lands on the primary instance. That is a degradation, not a
|
|
294
295
|
* misattribution.
|
|
295
296
|
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
297
|
+
* Returned as a `{ command, env }` pair since Issue #1846, instead of as one
|
|
298
|
+
* string with the variable written onto the front of `gemini`. Three other
|
|
299
|
+
* sources had built the same prefix by hand; `renderAgentLaunchCommand` now
|
|
300
|
+
* writes it once, and the line a pane receives is byte-identical.
|
|
301
|
+
*
|
|
302
|
+
* Never throws, and returns the executable with an empty environment when
|
|
303
|
+
* injection is off, so `CM_AGENT_HOOKS_INJECT=0` produces the byte-identical
|
|
304
|
+
* command line this tool used before #1762.
|
|
299
305
|
*
|
|
300
306
|
* @param executablePath - `gemini`, or a resolved path to it
|
|
301
307
|
* @param target - The instance being started
|
|
302
|
-
* @returns The command
|
|
308
|
+
* @returns The command to type into the pane and the environment it needs
|
|
303
309
|
*/
|
|
304
310
|
function buildGeminiLaunchCommand(executablePath, target, options = {}) {
|
|
305
311
|
if (!(0, hook_settings_generator_1.isHookInjectionEnabled)())
|
|
306
|
-
return executablePath;
|
|
312
|
+
return { command: executablePath, env: {} };
|
|
307
313
|
if (!(0, types_1.isValidInstanceId)((0, hook_settings_generator_1.resolveTargetInstanceId)(target))) {
|
|
308
314
|
// About to become a URL parameter the receiver re-validates; a value that
|
|
309
315
|
// would be rejected there is not worth injecting.
|
|
310
316
|
logger.warn('gemini-hook-invalid-instance-id', { worktreeId: target.worktreeId });
|
|
311
|
-
return executablePath;
|
|
317
|
+
return { command: executablePath, env: {} };
|
|
312
318
|
}
|
|
313
319
|
const url = (0, hook_settings_generator_1.buildAgentEventUrl)({ ...target, cliToolId: tool_id_1.GEMINI_CLI_TOOL_ID }, options);
|
|
314
|
-
return
|
|
320
|
+
return { command: (0, hook_settings_generator_1.shellQuote)(executablePath), env: { [launch_command_1.AGENT_EVENT_URL_ENV_VAR]: url } };
|
|
315
321
|
}
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
*/
|
|
58
58
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
59
|
exports.geminiAgentEventSource = void 0;
|
|
60
|
-
exports.
|
|
60
|
+
exports.prepareGeminiLaunch = prepareGeminiLaunch;
|
|
61
61
|
const types_1 = require("../../../../lib/cli-tools/types");
|
|
62
62
|
const define_source_1 = require("../define-source");
|
|
63
63
|
const event_mapper_1 = require("../event-mapper");
|
|
@@ -66,32 +66,40 @@ const event_vocabulary_1 = require("./event-vocabulary");
|
|
|
66
66
|
const settings_generator_1 = require("./settings-generator");
|
|
67
67
|
const tool_id_1 = require("./tool-id");
|
|
68
68
|
/**
|
|
69
|
-
* Write this worktree's `.gemini/settings.json
|
|
69
|
+
* Write this worktree's `.gemini/settings.json` and build the launch line
|
|
70
|
+
* (S3 / S4 / S5).
|
|
70
71
|
*
|
|
71
|
-
* **This
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* passes it here and `prepareLaunch` builds the command line.
|
|
72
|
+
* **This used to be the one thing {@link AgentEventSource} could not express.**
|
|
73
|
+
* `prepareLaunch` took `(target, executablePath)`, gemini's config lives at
|
|
74
|
+
* `<worktree>/.gemini/settings.json`, and an `AgentInstanceRef` carries no
|
|
75
|
+
* filesystem path — so #1762 exported `injectGeminiHookSettings(worktreePath,
|
|
76
|
+
* target)` beside the source and `cli-tools/gemini.ts` called both. One of the
|
|
77
|
+
* six sources wrote its config from a different call site than the other five,
|
|
78
|
+
* and a reader of this file could not tell that the config was written at all.
|
|
79
79
|
*
|
|
80
|
-
*
|
|
80
|
+
* Issue #1846 put `worktreePath` on {@link AgentLaunchContext} instead, so the
|
|
81
|
+
* write happens here, `settingsPath` reports the file that was actually
|
|
82
|
+
* written, and the second entry point is gone.
|
|
81
83
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
84
|
+
* Never throws — `writeGeminiHookSettings` swallows its own failures and answers
|
|
85
|
+
* null, and a gemini session with no hooks is the pre-#1762 status quo.
|
|
86
|
+
*
|
|
87
|
+
* @param context - The instance, its executable, and the worktree it runs in
|
|
88
|
+
* @returns The command, its environment, and the settings file when one landed
|
|
85
89
|
*/
|
|
86
|
-
function
|
|
90
|
+
function prepareGeminiLaunch({ target, executablePath, worktreePath, }) {
|
|
87
91
|
const settingsTarget = {
|
|
88
92
|
worktreeId: target.worktreeId,
|
|
89
93
|
instanceId: target.instanceId,
|
|
90
94
|
cliToolId: tool_id_1.GEMINI_CLI_TOOL_ID,
|
|
91
95
|
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
// Checked before the write as well as inside `buildGeminiLaunchCommand`: an
|
|
97
|
+
// id the receiver would reject is an id no settings file should name either.
|
|
98
|
+
const settingsPath = (0, types_1.isValidInstanceId)(target.instanceId ?? tool_id_1.GEMINI_CLI_TOOL_ID)
|
|
99
|
+
? (0, settings_generator_1.writeGeminiHookSettings)(worktreePath, settingsTarget)
|
|
100
|
+
: null;
|
|
101
|
+
const { command, env } = (0, settings_generator_1.buildGeminiLaunchCommand)(executablePath, settingsTarget);
|
|
102
|
+
return { command, settingsPath, env };
|
|
95
103
|
}
|
|
96
104
|
/**
|
|
97
105
|
* Gemini CLI as an event source.
|
|
@@ -137,15 +145,7 @@ exports.geminiAgentEventSource = (0, define_source_1.definePushHookSource)({
|
|
|
137
145
|
// escalation would add dialogs the Policy Engine had already settled. So every
|
|
138
146
|
// verdict encodes to the empty body, which the bundle reads as no opinion.
|
|
139
147
|
encodeVerdict: () => ({}),
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
prepareLaunch: (target, executablePath) => ({
|
|
144
|
-
command: (0, settings_generator_1.buildGeminiLaunchCommand)(executablePath, {
|
|
145
|
-
worktreeId: target.worktreeId,
|
|
146
|
-
instanceId: target.instanceId,
|
|
147
|
-
cliToolId: tool_id_1.GEMINI_CLI_TOOL_ID,
|
|
148
|
-
}),
|
|
149
|
-
settingsPath: null,
|
|
150
|
-
}),
|
|
148
|
+
// Config write and command line, both from here since #1846. `CM_HOOK_URL` is
|
|
149
|
+
// the plan's `env` rather than a prefix on `command`.
|
|
150
|
+
prepareLaunch: prepareGeminiLaunch,
|
|
151
151
|
});
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* @module lib/hooks/sources
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.OPENCODE_CLI_TOOL_ID = exports.CODEX_CLI_TOOL_ID = exports.ANTIGRAVITY_CLI_TOOL_ID = exports.GEMINI_CLI_TOOL_ID = exports.COPILOT_CLI_TOOL_ID = exports.CLAUDE_CLI_TOOL_ID = exports.isAbstainSafe = exports.describeNoDecision = exports.describeAbstain = exports.resetPendingDecisions = exports.openDecisionSlot = exports.listOpenDecisions = exports.closeDecisionSlot = exports.answerPendingDecision = exports.TOOL_CALL_ID_FIELDS = exports.SESSION_ID_FIELDS = exports.extractSnakeCaseEventDetail = exports.CAMEL_CASE_HOOK_EVENT_NAMES = exports.whenNamed = exports.runEventMappers = exports.resetUnknownEventTallies = exports.readStringField = exports.readNestedString = exports.readFirstStringField = exports.isPlainObject = exports.getUnknownEventTally = exports.fromNameTable = exports.boundDetail = exports.definePushHookSource = exports.definePullEventSource = exports.unregisterAgentEventSource = exports.registerAgentEventSource = exports.listAgentEventSources = exports.hasAgentEventSource = exports.getAgentEventSource = void 0;
|
|
15
|
+
exports.OPENCODE_CLI_TOOL_ID = exports.CODEX_CLI_TOOL_ID = exports.ANTIGRAVITY_CLI_TOOL_ID = exports.GEMINI_CLI_TOOL_ID = exports.COPILOT_CLI_TOOL_ID = exports.CLAUDE_CLI_TOOL_ID = exports.isAbstainSafe = exports.describeNoDecision = exports.describeAbstain = exports.resetPendingDecisions = exports.openDecisionSlot = exports.listOpenDecisions = exports.closeDecisionSlot = exports.answerPendingDecision = exports.TOOL_CALL_ID_FIELDS = exports.SESSION_ID_FIELDS = exports.extractSnakeCaseEventDetail = exports.CAMEL_CASE_HOOK_EVENT_NAMES = exports.whenNamed = exports.runEventMappers = exports.resetUnknownEventTallies = exports.readStringField = exports.readNestedString = exports.readFirstStringField = exports.isPlainObject = exports.getUnknownEventTally = exports.fromNameTable = exports.boundDetail = exports.renderAgentLaunchCommand = exports.AGENT_EVENT_URL_ENV_VAR = exports.definePushHookSource = exports.definePullEventSource = exports.unregisterAgentEventSource = exports.registerAgentEventSource = exports.listAgentEventSources = exports.hasAgentEventSource = exports.getAgentEventSource = void 0;
|
|
16
16
|
var registry_1 = require("./registry");
|
|
17
17
|
Object.defineProperty(exports, "getAgentEventSource", { enumerable: true, get: function () { return registry_1.getAgentEventSource; } });
|
|
18
18
|
Object.defineProperty(exports, "hasAgentEventSource", { enumerable: true, get: function () { return registry_1.hasAgentEventSource; } });
|
|
@@ -22,6 +22,9 @@ Object.defineProperty(exports, "unregisterAgentEventSource", { enumerable: true,
|
|
|
22
22
|
var define_source_1 = require("./define-source");
|
|
23
23
|
Object.defineProperty(exports, "definePullEventSource", { enumerable: true, get: function () { return define_source_1.definePullEventSource; } });
|
|
24
24
|
Object.defineProperty(exports, "definePushHookSource", { enumerable: true, get: function () { return define_source_1.definePushHookSource; } });
|
|
25
|
+
var launch_command_1 = require("./launch-command");
|
|
26
|
+
Object.defineProperty(exports, "AGENT_EVENT_URL_ENV_VAR", { enumerable: true, get: function () { return launch_command_1.AGENT_EVENT_URL_ENV_VAR; } });
|
|
27
|
+
Object.defineProperty(exports, "renderAgentLaunchCommand", { enumerable: true, get: function () { return launch_command_1.renderAgentLaunchCommand; } });
|
|
25
28
|
var event_mapper_1 = require("./event-mapper");
|
|
26
29
|
Object.defineProperty(exports, "boundDetail", { enumerable: true, get: function () { return event_mapper_1.boundDetail; } });
|
|
27
30
|
Object.defineProperty(exports, "fromNameTable", { enumerable: true, get: function () { return event_mapper_1.fromNameTable; } });
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The one place an {@link AgentLaunchPlan}'s environment is applied
|
|
4
|
+
* (Issue #1846, 申し送り 2).
|
|
5
|
+
*
|
|
6
|
+
* Before this module, four of the six sources — codex, copilot, gemini,
|
|
7
|
+
* antigravity — each wrote their own `NAME=value ` prefix onto the front of
|
|
8
|
+
* `AgentLaunchPlan.command` and trusted the caller to paste the result into a
|
|
9
|
+
* shell. Four implementations, four independently written prefixes, one
|
|
10
|
+
* assumption none of them declared: that the launcher is a shell.
|
|
11
|
+
*
|
|
12
|
+
* The assumption holds today, because every one of those launchers is
|
|
13
|
+
* `sendKeys(sessionName, line, true)` typing into a tmux pane. It is still the
|
|
14
|
+
* wrong place for the knowledge:
|
|
15
|
+
*
|
|
16
|
+
* - a source whose executable is argv rather than a shell word cannot use the
|
|
17
|
+
* trick at all, and the seventh tool is as likely to be that as not;
|
|
18
|
+
* - a caller that wants to log or display the command has no way to take the
|
|
19
|
+
* assignments back off, and those values are URLs with correlation keys in
|
|
20
|
+
* them;
|
|
21
|
+
* - quoting is a shell property, and four copies of it is four chances to get
|
|
22
|
+
* a worktree path with a space in it wrong.
|
|
23
|
+
*
|
|
24
|
+
* So the plan declares `env` as data and this renders it, once. A source that
|
|
25
|
+
* needs no environment declares `{}` and renders to its bare command, byte for
|
|
26
|
+
* byte what it produced before.
|
|
27
|
+
*
|
|
28
|
+
* @module lib/hooks/sources/launch-command
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.AGENT_EVENT_URL_ENV_VAR = void 0;
|
|
32
|
+
exports.renderAgentLaunchCommand = renderAgentLaunchCommand;
|
|
33
|
+
const hook_settings_generator_1 = require("../../../lib/hooks/hook-settings-generator");
|
|
34
|
+
/**
|
|
35
|
+
* The variable `scripts/hooks/cmate-agent-event.sh` reads its endpoint from.
|
|
36
|
+
*
|
|
37
|
+
* One constant since Issue #1846. codex, gemini and antigravity each carried
|
|
38
|
+
* their own `'CM_HOOK_URL'` literal, which is three places for the relay's one
|
|
39
|
+
* contract to drift — and the failure mode of a drifted name is a relay that
|
|
40
|
+
* silently falls back to its default endpoint and attributes every event to the
|
|
41
|
+
* primary instance.
|
|
42
|
+
*/
|
|
43
|
+
exports.AGENT_EVENT_URL_ENV_VAR = 'CM_HOOK_URL';
|
|
44
|
+
/**
|
|
45
|
+
* Turn a plan into the line a shell can run.
|
|
46
|
+
*
|
|
47
|
+
* Assignments come out in declaration order — `Record` preserves insertion
|
|
48
|
+
* order for string keys — so a plan that puts `CODEX_HOME` first still renders
|
|
49
|
+
* it first, which is what keeps this a refactor rather than a change of bytes.
|
|
50
|
+
* Values are quoted; names are not, because a name that needed quoting would
|
|
51
|
+
* not be a name a shell would assign.
|
|
52
|
+
*
|
|
53
|
+
* @param plan - What the source's `prepareLaunch` returned
|
|
54
|
+
* @returns `NAME='value' … command`, or just `command` when `env` is empty
|
|
55
|
+
*/
|
|
56
|
+
function renderAgentLaunchCommand(plan) {
|
|
57
|
+
const assignments = Object.entries(plan.env).map(([name, value]) => `${name}=${(0, hook_settings_generator_1.shellQuote)(value)}`);
|
|
58
|
+
if (assignments.length === 0)
|
|
59
|
+
return plan.command;
|
|
60
|
+
return `${assignments.join(' ')} ${plan.command}`;
|
|
61
|
+
}
|
|
@@ -76,9 +76,10 @@ function createLegacyRelaySource(cliToolId) {
|
|
|
76
76
|
encodeVerdict: source_1.encodeClaudeVerdict,
|
|
77
77
|
// CommandMate does not inject config for these tools; their `startSession`
|
|
78
78
|
// in `src/lib/cli-tools/<tool>.ts` launches them bare.
|
|
79
|
-
prepareLaunch: (
|
|
79
|
+
prepareLaunch: ({ executablePath }) => ({
|
|
80
80
|
command: executablePath,
|
|
81
81
|
settingsPath: null,
|
|
82
|
+
env: {},
|
|
82
83
|
}),
|
|
83
84
|
});
|
|
84
85
|
}
|
|
@@ -202,19 +202,22 @@ async function probeOpencodeActivity(target) {
|
|
|
202
202
|
* because the default is the security property: the server is unauthenticated,
|
|
203
203
|
* and `--mdns` (never passed) would move it to `0.0.0.0` (#1758 §5.8.3).
|
|
204
204
|
*/
|
|
205
|
-
function prepareOpencodeLaunch(target, executablePath) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
function prepareOpencodeLaunch({ target, executablePath, }) {
|
|
206
|
+
// `env` is empty for every branch: opencode is the one source that needs no
|
|
207
|
+
// correlation variable at all, because CommandMate holds the connection and
|
|
208
|
+
// therefore already knows which instance the frames belong to (#1846).
|
|
209
|
+
const bare = { command: executablePath, settingsPath: null, env: {} };
|
|
210
|
+
if (!(0, hook_settings_generator_1.isHookInjectionEnabled)())
|
|
211
|
+
return bare;
|
|
209
212
|
const port = (0, ports_1.getAssignedOpencodePort)(target);
|
|
210
|
-
if (port === null)
|
|
211
|
-
return
|
|
212
|
-
}
|
|
213
|
+
if (port === null)
|
|
214
|
+
return bare;
|
|
213
215
|
return {
|
|
214
216
|
command: `${(0, hook_settings_generator_1.shellQuote)(executablePath)} --port ${port} --hostname ${client_1.OPENCODE_SERVER_HOST}`,
|
|
215
217
|
// Nothing is written to disk. `configScope: 'none'` says the same thing to
|
|
216
218
|
// a caller that asks the capabilities instead of the plan.
|
|
217
219
|
settingsPath: null,
|
|
220
|
+
env: {},
|
|
218
221
|
};
|
|
219
222
|
}
|
|
220
223
|
/**
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* In-memory tally of prompts the content-hash dedup guard declined to save
|
|
4
|
+
* (Issue #1695).
|
|
5
|
+
*
|
|
6
|
+
* `isDuplicatePrompt` (see `prompt-dedup.ts`, Issue #565) drops a detected
|
|
7
|
+
* prompt without writing anything to the database, and until now the only trace
|
|
8
|
+
* it left was `logger.info('duplicate-prompt-skipped', …)` in the server log.
|
|
9
|
+
* From `commandmate capture --json` the outcome is indistinguishable from the
|
|
10
|
+
* detection layer never having classified the frame at all (Issue #1676): both
|
|
11
|
+
* answer "no prompt was recorded". This module keeps the skip count and the
|
|
12
|
+
* last skip time per session so `buildCurrentOutput` can publish them and an
|
|
13
|
+
* operator can tell "the guard suppressed it" from "nothing saw it".
|
|
14
|
+
*
|
|
15
|
+
* Exposure only: nothing reads this to make a decision. The guard itself keeps
|
|
16
|
+
* its own hash cache and is untouched by whether anyone reads the tally.
|
|
17
|
+
*
|
|
18
|
+
* **Cumulative, not per-turn.** The hash cache behind the guard is cleared by
|
|
19
|
+
* `stopPolling()` at the end of every polling cycle; the tally deliberately is
|
|
20
|
+
* not, because the question it answers ("has this session been dropping
|
|
21
|
+
* prompts, and how recently?") spans turns. `lastSkippedAt` is what dates the
|
|
22
|
+
* evidence — a large `skippedCount` with an hours-old `lastSkippedAt` is
|
|
23
|
+
* history, the same count with a fresh one is happening right now.
|
|
24
|
+
*
|
|
25
|
+
* Keyed like `auto-yes-suppression-state` (worktreeId / cliToolId / instanceId
|
|
26
|
+
* via `buildCompositeKey`) so the reader in `current-output-builder` can look it
|
|
27
|
+
* up with the identifiers the request already carries, rather than with the
|
|
28
|
+
* poller key, whose builder lives behind `response-poller-core`'s import graph.
|
|
29
|
+
*
|
|
30
|
+
* In-memory and not in SQLite for the same reason the suppression record is:
|
|
31
|
+
* it describes a live tmux session, and a session does not survive a server
|
|
32
|
+
* restart for the record to still be about.
|
|
33
|
+
*
|
|
34
|
+
* @module lib/polling/prompt-dedup-state
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.recordPromptDedupSkip = recordPromptDedupSkip;
|
|
38
|
+
exports.getPromptDedupSkips = getPromptDedupSkips;
|
|
39
|
+
exports.renamePromptDedupSkips = renamePromptDedupSkips;
|
|
40
|
+
exports.clearPromptDedupSkips = clearPromptDedupSkips;
|
|
41
|
+
const auto_yes_state_1 = require("../../lib/auto-yes-state");
|
|
42
|
+
/** compositeKey -> running tally of suppressed prompts for that session. */
|
|
43
|
+
const promptDedupSkips = globalThis.__promptDedupSkips ??
|
|
44
|
+
(globalThis.__promptDedupSkips = new Map());
|
|
45
|
+
/**
|
|
46
|
+
* Count one prompt the dedup guard suppressed for `instanceId`.
|
|
47
|
+
*
|
|
48
|
+
* @param at - Epoch ms; defaults to now. Overridable so tests are deterministic.
|
|
49
|
+
*/
|
|
50
|
+
function recordPromptDedupSkip(worktreeId, cliToolId, instanceId, at = Date.now()) {
|
|
51
|
+
const key = (0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId);
|
|
52
|
+
const previous = promptDedupSkips.get(key);
|
|
53
|
+
promptDedupSkips.set(key, {
|
|
54
|
+
skippedCount: (previous?.skippedCount ?? 0) + 1,
|
|
55
|
+
lastSkippedAt: at,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* @returns The tally for this session. Never null: a session the guard has
|
|
60
|
+
* never fired for reports `{ skippedCount: 0, lastSkippedAt: null }`, so
|
|
61
|
+
* `capture --json` prints an explicit "no prompt was suppressed" rather than
|
|
62
|
+
* an absent key a consumer has to guess about.
|
|
63
|
+
*/
|
|
64
|
+
function getPromptDedupSkips(worktreeId, cliToolId, instanceId) {
|
|
65
|
+
return (promptDedupSkips.get((0, auto_yes_state_1.buildCompositeKey)(worktreeId, cliToolId, instanceId)) ?? {
|
|
66
|
+
skippedCount: 0,
|
|
67
|
+
lastSkippedAt: null,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Carry the tally across a worktree-ID rename (Issue #1621 Phase 3).
|
|
72
|
+
*
|
|
73
|
+
* The dedup hash caches are deliberately moved rather than dropped by
|
|
74
|
+
* `migrateResponsePollerWorktreeIds`, so the guard goes on suppressing the
|
|
75
|
+
* prompt currently on screen under the new ID. A tally left behind would then
|
|
76
|
+
* report zero skips for a session that is still skipping — the false negative
|
|
77
|
+
* this module exists to prevent, appearing at exactly the moment the record
|
|
78
|
+
* matters.
|
|
79
|
+
*
|
|
80
|
+
* No-op when the IDs match or nothing is recorded under the old one.
|
|
81
|
+
*/
|
|
82
|
+
function renamePromptDedupSkips(oldWorktreeId, newWorktreeId, cliToolId, instanceId) {
|
|
83
|
+
if (oldWorktreeId === newWorktreeId)
|
|
84
|
+
return;
|
|
85
|
+
const oldKey = (0, auto_yes_state_1.buildCompositeKey)(oldWorktreeId, cliToolId, instanceId);
|
|
86
|
+
const record = promptDedupSkips.get(oldKey);
|
|
87
|
+
promptDedupSkips.delete(oldKey);
|
|
88
|
+
if (record !== undefined) {
|
|
89
|
+
promptDedupSkips.set((0, auto_yes_state_1.buildCompositeKey)(newWorktreeId, cliToolId, instanceId), record);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** Drop every recorded tally. Test seam. */
|
|
93
|
+
function clearPromptDedupSkips() {
|
|
94
|
+
promptDedupSkips.clear();
|
|
95
|
+
}
|
|
@@ -25,6 +25,7 @@ const response_cleaner_1 = require("../response-cleaner");
|
|
|
25
25
|
const copilot_constants_1 = require("../../config/copilot-constants");
|
|
26
26
|
const tui_accumulator_1 = require("../tui-accumulator");
|
|
27
27
|
const prompt_dedup_1 = require("./prompt-dedup");
|
|
28
|
+
const prompt_dedup_state_1 = require("./prompt-dedup-state");
|
|
28
29
|
const response_dedup_1 = require("./response-dedup");
|
|
29
30
|
const response_poller_core_1 = require("./response-poller-core");
|
|
30
31
|
const push_1 = require("../../lib/push");
|
|
@@ -446,6 +447,11 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
446
447
|
const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
|
|
447
448
|
const normalizedForDedup = (0, prompt_dedup_1.normalizePromptForDedup)(promptContent, cliToolId);
|
|
448
449
|
if ((0, prompt_dedup_1.isDuplicatePrompt)(pollerKey, normalizedForDedup)) {
|
|
450
|
+
// Issue #1695: the log line below is invisible to `commandmate capture
|
|
451
|
+
// --json`, so a suppressed prompt and a prompt the detection layer never
|
|
452
|
+
// classified (#1676) look identical from the CLI — both say "nothing was
|
|
453
|
+
// recorded". Count the skip so the payload can tell them apart.
|
|
454
|
+
(0, prompt_dedup_state_1.recordPromptDedupSkip)(worktreeId, cliToolId, instanceId);
|
|
449
455
|
logger.info('duplicate-prompt-skipped', { worktreeId, cliToolId });
|
|
450
456
|
return false;
|
|
451
457
|
}
|
|
@@ -565,6 +571,12 @@ async function checkForResponse(worktreeId, cliToolId, instanceId) {
|
|
|
565
571
|
if (!lineCountIsCursor) {
|
|
566
572
|
const pollerKey = (0, response_poller_core_1.getPollerKey)(worktreeId, cliToolId, instanceId);
|
|
567
573
|
if ((0, response_dedup_1.isDuplicateResponse)(pollerKey, cleanedResponse)) {
|
|
574
|
+
// Issue #1695: this branch used to drop the response silently — the
|
|
575
|
+
// prompt-side guard above has logged its skip since #565, this one
|
|
576
|
+
// logged nothing at all, so a reply that never reached History left no
|
|
577
|
+
// trace anywhere. Same action name shape as its sibling so both skips
|
|
578
|
+
// are found by one grep.
|
|
579
|
+
logger.info('duplicate-response-skipped', { worktreeId, cliToolId, instanceId: resolvedInstanceId });
|
|
568
580
|
(0, db_1.updateSessionState)(db, worktreeId, cliToolId, result.lineCount, resolvedInstanceId);
|
|
569
581
|
return false;
|
|
570
582
|
}
|