coding-bridge 2026.7.2.1__tar.gz → 2026.7.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/PKG-INFO +1 -1
  2. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/connection.py +12 -4
  3. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/history.py +48 -0
  4. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/pyproject.toml +1 -1
  5. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_history.py +59 -0
  6. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/.env.example +0 -0
  7. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/.github/workflows/ci.yaml +0 -0
  8. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/.github/workflows/publish.yml +0 -0
  9. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/.gitignore +0 -0
  10. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/LICENSE +0 -0
  11. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/NOTICE +0 -0
  12. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/README.md +0 -0
  13. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/__init__.py +0 -0
  14. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/__main__.py +0 -0
  15. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/attachments.py +0 -0
  16. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/capabilities.py +0 -0
  17. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/cli.py +0 -0
  18. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/config.py +0 -0
  19. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/fs.py +0 -0
  20. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/images.py +0 -0
  21. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/locking.py +0 -0
  22. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/logs.py +0 -0
  23. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/pairing.py +0 -0
  24. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/permissions.py +0 -0
  25. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/protocol.py +0 -0
  26. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/providers/__init__.py +0 -0
  27. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/providers/base.py +0 -0
  28. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/providers/claude.py +0 -0
  29. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/providers/codex.py +0 -0
  30. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/providers/copilot.py +0 -0
  31. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/session.py +0 -0
  32. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/session_meta.py +0 -0
  33. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/coding_bridge/store.py +0 -0
  34. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/docs/design/reliable-event-delivery.md +0 -0
  35. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/conftest.py +0 -0
  36. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_ask_user_question.py +0 -0
  37. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_attachments.py +0 -0
  38. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_capabilities.py +0 -0
  39. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_codex.py +0 -0
  40. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_config.py +0 -0
  41. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_connection.py +0 -0
  42. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_copilot.py +0 -0
  43. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_edit_fork.py +0 -0
  44. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_fs.py +0 -0
  45. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_images.py +0 -0
  46. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_locking.py +0 -0
  47. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_outbox.py +0 -0
  48. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_pairing.py +0 -0
  49. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_permissions.py +0 -0
  50. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_protocol.py +0 -0
  51. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_resume_guard.py +0 -0
  52. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_session.py +0 -0
  53. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_session_meta.py +0 -0
  54. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_slash_commands.py +0 -0
  55. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_store.py +0 -0
  56. {coding_bridge-2026.7.2.1 → coding_bridge-2026.7.2.2}/tests/test_streaming.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-bridge
3
- Version: 2026.7.2.1
3
+ Version: 2026.7.2.2
4
4
  Summary: Node daemon for AceDataCloud Coding Bridge — run Claude Code on your own machine and drive it from the web.
5
5
  Project-URL: Homepage, https://github.com/AceDataCloud/CodingBridge
6
6
  Project-URL: Issues, https://github.com/AceDataCloud/CodingBridge/issues
@@ -334,6 +334,16 @@ class BridgeConnection:
334
334
  **_session_overrides(payload),
335
335
  )
336
336
  return
337
+ resume = payload.get("resume_session_id") or None
338
+ prompt = payload.get("prompt", "")
339
+ # Continue a Copilot session the CLI can't natively resume (e.g. a VS Code
340
+ # Copilot Chat session opened from history): seed its transcript into a
341
+ # fresh session instead of a doomed session/resume.
342
+ if provider == "copilot" and resume and not history.copilot_native(resume):
343
+ seed = await asyncio.to_thread(history.build_seed, resume)
344
+ if seed:
345
+ prompt = f"{seed}\n\n{prompt}".strip() if prompt else seed
346
+ resume = None
337
347
  session = Session(
338
348
  session_id,
339
349
  self.provider_factory,
@@ -344,14 +354,12 @@ class BridgeConnection:
344
354
  permission_mode=payload.get("permission_mode") or "default",
345
355
  provider=provider,
346
356
  effort=payload.get("effort") or None,
347
- resume=payload.get("resume_session_id") or None,
357
+ resume=resume,
348
358
  trace_id=payload.get("trace_id"),
349
359
  on_rekey=self._rekey_session,
350
360
  )
351
361
  self.sessions[session_id] = session
352
- await session.start(
353
- payload.get("prompt", ""), payload.get("images"), payload.get("attachments")
354
- )
362
+ await session.start(prompt, payload.get("images"), payload.get("attachments"))
355
363
 
356
364
  def _session(self, session_id: str | None) -> Session | None:
357
365
  """Resolve a session by its canonical id or a still-live provisional alias."""
@@ -131,6 +131,54 @@ def claude_user_uuid_after(session_id: str, cut_uuid: str | None) -> str | None:
131
131
  return None
132
132
 
133
133
 
