@inetafrica/open-claudia 3.0.14 → 3.0.15

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.0.15 — the owner keeps their memory on Kazee
4
+
5
+ - **Episodic recall no longer goes dark on Kazee / group chats.** Cross-conversation episodic memory (relevant snippets from the owner's past transcripts) is deliberately owner-only. The gate that decides "is this speaker the owner?" resolved the person by `findByHandle(adapter, channelId)` — but on Kazee the `channelId` is the *chat/room id*, while the owner's stored handle is their Kazee *user id*, so the lookup missed and fail-closed suppressed episodes. The owner's canonical brain, settings, and session were shared via identity unification, but this one recall layer silently went quiet — which read as "less context on Kazee." The gate now resolves the speaker by the envelope's **canonical user id** first (derived from the authenticated sender and unified across a person's linked channels), falling back to the channelId handle lookup when no canonical is in context. Fail-closed semantics for non-owners are preserved: a non-owner's canonical never matches an owner record. Regression pinned in `test-recall-relationship-gate.js` (owner recognised via canonical on a room-id channel; external still blocked).
6
+ - **Agent-space / OpenClaw:** no new deps, no new env, no schema change; Docker image builds identically (FROM the pre-baked `:base`). Existing pods pick this up on their next approved upgrade.
7
+
3
8
  ## v3.0.14 — the Telegram poll stops wedging, and every reboot says why
4
9
 
5
10
  - **Root-cause fix for the restart flap.** In the cluster, DNS hands back a dead IPv6 address for `api.telegram.org` first and IPv6 egress is unreachable; under Node's default `verbatim` resolution the long-poll dials the dead address first (a latency penalty always, and a hard wedge on any AAAA-only window). `bot.js` now sets `dns.setDefaultResultOrder("ipv4first")` before any socket opens, eliminating that path for every request the process makes.
@@ -109,12 +109,21 @@ const EXCERPT_CHARS = 600;
109
109
  // e.g. CLI/tests) may pull them. Same-conversation packs/entities are unaffected.
110
110
  function episodesAllowedForSpeaker() {
111
111
  try {
112
- const { currentAdapter, currentChannelId } = require("../context");
112
+ const { currentAdapter, currentChannelId, currentCanonicalUserId } = require("../context");
113
113
  const adapter = currentAdapter();
114
114
  const channelId = currentChannelId();
115
115
  if (!adapter || !channelId) return true; // no speaker context (CLI/tests) → ungated
116
116
  const people = require("../people");
117
- const speaker = people.findByHandle(adapter.type, channelId);
117
+ // Resolve the speaker by canonical user id first. On transports where the
118
+ // channelId is a room/chat id rather than the sender's user id (Kazee, group
119
+ // chats), a handle lookup keyed on channelId misses the owner and fails
120
+ // closed — silently starving them of episodic recall. The canonical id is
121
+ // derived from the authenticated sender and is unified across a person's
122
+ // linked channels, so it recognises the owner on every transport. Falls back
123
+ // to the channelId handle lookup when no canonical is in context.
124
+ const canonical = currentCanonicalUserId();
125
+ const speaker = (canonical && people.findByCanonicalUserId(canonical))
126
+ || people.findByHandle(adapter.type, channelId);
118
127
  return !!(speaker && speaker.isOwner); // unknown/non-owner → gated
119
128
  } catch (e) {
120
129
  return false; // resolution error → fail closed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "3.0.14",
3
+ "version": "3.0.15",
4
4
  "description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
5
5
  "main": "bot.js",
6
6
  "bin": {
@@ -32,12 +32,11 @@ assert.doesNotMatch(setup, /Your Claude Code bot is connected|Description=Claude
32
32
 
33
33
  const pkg = JSON.parse(read("package.json"));
34
34
  assert.match(pkg.description, /provider-agnostic coding-agent harness/i);
35
- // 3.0.14 approved by Sumeet 2026-07-13 ("Proceed" on the Telegram poll-wedge
36
- // fix): dns ipv4first + 40s socket-idle timeout on the Telegram long-poll so
37
- // the half-open socket heals on a fresh poll instead of wedging into a 10-min
38
- // self-exit restart flap, plus code-side reboot-reason reporting appended to
39
- // the "Back online" greeting via core/restart-reason.js.
40
- assert.strictEqual(pkg.version, "3.0.14", "release version must remain unchanged without explicit approval");
35
+ // 3.0.15 approved by Sumeet 2026-07-14 ("Yes fix and push"): episodic-recall
36
+ // owner gate now resolves the speaker by canonical user id (not raw channelId),
37
+ // so the owner keeps cross-conversation episodic memory on Kazee / group chats
38
+ // where channelId is a room id that never matches the stored user-id handle.
39
+ assert.strictEqual(pkg.version, "3.0.15", "release version must remain unchanged without explicit approval");
41
40
  for (const keyword of ["claude", "codex", "coding-agent", "provider-agnostic"]) {
42
41
  assert.ok(pkg.keywords.includes(keyword), `package keyword missing: ${keyword}`);
43
42
  }
@@ -39,4 +39,25 @@ assert.strictEqual(inChat("222", () => episodesAllowedForSpeaker()), false, "ext
39
39
  // Unknown channel (no person record) → fail closed.
40
40
  assert.strictEqual(inChat("999", () => episodesAllowedForSpeaker()), false, "unknown speaker → episodes blocked");
41
41
 
42
+ // Kazee / group case: the channelId is a room/chat id that does NOT match the
43
+ // owner's stored handle, but the envelope carries the owner's unified canonical
44
+ // id. Resolving by canonical must recognise the owner even though a handle
45
+ // lookup on the room id misses — otherwise the owner silently loses episodic
46
+ // recall on those transports (the Kazee "less context" regression).
47
+ const withCanon = (channelId, canonicalUserId, fn) =>
48
+ runInChat({ adapter, channelId, canonicalUserId, transport: "telegram" }, fn);
49
+ const ownerCanon = people.findById(owner.id).handles[0].canonicalUserId;
50
+ assert.strictEqual(
51
+ withCanon("room-xyz", ownerCanon, () => episodesAllowedForSpeaker()),
52
+ true,
53
+ "owner via canonical on a room-id channel → episodes allowed (Kazee/group regression)",
54
+ );
55
+ // A non-owner on a room-id channel with their own canonical stays blocked.
56
+ const extCanon = people.findById(ext.id).handles[0].canonicalUserId;
57
+ assert.strictEqual(
58
+ withCanon("room-abc", extCanon, () => episodesAllowedForSpeaker()),
59
+ false,
60
+ "external via canonical → episodes still blocked",
61
+ );
62
+
42
63
  console.log("recall relationship gate OK");