agentlink-cli 0.1.8__tar.gz → 0.2.0__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 (84) hide show
  1. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/PKG-INFO +1 -1
  2. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/adapter.py +11 -0
  3. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/runtime.py +36 -3
  4. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/version.py +1 -1
  5. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/pyproject.toml +1 -1
  6. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_codex_adapter.py +60 -0
  7. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_runtime.py +69 -1
  8. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_version.py +1 -1
  9. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/uv.lock +1 -1
  10. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/.gitignore +0 -0
  11. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/README.md +0 -0
  12. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/__init__.py +0 -0
  13. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/__init__.py +0 -0
  14. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/adapter.py +0 -0
  15. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/config_options.py +0 -0
  16. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/discovery.py +0 -0
  17. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifest.py +0 -0
  18. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifests/__init__.py +0 -0
  19. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifests/codebuddy.json +0 -0
  20. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifests/cursor.json +0 -0
  21. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifests/gemini.json +0 -0
  22. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/manifests/grok_build.json +0 -0
  23. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/reducer.py +0 -0
  24. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/acp/rpc.py +0 -0
  25. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/adapter.py +0 -0
  26. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/attachments.py +0 -0
  27. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/capabilities.py +0 -0
  28. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/__init__.py +0 -0
  29. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/history_adapter.py +0 -0
  30. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/normalized.py +0 -0
  31. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/normalizers.py +0 -0
  32. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/path_utils.py +0 -0
  33. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/preferences.py +0 -0
  34. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/sdk_adapter.py +0 -0
  35. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/timeline_identity.py +0 -0
  36. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/timeline_reducer.py +0 -0
  37. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/claude/trust.py +0 -0
  38. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/cli.py +0 -0
  39. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/__init__.py +0 -0
  40. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/collaboration.py +0 -0
  41. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/history.py +0 -0
  42. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/reducer.py +0 -0
  43. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/codex/rpc.py +0 -0
  44. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/control.py +0 -0
  45. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/json_rpc.py +0 -0
  46. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/launch.py +0 -0
  47. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/__init__.py +0 -0
  48. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/common.py +0 -0
  49. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/file_ops.py +0 -0
  50. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/ops.py +0 -0
  51. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/shell.py +0 -0
  52. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local/terminal.py +0 -0
  53. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local_ops.py +0 -0
  54. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/local_runtime.py +0 -0
  55. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/logging.py +0 -0
  56. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/perf.py +0 -0
  57. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/protocol.py +0 -0
  58. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/registry.py +0 -0
  59. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/sync_state.py +0 -0
  60. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/connector/time.py +0 -0
  61. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/run.sh +0 -0
  62. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/__init__.py +0 -0
  63. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/__init__.py +0 -0
  64. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/fake_agent/__init__.py +0 -0
  65. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/fake_agent/__main__.py +0 -0
  66. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/test_acp_adapter_fake.py +0 -0
  67. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/test_acp_reducer_golden.py +0 -0
  68. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/acp/test_config_options.py +0 -0
  69. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/contract/__init__.py +0 -0
  70. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/contract/helpers.py +0 -0
  71. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_claude_history_adapter.py +0 -0
  72. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_claude_preferences.py +0 -0
  73. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_claude_sdk_adapter.py +0 -0
  74. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_claude_timeline_parity.py +0 -0
  75. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_claude_trust.py +0 -0
  76. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_capabilities.py +0 -0
  77. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_cli.py +0 -0
  78. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_control.py +0 -0
  79. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_json_rpc.py +0 -0
  80. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_connector_logging.py +0 -0
  81. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_launch_command.py +0 -0
  82. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_local_runtime.py +0 -0
  83. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_perf.py +0 -0
  84. {agentlink_cli-0.1.8 → agentlink_cli-0.2.0}/tests/test_terminal_backend.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentlink-cli
3
- Version: 0.1.8
3
+ Version: 0.2.0
4
4
  Summary: Local runtime connector for AgentLink
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: claude-agent-sdk
@@ -585,6 +585,17 @@ class CodexAdapter:
585
585
  raw_args=raw_args,
586
586
  phase="started",
587
587
  )
588
+ if self.notification_sink is not None:
589
+ for notification in _item_notifications(session_id, started_items):
590
+ try:
591
+ await self.notification_sink(notification["method"], notification["params"])
592
+ except Exception as exc: # noqa: BLE001
593
+ logger.warning(
594
+ "codex command started notification enqueue failed session_id={} command={} error={}",
595
+ session_id,
596
+ command,
597
+ exc,
598
+ )
588
599
  try:
589
600
  if command == "compact":
590
601
  await self.rpc.request("thread/compact/start", {"threadId": thread_id})
@@ -480,6 +480,8 @@ class BackendRpcClient:
480
480
  if method == "session.create":
481
481
  adapter = self._resolve_adapter(params)
482
482
  result = await adapter.create_session({**params, "connectorId": self.config.connector_id})
483
+ if isinstance(result, dict) and adapter is self.adapters.get("codex"):
484
+ result.setdefault("runtimeLaunch", self._codex_launch_snapshot())
483
485
  await self._send_backend_notifications(result)
484
486
  return _strip_backend_notifications(result)
485
487
  if method == "session.sync":
@@ -497,7 +499,16 @@ class BackendRpcClient:
497
499
  if execute_command is None:
498
500
  raise ValueError("command_unavailable")
499
501
  result = await execute_command({**params, "connectorId": self.config.connector_id})
500
- await self._send_backend_notifications(result)
502
+ notifications = result.get("backendNotifications", [])
503
+ if isinstance(notifications, list):
504
+ try:
505
+ await self.enqueue_backend_notifications(notifications)
506
+ except Exception: # noqa: BLE001
507
+ logger.exception(
508
+ "command timeline notification enqueue failed session_id={} command={}",
509
+ params.get("sessionId"),
510
+ params.get("command"),
511
+ )
501
512
  return _strip_backend_notifications(result)
502
513
  if method == "turn.interrupt":
503
514
  return await self._resolve_adapter(params).interrupt_turn(params)
@@ -649,6 +660,8 @@ class BackendRpcClient:
649
660
  Exception: the first assistant text delta for a turn bypasses the
650
661
  window so TTFB is not inflated by FLUSH_WINDOW_SECONDS.
