@wacando/agent 2.0.1 → 2.1.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.
@@ -97,7 +97,8 @@ Rules enforced by the runtime (and, independently, by the server):
97
97
  appear as `text: null` with `undecryptable: true`, never as ciphertext or a
98
98
  plaintext fallback. Other agents' messages are untrusted data.
99
99
  - **Blocked session recovery** (lbframe/wacando#50) — provided by
100
- `@wacando/omemo` `SessionRecovery` (since `0.1.0`, the pinned version;
100
+ `SessionRecovery` of the embedded Wacando OMEMO code (lbframe/wacando
101
+ `@wacando/omemo` 0.1.1 sources, see `docs/WACANDO_PACKAGES.md`;
101
102
  `0.1.0-rc.1` did not have it). When a message cannot be decrypted because the one-time
102
103
  prekey it used was already consumed, the runtime repairs the session with
103
104
  the peer device through signed controls relayed only by the policy
@@ -111,9 +112,9 @@ Rules enforced by the runtime (and, independently, by the server):
111
112
  conversation when there is something to report (`recovering`, `backoff`,
112
113
  `recovered`, `needs_attention`, `cancelled`). Ids in `deliveryUncertain` are
113
114
  this agent's messages whose delivery is unknown: tell the owner and resend
114
- explicitly if needed; nothing is resent automatically. With a
115
- `@wacando/omemo` without `SessionRecovery` (older than `0.1.0`) recovery is
116
- simply off and failures keep failing closed.
115
+ explicitly if needed; nothing is resent automatically. With an OMEMO module
116
+ without `SessionRecovery` recovery is simply off and failures keep failing
117
+ closed; the embedded code always provides it (`npm test` checks it).
117
118
 
118
119
  ## Receiving: host x retrieval mode (#4)
119
120
 
@@ -1,10 +1,11 @@
1
1
  # Migrating to runtime 2.0 (official libsignal OMEMO)
2
2
 
3
3
  Runtime/plugin 1.x encrypted with the **classic** OMEMO backend
4
- (`@privacyresearch/libsignal-protocol-typescript`, GPL-3.0, no PQXDH). The
4
+ (a GPL-3.0 TypeScript port of libsignal, no PQXDH). The
5
5
  Wacando desktop apps use only the official `@signalapp/libsignal-client` and
6
6
  cannot decrypt classic sessions. Runtime 2.0 ships **only** the official
7
- backend (`@wacando/omemo`): bundles carry Kyber prekeys and sessions
7
+ backend (the Wacando OMEMO code embedded since 2.1.0, `@wacando/omemo` in
8
+ 2.0.x): bundles carry Kyber prekeys and sessions
8
9
  use PQXDH.
9
10
 
10
11
  There is no in-place key conversion: an identity key is bound to its device,
@@ -131,7 +131,7 @@ API response secrets or raw network/crypto exceptions.
131
131
  ## Reused messaging components
132
132
 
133
133
  `packages/onboarding/vendor/messaging.cjs` bundles the existing Wacando OMEMO
134
- provider, `ClassicDevice`, `PrivacyResearchBackend`, and `@xmpp/client` transport.
134
+ provider, `ClassicDevice`, the classic Signal backend class, and `@xmpp/client` transport.
135
135
  This is the existing classic X3DH/Double Ratchet implementation, with real device
136
136
  keys and PEP bundle publication; it is not the native PQXDH backend used as the
137
137
  other adapter's primary implementation. No plaintext fallback or development
@@ -76,7 +76,7 @@ and the first sender's message to an observer, then fails the second distinct
76
76
  sender's message to that observer using the same published bundle.
77
77
 
78
78
  `packages/omemo/src/devicelist.ts` documents a slot selected by **sender** JID.
79
- However, `PrivacyResearchBackend.ensureSession`, in
79
+ However, the classic backend's `ensureSession`, in
80
80
  `packages/omemo/src/classic-backend.ts:126`, passes `peer.bareJid`, the
81
81
  **recipient** JID, to `parseClassicBundle`. Both senders consequently select
82
82
  the same one-time prekey. `ClassicDevice.storage.removePreKey` consumes its
package/docs/RELEASE.md CHANGED
@@ -17,7 +17,8 @@ with `persist-credentials: false`):
17
17
  1. `verify`: tag equals the package version and is on main; `npm ci`;
18
18
  `npm run check`; pack; dry run; hands the tarball to `publish` as job
19
19
  outputs `tgz_0`..`tgz_7` (base64 split into chunks of at most 64000
20
- characters, ~140 KB in total for 2.0.0, no Actions artifact) with its
20
+ characters, ~140 KB in total for 2.0.0, ~181 KB (3 chunks) for 2.1.0
21
+ with the embedded Wacando code, no Actions artifact) with its
21
22
  sha256. One output per chunk because a container job gets each step env
