agentlink-cli 0.1.2__tar.gz → 0.1.4__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.2 → agentlink_cli-0.1.4}/.gitignore +8 -0
  2. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/PKG-INFO +4 -4
  3. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/README.md +3 -3
  4. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/reducer.py +32 -1
  5. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/version.py +1 -1
  6. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/pyproject.toml +1 -1
  7. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_codex_adapter.py +100 -0
  8. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_version.py +1 -1
  9. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/uv.lock +1 -1
  10. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/__init__.py +0 -0
  11. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/__init__.py +0 -0
  12. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/adapter.py +0 -0
  13. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/config_options.py +0 -0
  14. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/discovery.py +0 -0
  15. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifest.py +0 -0
  16. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifests/__init__.py +0 -0
  17. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifests/codebuddy.json +0 -0
  18. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifests/cursor.json +0 -0
  19. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifests/gemini.json +0 -0
  20. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/manifests/grok_build.json +0 -0
  21. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/reducer.py +0 -0
  22. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/acp/rpc.py +0 -0
  23. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/adapter.py +0 -0
  24. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/attachments.py +0 -0
  25. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/capabilities.py +0 -0
  26. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/__init__.py +0 -0
  27. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/history_adapter.py +0 -0
  28. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/normalized.py +0 -0
  29. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/normalizers.py +0 -0
  30. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/path_utils.py +0 -0
  31. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/preferences.py +0 -0
  32. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/sdk_adapter.py +0 -0
  33. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/timeline_identity.py +0 -0
  34. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/timeline_reducer.py +0 -0
  35. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/claude/trust.py +0 -0
  36. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/cli.py +0 -0
  37. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/__init__.py +0 -0
  38. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/adapter.py +0 -0
  39. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/collaboration.py +0 -0
  40. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/history.py +0 -0
  41. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/codex/rpc.py +0 -0
  42. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/control.py +0 -0
  43. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/json_rpc.py +0 -0
  44. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/launch.py +0 -0
  45. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/__init__.py +0 -0
  46. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/common.py +0 -0
  47. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/file_ops.py +0 -0
  48. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/ops.py +0 -0
  49. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/shell.py +0 -0
  50. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local/terminal.py +0 -0
  51. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local_ops.py +0 -0
  52. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/local_runtime.py +0 -0
  53. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/logging.py +0 -0
  54. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/perf.py +0 -0
  55. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/protocol.py +0 -0
  56. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/registry.py +0 -0
  57. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/runtime.py +0 -0
  58. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/sync_state.py +0 -0
  59. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/connector/time.py +0 -0
  60. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/run.sh +0 -0
  61. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/__init__.py +0 -0
  62. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/__init__.py +0 -0
  63. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/fake_agent/__init__.py +0 -0
  64. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/fake_agent/__main__.py +0 -0
  65. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/test_acp_adapter_fake.py +0 -0
  66. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/test_acp_reducer_golden.py +0 -0
  67. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/acp/test_config_options.py +0 -0
  68. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/contract/__init__.py +0 -0
  69. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/contract/helpers.py +0 -0
  70. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_claude_history_adapter.py +0 -0
  71. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_claude_preferences.py +0 -0
  72. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_claude_sdk_adapter.py +0 -0
  73. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_claude_timeline_parity.py +0 -0
  74. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_claude_trust.py +0 -0
  75. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_capabilities.py +0 -0
  76. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_cli.py +0 -0
  77. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_control.py +0 -0
  78. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_json_rpc.py +0 -0
  79. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_logging.py +0 -0
  80. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_connector_runtime.py +0 -0
  81. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_launch_command.py +0 -0
  82. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_local_runtime.py +0 -0
  83. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_perf.py +0 -0
  84. {agentlink_cli-0.1.2 → agentlink_cli-0.1.4}/tests/test_terminal_backend.py +0 -0
@@ -10,6 +10,14 @@ __pycache__/
10
10
  web-next/out/
11
11
  .env.local
12
12
  .env.*.local
13
+ .env
14
+ !.env.example
15
+
16
+ # Android SDK and release signing secrets
17
+ android/local.properties
18
+ android/keystore.properties
19
+ *.jks
20
+ *.keystore
13
21
 
14
22
  # Local Claude tooling (launch.json, transcripts, worktree metadata)
15
23
  .claude/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentlink-cli
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Local runtime connector for AgentLink
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: claude-agent-sdk
@@ -47,7 +47,7 @@ uv sync
47
47
  Start with explicit credentials from the web pairing flow:
48
48
 
