agentlink-cli 0.1.8__tar.gz → 0.1.9__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.1.9}/PKG-INFO +1 -1
  2. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/adapter.py +11 -0
  3. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/runtime.py +10 -1
  4. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/version.py +1 -1
  5. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/pyproject.toml +1 -1
  6. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_codex_adapter.py +60 -0
  7. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_runtime.py +64 -0
  8. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_version.py +1 -1
  9. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/uv.lock +1 -1
  10. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/.gitignore +0 -0
  11. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/README.md +0 -0
  12. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/__init__.py +0 -0
  13. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/__init__.py +0 -0
  14. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/adapter.py +0 -0
  15. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/config_options.py +0 -0
  16. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/discovery.py +0 -0
  17. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifest.py +0 -0
  18. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifests/__init__.py +0 -0
  19. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifests/codebuddy.json +0 -0
  20. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifests/cursor.json +0 -0
  21. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifests/gemini.json +0 -0
  22. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/manifests/grok_build.json +0 -0
  23. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/reducer.py +0 -0
  24. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/acp/rpc.py +0 -0
  25. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/adapter.py +0 -0
  26. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/attachments.py +0 -0
  27. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/capabilities.py +0 -0
  28. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/__init__.py +0 -0
  29. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/history_adapter.py +0 -0
  30. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/normalized.py +0 -0
  31. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/normalizers.py +0 -0
  32. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/path_utils.py +0 -0
  33. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/preferences.py +0 -0
  34. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/sdk_adapter.py +0 -0
  35. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/timeline_identity.py +0 -0
  36. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/timeline_reducer.py +0 -0
  37. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/claude/trust.py +0 -0
  38. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/cli.py +0 -0
  39. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/__init__.py +0 -0
  40. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/collaboration.py +0 -0
  41. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/history.py +0 -0
  42. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/reducer.py +0 -0
  43. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/codex/rpc.py +0 -0
  44. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/control.py +0 -0
  45. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/json_rpc.py +0 -0
  46. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/launch.py +0 -0
  47. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/__init__.py +0 -0
  48. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/common.py +0 -0
  49. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/file_ops.py +0 -0
  50. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/ops.py +0 -0
  51. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/shell.py +0 -0
  52. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local/terminal.py +0 -0
  53. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local_ops.py +0 -0
  54. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/local_runtime.py +0 -0
  55. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/logging.py +0 -0
  56. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/perf.py +0 -0
  57. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/protocol.py +0 -0
  58. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/registry.py +0 -0
  59. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/sync_state.py +0 -0
  60. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/connector/time.py +0 -0
  61. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/run.sh +0 -0
  62. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/__init__.py +0 -0
  63. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/__init__.py +0 -0
  64. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/fake_agent/__init__.py +0 -0
  65. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/fake_agent/__main__.py +0 -0
  66. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/test_acp_adapter_fake.py +0 -0
  67. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/test_acp_reducer_golden.py +0 -0
  68. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/acp/test_config_options.py +0 -0
  69. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/contract/__init__.py +0 -0
  70. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/contract/helpers.py +0 -0
  71. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_claude_history_adapter.py +0 -0
  72. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_claude_preferences.py +0 -0
  73. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_claude_sdk_adapter.py +0 -0
  74. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_claude_timeline_parity.py +0 -0
  75. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_claude_trust.py +0 -0
  76. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_capabilities.py +0 -0
  77. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_cli.py +0 -0
  78. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_control.py +0 -0
  79. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_json_rpc.py +0 -0
  80. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_connector_logging.py +0 -0
  81. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_launch_command.py +0 -0
  82. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_local_runtime.py +0 -0
  83. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/tests/test_perf.py +0 -0
  84. {agentlink_cli-0.1.8 → agentlink_cli-0.1.9}/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.1.9
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})
@@ -497,7 +497,16 @@ class BackendRpcClient:
497
497
  if execute_command is None:
498
498
  raise ValueError("command_unavailable")
499
499
  result = await execute_command({**params, "connectorId": self.config.connector_id})
500
- await self._send_backend_notifications(result)
500
+ notifications = result.get("backendNotifications", [])
501
+ if isinstance(notifications, list):
502
+ try:
503
+ await self.enqueue_backend_notifications(notifications)
504
+ except Exception: # noqa: BLE001
505
+ logger.exception(
506
+ "command timeline notification enqueue failed session_id={} command={}",
507
+ params.get("sessionId"),
508
+ params.get("command"),
509
+ )
501
510
  return _strip_backend_notifications(result)
502
511
  if method == "turn.interrupt":
503
512
  return await self._resolve_adapter(params).interrupt_turn(params)
@@ -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.1.9"
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.1.9"
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(
@@ -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.1.9"
@@ -8,7 +8,7 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "agentlink-cli"
11
- version = "0.1.8"
11
+ version = "0.1.9"
12
12
  source = { editable = "." }
13
13
  dependencies = [
14
14
  { name = "claude-agent-sdk" },
File without changes
File without changes
File without changes