mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-26 17:51:04 -07:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f81d3752c | |||
| 9f29ab56bc | |||
| 297d3a3067 | |||
| 749657cbf9 | |||
| 83f46557c0 | |||
| 8cda688fe2 | |||
| 5ee05c775e | |||
| 31d5947d37 | |||
| 8f03aa320e | |||
| 583839ba46 | |||
| 022e8baefc | |||
| 120dfc724d | |||
| c37b9113d7 | |||
| 2334e9b1c4 | |||
| f901a4e6b7 | |||
| c987b99394 | |||
| c4973d01da | |||
| 27a39b04b0 | |||
| ebe15553a9 | |||
| bc730b2c0f | |||
| 9fe8643552 | |||
| 07792f98cd | |||
| 7a9ed4c20a | |||
| 11a9edc808 | |||
| 24b98ade86 | |||
| 9f759f97a2 | |||
| 9ff7304391 | |||
| 84fc5cd533 | |||
| e1b3ce5b36 | |||
| 8e58df72c6 | |||
| c0d5ab1f1e | |||
| 1340c96071 | |||
| f8198a25d8 |
@@ -5,7 +5,10 @@
|
||||
"autoMemory": true,
|
||||
"memoryManager": true,
|
||||
"topicUpdateNarration": true,
|
||||
"voiceMode": true
|
||||
"voiceMode": true,
|
||||
"adk": {
|
||||
"agentSessionNoninteractiveEnabled": true
|
||||
}
|
||||
},
|
||||
"general": {
|
||||
"devtools": true
|
||||
|
||||
@@ -114,13 +114,14 @@ runs:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
DRY_RUN: '${{ inputs.dry-run }}'
|
||||
RELEASE_TAG: '${{ inputs.release-tag }}'
|
||||
GIT_PUSH_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
|
||||
run: |-
|
||||
set -e
|
||||
git add package.json package-lock.json packages/*/package.json
|
||||
git commit -m "chore(release): ${RELEASE_TAG}"
|
||||
if [[ "${DRY_RUN}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin "${BRANCH_NAME}" --follow-tags
|
||||
git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" "HEAD:${BRANCH_NAME}" --follow-tags
|
||||
else
|
||||
echo "Dry run enabled. Skipping push."
|
||||
fi
|
||||
@@ -174,9 +175,9 @@ runs:
|
||||
npm publish \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
--tag staging-tmp
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} false
|
||||
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} staging-tmp
|
||||
fi
|
||||
|
||||
- name: '🔗 Install latest core package'
|
||||
@@ -222,9 +223,9 @@ runs:
|
||||
npm publish \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
--tag staging-tmp
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} false
|
||||
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} staging-tmp
|
||||
fi
|
||||
|
||||
- name: 'Get a2a-server Token'
|
||||
@@ -249,9 +250,9 @@ runs:
|
||||
npm publish \
|
||||
--dry-run="${INPUTS_DRY_RUN}" \
|
||||
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
|
||||
--no-tag
|
||||
--tag staging-tmp
|
||||
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
|
||||
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} false
|
||||
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} staging-tmp
|
||||
fi
|
||||
|
||||
- name: '🔬 Verify NPM release by version'
|
||||
@@ -336,7 +337,8 @@ runs:
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "Cleaning up release branch ${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}..."
|
||||
git push origin --delete "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}"
|
||||
git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" --delete "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}"
|
||||
|
||||
env:
|
||||
GIT_PUSH_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
|
||||
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
|
||||
@@ -85,14 +85,51 @@ module.exports = async ({ github, context, core }) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const labelsToAdd = entry.labels_to_add || [];
|
||||
labelsToAdd.push('status/bot-triaged');
|
||||
|
||||
let labelsToAdd = entry.labels_to_add || [];
|
||||
let labelsToRemove = entry.labels_to_remove || [];
|
||||
|
||||
labelsToRemove.push('status/need-triage');
|
||||
// Deduplicate array
|
||||
|
||||
if (labelsToAdd.includes('status/manual-triage')) {
|
||||
// If the AI flagged it for manual triage, remove bot-triaged if it exists
|
||||
labelsToRemove.push('status/bot-triaged');
|
||||
// Ensure we don't accidentally try to add bot-triaged if the AI returned it
|
||||
labelsToAdd = labelsToAdd.filter((l) => l !== 'status/bot-triaged');
|
||||
} else {
|
||||
// Standard successful bot triage
|
||||
labelsToAdd.push('status/bot-triaged');
|
||||
}
|
||||
|
||||
// Deduplicate arrays
|
||||
labelsToAdd = [...new Set(labelsToAdd)];
|
||||
labelsToRemove = [...new Set(labelsToRemove)];
|
||||
|
||||
// Enforce mutually exclusive area labels
|
||||
const areaLabelsToAdd = labelsToAdd.filter((l) => l.startsWith('area/'));
|
||||
if (areaLabelsToAdd.length > 1) {
|
||||
core.warning(
|
||||
`Issue #${issueNumber} has multiple area labels to add: ${areaLabelsToAdd.join(', ')}. Keeping only the first one.`,
|
||||
);
|
||||
const firstArea = areaLabelsToAdd[0];
|
||||
labelsToAdd = labelsToAdd.filter(
|
||||
(l) => !l.startsWith('area/') || l === firstArea,
|
||||
);
|
||||
}
|
||||
|
||||
// Enforce mutually exclusive priority labels
|
||||
const priorityLabelsToAdd = labelsToAdd.filter((l) =>
|
||||
l.startsWith('priority/'),
|
||||
);
|
||||
if (priorityLabelsToAdd.length > 1) {
|
||||
core.warning(
|
||||
`Issue #${issueNumber} has multiple priority labels to add: ${priorityLabelsToAdd.join(', ')}. Keeping only the first one.`,
|
||||
);
|
||||
const firstPriority = priorityLabelsToAdd[0];
|
||||
labelsToAdd = labelsToAdd.filter(
|
||||
(l) => !l.startsWith('priority/') || l === firstPriority,
|
||||
);
|
||||
}
|
||||
|
||||
if (labelsToAdd.length > 0) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
const fs = require('node:fs');
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
core.info('Fetching open issues to check for conflicting labels...');
|
||||
|
||||
const issues = await github.paginate(github.rest.issues.listForRepo, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const conflictingLabelIssues = [];
|
||||
|
||||
for (const issue of issues) {
|
||||
if (issue.pull_request) continue;
|
||||
|
||||
const areaLabels = issue.labels
|
||||
.filter((l) => l.name && l.name.startsWith('area/'))
|
||||
.map((l) => l.name);
|
||||
|
||||
const priorityLabels = issue.labels
|
||||
.filter((l) => l.name && l.name.startsWith('priority/'))
|
||||
.map((l) => l.name);
|
||||
|
||||
if (areaLabels.length > 1 || priorityLabels.length > 1) {
|
||||
let message = `Issue #${issue.number} has conflicting labels:`;
|
||||
if (areaLabels.length > 1)
|
||||
message += ` multiple areas (${areaLabels.join(', ')}).`;
|
||||
if (priorityLabels.length > 1)
|
||||
message += ` multiple priorities (${priorityLabels.join(', ')}).`;
|
||||
|
||||
core.info(message);
|
||||
|
||||
conflictingLabelIssues.push({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
body: issue.body || '',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Limit to 50 to avoid overwhelming the AI in a single run
|
||||
const issuesToProcess = conflictingLabelIssues.slice(0, 50);
|
||||
|
||||
fs.writeFileSync(
|
||||
'conflicting_labels_issues.json',
|
||||
JSON.stringify(issuesToProcess, null, 2),
|
||||
);
|
||||
|
||||
core.info(
|
||||
`Found ${conflictingLabelIssues.length} issues with conflicting labels. Wrote ${issuesToProcess.length} to conflicting_labels_issues.json`,
|
||||
);
|
||||
};
|
||||
@@ -30,6 +30,7 @@ jobs:
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ inputs.ref || github.ref }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
|
||||
@@ -148,6 +148,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
@@ -193,6 +194,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
@@ -233,6 +235,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
@@ -314,6 +317,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
|
||||
@@ -57,6 +57,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -130,6 +131,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 'Link Checker'
|
||||
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
|
||||
with:
|
||||
@@ -157,6 +160,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
@@ -252,6 +257,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
@@ -339,6 +346,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
|
||||
- name: 'Initialize CodeQL'
|
||||
@@ -363,6 +371,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
fetch-depth: 1
|
||||
|
||||
@@ -390,6 +399,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
|
||||
@@ -43,6 +43,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
@@ -86,6 +87,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
@@ -125,6 +127,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
|
||||
@@ -19,6 +19,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: 'main'
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
|
||||
@@ -24,6 +24,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Setup Pages'
|
||||
uses: 'actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b' # ratchet:actions/configure-pages@v5
|
||||
|
||||
@@ -38,6 +38,7 @@ jobs:
|
||||
with:
|
||||
# Check out the trusted code from main for detection
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Detect Steering Changes'
|
||||
id: 'detect'
|
||||
@@ -102,6 +103,7 @@ jobs:
|
||||
# This only runs AFTER manual approval
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Remove Approval Notification'
|
||||
# Run even if other steps fail, to ensure we clean up the "Action Required" message
|
||||
|
||||
@@ -46,6 +46,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
@@ -105,6 +107,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Download Logs'
|
||||
uses: 'actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806' # ratchet:actions/download-artifact@v4
|
||||
|
||||
@@ -48,6 +48,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Log in to GitHub Container Registry'
|
||||
uses: 'docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1' # ratchet:docker/login-action@v3
|
||||
|
||||
@@ -90,6 +90,8 @@ jobs:
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
|
||||
@@ -29,7 +29,7 @@ on:
|
||||
default: false
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number || github.event.inputs.issue_number || github.ref }}'
|
||||
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || github.ref }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@@ -41,14 +41,12 @@ jobs:
|
||||
github.event_name == 'schedule' ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive != 'true') ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive == 'true') ||
|
||||
(github.event_name == 'issue_comment' && github.event.comment.user.login != 'gemini-cli[bot]' && contains(github.event.comment.body, '@gemini-cli') && contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association)) ||
|
||||
(github.event_name == 'pull_request_review_comment' && github.event.comment.user.login != 'gemini-cli[bot]' && contains(github.event.comment.body, '@gemini-cli') && contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association))
|
||||
(github.event_name == 'issue_comment' && github.event.comment.user.login != 'gemini-cli[bot]' && contains(github.event.comment.body, '@gemini-cli') && contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association))
|
||||
)
|
||||
# The reasoning phase is strictly readonly.
|
||||
permissions:
|
||||
contents: 'read'
|
||||
issues: 'read'
|
||||
pull-requests: 'read'
|
||||
actions: 'read'
|
||||
env:
|
||||
GEMINI_CLI_TRUST_WORKSPACE: 'true'
|
||||
@@ -57,7 +55,7 @@ jobs:
|
||||
id: 'determine_ref'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.pull_request.number || github.event.inputs.issue_number }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
run: |
|
||||
REF="${{ github.ref }}"
|
||||
if [ -n "$ISSUE_NUMBER" ]; then
|
||||
@@ -125,11 +123,12 @@ jobs:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GEMINI_MODEL: 'gemini-3-flash-preview'
|
||||
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
|
||||
ENABLE_PRS: "${{ github.event.inputs.enable_prs || 'false' }}"
|
||||
TRIGGER_ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
TRIGGER_COMMENT_ID: '${{ github.event.comment.id || github.event.inputs.comment_id }}'
|
||||
run: |
|
||||
PROMPT_PATH="tools/gemini-cli-bot/brain/metrics.md"
|
||||
PROMPT_PATH="tools/gemini-cli-bot/brain/scheduled.md"
|
||||
if [ "${{ github.event_name }}" = "issue_comment" ] || [ "${{ github.event.inputs.run_interactive }}" = "true" ]; then
|
||||
PROMPT_PATH="tools/gemini-cli-bot/brain/interactive.md"
|
||||
export ENABLE_PRS="true"
|
||||
@@ -152,9 +151,16 @@ jobs:
|
||||
echo "</untrusted_context>" >> trigger_context.md
|
||||
fi
|
||||
|
||||
cat trigger_context.md "$PROMPT_PATH" tools/gemini-cli-bot/brain/common.md > combined_prompt.md
|
||||
if [ "$ENABLE_PRS" = "true" ]; then
|
||||
echo "**System Directive**: PR creation is ENABLED for this run. You MUST activate the **'prs' skill** to stage your changes and generate a \`pr-description.md\` file if you are proposing fixes." >> trigger_context.md
|
||||
echo "**CRITICAL System Directive**: You MUST ONLY propose and implement a **SINGLE** improvement or fix per run. Bundling unrelated changes (e.g., a documentation update and a script fix, or a metrics update and a logic fix) into a single PR is STRICTLY FORBIDDEN and will result in immediate rejection during the critique phase. If you identify multiple issues, pick the most impactful one and ignore the others for now." >> trigger_context.md
|
||||
else
|
||||
echo "**System Directive**: PR creation is DISABLED for this run. You MUST NOT stage files or attempt to create a PR description." >> trigger_context.md
|
||||
fi
|
||||
echo "" >> trigger_context.md
|
||||
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml -p "$(cat combined_prompt.md)"
|
||||
cat trigger_context.md "$PROMPT_PATH" > combined_prompt.md
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat combined_prompt.md)"
|
||||
|
||||
if [ -n "$TRIGGER_ISSUE_NUMBER" ] && [ ! -s "issue-comment.md" ] && [ ! -s "pr-comment.md" ]; then
|
||||
echo "Agent failed to respond. Generating fallback error message."
|
||||
@@ -164,17 +170,18 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: 'Run Critique Phase'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GEMINI_MODEL: 'gemini-3-flash-preview'
|
||||
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
|
||||
run: |
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes staged. Skipping critique."
|
||||
echo "[APPROVED]" > critique_result.txt
|
||||
else
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml -p "$(cat tools/gemini-cli-bot/brain/critique.md)" 2>&1 | tee critique_output.log
|
||||
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat tools/gemini-cli-bot/.gemini/skills/critique/SKILL.md)" 2>&1 | tee critique_output.log
|
||||
|
||||
if [ "${PIPESTATUS[0]}" -eq 0 ] && grep -q "\[APPROVED\]" critique_output.log && ! grep -q "\[REJECTED\]" critique_output.log; then
|
||||
echo "[APPROVED]" > critique_result.txt
|
||||
@@ -185,7 +192,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: 'Generate Patch'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
run: |
|
||||
touch bot-changes.patch
|
||||
touch pr-description.md
|
||||
@@ -223,7 +230,7 @@ jobs:
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token 🔑'
|
||||
id: 'generate_token'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
@@ -238,7 +245,7 @@ jobs:
|
||||
id: 'determine_ref'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.pull_request.number || github.event.inputs.issue_number }}'
|
||||
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
run: |
|
||||
REF="main"
|
||||
if [ -n "$ISSUE_NUMBER" ]; then
|
||||
@@ -263,7 +270,7 @@ jobs:
|
||||
path: '${{ runner.temp }}/brain-data/'
|
||||
|
||||
- name: 'Create or Update PR'
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
|
||||
env:
|
||||
GH_TOKEN: '${{ steps.generate_token.outputs.token }}'
|
||||
FALLBACK_PAT: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
|
||||
@@ -23,6 +23,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
|
||||
@@ -33,6 +33,8 @@ jobs:
|
||||
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Lifecycle Management'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
|
||||
@@ -28,6 +28,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Log in to GitHub Container Registry'
|
||||
uses: 'docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1' # ratchet:docker/login-action@v3
|
||||
|
||||
@@ -30,6 +30,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
@@ -61,6 +63,16 @@ jobs:
|
||||
const syncIssueTypes = require('./.github/scripts/sync-issue-types.cjs');
|
||||
await syncIssueTypes({ github, context, core });
|
||||
|
||||
- name: 'Find Issues with Conflicting Labels'
|
||||
if: |-
|
||||
${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
script: |-
|
||||
const findConflictingLabels = require('./.github/scripts/find-conflicting-labels.cjs');
|
||||
await findConflictingLabels({ github, context, core });
|
||||
|
||||
- name: 'Find untriaged issues'
|
||||
if: |-
|
||||
${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
||||
@@ -81,22 +93,31 @@ jobs:
|
||||
|
||||
echo '🏷️ Finding issues missing priority labels...'
|
||||
gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:status/bot-triaged -label:priority/p0 -label:priority/p1 -label:priority/p2 -label:priority/p3 -label:priority/unknown' --limit 50 --json number,title,body > no_priority_issues.json
|
||||
--search 'is:open is:issue -label:priority/p0 -label:priority/p1 -label:priority/p2 -label:priority/p3 -label:priority/unknown' --limit 50 --json number,title,body > no_priority_issues.json
|
||||
|
||||
echo '📏 Finding issues missing effort labels...'
|
||||
gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:status/bot-triaged -label:effort/small -label:effort/medium -label:effort/large label:area/core,area/extensions,area/site,area/non-interactive' --limit 20 --json number,title,body > no_effort_issues.json
|
||||
--search 'is:open is:issue -label:effort/small -label:effort/medium -label:effort/large label:area/core,area/extensions,area/site,area/non-interactive' --limit 5 --json number,title,body > no_effort_issues.json
|
||||
|
||||
echo '🔄 Merging and deduplicating issues...'
|
||||
jq -c -s 'add | unique_by(.number)' no_area_issues.json no_kind_issues.json no_priority_issues.json no_effort_issues.json no_type_issues.json > issues_to_triage.json
|
||||
echo '🔄 Merging and deduplicating standard triage issues...'
|
||||
if [ ! -f conflicting_labels_issues.json ]; then echo "[]" > conflicting_labels_issues.json; fi
|
||||
jq -c -s 'add | unique_by(.number)' no_area_issues.json no_kind_issues.json no_priority_issues.json conflicting_labels_issues.json > standard_issues_to_triage.json
|
||||
|
||||
ISSUE_COUNT="$(jq 'length' issues_to_triage.json)"
|
||||
if [ "$ISSUE_COUNT" -gt 0 ]; then
|
||||
echo '📏 Deduplicating effort issues...'
|
||||
jq -c -s 'add | unique_by(.number)' no_effort_issues.json > effort_issues_to_triage.json
|
||||
|
||||
STANDARD_COUNT="$(jq 'length' standard_issues_to_triage.json)"
|
||||
EFFORT_COUNT="$(jq 'length' effort_issues_to_triage.json)"
|
||||
if [ "$STANDARD_COUNT" -gt 0 ] || [ "$EFFORT_COUNT" -gt 0 ]; then
|
||||
echo "has_issues=true" >> "${GITHUB_OUTPUT}"
|
||||
echo "has_standard_issues=$([ "$STANDARD_COUNT" -gt 0 ] && echo 'true' || echo 'false')" >> "${GITHUB_OUTPUT}"
|
||||
echo "has_effort_issues=$([ "$EFFORT_COUNT" -gt 0 ] && echo 'true' || echo 'false')" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "has_issues=false" >> "${GITHUB_OUTPUT}"
|
||||
echo "has_standard_issues=false" >> "${GITHUB_OUTPUT}"
|
||||
echo "has_effort_issues=false" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
echo "✅ Found ${ISSUE_COUNT} unique issues to triage! 🎯"
|
||||
echo "✅ Found ${STANDARD_COUNT} standard issues and ${EFFORT_COUNT} effort issues to triage! 🎯"
|
||||
|
||||
- name: 'Create Gemini CLI Experiments Override'
|
||||
if: |-
|
||||
@@ -129,11 +150,128 @@ jobs:
|
||||
core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`);
|
||||
return labelNames;
|
||||
|
||||
- name: 'Run Gemini Issue Analysis'
|
||||
- name: 'Run Standard Triage Analysis'
|
||||
if: |-
|
||||
steps.get_issue_from_event.outputs.has_issues == 'true' || steps.find_issues.outputs.has_issues == 'true'
|
||||
steps.get_issue_from_event.outputs.has_issues == 'true' || steps.find_issues.outputs.has_standard_issues == 'true'
|
||||
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
||||
id: 'gemini_issue_analysis'
|
||||
id: 'gemini_standard_issue_analysis'
|
||||
env:
|
||||
GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
|
||||
GEMINI_CLI_TRUST_WORKSPACE: 'true'
|
||||
GEMINI_EXP: 'gemini_exp.json'
|
||||
GEMINI_STRICT_TELEMETRY_LIMITS: 'true'
|
||||
with:
|
||||
gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
|
||||
gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
|
||||
gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}'
|
||||
gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}'
|
||||
use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}'
|
||||
settings: |-
|
||||
{
|
||||
"maxSessionTurns": 25,
|
||||
"coreTools": [
|
||||
"run_shell_command(echo)",
|
||||
"read_file"
|
||||
],
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"target": "gcp"
|
||||
}
|
||||
}
|
||||
prompt: |-
|
||||
## Role
|
||||
|
||||
You are an issue triage assistant. Analyze issues and identify
|
||||
appropriate labels. Use the available tools to gather information;
|
||||
do not ask for information to be provided.
|
||||
|
||||
## Steps
|
||||
|
||||
1. You are only able to use the echo and read_file commands. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
|
||||
2. Use the read_file tool to read the file "standard_issues_to_triage.json" which contains the JSON array of issues to triage.
|
||||
3. Review the issue title, body and any comments provided in the JSON file.
|
||||
4. Identify the most relevant labels from the existing labels, specifically focusing on area/*, kind/*, and priority/*.
|
||||
5. Label Policy:
|
||||
- If the issue already has a kind/ label, do not change it.
|
||||
- If the issue has exactly ONE priority/ label, do not change it.
|
||||
- If the issue is missing a priority/ label, OR if the issue currently has MULTIPLE priority/ labels, you must evaluate the issue's impact to determine exactly ONE priority level (priority/p0, priority/p1, priority/p2, priority/p3, or priority/unknown) based the guidelines. If you are fixing an issue with multiple priority/ labels, put the correct one in `labels_to_add` and put all the incorrect ones in `labels_to_remove`.
|
||||
- If the issue has exactly ONE area/ label, do not change it.
|
||||
- If the issue is missing an area/ label, OR if the issue currently has MULTIPLE area/ labels, select exactly ONE area/ label that best fits the issue. Issues MUST NOT have multiple area/ labels. If you are fixing an issue with multiple area/ labels, put the correct one in `labels_to_add` and put all the incorrect ones in `labels_to_remove`.
|
||||
- If any of these are missing, select exactly ONE appropriate label for the missing category.
|
||||
6. Identify other applicable labels based on the issue content, such as status/*, help wanted, good first issue, etc.
|
||||
7. Give me a single short explanation about why you are selecting each label in the process.
|
||||
8. Output a JSON array of objects, each containing the issue number
|
||||
and the labels to add and remove, along with an explanation. For example:
|
||||
```
|
||||
[
|
||||
{
|
||||
"issue_number": 123,
|
||||
"labels_to_add": ["area/core", "kind/bug", "priority/p2"],
|
||||
"labels_to_remove": ["status/need-triage"],
|
||||
"explanation": "This issue is a UI bug that needs to be addressed with medium priority."
|
||||
}
|
||||
]
|
||||
```
|
||||
If an issue cannot be classified, do not include it in the output array.
|
||||
9. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
|
||||
- Anything more than 6 versions older than the most recent should add the status/need-retesting label
|
||||
10. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
|
||||
11. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
|
||||
12. If you are uncertain about a category, use the area/unknown, kind/question, or priority/unknown labels as appropriate. If you are extremely uncertain, apply the status/manual-triage label.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Output only valid JSON format
|
||||
- Do not include any explanation or additional text, just the JSON
|
||||
- Only use labels that already exist in the repository.
|
||||
- Do not add comments or modify the issue content.
|
||||
- Do not remove the following labels maintainer, help wanted or good first issue.
|
||||
- Triage only the current issue.
|
||||
- Identify exactly ONE area/ label. Do NOT assign multiple area/ labels to a single issue.
|
||||
- Identify only one kind/ label (Do not apply kind/duplicate or kind/parent-issue)
|
||||
- Identify exactly ONE priority/ label. Do NOT assign multiple priority/ labels to a single issue.
|
||||
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
|
||||
|
||||
Categorization Guidelines (Priority):
|
||||
P0 - Urgent Blocking Issues:
|
||||
- Definition: Critical failures breaking core functionality for a large portion of users. Examples: CLI fails to launch globally, core commands (gemini run) crash on valid input, unhandled promise rejections on boot, critical security vulnerability.
|
||||
- Note: You must apply status/manual-triage instead of priority/p0.
|
||||
P1 - Critical but Workable:
|
||||
- Definition: Severe issues without a reasonable workaround, significantly degrading the developer experience but not globally blocking. Examples: Specific tools failing consistently (e.g., `web_search` returns 500s), persistent PTY streaming hangs, memory leaks leading to OOM after short use.
|
||||
P2 - Significant Issues:
|
||||
- Definition: Affect some workflows but a clear workaround exists, or non-critical bugs. Examples: Theme flickering, confusing error messages, minor UI misalignment, failing to read deeply nested config files correctly.
|
||||
P3 - Minor/Enhancements:
|
||||
- Definition: Trivial bugs, typos, documentation requests, or feature requests.
|
||||
|
||||
Categorization Guidelines (Kind):
|
||||
kind/bug: The issue is describing an unexpected behavior or failure in the application.
|
||||
kind/enhancement: The issue is describing a feature request or an improvement to an existing feature.
|
||||
kind/question: The issue is asking a question about how to use the CLI or about a specific feature.
|
||||
|
||||
Categorization Guidelines (Area):
|
||||
area/agent: The "brain" of the CLI. Core agent logic, model quality, tool/function calling, memory, web search, generated code quality, sub-agents.
|
||||
area/core: The fundamental CLI app. UI/UX, installation, OS compatibility, performance, command parsing, theming, flickering.
|
||||
area/documentation: Website docs, READMEs, inline help text.
|
||||
area/enterprise: Telemetry, Policy, Quota / Licensing
|
||||
area/extensions: Gemini CLI extensions capability
|
||||
area/non-interactive: GitHub Actions, SDK, 3P Integrations, Shell Scripting, Command line automation
|
||||
area/platform: Platform specific behavior
|
||||
area/security: Authentication, authorization, privacy, data leaks, credential storage.
|
||||
|
||||
- name: 'Stop Telemetry Collector'
|
||||
if: |-
|
||||
steps.find_issues.outputs.has_effort_issues == 'true'
|
||||
run: 'docker rm -f gemini-telemetry-collector || true'
|
||||
|
||||
- name: 'Run Effort Triage Analysis'
|
||||
if: |-
|
||||
steps.find_issues.outputs.has_effort_issues == 'true'
|
||||
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
||||
id: 'gemini_effort_issue_analysis'
|
||||
env:
|
||||
GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
@@ -166,57 +304,30 @@ jobs:
|
||||
prompt: |-
|
||||
## Role
|
||||
|
||||
You are an issue triage assistant. Analyze issues and identify
|
||||
appropriate labels. Use the available tools to gather information;
|
||||
do not ask for information to be provided.
|
||||
You are an expert software architect. Analyze the provided GitHub issues and assign the correct `effort/*` label based on the codebase complexity.
|
||||
|
||||
## Steps
|
||||
|
||||
1. You are only able to use the echo and read_file commands. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
|
||||
2. Use the read_file tool to read the file "issues_to_triage.json" which contains the JSON array of issues to triage.
|
||||
3. Review the issue title, body and any comments provided in the JSON file.
|
||||
4. Identify the most relevant labels from the existing labels, specifically focusing on area/*, kind/*, priority/*, and effort/*.
|
||||
5. Label Policy:
|
||||
- If the issue already has a kind/ label, do not change it.
|
||||
- If the issue already has a priority/ label, do not change it.
|
||||
- If the issue already has an area/ label, do not change it.
|
||||
- If the issue already has an effort/ label, do not change it.
|
||||
- If the issue is missing an effort/ label AND its area is area/core, area/extensions, area/site, or area/non-interactive, you must evaluate the architectural complexity to determine the effort level. You MUST NOT guess the root cause. You MUST actively use your codebase search tools (grep_search and glob) to search for keywords from the issue and explore the codebase. You must identify the specific files and components involved before deciding the effort. Do NOT evaluate or assign an effort/ label to issues in any other areas (such as area/agent).
|
||||
- If any of these are missing, select exactly ONE appropriate label for the missing category.
|
||||
6. Identify other applicable labels based on the issue content, such as status/*, help wanted, good first issue, etc.
|
||||
7. Give me a single short explanation about why you are selecting each label in the process.
|
||||
8. Output a JSON array of objects, each containing the issue number
|
||||
and the labels to add and remove, along with an explanation. If you assigned an effort/ label, you MUST also include an effort_analysis field. This effort_analysis must be highly detailed, technical, and empirical. It MUST NOT contain vague guesses (e.g., avoid words like "likely points to" or "possibly"). You must explicitly cite the specific file paths and architectural mechanisms you discovered using your search tools, explain the root cause, and then explicitly state how that complexity maps to the chosen effort level guidelines. For example:
|
||||
1. Use the read_file tool to read "effort_issues_to_triage.json".
|
||||
2. For each issue in the array:
|
||||
- You must evaluate the architectural complexity to determine the effort level. You MUST NOT guess the root cause. You MUST actively use your codebase search tools (grep_search and glob) to search for keywords from the issue and explore the codebase. You must identify the specific files and components involved before deciding the effort.
|
||||
3. Output a JSON array of objects, each containing the issue number and the effort label to add, along with an explanation and an effort_analysis field. This effort_analysis must be highly detailed, technical, and empirical. It MUST NOT contain vague guesses (e.g., avoid words like "likely points to" or "possibly"). You must explicitly cite the specific file paths and architectural mechanisms you discovered using your search tools, explain the root cause, and then explicitly state how that complexity maps to the chosen effort level guidelines. For example:
|
||||
```
|
||||
[
|
||||
{
|
||||
"issue_number": 123,
|
||||
"labels_to_add": ["area/core", "kind/bug", "priority/p2", "effort/small"],
|
||||
"labels_to_remove": ["status/need-triage"],
|
||||
"explanation": "This issue is a UI bug that needs to be addressed with medium priority.",
|
||||
"labels_to_add": ["effort/small"],
|
||||
"explanation": "This is a simple logic fix.",
|
||||
"effort_analysis": "The `vscode-ide-companion` extension indiscriminately tracks active text editors via `vscode.window.onDidChangeActiveTextEditor` in `open-files-manager.ts`. When a user opens `.vscode/settings.json`, its content is sent to the CLI's context. The fix is highly localized to the VS Code companion extension's event listener. It involves adding a simple conditional check to exclude specific configuration files from the active editor tracking logic, which is a trivial logic adjustment with a clear root cause."
|
||||
}
|
||||
]
|
||||
```
|
||||
If an issue cannot be classified, do not include it in the output array.
|
||||
9. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
|
||||
- Anything more than 6 versions older than the most recent should add the status/need-retesting label
|
||||
10. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
|
||||
11. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
|
||||
12. If you are uncertain about a category, use the area/unknown, kind/question, or priority/unknown labels as appropriate. If you are extremely uncertain, apply the status/manual-triage label.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Output only valid JSON format
|
||||
- Do not include any explanation or additional text, just the JSON
|
||||
- Only use labels that already exist in the repository.
|
||||
- Do not add comments or modify the issue content.
|
||||
- Do not remove the following labels maintainer, help wanted or good first issue.
|
||||
- Triage only the current issue.
|
||||
- Identify only one area/ label.
|
||||
- Identify only one kind/ label (Do not apply kind/duplicate or kind/parent-issue)
|
||||
- Identify only one priority/ label.
|
||||
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
|
||||
- Triage only the current issue.
|
||||
|
||||
Categorization Guidelines (Effort):
|
||||
effort/small (1 day or less):
|
||||
@@ -272,13 +383,29 @@ jobs:
|
||||
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
|
||||
- When users report that they dont expect the model to change those would be categorized as feature requests.
|
||||
|
||||
- name: 'Apply Labels to Issues'
|
||||
- name: 'Apply Standard Labels to Issues'
|
||||
if: |-
|
||||
${{ steps.gemini_issue_analysis.outcome == 'success' &&
|
||||
steps.gemini_issue_analysis.outputs.summary != '[]' }}
|
||||
${{ steps.gemini_standard_issue_analysis.outcome == 'success' &&
|
||||
steps.gemini_standard_issue_analysis.outputs.summary != '[]' &&
|
||||
steps.gemini_standard_issue_analysis.outputs.summary != '' }}
|
||||
env:
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
|
||||
LABELS_OUTPUT: '${{ steps.gemini_standard_issue_analysis.outputs.summary }}'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
script: |-
|
||||
const applyLabels = require('./.github/scripts/apply-issue-labels.cjs');
|
||||
await applyLabels({ github, context, core });
|
||||
|
||||
- name: 'Apply Effort Labels to Issues'
|
||||
if: |-
|
||||
${{ steps.gemini_effort_issue_analysis.outcome == 'success' &&
|
||||
steps.gemini_effort_issue_analysis.outputs.summary != '[]' &&
|
||||
steps.gemini_effort_issue_analysis.outputs.summary != '' }}
|
||||
env:
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
LABELS_OUTPUT: '${{ steps.gemini_effort_issue_analysis.outputs.summary }}'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
|
||||
@@ -21,6 +21,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
|
||||
@@ -19,6 +19,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
@@ -41,6 +43,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
|
||||
@@ -17,6 +17,8 @@ jobs:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Link Checker'
|
||||
id: 'lychee'
|
||||
|
||||
@@ -16,6 +16,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
|
||||
@@ -16,6 +16,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
|
||||
@@ -44,6 +44,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.ref }}'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
|
||||
@@ -65,11 +65,13 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -50,11 +50,13 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
# The user-level skills need to be available to the workflow
|
||||
fetch-depth: 0
|
||||
ref: 'main'
|
||||
|
||||
@@ -17,6 +17,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 'Slash Command Dispatch'
|
||||
|
||||
@@ -54,6 +54,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
|
||||
@@ -64,6 +64,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: "${{ github.event.inputs.workflow_ref || 'main' }}"
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -53,12 +53,14 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.release_ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -55,6 +55,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
@@ -171,11 +172,13 @@ jobs:
|
||||
- name: 'Checkout Ref'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
|
||||
- name: 'Checkout correct SHA'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ matrix.sha }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
@@ -216,11 +219,13 @@ jobs:
|
||||
- name: 'Checkout Ref'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
|
||||
- name: 'Checkout correct SHA'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ needs.calculate-versions.outputs.PREVIEW_SHA }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
@@ -288,11 +293,13 @@ jobs:
|
||||
- name: 'Checkout Ref'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
|
||||
- name: 'Checkout correct SHA'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ needs.calculate-versions.outputs.STABLE_SHA }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
@@ -360,6 +367,7 @@ jobs:
|
||||
- name: 'Checkout Ref'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
@@ -395,6 +403,7 @@ jobs:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: '${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}'
|
||||
GIT_PUSH_TOKEN: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
run: |-
|
||||
git add package.json packages/*/package.json
|
||||
if [ -f package-lock.json ]; then
|
||||
@@ -403,7 +412,7 @@ jobs:
|
||||
git commit -m "chore(release): bump version to ${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"
|
||||
if [[ "${DRY_RUN}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin "${BRANCH_NAME}"
|
||||
git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" "HEAD:${BRANCH_NAME}" --follow-tags
|
||||
else
|
||||
echo "Dry run enabled. Skipping push."
|
||||
fi
|
||||
|
||||
@@ -52,6 +52,7 @@ jobs:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -192,7 +193,7 @@ jobs:
|
||||
run: |
|
||||
echo "ROLLBACK_TAG=$ROLLBACK_TAG_NAME" >> "$GITHUB_OUTPUT"
|
||||
git tag "$ROLLBACK_TAG_NAME" "${ORIGIN_HASH}"
|
||||
git push origin --tags
|
||||
git push "https://x-access-token:${GITHUB_TOKEN}@github.com/${{ github.repository }}.git" --tags
|
||||
|
||||
- name: 'Verify Rollback Tag Added'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' }}"
|
||||
|
||||
@@ -26,6 +26,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
- name: 'Push'
|
||||
|
||||
@@ -32,6 +32,7 @@ jobs:
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
- name: 'Build bundle'
|
||||
|
||||
@@ -34,6 +34,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Optimize Windows Performance'
|
||||
if: "matrix.os == 'windows-latest'"
|
||||
|
||||
@@ -44,6 +44,8 @@ jobs:
|
||||
shell: 'bash'
|
||||
run: 'echo "${{ toJSON(vars) }}"'
|
||||
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 'Verify release'
|
||||
uses: './.github/actions/verify-release'
|
||||
with:
|
||||
|
||||
Vendored
+5
-1
@@ -43,9 +43,13 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"name": "CLI: Run Current File",
|
||||
"runtimeExecutable": "node",
|
||||
"runtimeArgs": ["--import", "tsx"],
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${file}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"outFiles": ["${workspaceFolder}/**/*.js"]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -18,6 +18,22 @@ on GitHub.
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.42.0 - 2026-05-12
|
||||
|
||||
- **Auto Memory Inbox:** Introduced a new inbox flow for Auto Memory with a
|
||||
canonical-patch contract for seamless skill management
|
||||
([#26338](https://github.com/google-gemini/gemini-cli/pull/26338) by
|
||||
@SandyTao520).
|
||||
- **Gemma 4 by Default:** Enabled Gemma 4 models by default via the Gemini API
|
||||
for all users
|
||||
([#26307](https://github.com/google-gemini/gemini-cli/pull/26307) by
|
||||
@Abhijit-2592).
|
||||
- **Voice Mode Enhancements:** Added wave animations and privacy/compliance UX
|
||||
warnings for the Gemini Live backend
|
||||
([#26284](https://github.com/google-gemini/gemini-cli/pull/26284) by
|
||||
@devr0306, [#26454](https://github.com/google-gemini/gemini-cli/pull/26454) by
|
||||
@cocosheng-g).
|
||||
|
||||
## Announcements: v0.41.0 - 2026-05-05
|
||||
|
||||
- **Real-time Voice Mode:** Implemented real-time voice mode with cloud and
|
||||
|
||||
+261
-108
@@ -1,6 +1,6 @@
|
||||
# Latest stable release: v0.41.0
|
||||
# Latest stable release: v0.42.0
|
||||
|
||||
Released: May 05, 2026
|
||||
Released: May 12, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
@@ -11,119 +11,272 @@ npm install -g @google/gemini-cli
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Real-time Voice Mode:** Introduced support for real-time voice interaction
|
||||
with both cloud-based and local processing backends.
|
||||
- **Enhanced Security:** Implemented mandatory workspace trust for headless
|
||||
environments and secured the loading of `.env` configuration files.
|
||||
- **Advanced Shell Validation:** Added a robust shell command validation layer
|
||||
and a core tools allowlist to prevent unauthorized execution.
|
||||
- **Improved Context Management:** Integrated a new `ContextManager` and
|
||||
`AgentChatHistory` to provide more reliable and efficient session handling.
|
||||
- **Auto-Memory Persistence:** Enabled the persistence of the auto-memory
|
||||
scratchpad, allowing for seamless skill extraction across turns.
|
||||
- **Auto Memory Inbox:** Introduced a new inbox flow for Auto Memory using a
|
||||
canonical-patch contract, enabling more robust and manageable skill
|
||||
extraction.
|
||||
- **Gemma 4 Default:** Gemma 4 models are now enabled by default via the Gemini
|
||||
API, providing improved performance and capabilities out of the box.
|
||||
- **Voice Mode Polish:** Added wave animations for visual feedback and
|
||||
privacy/compliance UX warnings specifically for the Gemini Live backend.
|
||||
- **Session Management:** Added a `--delete` flag to the `/exit` command for
|
||||
instant session deletion and introduced `/bug-memory` for easier heap
|
||||
diagnostics.
|
||||
- **Improved Reliability:** Reduced default API timeouts to 60s and implemented
|
||||
retries for undici and premature stream closure errors.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- chore(release): bump version to 0.41.0-nightly.20260423.gaa05b4583 by
|
||||
- fix(cli): prevent automatic updates from switching to less stable channels by
|
||||
@Adib234 in [#26132](https://github.com/google-gemini/gemini-cli/pull/26132)
|
||||
- chore(release): bump version to 0.42.0-nightly.20260428.g59b2dea0e by
|
||||
@gemini-cli-robot in
|
||||
[#25847](https://github.com/google-gemini/gemini-cli/pull/25847)
|
||||
- fix(core): only show `list` suggestion if the partial input is empty by
|
||||
@cynthialong0-0 in
|
||||
[#25821](https://github.com/google-gemini/gemini-cli/pull/25821)
|
||||
- feat(cli): secure .env loading and enforce workspace trust in headless mode by
|
||||
@ehedlund in [#25814](https://github.com/google-gemini/gemini-cli/pull/25814)
|
||||
- fix: fatal hard-crash on loop detection via unhandled AbortError by @hsm207 in
|
||||
[#20108](https://github.com/google-gemini/gemini-cli/pull/20108)
|
||||
- update package-lock.json by @ehedlund in
|
||||
[#25876](https://github.com/google-gemini/gemini-cli/pull/25876)
|
||||
- feat(core): enhance shell command validation and add core tools allowlist by
|
||||
@galz10 in [#25720](https://github.com/google-gemini/gemini-cli/pull/25720)
|
||||
- fix(ui): corrected background color check in user message components by
|
||||
@devr0306 in [#25880](https://github.com/google-gemini/gemini-cli/pull/25880)
|
||||
- perf(core): fix slow boot by fetching experiments and quota asynchronously by
|
||||
@spencer426 in
|
||||
[#25758](https://github.com/google-gemini/gemini-cli/pull/25758)
|
||||
- feat(core,cli): add support for Gemma 4 models (experimental) by @Abhijit-2592
|
||||
in [#25604](https://github.com/google-gemini/gemini-cli/pull/25604)
|
||||
- update FatalUntrustedWorkspaceError message to include doc link by @ehedlund
|
||||
in [#25874](https://github.com/google-gemini/gemini-cli/pull/25874)
|
||||
- docs: add Gemini CLI course link to README by @JayadityaGit in
|
||||
[#25925](https://github.com/google-gemini/gemini-cli/pull/25925)
|
||||
- feat(repo): add gemini-cli-bot metrics and workflows by @gundermanc in
|
||||
[#25888](https://github.com/google-gemini/gemini-cli/pull/25888)
|
||||
- fix(cli): allow output redirection for cli commands by @spencer426 in
|
||||
[#25894](https://github.com/google-gemini/gemini-cli/pull/25894)
|
||||
- fix(core): fail closed in YOLO mode when shell parsing fails for restricted
|
||||
rules by @ehedlund in
|
||||
[#25935](https://github.com/google-gemini/gemini-cli/pull/25935)
|
||||
- fix(cli-ui): revert backspace handling to fix Windows regression by @scidomino
|
||||
in [#25941](https://github.com/google-gemini/gemini-cli/pull/25941)
|
||||
- feat(voice): implement real-time voice mode with cloud and local backends by
|
||||
[#26142](https://github.com/google-gemini/gemini-cli/pull/26142)
|
||||
- fix(cli): pass node arguments via NODE_OPTIONS during relaunch to support SEA
|
||||
by @cocosheng-g in
|
||||
[#26130](https://github.com/google-gemini/gemini-cli/pull/26130)
|
||||
- fix(cli): handle DECKPAM keypad Enter sequences in terminal by @Gitanaskhan26
|
||||
in [#26092](https://github.com/google-gemini/gemini-cli/pull/26092)
|
||||
- docs(cli): point plan-mode session retention to actual /settings labels by
|
||||
@ifitisit in [#25978](https://github.com/google-gemini/gemini-cli/pull/25978)
|
||||
- fix(core): add missing oauth fields support in subagent parsing by
|
||||
@abhipatel12 in
|
||||
[#26141](https://github.com/google-gemini/gemini-cli/pull/26141)
|
||||
- fix(core): disconnect extension-backed MCP clients in stopExtension by
|
||||
@cocosheng-g in
|
||||
[#26136](https://github.com/google-gemini/gemini-cli/pull/26136)
|
||||
- Update documentation workflows with workspace trust by @g-samroberts in
|
||||
[#26150](https://github.com/google-gemini/gemini-cli/pull/26150)
|
||||
- refactor(acp): modularize monolithic acpClient into specialized files by
|
||||
@sripasg in [#26143](https://github.com/google-gemini/gemini-cli/pull/26143)
|
||||
- test: fix failures due to antigravity environment leakage by @adamfweidman in
|
||||
[#26162](https://github.com/google-gemini/gemini-cli/pull/26162)
|
||||
- fix(core): add explicit empty log guard in A2A pushMessage by @adamfweidman in
|
||||
[#26198](https://github.com/google-gemini/gemini-cli/pull/26198)
|
||||
- feat(cli): add --delete flag to /exit command for session deletion by
|
||||
@AbdulTawabJuly in
|
||||
[#19332](https://github.com/google-gemini/gemini-cli/pull/19332)
|
||||
- test(core): add regression test for issue for ToolConfirmationResponse by
|
||||
@Adib234 in [#26194](https://github.com/google-gemini/gemini-cli/pull/26194)
|
||||
- Add the ability to @ mention the gemini robot. by @gundermanc in
|
||||
[#26207](https://github.com/google-gemini/gemini-cli/pull/26207)
|
||||
- test(evals): add EvalMetadata JSDoc annotations to older tests by @akh64bit in
|
||||
[#26147](https://github.com/google-gemini/gemini-cli/pull/26147)
|
||||
- fix(core): reduce default API timeout to 60s and enable retries for undici
|
||||
timeouts by @Adib234 in
|
||||
[#26191](https://github.com/google-gemini/gemini-cli/pull/26191)
|
||||
- fix(core): distinguish fallback chains and fix maxAttempts for auto vs
|
||||
explicit model selection by @adamfweidman in
|
||||
[#26163](https://github.com/google-gemini/gemini-cli/pull/26163)
|
||||
- fix(cli): handle InvalidStream event gracefully without throwing by
|
||||
@adamfweidman in
|
||||
[#26218](https://github.com/google-gemini/gemini-cli/pull/26218)
|
||||
- ci(github-actions): switch to github app token and fix bot self-trigger by
|
||||
@gundermanc in
|
||||
[#26223](https://github.com/google-gemini/gemini-cli/pull/26223)
|
||||
- Respect logPrompts flag for logging sensitive fields by @lp-peg in
|
||||
[#26153](https://github.com/google-gemini/gemini-cli/pull/26153)
|
||||
- fix: correct API key validation logic in handleApiKeySubmit by
|
||||
@martin-hsu-test in
|
||||
[#25453](https://github.com/google-gemini/gemini-cli/pull/25453)
|
||||
- fix(agent): prevent exit_plan_mode from being called via shell by
|
||||
@Abhijit-2592 in
|
||||
[#24174](https://github.com/google-gemini/gemini-cli/pull/24174)
|
||||
- Changelog for v0.39.0 by @gemini-cli-robot in
|
||||
[#25848](https://github.com/google-gemini/gemini-cli/pull/25848)
|
||||
- feat(memory): persist auto-memory scratchpad for skill extraction by
|
||||
[#26230](https://github.com/google-gemini/gemini-cli/pull/26230)
|
||||
- # Fix: Inconsistent Case-Sensitivity in GrepTool by @.github/workflows/gemini-cli-bot-pulse.yml[bot] in [#26235](https://github.com/google-gemini/gemini-cli/pull/26235)
|
||||
- docs(core): add automated gemma setup guide by @Samee24 in
|
||||
[#26233](https://github.com/google-gemini/gemini-cli/pull/26233)
|
||||
- Allow non-https proxy urls to support container environments by @stevemk14ebr
|
||||
in [#26234](https://github.com/google-gemini/gemini-cli/pull/26234)
|
||||
- fix(bot): productivity and backlog optimizations by @gundermanc in
|
||||
[#26236](https://github.com/google-gemini/gemini-cli/pull/26236)
|
||||
- refactor(acp): delegate prompt turn processing logic to GeminiClient by
|
||||
@sripasg in [#26222](https://github.com/google-gemini/gemini-cli/pull/26222)
|
||||
- fix(cli): refine platform-specific undo/redo and smart bubbling for WSL by
|
||||
@cocosheng-g in
|
||||
[#26202](https://github.com/google-gemini/gemini-cli/pull/26202)
|
||||
- fix: suppress duplicate extension warnings during startup by @cocosheng-g in
|
||||
[#26208](https://github.com/google-gemini/gemini-cli/pull/26208)
|
||||
- fix(cli): use byte length instead of string length for readStdin size limits
|
||||
by @Adib234 in
|
||||
[#26224](https://github.com/google-gemini/gemini-cli/pull/26224)
|
||||
- fix(ui): made shell tool header wrap on Ctrl+O by @devr0306 in
|
||||
[#26229](https://github.com/google-gemini/gemini-cli/pull/26229)
|
||||
- Changelog for v0.41.0-preview.0 by @gemini-cli-robot in
|
||||
[#26244](https://github.com/google-gemini/gemini-cli/pull/26244)
|
||||
- Skip binary CLI relaunch by @ruomengz in
|
||||
[#26261](https://github.com/google-gemini/gemini-cli/pull/26261)
|
||||
- fix(cli): do not override GOOGLE_CLOUD_PROJECT in Cloud Shell when using
|
||||
Vertex AI by @jackwotherspoon in
|
||||
[#24455](https://github.com/google-gemini/gemini-cli/pull/24455)
|
||||
- docs(cli): add skill discovery troubleshooting checklist to tutorial by
|
||||
@pmenic in [#26018](https://github.com/google-gemini/gemini-cli/pull/26018)
|
||||
- docs(policy-engine): link to tools reference for tool names and args by
|
||||
@Aaxhirrr in [#22081](https://github.com/google-gemini/gemini-cli/pull/22081)
|
||||
- Fix posting invalid response to a comment by @gundermanc in
|
||||
[#26266](https://github.com/google-gemini/gemini-cli/pull/26266)
|
||||
- fix(cli): prevent informational logs from polluting json output by
|
||||
@cocosheng-g in
|
||||
[#26264](https://github.com/google-gemini/gemini-cli/pull/26264)
|
||||
- feat(ui): added microphone and updated placeholder for voice mode by @devr0306
|
||||
in [#26270](https://github.com/google-gemini/gemini-cli/pull/26270)
|
||||
- feat(cli): Add 'list' subcommand to '/commands' by @Jwhyee in
|
||||
[#22324](https://github.com/google-gemini/gemini-cli/pull/22324)
|
||||
- fix(core): ensure tool output cleanup on session deletion for legacy files by
|
||||
@cocosheng-g in
|
||||
[#26263](https://github.com/google-gemini/gemini-cli/pull/26263)
|
||||
- Docs: Update Agent Skills documentation by @jkcinouye in
|
||||
[#22388](https://github.com/google-gemini/gemini-cli/pull/22388)
|
||||
- test(acp): add missing coverage for extensions command error paths by
|
||||
@sahilkirad in
|
||||
[#25313](https://github.com/google-gemini/gemini-cli/pull/25313)
|
||||
- Changelog for v0.40.0 by @gemini-cli-robot in
|
||||
[#26245](https://github.com/google-gemini/gemini-cli/pull/26245)
|
||||
- fix: report AgentExecutionBlocked in non-interactive programmatic modes by
|
||||
@cocosheng-g in
|
||||
[#26262](https://github.com/google-gemini/gemini-cli/pull/26262)
|
||||
- feat(extensions): add 'delete' as an alias for /extensions uninstall by
|
||||
@martin-hsu-test in
|
||||
[#25660](https://github.com/google-gemini/gemini-cli/pull/25660)
|
||||
- fix(core): silently skip GEMINI.md paths that are directories (EISDIR) by
|
||||
@martin-hsu-test in
|
||||
[#25662](https://github.com/google-gemini/gemini-cli/pull/25662)
|
||||
- fix(ci): checkout PR branch instead of main in bot workflow by @gundermanc in
|
||||
[#26289](https://github.com/google-gemini/gemini-cli/pull/26289)
|
||||
- fix(cli): use resolved sandbox state for auto-update check by @Adib234 in
|
||||
[#26285](https://github.com/google-gemini/gemini-cli/pull/26285)
|
||||
- # Metrics Integrity & Standardized Reporting (BT-01) by @.github/workflows/gemini-cli-bot-pulse.yml[bot] in [#26240](https://github.com/google-gemini/gemini-cli/pull/26240)
|
||||
- Add Star History section to README by @bdmorgan in
|
||||
[#26290](https://github.com/google-gemini/gemini-cli/pull/26290)
|
||||
- Add Star History section to README by @bdmorgan in
|
||||
[#26308](https://github.com/google-gemini/gemini-cli/pull/26308)
|
||||
- Remove Star History section from README by @bdmorgan in
|
||||
[#26309](https://github.com/google-gemini/gemini-cli/pull/26309)
|
||||
- test(evals): add behavioral eval for file creation and write_file tool
|
||||
selection by @akh64bit in
|
||||
[#26292](https://github.com/google-gemini/gemini-cli/pull/26292)
|
||||
- feat(config): enable Gemma 4 models by default via Gemini API by @Abhijit-2592
|
||||
in [#26307](https://github.com/google-gemini/gemini-cli/pull/26307)
|
||||
- fix(cli): insert voice transcription at cursor position instead of ap… by
|
||||
@Zheyuan-Lin in
|
||||
[#26287](https://github.com/google-gemini/gemini-cli/pull/26287)
|
||||
- fix(ui): fix issue with box edges by @gundermanc in
|
||||
[#26148](https://github.com/google-gemini/gemini-cli/pull/26148)
|
||||
- fix(cli): respect .env override for GOOGLE_CLOUD_PROJECT by @DavidAPierce in
|
||||
[#26288](https://github.com/google-gemini/gemini-cli/pull/26288)
|
||||
- fix(ci): robust version checking in release verification by @scidomino in
|
||||
[#26337](https://github.com/google-gemini/gemini-cli/pull/26337)
|
||||
- fix(cli): enable daemon relaunch in binary and bundle keytar by @ruomengz in
|
||||
[#26333](https://github.com/google-gemini/gemini-cli/pull/26333)
|
||||
- fix(core): discourage unprompted git add . in prompt snippets by @akh64bit in
|
||||
[#26220](https://github.com/google-gemini/gemini-cli/pull/26220)
|
||||
- feat(ui): added wave animation for voice mode by @devr0306 in
|
||||
[#26284](https://github.com/google-gemini/gemini-cli/pull/26284)
|
||||
- fix(cli): prevent Escape from clearing input buffer (#17083) by @cocosheng-g
|
||||
in [#26339](https://github.com/google-gemini/gemini-cli/pull/26339)
|
||||
- fix(cli): undeprecate --prompt and correct positional query docs by @Adib234
|
||||
in [#26329](https://github.com/google-gemini/gemini-cli/pull/26329)
|
||||
- Metrics updates by @.github/workflows/gemini-cli-bot-pulse.yml[bot] in
|
||||
[#26348](https://github.com/google-gemini/gemini-cli/pull/26348)
|
||||
- fix(core): remove "System: Please continue." injection on InvalidStream events
|
||||
by @SandyTao520 in
|
||||
[#26340](https://github.com/google-gemini/gemini-cli/pull/26340)
|
||||
- docs(policy-engine): add tool argument keys reference and shell policy
|
||||
cross-links by @harshpujari in
|
||||
[#25292](https://github.com/google-gemini/gemini-cli/pull/25292)
|
||||
- fix(cli): resolve Ghostty/raw-mode False Cancellation in oauth flow by
|
||||
@Aarchi-07 in [#25026](https://github.com/google-gemini/gemini-cli/pull/25026)
|
||||
- fix(core): reset session-scoped state on resumption by @cocosheng-g in
|
||||
[#26342](https://github.com/google-gemini/gemini-cli/pull/26342)
|
||||
- Fix bulk of remaining issues with generalist profile by @joshualitt in
|
||||
[#26073](https://github.com/google-gemini/gemini-cli/pull/26073)
|
||||
- fix(core): make subagents aware of active approval modes by @akh64bit in
|
||||
[#23608](https://github.com/google-gemini/gemini-cli/pull/23608)
|
||||
- fix(acp): resolve agent mode disconnect and improve mode awareness by @sripasg
|
||||
in [#26332](https://github.com/google-gemini/gemini-cli/pull/26332)
|
||||
- docs(sdk): add JSDoc to exported interfaces in packages/sdk/src/types.ts by
|
||||
@cocosheng-g in
|
||||
[#26441](https://github.com/google-gemini/gemini-cli/pull/26441)
|
||||
- perf: skip redundant GEMINI.md loading in partialConfig by @cocosheng-g in
|
||||
[#26443](https://github.com/google-gemini/gemini-cli/pull/26443)
|
||||
- Enhance React guidelines by @psinha40898 in
|
||||
[#22667](https://github.com/google-gemini/gemini-cli/pull/22667)
|
||||
- feat(core): reinforce Inquiry constraints to prevent unauthorized changes by
|
||||
@akh64bit in [#26310](https://github.com/google-gemini/gemini-cli/pull/26310)
|
||||
- revert: fix(ci): robust version checking in release verification (#26337) by
|
||||
@scidomino in [#26450](https://github.com/google-gemini/gemini-cli/pull/26450)
|
||||
- refactor(UI): created constants file for ThemeDialog by @devr0306 in
|
||||
[#26446](https://github.com/google-gemini/gemini-cli/pull/26446)
|
||||
- docs: fix GitHub capitalization in releases guide by @haosenwang1018 in
|
||||
[#26379](https://github.com/google-gemini/gemini-cli/pull/26379)
|
||||
- fix(cli): ensure branch indicator updates in sub-directories and worktrees by
|
||||
@Adib234 in [#26330](https://github.com/google-gemini/gemini-cli/pull/26330)
|
||||
- feat: add minimal V8 heap snapshot utility for memory diagnostics by
|
||||
@cocosheng-g in
|
||||
[#26440](https://github.com/google-gemini/gemini-cli/pull/26440)
|
||||
- fix(hooks): preserve non-text parts in fromHookLLMRequest by @SandyTao520 in
|
||||
[#26275](https://github.com/google-gemini/gemini-cli/pull/26275)
|
||||
- fix(cli): allow early stdout when config is undefined by @cocosheng-g in
|
||||
[#26453](https://github.com/google-gemini/gemini-cli/pull/26453)
|
||||
- fix(cli)#21297: clear skills consent dialog before reload by @manavmax in
|
||||
[#26431](https://github.com/google-gemini/gemini-cli/pull/26431)
|
||||
- fix(cli): render LaTeX-style output as Unicode in the TUI by @dimssu in
|
||||
[#25802](https://github.com/google-gemini/gemini-cli/pull/25802)
|
||||
- fix(core): use close event instead of exit in child_process fallback by
|
||||
@tusaryan in [#25695](https://github.com/google-gemini/gemini-cli/pull/25695)
|
||||
- feat(voice): add privacy and compliance UX warning for Gemini Live backend by
|
||||
@cocosheng-g in
|
||||
[#26454](https://github.com/google-gemini/gemini-cli/pull/26454)
|
||||
- feat(memory): add Auto Memory inbox flow with canonical-patch contract by
|
||||
@SandyTao520 in
|
||||
[#25873](https://github.com/google-gemini/gemini-cli/pull/25873)
|
||||
- fix(cli): add missing response key to custom theme text schema by @gaurav0107
|
||||
in [#25822](https://github.com/google-gemini/gemini-cli/pull/25822)
|
||||
- fix(cli): provide manual update command when automatic update fails by
|
||||
@cocosheng-g in
|
||||
[#26052](https://github.com/google-gemini/gemini-cli/pull/26052)
|
||||
- test(cli): add unit tests for restore ACP command (#23402) by @cocosheng-g in
|
||||
[#26053](https://github.com/google-gemini/gemini-cli/pull/26053)
|
||||
- fix(ui): better error messages for ECONNRESET and ETIMEDOUT by @devr0306 in
|
||||
[#26059](https://github.com/google-gemini/gemini-cli/pull/26059)
|
||||
- feat(core): wire up the new ContextManager and AgentChatHistory by @joshualitt
|
||||
in [#25409](https://github.com/google-gemini/gemini-cli/pull/25409)
|
||||
- fix(cli): ensure sandbox proxy cleanup and remove handler leaks by @ehedlund
|
||||
in [#26065](https://github.com/google-gemini/gemini-cli/pull/26065)
|
||||
- fix(cli): correct alternate buffer warning logic for JetBrains by @Adib234 in
|
||||
[#26067](https://github.com/google-gemini/gemini-cli/pull/26067)
|
||||
- fix(cli): make MCP ping optional in list command and use configured timeout by
|
||||
@cocosheng-g in
|
||||
[#26068](https://github.com/google-gemini/gemini-cli/pull/26068)
|
||||
- fix(core): better error message for failed cloudshell-gca auth by @devr0306 in
|
||||
[#26079](https://github.com/google-gemini/gemini-cli/pull/26079)
|
||||
- feat(cli): provide manual session UUID via command line arg by @cocosheng-g in
|
||||
[#26060](https://github.com/google-gemini/gemini-cli/pull/26060)
|
||||
- Changelog for v0.40.0-preview.2 by @gemini-cli-robot in
|
||||
[#25846](https://github.com/google-gemini/gemini-cli/pull/25846)
|
||||
- (docs) update sandboxing documentation by @g-samroberts in
|
||||
[#25930](https://github.com/google-gemini/gemini-cli/pull/25930)
|
||||
- fix(core): enforce parallel task tracker updates by @anj-s in
|
||||
[#24477](https://github.com/google-gemini/gemini-cli/pull/24477)
|
||||
- Update policy so transient errors are not marked terminal by @DavidAPierce in
|
||||
[#26066](https://github.com/google-gemini/gemini-cli/pull/26066)
|
||||
- Implement bot that performs time-series metric analysis and suggests repo
|
||||
management improvements by @gundermanc in
|
||||
[#25945](https://github.com/google-gemini/gemini-cli/pull/25945)
|
||||
- fix(core): handle non-string model flags in resolution by @Adib234 in
|
||||
[#26069](https://github.com/google-gemini/gemini-cli/pull/26069)
|
||||
- fix(ux): added error message for ENOTDIR by @devr0306 in
|
||||
[#26128](https://github.com/google-gemini/gemini-cli/pull/26128)
|
||||
- Changelog for v0.40.0-preview.3 by @gemini-cli-robot in
|
||||
[#25904](https://github.com/google-gemini/gemini-cli/pull/25904)
|
||||
- fix(cli): prevent ACP stdout pollution from SessionEnd hooks by @cocosheng-g
|
||||
in [#26125](https://github.com/google-gemini/gemini-cli/pull/26125)
|
||||
- feat(cli): support boolean and number casting for env vars in settings.json by
|
||||
@cocosheng-g in
|
||||
[#26118](https://github.com/google-gemini/gemini-cli/pull/26118)
|
||||
- fix(cli): preserve Request headers in DevTools activity logger by @Adib234 in
|
||||
[#26078](https://github.com/google-gemini/gemini-cli/pull/26078)
|
||||
- fix(patch): cherry-pick 2194da2 to release/v0.41.0-preview.0-pr-26153 to patch
|
||||
version v0.41.0-preview.0 and create version 0.41.0-preview.1 by
|
||||
[#26338](https://github.com/google-gemini/gemini-cli/pull/26338)
|
||||
- test(cleanup): fix temporary directory leaks in test suites by @Adib234 in
|
||||
[#26217](https://github.com/google-gemini/gemini-cli/pull/26217)
|
||||
- feat: add ignoreLocalEnv setting and --ignore-env flag (#2493) by @cocosheng-g
|
||||
in [#26445](https://github.com/google-gemini/gemini-cli/pull/26445)
|
||||
- docs(sdk): add JSDoc to all exported interfaces and types by @fauzan171 in
|
||||
[#26277](https://github.com/google-gemini/gemini-cli/pull/26277)
|
||||
- feat(cli): improve /agents refresh logging by @cocosheng-g in
|
||||
[#26442](https://github.com/google-gemini/gemini-cli/pull/26442)
|
||||
- Fix: make Dockerfile self-contained with multi-stage build by @Famous077 in
|
||||
[#24277](https://github.com/google-gemini/gemini-cli/pull/24277)
|
||||
- fix(core): filter unsupported multimodal types from tool responses by
|
||||
@aishaneeshah in
|
||||
[#26352](https://github.com/google-gemini/gemini-cli/pull/26352)
|
||||
- fix(core): properly format markdown in AskUser tool by unescaping newlines by
|
||||
@Adib234 in [#26349](https://github.com/google-gemini/gemini-cli/pull/26349)
|
||||
- feat(bot): add actions spend metric script by @gundermanc in
|
||||
[#26463](https://github.com/google-gemini/gemini-cli/pull/26463)
|
||||
- feat(cli): add /bug-memory command and auto-capture heap snapshot in /bug by
|
||||
@Anjaligarhwal in
|
||||
[#25639](https://github.com/google-gemini/gemini-cli/pull/25639)
|
||||
- fix(cli): make SkillInboxDialog fit and scroll in alternate buffer by
|
||||
@SandyTao520 in
|
||||
[#26455](https://github.com/google-gemini/gemini-cli/pull/26455)
|
||||
- Robust Scale-Safe Lifecycle Consolidation by @gemini-cli-robot in
|
||||
[#26355](https://github.com/google-gemini/gemini-cli/pull/26355)
|
||||
- fix(ci): respect exempt labels when closing stale items by @gundermanc in
|
||||
[#26475](https://github.com/google-gemini/gemini-cli/pull/26475)
|
||||
- fix(cli): use os.homedir() for home directory warning check by @TirthNaik-99
|
||||
in [#25890](https://github.com/google-gemini/gemini-cli/pull/25890)
|
||||
- fix(a2a-server): resolve tool approval race condition and improve status
|
||||
reporting by @kschaab in
|
||||
[#26479](https://github.com/google-gemini/gemini-cli/pull/26479)
|
||||
- fix(cli): prevent settings dialog border clipping using maxHeight by
|
||||
@jackwotherspoon in
|
||||
[#26507](https://github.com/google-gemini/gemini-cli/pull/26507)
|
||||
- feat: allow queuing messages during compression (#24071) by @cocosheng-g in
|
||||
[#26506](https://github.com/google-gemini/gemini-cli/pull/26506)
|
||||
- fix(core): retry on ERR_STREAM_PREMATURE_CLOSE errors by @cocosheng-g in
|
||||
[#26519](https://github.com/google-gemini/gemini-cli/pull/26519)
|
||||
- fix(core): Minor fixes for generalist profile. by @joshualitt in
|
||||
[#26357](https://github.com/google-gemini/gemini-cli/pull/26357)
|
||||
- fix(patch): cherry-pick 3627f47 to release/v0.42.0-preview.0-pr-26542 to patch
|
||||
version v0.42.0-preview.0 and create version 0.42.0-preview.1 by
|
||||
@gemini-cli-robot in
|
||||
[#26269](https://github.com/google-gemini/gemini-cli/pull/26269)
|
||||
- fix(patch): cherry-pick 1d72a12 to release/v0.41.0-preview.1-pr-26479 to patch
|
||||
version v0.41.0-preview.1 and create version 0.41.0-preview.2 by
|
||||
[#26544](https://github.com/google-gemini/gemini-cli/pull/26544)
|
||||
- fix(patch): cherry-pick 02995ba to release/v0.42.0-preview.1-pr-26568 to patch
|
||||
version v0.42.0-preview.1 and create version 0.42.0-preview.2 by
|
||||
@gemini-cli-robot in
|
||||
[#26508](https://github.com/google-gemini/gemini-cli/pull/26508)
|
||||
- fix(patch): cherry-pick 7cc19c2 to release/v0.41.0-preview.2-pr-26507 to patch
|
||||
version v0.41.0-preview.2 and create version 0.41.0-preview.3 by
|
||||
@gemini-cli-robot in
|
||||
[#26530](https://github.com/google-gemini/gemini-cli/pull/26530)
|
||||
[#26590](https://github.com/google-gemini/gemini-cli/pull/26590)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.40.1...v0.41.0
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.41.2...v0.42.0
|
||||
|
||||
+178
-227
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.42.0-preview.2
|
||||
# Preview release: v0.43.0-preview.0
|
||||
|
||||
Released: May 06, 2026
|
||||
Released: May 12, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -13,233 +13,184 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Auto Memory Enhancements:** Introduced an Auto Memory inbox flow with a
|
||||
canonical-patch contract for better memory management.
|
||||
- **Improved Voice Mode:** Added a wave animation, microphone icon updates, and
|
||||
privacy/compliance UX warnings for the Gemini Live backend.
|
||||
- **New CLI Commands & Flags:** Added a `--delete` flag to the `/exit` command
|
||||
for session deletion, a `list` subcommand to `/commands`, and a `/bug-memory`
|
||||
command for heap snapshots.
|
||||
- **Expanded Model Support:** Gemma 4 models are now enabled by default via the
|
||||
Gemini API.
|
||||
- **Enhanced Core Resilience:** Improved API resilience with reduced timeouts,
|
||||
automatic retries for stream errors, and better handling of invalid stream
|
||||
events.
|
||||
- **Surgical Code Edits:** Steer models to use the `edit` tool for precise code
|
||||
modifications, improving accuracy and reducing context usage.
|
||||
- **Session Portability:** Added ability to export chat sessions to files and
|
||||
import them via a new CLI flag, enabling session persistence and sharing.
|
||||
- **Enhanced Security:** Introduced comprehensive shell command safety
|
||||
evaluations and strengthened model steering to prevent unauthorized changes.
|
||||
- **Context Management:** Implemented a new adaptive token calculator for more
|
||||
accurate content size estimations and optimized context pipelines.
|
||||
- **UX Improvements:** Enhanced tool call visibility with prefixed IDs and
|
||||
improved the UI for session resumption and MCP list management.
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(cli): prevent automatic updates from switching to less stable channels in
|
||||
[#26132](https://github.com/google-gemini/gemini-cli/pull/26132)
|
||||
- chore(release): bump version to 0.42.0-nightly.20260428.g59b2dea0e in
|
||||
[#26142](https://github.com/google-gemini/gemini-cli/pull/26142)
|
||||
- fix(cli): pass node arguments via NODE_OPTIONS during relaunch to support SEA
|
||||
in [#26130](https://github.com/google-gemini/gemini-cli/pull/26130)
|
||||
- fix(cli): handle DECKPAM keypad Enter sequences in terminal in
|
||||
[#26092](https://github.com/google-gemini/gemini-cli/pull/26092)
|
||||
- docs(cli): point plan-mode session retention to actual /settings labels in
|
||||
[#25978](https://github.com/google-gemini/gemini-cli/pull/25978)
|
||||
- fix(core): add missing oauth fields support in subagent parsing in
|
||||
[#26141](https://github.com/google-gemini/gemini-cli/pull/26141)
|
||||
- fix(core): disconnect extension-backed MCP clients in stopExtension in
|
||||
[#26136](https://github.com/google-gemini/gemini-cli/pull/26136)
|
||||
- Update documentation workflows with workspace trust in
|
||||
[#26150](https://github.com/google-gemini/gemini-cli/pull/26150)
|
||||
- refactor(acp): modularize monolithic acpClient into specialized files in
|
||||
[#26143](https://github.com/google-gemini/gemini-cli/pull/26143)
|
||||
- test: fix failures due to antigravity environment leakage in
|
||||
[#26162](https://github.com/google-gemini/gemini-cli/pull/26162)
|
||||
- fix(core): add explicit empty log guard in A2A pushMessage in
|
||||
[#26198](https://github.com/google-gemini/gemini-cli/pull/26198)
|
||||
- feat(cli): add --delete flag to /exit command for session deletion in
|
||||
[#19332](https://github.com/google-gemini/gemini-cli/pull/19332)
|
||||
- test(core): add regression test for issue for ToolConfirmationResponse in
|
||||
[#26194](https://github.com/google-gemini/gemini-cli/pull/26194)
|
||||
- Add the ability to @ mention the gemini robot. in
|
||||
[#26207](https://github.com/google-gemini/gemini-cli/pull/26207)
|
||||
- test(evals): add EvalMetadata JSDoc annotations to older tests in
|
||||
[#26147](https://github.com/google-gemini/gemini-cli/pull/26147)
|
||||
- fix(core): reduce default API timeout to 60s and enable retries for undici
|
||||
timeouts in [#26191](https://github.com/google-gemini/gemini-cli/pull/26191)
|
||||
- fix(core): distinguish fallback chains and fix maxAttempts for auto vs
|
||||
explicit model selection in
|
||||
[#26163](https://github.com/google-gemini/gemini-cli/pull/26163)
|
||||
- fix(cli): handle InvalidStream event gracefully without throwing in
|
||||
[#26218](https://github.com/google-gemini/gemini-cli/pull/26218)
|
||||
- ci(github-actions): switch to github app token and fix bot self-trigger in
|
||||
[#26223](https://github.com/google-gemini/gemini-cli/pull/26223)
|
||||
- Respect logPrompts flag for logging sensitive fields in
|
||||
[#26153](https://github.com/google-gemini/gemini-cli/pull/26153)
|
||||
- fix: correct API key validation logic in handleApiKeySubmit in
|
||||
[#25453](https://github.com/google-gemini/gemini-cli/pull/25453)
|
||||
- fix(agent): prevent exit_plan_mode from being called via shell in
|
||||
[#26230](https://github.com/google-gemini/gemini-cli/pull/26230)
|
||||
- # Fix: Inconsistent Case-Sensitivity in GrepTool in [#26235](https://github.com/google-gemini/gemini-cli/pull/26235)
|
||||
- docs(core): add automated gemma setup guide in
|
||||
[#26233](https://github.com/google-gemini/gemini-cli/pull/26233)
|
||||
- Allow non-https proxy urls to support container environments in
|
||||
[#26234](https://github.com/google-gemini/gemini-cli/pull/26234)
|
||||
- fix(bot): productivity and backlog optimizations in
|
||||
[#26236](https://github.com/google-gemini/gemini-cli/pull/26236)
|
||||
- refactor(acp): delegate prompt turn processing logic to GeminiClient in
|
||||
[#26222](https://github.com/google-gemini/gemini-cli/pull/26222)
|
||||
- fix(cli): refine platform-specific undo/redo and smart bubbling for WSL in
|
||||
[#26202](https://github.com/google-gemini/gemini-cli/pull/26202)
|
||||
- fix: suppress duplicate extension warnings during startup in
|
||||
[#26208](https://github.com/google-gemini/gemini-cli/pull/26208)
|
||||
- fix(cli): use byte length instead of string length for readStdin size limits
|
||||
in [#26224](https://github.com/google-gemini/gemini-cli/pull/26224)
|
||||
- fix(ui): made shell tool header wrap on Ctrl+O in
|
||||
[#26229](https://github.com/google-gemini/gemini-cli/pull/26229)
|
||||
- Changelog for v0.41.0-preview.0 in
|
||||
[#26244](https://github.com/google-gemini/gemini-cli/pull/26244)
|
||||
- Skip binary CLI relaunch in
|
||||
[#26261](https://github.com/google-gemini/gemini-cli/pull/26261)
|
||||
- fix(cli): do not override GOOGLE_CLOUD_PROJECT in Cloud Shell when using
|
||||
Vertex AI in [#24455](https://github.com/google-gemini/gemini-cli/pull/24455)
|
||||
- docs(cli): add skill discovery troubleshooting checklist to tutorial in
|
||||
[#26018](https://github.com/google-gemini/gemini-cli/pull/26018)
|
||||
- docs(policy-engine): link to tools reference for tool names and args in
|
||||
[#22081](https://github.com/google-gemini/gemini-cli/pull/22081)
|
||||
- Fix posting invalid response to a comment in
|
||||
[#26266](https://github.com/google-gemini/gemini-cli/pull/26266)
|
||||
- fix(cli): prevent informational logs from polluting json output in
|
||||
[#26264](https://github.com/google-gemini/gemini-cli/pull/26264)
|
||||
- feat(ui): added microphone and updated placeholder for voice mode in
|
||||
[#26270](https://github.com/google-gemini/gemini-cli/pull/26270)
|
||||
- feat(cli): Add 'list' subcommand to '/commands' in
|
||||
[#22324](https://github.com/google-gemini/gemini-cli/pull/22324)
|
||||
- fix(core): ensure tool output cleanup on session deletion for legacy files in
|
||||
[#26263](https://github.com/google-gemini/gemini-cli/pull/26263)
|
||||
- Docs: Update Agent Skills documentation in
|
||||
[#22388](https://github.com/google-gemini/gemini-cli/pull/22388)
|
||||
- test(acp): add missing coverage for extensions command error paths in
|
||||
[#25313](https://github.com/google-gemini/gemini-cli/pull/25313)
|
||||
- Changelog for v0.40.0 in
|
||||
[#26245](https://github.com/google-gemini/gemini-cli/pull/26245)
|
||||
- fix: report AgentExecutionBlocked in non-interactive programmatic modes in
|
||||
[#26262](https://github.com/google-gemini/gemini-cli/pull/26262)
|
||||
- feat(extensions): add 'delete' as an alias for /extensions uninstall in
|
||||
[#25660](https://github.com/google-gemini/gemini-cli/pull/25660)
|
||||
- fix(core): silently skip GEMINI.md paths that are directories (EISDIR) in
|
||||
[#25662](https://github.com/google-gemini/gemini-cli/pull/25662)
|
||||
- fix(ci): checkout PR branch instead of main in bot workflow in
|
||||
[#26289](https://github.com/google-gemini/gemini-cli/pull/26289)
|
||||
- fix(cli): use resolved sandbox state for auto-update check in
|
||||
[#26285](https://github.com/google-gemini/gemini-cli/pull/26285)
|
||||
- # Metrics Integrity & Standardized Reporting (BT-01) in [#26240](https://github.com/google-gemini/gemini-cli/pull/26240)
|
||||
- Add Star History section to README in
|
||||
[#26290](https://github.com/google-gemini/gemini-cli/pull/26290)
|
||||
- Add Star History section to README in
|
||||
[#26308](https://github.com/google-gemini/gemini-cli/pull/26308)
|
||||
- Remove Star History section from README in
|
||||
[#26309](https://github.com/google-gemini/gemini-cli/pull/26309)
|
||||
- test(evals): add behavioral eval for file creation and write_file tool
|
||||
selection in [#26292](https://github.com/google-gemini/gemini-cli/pull/26292)
|
||||
- feat(config): enable Gemma 4 models by default via Gemini API in
|
||||
[#26307](https://github.com/google-gemini/gemini-cli/pull/26307)
|
||||
- fix(cli): insert voice transcription at cursor position instead of ap… in
|
||||
[#26287](https://github.com/google-gemini/gemini-cli/pull/26287)
|
||||
- fix(ui): fix issue with box edges in
|
||||
[#26148](https://github.com/google-gemini/gemini-cli/pull/26148)
|
||||
- fix(cli): respect .env override for GOOGLE_CLOUD_PROJECT in
|
||||
[#26288](https://github.com/google-gemini/gemini-cli/pull/26288)
|
||||
- fix(ci): robust version checking in release verification in
|
||||
[#26337](https://github.com/google-gemini/gemini-cli/pull/26337)
|
||||
- fix(cli): enable daemon relaunch in binary and bundle keytar in
|
||||
[#26333](https://github.com/google-gemini/gemini-cli/pull/26333)
|
||||
- fix(core): discourage unprompted git add . in prompt snippets in
|
||||
[#26220](https://github.com/google-gemini/gemini-cli/pull/26220)
|
||||
- feat(ui): added wave animation for voice mode in
|
||||
[#26284](https://github.com/google-gemini/gemini-cli/pull/26284)
|
||||
- fix(cli): prevent Escape from clearing input buffer (#17083) in
|
||||
[#26339](https://github.com/google-gemini/gemini-cli/pull/26339)
|
||||
- fix(cli): undeprecate --prompt and correct positional query docs in
|
||||
[#26329](https://github.com/google-gemini/gemini-cli/pull/26329)
|
||||
- Metrics updates in
|
||||
[#26348](https://github.com/google-gemini/gemini-cli/pull/26348)
|
||||
- fix(core): remove "System: Please continue." injection on InvalidStream events
|
||||
in [#26340](https://github.com/google-gemini/gemini-cli/pull/26340)
|
||||
- docs(policy-engine): add tool argument keys reference and shell policy
|
||||
cross-links in
|
||||
[#25292](https://github.com/google-gemini/gemini-cli/pull/25292)
|
||||
- fix(cli): resolve Ghostty/raw-mode False Cancellation in oauth flow in
|
||||
[#25026](https://github.com/google-gemini/gemini-cli/pull/25026)
|
||||
- fix(core): reset session-scoped state on resumption in
|
||||
[#26342](https://github.com/google-gemini/gemini-cli/pull/26342)
|
||||
- Fix bulk of remaining issues with generalist profile in
|
||||
[#26073](https://github.com/google-gemini/gemini-cli/pull/26073)
|
||||
- fix(core): make subagents aware of active approval modes in
|
||||
[#23608](https://github.com/google-gemini/gemini-cli/pull/23608)
|
||||
- fix(acp): resolve agent mode disconnect and improve mode awareness in
|
||||
[#26332](https://github.com/google-gemini/gemini-cli/pull/26332)
|
||||
- docs(sdk): add JSDoc to exported interfaces in packages/sdk/src/types.ts in
|
||||
[#26441](https://github.com/google-gemini/gemini-cli/pull/26441)
|
||||
- perf: skip redundant GEMINI.md loading in partialConfig in
|
||||
[#26443](https://github.com/google-gemini/gemini-cli/pull/26443)
|
||||
- Enhance React guidelines in
|
||||
[#22667](https://github.com/google-gemini/gemini-cli/pull/22667)
|
||||
- feat(core): reinforce Inquiry constraints to prevent unauthorized changes in
|
||||
[#26310](https://github.com/google-gemini/gemini-cli/pull/26310)
|
||||
- revert: fix(ci): robust version checking in release verification (#26337) in
|
||||
[#26450](https://github.com/google-gemini/gemini-cli/pull/26450)
|
||||
- refactor(UI): created constants file for ThemeDialog in
|
||||
[#26446](https://github.com/google-gemini/gemini-cli/pull/26446)
|
||||
- docs: fix GitHub capitalization in releases guide in
|
||||
[#26379](https://github.com/google-gemini/gemini-cli/pull/26379)
|
||||
- fix(cli): ensure branch indicator updates in sub-directories and worktrees in
|
||||
[#26330](https://github.com/google-gemini/gemini-cli/pull/26330)
|
||||
- feat: add minimal V8 heap snapshot utility for memory diagnostics in
|
||||
[#26440](https://github.com/google-gemini/gemini-cli/pull/26440)
|
||||
- fix(hooks): preserve non-text parts in fromHookLLMRequest in
|
||||
[#26275](https://github.com/google-gemini/gemini-cli/pull/26275)
|
||||
- fix(cli): allow early stdout when config is undefined in
|
||||
[#26453](https://github.com/google-gemini/gemini-cli/pull/26453)
|
||||
- fix(cli)#21297: clear skills consent dialog before reload in
|
||||
[#26431](https://github.com/google-gemini/gemini-cli/pull/26431)
|
||||
- fix(cli): render LaTeX-style output as Unicode in the TUI in
|
||||
[#25802](https://github.com/google-gemini/gemini-cli/pull/25802)
|
||||
- fix(core): use close event instead of exit in child_process fallback in
|
||||
[#25695](https://github.com/google-gemini/gemini-cli/pull/25695)
|
||||
- feat(voice): add privacy and compliance UX warning for Gemini Live backend in
|
||||
[#26454](https://github.com/google-gemini/gemini-cli/pull/26454)
|
||||
- feat(memory): add Auto Memory inbox flow with canonical-patch contract in
|
||||
[#26338](https://github.com/google-gemini/gemini-cli/pull/26338)
|
||||
- test(cleanup): fix temporary directory leaks in test suites in
|
||||
[#26217](https://github.com/google-gemini/gemini-cli/pull/26217)
|
||||
- feat: add ignoreLocalEnv setting and --ignore-env flag (#2493) in
|
||||
[#26445](https://github.com/google-gemini/gemini-cli/pull/26445)
|
||||
- docs(sdk): add JSDoc to all exported interfaces and types in
|
||||
[#26277](https://github.com/google-gemini/gemini-cli/pull/26277)
|
||||
- feat(cli): improve /agents refresh logging in
|
||||
[#26442](https://github.com/google-gemini/gemini-cli/pull/26442)
|
||||
- Fix: make Dockerfile self-contained with multi-stage build in
|
||||
[#24277](https://github.com/google-gemini/gemini-cli/pull/24277)
|
||||
- fix(core): filter unsupported multimodal types from tool responses in
|
||||
[#26352](https://github.com/google-gemini/gemini-cli/pull/26352)
|
||||
- fix(core): properly format markdown in AskUser tool by unescaping newlines in
|
||||
[#26349](https://github.com/google-gemini/gemini-cli/pull/26349)
|
||||
- feat(bot): add actions spend metric script in
|
||||
[#26463](https://github.com/google-gemini/gemini-cli/pull/26463)
|
||||
- feat(cli): add /bug-memory command and auto-capture heap snapshot in /bug in
|
||||
[#25639](https://github.com/google-gemini/gemini-cli/pull/25639)
|
||||
- fix(cli): make SkillInboxDialog fit and scroll in alternate buffer in
|
||||
[#26455](https://github.com/google-gemini/gemini-cli/pull/26455)
|
||||
- Robust Scale-Safe Lifecycle Consolidation in
|
||||
[#26355](https://github.com/google-gemini/gemini-cli/pull/26355)
|
||||
- fix(ci): respect exempt labels when closing stale items in
|
||||
[#26475](https://github.com/google-gemini/gemini-cli/pull/26475)
|
||||
- fix(cli): use os.homedir() for home directory warning check in
|
||||
[#25890](https://github.com/google-gemini/gemini-cli/pull/25890)
|
||||
- fix(a2a-server): resolve tool approval race condition and improve status
|
||||
reporting in [#26479](https://github.com/google-gemini/gemini-cli/pull/26479)
|
||||
- fix(cli): prevent settings dialog border clipping using maxHeight in
|
||||
[#26507](https://github.com/google-gemini/gemini-cli/pull/26507)
|
||||
- feat: allow queuing messages during compression (#24071) in
|
||||
[#26506](https://github.com/google-gemini/gemini-cli/pull/26506)
|
||||
- fix(core): retry on ERR_STREAM_PREMATURE_CLOSE errors in
|
||||
[#26519](https://github.com/google-gemini/gemini-cli/pull/26519)
|
||||
- fix(core): Minor fixes for generalist profile. in
|
||||
[#26357](https://github.com/google-gemini/gemini-cli/pull/26357)
|
||||
- feat(core): steer model to use edit tool for surgical edits, fix a typo in
|
||||
[#26480](https://github.com/google-gemini/gemini-cli/pull/26480)
|
||||
- docs: clarify Auto Memory proposes memory updates and skills in
|
||||
[#26527](https://github.com/google-gemini/gemini-cli/pull/26527)
|
||||
- fix(core): reject numeric project IDs in GOOGLE_CLOUD_PROJECT (#24695) in
|
||||
[#26532](https://github.com/google-gemini/gemini-cli/pull/26532)
|
||||
- fix(core): remove unsafe type assertion suppressions in error utils in
|
||||
[#19881](https://github.com/google-gemini/gemini-cli/pull/19881)
|
||||
- fix(core): allow redirection in YOLO and AUTO_EDIT modes without sandboxing in
|
||||
[#26542](https://github.com/google-gemini/gemini-cli/pull/26542)
|
||||
- ci(release): build and attach unsigned macOS binaries to releases in
|
||||
[#26462](https://github.com/google-gemini/gemini-cli/pull/26462)
|
||||
- fix(core): Fix chat corruption bug in context manager. in
|
||||
[#26534](https://github.com/google-gemini/gemini-cli/pull/26534)
|
||||
- fix(cli): provide JSON output for AgentExecutionStopped in non-interactive
|
||||
mode in [#26504](https://github.com/google-gemini/gemini-cli/pull/26504)
|
||||
- feat(evals): add shell command safety evals in
|
||||
[#26528](https://github.com/google-gemini/gemini-cli/pull/26528)
|
||||
- fix(core): handle invalid custom plans directory gracefully in
|
||||
[#26560](https://github.com/google-gemini/gemini-cli/pull/26560)
|
||||
- fix(acp): move tool explanation from thought stream to tool call content in
|
||||
[#26554](https://github.com/google-gemini/gemini-cli/pull/26554)
|
||||
- fix(a2a-server): Resolve race condition in tool completion waiting in
|
||||
[#26568](https://github.com/google-gemini/gemini-cli/pull/26568)
|
||||
- fix(cli): randomize sandbox container names in
|
||||
[#26014](https://github.com/google-gemini/gemini-cli/pull/26014)
|
||||
- fix(core): Fix hysteresis in async context management pipelines. in
|
||||
[#26452](https://github.com/google-gemini/gemini-cli/pull/26452)
|
||||
- Tighten private Auto Memory patch allowlist in
|
||||
[#26535](https://github.com/google-gemini/gemini-cli/pull/26535)
|
||||
- fix(cli): hide read-only settings scopes in
|
||||
[#26249](https://github.com/google-gemini/gemini-cli/pull/26249)
|
||||
- fix(ci): preserve executable bit for mac binaries in
|
||||
[#26600](https://github.com/google-gemini/gemini-cli/pull/26600)
|
||||
- fix(cli): improve mcp list UX in untrusted folders in
|
||||
[#26457](https://github.com/google-gemini/gemini-cli/pull/26457)
|
||||
- fix(core): prevent silent hang during OAuth auth on headless Linux in
|
||||
[#26571](https://github.com/google-gemini/gemini-cli/pull/26571)
|
||||
- Changelog for v0.42.0-preview.0 in
|
||||
[#26537](https://github.com/google-gemini/gemini-cli/pull/26537)
|
||||
- ci: fix Argument list too long in triage workflows in
|
||||
[#26603](https://github.com/google-gemini/gemini-cli/pull/26603)
|
||||
- refactor(cli): migrate core tools to native ToolDisplay property and fix UI
|
||||
rendering in [#25186](https://github.com/google-gemini/gemini-cli/pull/25186)
|
||||
- don't wrap args unnecessarily in
|
||||
[#26599](https://github.com/google-gemini/gemini-cli/pull/26599)
|
||||
- fix(core): preserve system PATH in Git environment to fix ENOENT (#25034) in
|
||||
[#26587](https://github.com/google-gemini/gemini-cli/pull/26587)
|
||||
- fix(routing): fix resolveClassifierModel argument mismatch in
|
||||
ApprovalModeStrategy in
|
||||
[#26658](https://github.com/google-gemini/gemini-cli/pull/26658)
|
||||
- docs: add vi mode shortcuts and clarify MCP/custom sandbox setup in
|
||||
[#23853](https://github.com/google-gemini/gemini-cli/pull/23853)
|
||||
- fix(ux): fixed issue with transcribed text not showing after releasing space
|
||||
in [#26609](https://github.com/google-gemini/gemini-cli/pull/26609)
|
||||
- ci: fix json parsing in scheduled triage workflow in
|
||||
[#26656](https://github.com/google-gemini/gemini-cli/pull/26656)
|
||||
- fix(cli): hide /memory add subcommand when memoryV2 is enabled in
|
||||
[#26605](https://github.com/google-gemini/gemini-cli/pull/26605)
|
||||
- fix: prevent false command conflicts when launching from home directory in
|
||||
[#23069](https://github.com/google-gemini/gemini-cli/pull/23069)
|
||||
- fix(core): cache model routing decision in LocalAgentExecutor in
|
||||
[#26548](https://github.com/google-gemini/gemini-cli/pull/26548)
|
||||
- Changelog for v0.42.0-preview.2 in
|
||||
[#26597](https://github.com/google-gemini/gemini-cli/pull/26597)
|
||||
- skip broken test in
|
||||
[#26705](https://github.com/google-gemini/gemini-cli/pull/26705)
|
||||
- feat: export session to file and import via flag in
|
||||
[#26514](https://github.com/google-gemini/gemini-cli/pull/26514)
|
||||
- Feat: Add Machine Hostname to CLI interface in
|
||||
[#25637](https://github.com/google-gemini/gemini-cli/pull/25637)
|
||||
- docs(extensions): refactor releasing guide and add update mechanisms in
|
||||
[#26595](https://github.com/google-gemini/gemini-cli/pull/26595)
|
||||
- fix(ci): fix maintainer identification in lifecycle manager in
|
||||
[#26706](https://github.com/google-gemini/gemini-cli/pull/26706)
|
||||
- fix(ui): added quotes around session id in resume tip in
|
||||
[#26669](https://github.com/google-gemini/gemini-cli/pull/26669)
|
||||
- Changelog for v0.41.0 in
|
||||
[#26670](https://github.com/google-gemini/gemini-cli/pull/26670)
|
||||
- refactor(core): agent session protocol changes in
|
||||
[#26661](https://github.com/google-gemini/gemini-cli/pull/26661)
|
||||
- fix(context): implement loose boundary policy for gc backstop. in
|
||||
[#26594](https://github.com/google-gemini/gemini-cli/pull/26594)
|
||||
- fix(core): throw explicit error on dropped tool responses in
|
||||
[#26668](https://github.com/google-gemini/gemini-cli/pull/26668)
|
||||
- fix: resolve "function response turn must come immediately after function
|
||||
call" error in
|
||||
[#26691](https://github.com/google-gemini/gemini-cli/pull/26691)
|
||||
- fix(core): resolve parallel tool call streaming ID collision in
|
||||
[#26646](https://github.com/google-gemini/gemini-cli/pull/26646)
|
||||
- feat(core): add LocalSubagentProtocol behind AgentProtocol in
|
||||
[#25302](https://github.com/google-gemini/gemini-cli/pull/25302)
|
||||
- fix(cli): remove noisy theme registration logs from terminal in
|
||||
[#25858](https://github.com/google-gemini/gemini-cli/pull/25858)
|
||||
- ci: implement codebase-aware effort level triage in
|
||||
[#26666](https://github.com/google-gemini/gemini-cli/pull/26666)
|
||||
- feat(acp/core): prefix tool call IDs with tool names to support tool rendering
|
||||
in ACP compliant IDEs. in
|
||||
[#26676](https://github.com/google-gemini/gemini-cli/pull/26676)
|
||||
- fix(mcp): treat GET 404 as 405 in StreamableHTTPClientTransport in
|
||||
[#24847](https://github.com/google-gemini/gemini-cli/pull/24847)
|
||||
- feat(core): add RemoteSubagentProtocol behind AgentProtocol in
|
||||
[#25303](https://github.com/google-gemini/gemini-cli/pull/25303)
|
||||
- feat(context): Improvements to the snapshotter. in
|
||||
[#26655](https://github.com/google-gemini/gemini-cli/pull/26655)
|
||||
- fix(context): Change snapshotter model config. in
|
||||
[#26745](https://github.com/google-gemini/gemini-cli/pull/26745)
|
||||
- fix(cli): allow installing extensions from ssh repo in
|
||||
[#26274](https://github.com/google-gemini/gemini-cli/pull/26274)
|
||||
- fix(cli): prevent duplicate SessionStart systemMessage render in
|
||||
[#25827](https://github.com/google-gemini/gemini-cli/pull/25827)
|
||||
- fix(cli/acp): prevent infinite thought loop in ACP mode by disablig
|
||||
nextSpeakerCheck in
|
||||
[#26874](https://github.com/google-gemini/gemini-cli/pull/26874)
|
||||
- fix(cli): use static tool name in confirmation prompt to avoid parsing errors
|
||||
in [#26866](https://github.com/google-gemini/gemini-cli/pull/26866)
|
||||
- fix(routing): Refactor tool turn handling for the conversation history in
|
||||
NumericalClassifierStrategy to prevent 400 Bad Request in
|
||||
[#26761](https://github.com/google-gemini/gemini-cli/pull/26761)
|
||||
- fix(core): handle malformed projects.json in ProjectRegistry in
|
||||
[#26885](https://github.com/google-gemini/gemini-cli/pull/26885)
|
||||
- fix(ui): added a gutter width to the input prompt width calculation in
|
||||
[#26882](https://github.com/google-gemini/gemini-cli/pull/26882)
|
||||
- fix: prevent EISDIR crash when customIgnoreFilePaths contains directories
|
||||
(#19868) in [#19898](https://github.com/google-gemini/gemini-cli/pull/19898)
|
||||
- revert 6b9b778d821728427eea07b1b97ba07378137d0b in
|
||||
[#26893](https://github.com/google-gemini/gemini-cli/pull/26893)
|
||||
- Fix/vscode run current file ts in
|
||||
[#22894](https://github.com/google-gemini/gemini-cli/pull/22894)
|
||||
- Allow Enter to select session while in search mode in /resume in
|
||||
[#21523](https://github.com/google-gemini/gemini-cli/pull/21523)
|
||||
- fix(core): ignore .pak and .rpa game archive formats by default in
|
||||
[#26884](https://github.com/google-gemini/gemini-cli/pull/26884)
|
||||
- fix(cli): enable adk non-interactive session in
|
||||
[#26895](https://github.com/google-gemini/gemini-cli/pull/26895)
|
||||
- fix(cli): restore resume for legacy sessions in
|
||||
[#26577](https://github.com/google-gemini/gemini-cli/pull/26577)
|
||||
- fix: respect explicit model selection after Flash quota exhaustion (#26759) in
|
||||
[#26872](https://github.com/google-gemini/gemini-cli/pull/26872)
|
||||
- feat(context): Introduce adaptive token calculator to more accurately
|
||||
calculate content sizes. in
|
||||
[#26888](https://github.com/google-gemini/gemini-cli/pull/26888)
|
||||
- chore: update checkout action configuration in workflows in
|
||||
[#26897](https://github.com/google-gemini/gemini-cli/pull/26897)
|
||||
- fix (telemetry): inject quota_project_id to prevent fallback to default oauth
|
||||
client in [#26698](https://github.com/google-gemini/gemini-cli/pull/26698)
|
||||
- Exclude extension context from skill extraction agent in
|
||||
[#26879](https://github.com/google-gemini/gemini-cli/pull/26879)
|
||||
- Enable NumericalRouter when using dynamic model configs in
|
||||
[#26929](https://github.com/google-gemini/gemini-cli/pull/26929)
|
||||
- ci: actively triage missing priority labels and intelligently clean up
|
||||
conflicting labels in
|
||||
[#26865](https://github.com/google-gemini/gemini-cli/pull/26865)
|
||||
- refactor(core): introduce SubagentState enum for progress in
|
||||
[#26934](https://github.com/google-gemini/gemini-cli/pull/26934)
|
||||
- fix(ci): replace brittle --no-tag with explicit staging-tmp tag in
|
||||
[#26940](https://github.com/google-gemini/gemini-cli/pull/26940)
|
||||
- Incremental refactor repo agent towards skills-based composition in
|
||||
[#26717](https://github.com/google-gemini/gemini-cli/pull/26717)
|
||||
- fix(ui): fixed line wrap padding for selection lists in
|
||||
[#26944](https://github.com/google-gemini/gemini-cli/pull/26944)
|
||||
- fix(core): update read_file schema for v1 compatibility (#22183) in
|
||||
[#26922](https://github.com/google-gemini/gemini-cli/pull/26922)
|
||||
- fix(ci): configure git remote with token for authentication in
|
||||
[#26949](https://github.com/google-gemini/gemini-cli/pull/26949)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.41.0-preview.3...v0.42.0-preview.2
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.42.0-preview.2...v0.43.0-preview.0
|
||||
|
||||
@@ -882,9 +882,10 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
},
|
||||
"auto": {
|
||||
"displayName": "Auto",
|
||||
"tier": "auto",
|
||||
"isPreview": true,
|
||||
"isVisible": false,
|
||||
"isVisible": true,
|
||||
"features": {
|
||||
"thinking": true,
|
||||
"multimodalToolUse": false
|
||||
@@ -918,26 +919,16 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"displayName": "Auto (Gemini 3)",
|
||||
"tier": "auto",
|
||||
"family": "gemini-3",
|
||||
"isPreview": true,
|
||||
"isVisible": true,
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash",
|
||||
"features": {
|
||||
"thinking": true,
|
||||
"multimodalToolUse": false
|
||||
}
|
||||
"isVisible": false
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"displayName": "Auto (Gemini 2.5)",
|
||||
"tier": "auto",
|
||||
"family": "gemini-2.5",
|
||||
"isPreview": false,
|
||||
"isVisible": true,
|
||||
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
|
||||
"features": {
|
||||
"thinking": false,
|
||||
"multimodalToolUse": false
|
||||
}
|
||||
"isVisible": false
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1020,33 +1011,15 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true,
|
||||
"useCustomTools": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview-customtools"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"releaseChannel": "stable"
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
@@ -1092,9 +1065,6 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"default": "gemini-2.5-pro"
|
||||
},
|
||||
"gemini-3.1-flash-lite-preview": {
|
||||
"default": "gemini-3.1-flash-lite-preview",
|
||||
"contexts": [
|
||||
@@ -1127,6 +1097,33 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"target": "gemini-3.1-flash-lite-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-3": {
|
||||
"default": "gemini-3-pro-preview",
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true,
|
||||
"useCustomTools": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview-customtools"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"useGemini3_1": true
|
||||
},
|
||||
"target": "gemini-3.1-pro-preview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auto-gemini-2.5": {
|
||||
"default": "gemini-2.5-pro"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1145,15 +1142,15 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-flash"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-3", "gemini-3-pro-preview"]
|
||||
"requestedModels": ["gemini-2.5-pro", "auto-gemini-2.5"]
|
||||
},
|
||||
"target": "gemini-3-flash-preview"
|
||||
"target": "gemini-2.5-flash"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1162,7 +1159,20 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
"contexts": [
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["auto-gemini-2.5", "gemini-2.5-pro"]
|
||||
"hasAccessToPreview": false
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"releaseChannel": "stable",
|
||||
"requestedModels": ["auto"]
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
{
|
||||
"condition": {
|
||||
"requestedModels": ["gemini-2.5-pro", "auto-gemini-2.5"]
|
||||
},
|
||||
"target": "gemini-2.5-pro"
|
||||
},
|
||||
|
||||
Generated
+12
-41
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
@@ -449,8 +449,7 @@
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
|
||||
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)",
|
||||
"peer": true
|
||||
"license": "(Apache-2.0 AND BSD-3-Clause)"
|
||||
},
|
||||
"node_modules/@bundled-es-modules/cookie": {
|
||||
"version": "2.0.1",
|
||||
@@ -1536,7 +1535,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
|
||||
"integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
@@ -2214,7 +2212,6 @@
|
||||
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.2",
|
||||
@@ -2395,7 +2392,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
@@ -2445,7 +2441,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz",
|
||||
"integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
@@ -2820,7 +2815,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz",
|
||||
"integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
@@ -2854,7 +2848,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.5.0.tgz",
|
||||
"integrity": "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0"
|
||||
@@ -2909,7 +2902,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz",
|
||||
"integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0",
|
||||
@@ -4147,7 +4139,6 @@
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -4421,7 +4412,6 @@
|
||||
"integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.58.2",
|
||||
"@typescript-eslint/types": "8.58.2",
|
||||
@@ -5197,7 +5187,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7315,8 +7304,7 @@
|
||||
"version": "0.0.1581282",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz",
|
||||
"integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/dezalgo": {
|
||||
"version": "1.0.4",
|
||||
@@ -7901,7 +7889,6 @@
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -8512,7 +8499,6 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -9779,7 +9765,6 @@
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz",
|
||||
"integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
@@ -10039,7 +10024,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.6.9.tgz",
|
||||
"integrity": "sha512-RL9sSiLQZECnjbmBwjIHOp8yVGdWF7C/uifg7ISv/e+F3nLNsfl7FdUFQs8iZARFMJAYxMFpxW6OW+HSt9drwQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^7.0.0",
|
||||
"ansi-styles": "^6.2.3",
|
||||
@@ -13815,7 +13799,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -13826,7 +13809,6 @@
|
||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.6.1",
|
||||
"ws": "^7"
|
||||
@@ -15979,7 +15961,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16202,8 +16183,7 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.3",
|
||||
@@ -16211,7 +16191,6 @@
|
||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
@@ -16377,7 +16356,6 @@
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16445,7 +16423,6 @@
|
||||
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.35.0",
|
||||
"@typescript-eslint/types": "8.35.0",
|
||||
@@ -16865,7 +16842,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz",
|
||||
"integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.27.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -17436,7 +17412,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -17449,7 +17424,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/expect": "3.2.4",
|
||||
@@ -18088,7 +18062,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@@ -18104,7 +18077,7 @@
|
||||
},
|
||||
"packages/a2a-server": {
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
"@google-cloud/storage": "^7.16.0",
|
||||
@@ -18233,7 +18206,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
@@ -18381,7 +18354,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@a2a-js/sdk": "0.3.11",
|
||||
@@ -18525,7 +18498,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
|
||||
"integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
@@ -18644,7 +18616,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
|
||||
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -18694,7 +18665,7 @@
|
||||
},
|
||||
"packages/devtools": {
|
||||
"name": "@google/gemini-cli-devtools",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"ws": "^8.16.0"
|
||||
@@ -18709,7 +18680,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@google/gemini-cli-sdk",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18740,7 +18711,7 @@
|
||||
},
|
||||
"packages/test-utils": {
|
||||
"name": "@google/gemini-cli-test-utils",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@google/gemini-cli-core": "file:../core",
|
||||
@@ -18772,7 +18743,7 @@
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"license": "LICENSE",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
@@ -14,7 +14,7 @@
|
||||
"url": "git+https://github.com/google-gemini/gemini-cli.git"
|
||||
},
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260428.g59b2dea0e"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.44.0-nightly.20260512.g022e8baef"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=development node scripts/start.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-a2a-server",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"description": "Gemini CLI A2A Server",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"description": "Gemini CLI",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"dist"
|
||||
],
|
||||
"config": {
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.42.0-nightly.20260428.g59b2dea0e"
|
||||
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.44.0-nightly.20260512.g022e8baef"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.16.1",
|
||||
|
||||
@@ -60,8 +60,11 @@ export class AcpFileSystemService implements FileSystemService {
|
||||
sessionId: this.sessionId,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return response.content;
|
||||
const content: unknown = response.content;
|
||||
if (typeof content !== 'string') {
|
||||
throw new Error('content must be a string'); // replace with other response type formats when modified in the future
|
||||
}
|
||||
return content;
|
||||
} catch (err: unknown) {
|
||||
this.normalizeFileSystemError(err);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import type * as acp from '@agentclientprotocol/sdk';
|
||||
import {
|
||||
AuthType,
|
||||
type Config,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
type MessageBus,
|
||||
type Storage,
|
||||
} from '@google/gemini-cli-core';
|
||||
@@ -208,7 +209,7 @@ describe('AcpSessionManager', () => {
|
||||
expect(response.models?.availableModels).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
modelId: 'auto-gemini-3',
|
||||
modelId: GEMINI_MODEL_ALIAS_AUTO,
|
||||
name: expect.stringContaining('Auto'),
|
||||
}),
|
||||
]),
|
||||
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
type ToolCallConfirmationDetails,
|
||||
Kind,
|
||||
ApprovalMode,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
@@ -23,6 +22,8 @@ import {
|
||||
getDisplayString,
|
||||
AuthType,
|
||||
ToolConfirmationOutcome,
|
||||
getChannelFromVersion,
|
||||
getAutoModelDescription,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type * as acp from '@agentclientprotocol/sdk';
|
||||
import { z } from 'zod';
|
||||
@@ -262,7 +263,7 @@ export function buildAvailableModels(
|
||||
}>;
|
||||
currentModelId: string;
|
||||
} {
|
||||
const preferredModel = config.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
|
||||
const preferredModel = config.getModel() || GEMINI_MODEL_ALIAS_AUTO;
|
||||
const shouldShowPreviewModels = config.getHasAccessToPreviewModel();
|
||||
const useGemini31 = config.getGemini31LaunchedSync?.() ?? false;
|
||||
const useGemini31FlashLite =
|
||||
@@ -271,6 +272,8 @@ export function buildAvailableModels(
|
||||
const useCustomToolModel =
|
||||
useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
|
||||
|
||||
const releaseChannel = getChannelFromVersion(config.clientVersion);
|
||||
|
||||
// --- DYNAMIC PATH ---
|
||||
if (
|
||||
config.getExperimentalDynamicModelConfiguration?.() === true &&
|
||||
@@ -281,6 +284,7 @@ export function buildAvailableModels(
|
||||
useGemini3_1FlashLite: useGemini31FlashLite,
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
releaseChannel,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -292,23 +296,12 @@ export function buildAvailableModels(
|
||||
// --- LEGACY PATH ---
|
||||
const mainOptions = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL_AUTO),
|
||||
description:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||
value: GEMINI_MODEL_ALIAS_AUTO,
|
||||
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO),
|
||||
description: getAutoModelDescription(releaseChannel, useGemini31),
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldShowPreviewModels) {
|
||||
mainOptions.unshift({
|
||||
value: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(PREVIEW_GEMINI_MODEL_AUTO),
|
||||
description: useGemini31
|
||||
? 'Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash'
|
||||
: 'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash',
|
||||
});
|
||||
}
|
||||
|
||||
const manualOptions = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL,
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface ConfigLogger {
|
||||
|
||||
export type RequestSettingCallback = (
|
||||
setting: ExtensionSetting,
|
||||
) => Promise<string>;
|
||||
) => Promise<string | undefined>;
|
||||
export type RequestConfirmationCallback = (message: string) => Promise<boolean>;
|
||||
|
||||
const defaultLogger: ConfigLogger = {
|
||||
@@ -47,8 +47,7 @@ const defaultRequestConfirmation: RequestConfirmationCallback = async (
|
||||
message,
|
||||
initial: false,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return response.confirm;
|
||||
return typeof response.confirm === 'boolean' ? response.confirm : false;
|
||||
};
|
||||
|
||||
export async function getExtensionManager() {
|
||||
|
||||
@@ -2058,7 +2058,7 @@ describe('loadCliConfig model selection', () => {
|
||||
argv,
|
||||
);
|
||||
|
||||
expect(config.getModel()).toBe('auto-gemini-3');
|
||||
expect(config.getModel()).toBe('auto');
|
||||
});
|
||||
|
||||
it('always prefers model from argv', async () => {
|
||||
@@ -2102,7 +2102,7 @@ describe('loadCliConfig model selection', () => {
|
||||
argv,
|
||||
);
|
||||
|
||||
expect(config.getModel()).toBe('auto-gemini-3');
|
||||
expect(config.getModel()).toBe('auto');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
loadServerHierarchicalMemory,
|
||||
ASK_USER_TOOL_NAME,
|
||||
getVersion,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
type HierarchicalMemory,
|
||||
coreEvents,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
@@ -866,7 +865,7 @@ export async function loadCliConfig(
|
||||
interactive,
|
||||
);
|
||||
|
||||
const defaultModel = PREVIEW_GEMINI_MODEL_AUTO;
|
||||
const defaultModel = GEMINI_MODEL_ALIAS_AUTO;
|
||||
const rawModel =
|
||||
argv.model || process.env['GEMINI_MODEL'] || settings.model?.name;
|
||||
|
||||
|
||||
@@ -88,7 +88,9 @@ interface ExtensionManagerParams {
|
||||
enabledExtensionOverrides?: string[];
|
||||
settings: MergedSettings;
|
||||
requestConsent: (consent: string) => Promise<boolean>;
|
||||
requestSetting: ((setting: ExtensionSetting) => Promise<string>) | null;
|
||||
requestSetting:
|
||||
| ((setting: ExtensionSetting) => Promise<string | undefined>)
|
||||
| null;
|
||||
workspaceDir: string;
|
||||
eventEmitter?: EventEmitter<ExtensionEvents>;
|
||||
clientVersion?: string;
|
||||
@@ -106,7 +108,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
private settings: MergedSettings;
|
||||
private requestConsent: (consent: string) => Promise<boolean>;
|
||||
private requestSetting:
|
||||
| ((setting: ExtensionSetting) => Promise<string>)
|
||||
| ((setting: ExtensionSetting) => Promise<string | undefined>)
|
||||
| undefined;
|
||||
private telemetryConfig: Config;
|
||||
private workspaceDir: string;
|
||||
@@ -161,7 +163,7 @@ export class ExtensionManager extends ExtensionLoader {
|
||||
}
|
||||
|
||||
setRequestSetting(
|
||||
requestSetting?: (setting: ExtensionSetting) => Promise<string>,
|
||||
requestSetting?: (setting: ExtensionSetting) => Promise<string | undefined>,
|
||||
): void {
|
||||
this.requestSetting = requestSetting;
|
||||
}
|
||||
|
||||
@@ -94,9 +94,8 @@ export class ExtensionRegistryClient {
|
||||
fuzzy: true,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const results = await fzf.find(query);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return results.map((r: { item: RegistryExtension }) => r.item);
|
||||
const results: Array<{ item: RegistryExtension }> = await fzf.find(query);
|
||||
return results.map((r) => r.item);
|
||||
}
|
||||
|
||||
async getExtension(id: string): Promise<RegistryExtension | undefined> {
|
||||
|
||||
@@ -8,6 +8,7 @@ import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { coreEvents, type GeminiCLIExtension } from '@google/gemini-cli-core';
|
||||
import { ExtensionStorage } from './storage.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
export interface ExtensionEnablementConfig {
|
||||
overrides: string[];
|
||||
@@ -179,8 +180,12 @@ export class ExtensionEnablementManager {
|
||||
readConfig(): AllExtensionsEnablementConfig {
|
||||
try {
|
||||
const content = fs.readFileSync(this.configFilePath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return JSON.parse(content);
|
||||
const parsed: unknown = JSON.parse(content);
|
||||
const schema = z.record(
|
||||
z.string(),
|
||||
z.object({ overrides: z.array(z.string()) }),
|
||||
);
|
||||
return schema.parse(parsed);
|
||||
} catch (error) {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
|
||||
@@ -62,7 +62,7 @@ export const getEnvFilePath = (
|
||||
export async function maybePromptForSettings(
|
||||
extensionConfig: ExtensionConfig,
|
||||
extensionId: string,
|
||||
requestSetting: (setting: ExtensionSetting) => Promise<string>,
|
||||
requestSetting: (setting: ExtensionSetting) => Promise<string | undefined>,
|
||||
previousExtensionConfig?: ExtensionConfig,
|
||||
previousSettings?: Record<string, string>,
|
||||
): Promise<void> {
|
||||
@@ -106,7 +106,9 @@ export async function maybePromptForSettings(
|
||||
settingsChanges.promptForEnv,
|
||||
)) {
|
||||
const answer = await requestSetting(setting);
|
||||
allSettings[setting.envVar] = answer;
|
||||
if (answer !== undefined) {
|
||||
allSettings[setting.envVar] = answer;
|
||||
}
|
||||
}
|
||||
|
||||
const nonSensitiveSettings: Record<string, string> = {};
|
||||
@@ -159,14 +161,13 @@ function formatEnvContent(settings: Record<string, string>): string {
|
||||
|
||||
export async function promptForSetting(
|
||||
setting: ExtensionSetting,
|
||||
): Promise<string> {
|
||||
): Promise<string | undefined> {
|
||||
const response = await prompts({
|
||||
type: setting.sensitive ? 'password' : 'text',
|
||||
name: 'value',
|
||||
message: `${setting.name}\n${setting.description}`,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return response.value;
|
||||
return typeof response.value === 'string' ? response.value : undefined;
|
||||
}
|
||||
|
||||
export async function getScopedEnvContents(
|
||||
@@ -230,7 +231,7 @@ export async function updateSetting(
|
||||
extensionConfig: ExtensionConfig,
|
||||
extensionId: string,
|
||||
settingKey: string,
|
||||
requestSetting: (setting: ExtensionSetting) => Promise<string>,
|
||||
requestSetting: (setting: ExtensionSetting) => Promise<string | undefined>,
|
||||
scope: ExtensionSettingScope,
|
||||
workspaceDir: string,
|
||||
): Promise<void> {
|
||||
@@ -250,6 +251,10 @@ export async function updateSetting(
|
||||
}
|
||||
|
||||
const newValue = await requestSetting(settingToUpdate);
|
||||
if (newValue === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const keychain = new KeychainTokenStorage(
|
||||
getKeychainStorageName(extensionName, extensionId, scope, workspaceDir),
|
||||
);
|
||||
|
||||
@@ -67,8 +67,7 @@ export function recursivelyHydrateStrings<T>(
|
||||
}
|
||||
if (Array.isArray(obj)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return obj.map((item) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return (obj as unknown[]).map((item) =>
|
||||
recursivelyHydrateStrings(item, values),
|
||||
) as unknown as T;
|
||||
}
|
||||
|
||||
@@ -2156,53 +2156,6 @@ const SETTINGS_SCHEMA = {
|
||||
},
|
||||
},
|
||||
},
|
||||
contextCaching: {
|
||||
type: 'object',
|
||||
label: 'Context Caching',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: {},
|
||||
description: 'Explicit context caching for the main agent.',
|
||||
showInDialog: true,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Context Caching',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
description: 'Enable explicit context caching for the main agent.',
|
||||
showInDialog: true,
|
||||
},
|
||||
thresholdTokens: {
|
||||
type: 'number',
|
||||
label: 'Threshold Tokens',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: 32768,
|
||||
description: 'Minimum tokens required to trigger explicit caching.',
|
||||
showInDialog: true,
|
||||
},
|
||||
ttlMinutes: {
|
||||
type: 'number',
|
||||
label: 'TTL (Minutes)',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: 60,
|
||||
description: 'Time to live for a cache resource in minutes.',
|
||||
showInDialog: true,
|
||||
},
|
||||
autoRenew: {
|
||||
type: 'boolean',
|
||||
label: 'Auto Renew',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: true,
|
||||
description: 'Automatically extend TTL on use.',
|
||||
showInDialog: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
adk: {
|
||||
type: 'object',
|
||||
label: 'ADK',
|
||||
@@ -3518,7 +3471,11 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
|
||||
family: { type: 'string' },
|
||||
isPreview: { type: 'boolean' },
|
||||
isVisible: { type: 'boolean' },
|
||||
dialogDescription: { type: 'string' },
|
||||
dialogDescription: {
|
||||
type: 'string',
|
||||
description:
|
||||
"A description of the model to display in the model selection dialog. For the 'auto' alias, this value is dynamically generated and any value provided here will be ignored.",
|
||||
},
|
||||
features: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
||||
@@ -1189,6 +1189,39 @@ describe('resolveSessionId', () => {
|
||||
expect(sessionId).toBe('new-id');
|
||||
expect(resumedSessionData).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should exit with FATAL_INPUT_ERROR when explicit resume session is missing', async () => {
|
||||
vi.mocked(SessionSelector).mockImplementation(
|
||||
() =>
|
||||
({
|
||||
resolveSession: vi
|
||||
.fn()
|
||||
.mockRejectedValue(SessionError.noSessionsFound()),
|
||||
}) as unknown as InstanceType<typeof SessionSelector>,
|
||||
);
|
||||
|
||||
const emitFeedbackSpy = vi.spyOn(coreEvents, 'emitFeedback');
|
||||
const processExitSpy = vi
|
||||
.spyOn(process, 'exit')
|
||||
.mockImplementation((code) => {
|
||||
throw new MockProcessExitError(code);
|
||||
});
|
||||
|
||||
try {
|
||||
await resolveSessionId('explicit-session-id');
|
||||
} catch (e) {
|
||||
if (!(e instanceof MockProcessExitError)) throw e;
|
||||
}
|
||||
|
||||
expect(emitFeedbackSpy).toHaveBeenCalledWith(
|
||||
'error',
|
||||
expect.stringContaining('Error resuming session:'),
|
||||
);
|
||||
expect(processExitSpy).toHaveBeenCalledWith(ExitCodes.FATAL_INPUT_ERROR);
|
||||
|
||||
emitFeedbackSpy.mockRestore();
|
||||
processExitSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('gemini.tsx main function exit codes', () => {
|
||||
|
||||
@@ -85,7 +85,11 @@ import { validateAuthMethod } from './config/auth.js';
|
||||
import { runAcpClient } from './acp/acpStdioTransport.js';
|
||||
import { validateNonInteractiveAuth } from './validateNonInterActiveAuth.js';
|
||||
import { appEvents, AppEvent } from './utils/events.js';
|
||||
import { SessionError, SessionSelector } from './utils/sessionUtils.js';
|
||||
import {
|
||||
RESUME_LATEST,
|
||||
SessionError,
|
||||
SessionSelector,
|
||||
} from './utils/sessionUtils.js';
|
||||
|
||||
import { relaunchOnExitCode } from './utils/relaunch.js';
|
||||
import { loadSandboxConfig } from './config/sandboxConfig.js';
|
||||
@@ -309,8 +313,10 @@ export async function resolveSessionId(
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof SessionError && error.code === 'NO_SESSIONS_FOUND') {
|
||||
coreEvents.emitFeedback('warning', error.message);
|
||||
return { sessionId: createSessionId() };
|
||||
if (resumeArg === RESUME_LATEST) {
|
||||
coreEvents.emitFeedback('warning', error.message);
|
||||
return { sessionId: createSessionId() };
|
||||
}
|
||||
}
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
|
||||
@@ -68,6 +68,9 @@ export async function runNonInteractive(
|
||||
): Promise<void> {
|
||||
const useAgentSession = params.config.getAgentSessionNoninteractiveEnabled();
|
||||
if (useAgentSession) {
|
||||
debugLogger.debug(
|
||||
'[ADK] Running non-interactive mode with ADK agent session',
|
||||
);
|
||||
return runNonInteractiveAgentSession(params);
|
||||
}
|
||||
|
||||
|
||||
@@ -112,5 +112,11 @@ export const createMockCommandContext = (
|
||||
return output;
|
||||
};
|
||||
|
||||
return merge(defaultMocks, overrides);
|
||||
const merged: unknown = merge(defaultMocks, overrides);
|
||||
const isCommandContext = (val: unknown): val is CommandContext =>
|
||||
typeof val === 'object' && val !== null;
|
||||
if (isCommandContext(merged)) {
|
||||
return merged;
|
||||
}
|
||||
throw new Error('Unreachable');
|
||||
};
|
||||
|
||||
@@ -2127,6 +2127,68 @@ describe('AppContainer State Management', () => {
|
||||
});
|
||||
|
||||
describe('CTRL+C', () => {
|
||||
it('should not trigger quit flow when input buffer has text', async () => {
|
||||
const setText = vi.fn();
|
||||
mockedUseTextBuffer.mockReturnValue({
|
||||
text: 'hello world',
|
||||
setText,
|
||||
lines: ['hello world'],
|
||||
cursor: [0, 11],
|
||||
handleInput: vi.fn().mockReturnValue(false),
|
||||
});
|
||||
await setupKeypressTest();
|
||||
|
||||
pressKey('\x03'); // Ctrl+C
|
||||
|
||||
expect(mockCancelOngoingRequest).not.toHaveBeenCalled();
|
||||
expect(mockHandleSlashCommand).not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should clear buffer and reset quit count when Ctrl+C is pressed with text', async () => {
|
||||
const setText = vi.fn();
|
||||
const bufferWithText = {
|
||||
text: 'hello world',
|
||||
setText,
|
||||
lines: ['hello world'],
|
||||
cursor: [0, 11],
|
||||
handleInput: vi.fn().mockReturnValue(false),
|
||||
};
|
||||
const bufferEmpty = {
|
||||
text: '',
|
||||
setText,
|
||||
lines: [''],
|
||||
cursor: [0, 0],
|
||||
handleInput: vi.fn().mockReturnValue(false),
|
||||
};
|
||||
|
||||
// First press with text
|
||||
mockedUseTextBuffer.mockReturnValue(bufferWithText);
|
||||
await setupKeypressTest();
|
||||
pressKey('\x03'); // Ctrl+C
|
||||
|
||||
expect(mockCancelOngoingRequest).not.toHaveBeenCalled();
|
||||
expect(mockHandleSlashCommand).not.toHaveBeenCalled();
|
||||
|
||||
// Second press with empty buffer (simulating clear happened)
|
||||
// Since we reset the count, this should be treated as the "first" press for quit flow
|
||||
mockedUseTextBuffer.mockReturnValue(bufferEmpty);
|
||||
pressKey('\x03'); // Ctrl+C
|
||||
expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(1);
|
||||
expect(mockHandleSlashCommand).not.toHaveBeenCalled();
|
||||
|
||||
// Third press with empty buffer should finally quit
|
||||
pressKey('\x03'); // Ctrl+C
|
||||
expect(mockCancelOngoingRequest).toHaveBeenCalledTimes(2);
|
||||
expect(mockHandleSlashCommand).toHaveBeenCalledWith(
|
||||
'/quit',
|
||||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
);
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('should cancel ongoing request on first press', async () => {
|
||||
mockedUseGeminiStream.mockReturnValue({
|
||||
...DEFAULT_GEMINI_STREAM_MOCK,
|
||||
|
||||
@@ -1649,11 +1649,14 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
[config, handleSlashCommand],
|
||||
);
|
||||
|
||||
const { pressCount: ctrlCPressCount, handlePress: handleCtrlCPress } =
|
||||
useRepeatedKeyPress({
|
||||
windowMs: WARNING_PROMPT_DURATION_MS,
|
||||
onRepeat: handleExitRepeat,
|
||||
});
|
||||
const {
|
||||
pressCount: ctrlCPressCount,
|
||||
handlePress: handleCtrlCPress,
|
||||
resetCount: resetCtrlCPress,
|
||||
} = useRepeatedKeyPress({
|
||||
windowMs: WARNING_PROMPT_DURATION_MS,
|
||||
onRepeat: handleExitRepeat,
|
||||
});
|
||||
|
||||
const { pressCount: ctrlDPressCount, handlePress: handleCtrlDPress } =
|
||||
useRepeatedKeyPress({
|
||||
@@ -1830,6 +1833,28 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
keyMatchers[Command.QUIT](key) &&
|
||||
isInputActive &&
|
||||
streamingState === StreamingState.Idle &&
|
||||
bufferRef.current.text.length > 0
|
||||
) {
|
||||
// Prevent quit flow when there is text.
|
||||
resetCtrlCPress();
|
||||
|
||||
if (keyMatchers[Command.CLEAR_INPUT](key)) {
|
||||
// Let InputPrompt handle it as edit.clear.
|
||||
// This preserves documented behavior and allows prompt-level state
|
||||
// (completions/history) to reset correctly.
|
||||
return false;
|
||||
}
|
||||
|
||||
// If QUIT is not also CLEAR_INPUT, we should still clear the buffer
|
||||
// to match user expectations for Ctrl+C (or equivalent).
|
||||
bufferRef.current.setText('');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyMatchers[Command.QUIT](key)) {
|
||||
// If the user presses Ctrl+C, we want to cancel any ongoing requests.
|
||||
// This should happen regardless of the count.
|
||||
@@ -2062,6 +2087,9 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
startRecording,
|
||||
stopRecording,
|
||||
mouseMode,
|
||||
isInputActive,
|
||||
streamingState,
|
||||
resetCtrlCPress,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -9,11 +9,7 @@ import open from 'open';
|
||||
import path from 'node:path';
|
||||
import { bugCommand } from './bugCommand.js';
|
||||
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
|
||||
import {
|
||||
getVersion,
|
||||
type Config,
|
||||
type ConversationRecord,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { getVersion, type Config } from '@google/gemini-cli-core';
|
||||
import { GIT_COMMIT_INFO } from '../../generated/git-commit.js';
|
||||
import { formatBytes } from '../utils/formatters.js';
|
||||
import { MessageType } from '../types.js';
|
||||
@@ -161,8 +157,6 @@ describe('bugCommand', () => {
|
||||
{ role: 'user', parts: [{ text: 'hello' }] },
|
||||
{ role: 'model', parts: [{ text: 'hi' }] },
|
||||
];
|
||||
const mockGetSubagentTrajectories = vi.fn().mockResolvedValue({});
|
||||
const mockGetConversation = vi.fn().mockReturnValue({ messages: [] });
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: {
|
||||
@@ -179,8 +173,6 @@ describe('bugCommand', () => {
|
||||
geminiClient: {
|
||||
getChat: () => ({
|
||||
getHistory: () => history,
|
||||
getSubagentTrajectories: mockGetSubagentTrajectories,
|
||||
getConversation: mockGetConversation,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -196,9 +188,7 @@ describe('bugCommand', () => {
|
||||
);
|
||||
expect(exportHistoryToFile).toHaveBeenCalledWith({
|
||||
history,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
|
||||
const addItemCall = vi.mocked(mockContext.ui.addItem).mock.calls[0];
|
||||
@@ -213,60 +203,6 @@ describe('bugCommand', () => {
|
||||
expect(messageText).toContain(encodeURIComponent(reminder));
|
||||
});
|
||||
|
||||
it('should include subagent trajectories in history export if available', async () => {
|
||||
const history = [
|
||||
{ role: 'user', parts: [{ text: 'hello' }] },
|
||||
{ role: 'model', parts: [{ text: 'hi' }] },
|
||||
];
|
||||
const trajectories = {
|
||||
'subagent-1': {
|
||||
sessionId: 'subagent-1',
|
||||
messages: [],
|
||||
} as unknown as ConversationRecord,
|
||||
};
|
||||
const mockGetSubagentTrajectories = vi.fn().mockResolvedValue(trajectories);
|
||||
const mockGetConversation = vi.fn().mockReturnValue({ messages: [] });
|
||||
|
||||
const mockContext = createMockCommandContext({
|
||||
services: {
|
||||
agentContext: {
|
||||
config: {
|
||||
getModel: () => 'gemini-pro',
|
||||
getBugCommand: () => undefined,
|
||||
getIdeMode: () => true,
|
||||
getContentGeneratorConfig: () => ({ authType: 'vertex-ai' }),
|
||||
storage: {
|
||||
getProjectTempDir: () => '/tmp/gemini',
|
||||
},
|
||||
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
||||
} as unknown as Config,
|
||||
geminiClient: {
|
||||
getChat: () => ({
|
||||
getHistory: () => history,
|
||||
getSubagentTrajectories: mockGetSubagentTrajectories,
|
||||
getConversation: mockGetConversation,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!bugCommand.action) throw new Error('Action is not defined');
|
||||
await bugCommand.action(mockContext, 'Bug with trajectories');
|
||||
|
||||
const expectedPath = path.join(
|
||||
'/tmp/gemini',
|
||||
'bug-report-history-1704067200000.json',
|
||||
);
|
||||
expect(mockGetSubagentTrajectories).toHaveBeenCalled();
|
||||
expect(exportHistoryToFile).toHaveBeenCalledWith({
|
||||
history,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories,
|
||||
});
|
||||
});
|
||||
|
||||
it('should use a custom URL template from config if provided', async () => {
|
||||
const customTemplate =
|
||||
'https://internal.bug-tracker.com/new?desc={title}&details={info}';
|
||||
|
||||
@@ -88,14 +88,7 @@ export const bugCommand: SlashCommand = {
|
||||
const historyFileName = `bug-report-history-${Date.now()}.json`;
|
||||
const historyFilePath = path.join(tempDir, historyFileName);
|
||||
try {
|
||||
const trajectories = await chat?.getSubagentTrajectories();
|
||||
const messages = chat?.getConversation()?.messages;
|
||||
await exportHistoryToFile({
|
||||
history,
|
||||
messages,
|
||||
filePath: historyFilePath,
|
||||
trajectories,
|
||||
});
|
||||
await exportHistoryToFile({ history, filePath: historyFilePath });
|
||||
historyFileMessage = `\n\n--------------------------------------------------------------------------------\n\n📄 **Chat History Exported**\nTo help us debug, we've exported your current chat history to:\n${historyFilePath}\n\nPlease consider attaching this file to your GitHub issue if you feel comfortable doing so.\n\n**Privacy Disclaimer:** Please do not upload any logs containing sensitive or private information that you are not comfortable sharing publicly.`;
|
||||
problemValue += `\n\n[ACTION REQUIRED] 📎 PLEASE ATTACH THE EXPORTED CHAT HISTORY JSON FILE TO THIS ISSUE IF YOU FEEL COMFORTABLE SHARING IT.`;
|
||||
} catch (err) {
|
||||
|
||||
@@ -63,8 +63,6 @@ describe('chatCommand', () => {
|
||||
mockGetHistory = vi.fn().mockReturnValue([]);
|
||||
mockGetChat = vi.fn().mockReturnValue({
|
||||
getHistory: mockGetHistory,
|
||||
getSubagentTrajectories: vi.fn().mockResolvedValue({}),
|
||||
getConversation: vi.fn().mockReturnValue({ messages: [] }),
|
||||
});
|
||||
mockSaveCheckpoint = vi.fn().mockResolvedValue(undefined);
|
||||
mockLoadCheckpoint = vi.fn().mockResolvedValue({ history: [] });
|
||||
@@ -232,12 +230,7 @@ describe('chatCommand', () => {
|
||||
|
||||
expect(mockCheckpointExists).not.toHaveBeenCalled(); // Should skip existence check
|
||||
expect(mockSaveCheckpoint).toHaveBeenCalledWith(
|
||||
{
|
||||
history,
|
||||
authType: AuthType.LOGIN_WITH_GOOGLE,
|
||||
trajectories: {},
|
||||
messages: [],
|
||||
},
|
||||
{ history, authType: AuthType.LOGIN_WITH_GOOGLE },
|
||||
tag,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
@@ -471,9 +464,7 @@ describe('chatCommand', () => {
|
||||
);
|
||||
expect(mockExport).toHaveBeenCalledWith({
|
||||
history: mockHistory,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
@@ -488,9 +479,7 @@ describe('chatCommand', () => {
|
||||
const expectedPath = path.join(process.cwd(), 'my-chat.json');
|
||||
expect(mockExport).toHaveBeenCalledWith({
|
||||
history: mockHistory,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
@@ -505,9 +494,7 @@ describe('chatCommand', () => {
|
||||
const expectedPath = path.join(process.cwd(), 'my-chat.md');
|
||||
expect(mockExport).toHaveBeenCalledWith({
|
||||
history: mockHistory,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
@@ -557,9 +544,7 @@ describe('chatCommand', () => {
|
||||
const expectedPath = path.join(process.cwd(), 'my-chat.json');
|
||||
expect(mockExport).toHaveBeenCalledWith({
|
||||
history: mockHistory,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -569,9 +554,7 @@ describe('chatCommand', () => {
|
||||
const expectedPath = path.join(process.cwd(), 'my-chat.md');
|
||||
expect(mockExport).toHaveBeenCalledWith({
|
||||
history: mockHistory,
|
||||
messages: [],
|
||||
filePath: expectedPath,
|
||||
trajectories: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -139,12 +139,7 @@ const saveCommand: SlashCommand = {
|
||||
const history = chat.getHistory();
|
||||
if (history.length > INITIAL_HISTORY_LENGTH) {
|
||||
const authType = config?.getContentGeneratorConfig()?.authType;
|
||||
const trajectories = await chat.getSubagentTrajectories();
|
||||
const messages = chat.getConversation()?.messages;
|
||||
await logger.saveCheckpoint(
|
||||
{ history, authType, trajectories, messages },
|
||||
tag,
|
||||
);
|
||||
await logger.saveCheckpoint({ history, authType }, tag);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
@@ -329,9 +324,7 @@ const shareCommand: SlashCommand = {
|
||||
}
|
||||
|
||||
try {
|
||||
const trajectories = await chat.getSubagentTrajectories();
|
||||
const messages = chat.getConversation()?.messages;
|
||||
await exportHistoryToFile({ history, filePath, trajectories, messages });
|
||||
await exportHistoryToFile({ history, filePath });
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
|
||||
@@ -1581,4 +1581,71 @@ describe('AskUserDialog', () => {
|
||||
expect(frame).toContain('1. Option 1');
|
||||
});
|
||||
});
|
||||
|
||||
it('indents multi-line descriptions correctly', async () => {
|
||||
const questions: Question[] = [
|
||||
{
|
||||
question: 'Single choice?',
|
||||
header: 'Indent Test',
|
||||
type: QuestionType.CHOICE,
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
description:
|
||||
'This is a very long description that is expected to wrap onto multiple lines in a narrow terminal. We want to ensure that all lines are correctly indented.',
|
||||
},
|
||||
],
|
||||
multiSelect: false,
|
||||
},
|
||||
];
|
||||
|
||||
const { lastFrame, waitUntilReady } = await renderWithProviders(
|
||||
<AskUserDialog
|
||||
questions={questions}
|
||||
onSubmit={vi.fn()}
|
||||
onCancel={vi.fn()}
|
||||
width={40} // Narrow width to force wrapping
|
||||
/>,
|
||||
{ width: 40 },
|
||||
);
|
||||
|
||||
await waitFor(async () => {
|
||||
await waitUntilReady();
|
||||
// Snapshot will capture the visual alignment
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('indents multi-line descriptions correctly in multi-select mode', async () => {
|
||||
const questions: Question[] = [
|
||||
{
|
||||
question: 'Multi-select?',
|
||||
header: 'Indent Test',
|
||||
type: QuestionType.CHOICE,
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
description:
|
||||
'This is a very long description that is expected to wrap onto multiple lines in a narrow terminal. We want to ensure that all lines are correctly indented even with checkboxes.',
|
||||
},
|
||||
],
|
||||
multiSelect: true,
|
||||
},
|
||||
];
|
||||
|
||||
const { lastFrame, waitUntilReady } = await renderWithProviders(
|
||||
<AskUserDialog
|
||||
questions={questions}
|
||||
onSubmit={vi.fn()}
|
||||
onCancel={vi.fn()}
|
||||
width={40} // Narrow width to force wrapping
|
||||
/>,
|
||||
{ width: 40 },
|
||||
);
|
||||
|
||||
await waitFor(async () => {
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1004,13 +1004,15 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
||||
)}
|
||||
</Box>
|
||||
{optionItem.description && (
|
||||
<Text color={theme.text.secondary} wrap="wrap">
|
||||
{' '}
|
||||
<RenderInline
|
||||
text={optionItem.description}
|
||||
defaultColor={theme.text.secondary}
|
||||
/>
|
||||
</Text>
|
||||
// Padding aligns with option label: 4 for multi-select (checkbox + space), 1 for single-select
|
||||
<Box paddingLeft={showCheck ? 4 : 1}>
|
||||
<Text color={theme.text.secondary} wrap="wrap">
|
||||
<RenderInline
|
||||
text={optionItem.description}
|
||||
defaultColor={theme.text.secondary}
|
||||
/>
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -5332,6 +5332,34 @@ describe('InputPrompt', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('terminal buffer rendering', () => {
|
||||
it('does not clip the last char of a visual line whose width equals inputWidth', async () => {
|
||||
const fullLine = '1234567890'; // 10 chars, exactly props.inputWidth
|
||||
props.inputWidth = 10;
|
||||
props.suggestionsWidth = 10;
|
||||
vi.spyOn(props.config, 'getUseTerminalBuffer').mockReturnValue(true);
|
||||
mockBuffer.text = fullLine;
|
||||
mockBuffer.lines = [fullLine];
|
||||
mockBuffer.allVisualLines = [fullLine];
|
||||
mockBuffer.viewportVisualLines = [fullLine];
|
||||
mockBuffer.visualToLogicalMap = [[0, 0]];
|
||||
mockBuffer.visualToTransformedMap = [0];
|
||||
mockBuffer.transformationsByLine = [[]];
|
||||
mockBuffer.cursor = [0, fullLine.length];
|
||||
mockBuffer.visualCursor = [0, fullLine.length];
|
||||
|
||||
const { lastFrame, unmount } = await renderWithProviders(
|
||||
<TestInputPrompt {...props} />,
|
||||
{ uiActions },
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(clean(lastFrame())).toContain(fullLine);
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function clean(str: string | undefined): string {
|
||||
|
||||
@@ -93,6 +93,8 @@ import { useIsHelpDismissKey } from '../utils/shortcutsHelp.js';
|
||||
import { useRepeatedKeyPress } from '../hooks/useRepeatedKeyPress.js';
|
||||
import { useKeyMatchers } from '../hooks/useKeyMatchers.js';
|
||||
|
||||
const SCROLLBAR_GUTTER_WIDTH = 1;
|
||||
|
||||
/**
|
||||
* Returns if the terminal can be trusted to handle paste events atomically
|
||||
* rather than potentially sending multiple paste events separated by line
|
||||
@@ -1868,7 +1870,7 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
|
||||
? `line-${item.absoluteVisualIdx}`
|
||||
: `ghost-${item.index}`
|
||||
}
|
||||
width={inputWidth}
|
||||
width={inputWidth + SCROLLBAR_GUTTER_WIDTH}
|
||||
backgroundColor={listBackgroundColor}
|
||||
containerHeight={Math.min(
|
||||
buffer.viewportHeight,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { waitFor } from '../../test-utils/async.js';
|
||||
import { createMockSettings } from '../../test-utils/settings.js';
|
||||
import {
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
@@ -93,7 +93,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mockGetModel.mockReturnValue(DEFAULT_GEMINI_MODEL_AUTO);
|
||||
mockGetModel.mockReturnValue(GEMINI_MODEL_ALIAS_AUTO);
|
||||
mockGetHasAccessToPreviewModel.mockReturnValue(false);
|
||||
mockGetGemini31LaunchedSync.mockReturnValue(false);
|
||||
mockGetGemini31FlashLiteLaunchedSync.mockReturnValue(false);
|
||||
@@ -102,8 +102,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
// Default implementation for getDisplayString
|
||||
mockGetDisplayString.mockImplementation((val: string) => {
|
||||
if (val === 'auto-gemini-2.5') return 'Auto (Gemini 2.5)';
|
||||
if (val === 'auto-gemini-3') return 'Auto (Preview)';
|
||||
if (val === 'auto') return 'Auto';
|
||||
return val;
|
||||
});
|
||||
});
|
||||
@@ -234,7 +233,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSetModel).toHaveBeenCalledWith(
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
true, // Session only by default
|
||||
);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
@@ -292,7 +291,7 @@ describe('<ModelDialog />', () => {
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockSetModel).toHaveBeenCalledWith(
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
false, // Persist enabled
|
||||
);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
@@ -355,7 +354,7 @@ describe('<ModelDialog />', () => {
|
||||
mockGetModel.mockReturnValue(DEFAULT_GEMINI_MODEL);
|
||||
mockGetDisplayString.mockImplementation((val: string) => {
|
||||
if (val === DEFAULT_GEMINI_MODEL) return 'My Custom Model Display';
|
||||
if (val === 'auto-gemini-2.5') return 'Auto (Gemini 2.5)';
|
||||
if (val === 'auto') return 'Auto';
|
||||
return val;
|
||||
});
|
||||
const { lastFrame, unmount } = await renderComponent();
|
||||
@@ -369,9 +368,9 @@ describe('<ModelDialog />', () => {
|
||||
mockGetHasAccessToPreviewModel.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('shows Auto (Preview) in main view when access is granted', async () => {
|
||||
it('shows Auto in main view when access is granted', async () => {
|
||||
const { lastFrame, unmount } = await renderComponent();
|
||||
expect(lastFrame()).toContain('Auto (Preview)');
|
||||
expect(lastFrame()).toContain('Auto');
|
||||
unmount();
|
||||
});
|
||||
|
||||
|
||||
@@ -14,11 +14,10 @@ import {
|
||||
PREVIEW_GEMINI_3_1_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
|
||||
PREVIEW_GEMINI_MODEL_AUTO,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_FLASH_LITE_MODEL,
|
||||
DEFAULT_GEMINI_MODEL_AUTO,
|
||||
GEMINI_MODEL_ALIAS_AUTO,
|
||||
GEMMA_4_31B_IT_MODEL,
|
||||
GEMMA_4_26B_A4B_IT_MODEL,
|
||||
ModelSlashCommandEvent,
|
||||
@@ -27,6 +26,8 @@ import {
|
||||
AuthType,
|
||||
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
|
||||
isProModel,
|
||||
getChannelFromVersion,
|
||||
getAutoModelDescription,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { useKeypress } from '../hooks/useKeypress.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
@@ -63,7 +64,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
}, [config]);
|
||||
|
||||
// Determine the Preferred Model (read once when the dialog opens).
|
||||
const preferredModel = config?.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
|
||||
const preferredModel = config?.getModel() || GEMINI_MODEL_ALIAS_AUTO;
|
||||
|
||||
const shouldShowPreviewModels = config?.getHasAccessToPreviewModel();
|
||||
const useGemini31 = config?.getGemini31LaunchedSync?.() ?? false;
|
||||
@@ -122,6 +123,11 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
{ isActive: true },
|
||||
);
|
||||
|
||||
const releaseChannel = useMemo(
|
||||
() => getChannelFromVersion(config?.clientVersion ?? ''),
|
||||
[config?.clientVersion],
|
||||
);
|
||||
|
||||
const mainOptions = useMemo(() => {
|
||||
// --- DYNAMIC PATH ---
|
||||
if (
|
||||
@@ -136,6 +142,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
releaseChannel,
|
||||
});
|
||||
|
||||
const list = allOptions
|
||||
@@ -161,11 +168,10 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
// --- LEGACY PATH ---
|
||||
const list = [
|
||||
{
|
||||
value: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(DEFAULT_GEMINI_MODEL_AUTO),
|
||||
description:
|
||||
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
|
||||
key: DEFAULT_GEMINI_MODEL_AUTO,
|
||||
value: GEMINI_MODEL_ALIAS_AUTO,
|
||||
title: getDisplayString(GEMINI_MODEL_ALIAS_AUTO),
|
||||
description: getAutoModelDescription(releaseChannel, useGemini31),
|
||||
key: GEMINI_MODEL_ALIAS_AUTO,
|
||||
},
|
||||
{
|
||||
value: 'Manual',
|
||||
@@ -177,16 +183,6 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
},
|
||||
];
|
||||
|
||||
if (shouldShowPreviewModels) {
|
||||
list.unshift({
|
||||
value: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
title: getDisplayString(PREVIEW_GEMINI_MODEL_AUTO),
|
||||
description: useGemini31
|
||||
? 'Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash'
|
||||
: 'Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash',
|
||||
key: PREVIEW_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
}, [
|
||||
config,
|
||||
@@ -196,6 +192,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
useGemini31FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
releaseChannel,
|
||||
]);
|
||||
|
||||
const manualOptions = useMemo(() => {
|
||||
@@ -212,6 +209,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
useCustomTools: useCustomToolModel,
|
||||
hasAccessToPreview: shouldShowPreviewModels,
|
||||
hasAccessToProModel,
|
||||
releaseChannel,
|
||||
});
|
||||
|
||||
return allOptions
|
||||
@@ -304,6 +302,7 @@ export function ModelDialog({ onClose }: ModelDialogProps): React.JSX.Element {
|
||||
useGemini31FlashLite,
|
||||
useCustomToolModel,
|
||||
hasAccessToProModel,
|
||||
releaseChannel,
|
||||
config,
|
||||
]);
|
||||
|
||||
|
||||
@@ -562,6 +562,13 @@ export const useSessionBrowserInput = (
|
||||
state.setActiveIndex(0);
|
||||
state.setScrollOffset(0);
|
||||
return true;
|
||||
} else if (key.name === 'enter') {
|
||||
const selectedSession =
|
||||
state.filteredAndSortedSessions[state.activeIndex];
|
||||
if (selectedSession && !selectedSession.isCurrentSession) {
|
||||
onResumeSession(selectedSession);
|
||||
}
|
||||
return true;
|
||||
} else if (
|
||||
key.sequence &&
|
||||
key.sequence.length === 1 &&
|
||||
|
||||
@@ -111,6 +111,42 @@ Enter to select · ↑/↓ to navigate · Esc to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AskUserDialog > indents multi-line descriptions correctly 1`] = `
|
||||
"Single choice?
|
||||
|
||||
● 1. Option 1
|
||||
This is a very long description
|
||||
that is expected to wrap onto
|
||||
multiple lines in a narrow
|
||||
terminal. We want to ensure that
|
||||
all lines are correctly indented.
|
||||
2. Enter a custom value
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc
|
||||
to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AskUserDialog > indents multi-line descriptions correctly in multi-select mode 1`] = `
|
||||
"Multi-select?
|
||||
(Select all that apply)
|
||||
|
||||
● 1. [ ] Option 1
|
||||
This is a very long description
|
||||
that is expected to wrap onto
|
||||
multiple lines in a narrow
|
||||
terminal. We want to ensure
|
||||
that all lines are correctly
|
||||
indented even with checkboxes.
|
||||
2. [ ] Enter a custom value
|
||||
Done
|
||||
Finish selection
|
||||
|
||||
Enter to select · ↑/↓ to navigate · Esc
|
||||
to cancel
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`AskUserDialog > renders question and options 1`] = `
|
||||
"Which authentication method should we use?
|
||||
|
||||
@@ -188,7 +224,7 @@ exports[`AskUserDialog > verifies "All of the above" visual state with snapshot
|
||||
1. [x] TypeScript
|
||||
2. [x] ESLint
|
||||
● 3. [x] All of the above
|
||||
Select all options
|
||||
Select all options
|
||||
4. [ ] Enter a custom value
|
||||
Done
|
||||
Finish selection
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="71" viewBox="0 0 920 71">
|
||||
<style>
|
||||
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
|
||||
</style>
|
||||
<rect width="920" height="71" fill="#000000" />
|
||||
<g transform="translate(10, 10)">
|
||||
<text x="0" y="2" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">────────────────────────────────────────────────────────────────────────────────────────────────────</text>
|
||||
<text x="0" y="19" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs"> > hello </text>
|
||||
<text x="0" y="36" fill="#ffffff" textLength="900" lengthAdjust="spacingAndGlyphs">────────────────────────────────────────────────────────────────────────────────────────────────────</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -60,6 +60,12 @@ exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios >
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios > should display cursor correctly 'at the end of the line' 2`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
> hello
|
||||
────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > Highlighting and Cursor Display > single-line scenarios > should display cursor correctly 'for multi-byte unicode characters' 1`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
> hello 👍 world
|
||||
@@ -168,6 +174,27 @@ exports[`InputPrompt > mouse interaction > should toggle paste expansion on doub
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 4`] = `
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
> [Pasted Text: 10 lines]
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 5`] = `
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
> [Pasted Text: 10 lines]
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > mouse interaction > should toggle paste expansion on double-click 6`] = `
|
||||
"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
||||
> [Pasted Text: 10 lines]
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`InputPrompt > multiline rendering > should correctly render multiline input including blank lines 1`] = `
|
||||
"────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||
> hello
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
import { waitFor } from '../../../test-utils/async.js';
|
||||
import { renderWithProviders } from '../../../test-utils/render.js';
|
||||
import { SubagentGroupDisplay } from './SubagentGroupDisplay.js';
|
||||
import { Kind, CoreToolCallStatus } from '@google/gemini-cli-core';
|
||||
import {
|
||||
Kind,
|
||||
CoreToolCallStatus,
|
||||
SubagentState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import type { IndividualToolCallDisplay } from '../../types.js';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { Text } from 'ink';
|
||||
@@ -27,12 +31,12 @@ describe('<SubagentGroupDisplay />', () => {
|
||||
resultDisplay: {
|
||||
isSubagentProgress: true,
|
||||
agentName: 'api-monitor',
|
||||
state: 'running',
|
||||
state: SubagentState.RUNNING,
|
||||
recentActivity: [
|
||||
{
|
||||
id: 'act-1',
|
||||
type: 'tool_call',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
content: '',
|
||||
displayName: 'Action Required',
|
||||
description: 'Verify server is running',
|
||||
@@ -50,13 +54,13 @@ describe('<SubagentGroupDisplay />', () => {
|
||||
resultDisplay: {
|
||||
isSubagentProgress: true,
|
||||
agentName: 'db-manager',
|
||||
state: 'completed',
|
||||
state: SubagentState.COMPLETED,
|
||||
result: 'Database schema validated',
|
||||
recentActivity: [
|
||||
{
|
||||
id: 'act-2',
|
||||
type: 'thought',
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
content: 'Database schema validated',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
isSubagentProgress,
|
||||
checkExhaustive,
|
||||
type SubagentActivityItem,
|
||||
SubagentState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import {
|
||||
SubagentProgressDisplay,
|
||||
@@ -66,13 +67,13 @@ export const SubagentGroupDisplay: React.FC<SubagentGroupDisplayProps> = ({
|
||||
const singleAgent = toolCalls[0].resultDisplay;
|
||||
if (isSubagentProgress(singleAgent)) {
|
||||
switch (singleAgent.state) {
|
||||
case 'completed':
|
||||
case SubagentState.COMPLETED:
|
||||
headerText = 'Agent Completed';
|
||||
break;
|
||||
case 'cancelled':
|
||||
case SubagentState.CANCELLED:
|
||||
headerText = 'Agent Cancelled';
|
||||
break;
|
||||
case 'error':
|
||||
case SubagentState.ERROR:
|
||||
headerText = 'Agent Error';
|
||||
break;
|
||||
default:
|
||||
@@ -88,8 +89,8 @@ export const SubagentGroupDisplay: React.FC<SubagentGroupDisplayProps> = ({
|
||||
for (const tc of toolCalls) {
|
||||
const progress = tc.resultDisplay;
|
||||
if (isSubagentProgress(progress)) {
|
||||
if (progress.state === 'completed') completedCount++;
|
||||
else if (progress.state === 'running') runningCount++;
|
||||
if (progress.state === SubagentState.COMPLETED) completedCount++;
|
||||
else if (progress.state === SubagentState.RUNNING) runningCount++;
|
||||
} else {
|
||||
// It hasn't emitted progress yet, but it is "running"
|
||||
runningCount++;
|
||||
@@ -200,7 +201,7 @@ export const SubagentGroupDisplay: React.FC<SubagentGroupDisplayProps> = ({
|
||||
let content = 'Starting...';
|
||||
let formattedArgs: string | undefined;
|
||||
|
||||
if (progress.state === 'completed') {
|
||||
if (progress.state === SubagentState.COMPLETED) {
|
||||
if (
|
||||
progress.terminateReason &&
|
||||
progress.terminateReason !== 'GOAL'
|
||||
@@ -223,18 +224,18 @@ export const SubagentGroupDisplay: React.FC<SubagentGroupDisplayProps> = ({
|
||||
}
|
||||
|
||||
const displayArgs =
|
||||
progress.state === 'completed' ? '' : formattedArgs;
|
||||
progress.state === SubagentState.COMPLETED ? '' : formattedArgs;
|
||||
|
||||
const renderStatusIcon = () => {
|
||||
const state = progress.state ?? 'running';
|
||||
const state = progress.state ?? SubagentState.RUNNING;
|
||||
switch (state) {
|
||||
case 'running':
|
||||
case SubagentState.RUNNING:
|
||||
return <Text color={theme.text.primary}>!</Text>;
|
||||
case 'completed':
|
||||
case SubagentState.COMPLETED:
|
||||
return <Text color={theme.status.success}>✓</Text>;
|
||||
case 'cancelled':
|
||||
case SubagentState.CANCELLED:
|
||||
return <Text color={theme.status.warning}>ℹ</Text>;
|
||||
case 'error':
|
||||
case SubagentState.ERROR:
|
||||
return <Text color={theme.status.error}>✗</Text>;
|
||||
default:
|
||||
return checkExhaustive(state);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { renderWithProviders } from '../../../test-utils/render.js';
|
||||
import { SubagentHistoryMessage } from './SubagentHistoryMessage.js';
|
||||
import type { HistoryItemSubagent } from '../../types.js';
|
||||
import { SubagentState } from '@google/gemini-cli-core';
|
||||
|
||||
describe('SubagentHistoryMessage', () => {
|
||||
const mockItem: HistoryItemSubagent = {
|
||||
@@ -18,19 +19,19 @@ describe('SubagentHistoryMessage', () => {
|
||||
id: '1',
|
||||
type: 'thought',
|
||||
content: 'Thinking about the problem',
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: 'tool_call',
|
||||
content: 'Calling search_web',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
type: 'tool_call',
|
||||
content: 'Calling read_file fail',
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { render, cleanup } from '../../../test-utils/render.js';
|
||||
import { SubagentProgressDisplay } from './SubagentProgressDisplay.js';
|
||||
import type { SubagentProgress } from '@google/gemini-cli-core';
|
||||
import { type SubagentProgress, SubagentState } from '@google/gemini-cli-core';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
describe('<SubagentProgressDisplay />', () => {
|
||||
@@ -25,7 +25,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
type: 'tool_call',
|
||||
content: 'run_shell_command',
|
||||
args: '{"command": "echo hello", "description": "Say hello"}',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -48,7 +48,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
displayName: 'RunShellCommand',
|
||||
description: 'Executing echo hello',
|
||||
args: '{"command": "echo hello"}',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -69,7 +69,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
type: 'tool_call',
|
||||
content: 'run_shell_command',
|
||||
args: '{"command": "echo hello"}',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -90,7 +90,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
type: 'tool_call',
|
||||
content: 'write_file',
|
||||
args: '{"file_path": "/tmp/test.txt", "content": "foo"}',
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -113,7 +113,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
type: 'tool_call',
|
||||
content: 'run_shell_command',
|
||||
args: JSON.stringify({ description: longDesc }),
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -133,7 +133,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
id: '5',
|
||||
type: 'thought',
|
||||
content: 'Thinking about life',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -149,7 +149,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
isSubagentProgress: true,
|
||||
agentName: 'TestAgent',
|
||||
recentActivity: [],
|
||||
state: 'cancelled',
|
||||
state: SubagentState.CANCELLED,
|
||||
};
|
||||
|
||||
const { lastFrame } = await render(
|
||||
@@ -167,7 +167,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
id: '6',
|
||||
type: 'thought',
|
||||
content: 'Request cancelled.',
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -188,7 +188,7 @@ describe('<SubagentProgressDisplay />', () => {
|
||||
type: 'tool_call',
|
||||
content: 'run_shell_command',
|
||||
args: '{"command": "echo hello"}',
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -9,9 +9,10 @@ import { Box, Text } from 'ink';
|
||||
import { theme } from '../../semantic-colors.js';
|
||||
import Spinner from 'ink-spinner';
|
||||
import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js';
|
||||
import type {
|
||||
SubagentProgress,
|
||||
SubagentActivityItem,
|
||||
import {
|
||||
type SubagentProgress,
|
||||
type SubagentActivityItem,
|
||||
SubagentState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { TOOL_STATUS } from '../../constants.js';
|
||||
import { STATUS_INDICATOR_WIDTH } from './ToolShared.js';
|
||||
@@ -62,13 +63,13 @@ export const SubagentProgressDisplay: React.FC<
|
||||
let headerText: string | undefined;
|
||||
let headerColor = theme.text.secondary;
|
||||
|
||||
if (progress.state === 'cancelled') {
|
||||
if (progress.state === SubagentState.CANCELLED) {
|
||||
headerText = `Subagent ${progress.agentName} was cancelled.`;
|
||||
headerColor = theme.status.warning;
|
||||
} else if (progress.state === 'error') {
|
||||
} else if (progress.state === SubagentState.ERROR) {
|
||||
headerText = `Subagent ${progress.agentName} failed.`;
|
||||
headerColor = theme.status.error;
|
||||
} else if (progress.state === 'completed') {
|
||||
} else if (progress.state === SubagentState.COMPLETED) {
|
||||
headerText = `Subagent ${progress.agentName} completed.`;
|
||||
headerColor = theme.status.success;
|
||||
} else {
|
||||
@@ -107,13 +108,13 @@ export const SubagentProgressDisplay: React.FC<
|
||||
);
|
||||
} else if (item.type === 'tool_call') {
|
||||
const statusSymbol =
|
||||
item.status === 'running' ? (
|
||||
item.status === SubagentState.RUNNING ? (
|
||||
<Spinner type="dots" />
|
||||
) : item.status === 'completed' ? (
|
||||
) : item.status === SubagentState.COMPLETED ? (
|
||||
<Text color={theme.status.success}>
|
||||
{TOOL_STATUS.SUCCESS}
|
||||
</Text>
|
||||
) : item.status === 'cancelled' ? (
|
||||
) : item.status === SubagentState.CANCELLED ? (
|
||||
<Text color={theme.status.warning} bold>
|
||||
{TOOL_STATUS.CANCELED}
|
||||
</Text>
|
||||
@@ -135,7 +136,7 @@ export const SubagentProgressDisplay: React.FC<
|
||||
<Text
|
||||
bold
|
||||
color={theme.text.primary}
|
||||
strikethrough={item.status === 'cancelled'}
|
||||
strikethrough={item.status === SubagentState.CANCELLED}
|
||||
>
|
||||
{item.displayName || item.content}
|
||||
</Text>
|
||||
@@ -144,7 +145,9 @@ export const SubagentProgressDisplay: React.FC<
|
||||
<Text
|
||||
color={theme.text.secondary}
|
||||
wrap="truncate"
|
||||
strikethrough={item.status === 'cancelled'}
|
||||
strikethrough={
|
||||
item.status === SubagentState.CANCELLED
|
||||
}
|
||||
>
|
||||
{displayArgs}
|
||||
</Text>
|
||||
@@ -170,7 +173,7 @@ export const SubagentProgressDisplay: React.FC<
|
||||
)}
|
||||
<MarkdownDisplay
|
||||
text={safeJsonToMarkdown(progress.result)}
|
||||
isPending={progress.state !== 'completed'}
|
||||
isPending={progress.state !== SubagentState.COMPLETED}
|
||||
terminalWidth={terminalWidth}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ApprovalMode,
|
||||
WRITE_FILE_DISPLAY_NAME,
|
||||
Kind,
|
||||
SubagentState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import os from 'node:os';
|
||||
import { createMockSettings } from '../../../test-utils/settings.js';
|
||||
@@ -76,7 +77,7 @@ describe('ToolGroupMessage Regression Tests', () => {
|
||||
resultDisplay: {
|
||||
isSubagentProgress: true,
|
||||
agentName: 'TestAgent',
|
||||
state: 'running',
|
||||
state: SubagentState.RUNNING,
|
||||
recentActivity: [],
|
||||
},
|
||||
}),
|
||||
@@ -112,7 +113,7 @@ describe('ToolGroupMessage Regression Tests', () => {
|
||||
resultDisplay: {
|
||||
isSubagentProgress: true,
|
||||
agentName: 'TestAgent',
|
||||
state: 'completed',
|
||||
state: SubagentState.COMPLETED,
|
||||
recentActivity: [],
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -170,13 +170,13 @@ async function searchResourceCandidates(
|
||||
selector: (candidate: ResourceSuggestionCandidate) => candidate.searchKey,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const results = await fzf.find(normalizedPattern, {
|
||||
limit: MAX_SUGGESTIONS_TO_SHOW * 3,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return results.map(
|
||||
(result: { item: ResourceSuggestionCandidate }) => result.item.suggestion,
|
||||
const results: Array<{ item: ResourceSuggestionCandidate }> = await fzf.find(
|
||||
normalizedPattern,
|
||||
{
|
||||
limit: MAX_SUGGESTIONS_TO_SHOW * 3,
|
||||
},
|
||||
);
|
||||
return results.map((result) => result.item.suggestion);
|
||||
}
|
||||
|
||||
async function searchAgentCandidates(
|
||||
@@ -194,11 +194,13 @@ async function searchAgentCandidates(
|
||||
selector: (s: Suggestion) => s.label,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const results = await fzf.find(normalizedPattern, {
|
||||
limit: MAX_SUGGESTIONS_TO_SHOW,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return results.map((r: { item: Suggestion }) => r.item);
|
||||
const results: Array<{ item: Suggestion }> = await fzf.find(
|
||||
normalizedPattern,
|
||||
{
|
||||
limit: MAX_SUGGESTIONS_TO_SHOW,
|
||||
},
|
||||
);
|
||||
return results.map((r) => r.item);
|
||||
}
|
||||
|
||||
export function useAtCompletion(props: UseAtCompletionProps): void {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
ROOT_SCHEDULER_ID,
|
||||
CoreToolCallStatus,
|
||||
type WaitingToolCall,
|
||||
SubagentState,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { createMockMessageBus } from '@google/gemini-cli-core/src/test-utils/mock-message-bus.js';
|
||||
|
||||
@@ -630,7 +631,7 @@ describe('useToolScheduler', () => {
|
||||
id: '1',
|
||||
type: 'thought',
|
||||
content: 'Thinking...',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -648,7 +649,7 @@ describe('useToolScheduler', () => {
|
||||
id: '2',
|
||||
type: 'tool_call',
|
||||
content: 'Calling tool',
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -697,7 +698,7 @@ describe('useToolScheduler', () => {
|
||||
id: '1',
|
||||
type: 'thought',
|
||||
content: 'Thinking...',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -716,7 +717,7 @@ describe('useToolScheduler', () => {
|
||||
id: '1',
|
||||
type: 'thought',
|
||||
content: 'Thinking... Done!',
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -726,6 +727,8 @@ describe('useToolScheduler', () => {
|
||||
expect(result.current[0][0].subagentHistory![0].content).toBe(
|
||||
'Thinking... Done!',
|
||||
);
|
||||
expect(result.current[0][0].subagentHistory![0].status).toBe('completed');
|
||||
expect(result.current[0][0].subagentHistory![0].status).toBe(
|
||||
SubagentState.COMPLETED,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -174,7 +174,10 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
}
|
||||
|
||||
// --- Pre-wrap and Optimize Widths ---
|
||||
const actualColumnWidths = new Array(numColumns).fill(0);
|
||||
const actualColumnWidths: number[] = [];
|
||||
for (let i = 0; i < numColumns; i++) {
|
||||
actualColumnWidths.push(0);
|
||||
}
|
||||
|
||||
const wrapAndProcessRow = (row: StyledLine[]) => {
|
||||
const rowResult: ProcessedLine[][] = [];
|
||||
@@ -208,11 +211,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
const wrappedRows = styledRows.map((row) => wrapAndProcessRow(row));
|
||||
|
||||
// Use the TIGHTEST widths that fit the wrapped content + padding
|
||||
const adjustedWidths = actualColumnWidths.map(
|
||||
(w) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
w + COLUMN_PADDING,
|
||||
);
|
||||
const adjustedWidths = actualColumnWidths.map((w) => w + COLUMN_PADDING);
|
||||
|
||||
return { wrappedHeaders, wrappedRows, adjustedWidths };
|
||||
}, [styledHeaders, styledRows, terminalWidth]);
|
||||
@@ -263,7 +262,6 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
|
||||
isHeader = false,
|
||||
): React.ReactNode => {
|
||||
const renderedCells = cells.map((cell, index) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const width = adjustedWidths[index] || 0;
|
||||
return renderCell(cell, width, isHeader);
|
||||
});
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
import * as fsPromises from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import type { Content } from '@google/genai';
|
||||
import type {
|
||||
ConversationRecord,
|
||||
MessageRecord,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
/**
|
||||
* Serializes chat history to a Markdown string.
|
||||
@@ -55,17 +51,8 @@ export function serializeHistoryToMarkdown(
|
||||
* Options for exporting chat history.
|
||||
*/
|
||||
export interface ExportHistoryOptions {
|
||||
/** The standard history array used for model requests. */
|
||||
history: readonly Content[];
|
||||
/** The file path to export to. */
|
||||
filePath: string;
|
||||
/** Optional subagent trajectories to include. */
|
||||
trajectories?: Record<string, ConversationRecord>;
|
||||
/**
|
||||
* Optional full message records which contain metadata like agentId for tool calls,
|
||||
* providing the link between history and trajectories.
|
||||
*/
|
||||
messages?: MessageRecord[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,16 +61,12 @@ export interface ExportHistoryOptions {
|
||||
export async function exportHistoryToFile(
|
||||
options: ExportHistoryOptions,
|
||||
): Promise<void> {
|
||||
const { history, filePath, trajectories, messages } = options;
|
||||
const { history, filePath } = options;
|
||||
const extension = path.extname(filePath).toLowerCase();
|
||||
|
||||
let content: string;
|
||||
if (extension === '.json') {
|
||||
if (trajectories && Object.keys(trajectories).length > 0) {
|
||||
content = JSON.stringify({ history, messages, trajectories }, null, 2);
|
||||
} else {
|
||||
content = JSON.stringify(history, null, 2);
|
||||
}
|
||||
content = JSON.stringify(history, null, 2);
|
||||
} else if (extension === '.md') {
|
||||
content = serializeHistoryToMarkdown(history);
|
||||
} else {
|
||||
|
||||
@@ -111,18 +111,20 @@ function resolveEnvVarsInObjectInternal<T>(
|
||||
// Check for circular reference
|
||||
if (visited.has(obj)) {
|
||||
// Return a shallow copy to break the cycle
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return [...obj] as unknown as T;
|
||||
const copy: unknown = [...obj];
|
||||
const isTArray = (val: unknown): val is T => Array.isArray(val);
|
||||
if (isTArray(copy)) return copy;
|
||||
throw new Error('Unreachable');
|
||||
}
|
||||
|
||||
visited.add(obj);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const result = obj.map((item) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
const mapped: unknown = obj.map((item: unknown) =>
|
||||
resolveEnvVarsInObjectInternal(item, visited, customEnv),
|
||||
) as unknown as T;
|
||||
);
|
||||
visited.delete(obj);
|
||||
return result;
|
||||
const isTArray = (val: unknown): val is T => Array.isArray(val);
|
||||
if (isTArray(mapped)) return mapped;
|
||||
throw new Error('Unreachable');
|
||||
}
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
|
||||
@@ -83,8 +83,7 @@ export const getLatestGitHubRelease = async (
|
||||
if (!releaseTag) {
|
||||
throw new Error(`Response did not include tag_name field`);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return releaseTag;
|
||||
return typeof releaseTag === 'string' ? releaseTag : '';
|
||||
} catch (error) {
|
||||
debugLogger.debug(
|
||||
`Failed to determine latest run-gemini-cli release:`,
|
||||
|
||||
@@ -29,8 +29,7 @@ export function tryParseJSON(input: string): object | null {
|
||||
if (!checkInput(input)) return null;
|
||||
const trimmed = input.trim();
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const parsed = JSON.parse(trimmed);
|
||||
const parsed: unknown = JSON.parse(trimmed);
|
||||
if (parsed === null || typeof parsed !== 'object') {
|
||||
return null;
|
||||
}
|
||||
@@ -40,7 +39,6 @@ export function tryParseJSON(input: string): object | null {
|
||||
|
||||
if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return parsed;
|
||||
} catch {
|
||||
return null;
|
||||
|
||||
@@ -616,6 +616,120 @@ describe('SessionSelector', () => {
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].id).toBe(mainSessionId);
|
||||
});
|
||||
|
||||
it('should list legacy session JSON without timestamps (regression #18593)', async () => {
|
||||
const sessionId = randomUUID();
|
||||
|
||||
const chatsDir = path.join(tmpDir, 'chats');
|
||||
await fs.mkdir(chatsDir, { recursive: true });
|
||||
|
||||
const session = {
|
||||
sessionId,
|
||||
projectHash: 'test-hash',
|
||||
messages: [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Legacy session message',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const filePath = path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId.slice(0, 8)}.json`,
|
||||
);
|
||||
await fs.writeFile(filePath, JSON.stringify(session, null, 2));
|
||||
const fallbackTimestamp = new Date('2024-01-01T10:30:00.000Z');
|
||||
await fs.utimes(filePath, fallbackTimestamp, fallbackTimestamp);
|
||||
|
||||
const sessionSelector = new SessionSelector(storage);
|
||||
const sessions = await sessionSelector.listSessions();
|
||||
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].id).toBe(sessionId);
|
||||
expect(sessions[0].startTime).toBe(fallbackTimestamp.toISOString());
|
||||
expect(sessions[0].lastUpdated).toBe(fallbackTimestamp.toISOString());
|
||||
});
|
||||
|
||||
it('should resolve legacy session JSON without timestamps by UUID (regression #18593)', async () => {
|
||||
const sessionId = randomUUID();
|
||||
|
||||
const chatsDir = path.join(tmpDir, 'chats');
|
||||
await fs.mkdir(chatsDir, { recursive: true });
|
||||
|
||||
const session = {
|
||||
sessionId,
|
||||
projectHash: 'test-hash',
|
||||
messages: [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Legacy session message',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const filePath = path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T10-00-${sessionId.slice(0, 8)}.json`,
|
||||
);
|
||||
await fs.writeFile(filePath, JSON.stringify(session, null, 2));
|
||||
const fallbackTimestamp = new Date('2024-01-01T10:30:00.000Z');
|
||||
await fs.utimes(filePath, fallbackTimestamp, fallbackTimestamp);
|
||||
|
||||
const sessionSelector = new SessionSelector(storage);
|
||||
const result = await sessionSelector.resolveSession(sessionId);
|
||||
|
||||
expect(result.sessionData.sessionId).toBe(sessionId);
|
||||
expect(result.sessionData.startTime).toBe(fallbackTimestamp.toISOString());
|
||||
expect(result.sessionData.lastUpdated).toBe(
|
||||
fallbackTimestamp.toISOString(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw INVALID_SESSION_IDENTIFIER for a UUID that does not exist on disk at all', async () => {
|
||||
const existingSessionId = randomUUID();
|
||||
const nonExistentId = randomUUID();
|
||||
|
||||
const chatsDir = path.join(tmpDir, 'chats');
|
||||
await fs.mkdir(chatsDir, { recursive: true });
|
||||
|
||||
const session = {
|
||||
sessionId: existingSessionId,
|
||||
projectHash: 'test-hash',
|
||||
startTime: '2024-01-01T10:00:00.000Z',
|
||||
lastUpdated: '2024-01-01T10:30:00.000Z',
|
||||
messages: [
|
||||
{
|
||||
type: 'user',
|
||||
content: 'Hello',
|
||||
id: 'msg1',
|
||||
timestamp: '2024-01-01T10:00:00.000Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
await fs.writeFile(
|
||||
path.join(
|
||||
chatsDir,
|
||||
`${SESSION_FILE_PREFIX}2024-01-01T10-00-${existingSessionId.slice(0, 8)}.json`,
|
||||
),
|
||||
JSON.stringify(session, null, 2),
|
||||
);
|
||||
|
||||
const sessionSelector = new SessionSelector(storage);
|
||||
|
||||
await expect(sessionSelector.findSession(nonExistentId)).rejects.toSatisfy(
|
||||
(error) => {
|
||||
expect(error).toBeInstanceOf(SessionError);
|
||||
expect((error as SessionError).code).toBe('INVALID_SESSION_IDENTIFIER');
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractFirstUserMessage', () => {
|
||||
|
||||
@@ -270,15 +270,23 @@ export const getAllSessionFiles = async (
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if (
|
||||
!content.sessionId ||
|
||||
!content.startTime ||
|
||||
!content.lastUpdated
|
||||
) {
|
||||
if (!content.sessionId) {
|
||||
// Missing required fields - treat as corrupted
|
||||
return { fileName: file, sessionInfo: null };
|
||||
}
|
||||
|
||||
const fileTimestamp =
|
||||
!content.startTime || !content.lastUpdated
|
||||
? (
|
||||
await fs.stat(filePath).catch(() => undefined)
|
||||
)?.mtime.toISOString()
|
||||
: undefined;
|
||||
const fallbackTimestamp = fileTimestamp ?? new Date().toISOString();
|
||||
const startTime =
|
||||
content.startTime || content.lastUpdated || fallbackTimestamp;
|
||||
const lastUpdated =
|
||||
content.lastUpdated || content.startTime || fallbackTimestamp;
|
||||
|
||||
// Skip sessions that only contain system messages (info, error, warning)
|
||||
if (!content.hasUserOrAssistantMessage) {
|
||||
return { fileName: file, sessionInfo: null };
|
||||
@@ -319,8 +327,8 @@ export const getAllSessionFiles = async (
|
||||
id: content.sessionId,
|
||||
file: file.replace(/\.jsonl?$/, ''),
|
||||
fileName: file,
|
||||
startTime: content.startTime,
|
||||
lastUpdated: content.lastUpdated,
|
||||
startTime,
|
||||
lastUpdated,
|
||||
messageCount: content.messageCount ?? content.messages.length,
|
||||
displayName: content.summary
|
||||
? stripUnsafeCharacters(content.summary)
|
||||
@@ -546,12 +554,17 @@ export class SessionSelector {
|
||||
if (!sessionData) {
|
||||
throw new Error('Failed to load session data');
|
||||
}
|
||||
const normalizedSessionData = {
|
||||
...sessionData,
|
||||
startTime: sessionData.startTime || sessionInfo.startTime,
|
||||
lastUpdated: sessionData.lastUpdated || sessionInfo.lastUpdated,
|
||||
};
|
||||
|
||||
const displayInfo = `Session ${sessionInfo.index}: ${sessionInfo.firstUserMessage} (${sessionInfo.messageCount} messages, ${formatRelativeTime(sessionInfo.lastUpdated)})`;
|
||||
|
||||
return {
|
||||
sessionPath,
|
||||
sessionData,
|
||||
sessionData: normalizedSessionData,
|
||||
displayInfo,
|
||||
};
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@google/gemini-cli-core",
|
||||
"version": "0.42.0-nightly.20260428.g59b2dea0e",
|
||||
"version": "0.44.0-nightly.20260512.g022e8baef",
|
||||
"description": "Gemini CLI Core",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
|
||||
@@ -16,7 +16,7 @@ import type {
|
||||
AgentInterface,
|
||||
} from '@a2a-js/sdk';
|
||||
import type { SendMessageResult } from './a2a-client-manager.js';
|
||||
import type { SubagentActivityItem } from './types.js';
|
||||
import { type SubagentActivityItem, SubagentState } from './types.js';
|
||||
|
||||
export const AUTH_REQUIRED_MSG = `[Authorization Required] The agent has indicated it requires authorization to proceed. Please follow the agent's instructions.`;
|
||||
|
||||
@@ -143,7 +143,7 @@ export class A2AResultReassembler {
|
||||
id: 'auth-required',
|
||||
type: 'thought',
|
||||
content: AUTH_REQUIRED_MSG,
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ export class A2AResultReassembler {
|
||||
id: `msg-${index}`,
|
||||
type: 'thought',
|
||||
content: msg.trim(),
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -161,7 +161,7 @@ export class A2AResultReassembler {
|
||||
id: 'pending',
|
||||
type: 'thought',
|
||||
content: 'Working...',
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
type SubagentActivityItem,
|
||||
AgentTerminateMode,
|
||||
isToolActivityError,
|
||||
SubagentState,
|
||||
} from '../types.js';
|
||||
import type { MessageBus } from '../../confirmation-bus/message-bus.js';
|
||||
import { createBrowserAgentDefinition } from './browserAgentFactory.js';
|
||||
@@ -123,7 +124,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
isSubagentProgress: true,
|
||||
agentName: this.agentName,
|
||||
recentActivity: [],
|
||||
state: 'running',
|
||||
state: SubagentState.RUNNING,
|
||||
};
|
||||
updateOutput(initialProgress);
|
||||
}
|
||||
@@ -137,7 +138,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
id: randomUUID(),
|
||||
type: 'thought',
|
||||
content: sanitizedMsg,
|
||||
status: 'completed',
|
||||
status: SubagentState.COMPLETED,
|
||||
});
|
||||
if (recentActivity.length > MAX_RECENT_ACTIVITY) {
|
||||
recentActivity = recentActivity.slice(-MAX_RECENT_ACTIVITY);
|
||||
@@ -146,7 +147,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
isSubagentProgress: true,
|
||||
agentName: this.agentName,
|
||||
recentActivity: [...recentActivity],
|
||||
state: 'running',
|
||||
state: SubagentState.RUNNING,
|
||||
} as SubagentProgress);
|
||||
}
|
||||
: undefined;
|
||||
@@ -175,7 +176,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
if (
|
||||
lastItem &&
|
||||
lastItem.type === 'thought' &&
|
||||
lastItem.status === 'running'
|
||||
lastItem.status === SubagentState.RUNNING
|
||||
) {
|
||||
lastItem.content = sanitizeThoughtContent(text);
|
||||
} else {
|
||||
@@ -183,7 +184,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
id: randomUUID(),
|
||||
type: 'thought',
|
||||
content: sanitizeThoughtContent(text),
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
});
|
||||
}
|
||||
updated = true;
|
||||
@@ -210,7 +211,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
displayName,
|
||||
description,
|
||||
args,
|
||||
status: 'running',
|
||||
status: SubagentState.RUNNING,
|
||||
});
|
||||
updated = true;
|
||||
break;
|
||||
@@ -227,9 +228,11 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
recentActivity[i].type === 'tool_call' &&
|
||||
callId != null &&
|
||||
recentActivity[i].id === callId &&
|
||||
recentActivity[i].status === 'running'
|
||||
recentActivity[i].status === SubagentState.RUNNING
|
||||
) {
|
||||
recentActivity[i].status = isError ? 'error' : 'completed';
|
||||
recentActivity[i].status = isError
|
||||
? SubagentState.ERROR
|
||||
: SubagentState.COMPLETED;
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
@@ -242,7 +245,9 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
const callId = activity.data['callId']
|
||||
? String(activity.data['callId'])
|
||||
: undefined;
|
||||
const newStatus = isCancellation ? 'cancelled' : 'error';
|
||||
const newStatus = isCancellation
|
||||
? SubagentState.CANCELLED
|
||||
: SubagentState.ERROR;
|
||||
|
||||
if (callId) {
|
||||
// Mark the specific tool as error/cancelled
|
||||
@@ -250,7 +255,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
if (
|
||||
recentActivity[i].type === 'tool_call' &&
|
||||
recentActivity[i].id === callId &&
|
||||
recentActivity[i].status === 'running'
|
||||
recentActivity[i].status === SubagentState.RUNNING
|
||||
) {
|
||||
recentActivity[i].status = newStatus;
|
||||
updated = true;
|
||||
@@ -260,7 +265,10 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
} else {
|
||||
// No specific tool — mark ALL running tool_call items
|
||||
for (const item of recentActivity) {
|
||||
if (item.type === 'tool_call' && item.status === 'running') {
|
||||
if (
|
||||
item.type === 'tool_call' &&
|
||||
item.status === SubagentState.RUNNING
|
||||
) {
|
||||
item.status = newStatus;
|
||||
updated = true;
|
||||
}
|
||||
@@ -293,7 +301,7 @@ export class BrowserAgentInvocation extends BaseToolInvocation<
|
||||
isSubagentProgress: true,
|
||||
agentName: this.agentName,
|
||||
recentActivity: [...recentActivity],
|
||||
state: 'running',
|
||||
state: SubagentState.RUNNING,
|
||||
};
|
||||
updateOutput(progress);
|
||||
}
|
||||
@@ -330,13 +338,13 @@ ${output.result}`;
|
||||
// GOAL = agent completed its task normally.
|
||||
// ABORTED = user cancelled.
|
||||
// Others (ERROR, MAX_TURNS, ERROR_NO_COMPLETE_TASK_CALL) = error.
|
||||
let progressState: SubagentProgress['state'];
|
||||
let progressState: SubagentState;
|
||||
if (output.terminate_reason === AgentTerminateMode.ABORTED) {
|
||||
progressState = 'cancelled';
|
||||
progressState = SubagentState.CANCELLED;
|
||||
} else if (output.terminate_reason === AgentTerminateMode.GOAL) {
|
||||
progressState = 'completed';
|
||||
progressState = SubagentState.COMPLETED;
|
||||
} else {
|
||||
progressState = 'error';
|
||||
progressState = SubagentState.ERROR;
|
||||
}
|
||||
|
||||
const progress: SubagentProgress = {
|
||||
@@ -366,8 +374,8 @@ ${output.result}`;
|
||||
|
||||
// Mark any running items as error/cancelled
|
||||
for (const item of recentActivity) {
|
||||
if (item.status === 'running') {
|
||||
item.status = isAbort ? 'cancelled' : 'error';
|
||||
if (item.status === SubagentState.RUNNING) {
|
||||
item.status = isAbort ? SubagentState.CANCELLED : SubagentState.ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +383,7 @@ ${output.result}`;
|
||||
isSubagentProgress: true,
|
||||
agentName: this.agentName,
|
||||
recentActivity: [...recentActivity],
|
||||
state: isAbort ? 'cancelled' : 'error',
|
||||
state: isAbort ? SubagentState.CANCELLED : SubagentState.ERROR,
|
||||
};
|
||||
|
||||
if (updateOutput) {
|
||||
|
||||
@@ -4199,6 +4199,49 @@ describe('LocalAgentExecutor', () => {
|
||||
expect(memoryPart).toBeDefined();
|
||||
expect(memoryPart?.text).toContain(mockMemory);
|
||||
});
|
||||
|
||||
it('should omit extension context from session memory when disabled by the agent', async () => {
|
||||
const definition = createTestDefinition();
|
||||
definition.includeExtensionContext = false;
|
||||
const executor = await LocalAgentExecutor.create(
|
||||
definition,
|
||||
mockConfig,
|
||||
onActivity,
|
||||
);
|
||||
|
||||
const getSessionMemorySpy = vi
|
||||
.spyOn(mockConfig, 'getSessionMemory')
|
||||
.mockImplementation(
|
||||
(options?: { includeExtensionContext?: boolean }) =>
|
||||
options?.includeExtensionContext === false
|
||||
? '<loaded_context>\n<project_context>\nProject memory rule\n</project_context>\n</loaded_context>'
|
||||
: '<loaded_context>\n<extension_context>\nExtension memory rule\n</extension_context>\n<project_context>\nProject memory rule\n</project_context>\n</loaded_context>',
|
||||
);
|
||||
vi.spyOn(mockConfig, 'isJitContextEnabled').mockReturnValue(true);
|
||||
|
||||
mockModelResponse([
|
||||
{
|
||||
name: COMPLETE_TASK_TOOL_NAME,
|
||||
args: { finalResult: 'done' },
|
||||
id: 'call1',
|
||||
},
|
||||
]);
|
||||
|
||||
await executor.run({ goal: 'test' }, signal);
|
||||
|
||||
expect(getSessionMemorySpy).toHaveBeenCalledWith({
|
||||
includeExtensionContext: false,
|
||||
});
|
||||
const { message } = getMockMessageParams(0);
|
||||
const parts = message as Part[];
|
||||
const memoryPart = parts.find((p) =>
|
||||
p.text?.includes('<loaded_context>'),
|
||||
);
|
||||
|
||||
expect(memoryPart?.text).toContain('Project memory rule');
|
||||
expect(memoryPart?.text).not.toContain('<extension_context>');
|
||||
expect(memoryPart?.text).not.toContain('Extension memory rule');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -640,10 +640,19 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
);
|
||||
const formattedInitialHints = formatUserHintsForModel(initialHints);
|
||||
|
||||
// Inject loaded memory files (JIT + extension/project memory)
|
||||
const environmentMemory = this.context.config.isJitContextEnabled?.()
|
||||
? this.context.config.getSessionMemory()
|
||||
: this.context.config.getEnvironmentMemory();
|
||||
// Inject loaded memory files. Some background agents opt out of
|
||||
// extension memory while still retaining project session context.
|
||||
let environmentMemory: string;
|
||||
if (this.context.config.isJitContextEnabled?.()) {
|
||||
environmentMemory =
|
||||
this.definition.includeExtensionContext === false
|
||||
? this.context.config.getSessionMemory({
|
||||
includeExtensionContext: false,
|
||||
})
|
||||
: this.context.config.getSessionMemory();
|
||||
} else {
|
||||
environmentMemory = this.context.config.getEnvironmentMemory();
|
||||
}
|
||||
|
||||
const initialParts: Part[] = [];
|
||||
if (environmentMemory) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
type SubagentProgress,
|
||||
SubagentActivityErrorType,
|
||||
SUBAGENT_REJECTED_ERROR_PREFIX,
|
||||
SubagentState,
|
||||
} from './types.js';
|
||||
import { LocalSubagentInvocation } from './local-invocation.js';
|
||||
import { LocalAgentExecutor } from './local-executor.js';
|
||||
@@ -215,7 +216,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
]);
|
||||
const display = result.returnDisplay as SubagentProgress;
|
||||
expect(display.isSubagentProgress).toBe(true);
|
||||
expect(display.state).toBe('completed');
|
||||
expect(display.state).toBe(SubagentState.COMPLETED);
|
||||
expect(display.result).toBe('Analysis complete.');
|
||||
expect(display.terminateReason).toBe(AgentTerminateMode.GOAL);
|
||||
});
|
||||
@@ -234,7 +235,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
|
||||
const display = result.returnDisplay as SubagentProgress;
|
||||
expect(display.isSubagentProgress).toBe(true);
|
||||
expect(display.state).toBe('completed');
|
||||
expect(display.state).toBe(SubagentState.COMPLETED);
|
||||
expect(display.result).toBe('Partial progress...');
|
||||
expect(display.terminateReason).toBe(AgentTerminateMode.TIMEOUT);
|
||||
});
|
||||
@@ -340,7 +341,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect.objectContaining({
|
||||
type: 'thought',
|
||||
content: 'Error: Failed',
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -376,7 +377,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect.objectContaining({
|
||||
type: 'tool_call',
|
||||
content: 'ls',
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -418,7 +419,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect.objectContaining({
|
||||
type: 'tool_call',
|
||||
content: 'ls',
|
||||
status: 'cancelled',
|
||||
status: SubagentState.CANCELLED,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -443,7 +444,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect(result.error).toBeUndefined();
|
||||
const display = result.returnDisplay as SubagentProgress;
|
||||
expect(display.isSubagentProgress).toBe(true);
|
||||
expect(display.state).toBe('completed');
|
||||
expect(display.state).toBe(SubagentState.COMPLETED);
|
||||
expect(display.result).toBe('Done');
|
||||
});
|
||||
|
||||
@@ -466,7 +467,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect.objectContaining({
|
||||
type: 'thought',
|
||||
content: `Error: ${error.message}`,
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -488,7 +489,7 @@ describe('LocalSubagentInvocation', () => {
|
||||
expect(display.recentActivity).toContainEqual(
|
||||
expect.objectContaining({
|
||||
content: `Error: ${creationError.message}`,
|
||||
status: 'error',
|
||||
status: SubagentState.ERROR,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user