134
+ # Cap the seeded transcript so continuing a huge conversation doesn't blow the
135
+ # first prompt; keep the most recent turns (the live context).
136
+ _SEED_MAX_CHARS = 40000
137
+
138
+
139
+ def copilot_native(session_id: str) -> bool:
140
+ """True if a Copilot session id belongs to the CLI store (natively resumable).
141
+
142
+ VS Code Copilot Chat sessions live in a different store the CLI can't resume,
143
+ so they must be continued by seeding the transcript into a fresh session.
144
+ """
145
+ return _copilot_path(session_id) is not None
146
+
147
+
148
+ def build_seed(session_id: str) -> str:
149
+ """Render a Copilot transcript as a compact preamble to continue it elsewhere.
150
+
151
+ Used when a session can't be natively resumed (e.g. a VS Code Copilot Chat
152
+ session continued from the phone): the prior user/assistant turns are replayed
153
+ as context in a fresh session. Tool calls and thinking are omitted; only typed
154
+ prompts and assistant text are kept, tail-capped at ``_SEED_MAX_CHARS``.
155
+ """
156
+ try:
157
+ detail = read_session("copilot", session_id)
158
+ except (FileNotFoundError, OSError, ValueError):
159
+ return ""
160
+ lines: list[str] = []
161
+ for event in detail.get("events", []):
162
+ kind = event.get("kind")
163
+ text = event.get("text")
164
+ if not isinstance(text, str) or not text.strip():
165
+ continue
166
+ if kind == "prompt":
167
+ lines.append(f"User: {text.strip()}")
168
+ elif kind == "text":
169
+ lines.append(f"Assistant: {text.strip()}")
170
+ if not lines:
171
+ return ""
172
+ body = "\n\n".join(lines)
173
+ if len(body) > _SEED_MAX_CHARS:
174
+ body = "…(earlier turns omitted)…\n\n" + body[-_SEED_MAX_CHARS:]
175
+ return (
176
+ "[Continuing a previous conversation. Transcript so far:]\n\n"
177
+ + body
178
+ + "\n\n[End of previous transcript — continue from here.]"
179
+ )
180
+
181
+
134
182
  def read_session(provider: str, session_id: str) -> dict[str, Any]:
135
183
  """Return a normalised transcript ``{provider,title,cwd,model,...,events}``."""
136
184
  if provider == "claude":
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "coding-bridge"
7
- version = "2026.7.2.1"
7
+ version = "2026.7.2.2"
8
8
  description = "Node daemon for AceDataCloud Coding Bridge — run Claude Code on your own machine and drive it from the web."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -705,3 +705,62 @@ def test_vscode_chat_tolerates_malformed(monkeypatch, tmp_path):
705
705
  detail = history.read_session("copilot", VSCODE_SID)
706
706
  prompts = [e["text"] for e in detail["events"] if e["kind"] == "prompt"]
707
707
  assert prompts == ["real"]
708
+
709
+
710
+ def test_copilot_native_and_build_seed(monkeypatch, tmp_path):
711
+ # A VS Code chat session is NOT natively resumable but seeds fine.
712
+ storage = tmp_path / "workspaceStorage"
713
+ _seed_vscode_chat(storage)
714
+ monkeypatch.setattr(history, "VSCODE_CHAT_ROOTS", [storage])
715
+ monkeypatch.setattr(history, "COPILOT_ROOT", tmp_path / "none")
716
+
717
+ assert history.copilot_native(VSCODE_SID) is False
718
+ seed = history.build_seed(VSCODE_SID)
719
+ assert "User: fix the parser" in seed
720
+ assert "Assistant: Here is the fix." in seed
721
+ assert seed.startswith("[Continuing a previous conversation")
722
+ assert seed.rstrip().endswith("continue from here.]")
723
+ # tool_use / thinking are omitted from the seed
724
+ assert "copilot_readFile" not in seed
725
+ assert "let me look" not in seed
726
+
727
+
728
+ def test_build_seed_missing_returns_empty(monkeypatch, tmp_path):
729
+ monkeypatch.setattr(history, "VSCODE_CHAT_ROOTS", [tmp_path / "none-vs"])
730
+ monkeypatch.setattr(history, "COPILOT_ROOT", tmp_path / "none-cli")
731
+ assert history.build_seed("00000000-0000-0000-0000-000000000000") == ""
732
+
733
+
734
+ def test_build_seed_tail_caps(monkeypatch, tmp_path):
735
+ storage = tmp_path / "workspaceStorage"
736
+ ws = storage / "h" / "chatSessions"
737
+ ws.mkdir(parents=True, exist_ok=True)
738
+ big = "x" * 60000
739
+ _write_jsonl(
740
+ ws / f"{VSCODE_SID}.jsonl",
741
+ [
742
+ {
743
+ "kind": 2,
744
+ "v": [
745
+ {
746
+ "requestId": "r1",
747
+ "timestamp": 1,
748
+ "message": {"text": "early turn " + big},
749
+ "response": [{"value": "ok"}],
750
+ },
751
+ {
752
+ "requestId": "r2",
753
+ "timestamp": 2,
754
+ "message": {"text": "latest question"},
755
+ "response": [{"value": "latest answer"}],
756
+ },
757
+ ],
758
+ }
759
+ ],
760
+ )
761
+ monkeypatch.setattr(history, "VSCODE_CHAT_ROOTS", [storage])
762
+ monkeypatch.setattr(history, "COPILOT_ROOT", tmp_path / "none")
763
+ seed = history.build_seed(VSCODE_SID)
764
+ assert len(seed) < 45000 # capped
765
+ assert "latest question" in seed # tail kept
766
+ assert "omitted" in seed