Commit Graph

4 Commits

Author SHA1 Message Date
Sergio Sánchez Zavala 323b6220cc Fix Gemini headless invocation stalls (#8368)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Local adapters turn Paperclip runs into unattended CLI invocations.
> - The `gemini_local` adapter depends on Gemini CLI behaving
non-interactively in headless worker sessions.
> - When Gemini CLI falls back to browser-based auth, the process can
stall at startup instead of producing stream-json output.
> - The adapter should make headless intent explicit and turn missing
auth into a classified, fast failure.
> - This pull request hardens the Gemini child-process environment and
parser classification around that failure mode.
> - The benefit is that operators get actionable `gemini_auth_required`
failures instead of silent hung runs.

## Linked Issues or Issue Description

No exact public issue exists for this runtime stall.

Related: #2344 covers a Gemini CLI adapter environment/auth probe
failure. This PR addresses a different runtime path: unattended
`gemini_local` executions that can stall when Gemini CLI attempts
interactive browser auth in a headless session.

Bug summary:
- Adapter: `gemini_local`
- Symptom: child Gemini CLI process starts but produces no stream-json
output when auth requires interactive browser flow
- Expected: unattended runs either produce stream-json output or fail
quickly with a classified auth error
- Actual: the run can hang at invocation until an external watchdog
kills it
- Scope: Gemini adapter process env, auth-required parsing, and adapter
docs/tests

Duplicate search performed:
- `gh pr list --state all --search "gemini headless invocation stall"`
found only this PR.
- `gh pr list --state all --search "gemini NO_BROWSER NO_COLOR"` found
only this PR.
- `gh issue list --state all --search "gemini headless authentication"`
found related issue #2344 but no exact duplicate.

## What Changed

- Set a headless-safe Gemini invocation env at the final child-process
boundary: `TERM=xterm-256color`, `COLORTERM=truecolor`, and
`NO_BROWSER=1`.
- Delete inherited `NO_COLOR` from the child env so Gemini CLI keeps
color-capable terminal behavior when deciding whether it can run
non-interactively.
- Classify Gemini `FatalAuthenticationError: Manual authorization is
required...` failures as `gemini_auth_required`.
- Update Gemini adapter docs to describe the non-interactive `--prompt`
path and headless env behavior.
- Add/extend focused parser and remote execution tests for the
auth-required and env invariants.

## Verification

- `pnpm exec vitest run
packages/adapters/gemini-local/src/server/parse.test.ts
packages/adapters/gemini-local/src/server/execute.remote.test.ts` — 23
tests passed.
- `pnpm --filter @paperclipai/adapter-gemini-local typecheck` — passed.
- Local Gemini CLI probe confirmed `NO_BROWSER=1` turns the browser-auth
stall into a fast auth error instead of an interactive wait.

## Risks

Low risk. The change is limited to `gemini_local` invocation env,
parsing, docs, and tests. It does not touch schemas, API routes,
persisted data, or other adapters.

Operational risk: environments that intentionally rely on `NO_COLOR`
being inherited by Gemini child processes will no longer pass that
variable through. That is intentional here because Gemini CLI
auth/headless behavior should not depend on inherited color suppression.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

OpenAI GPT-5 Codex via Codex CLI, with shell/file-editing tool use for
repository inspection, code edits, tests, and GitHub PR maintenance.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

/cc @codex — please review.
2026-06-19 21:31:36 -07:00
Sherif Kozman b8fb81dee9 fix(gemini-local): treat token-overflow as a fresh-session signal (#4932)
## Thinking Path

The same 2026-04-30 audit that produced PR #4118 (`Invalid session`
regex extension) and the ENOTFOUND classifier (#4931) identified a third
stuck-session pattern: **13 failures in 7 days, all on a single agent
(Ernest)**, with stderr matching:

```
_ApiError: {"error":{"code":400,"message":"The input token count exceeds
the maximum number of tokens allowed 1048576","status":"INVALID_ARGUMENT"}}
  at ChatCompressionService.compress
```

The root cause is that gemini-cli's `ChatCompressionService` blew the 1M
token context limit **during its compression step itself**. Resuming the
same session ID will hit the same wall on the next attempt — the session
is effectively dead the same way it is when "Invalid session identifier"
fires (PR #4118).

## What Changed

Extends the `isGeminiUnknownSessionError` regex in `parse.ts` with two
phrases:
- `exceeds\s+the\s+maximum\s+number\s+of\s+tokens`
- `input\s+token\s+count\s+exceeds`

Both trigger the **existing** fresh-session retry path in
`execute.ts:596` — no new code path. Same extension pattern as PR #4118.

## Verification

- `npx vitest run --project @paperclipai/adapter-gemini-local` → 14/14
pass (11 in `parse.test.ts` + 3 existing in `execute.remote.test.ts`)
- 2 new tests cover the token-overflow patterns
- `pnpm --filter @paperclipai/adapter-gemini-local typecheck` → clean
- Audit query against `heartbeat_runs.stderr_excerpt` confirms regex
matches all 13 occurrences

## Stacking

This PR is stacked on top of #4931 (the ENOTFOUND classifier) which adds
the `parse.test.ts` file. If #4931 merges first, this PR's diff is just
the regex + 2 tests. If this PR is reviewed first, please merge #4931
first to avoid touching the same test scaffolding twice.

## Risks

- **Low.** Single-line regex extension. No new code paths.
- The session-reset path is well-trodden (PR #4118 in flight).
- If a non-Gemini caller produces a stderr containing "exceeds the
maximum number of tokens" by coincidence, they would trigger one
unnecessary fresh-session retry. Not plausible in the gemini-cli output
context where this stderr is sourced.

## Model Used

Claude Opus 4.7 (1M context), Anthropic SDK via Claude Code CLI.

## Checklist

- [x] Thinking path traces from audit data to single-line regex change
- [x] Model specified
- [x] No duplicate of planned core work
- [x] Tests pass locally
- [x] Tests added (2 new)
- [x] N/A — server-side regex
- [x] Internal pattern; no docs change
- [x] Risks documented
- [x] Will address Greptile + reviewer comments before merge
- [x] I searched the GitHub PR list for similar PRs and confirmed this
is not a duplicate (related: #4118 covers the "Invalid session
identifier" regex; this PR extends the same regex with token-overflow
phrases)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Devin Foley <devin@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-06-10 19:10:29 -07:00
Devin Foley ea7f53fd7d Handle Gemini CLI v0.38 stream-json wire format across parser, UI, and CLI formatter (#5273)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies
> - Each agent uses an adapter that drives a CLI (Claude, Gemini, Codex,
etc.)
> - The Gemini adapter parses a JSONL transcript stream the CLI emits to
learn what the model said
> - Gemini CLI v0.38 changed the transcript shape: assistant text now
comes through `type=message` with `role`/`content` and terminal status
comes through `type=status` / `type=stats`
> - The existing parser was written against the older `type=assistant` /
`type=result` shape, so post-v0.38 outputs left the parsed summary empty
and downgraded the SSH hello probe to "unexpected output"
> - This pull request updates every Gemini consumer (server parser, UI
parser, CLI formatter) to accept the v0.38 shape while keeping the
legacy shape working
> - The benefit is the Gemini adapter handles current upstream output
without losing backward compatibility, with explicit test coverage for
both shapes

## What Changed

- `packages/adapters/gemini-local/src/server/parse.ts` recognizes
`type=message` events with role/content and stops downgrading them
- `packages/adapters/gemini-local/src/ui/parse-stdout.ts` mirrors the
parser changes for the live UI transcript
- `packages/adapters/gemini-local/src/cli/format-event.ts` formats the
new event shape correctly for CLI output
- `parse.test.ts` and `parse-stdout.test.ts` add v0.38 coverage;
`gemini-local-adapter.test.ts` and `execute.remote.test.ts` switch
happy-path fixtures to the current real wire format and keep dedicated
tests for the older schema

## Verification

- `pnpm vitest run --no-coverage --project
@paperclipai/adapter-gemini-local` — full suite passes including new
v0.38 cases and preserved legacy cases
- `pnpm typecheck` clean

## Risks

Low risk — additive event handling. Legacy event shape path is preserved
with its own tests, so existing fixtures continue to parse identically.

## Model Used

Claude Opus 4.7 (1M context)

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots — N/A (no UI)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-05-05 08:00:14 -07:00
Devin Foley a5430f010d Handle Gemini assistant message events in JSONL parser (#5143)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies, including
agents
>   running the Gemini CLI (`gemini-local` adapter)
> - The Gemini CLI emits a JSONL event stream during a run that the
adapter
> parses to extract the assistant's response text, tool results, and
usage
> - Recent versions of the Gemini CLI emit assistant responses as
> `{ "type": "message", "role": "assistant", "content": ... }` events in
>   addition to the previously-handled event shapes
> - The parser was not handling the new event type, so the assistant's
actual
> response text was being silently dropped from parsed output. Callers
ended
>   up with empty assistant messages even when Gemini had successfully
>   responded
> - This PR teaches the parser to recognize `{type: "message", role:
>   "assistant"}` events and extract their content text via the same
>   `collectMessageText` helper used for other message-shaped events
> - The benefit is that Gemini runs surface the assistant's real
response in
> downstream consumers (issue comments, run logs, downstream agent
context)
>   instead of vanishing

## What Changed

- `packages/adapters/gemini-local/src/server/parse.ts`: in
`parseGeminiJsonl(...)`, add a branch for `event.type === "message"`
with
  `role === "assistant"` that calls
  `messages.push(...collectMessageText(event.content))`.
- `packages/adapters/gemini-local/src/server/parse.test.ts`: ~19 lines
of
  coverage for the new branch.

## Verification

- `pnpm --filter @paperclipai/adapter-gemini-local test -- parse`
- Manual QA: run a Gemini agent on an issue, confirm the assistant's
response
appears as the issue comment / run output. Before this fix the comment
was
  empty even when the run completed successfully.

## Risks

- Tightly scoped: 8 lines of production code in one parser branch. No
effect
  on existing event shapes or other adapters.
- If the Gemini CLI changes its event schema again, this branch may need
to be
  revisited — but adding it is strictly additive over current behaviour.

## Model Used

- OpenAI GPT-5.4 (reasoning effort: high) via Codex CLI
- Provider: OpenAI
- Used to author the code changes in this PR

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots — N/A
- [ ] I have updated relevant documentation to reflect my changes — N/A
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-05-03 18:36:50 -07:00