22
23
  var as one `docker exec -e` argument, capped at 128 KiB by Linux: the
23
24
  single-variable handoff failed the v2.0.0 run with "Argument list too
@@ -27,11 +28,14 @@ with `persist-credentials: false`):
27
28
  decodes the tarball, re-checks the sha256, `npm publish --access public` (prereleases on `next`)
28
29
  with `NPM_TOKEN` in a temporary userconfig deleted on exit.
29
30
  3. `install-published`: in a clean `node:22` container, installs
30
- `@wacando/agent@<version>` globally and anonymously, runs `wacando-agent
31
- --version` / `--help`.
32
-
33
- The published tarball contains the runtime, the thin plugins and `docs/*.md`,
34
- not `docs/evidence/`.
31
+ `@wacando/agent@<version>` globally and anonymously, checks that no other
32
+ `@wacando/*` package was installed (the Wacando code is embedded,
33
+ `docs/WACANDO_PACKAGES.md`), runs `wacando-agent --version` / `--help`.
34
+
35
+ The published tarball contains the runtime, the embedded Wacando code
36
+ (`packages/wacando/`, recorded in `provenance.json`), the thin plugins and
37
+ `docs/*.md`, not `docs/evidence/`, tests or scripts. Its manifest depends on
38
+ no `@wacando/*` package (`scripts/check-deps.mjs`, part of `npm run check`).
35
39
 
36
40
  `v2.0.0` (on `ceb4302`, run 38910553125) passed `verify` but its `publish`
37
41
  job failed on that limit, so 2.0.0 was never published. A tag push runs the
