Skip to content

Commit c434e20

Browse files
committed
Sync adapter docs with current Gemini CLI contract
1 parent 4047daf commit c434e20

File tree

6 files changed

+32
-19
lines changed

6 files changed

+32
-19
lines changed

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ If no new rule is detected -> do not update the file.
9191
- Upstream sync automation must track real `google-gemini/gemini-cli` CLI changes (flags/models/features), not TypeScript SDK surface diffs, and open actionable repository issues for required SDK follow-up.
9292
- Automatically opened upstream sync issues must include change summary/checklist and assign Copilot by default.
9393
- For `google-gemini/gemini-cli` repo sync/update work, always inspect `submodules/google-gemini-cli/gemini-rs/core/models.json` and reconcile SDK model constants against that bundled catalog because it is the repo-authoritative model source.
94+
- When adapting the SDK to upstream Gemini CLI changes, prioritize reflecting real CLI-specific behavior while keeping the `GeminiClient` / `GeminiThread` contract coherent with `GeminiSharpSDK.Extensions.AI` and `GeminiSharpSDK.Extensions.AgentFramework`.
9495
- At the end of implementation/code-change tasks, create a git commit unless the user explicitly says not to, so the workspace ends in a reviewable state.
9596
- Run verification in this order:
9697
- focused tests for changed behavior
@@ -132,6 +133,7 @@ If no new rule is detected -> do not update the file.
132133
- CI/release full-solution runs must exclude auth-required tests using `-- --treenode-filter "/*/*/*/*[RequiresGeminiAuth!=true]"` so pipelines remain non-auth and deterministic.
133134
- Cross-platform non-auth smoke must run `gemini` from local installation in CI and verify unauthenticated behavior explicitly (for example `gemini login status` in isolated profile returns "Not logged in"), proving binary discovery + process launch on each platform.
134135
- Real Gemini integration tests must rely on existing local Gemini CLI login/session only; do not read or require `OPENAI_API_KEY` in test setup.
136+
- Adapter regression coverage is critical: when CLI contract changes affect streaming/events/options, keep the `Extensions.AI` and `Extensions.AgentFramework` tests green in the same pass.
135137
- Do not use nullable `TryGetSettings()` + early `return` skip patterns in real integration tests; resolve required settings directly and fail fast with actionable errors when missing.
136138
- Do not bypass integration tests on Windows with unconditional early returns; keep tests cross-platform for supported Gemini CLI environments.
137139
- Parser changes require tests in `ThreadEventParserTests` for supported and invalid payloads.

docs/ADR/001-codex-cli-wrapper.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
## Context
77

8-
Gemini is CLI-oriented and communicates via `gemini exec --json` with JSONL events.
8+
Gemini is CLI-oriented and the current headless contract is the root command
9+
`gemini --prompt ... --output-format stream-json`, which emits newline-delimited JSON events.
910
To keep behavior parity and reduce protocol drift, this .NET SDK needs a transport strategy aligned with real CLI behavior.
1011

