@cello-protocol/cli 0.0.35 → 0.0.36
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/dist/hermes/assets.d.ts.map +1 -1
- package/dist/hermes/assets.js +113 -29
- package/dist/hermes/assets.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,ysBAiB9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,ysBAiB9B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QA2kBjC,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,eAAe,yoEA+C3B,CAAC"}
|
package/dist/hermes/assets.js
CHANGED
|
@@ -110,6 +110,11 @@ _CONTENT_FIELD_NAMES = {
|
|
|
110
110
|
# conservative charset (or overlong) renders as 'unknown' in the wake prompt.
|
|
111
111
|
_SAFE_SCALAR_RE = re.compile(r"^[A-Za-z0-9+/=_.:@-]{1,120}$")
|
|
112
112
|
|
|
113
|
+
# The protocol's moniker charset (core/protocol-types MONIKER_RE). Deliberately excludes spaces,
|
|
114
|
+
# quotes, parentheses and markup, so a fingerprint ("agent 77d0c806...") never matches and neither
|
|
115
|
+
# does anything that could restructure the wake sentence.
|
|
116
|
+
_MONIKER_RE = re.compile(r"^[A-Za-z0-9_-]{1,64}$")
|
|
117
|
+
|
|
113
118
|
|
|
114
119
|
def _cello_socket_path() -> Path:
|
|
115
120
|
cello_dir = os.environ.get("CELLO_DIR") or str(Path.home() / ".cello")
|
|
@@ -140,11 +145,37 @@ def _has_content_field(value: Any) -> bool:
|
|
|
140
145
|
def _safe_scalar(value: Any, default: str = "unknown") -> str:
|
|
141
146
|
if not isinstance(value, str) or not value:
|
|
142
147
|
return default
|
|
143
|
-
|
|
148
|
+
# fullmatch, never match: in Python a trailing '$' ALSO matches just before a final newline,
|
|
149
|
+
# so re.match(r"^...$", "abc\n") succeeds where the daemon's JS equivalent rejects. A
|
|
150
|
+
# re-validation layer that is laxer than the rule it mirrors is not a layer.
|
|
151
|
+
if not _SAFE_SCALAR_RE.fullmatch(value):
|
|
144
152
|
return default
|
|
145
153
|
return value
|
|
146
154
|
|
|
147
155
|
|
|
156
|
+
def _render_who(data: Any) -> Optional[str]:
|
|
157
|
+
"""DOD-HERMES-3: the daemon-resolved counterparty label, or None.
|
|
158
|
+
|
|
159
|
+
The daemon stamps who/whoKnown on both counterparty-bearing frames (MONIKER-4 AC2) with three
|
|
160
|
+
tiers: the operator's own pet name, the caller's unverified offered name, or a fingerprint.
|
|
161
|
+
Only a NAME is rendered - the fingerprint tier ('agent 77d0c806...') is derived from the very
|
|
162
|
+
pubkey every wake already carries in full, so echoing it is noise. An unverified name is marked
|
|
163
|
+
as a claim exactly as the Claude Code shim marks it, and the marker cannot be forged because
|
|
164
|
+
MONIKER_RE excludes quotes and parentheses.
|
|
165
|
+
|
|
166
|
+
Re-validated here rather than trusted: Hermes has no metadata layer, so this prose IS the frame
|
|
167
|
+
(spec §11) and a name-shaped token is the only thing that may ever enter it.
|
|
168
|
+
"""
|
|
169
|
+
# fullmatch, never match - see _safe_scalar. re.match would admit "CELLO_Support\n", putting a
|
|
170
|
+
# newline into prose that IS the frame. Rejected whole; never stripped (§3: no mutation oracle).
|
|
171
|
+
who = data.get("who") if isinstance(data, dict) else None
|
|
172
|
+
if not isinstance(who, str) or not _MONIKER_RE.fullmatch(who):
|
|
173
|
+
return None
|
|
174
|
+
if data.get("whoKnown") is True:
|
|
175
|
+
return who
|
|
176
|
+
return '"' + who + '" (unverified)'
|
|
177
|
+
|
|
178
|
+
|
|
148
179
|
class CelloAdapter(BasePlatformAdapter):
|
|
149
180
|
"""Wake-only adapter: daemon IPC notifications in, nothing out.
|
|
150
181
|
|
|
@@ -422,34 +453,68 @@ class CelloAdapter(BasePlatformAdapter):
|
|
|
422
453
|
# Unlike Raft's hardcoded generic hint, surface the (content-free) metadata the
|
|
423
454
|
# daemon pushes - session id, counterparty pubkey, state - so the agent can act
|
|
424
455
|
# without an extra discovery round-trip. Message CONTENT is never present here.
|
|
456
|
+
#
|
|
457
|
+
# OBSERVED 2026-07-09: a woken agent handed "reply [SILENT] if no action is needed"
|
|
458
|
+
# takes that exit every time. Six wakes, six [SILENT]s, ZERO tool calls - the Hermes
|
|
459
|
+
# transcript shows a "CELLO wake..." user turn followed immediately by an assistant
|
|
460
|
+
# turn of "[SILENT]", with nothing in between.
|
|
461
|
+
# A MESSAGE-ARRIVAL wake is never a no-action event: a peer is blocked waiting on a
|
|
462
|
+
# reply. So silence is offered ONLY on state-change wakes; the message path spells out
|
|
463
|
+
# the mandatory steps and forbids silence.
|
|
464
|
+
#
|
|
465
|
+
# cello_use_agent must come FIRST. The cello MCP server holds its own daemon connection,
|
|
466
|
+
# separate from this adapter's, and a freshly started one has no current agent. With more
|
|
467
|
+
# than one agent online the daemon's sole-online fallback cannot resolve, so every other
|
|
468
|
+
# cello_* call would fail with no_current_agent.
|
|
425
469
|
session_id = _safe_scalar(data.get("session_id") or data.get("sessionId"))
|
|
470
|
+
agent = self._agent_name
|
|
426
471
|
if kind == "cello_message":
|
|
427
472
|
sender = _safe_scalar(data.get("from"))
|
|
428
|
-
|
|
473
|
+
# DOD-HERMES-3 AC1/AC2: the name LEADS, the pubkey rides beside it (spec §11).
|
|
474
|
+
who = _render_who(data)
|
|
475
|
+
origin = (
|
|
476
|
+
"from " + who + " (counterparty pubkey " + sender + ")"
|
|
477
|
+
if who is not None
|
|
478
|
+
else "from counterparty pubkey " + sender
|
|
479
|
+
)
|
|
480
|
+
return (
|
|
429
481
|
"CELLO wake: a new message arrived on session "
|
|
430
482
|
+ session_id
|
|
431
|
-
+ "
|
|
432
|
-
+
|
|
433
|
-
+ "."
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
"
|
|
483
|
+
+ " "
|
|
484
|
+
+ origin
|
|
485
|
+
+ ". A peer is waiting on you. Message content is never pushed, so you must fetch"
|
|
486
|
+
" it. Use the MCP tools named cello_* (from the 'cello' MCP server) - they are"
|
|
487
|
+
" already available to you. Do NOT run the 'cello' command-line program, and do"
|
|
488
|
+
" NOT restart anything. Do this now, in order: (1) call the cello_use_agent tool"
|
|
489
|
+
" with name='"
|
|
490
|
+
+ agent
|
|
491
|
+
+ "'; (2) call the cello_receive tool with session_id='"
|
|
440
492
|
+ session_id
|
|
441
|
-
+ "
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
+ ")."
|
|
493
|
+
+ "' to read the message; (3) reply with the cello_send tool on that same session"
|
|
494
|
+
" unless the message genuinely needs no answer. cello_receive must precede"
|
|
495
|
+
" cello_send or the daemon rejects the send with session_not_current."
|
|
496
|
+
" Do NOT answer [SILENT] on a message wake - reading the message is not optional."
|
|
446
497
|
)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
"
|
|
498
|
+
|
|
499
|
+
state = _safe_scalar(data.get("state"))
|
|
500
|
+
counterparty = _safe_scalar(data.get("counterpartyPubkey"))
|
|
501
|
+
who = _render_who(data)
|
|
502
|
+
subject = (
|
|
503
|
+
who + " (counterparty pubkey " + counterparty + ")"
|
|
504
|
+
if who is not None
|
|
505
|
+
else "counterparty pubkey " + counterparty
|
|
506
|
+
)
|
|
507
|
+
return (
|
|
508
|
+
"CELLO wake: session "
|
|
509
|
+
+ session_id
|
|
510
|
+
+ " with "
|
|
511
|
+
+ subject
|
|
512
|
+
+ " changed state to '"
|
|
513
|
+
+ state
|
|
514
|
+
+ "'. This is a state notice, not a message. If it needs no action, reply with exactly"
|
|
515
|
+
" [SILENT]. If you do need to act, call cello_use_agent with name='"
|
|
516
|
+
+ agent
|
|
517
|
+
+ "' first, then whichever cello_* tool you need."
|
|
453
518
|
)
|
|
454
519
|
|
|
455
520
|
# ------------------------------------------------------------------ outbound (no-op)
|
|
@@ -540,13 +605,32 @@ def register(ctx) -> None:
|
|
|
540
605
|
emoji="\U0001F3BB",
|
|
541
606
|
platform_hint=(
|
|
542
607
|
"You are connected to CELLO, a peer-to-peer identity and trust layer for "
|
|
543
|
-
"agent-to-agent communication
|
|
544
|
-
"
|
|
545
|
-
"
|
|
546
|
-
"
|
|
547
|
-
"
|
|
548
|
-
"
|
|
549
|
-
"
|
|
608
|
+
"agent-to-agent communication.\n"
|
|
609
|
+
"\n"
|
|
610
|
+
"HOW TO USE CELLO: exclusively through the MCP tools named cello_* , served by "
|
|
611
|
+
"the MCP server called 'cello'. They are already available to you. Their names "
|
|
612
|
+
"are cello_use_agent, cello_receive, cello_send, cello_check_notifications, "
|
|
613
|
+
"cello_list_sessions, cello_close_session, cello_status, and others.\n"
|
|
614
|
+
"\n"
|
|
615
|
+
"NEVER run the 'cello' command-line program. It is not the way in, and running "
|
|
616
|
+
"it from inside a turn spawns a daemon bound to your process that dies when your "
|
|
617
|
+
"turn ends. NEVER run 'cello login', 'cello logout', or anything that restarts "
|
|
618
|
+
"the CELLO daemon: a single daemon serves EVERY agent on this machine, so "
|
|
619
|
+
"restarting it takes all of them offline and orphans every connected MCP server. "
|
|
620
|
+
"Restarting is never the fix. If a cello_* tool returns an error, read the error, "
|
|
621
|
+
"fix the cause, and retry the tool.\n"
|
|
622
|
+
"\n"
|
|
623
|
+
"Before any other cello_* call, select your agent with cello_use_agent "
|
|
624
|
+
"(name='" + os.environ.get("CELLO_AGENT_NAME", "your-agent") + "'). The cello MCP "
|
|
625
|
+
"server holds its own daemon connection with no agent selected, so other calls "
|
|
626
|
+
"fail with no_current_agent until you do.\n"
|
|
627
|
+
"\n"
|
|
628
|
+
"Always read before you send: cello_receive on a session before cello_send, or "
|
|
629
|
+
"the daemon rejects the send with session_not_current. Wake notices are "
|
|
630
|
+
"content-free by design - fetch the actual message with cello_receive. A wake "
|
|
631
|
+
"saying a MESSAGE ARRIVED is never a no-action event: a peer is waiting; read it "
|
|
632
|
+
"and reply. Answer [SILENT] only for a state-change notice that genuinely needs "
|
|
633
|
+
"nothing from you, never on a message wake."
|
|
550
634
|
),
|
|
551
635
|
)
|
|
552
636
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;CAiBjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/hermes/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;CAiBjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2kB9C,CAAC;AAEF,wEAAwE;AACxE,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+C9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cello-protocol/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"package.json"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@cello-protocol/daemon": "0.0.
|
|
28
|
+
"@cello-protocol/daemon": "0.0.39",
|
|
29
29
|
"@cello-protocol/protocol-types": "0.0.19"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|