@awebai/claude-channel 1.4.2 → 1.4.4

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,7 +1,7 @@
1
1
  {
2
2
  "name": "aweb-channel",
3
3
  "description": "aweb agent coordination channel — receive mail, chat, tasks, and control signals from your agent team in real time.",
4
- "version": "1.4.2",
4
+ "version": "1.4.4",
5
5
  "author": {
6
6
  "name": "awebai"
7
7
  },
package/dist/index.js CHANGED
@@ -22437,7 +22437,9 @@ async function* streamAgentEvents(client, signal) {
22437
22437
  } catch (err2) {
22438
22438
  if (signal.aborted)
22439
22439
  return;
22440
- console.error(`[aw-channel] events stream parse failed: ${err2}`);
22440
+ if (!isExpectedStreamTermination(err2)) {
22441
+ console.error(`[aw-channel] events stream parse failed: ${err2}`);
22442
+ }
22441
22443
  await sleep(1e3, signal);
22442
22444
  } finally {
22443
22445
  resp.body?.cancel().catch(() => {
@@ -22523,6 +22525,13 @@ function parseAgentEvent(eventName, data) {
22523
22525
  return { type: eventName };
22524
22526
  }
22525
22527
  }
22528
+ function isExpectedStreamTermination(err2) {
22529
+ if (!(err2 instanceof Error))
22530
+ return false;
22531
+ const name = err2.name.toLowerCase();
22532
+ const message = err2.message.toLowerCase();
22533
+ return name === "aborterror" || message === "terminated";
22534
+ }
22526
22535
  function sleep(ms, signal) {
22527
22536
  return new Promise((resolve) => {
22528
22537
  if (signal.aborted) {
@@ -25755,9 +25764,9 @@ import { mkdir, open, rename, rm } from "node:fs/promises";
25755
25764
  var PinStore = class _PinStore {
25756
25765
  pins = /* @__PURE__ */ new Map();
25757
25766
  addresses = /* @__PURE__ */ new Map();
25758
- /** Check whether a DID matches the stored pin for an address. */
25759
- checkPin(address, did, lifetime) {
25760
- if (lifetime === "ephemeral")
25767
+ /** Check whether a DID matches the stored pin for a global address. */
25768
+ checkPin(address, did, identityScope) {
25769
+ if (identityScope === "local")
25761
25770
  return "skipped";
25762
25771
  const pinnedDID = this.addresses.get(address);
25763
25772
  if (pinnedDID === void 0)
@@ -25953,7 +25962,7 @@ var RegistryResolver = class {
25953
25962
  address: `${split2.domain}/${split2.name}`,
25954
25963
  controllerDid: authority.controllerDid,
25955
25964
  custody: "self",
25956
- lifetime: "persistent"
25965
+ identityScope: "global"
25957
25966
  };
25958
25967
  }
25959
25968
  async discoverRegistry(domain2) {
@@ -26344,6 +26353,17 @@ function escapeJSON2(s) {
26344
26353
  // ../channel-core/dist/identity/trust.js
26345
26354
  etc.sha512Sync = (...m) => sha512(etc.concatBytes(...m));
26346
26355
  var ANNOUNCEMENT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
26356
+ function normalizeIdentityScope(identityScope, legacyLifetime, defaultScope) {
26357
+ const normalizedScope = (identityScope || "").trim().toLowerCase();
26358
+ if (normalizedScope === "global" || normalizedScope === "local")
26359
+ return normalizedScope;
26360
+ const normalizedLegacy = (legacyLifetime || "").trim().toLowerCase();
26361
+ if (normalizedLegacy === "persistent")
26362
+ return "global";
26363
+ if (normalizedLegacy === "ephemeral")
26364
+ return "local";
26365
+ return defaultScope;
26366
+ }
26347
26367
  var SenderTrustManager = class {
26348
26368
  client;
26349
26369
  registry;
@@ -26405,7 +26425,7 @@ var SenderTrustManager = class {
26405
26425
  if (!status || status !== "verified" && status !== "verified_custodial" || !fromDID || !trustAddress || !meta3.resolved) {
26406
26426
  return { status, stored: false };
26407
26427
  }
26408
- if (meta3.lifetime === "ephemeral") {
26428
+ if (meta3.identityScope === "local") {
26409
26429
  let removed = store.removeAddress(trustAddress);
26410
26430
  if (rawAddress && rawAddress !== trustAddress) {
26411
26431
  removed = store.removeAddress(rawAddress) || removed;
@@ -26432,7 +26452,7 @@ var SenderTrustManager = class {
26432
26452
  }
26433
26453
  }
26434
26454
  }
26435
- const pinResult = store.checkPin(trustAddress, pinKey, meta3.lifetime);
26455
+ const pinResult = store.checkPin(trustAddress, pinKey, meta3.identityScope);
26436
26456
  switch (pinResult) {
26437
26457
  case "new":
26438
26458
  store.storePin(pinKey, trustAddress, "", "");
@@ -26558,7 +26578,7 @@ var SenderTrustManager = class {
26558
26578
  const rawAddress = address.trim();
26559
26579
  const trustAddress = this.canonicalTrustAddress(rawAddress);
26560
26580
  if (!trustAddress) {
26561
- return { lifetime: "persistent", custody: "self", resolved: false };
26581
+ return { identityScope: "global", custody: "self", resolved: false };
26562
26582
  }
26563
26583
  const cached2 = this.metaCache.get(trustAddress);
26564
26584
  if (cached2)
@@ -26566,7 +26586,7 @@ var SenderTrustManager = class {
26566
26586
  try {
26567
26587
  const identity = await this.resolveIdentity(rawAddress);
26568
26588
  const meta3 = {
26569
- lifetime: identity.lifetime || "persistent",
26589
+ identityScope: identity.identityScope,
26570
26590
  custody: identity.custody || "self",
26571
26591
  controllerDid: identity.controllerDid,
26572
26592
  resolved: true
@@ -26574,7 +26594,7 @@ var SenderTrustManager = class {
26574
26594
  this.metaCache.set(trustAddress, meta3);
26575
26595
  return meta3;
26576
26596
  } catch {
26577
- return { lifetime: "persistent", custody: "self", resolved: false };
26597
+ return { identityScope: "global", custody: "self", resolved: false };
26578
26598
  }
26579
26599
  }
26580
26600
  async resolveIdentity(address) {
@@ -26594,7 +26614,7 @@ var SenderTrustManager = class {
26594
26614
  stableID: response.did_aw,
26595
26615
  address: response.address || `${this.teamID}/${trimmed}`,
26596
26616
  custody: "self",
26597
- lifetime: response.lifetime || "ephemeral"
26617
+ identityScope: normalizeIdentityScope(response.identity_scope, response.lifetime, "local")
26598
26618
  };
26599
26619
  }
26600
26620
  };
@@ -26665,9 +26685,11 @@ function escapeJSON3(s) {
26665
26685
  }
26666
26686
 
26667
26687
  // ../channel-core/dist/channel.js
26668
- import { join as join2 } from "node:path";
26688
+ import { dirname as dirname2, join as join2 } from "node:path";
26669
26689
  import { homedir } from "node:os";
26670
26690
  var DEFAULT_PIN_STORE_PATH = join2(homedir(), ".config", "aw", "known_agents.yaml");
26691
+ var DEFAULT_DELIVERY_STORE_PATH = join2(homedir(), ".config", "aw", "channel-delivered-ids.json");
26692
+ var DELIVERED_IDS_TTL_MS = 24 * 60 * 60 * 1e3;
26671
26693
 
26672
26694
  // src/index.ts
26673
26695
  var PIN_STORE_PATH = join3(homedir2(), ".config", "aw", "known_agents.yaml");
@@ -26725,7 +26747,7 @@ async function main() {
26725
26747
  },
26726
26748
  instructions: `Events from the aweb channel are coordination messages from other agents in your team. Use the aw CLI to respond, not MCP tools.
26727
26749
 
26728
- Mail events (type="mail") are async. Read them and act if needed. Delivery through this channel already acknowledges receipt, so there is no separate ack command. Reply with: aw mail reply <message_id> --body "<reply>"
26750
+ Mail events (type="mail") are async. Read them and act if needed. Confirmed channel delivery marks the source message handled so reconnects do not replay stale mail. Reply with: aw mail reply <message_id> --body "<reply>".
26729
26751
 
26730
26752
  Chat events (type="chat") may have sender_waiting="true", meaning the sender is blocked waiting for your reply. Respond promptly with: aw chat send-and-wait <from> "<reply>"
26731
26753
  If you need more time, send a status update the same way.
@@ -26783,7 +26805,10 @@ async function dispatchEvent(mcp, client, pinStore, trust, self, dispatched, eve
26783
26805
  if (isSelfSender(msg.from_alias, msg.from_address, msg.from_stable_id, msg.from_did, self)) continue;
26784
26806
  const conversationID = msg.conversation_id || event.conversation_id;
26785
26807
  const key = dispatchKey("mail", conversationID, msg.message_id);
26786
- if (dispatched.has(key)) continue;
26808
+ if (dispatched.has(key)) {
26809
+ if (!msg.read_at) await ackMessage(client, msg.message_id);
26810
+ continue;
26811
+ }
26787
26812
  dispatched.add(key);
26788
26813
  const from = senderDisplayAddress(msg.from_alias, msg.from_address);
26789
26814
  const tofu = await trust.normalizeTrust(
@@ -26813,9 +26838,7 @@ async function dispatchEvent(mcp, client, pinStore, trust, self, dispatched, eve
26813
26838
  method: "notifications/claude/channel",
26814
26839
  params: { content: msg.body, meta: meta3 }
26815
26840
  });
26816
- ackMessage(client, msg.message_id).catch(
26817
- (err2) => console.error(`[aw-channel] ack failed: ${err2}`)
26818
- );
26841
+ await ackMessage(client, msg.message_id);
26819
26842
  }
26820
26843
  if (pinsDirty) await pinStore.save(PIN_STORE_PATH);
26821
26844
  break;
@@ -26829,7 +26852,10 @@ async function dispatchEvent(mcp, client, pinStore, trust, self, dispatched, eve
26829
26852
  if (isSelfSender(msg.from_agent, msg.from_address, msg.from_stable_id, msg.from_did, self)) continue;
26830
26853
  const conversationID = msg.conversation_id || event.conversation_id || event.session_id;
26831
26854
  const key = dispatchKey("chat", conversationID, msg.message_id);
26832
- if (dispatched.has(key)) continue;
26855
+ if (dispatched.has(key)) {
26856
+ lastMessageId = msg.message_id;
26857
+ continue;
26858
+ }
26833
26859
  dispatched.add(key);
26834
26860
  const from = senderDisplayAddress(msg.from_agent, msg.from_address);
26835
26861
  const tofu = await trust.normalizeTrust(
@@ -26862,11 +26888,7 @@ async function dispatchEvent(mcp, client, pinStore, trust, self, dispatched, eve
26862
26888
  });
26863
26889
  lastMessageId = msg.message_id;
26864
26890
  }
26865
- if (lastMessageId) {
26866
- markRead(client, event.session_id, lastMessageId).catch(
26867
- (err2) => console.error(`[aw-channel] mark-read failed: ${err2}`)
26868
- );
26869
- }
26891
+ if (lastMessageId) await markRead(client, event.session_id, lastMessageId);
26870
26892
  if (pinsDirty) await pinStore.save(PIN_STORE_PATH);
26871
26893
  break;
26872
26894
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awebai/claude-channel",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",