1112
## Decision
@@ -14,16 +15,16 @@ Use the local Gemini CLI process as the only runtime transport layer for `Manage
1415

1516
- `GeminiExec` builds argument order and environment variables.
1617
- `DefaultGeminiProcessRunner` starts process and streams stdout lines asynchronously.
17-
- `ThreadEventParser` maps JSONL protocol to strongly typed events and items.
18+
- `ThreadEventParser` maps `stream-json` events to strongly typed events and items.
1819

1920
## Diagram
2021

2122
```mermaid
2223
flowchart LR
2324
SDK["ManagedCode.GeminiSharpSDK"] --> Exec["GeminiExec"]
24-
Exec --> Cli["gemini exec --json"]
25-
Cli --> Jsonl["stdout JSONL"]
26-
Jsonl --> Parser["ThreadEventParser"]
25+
Exec --> Cli["gemini --prompt ... --output-format stream-json"]
26+
Cli --> Json["stdout stream-json"]
27+
Json --> Parser["ThreadEventParser"]
2728
Parser --> Models["ThreadEvent / ThreadItem"]
2829
```
2930

@@ -33,12 +34,13 @@ flowchart LR
3334

3435
- High parity with upstream CLI behavior.
3536
- No separate protocol server to maintain.
36-
- Easy compatibility when Gemini CLI adds flags/events.
37+
- Easy compatibility when Gemini CLI adds headless flags/events.
3738

3839
### Negative
3940

4041
- Requires `gemini` binary availability in environment.
4142
- Runtime errors may come from external process failures.
43+
- Unsupported headless-only option gaps must fail explicitly instead of being guessed by the SDK.
4244

4345
### Neutral
4446

docs/ADR/002-protocol-parsing-and-thread-serialization.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@
55

66
## Context
77

8-
Gemini CLI emits dynamic JSONL events (`thread.started`, `item.completed`, etc.).
8+
Gemini CLI now emits `stream-json` events such as `init`, `message`, `tool_use`, `tool_result`, `result`, and `error`.
99
Without strict parsing rules and synchronized turn execution, SDK behavior can diverge under concurrency and protocol evolution.
1010
User rule in this repository explicitly forbids inline string literals for protocol token matching.
1111

1212
## Decision
1313

1414
1. Centralize protocol tokens in `GeminiProtocolConstants`.
1515
2. Parse events/items only through constant-based switches.
16-
3. Serialize execution per `GeminiThread` instance with `SemaphoreSlim`.
16+
3. Support the current `stream-json` contract first, while tolerating a limited set of legacy persisted fixtures/events where needed for backward-compatible tests.
17+
4. Serialize execution per `GeminiThread` instance with `SemaphoreSlim`.
1718

1819
## Diagram
1920

2021
```mermaid
2122
flowchart LR
22-
Line["JSONL line"] --> Parse["ThreadEventParser"]
23+
Line["stream-json line"] --> Parse["ThreadEventParser"]
2324
Parse --> Consts["GeminiProtocolConstants"]
2425
GeminiThread["GeminiThread.Run*Async"] --> Lock["SemaphoreSlim turn lock"]
2526
Lock --> Exec["GeminiExec.RunAsync"]
@@ -33,10 +34,11 @@ flowchart LR
3334
- No magic literals in parser logic.
3435
- Safer maintenance when protocol tokens change.
3536
- Eliminates race conditions for same thread instance.
37+
- Current runtime and test fixtures can evolve independently without hidden parser drift.
3638

3739
### Negative
3840

39-
- Additional constants maintenance when upstream adds new token names.
41+
- Additional constants maintenance when upstream adds new token names or retires old ones.
4042

4143
### Neutral
4244

docs/ADR/003-microsoft-extensions-ai-integration.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Implement `IChatClient` from `Microsoft.Extensions.AI.Abstractions` in a **separ
1717

1818
2. **Custom AIContent types** — Rich Gemini items (command execution, file changes, MCP tool calls, web searches, multi-agent collaboration) are surfaced as typed `AIContent` subclasses rather than being flattened to text. This preserves full fidelity of Gemini output.
1919

20-
3. **Gemini-specific options via AdditionalProperties** — Standard `ChatOptions` properties (`ModelId`, `ConversationId`) map directly. Gemini-unique features use `gemini:*` prefixed keys in `ChatOptions.AdditionalProperties` (e.g., `gemini:sandbox_mode`, `gemini:reasoning_effort`).
20+
3. **Gemini-specific options via AdditionalProperties** — Standard `ChatOptions` properties (`ModelId`, `ConversationId`) map directly. Gemini-unique features use `gemini:*` prefixed keys in `ChatOptions.AdditionalProperties` (for example `gemini:sandbox_mode`). Options not supported by the current headless CLI contract fail fast instead of silently degrading.
2121

