@signetai/connector-hermes-agent 0.182.9 → 0.183.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/dist/index.js CHANGED
@@ -10451,6 +10451,55 @@ function up114(db) {
10451
10451
  ON entity_attributes(memory_id, agent_id, status, importance);
10452
10452
  `);
10453
10453
  }
10454
+ function up115(db) {
10455
+ db.exec(`
10456
+ CREATE TABLE IF NOT EXISTS cross_agent_messages (
10457
+ id TEXT PRIMARY KEY,
10458
+ from_agent_id TEXT NOT NULL,
10459
+ from_session_key TEXT,
10460
+ to_agent_id TEXT,
10461
+ to_session_key TEXT,
10462
+ to_session_agent_id TEXT,
10463
+ broadcast INTEGER NOT NULL DEFAULT 0 CHECK(broadcast IN (0, 1)),
10464
+ message_type TEXT NOT NULL CHECK(message_type IN ('assist_request', 'decision_update', 'info', 'question')),
10465
+ content TEXT NOT NULL,
10466
+ delivery_path TEXT NOT NULL CHECK(delivery_path IN ('local', 'acp')),
10467
+ delivery_status TEXT NOT NULL CHECK(delivery_status IN ('queued', 'delivered', 'failed')),
10468
+ delivery_error TEXT,
10469
+ delivery_receipt_json TEXT,
10470
+ created_at TEXT NOT NULL,
10471
+ expires_at TEXT NOT NULL,
10472
+ CHECK(
10473
+ delivery_path = 'acp'
10474
+ OR (
10475
+ CAST(to_agent_id IS NOT NULL AS INTEGER)
10476
+ + CAST(to_session_key IS NOT NULL AS INTEGER)
10477
+ + broadcast = 1
10478
+ )
10479
+ )
10480
+ );
10481
+
10482
+ CREATE TABLE IF NOT EXISTS cross_agent_message_receipts (
10483
+ message_id TEXT NOT NULL REFERENCES cross_agent_messages(id) ON DELETE CASCADE,
10484
+ agent_id TEXT NOT NULL,
10485
+ acknowledged_at TEXT NOT NULL,
10486
+ PRIMARY KEY (message_id, agent_id)
10487
+ );
10488
+
10489
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_agent
10490
+ ON cross_agent_messages(to_agent_id, expires_at, created_at, id);
10491
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_session
10492
+ ON cross_agent_messages(to_session_key, expires_at, created_at, id);
10493
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_session_agent
10494
+ ON cross_agent_messages(to_session_agent_id, expires_at, created_at, id);
10495
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_broadcast
10496
+ ON cross_agent_messages(broadcast, expires_at, created_at, id);
10497
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_sender
10498
+ ON cross_agent_messages(from_agent_id, expires_at, created_at, id);
10499
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_receipts_agent
10500
+ ON cross_agent_message_receipts(agent_id, acknowledged_at, message_id);
10501
+ `);
10502
+ }
10454
10503
  var MIGRATIONS = [
10455
10504
  {
10456
10505
  version: 1,
@@ -11368,6 +11417,14 @@ var MIGRATIONS = [
11368
11417
  version: 114,
11369
11418
  name: "memory-traversal-hydration-index",
11370
11419
  up: up114
11420
+ },
11421
+ {
11422
+ version: 115,
11423
+ name: "cross-agent-message-notifications",
11424
+ up: up115,
11425
+ artifacts: {
11426
+ tables: ["cross_agent_messages", "cross_agent_message_receipts"]
11427
+ }
11371
11428
  }
11372
11429
  ];
11373
11430
  var LATEST_SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;
@@ -18635,7 +18692,7 @@ function memoriesFtsNeedsTokenizerRepair2(sql) {
18635
18692
  return true;
18636
18693
  return !normalized.includes(`tokenize='${MEMORIES_FTS_TOKENIZER2}'`);
18637
18694
  }
18638
- function up115(db) {
18695
+ function up116(db) {
18639
18696
  db.exec(`
18640
18697
  CREATE TABLE IF NOT EXISTS schema_migrations (
18641
18698
  version INTEGER PRIMARY KEY,
@@ -19058,7 +19115,7 @@ function up1010(db) {
19058
19115
  )
19059
19116
  `);
19060
19117
  }
19061
- function up116(db) {
19118
+ function up117(db) {
19062
19119
  db.exec(`
19063
19120
  CREATE TABLE IF NOT EXISTS session_scores (
19064
19121
  id TEXT PRIMARY KEY,
@@ -22118,11 +22175,60 @@ function up1142(db) {
22118
22175
  ON entity_attributes(memory_id, agent_id, status, importance);
22119
22176
  `);
22120
22177
  }
22178
+ function up1152(db) {
22179
+ db.exec(`
22180
+ CREATE TABLE IF NOT EXISTS cross_agent_messages (
22181
+ id TEXT PRIMARY KEY,
22182
+ from_agent_id TEXT NOT NULL,
22183
+ from_session_key TEXT,
22184
+ to_agent_id TEXT,
22185
+ to_session_key TEXT,
22186
+ to_session_agent_id TEXT,
22187
+ broadcast INTEGER NOT NULL DEFAULT 0 CHECK(broadcast IN (0, 1)),
22188
+ message_type TEXT NOT NULL CHECK(message_type IN ('assist_request', 'decision_update', 'info', 'question')),
22189
+ content TEXT NOT NULL,
22190
+ delivery_path TEXT NOT NULL CHECK(delivery_path IN ('local', 'acp')),
22191
+ delivery_status TEXT NOT NULL CHECK(delivery_status IN ('queued', 'delivered', 'failed')),
22192
+ delivery_error TEXT,
22193
+ delivery_receipt_json TEXT,
22194
+ created_at TEXT NOT NULL,
22195
+ expires_at TEXT NOT NULL,
22196
+ CHECK(
22197
+ delivery_path = 'acp'
22198
+ OR (
22199
+ CAST(to_agent_id IS NOT NULL AS INTEGER)
22200
+ + CAST(to_session_key IS NOT NULL AS INTEGER)
22201
+ + broadcast = 1
22202
+ )
22203
+ )
22204
+ );
22205
+
22206
+ CREATE TABLE IF NOT EXISTS cross_agent_message_receipts (
22207
+ message_id TEXT NOT NULL REFERENCES cross_agent_messages(id) ON DELETE CASCADE,
22208
+ agent_id TEXT NOT NULL,
22209
+ acknowledged_at TEXT NOT NULL,
22210
+ PRIMARY KEY (message_id, agent_id)
22211
+ );
22212
+
22213
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_agent
22214
+ ON cross_agent_messages(to_agent_id, expires_at, created_at, id);
22215
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_session
22216
+ ON cross_agent_messages(to_session_key, expires_at, created_at, id);
22217
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_session_agent
22218
+ ON cross_agent_messages(to_session_agent_id, expires_at, created_at, id);
22219
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_broadcast
22220
+ ON cross_agent_messages(broadcast, expires_at, created_at, id);
22221
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_messages_sender
22222
+ ON cross_agent_messages(from_agent_id, expires_at, created_at, id);
22223
+ CREATE INDEX IF NOT EXISTS idx_cross_agent_receipts_agent
22224
+ ON cross_agent_message_receipts(agent_id, acknowledged_at, message_id);
22225
+ `);
22226
+ }
22121
22227
  var MIGRATIONS2 = [
22122
22228
  {
22123
22229
  version: 1,
22124
22230
  name: "baseline",
22125
- up: up115,
22231
+ up: up116,
22126
22232
  artifacts: { tables: ["memories", "conversations", "embeddings"] }
22127
22233
  },
22128
22234
  {
@@ -22188,7 +22294,7 @@ var MIGRATIONS2 = [
22188
22294
  {
22189
22295
  version: 11,
22190
22296
  name: "session-scores",
22191
- up: up116,
22297
+ up: up117,
22192
22298
  artifacts: { tables: ["session_scores"] }
22193
22299
  },
22194
22300
  {
@@ -23035,6 +23141,14 @@ var MIGRATIONS2 = [
23035
23141
  version: 114,
23036
23142
  name: "memory-traversal-hydration-index",
23037
23143
  up: up1142
23144
+ },
23145
+ {
23146
+ version: 115,
23147
+ name: "cross-agent-message-notifications",
23148
+ up: up1152,
23149
+ artifacts: {
23150
+ tables: ["cross_agent_messages", "cross_agent_message_receipts"]
23151
+ }
23038
23152
  }
23039
23153
  ];
23040
23154
  var LATEST_SCHEMA_VERSION2 = MIGRATIONS2[MIGRATIONS2.length - 1]?.version ?? 0;
@@ -327,6 +327,7 @@ class SignetMemoryProvider(MemoryProvider):
327
327
  self._inject_cache = ""
328
328
  self._inject_lock = threading.Lock()
329
329
  self._prefetch_result = ""
330
+ self._notification_result = ""
330
331
  self._prefetch_lock = threading.Lock()
331
332
  self._prefetch_thread: Optional[threading.Thread] = None
332
333
  self._prefetch_generation = 0
@@ -485,11 +486,27 @@ class SignetMemoryProvider(MemoryProvider):
485
486
  if self._prefetch_thread and self._prefetch_thread.is_alive():
486
487
  self._prefetch_thread.join(timeout=3.0)
487
488
 
489
+ client = self._client
490
+ if client:
491
+ try:
492
+ notification = client.notifications(
493
+ self._session_key,
494
+ "prefetch",
495
+ project=self._project,
496
+ )
497
+ if notification is not None:
498
+ notification_inject = notification.get("inject", "")
499
+ with self._prefetch_lock:
500
+ self._notification_result = notification_inject if isinstance(notification_inject, str) else ""
501
+ except Exception as e:
502
+ logger.debug("Signet notification prefetch failed: %s", e)
503
+
488
504
  with self._prefetch_lock:
489
- result = self._prefetch_result
505
+ parts = [self._prefetch_result, self._notification_result]
490
506
  self._prefetch_result = ""
507
+ self._notification_result = ""
491
508
 
492
- return result
509
+ return "\n".join(part for part in parts if part and part.strip())
493
510
 
494
511
  def queue_prefetch(self, query: str, *, session_id: str = "") -> None:
495
512
  """Fire a background recall via user-prompt-submit hook.
@@ -513,6 +530,7 @@ class SignetMemoryProvider(MemoryProvider):
513
530
  with self._prefetch_lock:
514
531
  self._prefetch_generation += 1
515
532
  self._prefetch_result = ""
533
+ self._notification_result = ""
516
534
  session_key = self._session_key
517
535
  project = self._project
518
536
  prefetch_generation = self._prefetch_generation
@@ -542,10 +560,16 @@ class SignetMemoryProvider(MemoryProvider):
542
560
  )
543
561
  return
544
562
  inject = result.get("inject", "")
545
- if inject and inject.strip():
563
+ notification = result.get("notifications")
564
+ notification_inject = notification.get("inject", "") if isinstance(notification, dict) else ""
565
+ recall_inject = inject
566
+ if notification_inject and inject.rstrip().endswith(notification_inject.strip()):
567
+ recall_inject = inject.rstrip()[: -len(notification_inject.strip())].rstrip()
568
+ if (recall_inject and recall_inject.strip()) or (notification_inject and notification_inject.strip()):
546
569
  with self._prefetch_lock:
547
570
  if prefetch_generation == self._prefetch_generation and session_key == self._session_key:
548
- self._prefetch_result = inject
571
+ self._prefetch_result = recall_inject
572
+ self._notification_result = notification_inject
549
573
  except Exception as e:
550
574
  logger.debug("Signet prefetch failed: %s", e)
551
575
 
@@ -584,6 +608,29 @@ class SignetMemoryProvider(MemoryProvider):
584
608
  if assistant_content:
585
609
  with self._transcript_lock:
586
610
  self._transcript_lines.append(f"assistant: {assistant_content}")
611
+ self._queue_notification_refresh("sync_turn")
612
+
613
+ def _queue_notification_refresh(self, hook: str) -> None:
614
+ """Fetch peer notifications without blocking the current Hermes callback."""
615
+ client = self._client
616
+ if not client:
617
+ return
618
+ session_key = self._session_key
619
+ project = self._project
620
+ with self._prefetch_lock:
621
+ generation = self._prefetch_generation
622
+
623
+ def _run():
624
+ try:
625
+ result = client.notifications(session_key, hook, project=project)
626
+ inject = (result or {}).get("inject", "")
627
+ with self._prefetch_lock:
628
+ if generation == self._prefetch_generation and session_key == self._session_key:
629
+ self._notification_result = inject if isinstance(inject, str) else ""
630
+ except Exception as e:
631
+ logger.debug("Signet %s notification refresh failed: %s", hook, e)
632
+
633
+ threading.Thread(target=_run, daemon=True, name=f"signet-notify-{hook}").start()
587
634
 
588
635
  def on_session_switch(
589
636
  self,
@@ -615,6 +662,7 @@ class SignetMemoryProvider(MemoryProvider):
615
662
  with self._prefetch_lock:
616
663
  self._prefetch_generation += 1
617
664
  self._prefetch_result = ""
665
+ self._notification_result = ""
618
666
 
619
667
  agent_id = os.environ.get("SIGNET_AGENT_ID", "").strip() or "hermes-agent"
620
668
  self._project = _resolve_agent_workspace(agent_id, kwargs)
@@ -818,6 +866,7 @@ class SignetMemoryProvider(MemoryProvider):
818
866
 
819
867
  t = threading.Thread(target=_run, daemon=True, name="signet-delegation")
820
868
  t.start()
869
+ self._queue_notification_refresh("on_delegation")
821
870
 
822
871
  def _fire_checkpoint(self) -> None:
823
872
  """Fire a checkpoint-extract for long-running sessions."""
@@ -319,6 +319,26 @@ class SignetClient:
319
319
  timeout=_RECALL_TIMEOUT_SECS,
320
320
  )
321
321
 
322
+ def notifications(
323
+ self,
324
+ session_key: str,
325
+ hook: str,
326
+ *,
327
+ project: str = "",
328
+ ) -> Optional[Dict[str, Any]]:
329
+ """Fetch pending peer notifications for a compatible Hermes hook."""
330
+ return self._post(
331
+ "/api/hooks/notifications",
332
+ {
333
+ "harness": self._harness,
334
+ "hook": hook,
335
+ "sessionKey": session_key,
336
+ "agentId": self._agent_id,
337
+ "project": project,
338
+ },
339
+ timeout=_RECALL_TIMEOUT_SECS,
340
+ )
341
+
322
342
  def session_end(
323
343
  self,
324
344
  session_key: str,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signetai/connector-hermes-agent",
3
- "version": "0.182.9",
3
+ "version": "0.183.0",
4
4
  "description": "Signet connector for Hermes Agent — installs Signet as a pluggable memory provider",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,8 +25,8 @@
25
25
  "typecheck": "tsc --noEmit"
26
26
  },
27
27
  "dependencies": {
28
- "@signetai/connector-base": "0.182.9",
29
- "@signetai/core": "0.182.9"
28
+ "@signetai/connector-base": "0.183.0",
29
+ "@signetai/core": "0.183.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^22.0.0",