main
10 次代码提交
| 作者 | SHA1 | 备注 | 提交日期 | |
|---|---|---|---|---|
|
|
ef76bddd58 |
Make Telegram startup notices clearable (#1066)
## Problem Telegram's online notice was sent directly by the SDK runtime and its message ID was discarded, so `/clear` could not delete it. Moving delivery into the workflow also needs to keep slow sends from blocking commands and prevent acknowledged notices from losing clear ownership. ## Changes | Before | After | | --- | --- | | The Telegram runtime sent a transport-specific startup side effect. | The platform declares a semantic notice intent that the application gives to the workflow after transport readiness. | | Startup delivery bypassed the persisted message log. | The workflow renders and records each acknowledged notice in the same bounded log used by `/clear`. | | Serializing send and record held workflow state across platform I/O. | A dedicated clear generation reserves publication, delivery runs outside the state lock, and a short receipt finalizer commits or compensates. | | Concurrent clear, cancellation, or record failure could leave a delivered notice unowned. | Clear or cancellation deletes a late receipt; record failure deletes it; failed deletion restores tracking for a later `/clear`. | | A standalone `/clear` command could evict an older target at the log cap. | Successful standalone clear owns its command ID directly, while failed or cancelled clear records it for the next attempt. | | Startup ownership races were implicit. | Deterministic race, failure, cap, restart, and product-smoke coverage enforce the final state machine in version 3.5.10. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR makes Telegram startup notices clearable through the messaging workflow. The main changes are: - Moves the Telegram online notice out of the SDK runtime and into workflow-owned publication. - Adds a startup-notice intent to platform composition and publishes it after runtime start and restored-status repair. - Records delivered startup notice IDs for later `/clear` ownership, with delete compensation on interrupted ownership transfer. - Defers standalone `/clear` command ID recording so it cannot evict older deletion targets at the log cap. - Adds tests and smoke coverage for startup notice clearing, cancellation, failures, cap pressure, persistence, and startup ordering. - Bumps the package version and lockfile to 3.5.10. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues were 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** - A focused proof run for the telegram startup tests completed, showing 121 tests passed in 2.94 seconds with EXIT\_CODE 0. - A smoke proof run for the same flow completed, showing 18 tests skipped in 0.97 seconds with EXIT\_CODE 0. - The shell wrapper issue was addressed by re-running with bash -lc, producing a clean result with EXIT\_CODE 0 in the final artifact. <a href="https://app.greptile.com/trex/runs/14118230/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 workflow-owned startup notice sending, tracking, compensation, and clear-generation ordering. | | src/free_claude_code/messaging/turn_intake.py | Defers standalone `/clear` command ID recording until failure or cancellation paths need it. | | src/free_claude_code/runtime/application.py | Publishes optional startup notices after messaging runtime start and restored-status repair. | | src/free_claude_code/messaging/platforms/factory.py | Creates a Telegram startup-notice intent when an allowed Telegram user is configured. | | src/free_claude_code/messaging/platforms/telegram.py | Removes the direct Telegram runtime startup-message side effect. | </details> <sub>Reviews (2): Last reviewed commit: ["Make Telegram startup notices clearable"](https://github.com/alishahryar1/free-claude-code/commit/6e779006e0cdaf1df24c27a8d04784e2d7220a66) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43565904)</sub> <!-- /greptile_comment --> |
||
|
|
2bcaf3ac74 |
Make messaging trees atomic ownership boundaries (#1048)
## Problem Messaging tree state, queue coordination, task ownership, and persistence were jointly mutated across several classes. Raw message IDs were treated as globally unique, allowing cross-chat collisions and unsafe cancellation or clear ordering. ## Changes | Before | After | | --- | --- | | Managers and processors coordinated partial tree mutations through exposed locks and mutable nodes. | MessageTree owns atomic transitions and returns detached effects to task, UI, and persistence owners. | | Raw message IDs and task identities could collide across chats or detached generations. | Scoped tree identities and opaque claim IDs isolate chats and reject stale task writes. | | Session snapshots duplicated graph links and retained ingress payloads. | Lean scoped snapshots rebuild validated indexes and continue reading existing session files. | | Stop, clear, and runner persistence could interleave across commit boundaries. | Admission epochs, cancellation-safe detach, and authoritative writes make committed cleanup durable. | | Tree implementation classes leaked through messaging package exports. | Adapter-facing values and ports remain supported while tree internals stay internal. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR makes messaging trees own their state transitions and scoped identities. The main changes are: - Scoped tree and voice ownership by platform and chat. - Opaque claim IDs for queued task execution. - Manager-owned atomic cancellation, clear, and successor task launch paths. - Lean scoped snapshots with legacy session restore support. - Messaging tree internals removed from the public package surface. </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** - Executed the messaging and contract-boundary test suite with pytest across all specified test modules. - Observed the run completed with 96 tests passing in 4.10 seconds and an exit code of 0. - The exact pytest command used for the run is documented in the proof to enable reproducibility of the test scope. - The run log is available as an artifact for reviewers to inspect test output and details. <a href="https://app.greptile.com/trex/runs/14083891/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/trees/runtime.py | Adds claim-scoped state transitions and cancellation tombstones for late runner writes. | | src/free_claude_code/messaging/trees/manager.py | Centralizes aggregate publication, branch removal, global clear, and successor task launch decisions. | | src/free_claude_code/messaging/trees/processor.py | Runs tasks by opaque claim ID and routes cleanup through manager-owned finish handling. | | src/free_claude_code/messaging/trees/snapshot.py | Serializes scoped tree snapshots and restores supported legacy tree shapes. | | src/free_claude_code/messaging/voice.py | Scopes pending voice registrations by message scope and message ID. | </details> <!-- greptile_failed_comments --> <h3>Comments Outside Diff (1)</h3> 1. `src/free_claude_code/messaging/commands.py`, line 161-164 ([link](https://github.com/alishahryar1/free-claude-code/blob/a4e49f082a9d3ffc1f6cdf3ab6a065b0f22a759d/src/free_claude_code/messaging/commands.py#L161-L164)) <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **Voice Cancellation Is Unscoped** The tree reply path is now scoped by `incoming.scope`, but the voice fallback still cancels by only `chat_id` and `reply_id`. If Discord and Telegram both have the same raw chat/message IDs, a reply `/clear` from one platform can cancel a pending voice note from the other platform, crossing the ownership boundary this PR adds for message trees. <details><summary><strong>Artifacts</strong></summary><br /> **[Repro: focused pytest harness that models cross-platform raw ID collision for voice cancellation](https://app.greptile.com/trex/artifacts/0ad480a9-4e20-4b4e-970e-63aebc4577b3)** - Contains supporting evidence from the run (text/x-python; charset=utf-8). **[Repro: verbose pytest output showing unscoped cancel\_pending\_voice call and Discord-owned voice cancellation from Telegram /clear](https://app.greptile.com/trex/artifacts/ead824f3-2f41-4028-a8e3-8359c5f6d3a9)** - Keeps the command output available without making the summary code-heavy. <a href="https://app.greptile.com/trex/runs/14082218/artifacts?artifact=0ad480a9-4e20-4b4e-970e-63aebc4577b3"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifacts.svg?v=4"><img alt="View artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewArtifacts.svg?v=4"></picture></a> </details> <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> <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%22architecture%2Fmessage-tree-ownership%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22architecture%2Fmessage-tree-ownership%22.%0A%0AThis%20is%20a%20comment%20left%20during%20a%20code%20review.%0APath%3A%20src%2Ffree_claude_code%2Fmessaging%2Fcommands.py%0ALine%3A%20161-164%0A%0AComment%3A%0A**Voice%20Cancellation%20Is%20Unscoped**%0A%0AThe%20tree%20reply%20path%20is%20now%20scoped%20by%20%60incoming.scope%60%2C%20but%20the%20voice%20fallback%20still%20cancels%20by%20only%20%60chat_id%60%20and%20%60reply_id%60.%20If%20Discord%20and%20Telegram%20both%20have%20the%20same%20raw%20chat%2Fmessage%20IDs%2C%20a%20reply%20%60%2Fclear%60%20from%20one%20platform%20can%20cancel%20a%20pending%20voice%20note%20from%20the%20other%20platform%2C%20crossing%20the%20ownership%20boundary%20this%20PR%20adds%20for%20message%20trees.%0A%0AHow%20can%20I%20resolve%20this%3F%20If%20you%20propose%20a%20fix%2C%20please%20make%20it%20concise.&repo=alishahryar1%2Ffree-claude-code&pr=1048&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=6"><img alt="Fix in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixInCodex.svg?v=6"></picture></a> <!-- /greptile_failed_comments --> <sub>Reviews (4): Last reviewed commit: ["Serialize successor task publication wit..."](https://github.com/alishahryar1/free-claude-code/commit/ec8744a8882c1ebc38d5dc7e87aaf2e31b08f653) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43492190)</sub> <!-- /greptile_comment --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
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 --> |
||
|
|
05dae97248 | add telegram proxy support (#988) | ||
|
|
d281d52ced | Refactor messaging around explicit ports (#878) | ||
|
|
c4d8681000 | Backup/before cleanup 20260222 230402 (#58) | ||
|
|
b05d0d2703 | new linter rules and fixes | ||
|
|
4b4f87515d |
Phase 7: Directory restructuring (messaging/ and tests/)
- Create messaging/platforms/ (base, discord, telegram, factory) - Create messaging/rendering/ (discord_markdown, telegram_markdown) - Create messaging/trees/ (data, repository, processor, queue_manager) - Organize tests/ into api/, providers/, messaging/, cli/, config/ - Add backward-compatible re-exports at old locations - Update handler.py and test_messaging_factory.py imports - Fix Telegram type hints for TELEGRAM_AVAILABLE=False case - Fix Python 3 except syntax in discord_markdown Co-authored-by: Ali Khokhar <alishahryar2@gmail.com> |