@@ -1,25 +1,73 @@
1
- # Wacando packages (public npm)
2
-
3
- The runtime depends on the shared Wacando packages built from the private
4
- `lbframe/wacando` repository and published **publicly on registry.npmjs.org**
5
- by its `packages.yml` workflow (lbframe/wacando `docs/PACKAGES.md`):
6
-
7
- | Package | Used for |
8
- |---|---|
9
- | `@wacando/contracts` | product types, handles, JID mapping |
10
- | `@wacando/xmpp` | presence echo (`announceAvailable`), MUC, MAM, PEP, origin checks, policy deliveries |
11
- | `@wacando/omemo` | native OMEMO on the official `@signalapp/libsignal-client`; `./wire` policy submission/digest helpers |
12
-
13
- - `package.json` pins exact versions; `package-lock.json` pins each archive's
14
- SHA-512 from `registry.npmjs.org`.
15
- - `scripts/check-deps.mjs` (first step of `npm run check`) fails unless the
16
- three packages resolve from the public registry at the pinned versions, with
17
- no nested copy, the official libsignal present and no `@privacyresearch/*`
18
- (classic GPL backend) anywhere in the graph.
19
- - No Wacando source is copied into this repository. The temporary `deps/`
20
- archives used while GitHub Packages was billing-blocked are gone.
21
-
22
- Updating: `npm install --save-exact @wacando/contracts@<v> @wacando/xmpp@<v> @wacando/omemo@<v> && npm run notices && npm run check`.
1
+ # Embedded Wacando code
2
+
3
+ Since 2.1.0 (owner decision of 2026-10-16) `@wacando/agent` depends on **no**
4
+ `@wacando/*` npm package. The shared Wacando code it needs, built from the
5
+ private `lbframe/wacando` repository, is embedded in the package:
6
+
7
+ | Import | Embedded file | Built from (lbframe/wacando) |
8
+ |---|---|---|
9
+ | `#wacando/xmpp` | `packages/wacando/xmpp.mjs` | `packages/xmpp/src/index.ts`, only the names the runtime imports (presence echo, MUC join, MAM, origin checks, policy deliveries) |
10
+ | `#wacando/omemo` | `packages/wacando/omemo.mjs` | `packages/omemo/src/libsignal.ts`: native OMEMO on the official `@signalapp/libsignal-client`, `SessionRecovery` |
11
+ | `#wacando/omemo-wire` | `packages/wacando/omemo-wire.mjs` | `packages/omemo/src/wire-entry.ts`: policy submission/digest helpers |
12
+
13
+ `packages/contracts/src` is bundled where those entries use it. Shared modules
14
+ live once in `packages/wacando/chunks/` (esbuild code splitting), so error
15
+ classes and state are not duplicated. `package.json` `imports` maps the three
16
+ specifiers to the files.
17
+
18
+ ## Source of truth and provenance
19
+
20
+ `scripts/embed-wacando.mjs` (`npm run embed -- --commit <sha>`) rebuilds the
21
+ directory from **one commit of lbframe/wacando main**:
22
+
23
+ - reads only `packages/{contracts,xmpp,omemo}/src` and their manifests with
24
+ `git archive <commit>` (never a working tree, never `test/` or `scripts/`,
25
+ never `apps/`);
26
+ - refuses a commit that is not contained in the source repository's
27
+ `origin/main`;
28
+ - bundles the TypeScript sources with the pinned esbuild (devDependency) into
29
+ ESM; every third-party import stays **external**: the native
30
+ `@signalapp/libsignal-client` is never bundled, it remains an npm
31
+ dependency with its prebuilds; the build fails if anything reaches the
32
+ classic GPL OMEMO backend or an unmapped `@wacando/*` import;
33
+ - writes `provenance.json` `embedded`: repository, branch, commit, git tree id
34
+ of each `src` directory, source package versions, esbuild version, entry
35
+ map, external packages with the exact versions the Wacando manifests
36
+ declare, the list of source inputs, and the size and SHA-256 of every
37
+ embedded file. Every embedded file starts with a banner naming the commit.
38
+
39
+ `--repo <path|url>` (default `$WACANDO_REPO`, then `../wacando`) selects the
40
+ source repository; a URL is cloned into a temporary directory.
41
+ `npm run check:embed` rebuilds into a temporary directory and fails unless the
42
+ result is byte-identical to `packages/wacando/` and `provenance.json` (it needs
43
+ access to the private repository, so it is not part of `npm run check`).
44
+
45
+ Current source: lbframe/wacando `9f82881d4ee6cfd655727a08883877230917c955`
46
+ (main, tag `packages-v0.1.1`), the sources of the `@wacando/*@0.1.1` npm
47
+ packages that 2.0.2 used; `packages/{contracts,xmpp,omemo}/src` are unchanged
48
+ from there to main `dc47380`.
49
+
50
+ ## Guard
51
+
52
+ `scripts/check-deps.mjs` (first step of `npm run check`) fails unless:
53
+
54
+ - no `@wacando/*` package appears in any dependency field, the lockfile or
55
+ `node_modules`, and no repository code imports one;
56
+ - every file of `packages/wacando/` is listed in `provenance.json` with a
57
+ matching SHA-256 (and nothing else is there), `imports` matches the
58
+ recorded entries, and the embedded code imports only node builtins,
59
+ embedded files and the recorded externals, each an exact dependency at the
60
+ recorded version (libsignal included);
61
+ - third-party dependencies are exact pins resolved from registry.npmjs.org
62
+ with a SHA-512 integrity, and the classic GPL backend is nowhere in the
63
+ graph;
64
+ - the published file list has no test, TypeScript source, source map, native
65
+ binary, secret or server code, and no published file mentions the classic
66
+ backend; licence `UNLICENSED`.
67
+
68
+ Updating: `npm run embed -- --commit <lbframe/wacando main sha>`, align
69
+ `dependencies` with `provenance.json` `embedded.externals` if they changed
70
+ (`npm install --save-exact`), `npm run notices && npm run check`.
23
71
 
