文件历史

37 次代码提交

作者 SHA1 备注 提交日期
Ali Khokhar af12e7b2bb Coordinate provider recovery under concurrent load (#1205)
## Problem

Concurrent transient failures could start independent retry, replay,
continuation, and repair loops while holding provider concurrency slots.
This multiplied upstream attempts and could delay or strand terminal
errors under fan-out.

## Changes

| Before | After |
| --- | --- |
| Retry paths owned separate attempt budgets. | One logical-execution
session caps all upstream work at five attempts. |
| Concurrent failures backed off independently. | One provider-owned
recovery episode elects a single half-open probe while followers
coalesce. |
| Backoff occupied stream concurrency. | Concurrency is held only while
an upstream operation or stream is active. |
| Provider catalog calls and stream creation used separate admission
paths. | Every upstream operation uses one provider-generation admission
controller. |
| Cancellation could leave recovery ownership or follower state
unresolved. | Cancellation releases permits, transfers probe ownership,
and unregisters waiting followers. |
| Late in-flight failures could cross an exhausted episode boundary. |
Every coalesced execution retains that generation's terminal outcome. |
| Replay tests allowed loose lifecycle assertions. | Exact SSE contracts
prove retries and continuations emit one unduplicated response. |
| Recovery wrappers could mask final diagnostics. | Final responses and
traces retain the raw provider failure and request ID. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR coordinates provider recovery and retry work under concurrent
load. The main changes are:

- One five-attempt budget for each logical execution.
- Provider-wide recovery episodes with one elected probe.
- Shared admission for streams, catalog calls, rate limits, and
concurrency.
- Concurrency permits held only during active upstream work.
- Cancellation-safe probe ownership and preserved final diagnostics.
</details>

<h3>Confidence Score: 5/5</h3>

This looks safe to merge.

No blocking issues found in the changed code.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- Reviewed the coordinated-recovery-01-before.log to understand how the
exhausted generation outcome was not preserved in a late in-flight
failure.
- Reviewed the coordinated-recovery-02-after.log to confirm that the
updated implementation preserves the exhausted generation outcome for
the same focused contract set.
- Validated that the provider-admission-full-current.log shows the
complete requested test file passed under Python 3.14 with uv run pytest
-n 0.

<a
href="https://app.greptile.com/trex/runs/15050270/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/providers/admission.py | Adds shared admission,
retry budgets, recovery episodes, probe election, and cancellation
handling. |
| src/free_claude_code/providers/openai_chat/provider.py | Moves stream
creation, replay, continuation, and repair onto one admission-owned
retry session. |
| src/free_claude_code/providers/stream_recovery.py | Selects replay,
continuation, repair, or final failure using the remaining shared
attempt budget. |
| src/free_claude_code/providers/failure_policy.py | Adds recovery
exhaustion handling and preserves the underlying provider error for
final classification. |
| src/free_claude_code/providers/runtime/factory.py | Creates one
admission controller per provider generation and passes it through
provider factories. |

</details>

<details open><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant E as Execution
participant A as Admission controller
participant P as Provider
participant F as Concurrent follower

E->>A: Open attempt
A->>P: Send upstream request
P-->>E: Retryable failure
E->>A: Open recovery episode
F->>A: Request admission
A-->>F: Coalesce and wait
E->>A: Claim probe
A->>P: Send half-open probe
alt Probe succeeds
    P-->>E: Valid response
    E->>A: Close recovery episode
    A-->>F: Release waiter
else Probe fails
    P-->>E: Retryable failure
    E->>A: Schedule next probe or finalize error
end
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant E as Execution
participant A as Admission controller
participant P as Provider
participant F as Concurrent follower

E->>A: Open attempt
A->>P: Send upstream request
P-->>E: Retryable failure
E->>A: Open recovery episode
F->>A: Request admission
A-->>F: Coalesce and wait
E->>A: Claim probe
A->>P: Send half-open probe
alt Probe succeeds
    P-->>E: Valid response
    E->>A: Close recovery episode
    A-->>F: Release waiter
else Probe fails
    P-->>E: Retryable failure
    E->>A: Schedule next probe or finalize error
end
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["Harden coordinated retry
lifecycle
invar..."](https://github.com/alishahryar1/free-claude-code/commit/2e871c8649d148b5eb71d21f80bf870ae2d11708)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=45554917)</sub>

<!-- /greptile_comment -->
2026-07-19 22:43:29 -07:00
Ali Khokhar 3fba1c6fc3 Give messaging clear exact subtree semantics (#1072)
## Problem

Messaging `/clear` did not follow the selected platform message
boundary. Reply clears preserved the selected user prompt, while
standalone clears preserved user-authored messages and could reset FCC
state outside the invoking chat.

## Changes

| Before | After |
| --- | --- |
| Reply `/clear` removed a logical conversation branch but retained the
selected message. | Reply `/clear` deletes the selected message and its
literal reply subtree, including the clear command. |
| Standalone `/clear` retained user prompts and voice notes while
resetting global messaging state. | Standalone `/clear` deletes every
tracked message and resets FCC state only in the invoking platform and
chat. |
| Trees recorded only logical execution parentage. | Trees separately
persist logical execution ancestry and exact prompt/status reply
ownership. |
| Clear coordination used one global admission boundary. | Per-chat
clear generations coordinate admission, voice cancellation, persistence,
and best-effort platform deletion. |
| Persistence tracked only FCC-authored clearable output. | Persistence
tracks managed inbound and outbound messages and migrates legacy
entries. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR gives messaging `/clear` exact per-chat and reply-subtree
behavior. The main changes are:

- Per-chat clear generations for admission and startup-notice cleanup.
- Managed inbound and outbound message tracking for deletion.
- Exact prompt/status reply ownership in message trees.
- Scoped voice cancellation and clear persistence updates.
- Updated docs, smoke coverage, and messaging tests.
</details>

<h3>Confidence Score: 4/5</h3>

The clear flow is mostly well-contained, with one upgrade-path issue in
legacy tree restoration.

Newly created prompt/status subtrees use the new exact reference fields
consistently, and legacy snapshots can map old status replies to prompt
references. However, reply `/clear` on an upgraded status can miss
descendants and leave stale state/messages.

src/free_claude_code/messaging/trees/snapshot.py

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- T-Rex attempted to prepare and run a focused legacy snapshot
reproduction harness for legacy status replies detach, but tool access
was blocked before execution.
- A messaging clear smoke test harness was executed and reported a
passing result: 20 items collected and 20 passed in 1.69 seconds, with
traces for test\_reply\_clear\_uses\_literal and related paths shown in
the log.

<a
href="https://app.greptile.com/trex/runs/14131337/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/messaging/workflow.py | Adds per-chat clear
generations, managed inbound recording, scoped clears, and
startup-notice invalidation. |
| src/free_claude_code/messaging/trees/graph.py | Adds exact
prompt/status reference resolution and literal reply-subtree traversal.
|
| src/free_claude_code/messaging/trees/runtime.py | Adds exact
message-subtree removal and status-only clearing behavior. |
| src/free_claude_code/messaging/trees/snapshot.py | Adds
parent_reference_id persistence and legacy fallback; the fallback can
miss legacy status-reply descendants. |
| src/free_claude_code/messaging/session/managed_message_log.py |
Replaces the clearable output log with managed inbound and outbound
message tracking. |
| src/free_claude_code/messaging/commands.py | Routes reply and
standalone `/clear` through the new exact deletion ID flows. |

</details>

<details open><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Incoming message] --> B{Standalone /clear?}
B -- yes --> C[Clear invoking chat]
C --> D[Cancel scoped voice work]
C --> E[Collect managed and tree message IDs]
C --> F[Advance chat clear generation]
F --> G[Detach scoped trees]
G --> H[Clear scoped session store]
H --> I[Best-effort platform deletes]
B -- no --> J[Record managed inbound message]
J --> K[Admit with stop and clear token]
K --> L{Reply /clear?}
L -- yes --> M[Resolve exact prompt or status reference]
M --> N[Remove literal reference subtree]
N --> I
L -- no --> O[Queue or run tree node]
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[Incoming message] --> B{Standalone /clear?}
B -- yes --> C[Clear invoking chat]
C --> D[Cancel scoped voice work]
C --> E[Collect managed and tree message IDs]
C --> F[Advance chat clear generation]
F --> G[Detach scoped trees]
G --> H[Clear scoped session store]
H --> I[Best-effort platform deletes]
B -- no --> J[Record managed inbound message]
J --> K[Admit with stop and clear token]
K --> L{Reply /clear?}
L -- yes --> M[Resolve exact prompt or status reference]
M --> N[Remove literal reference subtree]
N --> I
L -- no --> O[Queue or run tree node]
```

</a>
</details>

<a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fclear-message-subtree%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fclear-message-subtree%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fmessaging%2Ftrees%2Fsnapshot.py%3A192-193%0A**Legacy%20Status%20Replies%20Detach**%0A%0AWhen%20an%20upgraded%20legacy%20snapshot%20contains%20a%20child%20that%20originally%20replied%20to%20its%20parent%20status%2C%20this%20fallback%20rewrites%20the%20missing%20exact%20reference%20to%20the%20parent%20prompt.%20A%20later%20reply%20%60%2Fclear%60%20on%20that%20status%20traverses%20from%20the%20status%20ID%2C%20finds%20no%20migrated%20child%20edge%2C%20and%20leaves%20the%20old%20status-reply%20descendants%20and%20their%20managed%20messages%20behind.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1072&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a>

<sub>Reviews (1): Last reviewed commit: ["Give messaging clear exact
subtree
seman..."](https://github.com/alishahryar1/free-claude-code/commit/0a3baecf9470da4bb866c864c1d1f01abd517085)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43593126)</sub>

> Greptile also left **1 inline comment** on this PR.

<!-- /greptile_comment -->
2026-07-11 22:26:16 -07:00
Ali Khokhar f3ea35a777 Consolidate OpenAI-compatible provider adapters (#1069)
## Problem

Sixteen OpenAI-compatible providers were represented by
configuration-only subclasses and one factory function each. The sole
provider family also lived under a multi-transport namespace that no
longer described the codebase, while provider defaults, IDs, and
instance caching passed through redundant forwarding layers.

## Changes

| Before | After |
| --- | --- |
| Sixteen provider IDs used configuration-only subclasses. | Immutable
OpenAI-chat profiles configure one concrete provider while preserving
each provider's request policy. |
| Every provider required a dedicated factory function. | Generic
profile construction is the default; only eight adapters with real state
or algorithms retain factories. |
| The sole provider family lived under `providers/transports/`. |
`providers/openai_chat/` directly owns shared request, stream, recovery,
tool, and usage behavior. |
| Provider defaults and IDs passed through forwarding modules, and
constructors repeated fallback resolution. | The neutral catalog
resolves complete immutable provider configuration once. |
| `ProviderRuntime` wrapped a pass-through provider cache. |
`ProviderRuntime` directly owns lazy provider instances and cleanup. |
| Production carried the duplicated adapter structure. | The final shape
removes a net 873 production lines with no compatibility shim or
customer-facing provider change. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR consolidates OpenAI-compatible providers behind shared profiles
and runtime construction. The main changes are:

- OpenAI-chat behavior moved into `providers/openai_chat`.
- Configuration-only provider subclasses replaced by immutable profiles.
- Provider runtime now owns lazy instance caching and cleanup directly.
- Provider defaults and IDs now resolve through the neutral catalog.
- Tests and smoke helpers updated for the new provider shape.
</details>


<h3>Confidence Score: 5/5</h3>

This looks safe to merge.

No blocking issues found in the changed code.

No files need attention.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- T-Rex produced a proof for a posted P1 finding and linked it to the
corresponding review comment for details.
- T-Rex saved contract-validation logs for provider consolidation and
preserved four log files that show the test hanging boundary and the
timeout exit code 124.

<a
href="https://app.greptile.com/trex/runs/14125428/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>


<details open><summary><h3>Important Files Changed</h3></summary>




| Filename | Overview |
|----------|----------|
| src/free_claude_code/providers/runtime/config.py | Builds resolved
provider configuration from catalog descriptors, including static
credentials for local providers. |
| src/free_claude_code/providers/openai_chat/provider.py | Creates
shared OpenAI-chat clients from resolved configuration and immutable
provider profiles. |
| src/free_claude_code/providers/openai_chat/profiles.py | Defines
declarative profiles for formerly configuration-only OpenAI-compatible
adapters. |

</details>


<!-- greptile_failed_comments -->
<h3>Comments Outside Diff (2)</h3>

1. General comment 

<a href="#"><img alt="P1"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9"
align="top"></a> **NIM OpenAI-compatible 504 exhausted streaming retry
path hangs instead of completing with the expected user-facing error**

   - **Bug**
- The broader provider consolidation suite failed all parametrized
`test_nim_stream_openai_5xx_exhausted_emits_user_message` cases under
xdist with worker crashes. A serial isolation run narrowed this to the
same OpenAI-compatible NIM exhausted 5xx streaming path: 500, 502, and
503 completed, but the 504 case did not finish before the explicit 120s
timeout, producing exit code 124. This contradicts the expected
streaming retry contract that exhausted transient provider failures
terminate and emit the configured user-facing error.
   - **Cause**
- The OpenAI-compatible streaming retry/exhaustion path for NIM 504
responses appears to wait indefinitely or otherwise fail to terminate
after retries are exhausted. The exact code-level loop/await point was
not isolated within the validation budget, but the failure is anchored
to the provider streaming retry contract exercised by
`tests/providers/test_openai_compat_5xx_retry.py::test_nim_stream_openai_5xx_exhausted_emits_user_message[504-temporarily
unavailable]`.
   - **Fix**
- Inspect the OpenAI-compatible/NIM streaming retry exhaustion handling
for 504 responses and ensure retry limits are enforced, the async stream
is closed/cancelled on exhaustion, and the provider raises/emits the
same terminal user-facing error contract as the 500/502/503 cases. Add
or keep a serial regression test for the 504 exhausted stream path to
prevent hangs.

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>

2. General comment 

<a href="#"><img alt="P1"
src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9"
align="top"></a> **OpenAI-compatible 5xx retry suite hangs on exhausted
502 retry path**

   - **Bug**
- The recommended non-xdist retry validation does not complete. A
verbose rerun with a 60 second timeout shows the suite passes tests
through the exhausted 500 case, then times out while running
`test_nim_stream_openai_5xx_exhausted_emits_user_message[502-temporarily
unavailable]`. This indicates the consolidated OpenAI-compatible
provider retry/error path can hang for at least the 502 exhausted-error
scenario, preventing reliable validation and potentially blocking
callers from receiving the expected `ExecutionFailure`.
   - **Cause**
- The OpenAI-compatible provider's exhausted 502 retry/error handling
path appears not to terminate promptly under the mocked repeated
`openai.InternalServerError` scenario. The exact code location was not
changed during validation, but the failure is isolated to the shared
OpenAI-chat 5xx retry behavior exercised by
`NvidiaNimProvider.stream_response`.
   - **Fix**
- Debug the shared OpenAI-chat retry loop/error classification for 502
responses. Ensure retries are bounded, patched `asyncio.sleep` is
awaited without real backoff during tests, and exhausted 502/503/504
errors consistently raise `ExecutionFailure` with the expected
temporary-unavailable message instead of continuing work indefinitely.

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>

<!-- /greptile_failed_comments -->

<sub>Reviews (2): Last reviewed commit: ["Prove local provider
credential
resoluti..."](https://github.com/alishahryar1/free-claude-code/commit/ba226d97efdae1889bea0372c7bacf268122d65c)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43580983)</sub>

<!-- /greptile_comment -->
2026-07-11 19:55:19 -07:00
Ali Khokhar e37b504636 Preserve user messages during messaging clear (#1068)
## Problem

Messaging `/clear` used one untyped collection for both internal reply
references and platform deletion targets. Clearing a branch or
cancelling a voice task could therefore delete the customer's prompt or
voice note along with FCC's own status and reply messages.

## Changes

| Before | After |
| --- | --- |
| Tree transitions exposed one message-ID set for repository unindexing
and platform deletion. | Tree transitions now separate internal
`reference_ids` from FCC-owned `clearable_message_ids`. |
| Reply and global clear deleted user prompts and voice notes with FCC
output. | Clear removes FCC statuses, replies, notices, and the explicit
`/clear` command while preserving user-authored messages. |
| The persisted message log accepted ordinary inbound content. | The
clearable-message log accepts only FCC output and explicit clear
commands, and drops legacy user-content entries when loading. |
| Tests treated user-message deletion as successful cleanup. |
Deterministic and live messaging coverage enforce preservation across
branch, global, and voice clear paths. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR narrows messaging clear behavior so user-authored messages are
preserved. The main changes are:

- Clearable platform IDs are separated from internal tree reference IDs.
- The session message log now tracks FCC-owned output and explicit clear
commands.
- Branch, global, and voice clear paths now avoid deleting user prompts
and voice notes.
- Tests, smoke coverage, docs, and the package version were updated.
</details>

<h3>Confidence Score: 5/5</h3>

This looks safe to merge after a small migration cleanup.

The clear paths now preserve user-authored messages, and current
clearable-log writers use the new shape consistently.

src/free_claude_code/messaging/session/clearable_message_log.py needs a
migration cleanup for old retained clear-command IDs during session
reload.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- Validated the messaging-clear-preservation contract by reviewing the
foreground pytest run log, which captured the exact command, working
directory, full test output, and exit code, and by examining the
artifact note and its capture log that summarize the command, test
count, exit code, and scope.

<a
href="https://app.greptile.com/trex/runs/14123111/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/messaging/session/clearable_message_log.py |
Replaces the broad message log with a clearable-message log, but the
migration filter can drop old retained clear-command IDs. |
| src/free_claude_code/messaging/session/store.py | Renames the
session-store API around clearable message IDs while keeping the same
persisted `message_log` key. |
| src/free_claude_code/messaging/trees/runtime.py | Returns internal
reference IDs separately from FCC-owned deletion IDs during branch
removal and chat-wide enumeration. |
| src/free_claude_code/messaging/trees/manager.py | Uses reference IDs
for repository cleanup and returns clearable IDs for platform deletion.
|
| src/free_claude_code/messaging/turn_intake.py | Stops recording
ordinary inbound content and records clear commands only when needed for
cleanup. |
| src/free_claude_code/messaging/commands.py | Deletes clearable IDs
plus the invoking clear command instead of deleting all branch reference
IDs. |
| src/free_claude_code/messaging/workflow.py | Aggregates clearable IDs
from tree state, the session log, and voice cancellation results. |
| src/free_claude_code/messaging/voice.py | Changes voice cancellation
deletion ownership so only FCC-authored status messages are clearable. |

</details>

<details open><summary><h3>Flowchart</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Incoming message] --> B{Clear command?}
  B -- No --> C[Handle normal turn]
  C --> D[Record FCC outbound status]
  B -- Reply clear --> E[Record clear command]
  E --> F[Clear branch or voice task]
  F --> G[Delete FCC-owned IDs plus clear command]
  B -- Global clear --> H[Collect clearable IDs]
  H --> I[Reset conversation state]
  I --> J[Delete FCC-owned IDs plus clear command]
  D --> K[Clearable-message log]
  K --> H
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
  A[Incoming message] --> B{Clear command?}
  B -- No --> C[Handle normal turn]
  C --> D[Record FCC outbound status]
  B -- Reply clear --> E[Record clear command]
  E --> F[Clear branch or voice task]
  F --> G[Delete FCC-owned IDs plus clear command]
  B -- Global clear --> H[Collect clearable IDs]
  H --> I[Reset conversation state]
  I --> J[Delete FCC-owned IDs plus clear command]
  D --> K[Clearable-message log]
  K --> H
```

</a>
</details>

<a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22ali%2Fpreserve-user-messages-on-clear%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22ali%2Fpreserve-user-messages-on-clear%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fmessaging%2Fsession%2Fclearable_message_log.py%3A36-37%0A**Legacy%20Clear%20Commands%20Are%20Dropped**%0A%0AWhen%20an%20existing%20session%20file%20contains%20a%20previously%20retained%20clear%20command%20from%20the%20old%20log%2C%20it%20is%20stored%20as%20%60direction%3D%22in%22%60%20and%20%60kind%3D%22command%22%60.%20This%20new%20load%20filter%20drops%20that%20entry%20because%20it%20only%20keeps%20%60kind%3D%22clear_command%22%60%2C%20so%20a%20failed%20or%20cancelled%20%60%2Fclear%60%20command%20recorded%20before%20the%20upgrade%20is%20no%20longer%20retried%20by%20the%20next%20clear%20and%20remains%20on%20the%20platform.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1068&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a>

<sub>Reviews (1): Last reviewed commit: ["Preserve user messages during
clear"](https://github.com/alishahryar1/free-claude-code/commit/af7a910facaa29a5ca8f7fb95ec2faae28798e62)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43576921)</sub>

> Greptile also left **1 inline comment** on this PR.

**Context used:**

- Context used - CLAUDE.md
([source](https://app.greptile.com/alishahryar1/github/Alishahryar1/free-claude-code/-/custom-context?memory=d2fd24d8-0dec-4faf-8ee4-e085e215a2f8))

<!-- /greptile_comment -->
2026-07-11 18:25:20 -07:00
Ali Khokhar 78377253d6 Keep voice ownership continuous through admission (#1063)
## Problem

Pending voice ownership ended before workflow admission completed. A
concurrent reply or global stop/clear could miss the handoff and return
while transcribed work was still able to enter the tree.

## Changes

| Before | After |
| --- | --- |
| The registry released voice and status aliases before the workflow
callback finished. | The registry retains both aliases and an owned
child through callback completion or explicit cancellation and join. |
| Caller and nested cancellation could interrupt cleanup or form
recursive joins. | Completion-driven cleanup preserves cancellation
state and excludes current or actively cancelling claims. |
| Commands coordinated voice-registry and message-tree primitives. | The
workflow exposes typed reply and global stop/clear use cases that own
voice-to-tree synchronization. |
| Admission could be interrupted between tree mutation, processor
publication, and persistence. | One workflow-owned transaction validates
the epoch, admits work, publishes processing, and persists its exact
snapshot. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR keeps voice-message ownership active until admission, stop, or
clear work finishes. The main changes are:

- Adds registry-managed voice handoff tasks and bulk cancellation.
- Moves reply `/stop` and `/clear` into workflow-owned voice/tree
operations.
- Wraps admission, stop, and clear work so state changes finish before
caller cancellation is restored.
- Updates platform ports, adapters, tests, smoke fixtures, docs, and
package metadata.
</details>

<h3>Confidence Score: 5/5</h3>

This looks safe to merge.

No blocking issues found in the changed code.

None.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- Ran the primary test suite for messaging and platform voice flow; 64
tests passed with exit code 0.
- Ran the live smoke tests for messaging product; 15 tests passed with
exit code 0.
- Captured and organized logs documenting the test commands, working
directory, pytest outputs, and exit codes for both runs.

<a
href="https://app.greptile.com/trex/runs/14110540/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/messaging/voice.py | Adds continuous
pending-voice ownership, handoff task tracking, alias cleanup, and
guarded bulk cancellation. |
| src/free_claude_code/messaging/platforms/voice_flow.py | Routes
transcription handoff and cleanup through the registry-owned lifecycle.
|
| src/free_claude_code/messaging/workflow.py | Coordinates voice
cancellation with tree admission, reply stop, reply clear, and global
stop/clear operations. |
| src/free_claude_code/messaging/commands.py | Delegates reply-scoped
stop and clear behavior to typed workflow operations. |

</details>

<sub>Reviews (3): Last reviewed commit: ["Keep voice ownership
continuous through
..."](https://github.com/alishahryar1/free-claude-code/commit/14acdfad8c38c1410620ac461fa05988aa417913)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43549792)</sub>

<!-- /greptile_comment -->
2026-07-11 13:31:08 -07:00
Ali Khokhar 4951983b5e Replace global runtime resources with explicit ownership (#1042)
## Problem

Provider, messaging, and transcription resources relied on
process-global state, leaving replacement, cancellation, and shutdown
ownership ambiguous. Separate server lifetimes could share
event-loop-bound resources or retain failed cleanup work.

## Changes

| Before | After |
| --- | --- |
| Provider clients found limiters through global singleton and scoped
registries. | Each provider instance receives and owns one explicitly
constructed limiter. |
| Messaging queues and voice pipelines relied on singleton or
module-global state. | Each platform owns its limiter and outbox, while
the application owns one injected transcriber. |
| Messaging shutdown mixed ingress, active work, delivery, and SDK
cleanup. | Application shutdown quiesces ingress, drains work, closes
delivery, then releases transcription and providers. |
| Cancelled or failed provider cleanup could be forgotten or treated as
complete. | The provider manager retains shielded generation and
unpublished-runtime cleanup until it succeeds. |
| Discord and Telegram startup tasks could outlive or poison runtime
readiness. | Platform runtimes observe long-lived tasks and retry only
independently repeatable lifecycle steps. |
| Constructor-captured security and diagnostic settings appeared
hot-applicable. | Admin marks those settings restart-required so applied
policy matches the running resource graph. |
| Lifecycle races lacked direct ownership coverage. | Deterministic
cancellation, retry, isolation, teardown, and live smoke contracts
protect the final ownership model. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR moves runtime resources from global state into explicitly owned
application objects. The main changes are:

- Provider generations own their rate limiters and cleanup tasks.
- Messaging platforms own their limiter, outbox, ingress, and delivery
lifecycle.
- Application shutdown now runs through ordered cleanup gates.
- Voice transcription is injected as an owned runtime resource.
- Admin config marks constructor-captured settings as restart-required.
</details>

<h3>Confidence Score: 4/5</h3>

The shutdown path needs a bounded cleanup result before merging.

Cleanup steps that hang never reach the retryable incomplete-shutdown
path. ASGI shutdown can remain stuck while waiting for an external SDK,
transcriber, workflow, or provider cleanup. The retry ownership model
works only after cleanup returns or raises.

src/free_claude_code/runtime/application.py

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- T-Rex ran the requested verification, but its local artifact
references were not uploaded.
- The validation run completed successfully with EXIT\_CODE: 0 and 62
tests passed in 3.91 seconds, using the command uv run pytest -vv
tests/runtime/test\_application\_runtime.py
tests/runtime/test\_provider\_manager.py
tests/providers/test\_provider\_runtime.py.

<a
href="https://app.greptile.com/trex/runs/14064214/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| src/free_claude_code/runtime/application.py | Refactors shutdown into
ordered retryable cleanup gates, but cleanup awaitables can still block
shutdown forever. |
| src/free_claude_code/runtime/asgi.py | Reports incomplete runtime
shutdown when `close()` returns false. |
| src/free_claude_code/runtime/provider_manager.py | Adds owned provider
cleanup retry state and shielded generation cleanup. |

</details>

<a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22refactor%2Fruntime-owned-resources%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22refactor%2Fruntime-owned-resources%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fruntime%2Fapplication.py%3A59%0A**Cleanup%20Await%20Blocks%20Shutdown**%0A%0AWhen%20a%20platform%20SDK%20stop%2C%20workflow%20drain%2C%20transcriber%20close%2C%20or%20provider%20cleanup%20hangs%2C%20this%20helper%20waits%20forever%20and%20never%20returns%20%60False%60.%20ASGI%20shutdown%20stays%20stuck%20in%20%60runtime.close%28%29%60%20instead%20of%20reporting%20an%20incomplete%20shutdown%2C%20so%20the%20retained%20resource%20graph%20cannot%20be%20retried%20cleanly.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1042&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a>

<sub>Reviews (2): Last reviewed commit: ["Report incomplete runtime
shutdown to
AS..."](https://github.com/alishahryar1/free-claude-code/commit/338b2bd179c3875b15bbd52818dd04c780e5d46d)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=43454593)</sub>

> Greptile also left **1 inline comment** on this PR.

**Context used:**

- Context used - CLAUDE.md
([source](https://app.greptile.com/alishahryar1/github/Alishahryar1/free-claude-code/-/custom-context?memory=d2fd24d8-0dec-4faf-8ee4-e085e215a2f8))

<!-- /greptile_comment -->
2026-07-10 18:46:50 -07:00
Ali Khokhar 71a78a0c5a Move runtime packages under src namespace (#1029)
## Problem

Runtime modules were published as generic top-level packages like `api`,
`cli`, and `providers`. That shape is fragile for PyPI packaging and
weakens explicit ownership boundaries.

## Changes

| Before | After |
| --- | --- |
| Runtime code lived in root-level packages. | Runtime code lives under
`src/free_claude_code/`. |
| Console scripts targeted top-level modules. | Console scripts target
namespaced modules. |
| Tests and smoke helpers imported old package roots. | Tests and smoke
helpers import `free_claude_code.*`. |
| Packaging listed six root packages. | Packaging builds the single
namespaced package. |
| Contracts allowed old root package directories. | Contracts require
the src namespace and reject old root imports. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR moves the runtime packages into the `src/free_claude_code`
namespace. The main changes are:

- Console scripts now point to `free_claude_code.*` entrypoints.
- Runtime imports, tests, and smoke helpers now use the namespaced
package.
- Packaging now builds the single `src/free_claude_code` package.
- Contract tests now reject old top-level runtime package roots and
imports.
</details>

<h3>Confidence Score: 5/5</h3>

This PR is safe to merge with minimal risk.

The changes are a broad but mostly mechanical namespace and
package-layout migration with updated packaging, tests, and contract
coverage.

No files require special attention.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- Reviewed the primary contract validation by examining the namespace
validation log, which documents the exact commands executed, the working
directory, exit codes, pytest output, wheel build output, install
output, and import/entrypoint resolution.
- Verified the wheel listing by inspecting the wheel listing artifact,
confirming the available wheel filenames for the namespace validation.
- Ran and inspected the isolated import/entrypoint validation harness
saved as package-installed-import-check.py to validate import resolution
and entrypoint exposure.
- Captured and noted the wheel filename record in
package-wheel-filename.txt to enable traceability of the observed
artifact.

<a
href="https://app.greptile.com/trex/runs/13810533/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| pyproject.toml | Updates packaging to build the single
`src/free_claude_code` package and retargets console scripts to
namespaced modules. |
| src/free_claude_code/config/env_template.py | Loads `.env.example`
from packaged resources with a source-checkout fallback after the
runtime package move. |
| src/free_claude_code/cli/entrypoints.py | Updates CLI entrypoint
imports to `free_claude_code.*` and continues to use the shared env
template loader. |
| src/free_claude_code/api/routes.py | Retargets API route dependencies
and handlers to the namespaced package without changing route behavior.
|
| src/free_claude_code/api/app.py | Updates app factory imports to the
namespaced package while preserving middleware, routers, and exception
handling. |
| src/free_claude_code/providers/runtime/factory.py | Updates lazy
provider factory imports to `free_claude_code.providers.*` under the new
package layout. |
| tests/contracts/test_import_boundaries.py | Adds contract coverage
requiring runtime packages to live under `src/free_claude_code` and
rejecting old top-level imports. |
| smoke/lib/child_process.py | Updates smoke child-process helpers to
import CLI entrypoints from the namespaced package. |
| README.md | Updates the project layout and extension guidance to refer
to `src/free_claude_code` and importable `free_claude_code.*` modules. |
| uv.lock | Reflects the package version bump associated with the
runtime packaging move. |

</details>

<details open><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant User as User / CLI
participant Script as Console script
participant Pkg as free_claude_code package
participant API as free_claude_code.api
participant Runtime as free_claude_code.providers.runtime
participant Provider as Provider adapter

User->>Script: run fcc-server / free-claude-code
Script->>Pkg: load free_claude_code.cli.entrypoints:serve
Pkg->>API: create FastAPI app and routes
API->>Runtime: resolve configured provider
Runtime->>Provider: instantiate namespaced adapter
Provider-->>Runtime: stream/model responses
Runtime-->>API: provider result
API-->>User: Anthropic/OpenAI-compatible response
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant User as User / CLI
participant Script as Console script
participant Pkg as free_claude_code package
participant API as free_claude_code.api
participant Runtime as free_claude_code.providers.runtime
participant Provider as Provider adapter

User->>Script: run fcc-server / free-claude-code
Script->>Pkg: load free_claude_code.cli.entrypoints:serve
Pkg->>API: create FastAPI app and routes
API->>Runtime: resolve configured provider
Runtime->>Provider: instantiate namespaced adapter
Provider-->>Runtime: stream/model responses
Runtime-->>API: provider result
API-->>User: Anthropic/OpenAI-compatible response
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["Fix documented package import
paths"](https://github.com/alishahryar1/free-claude-code/commit/bfa9f2704c45f3684da39657d5e13f3814e5d450)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=42950471)</sub>

<!-- /greptile_comment -->
2026-07-09 01:19:05 -07:00
Ali Khokhar 755a3851f7 Use batch delete boundary for messaging (#996)
## Problem

Messaging cleanup had two public queued delete paths. Command code could
loop single-message deletes and bypass Telegram batch deletion.

## Changes

| Before | After |
| --- | --- |
| Workflow code could call `queue_delete_message` or
`queue_delete_messages`. | Workflow code calls only
`queue_delete_messages`. |
| Telegram `/clear` cleanup used one API request per message. | Telegram
`/clear` cleanup uses `deleteMessages` in chunks of 100. |
| The outbox dedupe key used Python process hashing. | The outbox dedupe
key uses a stable SHA-based digest. |
| Voice and smoke cleanup depended on the single-delete queue API. |
Voice and smoke cleanup pass one-item delete lists. |

<!-- greptile_comment -->

<details open><summary><h3>Greptile Summary</h3></summary>

This PR moves messaging cleanup to a list-based delete boundary. The
main changes are:

- `/clear` now sends collected message IDs through
`queue_delete_messages`.
- Telegram deletion uses `deleteMessages` in 100-message chunks with
per-message fallback.
- Discord keeps per-message deletion behind the list-based outbound API.
- Delete-batch dedupe keys now use a stable SHA digest instead of Python
process hashing.
- Voice cleanup, smoke fakes, protocol tests, and messaging tests were
updated for the new delete boundary.
</details>

<h3>Confidence Score: 5/5</h3>

Safe to merge with minimal risk.

The change is well-scoped to the messaging delete boundary, keeps
platform-specific best-effort behavior, addresses the batch-fallback
concern, updates protocol consumers and tests, and includes the required
version and lockfile bump.

No files require special attention.

<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>

**What T-Rex did**
- No execution evidence is available for this session; no harness was
created, no tests were run, and no artifacts were produced.

<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>

<details open><summary><h3>Important Files Changed</h3></summary>

| Filename | Overview |
|----------|----------|
| messaging/commands.py | Routes `/clear` cleanup through
`queue_delete_messages` once per collected message set while preserving
best-effort state cleanup. |
| messaging/platforms/outbox.py | Removes single-delete queueing,
snapshots delete batches, and uses a stable SHA digest for delete dedupe
keys. |
| messaging/platforms/telegram_io.py | Adds Telegram `deleteMessages`
batching with 100-message chunks and per-message fallback when batch
deletion fails. |
| messaging/platforms/discord_io.py | Removes the public single
queued-delete wrapper and backs queued deletion with the list-based
outbox API. |
| messaging/platforms/voice_flow.py | Changes shared voice cleanup call
sites to submit one-item lists to the delete queue. |
| messaging/platforms/ports.py | Narrows the outbound protocol to the
list-based delete queue method. |
| tests/messaging/test_telegram.py | Adds Telegram batch delete,
chunking, and fallback coverage. |
| tests/messaging/test_platform_outbox.py | Covers stable delete-batch
dedupe keys and snapshotting mutable message ID lists before queueing. |
| pyproject.toml | Bumps the package patch version for the production
messaging changes. |
| uv.lock | Updates the editable package version in the lockfile to
match `pyproject.toml`. |

</details>

<details open><summary><h3>Sequence Diagram</h3></summary>

<a href="#gh-light-mode-only">

```mermaid
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Command as /clear or voice cleanup
participant Outbound as OutboundMessenger.queue_delete_messages
participant Outbox as PlatformOutbox
participant Telegram as TelegramMessenger
participant Discord as DiscordMessenger
participant API as Platform API

Command->>Outbound: queue_delete_messages(chat_id, message_ids)
Outbound->>Outbox: snapshot IDs and dedupe batch
alt Telegram
    Outbox->>Telegram: delete_messages(chat_id, ids)
    loop chunks of 100
        Telegram->>API: deleteMessages(chat_id, chunk)
        alt batch fails
            Telegram->>API: deleteMessage(chat_id, each id)
        end
    end
else Discord
    Outbox->>Discord: delete_messages(chat_id, ids)
    loop each id
        Discord->>API: fetch_message + delete
    end
end
```

</a>
<a href="#gh-dark-mode-only">

```mermaid
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Command as /clear or voice cleanup
participant Outbound as OutboundMessenger.queue_delete_messages
participant Outbox as PlatformOutbox
participant Telegram as TelegramMessenger
participant Discord as DiscordMessenger
participant API as Platform API

Command->>Outbound: queue_delete_messages(chat_id, message_ids)
Outbound->>Outbox: snapshot IDs and dedupe batch
alt Telegram
    Outbox->>Telegram: delete_messages(chat_id, ids)
    loop chunks of 100
        Telegram->>API: deleteMessages(chat_id, chunk)
        alt batch fails
            Telegram->>API: deleteMessage(chat_id, each id)
        end
    end
else Discord
    Outbox->>Discord: delete_messages(chat_id, ids)
    loop each id
        Discord->>API: fetch_message + delete
    end
end
```

</a>
</details>

<sub>Reviews (2): Last reviewed commit: ["Preserve Telegram batch delete
fallback"](https://github.com/alishahryar1/free-claude-code/commit/87090edcd95c4014cae37c2f540d9bfa4eba8962)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=41993526)</sub>

<!-- /greptile_comment -->
2026-07-05 22:07:09 -07:00
Ali Khokhar d281d52ced Refactor messaging around explicit ports (#878) 2026-06-20 09:35:38 -07:00
Ali Khokhar 6bee3104fe Refactor CLI surfaces around launchers and managed Claude (#861)
## Problem

The CLI package preserved a generic adapter layer and managed Codex
parser path that did not match the supported customer workflows.
Messaging runs Claude Code sessions, while Codex is supported through
`fcc-codex` and extensions.

## Changes

| Before | After |
| --- | --- |
| `fcc-claude` and `fcc-codex` shared generic adapter plumbing. |
`fcc-claude` and `fcc-codex` use explicit launcher modules. |
| Messaging depended on a generic CLI session abstraction. | Messaging
depends on managed Claude Code sessions. |
| Codex catalog generation lived as a top-level CLI helper. | Codex
catalog generation lives under the Codex launcher owner. |
| Tests asserted deleted internal adapter shapes. | Tests assert
launcher, managed-Claude, and customer-surface behavior. |
2026-06-18 18:36:37 -07:00
Alishahryar1 b926f60f64 feat: Anthropic web server tools, provider metadata, messaging hardening
- Add local web_search/web_fetch SSE handling and optional tool schemas
- Extend HeuristicToolParser for JSON-style WebFetch/WebSearch text
- Consolidate provider defaults, ids, and exception typing; stream contracts
- Messaging: typed options, voice config injection, platform contract cleanup
- Tests for web server tools, converters, parsers, contracts; ignore debug-*.log
2026-04-24 23:01:14 -07:00
Alishahryar1 835d0454e8 Fixes for issue 113 and 116 2026-04-18 16:32:31 -07:00
th-ch f703a0e403 Implement optional authentication (Anthropic style) (#80) 2026-03-27 11:11:47 -07:00
Alishahryar1 5a36a32836 feat: add llama.cpp provider for local anthropic messages API 2026-03-08 10:38:25 -07:00
Alishahryar1 2e8b22fa9d Remvoed root insert hack from conftest 2026-03-01 21:57:25 -08:00
Alishahryar1 a7d88d5cbd Updated README with per-model mapping, fixed test .env isolation 2026-03-01 21:52:35 -08:00
Ali Khokhar c4d8681000 Backup/before cleanup 20260222 230402 (#58) 2026-02-27 19:50:21 -08:00
Alishahryar1 d6a0e1a401 Provider inferred from model name using prefix 2026-02-19 20:53:02 -08:00
Claude 99f99fce90 Remove max_cli_sessions — CLI session pool is now unbounded
The max_sessions cap in CLISessionManager was the only thing enforcing
a limit on concurrent CLI processes. Now that provider concurrency is
controlled at the streaming layer (PROVIDER_MAX_CONCURRENCY semaphore),
the CLI session pool cap is redundant and removed entirely.

Changes:
- cli/manager.py: remove max_sessions param, cap check, _cleanup_idle_sessions_unlocked, max_sessions from get_stats()
- config/settings.py: remove max_cli_sessions field
- api/app.py: remove max_sessions=settings.max_cli_sessions from CLISessionManager constructor
- messaging/handler.py: remove "Waiting for slot" status check; stats display no longer shows Max CLI
- .env.example: remove MAX_CLI_SESSIONS line
- tests/cli/test_cli.py: remove max_sessions args and assertion from manager tests
- tests/cli/test_cli_manager_edge_cases.py: remove two tests for cap/cleanup behavior
- tests/api/test_app_lifespan_and_errors.py: remove max_cli_sessions from all SimpleNamespace settings
- tests/config/test_config.py: remove max_cli_sessions isinstance assertion
- tests/conftest.py: remove max_sessions from mock stats
- tests/messaging/test_handler.py: merge slot/capacity tests into single new-conversation test; remove Max CLI assertion from stats test
- tests/messaging/test_handler_markdown_and_status_edges.py: remove "Waiting for slot" assertion; drop max_sessions from all stats mocks

https://claude.ai/code/session_014mrF1WMNgmNjtPBuoQHsbg
2026-02-19 14:31:47 +00:00
Alishahryar1 16fa9d90cd Add message_thread_id support across messaging components
- Introduced message_thread_id to the IncomingMessage model for handling forum topic IDs in Telegram.
- Updated messaging platforms (Discord and Telegram) to accept and process message_thread_id in send_message methods.
- Modified message handlers to utilize message_thread_id when sending messages.
- Enhanced test cases to validate the integration of message_thread_id in message handling.

This change improves support for forum supergroups in Telegram and enhances message management across platforms.
2026-02-18 16:10:57 -08:00
Alishahryar1 b05d0d2703 new linter rules and fixes 2026-02-18 04:13:41 -08:00
Alishahryar1 7300156925 Add status message handling for voice note processing
- Introduced a new optional field `status_message_id` in the IncomingMessage model to track the status of voice note processing.
- Updated the Telegram and Discord platforms to utilize the `status_message_id` for editing status messages instead of sending new ones.
- Modified tests to assert the correct status message ID is used during voice note handling.
- Changed status message text from "Processing voice note..." to "Transcribing voice note..." for clarity.
2026-02-16 20:48:47 -08:00
Cursor Agent 72b7e34999 Phase 3: Fix encapsulation violations
- Add MessageTree.set_current_task() method
- Update tree_processor to use set_current_task instead of _current_task
- Move nim_settings out of ProviderConfig, pass only to NvidiaNimProvider
- Update api/dependencies and all tests

Co-authored-by: Ali Khokhar <alishahryar2@gmail.com>
2026-02-17 01:58:51 +00:00
Alishahryar1 539854fe7b Refactor done using GLM-5 2026-02-15 21:58:03 -08:00
Alishahryar1 b83be84313 Add LM Studio provider support
- Introduced `LMStudioProvider` to the provider system.
- Added a new fixture `lmstudio_provider` in `conftest.py` for testing.
- Updated `get_provider` function to handle `lmstudio` as a valid provider type.
- Enhanced README and `.env.example` to include LM Studio configuration details.
- Updated settings to accommodate LM Studio's base URL and provider type.
- Added tests to verify the functionality of the LM Studio provider.
2026-02-15 19:41:03 -08:00
Alishahryar1 e5a096049d feat: add OpenRouter support and configuration options
- Introduced OpenRouter as a new provider option in settings and environment configuration.
- Updated README.md to include instructions for using OpenRouter.
- Enhanced the message converter to support reasoning content for OpenRouter.
- Added tests for OpenRouter provider functionality and message conversion.
- Updated dependencies to include OpenRouterProvider.
2026-02-15 10:50:53 -08:00
Alishahryar1 0d292cd578 ci: enhance type checking in workflow and improve test coverage
- Added a step to fail the CI if any '# type: ignore' comments are found in Python files.
- Refactored tests to use mocking for better isolation and reliability.
- Updated type hints and casting in several files to improve type safety.
2026-02-14 23:01:11 -08:00
Alishahryar1 4ee0114891 Implemented /clear command 2026-02-13 16:24:52 -08:00
Alishahryar1 459ce6e8fd introduced per reply /stop command to stop just that task and fixed context leakage in reply session forking 2026-02-13 14:53:48 -08:00
Alishahryar1 6102583026 Major Refactor Part 2 with kimi-k2.5 in claude code 2026-02-05 16:09:16 -08:00
Alishahryar1 8ce86f4267 fixed type errors 2026-01-31 14:13:09 -08:00
Alishahryar1 7045e0ed44 add retry for telegram message edits with tests and updated rate telegram limit 2026-01-30 15:41:24 -08:00
Alishahryar1 12e7717c0b fixed test warning 2026-01-30 14:04:42 -08:00
Alishahryar1 3f8b9af2c3 improved test suite and fixed error propagation bug in telegram 2026-01-30 14:04:28 -08:00
Alishahryar1 f1efa22a82 fixed issues after refactor 2026-01-29 14:50:05 -08:00
Alishahryar1 8678a62915 Major refactor done by itself 2026-01-29 14:40:08 -08:00
Alishahryar1 b8e0360b37 added tests 2026-01-28 14:37:30 -08:00