@inline-chat/hermes-agent-adapter 0.0.17 → 0.0.18
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.
- package/README.md +11 -7
- package/dist/install.js +100 -82
- package/package.json +7 -7
- package/plugin/inline/adapter.py +12 -14
- package/plugin/inline/cli.py +48 -3
- package/plugin/inline/plugin.yaml +4 -1
- package/plugin/inline/sidecar/index.mjs +83 -1743
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inline-chat/hermes-agent-adapter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Hermes Agent platform adapter for Inline, with a native Python plugin and bundled Inline realtime sidecar.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
"adapter",
|
|
15
15
|
"realtime"
|
|
16
16
|
],
|
|
17
|
-
"homepage": "https://github.com/inline-chat/inline/tree/main/hermes-agent#readme",
|
|
17
|
+
"homepage": "https://github.com/inline-chat/inline/tree/main/plugins/hermes-agent#readme",
|
|
18
18
|
"bugs": {
|
|
19
19
|
"url": "https://github.com/inline-chat/inline/issues"
|
|
20
20
|
},
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
23
|
"url": "git+https://github.com/inline-chat/inline.git",
|
|
24
|
-
"directory": "hermes-agent"
|
|
24
|
+
"directory": "plugins/hermes-agent"
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
27
27
|
"LICENSE",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"build": "mkdir -p plugin/inline/sidecar && bun build ./src/install.ts --outdir dist --entry-naming '[name].js' --target=node --format=esm --packages=bundle && bun build ./src/sidecar/index.ts --outdir plugin/inline/sidecar --entry-naming 'index.mjs' --target=node --format=esm --packages=bundle && tsc -p tsconfig.json --emitDeclarationOnly",
|
|
54
|
-
"lint": "bunx oxlint --ignore-path
|
|
54
|
+
"lint": "bunx oxlint --ignore-path ../../.oxlintignore scripts/release-stage.mjs src/install.ts src/sidecar/contract.ts src/sidecar/index.ts src/sidecar/telemetry.ts src/sidecar/telemetry.test.ts src/sidecar/user-directory.ts src/sidecar/user-directory.test.ts src/sidecar/inbound-delivery.ts src/sidecar/inbound-delivery.test.ts src/sidecar/inbound-stream.ts src/sidecar/inbound-stream.test.ts tests/inbound-flow.test.ts tests/adapter-python.test.ts tests/install.test.ts tests/package-artifact.test.ts tests/sidecar-contract.test.ts tests/sidecar-runtime.test.ts tests/telemetry-python.test.ts vitest.config.ts",
|
|
55
55
|
"pretest": "bun run build",
|
|
56
56
|
"prepack": "bun run build",
|
|
57
57
|
"prepublishOnly": "bun run check",
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"npmSpec": "@inline-chat/hermes-agent-adapter"
|
|
82
82
|
},
|
|
83
83
|
"machineSetupProtocol": 1,
|
|
84
|
-
"minHermesVersion": "0.
|
|
85
|
-
"testedHermesVersion": "0.21.
|
|
86
|
-
"testedHermesCommit": "
|
|
84
|
+
"minHermesVersion": "0.21.4",
|
|
85
|
+
"testedHermesVersion": "0.21.5",
|
|
86
|
+
"testedHermesCommit": "f97608f178d1ffeca59860195ab7da295f7c8e5f"
|
|
87
87
|
}
|
|
88
88
|
}
|
package/plugin/inline/adapter.py
CHANGED
|
@@ -45,6 +45,7 @@ from gateway.platforms.base import (
|
|
|
45
45
|
SendResult,
|
|
46
46
|
cache_audio_from_url,
|
|
47
47
|
cache_image_from_url,
|
|
48
|
+
safe_url_for_log,
|
|
48
49
|
)
|
|
49
50
|
from gateway.platforms.helpers import strip_markdown
|
|
50
51
|
|
|
@@ -575,7 +576,7 @@ def _normalize_inline_command_description(raw: str) -> str:
|
|
|
575
576
|
|
|
576
577
|
|
|
577
578
|
def _inline_menu_commands(max_commands: int = _INLINE_COMMAND_LIMIT) -> tuple[List[Dict[str, Any]], int]:
|
|
578
|
-
from hermes_cli.
|
|
579
|
+
from hermes_cli.commands_platforms import telegram_menu_commands
|
|
579
580
|
|
|
580
581
|
command_specs = _inline_command_specs()
|
|
581
582
|
local_commands = [
|
|
@@ -1209,7 +1210,7 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1209
1210
|
|
|
1210
1211
|
try:
|
|
1211
1212
|
from gateway.run import _load_gateway_config
|
|
1212
|
-
from hermes_cli.
|
|
1213
|
+
from hermes_cli.model_switch_providers import list_picker_providers
|
|
1213
1214
|
|
|
1214
1215
|
cfg = _load_gateway_config() or {}
|
|
1215
1216
|
model_cfg = cfg.get("model") if isinstance(cfg, dict) else {}
|
|
@@ -3001,16 +3002,15 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
3001
3002
|
return await cache_audio_from_url(url, ext=_extension_for_media(mime, file_name, ".ogg"))
|
|
3002
3003
|
return await self._download_inline_media_url(url, mime=mime, file_name=file_name)
|
|
3003
3004
|
except Exception as exc:
|
|
3004
|
-
logger.warning("[inline] failed to cache %s attachment
|
|
3005
|
-
|
|
3005
|
+
logger.warning("[inline] failed to cache %s attachment (%s)", kind, type(exc).__name__)
|
|
3006
|
+
# Never pass an unvalidated URL on to Hermes for another fetch.
|
|
3007
|
+
return None
|
|
3006
3008
|
|
|
3007
3009
|
async def _download_inline_media_url(self, url: str, *, mime: str, file_name: Optional[str]) -> str:
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
except ImportError:
|
|
3013
|
-
pass
|
|
3010
|
+
# A missing host safety helper must stop the download, never bypass it.
|
|
3011
|
+
from tools.url_safety import is_safe_url
|
|
3012
|
+
if not is_safe_url(url):
|
|
3013
|
+
raise ValueError(f"blocked unsafe media URL: {safe_url_for_log(url)}")
|
|
3014
3014
|
_MEDIA_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
3015
3015
|
name = _safe_media_file_name(url=url, mime=mime, file_name=file_name)
|
|
3016
3016
|
path = _MEDIA_CACHE_DIR / name
|
|
@@ -4230,7 +4230,7 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4230
4230
|
label = group_id
|
|
4231
4231
|
member_slugs: list[str] = []
|
|
4232
4232
|
try:
|
|
4233
|
-
from hermes_cli.
|
|
4233
|
+
from hermes_cli.models_catalog_static import PROVIDER_GROUPS
|
|
4234
4234
|
label, _desc, members = PROVIDER_GROUPS.get(group_id, (group_id, "", []))
|
|
4235
4235
|
member_slugs = [str(member) for member in members]
|
|
4236
4236
|
except Exception:
|
|
@@ -4686,7 +4686,6 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4686
4686
|
|
|
4687
4687
|
async def send_image(self, chat_id: str, image_url: str, caption: Optional[str] = None, reply_to: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> SendResult:
|
|
4688
4688
|
try:
|
|
4689
|
-
from gateway.platforms.base import cache_image_from_url
|
|
4690
4689
|
local_path = await cache_image_from_url(image_url)
|
|
4691
4690
|
return await self.send_image_file(chat_id, local_path, caption, reply_to, metadata)
|
|
4692
4691
|
except Exception:
|
|
@@ -4694,7 +4693,6 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4694
4693
|
|
|
4695
4694
|
async def send_animation(self, chat_id: str, animation_url: str, caption: Optional[str] = None, reply_to: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> SendResult:
|
|
4696
4695
|
try:
|
|
4697
|
-
from gateway.platforms.base import cache_image_from_url
|
|
4698
4696
|
local_path = await cache_image_from_url(animation_url, ext=".gif")
|
|
4699
4697
|
return await self.send_document(chat_id, local_path, caption, file_name=Path(local_path).name, reply_to=reply_to, metadata=metadata)
|
|
4700
4698
|
except Exception:
|
|
@@ -4982,7 +4980,7 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4982
4980
|
}
|
|
4983
4981
|
actions: list[Dict[str, str]] = []
|
|
4984
4982
|
try:
|
|
4985
|
-
from hermes_cli.
|
|
4983
|
+
from hermes_cli.models_catalog_static import group_providers
|
|
4986
4984
|
grouped = group_providers(list(by_slug.keys()))
|
|
4987
4985
|
except Exception:
|
|
4988
4986
|
grouped = [{"kind": "single", "slug": slug} for slug in by_slug.keys()]
|
package/plugin/inline/cli.py
CHANGED
|
@@ -27,7 +27,7 @@ _CLI_INSTALL_URL = "https://inline.chat/cli/install.sh"
|
|
|
27
27
|
_MAX_TOKEN_BYTES = 16 * 1024
|
|
28
28
|
_MAX_PROBE_RESPONSE_BYTES = 64 * 1024
|
|
29
29
|
_MACHINE_SETUP_PROTOCOL_VERSION = 1
|
|
30
|
-
_PROBE_USER_AGENT = "inline-hermes-agent-adapter/0.0.
|
|
30
|
+
_PROBE_USER_AGENT = "inline-hermes-agent-adapter/0.0.18"
|
|
31
31
|
_ENV_REFERENCE_RE = re.compile(r"^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$")
|
|
32
32
|
|
|
33
33
|
|
|
@@ -362,6 +362,7 @@ def register_cli(parser: argparse.ArgumentParser) -> None:
|
|
|
362
362
|
setup.add_argument("--json", action="store_true", help="Print compact machine-readable setup output.")
|
|
363
363
|
status = subs.add_parser("status", help="Show Inline adapter status", description="Check Inline configuration, sidecar, Node runtime, and optional credential identity.")
|
|
364
364
|
status.add_argument("--json", action="store_true", help="Print compact machine-readable status output.")
|
|
365
|
+
status.add_argument("--check-compatibility", action="store_true", help="Validate the installed plugin with the Hermes loader.")
|
|
365
366
|
status.add_argument("--probe", action="store_true", help="Verify the configured Inline credential and bot identity.")
|
|
366
367
|
parser.set_defaults(func=dispatch)
|
|
367
368
|
|
|
@@ -453,7 +454,9 @@ def _status(args) -> int:
|
|
|
453
454
|
and sidecar["size"] > 0
|
|
454
455
|
)
|
|
455
456
|
runtime_usable = bool(sidecar["ok"] and node["ok"])
|
|
456
|
-
|
|
457
|
+
compatibility = _compatibility_status() if getattr(args, "check_compatibility", False) else None
|
|
458
|
+
compatible = compatibility is None or compatibility["ok"]
|
|
459
|
+
ready = compatible and runtime_usable and configured and (not probe_requested or bool(probe and probe.get("ok")))
|
|
457
460
|
result = {
|
|
458
461
|
"ok": ready,
|
|
459
462
|
"ready": ready,
|
|
@@ -467,11 +470,14 @@ def _status(args) -> int:
|
|
|
467
470
|
"node": node,
|
|
468
471
|
"probeRequested": probe_requested,
|
|
469
472
|
"gateway": _gateway_status(),
|
|
473
|
+
**({"compatibility": compatibility} if compatibility is not None else {}),
|
|
470
474
|
**({"probe": probe} if probe is not None else {}),
|
|
471
475
|
}
|
|
472
476
|
if getattr(args, "json", False):
|
|
473
477
|
print(json.dumps(result, separators=(",", ":")))
|
|
474
478
|
else:
|
|
479
|
+
if compatibility is not None:
|
|
480
|
+
print(f"Hermes plugin compatible: {'yes' if compatibility['ok'] else 'no'} ({compatibility['reason']})")
|
|
475
481
|
print(f"Inline configured: {'yes' if configured else 'no'}")
|
|
476
482
|
print(f"Inline sidecar usable: {'yes' if sidecar['ok'] else 'no'}")
|
|
477
483
|
print(f"Node available: {_node_status_text(node)}")
|
|
@@ -481,7 +487,46 @@ def _status(args) -> int:
|
|
|
481
487
|
elif probe_requested:
|
|
482
488
|
print(f"Inline credential probe: {'ready' if ready else 'failed'}")
|
|
483
489
|
print("Advanced diagnostics: inline-hermes doctor --json")
|
|
484
|
-
return 0 if runtime_usable and (not probe_requested or ready) else 1
|
|
490
|
+
return 0 if compatible and runtime_usable and (not probe_requested or ready) else 1
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def _compatibility_status() -> dict:
|
|
494
|
+
"""Attest only a successful host load of this exact installed plugin.
|
|
495
|
+
|
|
496
|
+
Never return host exception text: configuration or import errors may contain
|
|
497
|
+
credentials. A missing diagnostic API is an inconclusive check, not success.
|
|
498
|
+
"""
|
|
499
|
+
unavailable = {"ok": False, "reason": "loader_unavailable"}
|
|
500
|
+
try:
|
|
501
|
+
from hermes_cli.plugins import get_plugin_manager
|
|
502
|
+
|
|
503
|
+
manager = get_plugin_manager()
|
|
504
|
+
manager.discover_and_load()
|
|
505
|
+
plugin_dir = Path(__file__).resolve().parent
|
|
506
|
+
loaded = next((
|
|
507
|
+
item for item in manager._plugins.values()
|
|
508
|
+
if item.manifest.name == "inline-platform"
|
|
509
|
+
and Path(item.manifest.path).resolve() == plugin_dir
|
|
510
|
+
), None)
|
|
511
|
+
if loaded is None:
|
|
512
|
+
return {"ok": False, "reason": "plugin_not_loaded"}
|
|
513
|
+
if not loaded.enabled or loaded.error or loaded.module is None:
|
|
514
|
+
return {"ok": False, "reason": "plugin_load_failed"}
|
|
515
|
+
if "inline" not in loaded.tools_registered:
|
|
516
|
+
return {"ok": False, "reason": "tool_not_registered"}
|
|
517
|
+
# Older supported Hermes releases predate the moved-import scanner.
|
|
518
|
+
# Their loader still checks real imports and plugin registration above.
|
|
519
|
+
try:
|
|
520
|
+
from hermes_cli.plugin_compat import plugin_hits
|
|
521
|
+
except ModuleNotFoundError as error:
|
|
522
|
+
if error.name != "hermes_cli.plugin_compat":
|
|
523
|
+
raise
|
|
524
|
+
else:
|
|
525
|
+
if plugin_hits(loaded.manifest):
|
|
526
|
+
return {"ok": False, "reason": "deprecated_imports"}
|
|
527
|
+
return {"ok": True, "reason": "loaded", "pluginPath": str(plugin_dir)}
|
|
528
|
+
except Exception:
|
|
529
|
+
return unavailable
|
|
485
530
|
|
|
486
531
|
|
|
487
532
|
def _gateway_status() -> dict:
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
name: inline-platform
|
|
2
2
|
label: Inline
|
|
3
3
|
kind: platform
|
|
4
|
-
|
|
4
|
+
provides_tools:
|
|
5
|
+
- inline
|
|
6
|
+
version: 0.0.18
|
|
7
|
+
requires_hermes: ">=0.21.4"
|
|
5
8
|
description: >
|
|
6
9
|
Inline platform adapter for Hermes Agent. The adapter runs as a native
|
|
7
10
|
Hermes Python platform plugin and supervises a local Node sidecar that uses
|