49
49
  ```bash
50
- uvx agentlink-cli start \
50
+ uvx agentlink-cli@latest start \
51
51
  --server-url http://127.0.0.1:8000 \
52
52
  --connector-id conn_xxx \
53
53
  --connector-token cxt_xxx
@@ -60,12 +60,12 @@ listed below.
60
60
  Or save the config locally and start without arguments:
61
61
 
62
62
  ```bash
63
- uvx agentlink-cli configure \
63
+ uvx agentlink-cli@latest configure \
64
64
  --server-url http://127.0.0.1:8000 \
65
65
  --connector-id conn_xxx \
66
66
  --connector-token cxt_xxx
67
67
 
68
- uvx agentlink-cli start
68
+ uvx agentlink-cli@latest start
69
69
  ```
70
70
 
71
71
  The default config path is `~/.agent-server/connector.json`. Override it with
@@ -30,7 +30,7 @@ uv sync
30
30
  Start with explicit credentials from the web pairing flow:
31
31
 
32
32
  ```bash
33
- uvx agentlink-cli start \
33
+ uvx agentlink-cli@latest start \
34
34
  --server-url http://127.0.0.1:8000 \
35
35
  --connector-id conn_xxx \
36
36
  --connector-token cxt_xxx
@@ -43,12 +43,12 @@ listed below.
43
43
  Or save the config locally and start without arguments:
44
44
 
45
45
  ```bash
46
- uvx agentlink-cli configure \
46
+ uvx agentlink-cli@latest configure \
47
47
  --server-url http://127.0.0.1:8000 \
48
48
  --connector-id conn_xxx \
49
49
  --connector-token cxt_xxx
50
50
 
51
- uvx agentlink-cli start
51
+ uvx agentlink-cli@latest start
52
52
  ```
53
53
 
54
54
  The default config path is `~/.agent-server/connector.json`. Override it with
@@ -36,6 +36,8 @@ class TimelineReducer:
36
36
  self._client_message_by_turn: dict[tuple[str, str | None, str], dict[str, Any]] = {}
37
37
  self._pending_client_messages: dict[tuple[str, str | None], list[dict[str, Any]]] = {}
38
38
  self._reasoning_index_by_turn: dict[tuple[str, str], int] = {}
39
+ self._active_review_key: dict[tuple[str, str | None], str] = {}
40
+ self._review_key_by_item: dict[tuple[str, str | None, str], str] = {}
39
41
  self._pending_collaboration_mode: dict[tuple[str, str], str] = {}
40
42
  self._collaboration_mode_by_turn: OrderedDict[tuple[str, str, str], str] = OrderedDict()
41
43
  self._next_order = 1
@@ -506,7 +508,11 @@ class TimelineReducer:
506
508
  ) -> dict[str, Any] | None:
507
509
  codex_type = _string_value(item.get("type")) or "unknown"
508
510
  item_id = _string_value(item.get("id")) or _string_value(item.get("itemId")) or _string_value(item.get("call_id")) or _short_hash(item)
509
- derived_key = _stable_item_key(item)
511
+ derived_key = (
512
+ self._review_item_key(session_id, thread_id, turn_id, item_id, codex_type)
513
+ if codex_type in {"enteredReviewMode", "exitedReviewMode"}
514
+ else _stable_item_key(item)
515
+ )
510
516
  source_item_id = _string_value(item.get("_eventItemId")) or item_id
511
517
  status = _timeline_status(item.get("status")) or "done"
512
518
  role: str | None = None
@@ -639,6 +645,31 @@ class TimelineReducer:
639
645
  source_extra=source_extra,
640
646
  )
641
647
 
648
+ def _review_item_key(
649
+ self,
650
+ session_id: str,
651
+ thread_id: str | None,
652
+ turn_id: str | None,
653
+ item_id: str,
654
+ codex_type: str,
655
+ ) -> str:
656
+ if turn_id is not None:
657
+ return "review"
658
+
659
+ item_key = (session_id, thread_id, item_id)
660
+ known_key = self._review_key_by_item.get(item_key)
661
+ if known_key is not None:
662
+ return known_key
663
+
664
+ scope = (session_id, thread_id)
665
+ if codex_type == "enteredReviewMode":
666
+ review_key = f"review-{item_id}"
667
+ self._active_review_key[scope] = review_key
668
+ else:
669
+ review_key = self._active_review_key.pop(scope, None) or f"review-{item_id}"
670
+ self._review_key_by_item[item_key] = review_key
671
+ return review_key
672
+
642
673
  def _client_message_for_user_message(
643
674
  self,
644
675
  session_id: str,
@@ -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.2"
5
+ FALLBACK_VERSION = "0.1.4"
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.2"
3
+ version = "0.1.4"
4
4
  description = "Local runtime connector for AgentLink"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1173,9 +1173,109 @@ def test_reducer_normalizes_review_and_compaction_items() -> None:
1173
1173
  "phase": "completed",
1174
1174
  "text": "Looks good.",
1175
1175
  }
