agentlink-cli 0.1.9__tar.gz → 0.2.1__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.9 → agentlink_cli-0.2.1}/PKG-INFO +1 -1
  2. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/common.py +1 -3
  3. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/runtime.py +26 -2
  4. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/version.py +1 -1
  5. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/pyproject.toml +1 -1
  6. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_runtime.py +38 -1
  7. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_version.py +1 -1
  8. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/uv.lock +1 -1
  9. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/.gitignore +0 -0
  10. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/README.md +0 -0
  11. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/__init__.py +0 -0
  12. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/__init__.py +0 -0
  13. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/adapter.py +0 -0
  14. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/config_options.py +0 -0
  15. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/discovery.py +0 -0
  16. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifest.py +0 -0
  17. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifests/__init__.py +0 -0
  18. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifests/codebuddy.json +0 -0
  19. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifests/cursor.json +0 -0
  20. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifests/gemini.json +0 -0
  21. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/manifests/grok_build.json +0 -0
  22. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/reducer.py +0 -0
  23. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/acp/rpc.py +0 -0
  24. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/adapter.py +0 -0
  25. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/attachments.py +0 -0
  26. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/capabilities.py +0 -0
  27. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/__init__.py +0 -0
  28. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/history_adapter.py +0 -0
  29. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/normalized.py +0 -0
  30. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/normalizers.py +0 -0
  31. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/path_utils.py +0 -0
  32. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/preferences.py +0 -0
  33. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/sdk_adapter.py +0 -0
  34. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/timeline_identity.py +0 -0
  35. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/timeline_reducer.py +0 -0
  36. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/claude/trust.py +0 -0
  37. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/cli.py +0 -0
  38. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/__init__.py +0 -0
  39. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/adapter.py +0 -0
  40. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/collaboration.py +0 -0
  41. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/history.py +0 -0
  42. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/reducer.py +0 -0
  43. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/codex/rpc.py +0 -0
  44. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/control.py +0 -0
  45. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/json_rpc.py +0 -0
  46. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/launch.py +0 -0
  47. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/__init__.py +0 -0
  48. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/file_ops.py +0 -0
  49. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/ops.py +0 -0
  50. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/shell.py +0 -0
  51. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local/terminal.py +0 -0
  52. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local_ops.py +0 -0
  53. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/local_runtime.py +0 -0
  54. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/logging.py +0 -0
  55. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/perf.py +0 -0
  56. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/protocol.py +0 -0
  57. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/registry.py +0 -0
  58. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/sync_state.py +0 -0
  59. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/connector/time.py +0 -0
  60. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/run.sh +0 -0
  61. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/__init__.py +0 -0
  62. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/__init__.py +0 -0
  63. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/fake_agent/__init__.py +0 -0
  64. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/fake_agent/__main__.py +0 -0
  65. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/test_acp_adapter_fake.py +0 -0
  66. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/test_acp_reducer_golden.py +0 -0
  67. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/acp/test_config_options.py +0 -0
  68. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/contract/__init__.py +0 -0
  69. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/contract/helpers.py +0 -0
  70. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_claude_history_adapter.py +0 -0
  71. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_claude_preferences.py +0 -0
  72. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_claude_sdk_adapter.py +0 -0
  73. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_claude_timeline_parity.py +0 -0
  74. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_claude_trust.py +0 -0
  75. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_codex_adapter.py +0 -0
  76. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_capabilities.py +0 -0
  77. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_cli.py +0 -0
  78. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_control.py +0 -0
  79. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_json_rpc.py +0 -0
  80. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_connector_logging.py +0 -0
  81. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_launch_command.py +0 -0
  82. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_local_runtime.py +0 -0
  83. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/tests/test_perf.py +0 -0
  84. {agentlink_cli-0.1.9 → agentlink_cli-0.2.1}/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.9
3
+ Version: 0.2.1
4
4
  Summary: Local runtime connector for AgentLink
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: claude-agent-sdk
@@ -40,10 +40,8 @@ def workspace_root(params: dict[str, Any]) -> Path:
40
40
 
41
41
 
42
42
  def resolve_path(root: Path, raw_path: str) -> Path:
43
- if raw_path.startswith("~"):
44
- raise WorkspaceBoundaryError("path must stay within the workspace")
45
43
  root = root.resolve(strict=False)
46
- path = Path(raw_path)
44
+ path = Path(raw_path).expanduser()
47
45
  if not path.is_absolute():
48
46
  path = root / path
49
47
  resolved = path.resolve(strict=False)
@@ -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":
@@ -658,6 +660,8 @@ class BackendRpcClient:
658
660
  Exception: the first assistant text delta for a turn bypasses the
659
661
  window so TTFB is not inflated by FLUSH_WINDOW_SECONDS.
