@cello-protocol/daemon 0.0.18 → 0.0.20

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.
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAqB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAg6H7E"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,OAAO,KAAK,EACV,YAAY,EACZ,oBAAoB,EAQrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAM/D,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAqB7G,OAAO,KAAK,EAAE,kBAAkB,EAA+C,MAAM,yBAAyB,CAAC;AAM/G,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAqInF,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,SAAS,IAAI,oBAAoB,CAAC;IAClC;;;;OAIG;IACH,qBAAqB,IAAI,kBAAkB,CAAC;IAC5C;;;;;;;;OAQG;IACH,gBAAgB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrC;;;;OAIG;IACH,oBAAoB,IAAI,kBAAkB,CAAC;IAC3C;;;OAGG;IACH,iBAAiB,IAAI,eAAe,CAAC;CACtC;AAyCD,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAi9H7E"}
package/dist/daemon.js CHANGED
@@ -1576,7 +1576,14 @@ export async function startDaemon(config) {
1576
1576
  logger.error("session.sealed.signature.invalid", { sessionId: sidHex, reason: verdict.reason });
1577
1577
  return;
1578
1578
  }
1579
- logger.info("session.sealed.signature.checked", { sessionId: sidHex, verified: verdict.verified });
1579
+ // F2-a: on verified:false, surface WHY (signer_key_not_held / no_frost_share / …) so this
1580
+ // event can never be mistaken for a tolerated failed check. A real failure took the early
1581
+ // return above (session.sealed.signature.invalid) and never reaches here.
1582
+ logger.info("session.sealed.signature.checked", {
1583
+ sessionId: sidHex,
1584
+ verified: verdict.verified,
1585
+ ...(verdict.verified ? {} : { reason: verdict.reason }),
1586
+ });
1580
1587
  }
1581
1588
  // M7-SESSION-004 (AC-005): normalise the wire legibility (Uint8Array pubkeys → hex) into a
1582
1589
  // JSON-safe certificate and persist it with the sealed record so it survives a restart and