1176
+ assert review.timeline_items[0]["turnId"] == "turn_1"
1176
1177
  assert compact.timeline_items[0]["content"]["kind"] == "codex_compaction"
1177
1178
 
1178
1179
 
1180
+ def test_reducer_keeps_unscoped_review_lifecycle_on_stable_derived_key() -> None:
1181
+ reducer = TimelineReducer()
1182
+ reducer.bind_session("sess_1", "thr_1")
1183
+
1184
+ user_message = reducer.reduce_notification(
1185
+ {
1186
+ "method": "item/completed",
1187
+ "params": {
1188
+ "threadId": "thr_1",
1189
+ "turnId": "turn_review_1",
1190
+ "item": {
1191
+ "id": "review_request_1",
1192
+ "type": "userMessage",
1193
+ "text": "Review the current changes",
1194
+ },
1195
+ },
1196
+ }
1197
+ ).timeline_items[0]
1198
+ entered = reducer.reduce_notification(
1199
+ {
1200
+ "method": "item/completed",
1201
+ "params": {
1202
+ "threadId": "thr_1",
1203
+ "item": {
1204
+ "id": "review_entered_1",
1205
+ "type": "enteredReviewMode",
1206
+ "review": "current changes",
1207
+ },
1208
+ },
1209
+ }
1210
+ ).timeline_items[0]
1211
+ completed = reducer.reduce_notification(
1212
+ {
1213
+ "method": "item/completed",
1214
+ "params": {
1215
+ "threadId": "thr_1",
1216
+ "item": {
1217
+ "id": "review_exited_1",
1218
+ "type": "exitedReviewMode",
1219
+ "review": "Looks good.",
1220
+ },
1221
+ },
1222
+ }
1223
+ ).timeline_items[0]
1224
+ ordinary_message = reducer.reduce_notification(
1225
+ {
1226
+ "method": "item/completed",
1227
+ "params": {
1228
+ "threadId": "thr_1",
1229
+ "item": {
1230
+ "id": "msg_1",
1231
+ "type": "agentMessage",
1232
+ "text": "Ordinary reply",
1233
+ },
1234
+ },
1235
+ }
1236
+ ).timeline_items[0]
1237
+ next_entered = reducer.reduce_notification(
1238
+ {
1239
+ "method": "item/completed",
1240
+ "params": {
1241
+ "threadId": "thr_1",
1242
+ "item": {
1243
+ "id": "review_entered_2",
1244
+ "type": "enteredReviewMode",
1245
+ "review": "next changes",
1246
+ },
1247
+ },
1248
+ }
1249
+ ).timeline_items[0]
1250
+ next_completed = reducer.reduce_notification(
1251
+ {
1252
+ "method": "item/completed",
1253
+ "params": {
1254
+ "threadId": "thr_1",
1255
+ "item": {
1256
+ "id": "review_exited_2",
1257
+ "type": "exitedReviewMode",
1258
+ "review": "Second review.",
1259
+ },
1260
+ },
1261
+ }
1262
+ ).timeline_items[0]
1263
+
1264
+ assert entered.get("turnId") is None
1265
+ assert completed.get("turnId") is None
1266
+ assert completed["source"].get("turnId") is None
1267
+ assert entered["id"] == completed["id"]
1268
+ assert user_message["id"] != completed["id"]
1269
+ assert completed["source"]["derivedKey"] == "review-review_entered_1"
1270
+ assert entered["source"]["itemId"] == "review_entered_1"
1271
+ assert completed["source"]["itemId"] == "review_exited_1"
1272
+ assert next_entered["id"] == next_completed["id"]
1273
+ assert next_completed["id"] != completed["id"]
1274
+ assert next_completed["source"]["derivedKey"] == "review-review_entered_2"
1275
+ assert ordinary_message.get("turnId") is None
1276
+ assert ordinary_message["source"].get("turnId") is None
1277
+
1278
+
1179
1279
  async def _exercise_adapter() -> None:
1180
1280
  notifications: list[tuple[str, dict[str, Any]]] = []
1181
1281
 
@@ -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.2"
20
+ assert version_module.connector_version() == "0.1.4"
@@ -8,7 +8,7 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "agentlink-cli"
11
- version = "0.1.2"
11
+ version = "0.1.4"
12
12
  source = { editable = "." }
13
13
  dependencies = [
14
14
  { name = "claude-agent-sdk" },
File without changes