2222
4. **Thread-per-call with ConversationId resume** — Each `GetResponseAsync` call creates or resumes a `GeminiThread`. Thread ID flows via `ChatResponse.ConversationId` for multi-turn continuity.
2323

@@ -30,7 +30,7 @@ flowchart LR
3030
Consumer["Consumer code\n(IChatClient)"]
3131
Adapter["GeminiChatClient\n(Extensions.AI)"]
3232
Core["GeminiClient\n(Core SDK)"]
33-
CLI["gemini exec --json"]
33+
CLI["gemini --prompt ... --output-format stream-json"]
3434
3535
Consumer --> Adapter
3636
Adapter --> Core
@@ -55,12 +55,13 @@ flowchart LR
5555
- SDK participates in .NET AI ecosystem: DI registration, middleware pipelines, provider swapping.
5656
- Consumers get logging, caching, and telemetry for free via M.E.AI middleware.
5757
- Rich Gemini items preserved as typed content, not lost.
58+
- Adapter behavior stays aligned with the real installed CLI contract instead of a guessed chat abstraction.
5859

5960
### Negative
6061

6162
- Impedance mismatch: Gemini is an agentic coding tool, not a simple chat API. Multi-turn via message history doesn't map cleanly (uses thread resume instead).
62-
- No temperature/topP/topK (Gemini uses `ModelReasoningEffort`).
63-
- Streaming is item-level, not token-level.
63+
- Current headless CLI does not expose generic chat-tuning knobs such as temperature/topP/topK, and unsupported options are rejected explicitly.
64+
- Streaming is event-level (`init`/`message`/`tool_use`/`tool_result`/`result`), not token-level.
6465

6566
### Neutral
6667

docs/Features/agent-framework-integration.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ Enable GeminiSharpSDK consumers to use Microsoft Agent Framework (`AIAgent`) on
4848

4949
- New custom `AIAgent` runtime implementation parallel to `ChatClientAgent`
5050
- Microsoft Agent Framework hosting/workflows helpers (`Microsoft.Agents.AI.Hosting`, durable agents, DevUI)
51-
- Changes to core SDK execution, thread state, parsing, or CLI contracts
51+
- Redefining core SDK execution, thread state, parsing, or CLI contracts beyond consuming the already-supported `stream-json` runtime
5252

5353
---
5454

@@ -102,6 +102,7 @@ Enable GeminiSharpSDK consumers to use Microsoft Agent Framework (`AIAgent`) on
102102
- Side effects / emitted events: creates singleton `GeminiChatClient` and singleton/keyed `ChatClientAgent`
103103
- Idempotency: follows standard DI additive registration semantics; repeated registration adds additional descriptors
104104
- Error handling: null guard exceptions on registration inputs; runtime agent execution errors are delegated to existing `GeminiChatClient` / MAF behaviour
105+
- CLI specificity: the agent layer inherits the current Gemini headless contract (`--prompt ... --output-format stream-json`) through `GeminiChatClient`; this package does not introduce a second transport model
105106
- Security / permissions: no new permissions beyond existing `gemini` CLI prerequisites; MAF function invocation remains opt-in through consumer-supplied tools/options
106107
- Feature flags / toggles: none
107108
- Performance / SLAs: registration-time object construction only; no extra background work
@@ -118,7 +119,7 @@ flowchart LR
118119
MAF["ChatClientAgent (MAF)"]
119120
MEAI["GeminiChatClient (IChatClient)"]
120121
Core["GeminiClient / GeminiThread"]
121-
CLI["gemini exec --json"]
122+
CLI["gemini --prompt ... --output-format stream-json"]
122123
123124
Consumer --> DI
124125
DI --> MAF
@@ -167,6 +168,7 @@ flowchart LR
167168
| --- | --- | --- | --- | --- |
168169
| EDGE-001 | MAF decorates `IChatClient` with middleware | Unit | Agent-resolved chat client still exposes `ChatClientMetadata` with provider `GeminiCLI` | `GeminiAgentServiceCollectionExtensionsTests.AddGeminiAIAgent_RegistersAIAgentAndChatClient`, `AddKeyedGeminiAIAgent_RegistersKeyedAgent` |
169170
| EDGE-002 | Keyed agent configuration preserves instructions and default model | Unit | `ChatClientAgentOptions` and `ChatClientMetadata` reflect configured values | `GeminiAgentServiceCollectionExtensionsTests.AddKeyedGeminiAIAgent_WithConfiguration_AppliesKeyedAgentOptions` |
171+
| EDGE-003 | Agent execution uses the same CLI-specific chat adapter contract as direct `IChatClient` usage | Unit | No alternate transport or extra provider abstraction is introduced by the agent layer | covered by DI composition tests over `GeminiChatClient` |
170172

