oh-my-codex 0.14.4 → 0.15.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/.agents/plugins/marketplace.json +20 -0
- package/Cargo.lock +6 -6
- package/Cargo.toml +2 -1
- package/README.md +40 -7
- package/crates/omx-explore/Cargo.toml +1 -0
- package/crates/omx-explore/src/main.rs +246 -24
- package/crates/omx-mux/Cargo.toml +1 -0
- package/crates/omx-runtime/Cargo.toml +1 -0
- package/crates/omx-runtime-core/Cargo.toml +1 -0
- package/crates/omx-runtime-core/src/dispatch.rs +2 -1
- package/crates/omx-runtime-core/src/engine.rs +2 -2
- package/crates/omx-runtime-core/src/mailbox.rs +2 -1
- package/crates/omx-sparkshell/Cargo.toml +1 -0
- package/crates/omx-sparkshell/src/codex_bridge.rs +1 -0
- package/crates/omx-sparkshell/src/prompt.rs +1 -0
- package/crates/omx-sparkshell/src/threshold.rs +1 -0
- package/dist/agents/__tests__/native-config.test.js +98 -4
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +4 -0
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +19 -3
- package/dist/agents/native-config.js.map +1 -1
- package/dist/agents/policy.d.ts +10 -0
- package/dist/agents/policy.d.ts.map +1 -0
- package/dist/agents/policy.js +61 -0
- package/dist/agents/policy.js.map +1 -0
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts +2 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.d.ts.map +1 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +72 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -0
- package/dist/catalog/installable.d.ts +5 -0
- package/dist/catalog/installable.d.ts.map +1 -0
- package/dist/catalog/installable.js +13 -0
- package/dist/catalog/installable.js.map +1 -0
- package/dist/catalog/skill-mirror.d.ts +20 -0
- package/dist/catalog/skill-mirror.d.ts.map +1 -0
- package/dist/catalog/skill-mirror.js +104 -0
- package/dist/catalog/skill-mirror.js.map +1 -0
- package/dist/cli/__tests__/ask.test.js +4 -1
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.d.ts +2 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.d.ts.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +210 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-team.test.js +46 -1
- package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +225 -111
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +96 -1
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +26 -9
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +301 -7
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +358 -4
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/list.test.d.ts +2 -0
- package/dist/cli/__tests__/list.test.d.ts.map +1 -0
- package/dist/cli/__tests__/list.test.js +38 -0
- package/dist/cli/__tests__/list.test.js.map +1 -0
- package/dist/cli/__tests__/mcp-parity.test.js +86 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-serve.test.d.ts +2 -0
- package/dist/cli/__tests__/mcp-serve.test.d.ts.map +1 -0
- package/dist/cli/__tests__/mcp-serve.test.js +38 -0
- package/dist/cli/__tests__/mcp-serve.test.js.map +1 -0
- package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +58 -3
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +209 -5
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +146 -3
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-install-mode.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-install-mode.test.js +873 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -0
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +83 -22
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +120 -2
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +32 -13
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/sidecar.test.d.ts +2 -0
- package/dist/cli/__tests__/sidecar.test.d.ts.map +1 -0
- package/dist/cli/__tests__/sidecar.test.js +24 -0
- package/dist/cli/__tests__/sidecar.test.js.map +1 -0
- package/dist/cli/__tests__/team.test.js +90 -42
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/version-sync-contract.test.js +11 -0
- package/dist/cli/__tests__/version-sync-contract.test.js.map +1 -1
- package/dist/cli/codex-home.d.ts +4 -5
- package/dist/cli/codex-home.d.ts.map +1 -1
- package/dist/cli/codex-home.js +9 -37
- package/dist/cli/codex-home.js.map +1 -1
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +509 -278
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +13 -5
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +351 -60
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/list.d.ts +2 -0
- package/dist/cli/list.d.ts.map +1 -0
- package/dist/cli/list.js +40 -0
- package/dist/cli/list.js.map +1 -0
- package/dist/cli/mcp-serve.d.ts +12 -0
- package/dist/cli/mcp-serve.d.ts.map +1 -0
- package/dist/cli/mcp-serve.js +63 -0
- package/dist/cli/mcp-serve.js.map +1 -0
- package/dist/cli/plugin-marketplace.d.ts +13 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -0
- package/dist/cli/plugin-marketplace.js +77 -0
- package/dist/cli/plugin-marketplace.js.map +1 -0
- package/dist/cli/question.d.ts.map +1 -1
- package/dist/cli/question.js +27 -2
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +20 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -0
- package/dist/cli/setup-preferences.js +71 -0
- package/dist/cli/setup-preferences.js.map +1 -0
- package/dist/cli/setup.d.ts +12 -3
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +1080 -254
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts +1 -0
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +22 -3
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +2 -8
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +100 -3
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +6 -5
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/generator-status-line-presets.test.d.ts +2 -0
- package/dist/config/__tests__/generator-status-line-presets.test.d.ts.map +1 -0
- package/dist/config/__tests__/generator-status-line-presets.test.js +203 -0
- package/dist/config/__tests__/generator-status-line-presets.test.js.map +1 -0
- package/dist/config/__tests__/models.test.js +35 -2
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +2 -7
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +10 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +259 -76
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +13 -3
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +25 -5
- package/dist/config/models.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts +18 -0
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -0
- package/dist/config/omx-first-party-mcp.js +76 -0
- package/dist/config/omx-first-party-mcp.js.map +1 -0
- package/dist/document-refresh/__tests__/enforcer.test.d.ts +2 -0
- package/dist/document-refresh/__tests__/enforcer.test.d.ts.map +1 -0
- package/dist/document-refresh/__tests__/enforcer.test.js +128 -0
- package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -0
- package/dist/document-refresh/config.d.ts +9 -0
- package/dist/document-refresh/config.d.ts.map +1 -0
- package/dist/document-refresh/config.js +70 -0
- package/dist/document-refresh/config.js.map +1 -0
- package/dist/document-refresh/enforcer.d.ts +43 -0
- package/dist/document-refresh/enforcer.d.ts.map +1 -0
- package/dist/document-refresh/enforcer.js +329 -0
- package/dist/document-refresh/enforcer.js.map +1 -0
- package/dist/exec/followup.d.ts +44 -0
- package/dist/exec/followup.d.ts.map +1 -0
- package/dist/exec/followup.js +349 -0
- package/dist/exec/followup.js.map +1 -0
- package/dist/hooks/__tests__/codebase-map.test.js +63 -1
- package/dist/hooks/__tests__/codebase-map.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -7
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +89 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +84 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +23 -2
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +43 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +17 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +53 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +4 -4
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +103 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +27 -13
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +35 -0
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +70 -3
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js +5 -0
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +3 -2
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/prompt-refactor-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/prompt-refactor-contract.test.js +22 -0
- package/dist/hooks/__tests__/prompt-refactor-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js +28 -0
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js +59 -4
- package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -1
- package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/visual-ralph-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/visual-ralph-skill.test.js +44 -0
- package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -0
- package/dist/hooks/codebase-map.d.ts.map +1 -1
- package/dist/hooks/codebase-map.js +83 -6
- package/dist/hooks/codebase-map.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +19 -14
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts +6 -0
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +89 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +2 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +6 -0
- package/dist/hooks/session.js.map +1 -1
- package/dist/hooks/triage-heuristic.d.ts +2 -2
- package/dist/hooks/triage-heuristic.d.ts.map +1 -1
- package/dist/hooks/triage-heuristic.js +97 -20
- package/dist/hooks/triage-heuristic.js.map +1 -1
- package/dist/hooks/triage-state.d.ts +1 -1
- package/dist/hooks/triage-state.d.ts.map +1 -1
- package/dist/hooks/triage-state.js +2 -1
- package/dist/hooks/triage-state.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +4 -4
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +4 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/types.test.js +27 -0
- package/dist/hud/__tests__/types.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +8 -0
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +9 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +3 -0
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +45 -5
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +50 -7
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +67 -9
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +12 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +87 -29
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-server.d.ts +5 -11
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +16 -432
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.js +152 -1
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +9 -0
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +60 -1
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +1 -1
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/renderer.test.js +70 -0
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +49 -0
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/renderer.d.ts +14 -1
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +109 -10
- package/dist/question/renderer.js.map +1 -1
- package/dist/question/state.d.ts +2 -0
- package/dist/question/state.d.ts.map +1 -1
- package/dist/question/state.js +4 -0
- package/dist/question/state.js.map +1 -1
- package/dist/question/types.d.ts +2 -1
- package/dist/question/types.d.ts.map +1 -1
- package/dist/question/types.js.map +1 -1
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +28 -8
- package/dist/question/ui.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +143 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +21 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +54 -4
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1904 -230
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/generate-release-body.test.js +90 -1
- package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.js +110 -2
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.js +7 -93
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
- package/dist/scripts/__tests__/prompt-inventory.test.d.ts +2 -0
- package/dist/scripts/__tests__/prompt-inventory.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/prompt-inventory.test.js +56 -0
- package/dist/scripts/__tests__/prompt-inventory.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.d.ts +2 -0
- package/dist/scripts/__tests__/run-test-files.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +62 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -0
- package/dist/scripts/__tests__/smoke-packed-install.test.js +13 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.d.ts +2 -0
- package/dist/scripts/__tests__/verify-native-agents.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js +166 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -0
- package/dist/scripts/codex-execution-surface.d.ts +16 -0
- package/dist/scripts/codex-execution-surface.d.ts.map +1 -0
- package/dist/scripts/codex-execution-surface.js +42 -0
- package/dist/scripts/codex-execution-surface.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +418 -76
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +8 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +133 -19
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/generate-release-body.d.ts +1 -0
- package/dist/scripts/generate-release-body.d.ts.map +1 -1
- package/dist/scripts/generate-release-body.js +32 -3
- package/dist/scripts/generate-release-body.js.map +1 -1
- package/dist/scripts/hook-derived-watcher.js +46 -7
- package/dist/scripts/hook-derived-watcher.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +59 -9
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts +2 -0
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.d.ts.map +1 -0
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js +153 -0
- package/dist/scripts/notify-hook/__tests__/team-worker-posttooluse.test.js.map +1 -0
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +18 -1
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
- package/dist/scripts/notify-hook/process-runner.js +7 -3
- package/dist/scripts/notify-hook/process-runner.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +96 -11
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +3 -3
- package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts +34 -0
- package/dist/scripts/notify-hook/team-worker-posttooluse.d.ts.map +1 -0
- package/dist/scripts/notify-hook/team-worker-posttooluse.js +434 -0
- package/dist/scripts/notify-hook/team-worker-posttooluse.js.map +1 -0
- package/dist/scripts/notify-hook/team-worker.d.ts +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +3 -43
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook.js +36 -5
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/postinstall.d.ts +1 -5
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +3 -42
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/prompt-inventory.d.ts +29 -0
- package/dist/scripts/prompt-inventory.d.ts.map +1 -0
- package/dist/scripts/prompt-inventory.js +178 -0
- package/dist/scripts/prompt-inventory.js.map +1 -0
- package/dist/scripts/run-test-files.js +32 -2
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +3 -0
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +9 -1
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts +13 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -0
- package/dist/scripts/sync-plugin-mirror.js +242 -0
- package/dist/scripts/sync-plugin-mirror.js.map +1 -0
- package/dist/scripts/verify-native-agents.d.ts +16 -0
- package/dist/scripts/verify-native-agents.d.ts.map +1 -0
- package/dist/scripts/verify-native-agents.js +188 -0
- package/dist/scripts/verify-native-agents.js.map +1 -0
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +7 -2
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/boundary.test.d.ts +2 -0
- package/dist/sidecar/__tests__/boundary.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/boundary.test.js +48 -0
- package/dist/sidecar/__tests__/boundary.test.js.map +1 -0
- package/dist/sidecar/__tests__/collector.test.d.ts +2 -0
- package/dist/sidecar/__tests__/collector.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/collector.test.js +162 -0
- package/dist/sidecar/__tests__/collector.test.js.map +1 -0
- package/dist/sidecar/__tests__/render.test.d.ts +2 -0
- package/dist/sidecar/__tests__/render.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/render.test.js +67 -0
- package/dist/sidecar/__tests__/render.test.js.map +1 -0
- package/dist/sidecar/__tests__/tmux.test.d.ts +2 -0
- package/dist/sidecar/__tests__/tmux.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/tmux.test.js +30 -0
- package/dist/sidecar/__tests__/tmux.test.js.map +1 -0
- package/dist/sidecar/__tests__/watch.test.d.ts +2 -0
- package/dist/sidecar/__tests__/watch.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/watch.test.js +42 -0
- package/dist/sidecar/__tests__/watch.test.js.map +1 -0
- package/dist/sidecar/collector.d.ts +4 -0
- package/dist/sidecar/collector.d.ts.map +1 -0
- package/dist/sidecar/collector.js +377 -0
- package/dist/sidecar/collector.js.map +1 -0
- package/dist/sidecar/index.d.ts +25 -0
- package/dist/sidecar/index.d.ts.map +1 -0
- package/dist/sidecar/index.js +165 -0
- package/dist/sidecar/index.js.map +1 -0
- package/dist/sidecar/render.d.ts +3 -0
- package/dist/sidecar/render.d.ts.map +1 -0
- package/dist/sidecar/render.js +72 -0
- package/dist/sidecar/render.js.map +1 -0
- package/dist/sidecar/tmux.d.ts +13 -0
- package/dist/sidecar/tmux.d.ts.map +1 -0
- package/dist/sidecar/tmux.js +44 -0
- package/dist/sidecar/tmux.js.map +1 -0
- package/dist/sidecar/types.d.ts +125 -0
- package/dist/sidecar/types.d.ts.map +1 -0
- package/dist/sidecar/types.js +2 -0
- package/dist/sidecar/types.js.map +1 -0
- package/dist/state/__tests__/operations.test.js +50 -22
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +19 -7
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/commit-hygiene.test.d.ts +2 -0
- package/dist/team/__tests__/commit-hygiene.test.d.ts.map +1 -0
- package/dist/team/__tests__/commit-hygiene.test.js +93 -0
- package/dist/team/__tests__/commit-hygiene.test.js.map +1 -0
- package/dist/team/__tests__/delegation-policy.test.d.ts +2 -0
- package/dist/team/__tests__/delegation-policy.test.d.ts.map +1 -0
- package/dist/team/__tests__/delegation-policy.test.js +69 -0
- package/dist/team/__tests__/delegation-policy.test.js.map +1 -0
- package/dist/team/__tests__/events.test.js +54 -4
- package/dist/team/__tests__/events.test.js.map +1 -1
- package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts +2 -0
- package/dist/team/__tests__/hook-primary-e2e-contract.test.d.ts.map +1 -0
- package/dist/team/__tests__/hook-primary-e2e-contract.test.js +78 -0
- package/dist/team/__tests__/hook-primary-e2e-contract.test.js.map +1 -0
- package/dist/team/__tests__/model-contract.test.js +18 -2
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/repo-aware-decomposition.test.d.ts +2 -0
- package/dist/team/__tests__/repo-aware-decomposition.test.d.ts.map +1 -0
- package/dist/team/__tests__/repo-aware-decomposition.test.js +95 -0
- package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -0
- package/dist/team/__tests__/runtime.test.js +677 -17
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +177 -1
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +110 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +301 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +94 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/commit-hygiene.d.ts +22 -3
- package/dist/team/commit-hygiene.d.ts.map +1 -1
- package/dist/team/commit-hygiene.js +134 -2
- package/dist/team/commit-hygiene.js.map +1 -1
- package/dist/team/contracts.d.ts +1 -1
- package/dist/team/contracts.d.ts.map +1 -1
- package/dist/team/contracts.js +2 -0
- package/dist/team/contracts.js.map +1 -1
- package/dist/team/dag-schema.d.ts +38 -0
- package/dist/team/dag-schema.d.ts.map +1 -0
- package/dist/team/dag-schema.js +221 -0
- package/dist/team/dag-schema.js.map +1 -0
- package/dist/team/delegation-policy.d.ts +3 -0
- package/dist/team/delegation-policy.d.ts.map +1 -0
- package/dist/team/delegation-policy.js +82 -0
- package/dist/team/delegation-policy.js.map +1 -0
- package/dist/team/model-contract.d.ts +3 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +44 -5
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/repo-aware-decomposition.d.ts +60 -0
- package/dist/team/repo-aware-decomposition.d.ts.map +1 -0
- package/dist/team/repo-aware-decomposition.js +229 -0
- package/dist/team/repo-aware-decomposition.js.map +1 -0
- package/dist/team/runtime.d.ts +34 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +191 -50
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +33 -0
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state/types.d.ts +23 -1
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state-root.d.ts +35 -0
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +281 -1
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts +27 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +6 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +1 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +83 -6
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +3 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +77 -4
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +8 -0
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +42 -9
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts +3 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +25 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +2 -3
- package/dist/utils/agents-model-table.js.map +1 -1
- package/package.json +14 -7
- package/plugins/oh-my-codex/.app.json +3 -0
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +30 -0
- package/plugins/oh-my-codex/.mcp.json +44 -0
- package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +114 -0
- package/plugins/oh-my-codex/skills/analyze/SKILL.md +148 -0
- package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +61 -0
- package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +61 -0
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +231 -0
- package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +68 -0
- package/plugins/oh-my-codex/skills/cancel/SKILL.md +399 -0
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +290 -0
- package/plugins/oh-my-codex/skills/configure-notifications/SKILL.md +287 -0
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +464 -0
- package/plugins/oh-my-codex/skills/doctor/SKILL.md +239 -0
- package/plugins/oh-my-codex/skills/help/SKILL.md +202 -0
- package/plugins/oh-my-codex/skills/hud/SKILL.md +98 -0
- package/plugins/oh-my-codex/skills/note/SKILL.md +62 -0
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +135 -0
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +86 -0
- package/plugins/oh-my-codex/skills/plan/SKILL.md +276 -0
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +269 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +162 -0
- package/plugins/oh-my-codex/skills/security-review/SKILL.md +300 -0
- package/plugins/oh-my-codex/skills/skill/SKILL.md +835 -0
- package/plugins/oh-my-codex/skills/team/SKILL.md +510 -0
- package/plugins/oh-my-codex/skills/trace/SKILL.md +33 -0
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +143 -0
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +175 -0
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +153 -0
- package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +76 -0
- package/plugins/oh-my-codex/skills/wiki/SKILL.md +57 -0
- package/plugins/oh-my-codex/skills/worker/SKILL.md +106 -0
- package/prompts/critic.md +35 -83
- package/prompts/executor.md +32 -109
- package/prompts/explore-harness.md +1 -1
- package/prompts/explore.md +37 -90
- package/prompts/planner.md +28 -58
- package/prompts/researcher.md +46 -78
- package/prompts/verifier.md +23 -31
- package/skills/autopilot/SKILL.md +1 -4
- package/skills/deep-interview/SKILL.md +8 -5
- package/skills/doctor/SKILL.md +45 -17
- package/skills/help/SKILL.md +2 -2
- package/skills/omx-setup/SKILL.md +52 -9
- package/skills/plan/SKILL.md +1 -4
- package/skills/ralph/SKILL.md +3 -5
- package/skills/ralplan/SKILL.md +2 -6
- package/skills/team/SKILL.md +2 -5
- package/skills/ultraqa/SKILL.md +1 -4
- package/skills/ultrawork/SKILL.md +2 -3
- package/skills/visual-ralph/SKILL.md +153 -0
- package/skills/web-clone/SKILL.md +12 -354
- package/src/scripts/__tests__/codex-native-hook.test.ts +2306 -224
- package/src/scripts/__tests__/generate-release-body.test.ts +109 -0
- package/src/scripts/__tests__/hook-derived-watcher.test.ts +132 -2
- package/src/scripts/__tests__/postinstall.test.ts +7 -104
- package/src/scripts/__tests__/prompt-inventory.test.ts +64 -0
- package/src/scripts/__tests__/run-test-files.test.ts +72 -0
- package/src/scripts/__tests__/smoke-packed-install.test.ts +15 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +204 -0
- package/src/scripts/codex-execution-surface.ts +73 -0
- package/src/scripts/codex-native-hook.ts +579 -82
- package/src/scripts/codex-native-pre-post.ts +157 -19
- package/src/scripts/generate-release-body.ts +34 -2
- package/src/scripts/hook-derived-watcher.ts +51 -8
- package/src/scripts/notify-fallback-watcher.ts +65 -9
- package/src/scripts/notify-hook/__tests__/team-worker-posttooluse.test.ts +180 -0
- package/src/scripts/notify-hook/managed-tmux.ts +22 -4
- package/src/scripts/notify-hook/process-runner.ts +7 -3
- package/src/scripts/notify-hook/team-dispatch.ts +103 -11
- package/src/scripts/notify-hook/team-tmux-guard.ts +3 -3
- package/src/scripts/notify-hook/team-worker-posttooluse.ts +536 -0
- package/src/scripts/notify-hook/team-worker.ts +4 -48
- package/src/scripts/notify-hook.ts +36 -5
- package/src/scripts/postinstall.ts +4 -58
- package/src/scripts/prompt-inventory.ts +218 -0
- package/src/scripts/run-test-files.ts +41 -2
- package/src/scripts/smoke-packed-install.ts +10 -1
- package/src/scripts/sync-plugin-mirror.ts +360 -0
- package/src/scripts/verify-native-agents.ts +245 -0
- package/templates/AGENTS.md +26 -91
- package/templates/catalog-manifest.json +16 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { chmod, mkdtemp, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { handleTeamWorkerPostToolUseSuccess, teamWorkerPostToolUseInternals } from '../team-worker-posttooluse.js';
|
|
9
|
+
import { readTeamEvents } from '../../../team/state/events.js';
|
|
10
|
+
|
|
11
|
+
async function initWorkerFixture(): Promise<{ cwd: string; stateRoot: string; env: NodeJS.ProcessEnv }> {
|
|
12
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-posttooluse-worker-'));
|
|
13
|
+
execFileSync('git', ['init'], { cwd, stdio: 'ignore' });
|
|
14
|
+
execFileSync('git', ['config', 'user.email', 'test@example.com'], { cwd, stdio: 'ignore' });
|
|
15
|
+
execFileSync('git', ['config', 'user.name', 'Test User'], { cwd, stdio: 'ignore' });
|
|
16
|
+
await writeFile(join(cwd, 'README.md'), 'hello\n', 'utf-8');
|
|
17
|
+
execFileSync('git', ['add', 'README.md'], { cwd, stdio: 'ignore' });
|
|
18
|
+
execFileSync('git', ['commit', '-m', 'init'], { cwd, stdio: 'ignore' });
|
|
19
|
+
|
|
20
|
+
const stateRoot = join(cwd, '.omx', 'state');
|
|
21
|
+
const workerDir = join(stateRoot, 'team', 'demo-team', 'workers', 'worker-1');
|
|
22
|
+
await mkdir(workerDir, { recursive: true });
|
|
23
|
+
await writeFile(join(workerDir, 'identity.json'), JSON.stringify({
|
|
24
|
+
name: 'worker-1',
|
|
25
|
+
team_state_root: stateRoot,
|
|
26
|
+
worktree_path: cwd,
|
|
27
|
+
}, null, 2), 'utf-8');
|
|
28
|
+
await writeFile(join(stateRoot, 'team', 'demo-team', 'phase.json'), JSON.stringify({ current_phase: 'team-exec' }, null, 2), 'utf-8');
|
|
29
|
+
await writeFile(join(stateRoot, 'team', 'demo-team', 'config.json'), JSON.stringify({ leader_cwd: cwd }, null, 2), 'utf-8');
|
|
30
|
+
return {
|
|
31
|
+
cwd,
|
|
32
|
+
stateRoot,
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
OMX_TEAM_WORKER: 'demo-team/worker-1',
|
|
36
|
+
OMX_TEAM_STATE_ROOT: stateRoot,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const successPayload = {
|
|
42
|
+
hook_event_name: 'PostToolUse',
|
|
43
|
+
tool_name: 'Bash',
|
|
44
|
+
tool_response: { exit_code: 0 },
|
|
45
|
+
tool_use_id: 'tool-1',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('handleTeamWorkerPostToolUseSuccess', () => {
|
|
49
|
+
it('creates a safe worker checkpoint, ledger entries, leader signal, and dedupe marker', async () => {
|
|
50
|
+
const fixture = await initWorkerFixture();
|
|
51
|
+
await writeFile(join(fixture.cwd, 'feature.txt'), 'feature\n', 'utf-8');
|
|
52
|
+
|
|
53
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
54
|
+
|
|
55
|
+
assert.equal(result.handled, true);
|
|
56
|
+
assert.equal(result.status, 'applied');
|
|
57
|
+
assert.ok(result.checkpointCommit);
|
|
58
|
+
assert.deepEqual(result.operationKinds, ['auto_checkpoint', 'worker_clean_rebase', 'leader_integration_attempt']);
|
|
59
|
+
|
|
60
|
+
const log = execFileSync('git', ['log', '-1', '--pretty=%s'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
61
|
+
assert.equal(log, 'omx(team): auto-checkpoint worker-1');
|
|
62
|
+
|
|
63
|
+
const events = await readTeamEvents('demo-team', fixture.cwd, { type: 'worker_integration_attempt_requested' });
|
|
64
|
+
assert.equal(events.length, 1);
|
|
65
|
+
const event = events[0]!;
|
|
66
|
+
assert.equal(event.type, 'worker_integration_attempt_requested');
|
|
67
|
+
assert.equal(event.worker, 'worker-1');
|
|
68
|
+
const metadata = event.metadata as Record<string, unknown>;
|
|
69
|
+
assert.equal(metadata.operation_kind, 'leader_integration_attempt');
|
|
70
|
+
assert.equal(metadata.outcome_status, 'applied');
|
|
71
|
+
assert.equal(metadata.source, 'posttooluse');
|
|
72
|
+
assert.equal(metadata.dedupe_key, result.dedupeKey);
|
|
73
|
+
assert.match(String(metadata.leader_head_observed), /^[0-9a-f]{40}$/);
|
|
74
|
+
|
|
75
|
+
const dedupe = JSON.parse(await readFile(join(fixture.stateRoot, 'team', 'demo-team', 'workers', 'worker-1', 'posttooluse-dedupe.json'), 'utf-8')) as { keys: string[] };
|
|
76
|
+
assert.equal(dedupe.keys.includes(result.dedupeKey!), true);
|
|
77
|
+
|
|
78
|
+
const ledger = JSON.parse(await readFile(join(fixture.cwd, '.omx', 'reports', 'team-commit-hygiene', 'demo-team.ledger.json'), 'utf-8')) as { entries: Array<{ operation: string; status: string }> };
|
|
79
|
+
assert.equal(ledger.entries.some((entry) => entry.operation === 'auto_checkpoint' && entry.status === 'applied'), true);
|
|
80
|
+
assert.equal(ledger.entries.some((entry) => entry.operation === 'worker_clean_rebase' && entry.status === 'noop'), true);
|
|
81
|
+
assert.equal(ledger.entries.some((entry) => entry.operation === 'leader_integration_attempt' && entry.status === 'applied'), true);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('records noop without creating a checkpoint commit', async () => {
|
|
85
|
+
const fixture = await initWorkerFixture();
|
|
86
|
+
const before = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
87
|
+
|
|
88
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
89
|
+
|
|
90
|
+
const after = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
91
|
+
assert.equal(result.status, 'noop');
|
|
92
|
+
assert.equal(result.checkpointCommit, null);
|
|
93
|
+
assert.equal(after, before);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('skips protected runtime-only changes', async () => {
|
|
97
|
+
const fixture = await initWorkerFixture();
|
|
98
|
+
await writeFile(join(fixture.cwd, 'AGENTS.md'), 'generated worker instructions\n', 'utf-8');
|
|
99
|
+
|
|
100
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
101
|
+
|
|
102
|
+
assert.equal(result.status, 'skipped');
|
|
103
|
+
assert.equal(result.reason, 'only_protected_paths_changed');
|
|
104
|
+
assert.equal(result.checkpointCommit, null);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('does not checkpoint its own runtime report artifacts on a repeated PostToolUse turn', async () => {
|
|
108
|
+
const fixture = await initWorkerFixture();
|
|
109
|
+
await writeFile(join(fixture.cwd, 'feature.txt'), 'feature\n', 'utf-8');
|
|
110
|
+
|
|
111
|
+
const first = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
112
|
+
const headAfterFirst = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
113
|
+
const second = await handleTeamWorkerPostToolUseSuccess({ ...successPayload, tool_use_id: 'tool-2' }, fixture.cwd, fixture.env);
|
|
114
|
+
const headAfterSecond = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
115
|
+
const events = await readTeamEvents('demo-team', fixture.cwd, { type: 'worker_integration_attempt_requested' });
|
|
116
|
+
|
|
117
|
+
assert.equal(first.status, 'applied');
|
|
118
|
+
assert.equal(second.status, 'noop');
|
|
119
|
+
assert.equal(headAfterSecond, headAfterFirst);
|
|
120
|
+
assert.equal(events.length, 1);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('skips checkpointing when the index is already staged and preserves staged changes', async () => {
|
|
124
|
+
const fixture = await initWorkerFixture();
|
|
125
|
+
await writeFile(join(fixture.cwd, 'pre-staged.txt'), 'already staged\n', 'utf-8');
|
|
126
|
+
execFileSync('git', ['add', 'pre-staged.txt'], { cwd: fixture.cwd, stdio: 'ignore' });
|
|
127
|
+
await writeFile(join(fixture.cwd, 'unstaged.txt'), 'new work\n', 'utf-8');
|
|
128
|
+
|
|
129
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
130
|
+
const staged = execFileSync('git', ['diff', '--name-only', '--cached'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim().split('\n').filter(Boolean);
|
|
131
|
+
const status = execFileSync('git', ['status', '--porcelain=v1', '-uall'], { cwd: fixture.cwd, encoding: 'utf-8' });
|
|
132
|
+
|
|
133
|
+
assert.equal(result.status, 'skipped');
|
|
134
|
+
assert.equal(result.reason, 'index_not_clean');
|
|
135
|
+
assert.deepEqual(staged, ['pre-staged.txt']);
|
|
136
|
+
assert.match(status, /\?\? unstaged\.txt/);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('unstages checkpointable paths when checkpoint commit fails after staging', async () => {
|
|
140
|
+
const fixture = await initWorkerFixture();
|
|
141
|
+
const hookPath = join(fixture.cwd, '.git', 'hooks', 'prepare-commit-msg');
|
|
142
|
+
await writeFile(hookPath, '#!/bin/sh\nexit 42\n', 'utf-8');
|
|
143
|
+
await chmod(hookPath, 0o755);
|
|
144
|
+
await writeFile(join(fixture.cwd, 'commit-fail.txt'), 'must not remain staged\n', 'utf-8');
|
|
145
|
+
|
|
146
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, fixture.cwd, fixture.env);
|
|
147
|
+
const staged = execFileSync('git', ['diff', '--name-only', '--cached'], { cwd: fixture.cwd, encoding: 'utf-8' }).trim();
|
|
148
|
+
const status = execFileSync('git', ['status', '--porcelain=v1', '-uall'], { cwd: fixture.cwd, encoding: 'utf-8' });
|
|
149
|
+
|
|
150
|
+
assert.equal(result.status, 'skipped');
|
|
151
|
+
assert.match(result.reason ?? '', /^git_commit_failed:/);
|
|
152
|
+
assert.equal(staged, '');
|
|
153
|
+
assert.match(status, /\?\? commit-fail\.txt/);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('exports canonical dedupe and protected-path helpers for fallback suppression', () => {
|
|
157
|
+
const key = teamWorkerPostToolUseInternals.buildDedupeKey({
|
|
158
|
+
teamName: 'team',
|
|
159
|
+
workerName: 'worker-1',
|
|
160
|
+
workerHeadAfter: 'after',
|
|
161
|
+
operationKind: 'leader_integration_attempt',
|
|
162
|
+
});
|
|
163
|
+
assert.equal(key, 'team|worker-1|after|leader_integration_attempt');
|
|
164
|
+
assert.equal(teamWorkerPostToolUseInternals.isProtectedCheckpointPath('.omx/state/team/x'), true);
|
|
165
|
+
assert.equal(teamWorkerPostToolUseInternals.isProtectedCheckpointPath('src/index.ts'), false);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('fails closed for missing worker identity without guessed state writes', async () => {
|
|
169
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-posttooluse-missing-identity-'));
|
|
170
|
+
const result = await handleTeamWorkerPostToolUseSuccess(successPayload, cwd, {
|
|
171
|
+
...process.env,
|
|
172
|
+
OMX_TEAM_WORKER: 'demo-team/worker-1',
|
|
173
|
+
OMX_TEAM_STATE_ROOT: join(cwd, '.omx', 'state'),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
assert.equal(result.handled, false);
|
|
177
|
+
assert.equal(result.status, 'skipped');
|
|
178
|
+
assert.equal(existsSync(join(cwd, '.omx', 'state', 'team')), false);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
@@ -61,6 +61,10 @@ function readNativeSessionId(sessionState: { native_session_id?: unknown; codex_
|
|
|
61
61
|
return safeString(sessionState.native_session_id || sessionState.codex_session_id || '').trim();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function readAuthoritativeTmuxSessionName(sessionState: { tmux_session_name?: unknown; tmuxSessionName?: unknown }): string {
|
|
65
|
+
return safeString(sessionState.tmux_session_name || sessionState.tmuxSessionName || '').trim();
|
|
66
|
+
}
|
|
67
|
+
|
|
64
68
|
function readCurrentTmuxSessionName(): string {
|
|
65
69
|
if (!process.env.TMUX) return '';
|
|
66
70
|
try {
|
|
@@ -154,10 +158,12 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
const authoritativeSessionCwd = safeString(sessionState.cwd || cwd).trim() || cwd;
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
+
const authoritativeTmuxSessionName = readAuthoritativeTmuxSessionName(sessionState);
|
|
162
|
+
const expectedTmuxSessionName = authoritativeTmuxSessionName
|
|
163
|
+
|| buildExpectedManagedTmuxSessionName(
|
|
164
|
+
authoritativeSessionCwd,
|
|
165
|
+
canonicalSessionId || invocationSessionId,
|
|
166
|
+
);
|
|
161
167
|
const currentTmuxSessionName = readCurrentTmuxSessionName();
|
|
162
168
|
if (currentTmuxSessionName && currentTmuxSessionName === expectedTmuxSessionName) {
|
|
163
169
|
return {
|
|
@@ -171,6 +177,18 @@ export async function resolveManagedSessionContext(cwd: string, payload: any, {
|
|
|
171
177
|
currentTmuxSessionName,
|
|
172
178
|
};
|
|
173
179
|
}
|
|
180
|
+
if (authoritativeTmuxSessionName && currentTmuxSessionName) {
|
|
181
|
+
return {
|
|
182
|
+
managed: false,
|
|
183
|
+
reason: 'tmux_session_mismatch',
|
|
184
|
+
invocationSessionId,
|
|
185
|
+
canonicalSessionId,
|
|
186
|
+
nativeSessionId,
|
|
187
|
+
sessionState,
|
|
188
|
+
expectedTmuxSessionName,
|
|
189
|
+
currentTmuxSessionName,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
174
192
|
|
|
175
193
|
if (processHasAncestorPid(sessionState.pid)) {
|
|
176
194
|
return {
|
|
@@ -6,7 +6,11 @@ import { spawn } from 'child_process';
|
|
|
6
6
|
|
|
7
7
|
export function runProcess(command: string, args: string[], timeoutMs = 3000): Promise<{ stdout: string; stderr: string; code: number | null }> {
|
|
8
8
|
return new Promise((resolve, reject) => {
|
|
9
|
-
const
|
|
9
|
+
const usingTestTmux = command === 'tmux' && process.env.OMX_TEST_TMUX_BIN;
|
|
10
|
+
const relaxingTestTmuxTimeout = command === 'tmux' && process.env.OMX_TEST_RELAX_TMUX_TIMEOUT === '1';
|
|
11
|
+
const executable = usingTestTmux ? process.env.OMX_TEST_TMUX_BIN as string : command;
|
|
12
|
+
const effectiveTimeoutMs = usingTestTmux || relaxingTestTmuxTimeout ? Math.max(timeoutMs, 10_000) : timeoutMs;
|
|
13
|
+
const child = spawn(executable, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
10
14
|
let stdout = '';
|
|
11
15
|
let stderr = '';
|
|
12
16
|
let finished = false;
|
|
@@ -15,8 +19,8 @@ export function runProcess(command: string, args: string[], timeoutMs = 3000): P
|
|
|
15
19
|
if (finished) return;
|
|
16
20
|
finished = true;
|
|
17
21
|
child.kill('SIGTERM');
|
|
18
|
-
reject(new Error(`timeout after ${
|
|
19
|
-
},
|
|
22
|
+
reject(new Error(`timeout after ${effectiveTimeoutMs}ms`));
|
|
23
|
+
}, effectiveTimeoutMs);
|
|
20
24
|
|
|
21
25
|
child.stdout.on('data', (chunk: Buffer) => {
|
|
22
26
|
stdout += chunk.toString();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
import { appendFile, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'fs/promises';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
4
4
|
import { execFileSync } from 'child_process';
|
|
5
5
|
import { dirname, join, resolve } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'node:url';
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
* owned path.
|
|
28
28
|
* Disable entirely with OMX_RUNTIME_BRIDGE=0.
|
|
29
29
|
*/
|
|
30
|
-
function runtimeExec(command, stateDir) {
|
|
30
|
+
function runtimeExec(command, stateDir, team) {
|
|
31
31
|
if (process.env.OMX_RUNTIME_BRIDGE === '0') return;
|
|
32
32
|
try {
|
|
33
33
|
const binaryPath = resolveRuntimeBinaryPath();
|
|
@@ -36,11 +36,82 @@ function runtimeExec(command, stateDir) {
|
|
|
36
36
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
37
37
|
windowsHide: true,
|
|
38
38
|
});
|
|
39
|
-
} catch {
|
|
39
|
+
} catch (error) {
|
|
40
|
+
recordBridgeFallback({
|
|
41
|
+
stateDir,
|
|
42
|
+
team,
|
|
43
|
+
operation: 'runtimeExec',
|
|
44
|
+
fallbackTarget: 'js_state_mutation',
|
|
45
|
+
command: safeString(command?.command).trim() || 'unknown',
|
|
46
|
+
requestId: safeString(command?.request_id).trim() || undefined,
|
|
47
|
+
messageId: safeString(command?.message_id).trim() || undefined,
|
|
48
|
+
reason: bridgeErrorReason(error),
|
|
49
|
+
});
|
|
40
50
|
// non-fatal: JS path is the fallback
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
function bridgeErrorReason(error) {
|
|
55
|
+
const err = error || {};
|
|
56
|
+
const stderr = safeString(err.stderr).trim();
|
|
57
|
+
if (stderr) return stderr.slice(0, 500);
|
|
58
|
+
const message = safeString(err.message).trim();
|
|
59
|
+
if (message) return message.slice(0, 500);
|
|
60
|
+
return 'unknown_bridge_error';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function bridgeFallbackLogPath(stateDir) {
|
|
64
|
+
return join(dirname(stateDir), 'logs', `team-dispatch-${new Date().toISOString().slice(0, 10)}.jsonl`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function recordBridgeFallback({
|
|
68
|
+
stateDir,
|
|
69
|
+
team,
|
|
70
|
+
operation,
|
|
71
|
+
fallbackTarget,
|
|
72
|
+
command,
|
|
73
|
+
requestId,
|
|
74
|
+
messageId,
|
|
75
|
+
reason,
|
|
76
|
+
}) {
|
|
77
|
+
const event = {
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
type: 'bridge_fallback',
|
|
80
|
+
source: 'notify-hook.team-dispatch',
|
|
81
|
+
fallback: true,
|
|
82
|
+
counter: 'team_dispatch_bridge_fallback_total',
|
|
83
|
+
bridge_operation: operation,
|
|
84
|
+
fallback_target: fallbackTarget,
|
|
85
|
+
team: safeString(team).trim() || null,
|
|
86
|
+
command: safeString(command).trim() || null,
|
|
87
|
+
request_id: safeString(requestId).trim() || null,
|
|
88
|
+
message_id: safeString(messageId).trim() || null,
|
|
89
|
+
reason: safeString(reason).trim() || 'unknown_bridge_fallback',
|
|
90
|
+
};
|
|
91
|
+
try {
|
|
92
|
+
const logPath = bridgeFallbackLogPath(stateDir);
|
|
93
|
+
mkdirSync(dirname(logPath), { recursive: true });
|
|
94
|
+
appendFileSync(logPath, `${JSON.stringify(event)}\n`);
|
|
95
|
+
} catch {
|
|
96
|
+
// best effort observability only
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
appendTeamDeliveryLog(join(dirname(stateDir), 'logs'), {
|
|
100
|
+
event: 'bridge_fallback',
|
|
101
|
+
...event,
|
|
102
|
+
}).catch(() => {});
|
|
103
|
+
} catch {
|
|
104
|
+
// best effort observability only
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
process.emitWarning(`[omx] team-dispatch bridge fallback: ${event.bridge_operation} -> ${event.fallback_target}: ${event.reason}`, {
|
|
108
|
+
code: 'OMX_TEAM_DISPATCH_BRIDGE_FALLBACK',
|
|
109
|
+
});
|
|
110
|
+
} catch {
|
|
111
|
+
// best effort observability only
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
44
115
|
function readJson(path, fallback) {
|
|
45
116
|
return readFile(path, 'utf8')
|
|
46
117
|
.then((raw) => JSON.parse(raw))
|
|
@@ -50,8 +121,29 @@ function readJson(path, fallback) {
|
|
|
50
121
|
async function readBridgeDispatchRequests(stateDir, teamName) {
|
|
51
122
|
const candidate = join(stateDir, 'dispatch.json');
|
|
52
123
|
if (!existsSync(candidate)) return null;
|
|
53
|
-
|
|
54
|
-
|
|
124
|
+
let parsed;
|
|
125
|
+
try {
|
|
126
|
+
parsed = JSON.parse(await readFile(candidate, 'utf8'));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
recordBridgeFallback({
|
|
129
|
+
stateDir,
|
|
130
|
+
team: teamName,
|
|
131
|
+
operation: 'readBridgeDispatchRequests',
|
|
132
|
+
fallbackTarget: 'legacy_dispatch_requests',
|
|
133
|
+
reason: `parse_failed:${bridgeErrorReason(error)}`,
|
|
134
|
+
});
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
if (!parsed || !Array.isArray(parsed.records)) {
|
|
138
|
+
recordBridgeFallback({
|
|
139
|
+
stateDir,
|
|
140
|
+
team: teamName,
|
|
141
|
+
operation: 'readBridgeDispatchRequests',
|
|
142
|
+
fallbackTarget: 'legacy_dispatch_requests',
|
|
143
|
+
reason: 'invalid_dispatch_compat_schema',
|
|
144
|
+
});
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
55
147
|
return parsed.records
|
|
56
148
|
.map((record) => {
|
|
57
149
|
if (!record || typeof record !== 'object') return null;
|
|
@@ -501,7 +593,7 @@ async function finalizeClaimedDispatchRequest({
|
|
|
501
593
|
request.status = 'failed';
|
|
502
594
|
request.failed_at = nowIso;
|
|
503
595
|
request.last_reason = 'unconfirmed_after_max_retries';
|
|
504
|
-
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir);
|
|
596
|
+
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir, teamName);
|
|
505
597
|
summary.processed += 1;
|
|
506
598
|
summary.failed += 1;
|
|
507
599
|
mutated = true;
|
|
@@ -538,9 +630,9 @@ async function finalizeClaimedDispatchRequest({
|
|
|
538
630
|
request.status = 'notified';
|
|
539
631
|
request.notified_at = nowIso;
|
|
540
632
|
request.last_reason = result.reason;
|
|
541
|
-
runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir);
|
|
633
|
+
runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir, teamName);
|
|
542
634
|
if (request.kind === 'mailbox' && request.message_id) {
|
|
543
|
-
runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir);
|
|
635
|
+
runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir, teamName);
|
|
544
636
|
if (usingLegacyRequests) {
|
|
545
637
|
await updateMailboxNotified(stateDir, teamName, request.to_worker, request.message_id).catch(() => {});
|
|
546
638
|
}
|
|
@@ -571,7 +663,7 @@ async function finalizeClaimedDispatchRequest({
|
|
|
571
663
|
request.status = 'failed';
|
|
572
664
|
request.failed_at = nowIso;
|
|
573
665
|
request.last_reason = result.reason;
|
|
574
|
-
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir);
|
|
666
|
+
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir, teamName);
|
|
575
667
|
summary.processed += 1;
|
|
576
668
|
summary.failed += 1;
|
|
577
669
|
mutated = true;
|
|
@@ -752,7 +844,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
|
|
|
752
844
|
const wideCap = await runProcess('tmux', verifyWideArgv, 2000);
|
|
753
845
|
// Worker is actively processing (mirrors sync path tmux-session.ts:1292-1294)
|
|
754
846
|
if (paneHasActiveTask(wideCap.stdout)) {
|
|
755
|
-
runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir);
|
|
847
|
+
runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir, request.team_name);
|
|
756
848
|
return {
|
|
757
849
|
ok: true,
|
|
758
850
|
reason: 'tmux_send_keys_confirmed_active_task',
|
|
@@ -773,7 +865,7 @@ async function injectDispatchRequest(request, config, cwd, stateDir) {
|
|
|
773
865
|
const triggerInNarrow = capturedPaneContainsTrigger(narrowCap.stdout, request.trigger_message);
|
|
774
866
|
const triggerNearTail = capturedPaneContainsTriggerNearTail(wideCap.stdout, request.trigger_message);
|
|
775
867
|
if (!triggerInNarrow && !triggerNearTail) {
|
|
776
|
-
runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir);
|
|
868
|
+
runtimeExec({ command: 'MarkDelivered', request_id: request.request_id }, stateDir, request.team_name);
|
|
777
869
|
return {
|
|
778
870
|
ok: true,
|
|
779
871
|
reason: 'tmux_send_keys_confirmed',
|
|
@@ -39,7 +39,7 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
|
|
|
39
39
|
}
|
|
40
40
|
if (skipIfScrolling) {
|
|
41
41
|
try {
|
|
42
|
-
const modeResult = await runProcess('tmux', buildPaneInModeArgv(target),
|
|
42
|
+
const modeResult = await runProcess('tmux', buildPaneInModeArgv(target), 3000);
|
|
43
43
|
if (safeString(modeResult.stdout).trim() === '1') {
|
|
44
44
|
return {
|
|
45
45
|
ok: false,
|
|
@@ -67,7 +67,7 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
|
|
|
67
67
|
readinessEvidence,
|
|
68
68
|
});
|
|
69
69
|
try {
|
|
70
|
-
const result = await runProcess('tmux', buildPaneCurrentCommandArgv(target),
|
|
70
|
+
const result = await runProcess('tmux', buildPaneCurrentCommandArgv(target), 3000);
|
|
71
71
|
paneCurrentCommand = safeString(result.stdout).trim();
|
|
72
72
|
paneRunningShell = requireRunningAgent && isPaneRunningShell(paneCurrentCommand);
|
|
73
73
|
} catch {
|
|
@@ -75,7 +75,7 @@ export async function evaluatePaneInjectionReadiness(paneTarget: any, {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
-
const capture = await runProcess('tmux', buildCapturePaneArgv(target, captureLines),
|
|
78
|
+
const capture = await runProcess('tmux', buildCapturePaneArgv(target, captureLines), 3000);
|
|
79
79
|
const paneCapture = safeString(capture.stdout);
|
|
80
80
|
const hasCaptureEvidence = paneCapture.trim() !== '';
|
|
81
81
|
if (hasCaptureEvidence) {
|