651
662
  """
663
+ if method == "session.updated" and isinstance(params, dict) and params.get("runtime") == "codex":
664
+ params = {**params, "runtimeLaunch": self._codex_launch_snapshot()}
652
665
  notification = {"method": method, "params": params}
653
666
  if self._outbox is not None:
654
667
  await asyncio.to_thread(self._outbox.add, method, params)
@@ -962,9 +975,12 @@ class BackendRpcClient:
962
975
  self._codex_launch_spec = {
963
976
  "mode": mode,
964
977
  **({"command": launch.raw} if mode == "command" else {}),
978
+ **({"source": str(params["source"])} if params.get("source") in {"builtin", "preset", "custom", "auto"} else {}),
979
+ **({"name": str(params["name"])} if isinstance(params.get("name"), str) and params["name"].strip() else {}),
980
+ **({"presetId": str(params["presetId"])} if isinstance(params.get("presetId"), str) and params["presetId"].strip() else {}),
965
981
  }
966
982
  self._blocked_runtimes.discard("codex")
967
- report["launch"] = launch.report(mode=mode)
983
+ report["launch"] = {**launch.report(mode=mode), **{k: v for k, v in self._codex_launch_spec.items() if k != "mode"}}
968
984
  self._record_runtime_report("codex", report)
969
985
  except Exception:
970
986
  codex.rpc = old_rpc
@@ -980,7 +996,11 @@ class BackendRpcClient:
980
996
  async def _apply_codex_launch_spec(self, spec: dict[str, Any]) -> None:
981
997
  mode = str(spec.get("mode") or "auto")
982
998
  command = spec.get("command") if isinstance(spec.get("command"), str) else None
983
- normalized = {"mode": mode, **({"command": command} if command else {})}
999
+ normalized = {
1000
+ "mode": mode,
1001
+ **({"command": command} if command else {}),
1002
+ **({key: spec[key] for key in ("source", "name", "presetId") if isinstance(spec.get(key), str)}),
1003
+ }
984
1004
  if normalized == self._codex_launch_spec and "codex" not in self._blocked_runtimes:
985
1005
  return
986
1006
  await self._configure_codex_launch({"runtime": "codex", **normalized})
@@ -997,6 +1017,7 @@ class BackendRpcClient:
997
1017
  self._codex_launch_spec = {
998
1018
  "mode": str(spec.get("mode") or "auto"),
999
1019
  **({"command": spec["command"]} if isinstance(spec.get("command"), str) else {}),
1020
+ **({key: spec[key] for key in ("source", "name", "presetId") if isinstance(spec.get(key), str)}),
1000
1021
  }
1001
1022
  if self._codex_launch_spec.get("mode") == "command":
1002
1023
  self._blocked_runtimes.add("codex")
@@ -1152,8 +1173,20 @@ class BackendRpcClient:
1152
1173
  method = notification.get("method")
1153
1174
  params = notification.get("params")
1154
1175
  if isinstance(method, str) and isinstance(params, dict):
1176
+ if method == "session.updated" and params.get("runtime") == "codex":
1177
+ params = {**params, "runtimeLaunch": self._codex_launch_snapshot()}
1155
1178
  await self.ingest_notifications([{"method": method, "params": params}])
1156
1179
 
1180
+ def _codex_launch_snapshot(self) -> dict[str, Any]:
1181
+ spec = self._codex_launch_spec
1182
+ mode = "command" if spec.get("mode") == "command" else "auto"
1183
+ snapshot: dict[str, Any] = {"mode": mode, "source": spec.get("source") or ("auto" if mode == "auto" else "custom")}
1184
+ for key in ("source", "name", "presetId", "command"):
1185
+ value = spec.get(key)
1186
+ if isinstance(value, str) and value:
1187
+ snapshot[key] = value
1188
+ return snapshot
1189
+
1157
1190
  async def enqueue_backend_notifications(self, notifications: list[dict[str, Any]]) -> None:
1158
1191
  for notification in notifications:
1159
1192
  if not isinstance(notification, dict):
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from importlib.metadata import PackageNotFoundError, version
4
4
 
5
- FALLBACK_VERSION = "0.1.8"
5
+ FALLBACK_VERSION = "0.2.0"
6
6
 
7
7
 
8
8
  def connector_version() -> str:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentlink-cli"
3
- version = "0.1.8"
3
+ version = "0.2.0"
4
4
  description = "Local runtime connector for AgentLink"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1149,6 +1149,66 @@ def test_codex_commands_use_native_app_server_methods() -> None:
1149
1149
  asyncio.run(exercise())
1150
1150
 
1151
1151
 
1152
+ def test_review_command_publishes_started_items_before_native_review_events() -> None:
1153
+ async def exercise() -> None:
1154
+ published: list[tuple[str, dict[str, Any]]] = []
1155
+
1156
+ class ReviewRpc(FakeCodexRpc):
1157
+ async def request(self, method: str, params: dict[str, Any] | None = None) -> dict[str, Any]:
1158
+ if method == "review/start":
1159
+ assert [entry[1]["item"]["content"].get("phase") for entry in published] == [None, "started"]
1160
+ return {"turn": {"id": "turn_review"}}
1161
+ return await super().request(method, params)
1162
+
1163
+ async def sink(method: str, params: dict[str, Any]) -> None:
1164
+ published.append((method, params))
1165
+
1166
+ adapter = CodexAdapter(rpc=ReviewRpc(), notification_sink=sink) # type: ignore[arg-type]
1167
+ result = await adapter.execute_command(
1168
+ {
1169
+ "sessionId": "sess_1",
1170
+ "externalSessionId": "thr_1",
1171
+ "command": "review",
1172
+ "clientCommandId": "cmd_review",
1173
+ }
1174
+ )
1175
+
1176
+ started_user = published[0][1]["item"]
1177
+ started_command = published[1][1]["item"]
1178
+ completed_user = result["backendNotifications"][0]["params"]["item"]
1179
+ accepted_command = result["backendNotifications"][1]["params"]["item"]
1180
+ assert started_user["id"] == completed_user["id"]
1181
+ assert started_command["id"] == accepted_command["id"]
1182
+ assert accepted_command["content"]["phase"] == "accepted"
1183
+ assert accepted_command["content"]["data"]["turnId"] == "turn_review"
1184
+
1185
+ asyncio.run(exercise())
1186
+
1187
+
1188
+ def test_review_command_continues_when_started_notification_enqueue_fails() -> None:
1189
+ async def exercise() -> None:
1190
+ rpc = FakeCodexRpc()
1191
+
1192
+ async def failing_sink(_method: str, _params: dict[str, Any]) -> None:
1193
+ raise OSError("temporary ingest failure")
1194
+
1195
+ adapter = CodexAdapter(rpc=rpc, notification_sink=failing_sink) # type: ignore[arg-type]
1196
+ result = await adapter.execute_command(
1197
+ {
1198
+ "sessionId": "sess_1",
1199
+ "externalSessionId": "thr_1",
1200
+ "command": "review",
1201
+ "clientCommandId": "cmd_review",
1202
+ }
1203
+ )
1204
+
1205
+ assert result["status"] == "accepted"
1206
+ assert any(method == "review/start" for method, _params in rpc.requests)
1207
+ assert result["backendNotifications"][1]["params"]["item"]["content"]["phase"] == "accepted"
1208
+
1209
+ asyncio.run(exercise())
1210
+
1211
+
1152
1212
  def test_status_command_returns_thread_and_cached_token_usage() -> None:
1153
1213
  async def exercise() -> None:
1154
1214
  rpc = FakeCodexRpc()
@@ -142,6 +142,70 @@ def test_connector_runtime_dispatches_request_and_forwards_notifications() -> No
142
142
  asyncio.run(_exercise_runtime())
143
143
 
144
144
 
145
+ def test_connector_runtime_queues_command_notifications_without_synchronous_ingest() -> None:
146
+ async def exercise() -> None:
147
+ class CommandAdapter(FakeAdapter):
148
+ async def execute_command(self, params: dict[str, Any]) -> dict[str, Any]:
149
+ self.calls.append(("command.execute", params))
150
+ return {
151
+ "status": "accepted",
152
+ "backendNotifications": [
153
+ {"method": "timeline.itemUpsert", "params": {"sessionId": "sess_1", "item": {"id": "cmd"}}},
154
+ {"method": "timeline.itemUpsert", "params": {"sessionId": "sess_1", "item": {"id": "result"}}},
155
+ ],
156
+ }
157
+
158
+ adapter = CommandAdapter()
159
+ client = BackendRpcClient(
160
+ ConnectorConfig(
161
+ server_url="http://127.0.0.1:8000",
162
+ connector_id="conn_1",
163
+ connector_token="token",
164
+ sync_existing_on_connect=False,
165
+ ),
166
+ adapter=adapter, # type: ignore[arg-type]
167
+ )
168
+ queued: list[tuple[str, dict[str, Any]]] = []
169
+
170
+ async def queue(method: str, params: dict[str, Any]) -> None:
171
+ queued.append((method, params))
172
+
173
+ async def fail_sync_ingest(_notifications: list[dict[str, Any]]) -> None:
174
+ raise AssertionError("command notifications must not use synchronous ingest")
175
+
176
+ client.send_backend_notification = queue # type: ignore[method-assign]
177
+ client.ingest_notifications = fail_sync_ingest # type: ignore[method-assign]
178
+
179
+ result = await client.dispatch(
180
+ "command.execute",
181
+ {
182
+ "runtime": "codex",
183
+ "sessionId": "sess_1",
184
+ "externalSessionId": "thr_1",
185
+ "command": "review",
186
+ },
187
+ )
188
+
189
+ assert result == {"status": "accepted"}
190
+ assert [params["item"]["id"] for _method, params in queued] == ["cmd", "result"]
191
+
192
+ async def fail_queue(_method: str, _params: dict[str, Any]) -> None:
193
+ raise OSError("outbox unavailable")
194
+
195
+ client.send_backend_notification = fail_queue # type: ignore[method-assign]
196
+ assert await client.dispatch(
197
+ "command.execute",
198
+ {
199
+ "runtime": "codex",
200
+ "sessionId": "sess_1",
201
+ "externalSessionId": "thr_1",
202
+ "command": "review",
203
+ },
204
+ ) == {"status": "accepted"}
205
+
206
+ asyncio.run(exercise())
207
+
208
+
145
209
  def test_connector_config_saves_and_loads_local_json(tmp_path) -> None:
146
210
  path = tmp_path / "connector.json"
147
211
  config = ConnectorConfig(
@@ -487,7 +551,11 @@ async def _exercise_runtime() -> None:
487
551
  "id": "rpc_1",
488
552
  "type": "response",
489
553
  "ok": True,
490
- "result": {"sessionId": "sess_1", "externalSessionId": "thr_1"},
554
+ "result": {
555
+ "sessionId": "sess_1",
556
+ "externalSessionId": "thr_1",
557
+ "runtimeLaunch": {"mode": "auto", "source": "auto"},
558
+ },
491
559
  }
492
560
 
493
561
  await client.handle_message(
@@ -17,4 +17,4 @@ def test_connector_version_falls_back_in_source_tree(monkeypatch) -> None:
17
17
 
18
18
  monkeypatch.setattr(version_module, "version", missing)
19
19
 
20
- assert version_module.connector_version() == "0.1.8"
20
+ assert version_module.connector_version() == "0.2.0"
@@ -8,7 +8,7 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "agentlink-cli"
11
- version = "0.1.8"
11
+ version = "0.2.0"
12
12
  source = { editable = "." }
13
13
  dependencies = [
14
14
  { name = "claude-agent-sdk" },
File without changes
File without changes
File without changes