@@ -1878,9 +1885,10 @@ export async function startDaemon(config) {
1878
1885
  // NOTE: cello_await_session is NOT in this stub list — Seam 2 registers a real
1879
1886
  // handler for it below (inbound session establishment), with its own inline
1880
1887
  // no_current_agent guard.
1881
- const SESSION_TOOLS_REQUIRING_AGENT = [
1882
- "cello_receive_session",
1883
- ];
1888
+ // F1-a2: cello_receive_session is NO LONGER stubbed here — it is registered below as a true
1889
+ // alias of the real (blocking) cello_receive handler. Kept as an (empty) extension point for
1890
+ // future tools that need the plain no_current_agent guard without their own handler.
1891
+ const SESSION_TOOLS_REQUIRING_AGENT = [];
1884
1892
  const NO_CURRENT_AGENT_RESPONSE = {
1885
1893
  ok: false,
1886
1894
  reason: "no_current_agent",
@@ -3671,35 +3679,75 @@ export async function startDaemon(config) {
3671
3679
  void newRootHex;
3672
3680
  return { ok: true, sequence_number: leafIndex };
3673
3681
  });
3674
- // ─── CELLO-M7-DAEMON-004: cello_receive (returns from the daemon's own buffer) ──
3675
- handlers.set("cello_receive", async (params, connectionId) => {
3682
+ // ─── CELLO-M7-DAEMON-004 / F1-a: cello_receive (BLOCKING, session-scoped) ────────
3683
+ // F1-a fix: the daemon port had dropped the blocking receive (the handler was a
3684
+ // non-blocking buf.shift and cello_receive_session was a not_implemented stub). It now
3685
+ // BLOCKS up to timeout_ms, polling the received-content buffer — resolved by the next
3686
+ // arrival, a terminal seal answer (F1-b), or timeout. This is the "blocking receive
3687
+ // variant" the guidance names. Registered under both cello_receive and cello_receive_session
3688
+ // (F1-a2: one implementation, both names — the redundant tools are collapsed).
3689
+ const RECEIVE_DEFAULT_TIMEOUT_MS = 30000; // matches the cello-mcp shim's documented default
3690
+ const handleReceive = async (params, connectionId) => {
3676
3691
  const connState = perConnectionState.get(connectionId);
3677
3692
  if (!connState || !connState.currentAgent)
3678
3693
  return NO_CURRENT_AGENT_RESPONSE;
3679
- // round-2 BLOCKING: read the snake_case public field cello-mcp.ts actually sends.
3694
+ const agentName = connState.currentAgent;
3695
+ // Read the snake_case public field cello-mcp.ts actually sends.
3680
3696
  const sessionId = params?.session_id;
3681
3697
  if (!sessionId) {
3682
3698
  return { ok: false, reason: "missing_params", guidance: "Provide 'session_id' (hex) to receive content for a specific session." };
3683
3699
  }
3684
- const record = sessionNodeManager.getSessionRecord(connState.currentAgent, sessionId);
3700
+ const record = sessionNodeManager.getSessionRecord(agentName, sessionId);
3685
3701
  if (!record) {
3686
3702
  return { ok: false, reason: "session_not_found", guidance: "No session found with this ID. Check cello_list_sessions." };
3687
3703
  }
3688
- if (record.agent_name !== connState.currentAgent) {
3704
+ if (record.agent_name !== agentName) {
3689
3705
  return { ok: false, reason: "session_not_owned", guidance: "This session belongs to a different agent. Call cello_use_agent to switch to the agent that owns it, then retry." };
3690
3706
  }
3691
- const entry = sessionNodeManager.takeReceivedContent(connState.currentAgent, sessionId);
3692
- if (!entry) {
3693
- return { ok: true, content: null, guidance: "No content is currently buffered for this session. Call cello_receive again after the counterparty sends, or use the blocking receive variant." };
3707
+ const rawTimeout = params?.timeout_ms;
3708
+ const timeoutMs = typeof rawTimeout === "number" && Number.isFinite(rawTimeout) && rawTimeout >= 0
3709
+ ? rawTimeout
3710
+ : RECEIVE_DEFAULT_TIMEOUT_MS;
3711
+ const deadline = Date.now() + timeoutMs;
3712
+ for (;;) {
3713
+ // 1) Deliverable content wins — drain one buffered message per call (FIFO).
3714
+ const entry = sessionNodeManager.takeReceivedContent(agentName, sessionId);
3715
+ if (entry) {
3716
+ return {
3717
+ ok: true,
3718
+ content: Buffer.from(entry.contentHex, "hex").toString("utf8"),
3719
+ sessionId,
3720
+ sequence_number: entry.sequenceNumber,
3721
+ senderPubkey: entry.senderPubkey,
3722
+ };
3723
+ }
3724
+ // 2) F1-b: the session sealed while we were (or before we started) waiting — return the
3725
+ // terminal answer instead of hanging to timeout. unread_count reports messages that
3726
+ // were evicted unread (still durable — recoverable via cello_get_transcript).
3727
+ const terminal = sessionNodeManager.peekTerminalMarker(agentName, sessionId);
3728
+ if (terminal) {
3729
+ const sealedRoot = sessionNodeManager.getSealedRootHex(agentName, sessionId);
3730
+ return {
3731
+ ok: true,
3732
+ type: "session_sealed",
3733
+ session_id: sessionId,
3734
+ ...(sealedRoot ? { sealed_root: sealedRoot } : {}),
3735
+ unread_count: terminal.unreadCount,
3736
+ guidance: terminal.unreadCount > 0
3737
+ ? `The session has been sealed by both parties. ${terminal.unreadCount} message(s) arrived that were not read live — call cello_get_transcript to retrieve the full sealed history. No further actions are required on this session.`
3738
+ : "The session has been sealed by both parties. The full history is available via cello_get_transcript. No further actions are required on this session.",
3739
+ };
3740
+ }
3741
+ // 3) Out of time — non-blocking-equivalent empty answer.
3742
+ const remaining = deadline - Date.now();
3743
+ if (remaining <= 0) {
3744
+ return { ok: true, content: null, guidance: "No content arrived within timeout_ms. Call cello_receive again to keep waiting, or read cello_get_transcript for the full session history." };
3745
+ }
3746
+ await new Promise((r) => setTimeout(r, Math.min(20, remaining)));
3694
3747
  }
3695
- return {
3696
- ok: true,
3697
- content: Buffer.from(entry.contentHex, "hex").toString("utf8"),
3698
- sessionId,
3699
- sequence_number: entry.sequenceNumber,
3700
- senderPubkey: entry.senderPubkey,
3701
- };
3702
- });
3748
+ };
3749
+ handlers.set("cello_receive", handleReceive);
3750
+ handlers.set("cello_receive_session", handleReceive);
3703
3751
  let shutdownPromise = null;
3704
3752
  handlers.set("shutdown", async (_params, _connectionId) => {
3705
3753
  if (!shutdownPromise) {