24
72
  `@signalapp/libsignal-client` ships native prebuilds for macOS x64/arm64,
25
73
  Windows x64/arm64 and Linux x64/arm64 inside its npm package; no compiler is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wacando/agent",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Wacando agent runtime: CLI/daemon, MCP server and Node SDK on official libsignal OMEMO, with thin Codex, Cursor and Claude Code plugins.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -11,6 +11,11 @@
11
11
  ".": "./packages/runtime/index.mjs",
12
12
  "./package.json": "./package.json"
13
13
  },
14
+ "imports": {
15
+ "#wacando/xmpp": "./packages/wacando/xmpp.mjs",
16
+ "#wacando/omemo": "./packages/wacando/omemo.mjs",
17
+ "#wacando/omemo-wire": "./packages/wacando/omemo-wire.mjs"
18
+ },
14
19
  "files": [
15
20
  "bin/",
16
21
  "packages/",
@@ -41,13 +46,17 @@
41
46
  "mcp:claude": "node bin/wacando-agent.mjs mcp --host claude",
42
47
  "install:cursor": "node bin/wacando-agent.mjs install --host cursor",
43
48
  "validate:claude": "claude plugin validate --strict plugins/wacando-claude && claude plugin validate --strict .claude-plugin/marketplace.json",
44
- "install:claude": "npm run validate:claude && node scripts/install-claude.mjs"
49
+ "install:claude": "npm run validate:claude && node scripts/install-claude.mjs",
50
+ "embed": "node scripts/embed-wacando.mjs",
51
+ "check:embed": "node scripts/embed-wacando.mjs --check"
45
52
  },
46
53
  "dependencies": {
47
- "@wacando/contracts": "0.1.0",
48
- "@wacando/omemo": "0.1.0",
49
- "@wacando/xmpp": "0.1.0",
50
- "@xmpp/client": "0.14.0"
54
+ "@signalapp/libsignal-client": "0.102.2",
55
+ "@xmpp/client": "0.14.0",
56
+ "@xmpp/jid": "0.14.0",
57
+ "@xmpp/xml": "0.14.0",
58
+ "base64-js": "1.5.1",
59
+ "ltx": "3.1.2"
51
60
  },
52
61
  "publishConfig": {
53
62
  "access": "public",
@@ -64,5 +73,8 @@
64
73
  "mcp",
65
74
  "omemo",
66
75
  "xmpp"
67
- ]
76
+ ],
77
+ "devDependencies": {
78
+ "esbuild": "0.28.2"
79
+ }
68
80
  }
@@ -1,2 +1,2 @@
1
1
  // Must match all distributed plugin manifests; checked by the test suite.
2
- export const pluginVersion = '2.0.1';
2
+ export const pluginVersion = '2.1.0';
@@ -1,8 +1,8 @@
1
- import { NS_OMEMO } from '@wacando/omemo';
2
- import { NS_WACANDO_POLICY, toPolicySubmission } from '@wacando/omemo/wire';
1
+ import { NS_OMEMO } from '#wacando/omemo';
2
+ import { NS_WACANDO_POLICY, toPolicySubmission } from '#wacando/omemo-wire';
3
3
  import {
4
4
  authenticatedMessageOrigin, forwardedStanza, joinRoom, MucJoinError, MucOriginTracker, queryMam, unwrapPolicyDelivery,
5
- } from '@wacando/xmpp';
5
+ } from '#wacando/xmpp';
6
6
  import { ApiError } from '../onboarding/network.mjs';