171173
### Test mapping
172174

docs/Features/meai-integration.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Enable GeminiSharpSDK to participate as a first-class provider in the `Microsoft
3030
- `IEmbeddingGenerator` (Gemini CLI is not an embedding service)
3131
- `IImageGenerator` (Gemini CLI is not an image generator)
3232
- Consumer-side `AITool` registration (Gemini manages tools internally)
33-
- `Temperature`, `TopP`, `TopK` mapping (Gemini uses `ModelReasoningEffort`)
33+
- Generic tuning mappings not exposed by the current headless Gemini CLI contract (for example `Temperature`, `TopP`, `TopK`, and unsupported reasoning flags)
3434

3535
---
3636

@@ -41,8 +41,9 @@ Enable GeminiSharpSDK to participate as a first-class provider in the `Microsoft
4141
- Multiple `ChatMessage` entries are concatenated into a single prompt while preserving original message chronology (Gemini CLI is single-prompt-per-turn).
4242
- `ChatOptions.Tools` is silently ignored; tool results surface as custom `AIContent` types.
4343
- `GetService<ChatClientMetadata>()` returns provider name `"GeminiCLI"` with default model from options.
44-
- Streaming events map item-level, not token-level.
45-
- Turn failures (`TurnFailedEvent`) propagate as `InvalidOperationException`.
44+
- Streaming maps the real CLI event sequence (`init`, `message`, `tool_use`, `tool_result`, `result`, `error`), not token-level deltas.
45+
- Unsupported CLI options fail fast when `GeminiThread` cannot express them through the current headless contract.
46+
- Turn failures propagate from CLI `error` events or process/runtime failures as `InvalidOperationException`.
4647

4748
---
4849

@@ -148,12 +149,14 @@ flowchart LR
148149
MsgMapper["ChatMessageMapper"]
149150
OptMapper["ChatOptionsMapper"]
150151
Thread["GeminiThread.RunAsync"]
152+
Cli["gemini --prompt ... --output-format stream-json"]
151153
RespMapper["ChatResponseMapper"]
152154
Output["ChatResponse"]
153155
154156
Input --> MsgMapper
155157
MsgMapper --> Thread
156158
OptMapper --> Thread
159+
Thread --> Cli
157160
Thread --> RespMapper
158161
RespMapper --> Output
159162
```
@@ -172,6 +175,7 @@ flowchart LR
172175

173176
- Mapper tests: `GeminiSharpSDK.Extensions.AI.Tests/ChatMessageMapperTests.cs`, `ChatOptionsMapperTests.cs`, `ChatResponseMapperTests.cs`, `StreamingEventMapperTests.cs`
174177
- DI tests: `GeminiSharpSDK.Extensions.AI.Tests/GeminiServiceCollectionExtensionsTests.cs`
178+
- Adapter regression tests must stay aligned with the current real CLI event contract and the Agent Framework composition layer.
175179

176180
---
177181

0 commit comments

Comments
 (0)