@securecoms/agent-bridge 0.11.3 → 0.16.0
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 +76 -0
- package/connectors/hermes/adapter.py +11 -1
- package/connectors/hermes/tests/test_adapter_wiring.py +33 -0
- package/dist/bin.js +2668 -96
- package/dist/bridge-agent.d.ts +331 -0
- package/dist/config.d.ts +3 -0
- package/dist/errors.d.ts +1 -1
- package/dist/http-server.d.ts +4 -4
- package/dist/index.js +2641 -109
- package/dist/memory-taps.d.ts +6 -2
- package/dist/rate-limit.d.ts +10 -0
- package/dist/service/harness-wire.d.ts +4 -0
- package/dist/service/install-prep.d.ts +1 -0
- package/dist/service/wire.d.ts +30 -0
- package/dist/summarize.d.ts +54 -0
- package/dist/voice/fast-client.d.ts +13 -0
- package/dist/voice/fast-prompt.d.ts +53 -0
- package/dist/voice/persona.d.ts +23 -0
- package/dist/voice/prompt-compiler.d.ts +4 -0
- package/dist/voice/settings.d.ts +31 -0
- package/package.json +6 -1
- package/dist/attachments.js +0 -49
- package/dist/auth.js +0 -56
- package/dist/bin.js.pre-c1-f803007 +0 -5795
- package/dist/bin.js.pre-c3-096a9a3 +0 -5832
- package/dist/bin.js.pre-ffrebuild-b162d4a +0 -5639
- package/dist/bin.js.pre-kpheal-20260708-150924 +0 -5531
- package/dist/bin.js.pre-kppersist-20260708-152341 +0 -5543
- package/dist/bin.js.pre-p0-20260708-102415 +0 -5196
- package/dist/bin.js.pre-p1b-02f47e1 +0 -5639
- package/dist/bin.js.pre-warmfix-20260708-153705 +0 -5613
- package/dist/bin.js.pretrace-113205 +0 -5395
- package/dist/bridge-agent.js +0 -831
- package/dist/config.js +0 -74
- package/dist/coordination.js +0 -63
- package/dist/errors.js +0 -25
- package/dist/http-server.js +0 -220
- package/dist/index.js.pre-p0-20260708-102415 +0 -4879
- package/dist/latency.js +0 -87
- package/dist/rate-limit.js +0 -56
- package/dist/service/config.js +0 -26
- package/dist/service/install.js +0 -60
- package/dist/service/launchd.js +0 -30
- package/dist/service/systemd.js +0 -25
- package/dist/ws-server.js +0 -254
package/README.md
CHANGED
|
@@ -359,6 +359,7 @@ pm2 start "npx @agentvault/connect-agent-bridge --credential ./agent.json" \
|
|
|
359
359
|
- Install **verifies the daemon actually came up** and fails loudly with the log path if it didn't — no silent crash-loops.
|
|
360
360
|
- Install echoes the credential's `device_id`, `issued_at`, and a redacted api-key fingerprint so you can confirm you installed the newest download. **Re-running install is the only way to update a live (e.g. rotated) credential** — replacing the downloaded file alone does nothing.
|
|
361
361
|
- Optional `--harness openclaw|hermes|custom` prints a paste-ready config snippet with the chosen port and the token-file path (the token itself never appears in output).
|
|
362
|
+
- **One harness gateway serves one agent.** The connector registers a single platform and resolves a single bound identity for the life of the gateway process, so a connector directory (`~/.hermes/plugins/securecoms`, `~/.openclaw/local-plugins/securecoms`) holds exactly one agent. Install and `wire` therefore **refuse** to overwrite a binding that names a different agent — doing so would take the harness away from that agent, not add one alongside it. To run two agents on one host, give each its own harness profile with its own connector directory and binding. To deliberately move a harness to a different agent, pass `--rebind`.
|
|
362
363
|
|
|
363
364
|
---
|
|
364
365
|
|
|
@@ -404,6 +405,81 @@ attachments: the cryptography lives in the daemon, not the agent.
|
|
|
404
405
|
|
|
405
406
|
---
|
|
406
407
|
|
|
408
|
+
## Voice fast path
|
|
409
|
+
|
|
410
|
+
On a live voice call, the daemon can send an instant, persona-voiced
|
|
411
|
+
one-sentence acknowledgement while your full agent harness is still thinking —
|
|
412
|
+
so the human hears *something* immediately instead of dead air.
|
|
413
|
+
|
|
414
|
+
**The credential file's `voice` block is now a fallback, not the primary
|
|
415
|
+
source.** As of `@securecoms/agent-bridge` 0.13.0, the daemon fetches this
|
|
416
|
+
agent's voice settings from the backend (`GET /api/v1/agents/{identity_id}/voice-settings`)
|
|
417
|
+
on start and again whenever it receives a `voice_settings_changed` ping, and
|
|
418
|
+
prefers that backend row whenever its `version` is ≥ 1 — an owner has saved
|
|
419
|
+
settings from the web app's "Voice settings" page (`/app/agents/:identityId/voice`).
|
|
420
|
+
A `version: 0` response (nothing saved yet) keeps the file's persona/voice/pace
|
|
421
|
+
as a default, but **the fast-path LLM call goes through the SecureComs
|
|
422
|
+
backend proxy** (`POST /api/v1/voice/fast-complete`) whenever the settings
|
|
423
|
+
fetch itself succeeds — `voice_settings_source` is `"backend"`, or `"file"`
|
|
424
|
+
with `version: 0` — and in that case the file's own
|
|
425
|
+
`fast_path.api_key`/`base_url`/`model` are ignored; only the file's `persona`
|
|
426
|
+
and `enabled` flag still matter, as the fallback source for those fields. The
|
|
427
|
+
daemon needs no API key of its own for the proxied path — the backend proxy
|
|
428
|
+
holds `OPENROUTER_API_KEY` and does the call. **If the settings fetch itself
|
|
429
|
+
fails at boot** (network error, backend down), the daemon has no proxy config
|
|
430
|
+
to fall back to and instead falls back to the file's own `fast_path` block
|
|
431
|
+
in full, including its `api_key`/`api_key_env`/`base_url`/`model` — the
|
|
432
|
+
daemon then calls your own model provider directly with that key until a
|
|
433
|
+
later fetch succeeds.
|
|
434
|
+
|
|
435
|
+
Check which source is active via the daemon's health endpoint:
|
|
436
|
+
```bash
|
|
437
|
+
curl -H "Authorization: Bearer $(cat <credential-dir>/bridge-token)" \
|
|
438
|
+
http://127.0.0.1:7077/healthz | jq '.voice_settings_version, .voice_settings_source'
|
|
439
|
+
```
|
|
440
|
+
`voice_settings_source` is `"backend"` (a saved row won), `"file"` (fell back
|
|
441
|
+
to the credential file's `voice` block), or `"none"` (neither is configured).
|
|
442
|
+
|
|
443
|
+
Add a `voice` block to `agent.json` (used only as the fallback described above):
|
|
444
|
+
|
|
445
|
+
```json
|
|
446
|
+
"voice": {
|
|
447
|
+
"persona": { "display_name": "Cortina",
|
|
448
|
+
"persona": "You are Cortina, a crisp, warm operations lead who co-owns AgentVault with Eric and Chris.",
|
|
449
|
+
"quirks": ["opens with 'Right —' when sure", "asks one clarifying question instead of guessing"] },
|
|
450
|
+
"fast_path": { "enabled": true, "base_url": "https://api.openai.com/v1", "model": "gpt-4o-mini",
|
|
451
|
+
"api_key_env": "SECURECOMS_VOICE_LLM_KEY", "timeout_ms": 2500, "max_tokens": 80 }
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
- `persona` describes how the fast-path model should speak; it is folded into
|
|
456
|
+
a compiled-in system prompt shared with the daemon's other voice guidance.
|
|
457
|
+
- `fast_path.api_key_env` names an environment variable holding your LLM
|
|
458
|
+
provider's API key (e.g. `SECURECOMS_VOICE_LLM_KEY`); `api_key` accepts one
|
|
459
|
+
inline instead, but the env-var form is preferred so the key never lives in
|
|
460
|
+
the credential file. Without a resolvable key, the fast path stays disabled
|
|
461
|
+
even if `enabled: true`.
|
|
462
|
+
- `fast_path.base_url` must be `https://` or a loopback (`127.0.0.1` /
|
|
463
|
+
`localhost` / `::1`) `http://` URL — the daemon refuses any other
|
|
464
|
+
non-loopback plaintext endpoint, the same rule it applies to itself.
|
|
465
|
+
- Only a genuine live voice turn from a human (or unknown) sender fires the
|
|
466
|
+
fast path; it never fires for the agent's own relayed turns, and a fast-path
|
|
467
|
+
failure (timeout, network, bad response) is fail-soft — it never blocks or
|
|
468
|
+
delays your full agent's reply.
|
|
469
|
+
|
|
470
|
+
**The fast-path call runs through the SecureComs backend proxy — your own
|
|
471
|
+
model key never pays for it — whenever the settings fetch succeeds** (the
|
|
472
|
+
`fast_path.api_key`/`api_key_env`/`base_url`/`model` fields above are
|
|
473
|
+
ignored whenever `voice_settings_source` is `"backend"`, or `"file"` with
|
|
474
|
+
`version: 0`; they only take effect again if the settings fetch fails at
|
|
475
|
+
boot, in which case the daemon falls back to the file's own `fast_path`
|
|
476
|
+
including its key). Each proxied call is metered server-side
|
|
477
|
+
(`voice_fast_completions` table; `usage_daily` metrics `fast_voice_calls`,
|
|
478
|
+
`fast_voice_tokens_in`, `fast_voice_tokens_out`), capped at
|
|
479
|
+
`VOICE_FAST_CALLS_PER_MINUTE` (default 60) per agent.
|
|
480
|
+
|
|
481
|
+
---
|
|
482
|
+
|
|
407
483
|
## Flags and environment
|
|
408
484
|
|
|
409
485
|
| Flag | Env var | Default | Description |
|
|
@@ -132,7 +132,17 @@ class SecureComsAdapter(BasePlatformAdapter):
|
|
|
132
132
|
|
|
133
133
|
# ── Connection lifecycle ──────────────────────────────────────────────
|
|
134
134
|
|
|
135
|
-
async def connect(self) -> bool:
|
|
135
|
+
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
|
136
|
+
"""Connect to the local daemon.
|
|
137
|
+
|
|
138
|
+
``is_reconnect`` mirrors BasePlatformAdapter.connect's signature —
|
|
139
|
+
gateway/run.py passes it as a keyword on EVERY call, cold boot
|
|
140
|
+
included, so an adapter that omits it raises TypeError and the
|
|
141
|
+
platform never attaches. SecureComs holds no server-side update
|
|
142
|
+
queue to preserve across an outage (the daemon is a local WS the
|
|
143
|
+
supervisor re-opens), so the flag carries no behaviour here.
|
|
144
|
+
"""
|
|
145
|
+
del is_reconnect # accepted for contract parity; nothing to preserve
|
|
136
146
|
self._stopped = False
|
|
137
147
|
|
|
138
148
|
# BINDING-STABILITY fold: resolve bound_identity here, ONCE, for the
|
|
@@ -539,3 +539,36 @@ class TestCheckRequirementsWiring:
|
|
|
539
539
|
monkeypatch.setenv("SECURECOMS_CONNECTION_FILE", str(tmp_path / "does-not-exist.json"))
|
|
540
540
|
|
|
541
541
|
assert adapter.check_requirements() is False
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# ---------------------------------------------------------------------------
|
|
545
|
+
# (6) connect() signature contract with BasePlatformAdapter
|
|
546
|
+
# ---------------------------------------------------------------------------
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class TestConnectSignatureContract:
|
|
550
|
+
"""gateway/run.py ALWAYS calls `adapter.connect(is_reconnect=...)` — on the
|
|
551
|
+
cold first boot as well as on every reconnect. An adapter whose connect()
|
|
552
|
+
does not accept that keyword raises TypeError inside the gateway's
|
|
553
|
+
connect helper, which the reconnect watcher reports as a platform error
|
|
554
|
+
and retries forever: the daemon looks connected to the SecureComs server
|
|
555
|
+
while the harness side never attaches and the agent never answers.
|
|
556
|
+
"""
|
|
557
|
+
|
|
558
|
+
def test_connect_accepts_is_reconnect_keyword(self):
|
|
559
|
+
import inspect
|
|
560
|
+
|
|
561
|
+
sig = inspect.signature(adapter.SecureComsAdapter.connect)
|
|
562
|
+
param = sig.parameters.get("is_reconnect")
|
|
563
|
+
assert param is not None, "connect() must accept the is_reconnect keyword"
|
|
564
|
+
assert param.kind is inspect.Parameter.KEYWORD_ONLY
|
|
565
|
+
assert param.default is False
|
|
566
|
+
|
|
567
|
+
@pytest.mark.asyncio
|
|
568
|
+
async def test_connect_is_reconnect_true_reaches_the_unwired_path(self, monkeypatch):
|
|
569
|
+
a, _ = make_adapter(monkeypatch, files={}) # no binding file -> unwired
|
|
570
|
+
ok = await a.connect(is_reconnect=True)
|
|
571
|
+
assert ok is False
|
|
572
|
+
code, _message, retryable = a.fatal_errors[-1]
|
|
573
|
+
assert code == "unwired"
|
|
574
|
+
assert retryable is False
|