agentlink-cli 0.1.2__tar.gz → 0.1.3__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.
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/.gitignore +8 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/PKG-INFO +4 -4
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/README.md +3 -3
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/reducer.py +32 -1
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/version.py +1 -1
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/pyproject.toml +1 -1
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_codex_adapter.py +100 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_version.py +1 -1
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/uv.lock +1 -1
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/config_options.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/discovery.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifest.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifests/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifests/codebuddy.json +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifests/cursor.json +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifests/gemini.json +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/manifests/grok_build.json +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/reducer.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/acp/rpc.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/attachments.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/capabilities.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/history_adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/normalized.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/normalizers.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/path_utils.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/preferences.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/sdk_adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/timeline_identity.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/timeline_reducer.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/claude/trust.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/cli.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/collaboration.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/history.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/codex/rpc.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/control.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/json_rpc.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/launch.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/common.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/file_ops.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/ops.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/shell.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local/terminal.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local_ops.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/local_runtime.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/logging.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/perf.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/protocol.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/registry.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/runtime.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/sync_state.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/connector/time.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/run.sh +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/fake_agent/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/fake_agent/__main__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/test_acp_adapter_fake.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/test_acp_reducer_golden.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/acp/test_config_options.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/contract/__init__.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/contract/helpers.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_claude_history_adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_claude_preferences.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_claude_sdk_adapter.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_claude_timeline_parity.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_claude_trust.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_capabilities.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_cli.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_control.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_json_rpc.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_logging.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_connector_runtime.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_launch_command.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_local_runtime.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/tests/test_perf.py +0 -0
- {agentlink_cli-0.1.2 → agentlink_cli-0.1.3}/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.
|
|
3
|
+
Version: 0.1.3
|
|
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 =
|
|
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,
|
|
@@ -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
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|