diff --git a/packages/lib/src/core/templates-entrypoint/codex-resume-hint.ts b/packages/lib/src/core/templates-entrypoint/codex-resume-hint.ts new file mode 100644 index 0000000..0b09a66 --- /dev/null +++ b/packages/lib/src/core/templates-entrypoint/codex-resume-hint.ts @@ -0,0 +1,98 @@ +import type { TemplateConfig } from "../domain.js" + +const escapeForDoubleQuotes = (value: string): string => { + const backslash = String.fromCodePoint(92) + return value + .replaceAll(backslash, `${backslash}${backslash}`) + .replaceAll(String.fromCodePoint(34), `${backslash}${String.fromCodePoint(34)}`) +} + +const entrypointCodexResumeHintTemplate = `# Ensure codex resume hint is shown for interactive shells +CODEX_HINT_PATH="/etc/profile.d/zz-codex-resume.sh" +if [[ ! -s "$CODEX_HINT_PATH" ]]; then + cat <<'EOF' > "$CODEX_HINT_PATH" +docker_git_workspace_context_line() { + REPO_REF_VALUE="\${REPO_REF:-__REPO_REF_DEFAULT__}" + REPO_URL_VALUE="\${REPO_URL:-__REPO_URL_DEFAULT__}" + + if [[ "$REPO_REF_VALUE" == issue-* ]]; then + ISSUE_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -E 's#^issue-##')" + ISSUE_URL_VALUE="" + if [[ "$REPO_URL_VALUE" == https://github.com/* ]]; then + ISSUE_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$ISSUE_REPO_VALUE" ]]; then + ISSUE_URL_VALUE="https://github.com/$ISSUE_REPO_VALUE/issues/$ISSUE_ID_VALUE" + fi + fi + if [[ -n "$ISSUE_URL_VALUE" ]]; then + printf "%s\n" "Контекст workspace: issue #$ISSUE_ID_VALUE ($ISSUE_URL_VALUE)" + else + printf "%s\n" "Контекст workspace: issue #$ISSUE_ID_VALUE" + fi + return + fi + + if [[ "$REPO_REF_VALUE" == refs/pull/*/head ]]; then + PR_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -nE 's#^refs/pull/([0-9]+)/head$#\\1#p')" + PR_URL_VALUE="" + if [[ "$REPO_URL_VALUE" == https://github.com/* && -n "$PR_ID_VALUE" ]]; then + PR_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" + if [[ -n "$PR_REPO_VALUE" ]]; then + PR_URL_VALUE="https://github.com/$PR_REPO_VALUE/pull/$PR_ID_VALUE" + fi + fi + if [[ -n "$PR_ID_VALUE" && -n "$PR_URL_VALUE" ]]; then + printf "%s\n" "Контекст workspace: PR #$PR_ID_VALUE ($PR_URL_VALUE)" + elif [[ -n "$PR_ID_VALUE" ]]; then + printf "%s\n" "Контекст workspace: PR #$PR_ID_VALUE" + elif [[ -n "$REPO_REF_VALUE" ]]; then + printf "%s\n" "Контекст workspace: pull request ($REPO_REF_VALUE)" + fi + return + fi + + if [[ -n "$REPO_URL_VALUE" ]]; then + printf "%s\n" "Контекст workspace: $REPO_URL_VALUE" + fi +} + +docker_git_print_codex_resume_hint() { + if [ -z "\${CODEX_RESUME_HINT_SHOWN-}" ]; then + DOCKER_GIT_CONTEXT_LINE="$(docker_git_workspace_context_line)" + if [[ -n "$DOCKER_GIT_CONTEXT_LINE" ]]; then + echo "$DOCKER_GIT_CONTEXT_LINE" + fi + echo "Старые сессии можно запустить с помощью codex resume или codex resume , если знаешь айди." + export CODEX_RESUME_HINT_SHOWN=1 + fi +} + +if [ -n "$BASH_VERSION" ]; then + case "$-" in + *i*) + docker_git_print_codex_resume_hint + ;; + esac +fi +if [ -n "$ZSH_VERSION" ]; then + if [[ "$-" == *i* ]]; then + docker_git_print_codex_resume_hint + fi +fi +EOF + chmod 0644 "$CODEX_HINT_PATH" +fi +if ! grep -q "zz-codex-resume.sh" /etc/bash.bashrc 2>/dev/null; then + printf "%s\\n" "if [ -f /etc/profile.d/zz-codex-resume.sh ]; then . /etc/profile.d/zz-codex-resume.sh; fi" >> /etc/bash.bashrc +fi +if [[ -s /etc/zsh/zshrc ]] && ! grep -q "zz-codex-resume.sh" /etc/zsh/zshrc 2>/dev/null; then + printf "%s\\n" "if [ -f /etc/profile.d/zz-codex-resume.sh ]; then source /etc/profile.d/zz-codex-resume.sh; fi" >> /etc/zsh/zshrc +fi` + +// PURITY: CORE +// INVARIANT: rendered output contains shell-escaped repo ref and url placeholders +// COMPLEXITY: O(1) +export const renderEntrypointCodexResumeHint = (config: TemplateConfig): string => + entrypointCodexResumeHintTemplate + .replaceAll("__REPO_REF_DEFAULT__", escapeForDoubleQuotes(config.repoRef)) + .replaceAll("__REPO_URL_DEFAULT__", escapeForDoubleQuotes(config.repoUrl)) diff --git a/packages/lib/src/core/templates-entrypoint/codex.ts b/packages/lib/src/core/templates-entrypoint/codex.ts index 2a1cb2b..20edb11 100644 --- a/packages/lib/src/core/templates-entrypoint/codex.ts +++ b/packages/lib/src/core/templates-entrypoint/codex.ts @@ -1,4 +1,5 @@ import type { TemplateConfig } from "../domain.js" +export { renderEntrypointCodexResumeHint } from "./codex-resume-hint.js" export const renderEntrypointCodexHome = (config: TemplateConfig): string => `# Ensure Codex home exists if mounted @@ -65,8 +66,6 @@ else cat <<'EOF' > "$CODEX_CONFIG_FILE" # docker-git codex config model = "gpt-5.4" -model_context_window = 1050000 -model_auto_compact_token_limit = 945000 model_reasoning_effort = "xhigh" plan_mode_reasoning_effort = "xhigh" personality = "pragmatic" @@ -80,6 +79,13 @@ shell_snapshot = true multi_agent = true apps = true shell_tool = true + +[profiles.longcontx] +model = "gpt-5.4" +model_context_window = 1050000 +model_auto_compact_token_limit = 945000 +model_reasoning_effort = "xhigh" +plan_mode_reasoning_effort = "xhigh" EOF chown 1000:1000 "$CODEX_CONFIG_FILE" || true fi @@ -114,100 +120,6 @@ export const renderEntrypointMcpPlaywright = (config: TemplateConfig): string => .replaceAll("__CODEX_HOME__", config.codexHome) .replaceAll("__SERVICE_NAME__", config.serviceName) -const entrypointCodexResumeHintTemplate = `# Ensure codex resume hint is shown for interactive shells -CODEX_HINT_PATH="/etc/profile.d/zz-codex-resume.sh" -if [[ ! -s "$CODEX_HINT_PATH" ]]; then - cat <<'EOF' > "$CODEX_HINT_PATH" -docker_git_workspace_context_line() { - REPO_REF_VALUE="\${REPO_REF:-__REPO_REF_DEFAULT__}" - REPO_URL_VALUE="\${REPO_URL:-__REPO_URL_DEFAULT__}" - - if [[ "$REPO_REF_VALUE" == issue-* ]]; then - ISSUE_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -E 's#^issue-##')" - ISSUE_URL_VALUE="" - if [[ "$REPO_URL_VALUE" == https://github.com/* ]]; then - ISSUE_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" - if [[ -n "$ISSUE_REPO_VALUE" ]]; then - ISSUE_URL_VALUE="https://github.com/$ISSUE_REPO_VALUE/issues/$ISSUE_ID_VALUE" - fi - fi - if [[ -n "$ISSUE_URL_VALUE" ]]; then - printf "%s\n" "Контекст workspace: issue #$ISSUE_ID_VALUE ($ISSUE_URL_VALUE)" - else - printf "%s\n" "Контекст workspace: issue #$ISSUE_ID_VALUE" - fi - return - fi - - if [[ "$REPO_REF_VALUE" == refs/pull/*/head ]]; then - PR_ID_VALUE="$(printf "%s" "$REPO_REF_VALUE" | sed -nE 's#^refs/pull/([0-9]+)/head$#\\1#p')" - PR_URL_VALUE="" - if [[ "$REPO_URL_VALUE" == https://github.com/* && -n "$PR_ID_VALUE" ]]; then - PR_REPO_VALUE="$(printf "%s" "$REPO_URL_VALUE" | sed -E 's#^https://github.com/##; s#[.]git$##; s#/*$##')" - if [[ -n "$PR_REPO_VALUE" ]]; then - PR_URL_VALUE="https://github.com/$PR_REPO_VALUE/pull/$PR_ID_VALUE" - fi - fi - if [[ -n "$PR_ID_VALUE" && -n "$PR_URL_VALUE" ]]; then - printf "%s\n" "Контекст workspace: PR #$PR_ID_VALUE ($PR_URL_VALUE)" - elif [[ -n "$PR_ID_VALUE" ]]; then - printf "%s\n" "Контекст workspace: PR #$PR_ID_VALUE" - elif [[ -n "$REPO_REF_VALUE" ]]; then - printf "%s\n" "Контекст workspace: pull request ($REPO_REF_VALUE)" - fi - return - fi - - if [[ -n "$REPO_URL_VALUE" ]]; then - printf "%s\n" "Контекст workspace: $REPO_URL_VALUE" - fi -} - -docker_git_print_codex_resume_hint() { - if [ -z "\${CODEX_RESUME_HINT_SHOWN-}" ]; then - DOCKER_GIT_CONTEXT_LINE="$(docker_git_workspace_context_line)" - if [[ -n "$DOCKER_GIT_CONTEXT_LINE" ]]; then - echo "$DOCKER_GIT_CONTEXT_LINE" - fi - echo "Старые сессии можно запустить с помощью codex resume или codex resume , если знаешь айди." - export CODEX_RESUME_HINT_SHOWN=1 - fi -} - -if [ -n "$BASH_VERSION" ]; then - case "$-" in - *i*) - docker_git_print_codex_resume_hint - ;; - esac -fi -if [ -n "$ZSH_VERSION" ]; then - if [[ "$-" == *i* ]]; then - docker_git_print_codex_resume_hint - fi -fi -EOF - chmod 0644 "$CODEX_HINT_PATH" -fi -if ! grep -q "zz-codex-resume.sh" /etc/bash.bashrc 2>/dev/null; then - printf "%s\\n" "if [ -f /etc/profile.d/zz-codex-resume.sh ]; then . /etc/profile.d/zz-codex-resume.sh; fi" >> /etc/bash.bashrc -fi -if [[ -s /etc/zsh/zshrc ]] && ! grep -q "zz-codex-resume.sh" /etc/zsh/zshrc 2>/dev/null; then - printf "%s\\n" "if [ -f /etc/profile.d/zz-codex-resume.sh ]; then source /etc/profile.d/zz-codex-resume.sh; fi" >> /etc/zsh/zshrc -fi` - -const escapeForDoubleQuotes = (value: string): string => { - const backslash = String.fromCodePoint(92) - return value - .replaceAll(backslash, `${backslash}${backslash}`) - .replaceAll(String.fromCodePoint(34), `${backslash}${String.fromCodePoint(34)}`) -} - -export const renderEntrypointCodexResumeHint = (config: TemplateConfig): string => - entrypointCodexResumeHintTemplate - .replaceAll("__REPO_REF_DEFAULT__", escapeForDoubleQuotes(config.repoRef)) - .replaceAll("__REPO_URL_DEFAULT__", escapeForDoubleQuotes(config.repoUrl)) - const entrypointAgentsNoticeTemplate = String.raw`# Ensure global AGENTS.md exists for container context AGENTS_PATH="__CODEX_HOME__/AGENTS.md" LEGACY_AGENTS_PATH="/home/__SSH_USER__/AGENTS.md" diff --git a/packages/lib/src/usecases/auth-sync-helpers.ts b/packages/lib/src/usecases/auth-sync-helpers.ts index b3cc994..d53bea6 100644 --- a/packages/lib/src/usecases/auth-sync-helpers.ts +++ b/packages/lib/src/usecases/auth-sync-helpers.ts @@ -29,21 +29,19 @@ const JsonRecordFromStringSchema = Schema.parseJson(JsonRecordSchema) const defaultEnvContents = "# docker-git env\n# KEY=value\n" const codexConfigMarker = "# docker-git codex config" -// CHANGE: switch default model to gpt-5.4 and pin xhigh reasoning for default + plan mode -// WHY: keep plan mode aligned with development mode while preserving long-context defaults -// QUOTE(ТЗ): "Сделать plan mode тоже с xhigh режимом как и разработка по дефолту. Так же заменить модель на gpt-5.4" -// REF: github-issue-109 +// CHANGE: move model_context_window and model_auto_compact_token_limit to [profiles.longcontx] +// WHY: global context window settings apply to all models; profile-scoped settings allow opt-in +// QUOTE(ТЗ): "добавь longcontx" +// REF: github-issue-183 // SOURCE: n/a -// FORMAT THEOREM: ∀c: config(c) -> model(c)="gpt-5.4" ∧ reasoning(c)=xhigh ∧ plan_reasoning(c)=xhigh +// FORMAT THEOREM: ∀c: config(c) -> model(c)="gpt-5.4" ∧ reasoning(c)=xhigh ∧ longcontx_profile(c)=defined // PURITY: CORE // EFFECT: n/a -// INVARIANT: default config stays deterministic +// INVARIANT: default config stays deterministic; longcontx profile always present // COMPLEXITY: O(1) export const defaultCodexConfig = [ "# docker-git codex config", "model = \"gpt-5.4\"", - "model_context_window = 1050000", - "model_auto_compact_token_limit = 945000", "model_reasoning_effort = \"xhigh\"", "plan_mode_reasoning_effort = \"xhigh\"", "personality = \"pragmatic\"", @@ -56,7 +54,14 @@ export const defaultCodexConfig = [ "shell_snapshot = true", "multi_agent = true", "apps = true", - "shell_tool = true" + "shell_tool = true", + "", + "[profiles.longcontx]", + "model = \"gpt-5.4\"", + "model_context_window = 1050000", + "model_auto_compact_token_limit = 945000", + "model_reasoning_effort = \"xhigh\"", + "plan_mode_reasoning_effort = \"xhigh\"" ].join("\n") export const resolvePathFromBase = (