Compare commits

..

2 Commits

Author SHA1 Message Date
gemini-cli-robot 949f545ec0 chore(release): v0.30.0-test-github-release 2026-02-26 22:07:30 +00:00
Yuna Seol 412721c22c [Do not submit] delete prepare-github-release 2026-02-26 17:03:51 -05:00
63 changed files with 171 additions and 1984 deletions
+6 -10
View File
@@ -39,22 +39,18 @@ runs:
if: "inputs.dry-run != 'true'"
env:
GH_TOKEN: '${{ inputs.github-token }}'
INPUTS_BRANCH_NAME: '${{ inputs.branch-name }}'
INPUTS_PR_TITLE: '${{ inputs.pr-title }}'
INPUTS_PR_BODY: '${{ inputs.pr-body }}'
INPUTS_BASE_BRANCH: '${{ inputs.base-branch }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
set -e
if ! git ls-remote --exit-code --heads origin "${INPUTS_BRANCH_NAME}"; then
echo "::error::Branch '${INPUTS_BRANCH_NAME}' does not exist on the remote repository."
if ! git ls-remote --exit-code --heads origin "${{ inputs.branch-name }}"; then
echo "::error::Branch '${{ inputs.branch-name }}' does not exist on the remote repository."
exit 1
fi
PR_URL=$(gh pr create \
--title "${INPUTS_PR_TITLE}" \
--body "${INPUTS_PR_BODY}" \
--base "${INPUTS_BASE_BRANCH}" \
--head "${INPUTS_BRANCH_NAME}" \
--title "${{ inputs.pr-title }}" \
--body "${{ inputs.pr-body }}" \
--base "${{ inputs.base-branch }}" \
--head "${{ inputs.branch-name }}" \
--fill)
gh pr merge "$PR_URL" --auto
+6 -12
View File
@@ -30,22 +30,16 @@ runs:
id: 'npm_auth_token'
shell: 'bash'
run: |
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
PACKAGE_NAME="${INPUTS_PACKAGE_NAME}"
AUTH_TOKEN="${{ inputs.github-token }}"
PACKAGE_NAME="${{ inputs.package-name }}"
PRIVATE_REPO="@google-gemini/"
if [[ "$PACKAGE_NAME" == "$PRIVATE_REPO"* ]]; then
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
AUTH_TOKEN="${{ inputs.github-token }}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CLI}"
AUTH_TOKEN="${{ inputs.wombat-token-cli }}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-core" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CORE}"
AUTH_TOKEN="${{ inputs.wombat-token-core }}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-a2a-server" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_A2A_SERVER}"
AUTH_TOKEN="${{ inputs.wombat-token-a2a-server }}"
fi
echo "auth-token=$AUTH_TOKEN" >> $GITHUB_OUTPUT
env:
INPUTS_GITHUB_TOKEN: '${{ inputs.github-token }}'
INPUTS_PACKAGE_NAME: '${{ inputs.package-name }}'
INPUTS_WOMBAT_TOKEN_CLI: '${{ inputs.wombat-token-cli }}'
INPUTS_WOMBAT_TOKEN_CORE: '${{ inputs.wombat-token-core }}'
INPUTS_WOMBAT_TOKEN_A2A_SERVER: '${{ inputs.wombat-token-a2a-server }}'
+20 -49
View File
@@ -93,19 +93,15 @@ runs:
id: 'release_branch'
shell: 'bash'
run: |
BRANCH_NAME="release/${INPUTS_RELEASE_TAG}"
BRANCH_NAME="release/${{ inputs.release-tag }}"
git switch -c "${BRANCH_NAME}"
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
env:
INPUTS_RELEASE_TAG: '${{ inputs.release-tag }}'
- name: '⬆️ Update package versions'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
npm run release:version "${INPUTS_RELEASE_VERSION}"
env:
INPUTS_RELEASE_VERSION: '${{ inputs.release-version }}'
npm run release:version "${{ inputs.release-version }}"
- name: '💾 Commit and Conditionally Push package versions'
working-directory: '${{ inputs.working-directory }}'
@@ -138,14 +134,6 @@ runs:
run: |
npm run bundle
# TODO: Refactor this github specific publishing script to be generalized based upon inputs.
- name: '📦 Prepare for GitHub release'
if: "inputs.npm-registry-url == 'https://npm.pkg.github.com/'"
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
node ${{ github.workspace }}/scripts/prepare-github-release.js
- name: 'Configure npm for publishing to npm'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
with:
@@ -167,30 +155,23 @@ runs:
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
shell: 'bash'
run: |
npm publish \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
--dry-run="${{ inputs.dry-run }}" \
--workspace="${{ inputs.core-package-name }}" \
--no-tag
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} false --silent
npm dist-tag rm ${{ inputs.core-package-name }} false --silent
- name: '🔗 Install latest core package'
working-directory: '${{ inputs.working-directory }}'
if: "${{ inputs.dry-run != 'true' }}"
shell: 'bash'
run: |
npm install "${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_RELEASE_VERSION}" \
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
npm install "${{ inputs.core-package-name }}@${{ inputs.release-version }}" \
--workspace="${{ inputs.cli-package-name }}" \
--workspace="${{ inputs.a2a-package-name }}" \
--save-exact
env:
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_RELEASE_VERSION: '${{ inputs.release-version }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
- name: 'Get CLI Token'
uses: './.github/actions/npm-auth-token'
@@ -206,15 +187,13 @@ runs:
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
shell: 'bash'
run: |
npm publish \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
--dry-run="${{ inputs.dry-run }}" \
--workspace="${{ inputs.cli-package-name }}" \
--no-tag
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} false --silent
npm dist-tag rm ${{ inputs.cli-package-name }} false --silent
- name: 'Get a2a-server Token'
uses: './.github/actions/npm-auth-token'
@@ -230,16 +209,14 @@ runs:
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
shell: 'bash'
# Tag staging for initial release
run: |
npm publish \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
--dry-run="${{ inputs.dry-run }}" \
--workspace="${{ inputs.a2a-package-name }}" \
--no-tag
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} false --silent
npm dist-tag rm ${{ inputs.a2a-package-name }} false --silent
- name: '🔬 Verify NPM release by version'
uses: './.github/actions/verify-release'
@@ -273,16 +250,13 @@ runs:
if: "${{ inputs.dry-run != 'true' && inputs.skip-github-release != 'true' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}"
env:
GITHUB_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
INPUTS_RELEASE_TAG: '${{ inputs.release-tag }}'
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
INPUTS_PREVIOUS_TAG: '${{ inputs.previous-tag }}'
shell: 'bash'
run: |
gh release create "${INPUTS_RELEASE_TAG}" \
gh release create "${{ inputs.release-tag }}" \
bundle/gemini.js \
--target "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}" \
--title "Release ${INPUTS_RELEASE_TAG}" \
--notes-start-tag "${INPUTS_PREVIOUS_TAG}" \
--target "${{ steps.release_branch.outputs.BRANCH_NAME }}" \
--title "Release ${{ inputs.release-tag }}" \
--notes-start-tag "${{ inputs.previous-tag }}" \
--generate-notes \
${{ inputs.npm-tag != 'latest' && '--prerelease' || '' }}
@@ -292,8 +266,5 @@ runs:
continue-on-error: true
shell: 'bash'
run: |
echo "Cleaning up release branch ${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}..."
git push origin --delete "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}"
env:
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
echo "Cleaning up release branch ${{ steps.release_branch.outputs.BRANCH_NAME }}..."
git push origin --delete "${{ steps.release_branch.outputs.BRANCH_NAME }}"
+1 -3
View File
@@ -52,10 +52,8 @@ runs:
id: 'branch_name'
shell: 'bash'
run: |
REF_NAME="${INPUTS_REF_NAME}"
REF_NAME="${{ inputs.ref-name }}"
echo "name=${REF_NAME%/merge}" >> $GITHUB_OUTPUT
env:
INPUTS_REF_NAME: '${{ inputs.ref-name }}'
- name: 'Build and Push the Docker Image'
uses: 'docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83' # ratchet:docker/build-push-action@v6
with:
+4 -10
View File
@@ -56,8 +56,8 @@ runs:
id: 'image_tag'
shell: 'bash'
run: |-
SHELL_TAG_NAME="${INPUTS_GITHUB_REF_NAME}"
FINAL_TAG="${INPUTS_GITHUB_SHA}"
SHELL_TAG_NAME="${{ inputs.github-ref-name }}"
FINAL_TAG="${{ inputs.github-sha }}"
if [[ "$SHELL_TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
echo "Release detected."
FINAL_TAG="${SHELL_TAG_NAME#v}"
@@ -66,19 +66,15 @@ runs:
fi
echo "Determined image tag: $FINAL_TAG"
echo "FINAL_TAG=$FINAL_TAG" >> $GITHUB_OUTPUT
env:
INPUTS_GITHUB_REF_NAME: '${{ inputs.github-ref-name }}'
INPUTS_GITHUB_SHA: '${{ inputs.github-sha }}'
- name: 'build'
id: 'docker_build'
shell: 'bash'
env:
GEMINI_SANDBOX_IMAGE_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
GEMINI_SANDBOX: 'docker'
STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
run: |-
npm run build:sandbox -- \
--image google/gemini-cli-sandbox:${STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG} \
--image google/gemini-cli-sandbox:${{ steps.image_tag.outputs.FINAL_TAG }} \
--output-file final_image_uri.txt
echo "uri=$(cat final_image_uri.txt)" >> $GITHUB_OUTPUT
- name: 'verify'
@@ -93,9 +89,7 @@ runs:
shell: 'bash'
if: "${{ inputs.dry-run != 'true' }}"
run: |-
docker push "${STEPS_DOCKER_BUILD_OUTPUTS_URI}"
env:
STEPS_DOCKER_BUILD_OUTPUTS_URI: '${{ steps.docker_build.outputs.uri }}'
docker push "${{ steps.docker_build.outputs.uri }}"
- name: 'Create issue on failure'
if: |-
${{ failure() }}
+1 -3
View File
@@ -18,7 +18,5 @@ runs:
shell: 'bash'
run: |-
echo ""@google-gemini:registry=https://npm.pkg.github.com"" > ~/.npmrc
echo ""//npm.pkg.github.com/:_authToken=${INPUTS_GITHUB_TOKEN}"" >> ~/.npmrc
echo ""//npm.pkg.github.com/:_authToken=${{ inputs.github-token }}"" >> ~/.npmrc
echo ""@google:registry=https://wombat-dressing-room.appspot.com"" >> ~/.npmrc
env:
INPUTS_GITHUB_TOKEN: '${{ inputs.github-token }}'
+4 -24
View File
@@ -71,13 +71,10 @@ runs:
${{ inputs.dry-run != 'true' }}
env:
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
npm dist-tag add ${{ inputs.core-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
- name: 'Get cli Token'
uses: './.github/actions/npm-auth-token'
@@ -94,13 +91,10 @@ runs:
${{ inputs.dry-run != 'true' }}
env:
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_CLI_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
npm dist-tag add ${{ inputs.cli-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
- name: 'Get a2a Token'
uses: './.github/actions/npm-auth-token'
@@ -117,13 +111,10 @@ runs:
${{ inputs.dry-run == 'false' }}
env:
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_A2A_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
npm dist-tag add ${{ inputs.a2a-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
- name: 'Log dry run'
if: |-
@@ -131,15 +122,4 @@ runs:
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
echo "Dry run: Would have added tag '${INPUTS_CHANNEL}' to version '${INPUTS_VERSION}' for ${INPUTS_CLI_PACKAGE_NAME}, ${INPUTS_CORE_PACKAGE_NAME}, and ${INPUTS_A2A_PACKAGE_NAME}."
env:
INPUTS_CHANNEL: '${{ inputs.channel }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
echo "Dry run: Would have added tag '${{ inputs.channel }}' to version '${{ inputs.version }}' for ${{ inputs.cli-package-name }}, ${{ inputs.core-package-name }}, and ${{ inputs.a2a-package-name }}."
+5 -11
View File
@@ -64,13 +64,10 @@ runs:
working-directory: '${{ inputs.working-directory }}'
run: |-
gemini_version=$(gemini --version)
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
echo "❌ NPM Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
echo "❌ NPM Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
exit 1
fi
env:
INPUTS_EXPECTED_VERSION: '${{ inputs.expected-version }}'
INPUTS_NPM_PACKAGE: '${{ inputs.npm-package }}'
- name: 'Clear npm cache'
shell: 'bash'
@@ -80,14 +77,11 @@ runs:
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |-
gemini_version=$(npx --prefer-online "${INPUTS_NPM_PACKAGE}" --version)
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
gemini_version=$(npx --prefer-online "${{ inputs.npm-package}}" --version)
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
exit 1
fi
env:
INPUTS_NPM_PACKAGE: '${{ inputs.npm-package }}'
INPUTS_EXPECTED_VERSION: '${{ inputs.expected-version }}'
- name: 'Install dependencies for integration tests'
shell: 'bash'
+5 -10
View File
@@ -53,7 +53,7 @@ jobs:
REPO_NAME: '${{ github.event.inputs.repo_name }}'
run: |
mkdir -p ./pr
echo "${REPO_NAME}" > ./pr/repo_name
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'repo_name'
@@ -320,19 +320,14 @@ jobs:
steps:
- name: 'Check E2E test results'
run: |
if [[ ${NEEDS_E2E_LINUX_RESULT} != 'success' || \
${NEEDS_E2E_MAC_RESULT} != 'success' || \
${NEEDS_E2E_WINDOWS_RESULT} != 'success' || \
${NEEDS_EVALS_RESULT} != 'success' ]]; then
if [[ ${{ needs.e2e_linux.result }} != 'success' || \
${{ needs.e2e_mac.result }} != 'success' || \
${{ needs.e2e_windows.result }} != 'success' || \
${{ needs.evals.result }} != 'success' ]]; then
echo "One or more E2E jobs failed."
exit 1
fi
echo "All required E2E jobs passed!"
env:
NEEDS_E2E_LINUX_RESULT: '${{ needs.e2e_linux.result }}'
NEEDS_E2E_MAC_RESULT: '${{ needs.e2e_mac.result }}'
NEEDS_E2E_WINDOWS_RESULT: '${{ needs.e2e_windows.result }}'
NEEDS_EVALS_RESULT: '${{ needs.evals.result }}'
set_workflow_status:
runs-on: 'gemini-cli-ubuntu-16-core'
+7 -15
View File
@@ -464,22 +464,14 @@ jobs:
steps:
- name: 'Check all job results'
run: |
if [[ (${NEEDS_LINT_RESULT} != 'success' && ${NEEDS_LINT_RESULT} != 'skipped') || \
(${NEEDS_LINK_CHECKER_RESULT} != 'success' && ${NEEDS_LINK_CHECKER_RESULT} != 'skipped') || \
(${NEEDS_TEST_LINUX_RESULT} != 'success' && ${NEEDS_TEST_LINUX_RESULT} != 'skipped') || \
(${NEEDS_TEST_MAC_RESULT} != 'success' && ${NEEDS_TEST_MAC_RESULT} != 'skipped') || \
(${NEEDS_TEST_WINDOWS_RESULT} != 'success' && ${NEEDS_TEST_WINDOWS_RESULT} != 'skipped') || \
(${NEEDS_CODEQL_RESULT} != 'success' && ${NEEDS_CODEQL_RESULT} != 'skipped') || \
(${NEEDS_BUNDLE_SIZE_RESULT} != 'success' && ${NEEDS_BUNDLE_SIZE_RESULT} != 'skipped') ]]; then
if [[ (${{ needs.lint.result }} != 'success' && ${{ needs.lint.result }} != 'skipped') || \
(${{ needs.link_checker.result }} != 'success' && ${{ needs.link_checker.result }} != 'skipped') || \
(${{ needs.test_linux.result }} != 'success' && ${{ needs.test_linux.result }} != 'skipped') || \
(${{ needs.test_mac.result }} != 'success' && ${{ needs.test_mac.result }} != 'skipped') || \
(${{ needs.test_windows.result }} != 'success' && ${{ needs.test_windows.result }} != 'skipped') || \
(${{ needs.codeql.result }} != 'success' && ${{ needs.codeql.result }} != 'skipped') || \
(${{ needs.bundle_size.result }} != 'success' && ${{ needs.bundle_size.result }} != 'skipped') ]]; then
echo "One or more CI jobs failed."
exit 1
fi
echo "All CI jobs passed!"
env:
NEEDS_LINT_RESULT: '${{ needs.lint.result }}'
NEEDS_LINK_CHECKER_RESULT: '${{ needs.link_checker.result }}'
NEEDS_TEST_LINUX_RESULT: '${{ needs.test_linux.result }}'
NEEDS_TEST_MAC_RESULT: '${{ needs.test_mac.result }}'
NEEDS_TEST_WINDOWS_RESULT: '${{ needs.test_windows.result }}'
NEEDS_CODEQL_RESULT: '${{ needs.codeql.result }}'
NEEDS_BUNDLE_SIZE_RESULT: '${{ needs.bundle_size.result }}'
+5 -5
View File
@@ -68,10 +68,10 @@ jobs:
VERBOSE: 'true'
shell: 'bash'
run: |
if [[ "${IS_DOCKER}" == "true" ]]; then
npm run deflake:test:integration:sandbox:docker -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
if [[ "${{ env.IS_DOCKER }}" == "true" ]]; then
npm run deflake:test:integration:sandbox:docker -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
else
npm run deflake:test:integration:sandbox:none -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
fi
deflake_e2e_mac:
@@ -109,7 +109,7 @@ jobs:
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
VERBOSE: 'true'
run: |
npm run deflake:test:integration:sandbox:none -- --runs="${RUNS}" -- --testNamePattern "'${TEST_NAME_PATTERN}'"
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
deflake_e2e_windows:
name: 'Slow E2E - Win'
@@ -167,4 +167,4 @@ jobs:
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
shell: 'pwsh'
run: |
npm run deflake:test:integration:sandbox:none -- --runs="$env:RUNS" -- --testNamePattern "'$env:TEST_NAME_PATTERN'"
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
+1 -1
View File
@@ -44,5 +44,5 @@ jobs:
- name: 'Run evaluation'
working-directory: '/app'
run: |
poetry run exp_run --experiment-mode=on-demand --branch-or-commit="${GITHUB_REF_NAME}" --model-name=gemini-2.5-pro --dataset=swebench_verified --concurrency=15
poetry run exp_run --experiment-mode=on-demand --branch-or-commit=${{ github.ref_name }} --model-name=gemini-2.5-pro --dataset=swebench_verified --concurrency=15
poetry run python agent_prototypes/scripts/parse_gcli_logs_experiment.py --experiment_dir=experiments/adhoc/gcli_temp_exp --gcs-bucket="${EVAL_GCS_BUCKET}" --gcs-path=gh_action_artifacts
+1 -1
View File
@@ -62,7 +62,7 @@ jobs:
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
run: |
CMD="npm run test:all_evals"
PATTERN="${TEST_NAME_PATTERN}"
PATTERN="${{ env.TEST_NAME_PATTERN }}"
if [[ -n "$PATTERN" ]]; then
if [[ "$PATTERN" == *.ts || "$PATTERN" == *.js || "$PATTERN" == */* ]]; then
+5 -11
View File
@@ -118,7 +118,6 @@ jobs:
ORIGINAL_RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
ORIGINAL_RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
ORIGINAL_PREVIOUS_TAG: '${{ steps.patch_version.outputs.PREVIOUS_TAG }}'
VARS_CLI_PACKAGE_NAME: '${{ vars.CLI_PACKAGE_NAME }}'
run: |
echo "🔍 Verifying no concurrent patch releases have occurred..."
@@ -130,7 +129,7 @@ jobs:
# Re-run the same version calculation script
echo "Re-calculating version to check for changes..."
CURRENT_PATCH_JSON=$(node scripts/get-release-version.js --cli-package-name="${VARS_CLI_PACKAGE_NAME}" --type=patch --patch-from="${CHANNEL}")
CURRENT_PATCH_JSON=$(node scripts/get-release-version.js --cli-package-name="${{vars.CLI_PACKAGE_NAME}}" --type=patch --patch-from="${CHANNEL}")
CURRENT_RELEASE_VERSION=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseVersion)
CURRENT_RELEASE_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseTag)
CURRENT_PREVIOUS_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .previousReleaseTag)
@@ -163,15 +162,10 @@ jobs:
- name: 'Print Calculated Version'
run: |-
echo "Patch Release Summary:"
echo " Release Version: ${STEPS_PATCH_VERSION_OUTPUTS_RELEASE_VERSION}"
echo " Release Tag: ${STEPS_PATCH_VERSION_OUTPUTS_RELEASE_TAG}"
echo " NPM Tag: ${STEPS_PATCH_VERSION_OUTPUTS_NPM_TAG}"
echo " Previous Tag: ${STEPS_PATCH_VERSION_OUTPUTS_PREVIOUS_TAG}"
env:
STEPS_PATCH_VERSION_OUTPUTS_RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
STEPS_PATCH_VERSION_OUTPUTS_RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
STEPS_PATCH_VERSION_OUTPUTS_NPM_TAG: '${{ steps.patch_version.outputs.NPM_TAG }}'
STEPS_PATCH_VERSION_OUTPUTS_PREVIOUS_TAG: '${{ steps.patch_version.outputs.PREVIOUS_TAG }}'
echo " Release Version: ${{ steps.patch_version.outputs.RELEASE_VERSION }}"
echo " Release Tag: ${{ steps.patch_version.outputs.RELEASE_TAG }}"
echo " NPM Tag: ${{ steps.patch_version.outputs.NPM_TAG }}"
echo " Previous Tag: ${{ steps.patch_version.outputs.PREVIOUS_TAG }}"
- name: 'Run Tests'
if: "${{github.event.inputs.force_skip_tests != 'true'}}"
+3 -8
View File
@@ -362,28 +362,23 @@ jobs:
- name: 'Create and switch to a new branch'
id: 'release_branch'
run: |
BRANCH_NAME="chore/nightly-version-bump-${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"
BRANCH_NAME="chore/nightly-version-bump-${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}"
git switch -c "${BRANCH_NAME}"
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
env:
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: '${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}'
- name: 'Update package versions'
run: 'npm run release:version "${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"'
env:
NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION: '${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}'
run: 'npm run release:version "${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}"'
- name: 'Commit and Push package versions'
env:
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 }}'
run: |-
git add package.json packages/*/package.json
if [ -f package-lock.json ]; then
git add package-lock.json
fi
git commit -m "chore(release): bump version to ${NEEDS_CALCULATE_VERSIONS_OUTPUTS_NEXT_NIGHTLY_VERSION}"
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}"
+1 -1
View File
@@ -203,7 +203,7 @@ jobs:
run: |
ROLLBACK_COMMIT=$(git rev-parse -q --verify "$TARGET_TAG")
if [ "$ROLLBACK_COMMIT" != "$TARGET_HASH" ]; then
echo "❌ Failed to add tag ${TARGET_TAG} to commit ${TARGET_HASH}"
echo '❌ Failed to add tag $TARGET_TAG to commit $TARGET_HASH'
echo '❌ This means the tag was not added, and the workflow should fail.'
exit 1
fi
+2 -2
View File
@@ -23,8 +23,8 @@ jobs:
HEAD_SHA: '${{ github.event.inputs.head_sha || github.event.pull_request.head.sha }}'
run: |
mkdir -p ./pr
echo "${REPO_NAME}" > ./pr/repo_name
echo "${HEAD_SHA}" > ./pr/head_sha
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
echo '${{ env.HEAD_SHA }}' > ./pr/head_sha
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'repo_name'
-165
View File
@@ -1,165 +0,0 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect } from 'vitest';
import { appEvalTest } from './app-test-helper.js';
describe('generalist_delegation', () => {
// --- Positive Evals (Should Delegate) ---
appEvalTest('USUALLY_PASSES', {
name: 'should delegate batch error fixing to generalist agent',
configOverrides: {
agents: {
overrides: {
generalist: { enabled: true },
},
},
experimental: {
enableAgents: true,
},
excludeTools: ['run_shell_command'],
},
files: {
'file1.ts': 'console.log("no semi")',
'file2.ts': 'console.log("no semi")',
'file3.ts': 'console.log("no semi")',
'file4.ts': 'console.log("no semi")',
'file5.ts': 'console.log("no semi")',
'file6.ts': 'console.log("no semi")',
'file7.ts': 'console.log("no semi")',
'file8.ts': 'console.log("no semi")',
'file9.ts': 'console.log("no semi")',
'file10.ts': 'console.log("no semi")',
},
prompt:
'I have 10 files (file1.ts to file10.ts) that are missing semicolons. Can you fix them?',
setup: async (rig) => {
rig.setBreakpoint(['generalist']);
},
assert: async (rig) => {
const confirmation = await rig.waitForPendingConfirmation(
'generalist',
60000,
);
expect(
confirmation,
'Expected a tool call for generalist agent',
).toBeTruthy();
await rig.resolveTool(confirmation);
await rig.waitForIdle(60000);
},
});
appEvalTest('USUALLY_PASSES', {
name: 'should autonomously delegate complex batch task to generalist agent',
configOverrides: {
agents: {
overrides: {
generalist: { enabled: true },
},
},
experimental: {
enableAgents: true,
},
excludeTools: ['run_shell_command'],
},
files: {
'src/a.ts': 'export const a = 1;',
'src/b.ts': 'export const b = 2;',
'src/c.ts': 'export const c = 3;',
'src/d.ts': 'export const d = 4;',
'src/e.ts': 'export const e = 5;',
},
prompt:
'Please update all files in the src directory. For each file, add a comment at the top that says "Processed by Gemini".',
setup: async (rig) => {
rig.setBreakpoint(['generalist']);
},
assert: async (rig) => {
const confirmation = await rig.waitForPendingConfirmation(
'generalist',
60000,
);
expect(
confirmation,
'Expected autonomously delegate to generalist for batch task',
).toBeTruthy();
await rig.resolveTool(confirmation);
await rig.waitForIdle(60000);
},
});
// --- Negative Evals (Should NOT Delegate - Assertive Handling) ---
appEvalTest('USUALLY_PASSES', {
name: 'should NOT delegate simple read and fix to generalist agent',
configOverrides: {
agents: {
overrides: {
generalist: { enabled: true },
},
},
experimental: {
enableAgents: true,
},
excludeTools: ['run_shell_command'],
},
files: {
'README.md': 'This is a proyect.',
},
prompt:
'There is a typo in README.md ("proyect"). Please fix it to "project".',
setup: async (rig) => {
// Break on everything to see what it calls
rig.setBreakpoint(['*']);
},
assert: async (rig) => {
await rig.drainBreakpointsUntilIdle((confirmation) => {
expect(
confirmation.toolName,
`Agent should NOT have delegated to generalist.`,
).not.toBe('generalist');
});
const output = rig.getStaticOutput();
expect(output).toMatch(/project/i);
},
});
appEvalTest('USUALLY_PASSES', {
name: 'should NOT delegate simple direct question to generalist agent',
configOverrides: {
agents: {
overrides: {
generalist: { enabled: true },
},
},
experimental: {
enableAgents: true,
},
excludeTools: ['run_shell_command'],
},
files: {
'src/VERSION': '1.2.3',
},
prompt: 'Can you tell me the version number in the src folder?',
setup: async (rig) => {
rig.setBreakpoint(['*']);
},
assert: async (rig) => {
await rig.drainBreakpointsUntilIdle((confirmation) => {
expect(
confirmation.toolName,
`Agent should NOT have delegated to generalist.`,
).not.toBe('generalist');
});
const output = rig.getStaticOutput();
expect(output).toMatch(/1\.2\.3/);
},
});
});
+1 -1
View File
@@ -62,7 +62,7 @@ describe('Plan Mode', () => {
});
});
it.skip('should allow write_file only in the plans directory in plan mode', async () => {
it('should allow write_file only in the plans directory in plan mode', async () => {
await rig.setup(
'should allow write_file only in the plans directory in plan mode',
{
+10 -34
View File
@@ -1,12 +1,12 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"workspaces": [
"packages/*"
],
@@ -2292,7 +2292,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",
@@ -2473,7 +2472,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"
}
@@ -2523,7 +2521,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"
},
@@ -2898,7 +2895,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"
@@ -2932,7 +2928,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"
@@ -2987,7 +2982,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",
@@ -4184,7 +4178,6 @@
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
@@ -4458,7 +4451,6 @@
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
@@ -5306,7 +5298,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"
},
@@ -7860,7 +7851,6 @@
"integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -8493,7 +8483,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",
@@ -9788,7 +9777,6 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.2.tgz",
"integrity": "sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -10068,7 +10056,6 @@
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.11.tgz",
"integrity": "sha512-93LQlzT7vvZ1XJcmOMwN4s+6W334QegendeHOMnEJBlhnpIzr8bws6/aOEHG8ZCuVD/vNeeea5m1msHIdAY6ig==",
"license": "MIT",
"peer": true,
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.2.1",
"ansi-escapes": "^7.0.0",
@@ -13718,7 +13705,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"
}
@@ -13729,7 +13715,6 @@
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"shell-quote": "^1.6.1",
"ws": "^7"
@@ -15689,7 +15674,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -15913,8 +15897,7 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD",
"peer": true
"license": "0BSD"
},
"node_modules/tsx": {
"version": "4.20.3",
@@ -15922,7 +15905,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"
@@ -16082,7 +16064,6 @@
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -16291,7 +16272,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -16405,7 +16385,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -16418,7 +16397,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",
@@ -17063,7 +17041,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"
}
@@ -17079,7 +17056,7 @@
},
"packages/a2a-server": {
"name": "@google/gemini-cli-a2a-server",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
"@google-cloud/storage": "^7.16.0",
@@ -17137,7 +17114,7 @@
},
"packages/cli": {
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
@@ -17220,7 +17197,7 @@
},
"packages/core": {
"name": "@google/gemini-cli-core",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"dependencies": {
"@a2a-js/sdk": "^0.3.8",
@@ -17463,7 +17440,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -17486,7 +17462,7 @@
},
"packages/devtools": {
"name": "@google/gemini-cli-devtools",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"dependencies": {
"ws": "^8.16.0"
@@ -17501,7 +17477,7 @@
},
"packages/sdk": {
"name": "@google/gemini-cli-sdk",
"version": "0.29.0-nightly.20260203.71f46f116",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"dependencies": {
"@google/gemini-cli-core": "file:../core",
@@ -17518,7 +17494,7 @@
},
"packages/test-utils": {
"name": "@google/gemini-cli-test-utils",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"dependencies": {
"@google/gemini-cli-core": "file:../core",
@@ -17535,7 +17511,7 @@
},
"packages/vscode-ide-companion": {
"name": "gemini-cli-vscode-ide-companion",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "LICENSE",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.23.0",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"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.30.0-nightly.20260210.a2174751d"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.30.0-test-github-release"
},
"scripts": {
"start": "cross-env NODE_ENV=development node scripts/start.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-a2a-server",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"description": "Gemini CLI A2A Server",
"repository": {
"type": "git",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"description": "Gemini CLI",
"license": "Apache-2.0",
"repository": {
@@ -26,7 +26,7 @@
"dist"
],
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.30.0-nightly.20260210.a2174751d"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.30.0-test-github-release"
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.12.0",
+5 -173
View File
@@ -29,7 +29,6 @@ import {
createContentGenerator,
IdeClient,
debugLogger,
CoreToolCallStatus,
} from '@google/gemini-cli-core';
import {
type MockShellCommand,
@@ -37,47 +36,7 @@ import {
} from './MockShellExecutionService.js';
import { createMockSettings } from './settings.js';
import { type LoadedSettings } from '../config/settings.js';
import { AuthState, StreamingState } from '../ui/types.js';
import { randomUUID } from 'node:crypto';
import type {
TrackedCancelledToolCall,
TrackedCompletedToolCall,
TrackedToolCall,
} from '../ui/hooks/useToolScheduler.js';
// Global state observer for React-based signals
const sessionStateMap = new Map<string, StreamingState>();
const activeRigs = new Map<string, AppRig>();
// Mock StreamingContext to report state changes back to the observer
vi.mock('../ui/contexts/StreamingContext.js', async (importOriginal) => {
const original =
await importOriginal<typeof import('../ui/contexts/StreamingContext.js')>();
const { useConfig } = await import('../ui/contexts/ConfigContext.js');
const React = await import('react');
return {
...original,
useStreamingContext: () => {
const state = original.useStreamingContext();
const config = useConfig();
const sessionId = config.getSessionId();
React.useEffect(() => {
sessionStateMap.set(sessionId, state);
// If we see activity, we are no longer "awaiting" the start of a response
if (state !== StreamingState.Idle) {
const rig = activeRigs.get(sessionId);
if (rig) {
rig.awaitingResponse = false;
}
}
}, [sessionId, state]);
return state;
},
};
});
import { AuthState } from '../ui/types.js';
// Mock core functions globally for tests using AppRig.
vi.mock('@google/gemini-cli-core', async (importOriginal) => {
@@ -153,18 +112,9 @@ export class AppRig {
private breakpointTools = new Set<string | undefined>();
private lastAwaitedConfirmation: PendingConfirmation | undefined;
/**
* True if a message was just sent but React hasn't yet reported a non-idle state.
*/
awaitingResponse = false;
constructor(private options: AppRigOptions = {}) {
const uniqueId = randomUUID();
this.testDir = fs.mkdtempSync(
path.join(os.tmpdir(), `gemini-app-rig-${uniqueId.slice(0, 8)}-`),
);
this.sessionId = `test-session-${uniqueId}`;
activeRigs.set(this.sessionId, this);
this.testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gemini-app-rig-'));
this.sessionId = `test-session-${Math.random().toString(36).slice(2, 9)}`;
}
async initialize() {
@@ -295,8 +245,6 @@ export class AppRig {
};
}
private toolCalls: TrackedToolCall[] = [];
private setupMessageBusListeners() {
if (!this.config) return;
const messageBus = this.config.getMessageBus();
@@ -304,7 +252,6 @@ export class AppRig {
messageBus.subscribe(
MessageBusType.TOOL_CALLS_UPDATE,
(message: ToolCallsUpdateMessage) => {
this.toolCalls = message.toolCalls;
for (const call of message.toolCalls) {
if (call.status === 'awaiting_approval' && call.correlationId) {
const details = call.confirmationDetails;
@@ -334,48 +281,6 @@ export class AppRig {
);
}
/**
* Returns true if the agent is currently busy (responding or executing tools).
*/
isBusy(): boolean {
if (this.awaitingResponse) {
return true;
}
const reactState = sessionStateMap.get(this.sessionId);
// If we have a React-based state, use it as the definitive signal.
// 'responding' and 'waiting-for-confirmation' both count as busy for the overall task.
if (reactState !== undefined) {
return reactState !== StreamingState.Idle;
}
// Fallback to tool tracking if React hasn't reported yet
const isAnyToolActive = this.toolCalls.some((tc) => {
if (
tc.status === CoreToolCallStatus.Executing ||
tc.status === CoreToolCallStatus.Scheduled ||
tc.status === CoreToolCallStatus.Validating
) {
return true;
}
if (
tc.status === CoreToolCallStatus.Success ||
tc.status === CoreToolCallStatus.Error ||
tc.status === CoreToolCallStatus.Cancelled
) {
return !(tc as TrackedCompletedToolCall | TrackedCancelledToolCall)
.responseSubmittedToGemini;
}
return false;
});
const isAwaitingConfirmation = this.toolCalls.some(
(tc) => tc.status === CoreToolCallStatus.AwaitingApproval,
);
return isAnyToolActive || isAwaitingConfirmation;
}
render() {
if (!this.config || !this.settings)
throw new Error('AppRig not initialized');
@@ -429,21 +334,17 @@ export class AppRig {
this.setBreakpoint(name);
}
} else {
// Use undefined toolName to create a global rule if '*' is provided
const actualToolName = toolName === '*' ? undefined : toolName;
this.setToolPolicy(actualToolName, PolicyDecision.ASK_USER, 100);
this.setToolPolicy(toolName, PolicyDecision.ASK_USER, 100);
this.breakpointTools.add(toolName);
}
}
removeToolPolicy(toolName?: string, source = 'AppRig Override') {
if (!this.config) throw new Error('AppRig not initialized');
// Map '*' back to undefined for policy removal
const actualToolName = toolName === '*' ? undefined : toolName;
this.config
.getPolicyEngine()
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
.removeRulesForTool(actualToolName as string, source);
.removeRulesForTool(toolName as string, source);
this.breakpointTools.delete(toolName);
}
@@ -515,44 +416,6 @@ export class AppRig {
return matched!;
}
/**
* Waits for either a tool confirmation request OR for the agent to go idle.
*/
async waitForNextEvent(
timeout = 60000,
): Promise<
| { type: 'confirmation'; confirmation: PendingConfirmation }
| { type: 'idle' }
> {
let confirmation: PendingConfirmation | undefined;
let isIdle = false;
await this.waitUntil(
async () => {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
confirmation = this.getPendingConfirmations()[0];
// Now that we have a code-powered signal, this should be perfectly deterministic.
isIdle = !this.isBusy();
return !!confirmation || isIdle;
},
{
timeout,
message: 'Timed out waiting for next event (confirmation or idle).',
},
);
if (confirmation) {
this.lastAwaitedConfirmation = confirmation;
return { type: 'confirmation', confirmation };
}
// Ensure all renders are flushed before returning 'idle'
await this.renderResult?.waitUntilReady();
return { type: 'idle' };
}
async resolveTool(
toolNameOrDisplayName: string | RegExp | PendingConfirmation,
outcome: ToolConfirmationOutcome = ToolConfirmationOutcome.ProceedOnce,
@@ -608,32 +471,6 @@ export class AppRig {
});
}
/**
* Drains all pending tool calls that hit a breakpoint until the agent is idle.
* Useful for negative tests to ensure no unwanted tools (like generalist) are called.
*
* @param onConfirmation Optional callback to inspect each confirmation before resolving.
* Return true to skip the default resolveTool call (e.g. if you handled it).
*/
async drainBreakpointsUntilIdle(
onConfirmation?: (confirmation: PendingConfirmation) => void | boolean,
timeout = 60000,
) {
while (true) {
const event = await this.waitForNextEvent(timeout);
if (event.type === 'idle') {
break;
}
const confirmation = event.confirmation;
const handled = onConfirmation?.(confirmation);
if (!handled) {
await this.resolveTool(confirmation);
}
}
}
getConfig(): Config {
if (!this.config) throw new Error('AppRig not initialized');
return this.config;
@@ -693,16 +530,11 @@ export class AppRig {
}
async sendMessage(text: string) {
this.awaitingResponse = true;
await this.type(text);
await this.pressEnter();
}
async unmount() {
// Clean up global state for this session
sessionStateMap.delete(this.sessionId);
activeRigs.delete(this.sessionId);
// Poison the chat recording service to prevent late writes to the test directory
if (this.config) {
const recordingService = this.config
@@ -135,7 +135,6 @@ export const DialogManager = ({
isModelNotFoundError={
!!uiState.quota.proQuotaRequest.isModelNotFoundError
}
authType={uiState.quota.proQuotaRequest.authType}
onChoice={uiActions.handleProQuotaChoice}
/>
);
@@ -13,7 +13,6 @@ import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import {
PREVIEW_GEMINI_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
AuthType,
} from '@google/gemini-cli-core';
// Mock the child component to make it easier to test the parent
@@ -63,7 +62,7 @@ describe('ProQuotaDialog', () => {
describe('for non-flash model failures', () => {
describe('when it is a terminal quota error', () => {
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE', () => {
it('should render switch, upgrade, and stop options for paid tiers', () => {
const { unmount } = render(
<ProQuotaDialog
failedModel="gemini-2.5-pro"
@@ -71,7 +70,6 @@ describe('ProQuotaDialog', () => {
message="paid tier quota error"
isTerminalQuotaError={true}
isModelNotFoundError={false}
authType={AuthType.LOGIN_WITH_GOOGLE}
onChoice={mockOnChoice}
/>,
);
@@ -101,39 +99,6 @@ describe('ProQuotaDialog', () => {
unmount();
});
it('should NOT render upgrade option for USE_GEMINI', () => {
const { unmount } = render(
<ProQuotaDialog
failedModel="gemini-2.5-pro"
fallbackModel="gemini-2.5-flash"
message="paid tier quota error"
isTerminalQuotaError={true}
isModelNotFoundError={false}
authType={AuthType.USE_GEMINI}
onChoice={mockOnChoice}
/>,
);
expect(RadioButtonSelect).toHaveBeenCalledWith(
expect.objectContaining({
items: [
{
label: 'Switch to gemini-2.5-flash',
value: 'retry_always',
key: 'retry_always',
},
{
label: 'Stop',
value: 'retry_later',
key: 'retry_later',
},
],
}),
undefined,
);
unmount();
});
it('should render "Keep trying" and "Stop" options when failed model and fallback model are the same', () => {
const { unmount } = render(
<ProQuotaDialog
@@ -165,7 +130,7 @@ describe('ProQuotaDialog', () => {
unmount();
});
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE (free tier)', () => {
it('should render switch, upgrade, and stop options for free tier', () => {
const { unmount } = render(
<ProQuotaDialog
failedModel="gemini-2.5-pro"
@@ -173,7 +138,6 @@ describe('ProQuotaDialog', () => {
message="free tier quota error"
isTerminalQuotaError={true}
isModelNotFoundError={false}
authType={AuthType.LOGIN_WITH_GOOGLE}
onChoice={mockOnChoice}
/>,
);
@@ -240,7 +204,7 @@ describe('ProQuotaDialog', () => {
});
describe('when it is a model not found error', () => {
it('should render switch, upgrade, and stop options for LOGIN_WITH_GOOGLE', () => {
it('should render switch and stop options regardless of tier', () => {
const { unmount } = render(
<ProQuotaDialog
failedModel="gemini-3-pro-preview"
@@ -248,7 +212,6 @@ describe('ProQuotaDialog', () => {
message="You don't have access to gemini-3-pro-preview yet."
isTerminalQuotaError={false}
isModelNotFoundError={true}
authType={AuthType.LOGIN_WITH_GOOGLE}
onChoice={mockOnChoice}
/>,
);
@@ -278,7 +241,7 @@ describe('ProQuotaDialog', () => {
unmount();
});
it('should NOT render upgrade option for USE_GEMINI', () => {
it('should render switch and stop options for paid tier as well', () => {
const { unmount } = render(
<ProQuotaDialog
failedModel="gemini-3-pro-preview"
@@ -286,7 +249,6 @@ describe('ProQuotaDialog', () => {
message="You don't have access to gemini-3-pro-preview yet."
isTerminalQuotaError={false}
isModelNotFoundError={true}
authType={AuthType.USE_GEMINI}
onChoice={mockOnChoice}
/>,
);
@@ -299,6 +261,11 @@ describe('ProQuotaDialog', () => {
value: 'retry_always',
key: 'retry_always',
},
{
label: 'Upgrade for higher limits',
value: 'upgrade',
key: 'upgrade',
},
{
label: 'Stop',
value: 'retry_later',
@@ -8,7 +8,6 @@ import type React from 'react';
import { Box, Text } from 'ink';
import { RadioButtonSelect } from './shared/RadioButtonSelect.js';
import { theme } from '../semantic-colors.js';
import { AuthType } from '@google/gemini-cli-core';
interface ProQuotaDialogProps {
failedModel: string;
@@ -16,7 +15,6 @@ interface ProQuotaDialogProps {
message: string;
isTerminalQuotaError: boolean;
isModelNotFoundError?: boolean;
authType?: AuthType;
onChoice: (
choice: 'retry_later' | 'retry_once' | 'retry_always' | 'upgrade',
) => void;
@@ -28,7 +26,6 @@ export function ProQuotaDialog({
message,
isTerminalQuotaError,
isModelNotFoundError,
authType,
onChoice,
}: ProQuotaDialogProps): React.JSX.Element {
let items;
@@ -54,15 +51,11 @@ export function ProQuotaDialog({
value: 'retry_always' as const,
key: 'retry_always',
},
...(authType === AuthType.LOGIN_WITH_GOOGLE
? [
{
label: 'Upgrade for higher limits',
value: 'upgrade' as const,
key: 'upgrade',
},
]
: []),
{
label: 'Upgrade for higher limits',
value: 'upgrade' as const,
key: 'upgrade',
},
{
label: `Stop`,
value: 'retry_later' as const,
@@ -24,7 +24,6 @@ import type {
ApprovalMode,
UserTierId,
IdeInfo,
AuthType,
FallbackIntent,
ValidationIntent,
AgentDefinition,
@@ -43,7 +42,6 @@ export interface ProQuotaDialogRequest {
message: string;
isTerminalQuotaError: boolean;
isModelNotFoundError?: boolean;
authType?: AuthType;
resolve: (intent: FallbackIntent) => void;
}
@@ -96,13 +96,9 @@ describe('useQuotaAndFallback', () => {
});
describe('Fallback Handler Logic', () => {
it('should show fallback dialog but omit switch to API key message if authType is not LOGIN_WITH_GOOGLE', async () => {
// Override the default mock from beforeEach for this specific test
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
authType: AuthType.USE_GEMINI,
});
const { result } = renderHook(() =>
// Helper function to render the hook and extract the registered handler
const getRegisteredHandler = (): FallbackModelHandler => {
renderHook(() =>
useQuotaAndFallback({
config: mockConfig,
historyManager: mockHistoryManager,
@@ -111,24 +107,20 @@ describe('useQuotaAndFallback', () => {
onShowAuthSelection: mockOnShowAuthSelection,
}),
);
return setFallbackHandlerSpy.mock.calls[0][0] as FallbackModelHandler;
};
const handler = setFallbackHandlerSpy.mock
.calls[0][0] as FallbackModelHandler;
const error = new TerminalQuotaError(
'pro quota',
mockGoogleApiError,
1000 * 60 * 5,
);
act(() => {
void handler('gemini-pro', 'gemini-flash', error);
it('should return null and take no action if authType is not LOGIN_WITH_GOOGLE', async () => {
// Override the default mock from beforeEach for this specific test
vi.spyOn(mockConfig, 'getContentGeneratorConfig').mockReturnValue({
authType: AuthType.USE_GEMINI,
});
expect(result.current.proQuotaRequest).not.toBeNull();
expect(result.current.proQuotaRequest?.message).not.toContain(
'/auth to switch to API key.',
);
const handler = getRegisteredHandler();
const result = await handler('gemini-pro', 'gemini-flash', new Error());
expect(result).toBeNull();
expect(mockHistoryManager.addItem).not.toHaveBeenCalled();
});
describe('Interactive Fallback', () => {
@@ -55,7 +55,14 @@ export function useQuotaAndFallback({
fallbackModel,
error,
): Promise<FallbackIntent | null> => {
// Fallbacks are currently only handled for OAuth users.
const contentGeneratorConfig = config.getContentGeneratorConfig();
if (
!contentGeneratorConfig ||
contentGeneratorConfig.authType !== AuthType.LOGIN_WITH_GOOGLE
) {
return null;
}
let message: string;
let isTerminalQuotaError = false;
@@ -71,9 +78,7 @@ export function useQuotaAndFallback({
error.retryDelayMs ? getResetTimeMessage(error.retryDelayMs) : null,
`/stats model for usage details`,
`/model to switch models.`,
contentGeneratorConfig?.authType === AuthType.LOGIN_WITH_GOOGLE
? `/auth to switch to API key.`
: null,
`/auth to switch to API key.`,
].filter(Boolean);
message = messageLines.join('\n');
} else if (error instanceof ModelNotFoundError) {
@@ -117,7 +122,6 @@ export function useQuotaAndFallback({
message,
isTerminalQuotaError,
isModelNotFoundError,
authType: contentGeneratorConfig?.authType,
});
},
);
@@ -17,21 +17,20 @@ describe('TableRenderer', () => {
];
const terminalWidth = 80;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expect(output).toContain('Header 1');
expect(output).toContain('Row 1, Col 1');
expect(output).toContain('Row 3, Col 3');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -49,14 +48,13 @@ describe('TableRenderer', () => {
];
const terminalWidth = 80;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
@@ -64,7 +62,7 @@ describe('TableRenderer', () => {
// We just check for some of the content.
expect(output).toContain('Data 1.1');
expect(output).toContain('Data 3.4');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -79,20 +77,19 @@ describe('TableRenderer', () => {
];
const terminalWidth = 50;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expect(output).toContain('This is a very');
expect(output).toContain('long cell');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -107,19 +104,18 @@ describe('TableRenderer', () => {
];
const terminalWidth = 60;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expect(output).toContain('wrapping in');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -134,20 +130,19 @@ describe('TableRenderer', () => {
];
const terminalWidth = 50;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expect(output).toContain('Tiny');
expect(output).toContain('definitely needs');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -163,19 +158,18 @@ describe('TableRenderer', () => {
];
const terminalWidth = 60;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expect(output).toContain('Start. Stop.');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -184,21 +178,20 @@ describe('TableRenderer', () => {
const rows = [['Data 1', 'Data 2', 'Data 3']];
const terminalWidth = 50;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
// The output should NOT contain the literal '**'
expect(output).not.toContain('**Bold Header**');
expect(output).toContain('Bold Header');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -211,21 +204,20 @@ describe('TableRenderer', () => {
const rows = [['Data 1', 'Data 2', 'Data 3']];
const terminalWidth = 40;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
// Markers should be gone
expect(output).not.toContain('**');
expect(output).toContain('Very Long');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -255,7 +247,7 @@ describe('TableRenderer', () => {
const terminalWidth = 160;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
@@ -263,7 +255,6 @@ describe('TableRenderer', () => {
/>,
{ width: terminalWidth },
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
@@ -280,7 +271,7 @@ describe('TableRenderer', () => {
expect(output).toContain('J.');
expect(output).toContain('Doe');
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -326,7 +317,7 @@ describe('TableRenderer', () => {
expected: ['Mixed 😃 中文', '你好 😃', 'こんにちは 🚀'],
},
])('$name', async ({ headers, rows, terminalWidth, expected }) => {
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
@@ -334,14 +325,13 @@ describe('TableRenderer', () => {
/>,
{ width: terminalWidth },
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expected.forEach((text) => {
expect(output).toContain(text);
});
await expect(renderResult).toMatchSvgSnapshot();
expect(output).toMatchSnapshot();
unmount();
});
@@ -361,21 +351,19 @@ describe('TableRenderer', () => {
])('$name', async ({ headers, rows, expected }) => {
const terminalWidth = 50;
const renderResult = renderWithProviders(
const { lastFrame, waitUntilReady } = renderWithProviders(
<TableRenderer
headers={headers}
rows={rows}
terminalWidth={terminalWidth}
/>,
);
const { lastFrame, waitUntilReady, unmount } = renderResult;
await waitUntilReady();
const output = lastFrame();
expected.forEach((text) => {
expect(output).toContain(text);
});
await expect(renderResult).toMatchSvgSnapshot();
unmount();
expect(output).toMatchSnapshot();
});
});
@@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="139" viewBox="0 0 560 139">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="560" height="139" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">┌──────────────┬────────────┬───────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="63" lengthAdjust="spacingAndGlyphs">Emoji 😃</text>
<text x="117" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="36" fill="#89b4fa" textLength="90" lengthAdjust="spacingAndGlyphs">Asian 汉字</text>
<text x="234" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="36" fill="#89b4fa" textLength="108" lengthAdjust="spacingAndGlyphs">Mixed 🚀 Text</text>
<text x="378" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">├──────────────┼────────────┼───────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> Start 🌟 End</text>
<text x="117" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="70" fill="#ffffff" textLength="90" lengthAdjust="spacingAndGlyphs">你好世界 </text>
<text x="234" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Rocket 🚀 Man </text>
<text x="378" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> Thumbs 👍 Up</text>
<text x="117" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="87" fill="#ffffff" textLength="90" lengthAdjust="spacingAndGlyphs">こんにちは</text>
<text x="234" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Fire 🔥 </text>
<text x="378" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">└──────────────┴────────────┴───────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

@@ -1,47 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="190" viewBox="0 0 920 190">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="190" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="297" lengthAdjust="spacingAndGlyphs">┌─────────────┬───────┬─────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="81" lengthAdjust="spacingAndGlyphs">Very Long</text>
<text x="117" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Short</text>
<text x="189" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="216" y="36" fill="#89b4fa" textLength="63" lengthAdjust="spacingAndGlyphs">Another</text>
<text x="288" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="53" fill="#89b4fa" textLength="99" lengthAdjust="spacingAndGlyphs">Bold Header</text>
<text x="117" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="189" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="216" y="53" fill="#89b4fa" textLength="36" lengthAdjust="spacingAndGlyphs">Long</text>
<text x="288" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="70" fill="#89b4fa" textLength="81" lengthAdjust="spacingAndGlyphs">That Will</text>
<text x="117" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="189" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="216" y="70" fill="#89b4fa" textLength="54" lengthAdjust="spacingAndGlyphs">Header</text>
<text x="288" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="87" fill="#89b4fa" textLength="36" lengthAdjust="spacingAndGlyphs">Wrap</text>
<text x="117" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="189" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="297" lengthAdjust="spacingAndGlyphs">├─────────────┼───────┼─────────┤</text>
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="121" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> Data 1 </text>
<text x="117" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="121" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs">Data </text>
<text x="189" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="216" y="121" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Data 3 </text>
<text x="288" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="117" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="138" fill="#ffffff" textLength="45" lengthAdjust="spacingAndGlyphs">2 </text>
<text x="189" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="155" fill="#333333" textLength="297" lengthAdjust="spacingAndGlyphs">└─────────────┴───────┴─────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.4 KiB

@@ -1,39 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="156" viewBox="0 0 920 156">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="156" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">┌──────────────┬──────────────┬──────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 1</text>
<text x="126" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 2</text>
<text x="261" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 3</text>
<text x="405" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">├──────────────┼──────────────┼──────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> Row 1, Col 1</text>
<text x="126" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="70" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs">Row 1, Col 2</text>
<text x="261" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Row 1, Col 3 </text>
<text x="405" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> Row 2, Col 1</text>
<text x="126" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="87" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs">Row 2, Col 2</text>
<text x="261" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Row 2, Col 3 </text>
<text x="405" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="104" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> Row 3, Col 1</text>
<text x="126" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="104" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs">Row 3, Col 2</text>
<text x="261" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="104" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Row 3, Col 3 </text>
<text x="405" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">└──────────────┴──────────────┴──────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

@@ -1,401 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1460" height="615" viewBox="0 0 1460 615">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="1460" height="615" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="1404" lengthAdjust="spacingAndGlyphs">┌─────────────────────────────┬──────────────────────────────┬─────────────────────────────┬──────────────────────────────┬─────┬────────┬─────────┬───────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="243" lengthAdjust="spacingAndGlyphs">Comprehensive Architectural</text>
<text x="261" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="36" fill="#89b4fa" textLength="234" lengthAdjust="spacingAndGlyphs">Implementation Details for</text>
<text x="540" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="36" fill="#89b4fa" textLength="216" lengthAdjust="spacingAndGlyphs">Longitudinal Performance</text>
<text x="810" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="36" fill="#89b4fa" textLength="252" lengthAdjust="spacingAndGlyphs">Strategic Security Framework</text>
<text x="1089" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1116" y="36" fill="#89b4fa" textLength="27" lengthAdjust="spacingAndGlyphs">Key</text>
<text x="1143" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1170" y="36" fill="#89b4fa" textLength="54" lengthAdjust="spacingAndGlyphs">Status</text>
<text x="1224" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1251" y="36" fill="#89b4fa" textLength="63" lengthAdjust="spacingAndGlyphs">Version</text>
<text x="1314" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1341" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Owner</text>
<text x="1395" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="53" fill="#89b4fa" textLength="189" lengthAdjust="spacingAndGlyphs">Specification for the</text>
<text x="261" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="53" fill="#89b4fa" textLength="171" lengthAdjust="spacingAndGlyphs">the High-Throughput</text>
<text x="540" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="53" fill="#89b4fa" textLength="135" lengthAdjust="spacingAndGlyphs">Analysis Across</text>
<text x="810" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="53" fill="#89b4fa" textLength="252" lengthAdjust="spacingAndGlyphs">for Mitigating Sophisticated</text>
<text x="1089" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="70" fill="#89b4fa" textLength="234" lengthAdjust="spacingAndGlyphs">Distributed Infrastructure</text>
<text x="261" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="70" fill="#89b4fa" textLength="180" lengthAdjust="spacingAndGlyphs">Asynchronous Message</text>
<text x="540" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="70" fill="#89b4fa" textLength="180" lengthAdjust="spacingAndGlyphs">Multi-Regional Cloud</text>
<text x="810" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="70" fill="#89b4fa" textLength="180" lengthAdjust="spacingAndGlyphs">Cross-Site Scripting</text>
<text x="1089" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="87" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Layer</text>
<text x="261" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="87" fill="#89b4fa" textLength="216" lengthAdjust="spacingAndGlyphs">Processing Pipeline with</text>
<text x="540" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="87" fill="#89b4fa" textLength="171" lengthAdjust="spacingAndGlyphs">Deployment Clusters</text>
<text x="810" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="87" fill="#89b4fa" textLength="135" lengthAdjust="spacingAndGlyphs">Vulnerabilities</text>
<text x="1089" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="104" fill="#89b4fa" textLength="180" lengthAdjust="spacingAndGlyphs">Extended Scalability</text>
<text x="540" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="121" fill="#89b4fa" textLength="207" lengthAdjust="spacingAndGlyphs">Features and Redundancy</text>
<text x="540" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="138" fill="#89b4fa" textLength="81" lengthAdjust="spacingAndGlyphs">Protocols</text>
<text x="540" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="155" fill="#333333" textLength="1404" lengthAdjust="spacingAndGlyphs">├─────────────────────────────┼──────────────────────────────┼─────────────────────────────┼──────────────────────────────┼─────┼────────┼─────────┼───────┤</text>
<text x="0" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="172" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> The primary architecture </text>
<text x="261" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="172" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">Each message is processed </text>
<text x="540" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="172" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">Historical data indicates a</text>
<text x="810" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="172" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">A multi-layered defense </text>
<text x="1089" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1116" y="172" fill="#ffffff" textLength="27" lengthAdjust="spacingAndGlyphs">INF</text>
<text x="1143" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1170" y="172" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs">Active</text>
<text x="1224" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1251" y="172" fill="#ffffff" textLength="63" lengthAdjust="spacingAndGlyphs">v2.4 </text>
<text x="1314" y="172" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1341" y="172" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs">J. </text>
<text x="1395" y="172" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="189" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> utilizes a decoupled </text>
<text x="261" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="189" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">through a series of </text>
<text x="540" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="189" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">significant reduction in </text>
<text x="810" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="189" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">strategy incorporates </text>
<text x="1089" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="189" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1341" y="189" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs">Doe </text>
<text x="1395" y="189" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="206" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> microservices approach, </text>
<text x="261" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="206" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">specialized workers that </text>
<text x="540" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="206" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">tail latency when utilizing</text>
<text x="810" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="206" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">content security policies, </text>
<text x="1089" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="206" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="206" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="223" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> leveraging container </text>
<text x="261" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="223" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">handle data transformation, </text>
<text x="540" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="223" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">edge computing nodes closer</text>
<text x="810" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="223" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">input sanitization </text>
<text x="1089" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="223" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="223" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="240" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> orchestration for </text>
<text x="261" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="240" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">validation, and persistent </text>
<text x="540" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="240" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">to the geographic location </text>
<text x="810" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="240" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">libraries, and regular </text>
<text x="1089" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="240" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="240" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="257" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> scalability and fault </text>
<text x="261" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="257" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">storage using a persistent </text>
<text x="540" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="257" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">of the end-user base. </text>
<text x="810" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="257" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">automated penetration </text>
<text x="1089" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="257" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="257" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="274" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> tolerance in high-load </text>
<text x="261" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="274" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">queue. </text>
<text x="540" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="274" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">testing routines. </text>
<text x="1089" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="274" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="274" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="291" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> scenarios. </text>
<text x="261" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="540" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="291" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">Monitoring tools have </text>
<text x="810" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="291" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="291" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="308" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">The pipeline features </text>
<text x="540" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="308" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">captured a steady increase </text>
<text x="810" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="308" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">Developers are required to </text>
<text x="1089" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="308" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="308" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="325" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> This layer provides the </text>
<text x="261" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="325" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">built-in retry mechanisms </text>
<text x="540" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="325" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">in throughput efficiency </text>
<text x="810" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="325" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">undergo mandatory security </text>
<text x="1089" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="325" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="325" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="342" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> fundamental building blocks</text>
<text x="261" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="342" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">with exponential backoff to </text>
<text x="540" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="342" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">since the introduction of </text>
<text x="810" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="342" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">training focusing on the </text>
<text x="1089" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="342" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="342" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="359" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> for service discovery, load</text>
<text x="261" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="359" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">ensure message delivery </text>
<text x="540" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="359" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">the vectorized query engine</text>
<text x="810" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="359" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">OWASP Top Ten to ensure that</text>
<text x="1089" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="359" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="359" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="376" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> balancing, and </text>
<text x="261" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="376" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">integrity even during </text>
<text x="540" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="376" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">in the primary data </text>
<text x="810" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="376" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">security is integrated into </text>
<text x="1089" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="376" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="376" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="393" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> inter-service communication</text>
<text x="261" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="393" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">transient network or service</text>
<text x="540" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="393" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">warehouse. </text>
<text x="810" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="393" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">the initial design phase. </text>
<text x="1089" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="393" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="393" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="410" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> via highly efficient </text>
<text x="261" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="410" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">failures. </text>
<text x="540" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="410" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="410" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="427" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> protocol buffers. </text>
<text x="261" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="540" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="427" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">Resource utilization </text>
<text x="810" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="427" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">The implementation of a </text>
<text x="1089" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="427" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="427" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="261" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="444" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">Horizontal autoscaling is </text>
<text x="540" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="444" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">metrics demonstrate that </text>
<text x="810" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="444" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">robust Identity and Access </text>
<text x="1089" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="444" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="444" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="461" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> Advanced telemetry and </text>
<text x="261" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="461" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">triggered automatically </text>
<text x="540" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="461" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">the transition to </text>
<text x="810" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="461" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">Management system ensures </text>
<text x="1089" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="461" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="461" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="478" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> logging integrations allow </text>
<text x="261" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="478" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">based on the depth of the </text>
<text x="540" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="478" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">serverless compute for </text>
<text x="810" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="478" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">that the principle of least </text>
<text x="1089" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="478" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="478" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="495" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> for real-time monitoring of</text>
<text x="261" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="495" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">processing queue, ensuring </text>
<text x="540" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="495" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">intermittent tasks has </text>
<text x="810" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="495" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">privilege is strictly </text>
<text x="1089" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="495" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="495" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="512" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> system health and rapid </text>
<text x="261" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="512" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">consistent performance </text>
<text x="540" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="512" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">resulted in a thirty </text>
<text x="810" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="512" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">enforced across all </text>
<text x="1089" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="512" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="512" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="529" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> identification of </text>
<text x="261" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="529" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">during unexpected traffic </text>
<text x="540" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="567" y="529" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">percent cost optimization. </text>
<text x="810" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="837" y="529" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">environments. </text>
<text x="1089" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="529" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="529" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="546" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> bottlenecks within the </text>
<text x="261" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="546" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs">spikes. </text>
<text x="540" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="546" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="546" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="563" fill="#ffffff" textLength="252" lengthAdjust="spacingAndGlyphs"> service mesh. </text>
<text x="261" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="540" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="810" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1089" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1143" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1224" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1314" y="563" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="1395" y="563" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="580" fill="#333333" textLength="1404" lengthAdjust="spacingAndGlyphs">└─────────────────────────────┴──────────────────────────────┴─────────────────────────────┴──────────────────────────────┴─────┴────────┴─────────┴───────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 43 KiB

@@ -1,63 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="190" viewBox="0 0 920 190">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="190" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="639" lengthAdjust="spacingAndGlyphs">┌───────────────┬───────────────┬──────────────────┬──────────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="81" lengthAdjust="spacingAndGlyphs">Very Long</text>
<text x="135" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="36" fill="#89b4fa" textLength="81" lengthAdjust="spacingAndGlyphs">Very Long</text>
<text x="279" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="306" y="36" fill="#89b4fa" textLength="144" lengthAdjust="spacingAndGlyphs">Very Long Column</text>
<text x="450" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="477" y="36" fill="#89b4fa" textLength="144" lengthAdjust="spacingAndGlyphs">Very Long Column</text>
<text x="630" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="53" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Column Header</text>
<text x="135" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="53" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Column Header</text>
<text x="279" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="306" y="53" fill="#89b4fa" textLength="108" lengthAdjust="spacingAndGlyphs">Header Three</text>
<text x="450" y="53" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="477" y="53" fill="#89b4fa" textLength="99" lengthAdjust="spacingAndGlyphs">Header Four</text>
<text x="630" y="53" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="70" fill="#89b4fa" textLength="27" lengthAdjust="spacingAndGlyphs">One</text>
<text x="135" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="70" fill="#89b4fa" textLength="27" lengthAdjust="spacingAndGlyphs">Two</text>
<text x="279" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="450" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="630" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="639" lengthAdjust="spacingAndGlyphs">├───────────────┼───────────────┼──────────────────┼──────────────────┤</text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="104" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs"> Data 1.1 </text>
<text x="135" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="104" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Data 1.2 </text>
<text x="279" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="306" y="104" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Data 1.3 </text>
<text x="450" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="477" y="104" fill="#ffffff" textLength="153" lengthAdjust="spacingAndGlyphs">Data 1.4 </text>
<text x="630" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="121" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs"> Data 2.1 </text>
<text x="135" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="121" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Data 2.2 </text>
<text x="279" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="306" y="121" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Data 2.3 </text>
<text x="450" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="477" y="121" fill="#ffffff" textLength="153" lengthAdjust="spacingAndGlyphs">Data 2.4 </text>
<text x="630" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="138" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs"> Data 3.1 </text>
<text x="135" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="138" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Data 3.2 </text>
<text x="279" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="306" y="138" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Data 3.3 </text>
<text x="450" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="477" y="138" fill="#ffffff" textLength="153" lengthAdjust="spacingAndGlyphs">Data 3.4 </text>
<text x="630" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="155" fill="#333333" textLength="639" lengthAdjust="spacingAndGlyphs">└───────────────┴───────────────┴──────────────────┴──────────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.5 KiB

@@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="740" height="139" viewBox="0 0 740 139">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="740" height="139" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="486" lengthAdjust="spacingAndGlyphs">┌───────────────┬───────────────────┬────────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="108" lengthAdjust="spacingAndGlyphs">Mixed 😃 中文</text>
<text x="126" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="36" fill="#89b4fa" textLength="144" lengthAdjust="spacingAndGlyphs">Complex 🚀 日本語</text>
<text x="297" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Text 📝 한국어</text>
<text x="450" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="486" lengthAdjust="spacingAndGlyphs">├───────────────┼───────────────────┼────────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> 你好 😃 </text>
<text x="126" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="70" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">こんにちは 🚀 </text>
<text x="297" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="70" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">안녕하세요 📝 </text>
<text x="450" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> World 🌍 </text>
<text x="126" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="87" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Code 💻 </text>
<text x="297" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="87" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">Pizza 🍕 </text>
<text x="450" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="486" lengthAdjust="spacingAndGlyphs">└───────────────┴───────────────────┴────────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

@@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="139" viewBox="0 0 560 139">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="560" height="139" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="450" lengthAdjust="spacingAndGlyphs">┌──────────────┬─────────────────┬───────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="108" lengthAdjust="spacingAndGlyphs">Chinese 中文</text>
<text x="126" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="36" fill="#89b4fa" textLength="135" lengthAdjust="spacingAndGlyphs">Japanese 日本語</text>
<text x="288" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="315" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Korean 한국어</text>
<text x="441" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="450" lengthAdjust="spacingAndGlyphs">├──────────────┼─────────────────┼───────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> 你好 </text>
<text x="126" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="70" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs">こんにちは </text>
<text x="288" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="315" y="70" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">안녕하세요 </text>
<text x="441" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs"> 世界 </text>
<text x="126" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="153" y="87" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs">世界 </text>
<text x="288" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="315" y="87" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">세계 </text>
<text x="441" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="450" lengthAdjust="spacingAndGlyphs">└──────────────┴─────────────────┴───────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

@@ -1,32 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="560" height="139" viewBox="0 0 560 139">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="560" height="139" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="315" lengthAdjust="spacingAndGlyphs">┌──────────┬───────────┬──────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="63" lengthAdjust="spacingAndGlyphs">Happy 😀</text>
<text x="81" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="108" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Rocket 🚀</text>
<text x="180" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="207" y="36" fill="#89b4fa" textLength="63" lengthAdjust="spacingAndGlyphs">Heart ❤️</text>
<text x="279" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="315" lengthAdjust="spacingAndGlyphs">├──────────┼───────────┼──────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs"> Smile 😃</text>
<text x="81" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="108" y="70" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Fire 🔥 </text>
<text x="180" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="207" y="70" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Love 💖 </text>
<text x="279" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs"> Cool 😎 </text>
<text x="81" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="108" y="87" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Star ⭐ </text>
<text x="180" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="207" y="87" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Blue 💙 </text>
<text x="279" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="315" lengthAdjust="spacingAndGlyphs">└──────────┴───────────┴──────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

@@ -1,19 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="122" viewBox="0 0 920 122">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="122" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="171" lengthAdjust="spacingAndGlyphs">┌────────┬────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="72" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="162" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="171" lengthAdjust="spacingAndGlyphs">├────────┼────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="63" lengthAdjust="spacingAndGlyphs"> Data 1</text>
<text x="72" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="99" y="70" fill="#ffffff" textLength="63" lengthAdjust="spacingAndGlyphs">Data 2 </text>
<text x="162" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="171" lengthAdjust="spacingAndGlyphs">└────────┴────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,24 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="122" viewBox="0 0 920 122">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="122" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="306" lengthAdjust="spacingAndGlyphs">┌──────────┬──────────┬──────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 1</text>
<text x="90" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="117" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 2</text>
<text x="189" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="216" y="36" fill="#89b4fa" textLength="72" lengthAdjust="spacingAndGlyphs">Header 3</text>
<text x="297" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="306" lengthAdjust="spacingAndGlyphs">├──────────┼──────────┼──────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="81" lengthAdjust="spacingAndGlyphs"> Data 1 </text>
<text x="90" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="117" y="70" fill="#ffffff" textLength="72" lengthAdjust="spacingAndGlyphs">Data 2 </text>
<text x="189" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="297" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="306" lengthAdjust="spacingAndGlyphs">└──────────┴──────────┴──────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

@@ -1,25 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="122" viewBox="0 0 920 122">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="122" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">┌─────────────┬───────────────┬──────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="99" lengthAdjust="spacingAndGlyphs">Bold Header</text>
<text x="117" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Normal Header</text>
<text x="261" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="36" fill="#89b4fa" textLength="108" lengthAdjust="spacingAndGlyphs">Another Bold</text>
<text x="405" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">├─────────────┼───────────────┼──────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="108" lengthAdjust="spacingAndGlyphs"> Data 1 </text>
<text x="117" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Data 2 </text>
<text x="261" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="288" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Data 3 </text>
<text x="405" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="414" lengthAdjust="spacingAndGlyphs">└─────────────┴───────────────┴──────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -1,52 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="190" viewBox="0 0 920 190">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="190" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="477" lengthAdjust="spacingAndGlyphs">┌────────────────┬────────────────┬─────────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 1</text>
<text x="144" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 2</text>
<text x="297" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 3</text>
<text x="468" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="477" lengthAdjust="spacingAndGlyphs">├────────────────┼────────────────┼─────────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs"> This is a very</text>
<text x="144" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="70" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">This is also a</text>
<text x="297" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="70" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">And this is the </text>
<text x="468" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs"> long text that</text>
<text x="144" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="87" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">very long text</text>
<text x="297" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="87" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">third long text </text>
<text x="468" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="104" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs"> needs wrapping</text>
<text x="144" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="104" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">that needs </text>
<text x="297" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="104" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">that needs </text>
<text x="468" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="121" fill="#ffffff" textLength="135" lengthAdjust="spacingAndGlyphs"> in column 1 </text>
<text x="144" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="121" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">wrapping in </text>
<text x="297" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="121" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">wrapping in </text>
<text x="468" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="144" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="171" y="138" fill="#ffffff" textLength="126" lengthAdjust="spacingAndGlyphs">column 2 </text>
<text x="297" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="324" y="138" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">column 3 </text>
<text x="468" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="155" fill="#333333" textLength="477" lengthAdjust="spacingAndGlyphs">└────────────────┴────────────────┴─────────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

@@ -1,51 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="190" viewBox="0 0 920 190">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="190" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="495" lengthAdjust="spacingAndGlyphs">┌───────────────────┬───────────────┬─────────────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Punctuation 1</text>
<text x="171" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="198" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Punctuation 2</text>
<text x="315" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="36" fill="#89b4fa" textLength="117" lengthAdjust="spacingAndGlyphs">Punctuation 3</text>
<text x="486" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="495" lengthAdjust="spacingAndGlyphs">├───────────────────┼───────────────┼─────────────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="162" lengthAdjust="spacingAndGlyphs"> Start. Stop. </text>
<text x="171" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="198" y="70" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Semi; colon: </text>
<text x="315" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="70" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">At@ Hash# </text>
<text x="486" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="87" fill="#ffffff" textLength="162" lengthAdjust="spacingAndGlyphs"> Comma, separated.</text>
<text x="171" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="198" y="87" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Pipe| Slash/ </text>
<text x="315" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="87" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Dollar$ </text>
<text x="486" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="104" fill="#ffffff" textLength="162" lengthAdjust="spacingAndGlyphs"> Exclamation! </text>
<text x="171" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="198" y="104" fill="#ffffff" textLength="117" lengthAdjust="spacingAndGlyphs">Backslash\ </text>
<text x="315" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="104" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Percent% Caret^ </text>
<text x="486" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="121" fill="#ffffff" textLength="162" lengthAdjust="spacingAndGlyphs"> Question? </text>
<text x="171" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="315" y="121" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="121" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Ampersand&amp; </text>
<text x="486" y="121" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="138" fill="#ffffff" textLength="162" lengthAdjust="spacingAndGlyphs"> hyphen-ated </text>
<text x="171" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="315" y="138" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="342" y="138" fill="#ffffff" textLength="144" lengthAdjust="spacingAndGlyphs">Asterisk* </text>
<text x="486" y="138" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="155" fill="#333333" textLength="495" lengthAdjust="spacingAndGlyphs">└───────────────────┴───────────────┴─────────────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

@@ -1,35 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="156" viewBox="0 0 920 156">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="156" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="423" lengthAdjust="spacingAndGlyphs">┌───────┬─────────────────────────────┬───────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 1</text>
<text x="63" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 2</text>
<text x="333" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="360" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Col 3</text>
<text x="414" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="423" lengthAdjust="spacingAndGlyphs">├───────┼─────────────────────────────┼───────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs"> Short</text>
<text x="63" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="70" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">This is a very long cell </text>
<text x="333" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="360" y="70" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs">Short </text>
<text x="414" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="87" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">content that should wrap to</text>
<text x="333" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="414" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="104" fill="#ffffff" textLength="243" lengthAdjust="spacingAndGlyphs">multiple lines </text>
<text x="333" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="414" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="423" lengthAdjust="spacingAndGlyphs">└───────┴─────────────────────────────┴───────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

@@ -1,36 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="920" height="156" viewBox="0 0 920 156">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="920" height="156" fill="#000000" />
<g transform="translate(10, 10)">
<text x="0" y="19" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">┌───────┬──────────────────────────┬────────┐</text>
<text x="0" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="18" y="36" fill="#89b4fa" textLength="45" lengthAdjust="spacingAndGlyphs">Short</text>
<text x="63" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="36" fill="#89b4fa" textLength="36" lengthAdjust="spacingAndGlyphs">Long</text>
<text x="306" y="36" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="333" y="36" fill="#89b4fa" textLength="54" lengthAdjust="spacingAndGlyphs">Medium</text>
<text x="396" y="36" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="53" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">├───────┼──────────────────────────┼────────┤</text>
<text x="0" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="9" y="70" fill="#ffffff" textLength="54" lengthAdjust="spacingAndGlyphs"> Tiny </text>
<text x="63" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="70" fill="#ffffff" textLength="216" lengthAdjust="spacingAndGlyphs">This is a very long text</text>
<text x="306" y="70" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="333" y="70" fill="#ffffff" textLength="63" lengthAdjust="spacingAndGlyphs">Not so </text>
<text x="396" y="70" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="87" fill="#ffffff" textLength="216" lengthAdjust="spacingAndGlyphs">that definitely needs to</text>
<text x="306" y="87" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="333" y="87" fill="#ffffff" textLength="63" lengthAdjust="spacingAndGlyphs">long </text>
<text x="396" y="87" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="90" y="104" fill="#ffffff" textLength="216" lengthAdjust="spacingAndGlyphs">wrap to the next line </text>
<text x="306" y="104" fill="#333333" textLength="27" lengthAdjust="spacingAndGlyphs"></text>
<text x="396" y="104" fill="#333333" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#333333" textLength="405" lengthAdjust="spacingAndGlyphs">└───────┴──────────────────────────┴────────┘</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.5 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-core",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"description": "Gemini CLI Core",
"license": "Apache-2.0",
"repository": {
-63
View File
@@ -284,68 +284,5 @@ describe('a2aUtils', () => {
'Analyzing...\n\nProcessing...\n\nArtifact (Code):\nprint("Done")',
);
});
it('should fallback to history in a task chunk if no message or artifacts exist and task is terminal', () => {
const reassembler = new A2AResultReassembler();
reassembler.update({
kind: 'task',
status: { state: 'completed' },
history: [
{
kind: 'message',
role: 'agent',
parts: [{ kind: 'text', text: 'Answer from history' }],
} as Message,
],
} as unknown as SendMessageResult);
expect(reassembler.toString()).toBe('Answer from history');
});
it('should NOT fallback to history in a task chunk if task is not terminal', () => {
const reassembler = new A2AResultReassembler();
reassembler.update({
kind: 'task',
status: { state: 'working' },
history: [
{
kind: 'message',
role: 'agent',
parts: [{ kind: 'text', text: 'Answer from history' }],
} as Message,
],
} as unknown as SendMessageResult);
expect(reassembler.toString()).toBe('');
});
it('should not fallback to history if artifacts exist', () => {
const reassembler = new A2AResultReassembler();
reassembler.update({
kind: 'task',
status: { state: 'completed' },
artifacts: [
{
artifactId: 'art-1',
name: 'Data',
parts: [{ kind: 'text', text: 'Artifact Content' }],
},
],
history: [
{
kind: 'message',
role: 'agent',
parts: [{ kind: 'text', text: 'Answer from history' }],
} as Message,
],
} as unknown as SendMessageResult);
const output = reassembler.toString();
expect(output).toContain('Artifact (Data):');
expect(output).not.toContain('Answer from history');
});
});
});
+2 -21
View File
@@ -74,26 +74,6 @@ export class A2AResultReassembler {
]);
}
}
// History Fallback: Some agent implementations do not populate the
// status.message in their final terminal response, instead archiving
// the final answer in the task's history array. To ensure we don't
// present an empty result, we fallback to the most recent agent message
// in the history only when the task is terminal and no other content
// (message log or artifacts) has been reassembled.
if (
isTerminalState(chunk.status?.state) &&
this.messageLog.length === 0 &&
this.artifacts.size === 0 &&
chunk.history &&
chunk.history.length > 0
) {
const lastAgentMsg = [...chunk.history]
.reverse()
.find((m) => m.role?.toLowerCase().includes('agent'));
if (lastAgentMsg) {
this.pushMessage(lastAgentMsg);
}
}
break;
case 'message': {
@@ -146,7 +126,7 @@ export class A2AResultReassembler {
* Handles Text, Data (JSON), and File parts.
*/
export function extractMessageText(message: Message | undefined): string {
if (!message || !message.parts || !Array.isArray(message.parts)) {
if (!message) {
return '';
}
@@ -178,6 +158,7 @@ function extractPartText(part: Part): string {
}
if (isDataPart(part)) {
// Attempt to format known data types if metadata exists, otherwise JSON stringify
return `Data: ${JSON.stringify(part.data)}`;
}
+2 -1
View File
@@ -24,7 +24,8 @@ export const GeneralistAgent = (
name: 'generalist',
displayName: 'Generalist Agent',
description:
'A general-purpose AI agent with access to all tools. Highly recommended for tasks that are turn-intensive or involve processing large amounts of data. Use this to keep the main session history lean and efficient. Excellent for: batch refactoring/error fixing across multiple files, running commands with high-volume output, and speculative investigations.',
"A general-purpose AI agent with access to all tools. Use it for complex tasks that don't fit into other specialized agents.",
experimental: true,
inputConfig: {
inputSchema: {
type: 'object',
+2 -4
View File
@@ -50,7 +50,6 @@ function makeMockedConfig(params?: Partial<ConfigParameters>): Config {
} as unknown as ToolRegistry);
vi.spyOn(config, 'getAgentRegistry').mockReturnValue({
getDirectoryContext: () => 'mock directory context',
getAllDefinitions: () => [],
} as unknown as AgentRegistry);
return config;
}
@@ -263,7 +262,6 @@ describe('AgentRegistry', () => {
overrides: {
codebase_investigator: { enabled: false },
cli_help: { enabled: false },
generalist: { enabled: false },
},
},
});
@@ -301,13 +299,13 @@ describe('AgentRegistry', () => {
expect(registry.getDefinition('cli_help')).toBeUndefined();
});
it('should register generalist agent by default', async () => {
it('should NOT register generalist agent by default (because it is experimental)', async () => {
const config = makeMockedConfig();
const registry = new TestableAgentRegistry(config);
await registry.initialize();
expect(registry.getDefinition('generalist')).toBeDefined();
expect(registry.getDefinition('generalist')).toBeUndefined();
});
it('should register generalist agent if explicitly enabled via override', async () => {
@@ -57,18 +57,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -221,18 +209,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -504,18 +480,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -668,18 +632,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -1554,18 +1506,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -1717,18 +1657,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -1872,18 +1800,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -2027,18 +1943,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -2178,18 +2082,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -2329,18 +2221,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -2472,18 +2352,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -2622,18 +2490,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>test-agent</name>
@@ -3014,18 +2870,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -3165,18 +3009,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -3428,18 +3260,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -3579,18 +3399,6 @@ Use the following guidelines to optimize your search and read patterns.
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directly—simple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
<subagent>
<name>mock-agent</name>
@@ -77,12 +77,6 @@ export function getAuthTypeFromEnv(): AuthType | undefined {
if (process.env['GEMINI_API_KEY']) {
return AuthType.USE_GEMINI;
}
if (
process.env['CLOUD_SHELL'] === 'true' ||
process.env['GEMINI_CLI_USE_COMPUTE_ADC'] === 'true'
) {
return AuthType.COMPUTE_ADC;
}
return undefined;
}
-53
View File
@@ -1032,59 +1032,6 @@ describe('GeminiChat', () => {
LlmRole.MAIN,
);
});
it('should flush transcript before tool dispatch for pure tool call with no text or thoughts', async () => {
const pureToolCallStream = (async function* () {
yield {
candidates: [
{
content: {
role: 'model',
parts: [
{
functionCall: {
name: 'read_file',
args: { path: 'test.py' },
},
},
],
},
},
],
} as unknown as GenerateContentResponse;
})();
vi.mocked(mockContentGenerator.generateContentStream).mockResolvedValue(
pureToolCallStream,
);
const { default: fs } = await import('node:fs');
const writeFileSync = vi.mocked(fs.writeFileSync);
const writeCountBefore = writeFileSync.mock.calls.length;
const stream = await chat.sendMessageStream(
{ model: 'test-model' },
'analyze test.py',
'prompt-id-pure-tool-flush',
new AbortController().signal,
LlmRole.MAIN,
);
for await (const _ of stream) {
// consume
}
const newWrites = writeFileSync.mock.calls.slice(writeCountBefore);
expect(newWrites.length).toBeGreaterThan(0);
const lastWriteData = JSON.parse(
newWrites[newWrites.length - 1][1] as string,
) as { messages: Array<{ type: string }> };
const geminiMessages = lastWriteData.messages.filter(
(m) => m.type === 'gemini',
);
expect(geminiMessages.length).toBeGreaterThan(0);
});
});
describe('addHistory', () => {
+2 -6
View File
@@ -818,7 +818,6 @@ export class GeminiChat {
const modelResponseParts: Part[] = [];
let hasToolCall = false;
let hasThoughts = false;
let finishReason: FinishReason | undefined;
for await (const chunk of streamResponse) {
@@ -835,7 +834,6 @@ export class GeminiChat {
if (content?.parts) {
if (content.parts.some((part) => part.thought)) {
// Record thoughts
hasThoughts = true;
this.recordThoughtFromContent(content);
}
if (content.parts.some((part) => part.functionCall)) {
@@ -903,10 +901,8 @@ export class GeminiChat {
.join('')
.trim();
// Record model response text from the collected parts.
// Also flush when there are thoughts or a tool call (even with no text)
// so that BeforeTool hooks always see the latest transcript state.
if (responseText || hasThoughts || hasToolCall) {
// Record model response text from the collected parts
if (responseText) {
this.chatRecordingService.recordMessage({
model,
type: 'gemini',
-12
View File
@@ -228,18 +228,6 @@ export function renderSubAgents(subAgents?: SubAgentOptions[]): string {
Sub-agents are specialized expert agents. Each sub-agent is available as a tool of the same name. You MUST delegate tasks to the sub-agent with the most relevant expertise.
### Strategic Orchestration & Delegation
Operate as a **strategic orchestrator**. Your own context window is your most precious resource. Every turn you take adds to the permanent session history. To keep the session fast and efficient, use sub-agents to "compress" complex or repetitive work.
When you delegate, the sub-agent's entire execution is consolidated into a single summary in your history, keeping your main loop lean.
**High-Impact Delegation Candidates:**
- **Repetitive Batch Tasks:** Tasks involving more than 3 files or repeated steps (e.g., "Add license headers to all files in src/", "Fix all lint errors in the project").
- **High-Volume Output:** Commands or tools expected to return large amounts of data (e.g., verbose builds, exhaustive file searches).
- **Speculative Research:** Investigations that require many "trial and error" steps before a clear path is found.
**Assertive Action:** Continue to handle "surgical" tasks directlysimple reads, single-file edits, or direct questions that can be resolved in 1-2 turns. Delegation is an efficiency tool, not a way to avoid direct action when it is the fastest path.
<available_subagents>
${subAgentsXml}
</available_subagents>
@@ -32,28 +32,6 @@ describe('sanitizeEnvironment', () => {
expect(sanitized).toEqual(env);
});
it('should allow TERM and COLORTERM environment variables', () => {
const env = {
TERM: 'xterm-256color',
COLORTERM: 'truecolor',
};
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
expect(sanitized).toEqual(env);
});
it('should preserve TERM and COLORTERM even in strict sanitization mode', () => {
const env = {
GITHUB_SHA: 'abc123',
TERM: 'xterm-256color',
COLORTERM: 'truecolor',
SOME_OTHER_VAR: 'value',
};
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
expect(sanitized['TERM']).toBe('xterm-256color');
expect(sanitized['COLORTERM']).toBe('truecolor');
expect(sanitized['SOME_OTHER_VAR']).toBeUndefined();
});
it('should allow variables prefixed with GEMINI_CLI_', () => {
const env = {
GEMINI_CLI_FOO: 'bar',
@@ -71,10 +71,6 @@ export const ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES: ReadonlySet<string> =
'TMPDIR',
'USER',
'LOGNAME',
// Terminal capability variables (needed by editors like vim/emacs and
// interactive commands like top)
'TERM',
'COLORTERM',
// GitHub Action-related variables
'ADDITIONAL_CONTEXT',
'AVAILABLE_LABELS',
@@ -313,11 +313,10 @@ export class ShellExecutionService {
shell: false,
detached: !isWindows,
env: {
TERM: 'xterm-256color',
COLORTERM: 'truecolor',
...sanitizeEnvironment(process.env, sanitizationConfig),
[GEMINI_CLI_IDENTIFICATION_ENV_VAR]:
GEMINI_CLI_IDENTIFICATION_ENV_VAR_VALUE,
TERM: 'xterm-256color',
PAGER: 'cat',
GIT_PAGER: 'cat',
},
@@ -576,13 +575,12 @@ export class ShellExecutionService {
cols,
rows,
env: {
TERM: 'xterm-256color',
COLORTERM: 'truecolor',
...sanitizeEnvironment(
process.env,
shellExecutionConfig.sanitizationConfig,
),
GEMINI_CLI: '1',
TERM: 'xterm-256color',
PAGER: shellExecutionConfig.pager ?? 'cat',
GIT_PAGER: shellExecutionConfig.pager ?? 'cat',
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-devtools",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"license": "Apache-2.0",
"type": "module",
"main": "dist/src/index.js",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-sdk",
"version": "0.29.0-nightly.20260203.71f46f116",
"version": "0.30.0-test-github-release",
"description": "Gemini CLI SDK",
"license": "Apache-2.0",
"repository": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@google/gemini-cli-test-utils",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"private": true,
"main": "src/index.ts",
"license": "Apache-2.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "gemini-cli-vscode-ide-companion",
"displayName": "Gemini CLI Companion",
"description": "Enable Gemini CLI with direct access to your IDE workspace.",
"version": "0.30.0-nightly.20260210.a2174751d",
"version": "0.30.0-test-github-release",
"publisher": "google",
"icon": "assets/icon.png",
"repository": {