660
662
  """
663
+ if method == "session.updated" and isinstance(params, dict) and params.get("runtime") == "codex":
664
+ params = {**params, "runtimeLaunch": self._codex_launch_snapshot()}
661
665
  notification = {"method": method, "params": params}
662
666
  if self._outbox is not None:
663
667
  await asyncio.to_thread(self._outbox.add, method, params)
@@ -971,9 +975,12 @@ class BackendRpcClient:
971
975
  self._codex_launch_spec = {
972
976
  "mode": mode,
973
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 {}),
974
981
  }
975
982
  self._blocked_runtimes.discard("codex")
976
- 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"}}
977
984
  self._record_runtime_report("codex", report)
978
985
  except Exception:
979
986
  codex.rpc = old_rpc
@@ -989,7 +996,11 @@ class BackendRpcClient:
989
996
  async def _apply_codex_launch_spec(self, spec: dict[str, Any]) -> None:
990
997
  mode = str(spec.get("mode") or "auto")
991
998
  command = spec.get("command") if isinstance(spec.get("command"), str) else None
992
- 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
+ }
993
1004
  if normalized == self._codex_launch_spec and "codex" not in self._blocked_runtimes:
994
1005
  return
995
1006
  await self._configure_codex_launch({"runtime": "codex", **normalized})
@@ -1006,6 +1017,7 @@ class BackendRpcClient:
1006
1017
  self._codex_launch_spec = {
1007
1018
  "mode": str(spec.get("mode") or "auto"),
1008
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)}),
1009
1021
  }
1010
1022
  if self._codex_launch_spec.get("mode") == "command":
1011
1023
  self._blocked_runtimes.add("codex")
@@ -1161,8 +1173,20 @@ class BackendRpcClient:
1161
1173
  method = notification.get("method")
1162
1174
  params = notification.get("params")
1163
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()}
1164
1178
  await self.ingest_notifications([{"method": method, "params": params}])
1165
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
+
1166
1190
  async def enqueue_backend_notifications(self, notifications: list[dict[str, Any]]) -> None:
1167
1191
  for notification in notifications:
1168
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.9"
5
+ FALLBACK_VERSION = "0.2.1"
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.9"
3
+ version = "0.2.1"
4
4
  description = "Local runtime connector for AgentLink"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -324,6 +324,39 @@ def test_connector_rejects_workspace_symlink_escape(tmp_path) -> None:
324
324
  asyncio.run(exercise())
325
325
 
326
326
 
327
+ def test_connector_accepts_home_shorthand_within_home(tmp_path, monkeypatch) -> None:
328
+ async def exercise() -> None:
329
+ home = tmp_path / "home"
330
+ child = home / "project"
331
+ child.mkdir(parents=True)
332
+ (child / "README.md").write_text("home workspace", encoding="utf-8")
333
+ monkeypatch.setenv("HOME", str(home))
334
+ monkeypatch.setenv("USERPROFILE", str(home))
335
+
336
+ client = BackendRpcClient(
337
+ ConnectorConfig("http://127.0.0.1:8000", "conn_1", "secret"),
338
+ adapters={"codex": FakeAdapter()},
339
+ )
340
+ home_result = await client.dispatch("fs.readDir", {"root": "~", "path": "~"})
341
+ assert home_result["path"] == str(home)
342
+ assert [entry["name"] for entry in home_result["entries"]] == ["project"]
343
+
344
+ child_result = await client.dispatch(
345
+ "fs.readDir", {"root": "~", "path": "~/project"}
346
+ )
347
+ assert child_result["path"] == str(child)
348
+ assert [entry["name"] for entry in child_result["entries"]] == ["README.md"]
349
+
350
+ workspace = tmp_path / "workspace"
351
+ workspace.mkdir()
352
+ with pytest.raises(WorkspaceBoundaryError):
353
+ await client.dispatch(
354
+ "fs.readDir", {"root": str(workspace), "path": "~"}
355
+ )
356
+
357
+ asyncio.run(exercise())
358
+
359
+
327
360
  def test_notification_outbox_survives_reopen(tmp_path) -> None:
328
361
  path = tmp_path / "state.sqlite3"
329
362
  outbox = SqliteNotificationOutbox(
@@ -551,7 +584,11 @@ async def _exercise_runtime() -> None:
551
584
  "id": "rpc_1",
552
585
  "type": "response",
553
586
  "ok": True,
554
- "result": {"sessionId": "sess_1", "externalSessionId": "thr_1"},
587
+ "result": {
588
+ "sessionId": "sess_1",
589
+ "externalSessionId": "thr_1",
590
+ "runtimeLaunch": {"mode": "auto", "source": "auto"},
591
+ },
555
592
  }
556
593
 
557
594
  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.9"
20
+ assert version_module.connector_version() == "0.2.1"
@@ -8,7 +8,7 @@ resolution-markers = [
8
8
 
9
9
  [[package]]
10
10
  name = "agentlink-cli"
11
- version = "0.1.9"
11
+ version = "0.2.1"
12
12
  source = { editable = "." }
13
13
  dependencies = [
14
14
  { name = "claude-agent-sdk" },
File without changes
File without changes
File without changes