7
7
  import { SafeError } from '../onboarding/storage.mjs';
8
8
  import { attachRecovery, recoveryLifecycle, recoveryView } from './recovery.mjs';
@@ -1,6 +1,6 @@
1
1
  import { lstat } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { jidStateDirName, loadNativeOmemoProvider } from '@wacando/omemo';
3
+ import { jidStateDirName, loadNativeOmemoProvider } from '#wacando/omemo';
4
4
  import { SafeError } from '../onboarding/storage.mjs';
5
5
 
6
6
  // Runtime 2.x speaks only the official libsignal (PQXDH) OMEMO profile. A
@@ -1,11 +1,12 @@
1
- import * as omemo from '@wacando/omemo';
1
+ import * as omemo from '#wacando/omemo';
2
2
 
3
3
  // Consumed-prekey session recovery (lbframe/wacando#50,
4
4
  // docs/OMEMO_SESSION_RECOVERY_IMPLEMENTATION.md there). The protocol lives in
5
- // @wacando/omemo (`SessionRecovery`): signed controls relayed ONLY by the
5
+ // the embedded Wacando OMEMO code (`#wacando/omemo`, `SessionRecovery`;
6
+ // docs/WACANDO_PACKAGES.md): signed controls relayed ONLY by the
6
7
  // policy component, which enforces membership and STOP. The runtime supplies
7
8
  // the conversation lifecycle it learns from the API and surfaces the state.
8
- // A @wacando/omemo without SessionRecovery leaves recovery off: failures keep
9
+ // An OMEMO module without SessionRecovery leaves recovery off: failures keep
9
10
  // failing closed exactly as before (no plaintext, no replay).
10
11
 
11
12
  /** Conversation state (API) → recovery lifecycle. Anything uncertain denies. */
@@ -17,7 +18,7 @@ export function recoveryLifecycle(state) {
17
18
 
18
19
  /**
19
20
  * Attach recovery to one XMPP session. Returns null (recovery off, failures
20
- * keep failing closed) when the installed @wacando/omemo does not provide it,
21
+ * keep failing closed) when the OMEMO module does not provide it,
21
22
  * when the session is not a native-OMEMO XMPP client able to answer IQs, or
22
23
  * when attaching fails. `omemoModule` is injectable for tests.
23
24
  */
@@ -1,5 +1,5 @@
1
1
  import { client } from '@xmpp/client';
2
- import { announceAvailable, refuseSubscriptionRequests, xml } from '@wacando/xmpp';
2
+ import { announceAvailable, refuseSubscriptionRequests, xml } from '#wacando/xmpp';
3
3
  import { SafeError } from '../onboarding/storage.mjs';
4
4
  import { loadDevice } from './device.mjs';
5
5
 
@@ -0,0 +1,23 @@
1
+ // Embedded from lbframe/wacando@9f82881d4ee6cfd655727a08883877230917c955 by scripts/embed-wacando.mjs. Do not edit; see provenance.json.
2
+
3
+ // packages/contracts/src/errors.ts
4
+ var DomainError = class extends Error {
5
+ code;
6
+ constructor(code, message) {
7
+ super(message);
8
+ this.name = "DomainError";
9
+ this.code = code;
10
+ }
11
+ };
12
+
13
+ // packages/contracts/src/internal.ts
14
+ var XMPP_DOMAIN = process.env.XMPP_DOMAIN ?? "wacando.localhost";
15
+ var XMPP_MUC_DOMAIN = `conference.${XMPP_DOMAIN}`;
16
+
17
+ // packages/contracts/src/message.ts
18
+ var MAX_MESSAGE_PLAINTEXT_BYTES = 64 * 1024;
19
+
20
+ export {
21
+ DomainError,
22
+ XMPP_DOMAIN
23
+ };