@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.
- package/README.md +6 -2
- package/STATUS.md +3 -2
- package/THIRD-PARTY-NOTICES.txt +0 -1113
- package/docs/AGENT_TOOLS.md +5 -4
- package/docs/MIGRATION-2.0.md +3 -2
- package/docs/ONBOARDING.md +1 -1
- package/docs/PLUGIN_HANDOFF_FOLLOWUP.md +1 -1
- package/docs/RELEASE.md +10 -6
- package/docs/WACANDO_PACKAGES.md +70 -22
- package/package.json +19 -7
- package/packages/onboarding/version.mjs +1 -1
- package/packages/runtime/conversations.mjs +3 -3
- package/packages/runtime/device.mjs +1 -1
- package/packages/runtime/recovery.mjs +5 -4
- package/packages/runtime/session.mjs +1 -1
- package/packages/wacando/chunks/chunk-EIGFLZ2Y.mjs +23 -0
- package/packages/wacando/chunks/chunk-GASYW3SG.mjs +422 -0
- package/packages/wacando/chunks/chunk-OI3RF7TS.mjs +421 -0
- package/packages/wacando/omemo-wire.mjs +192 -0
- package/packages/wacando/omemo.mjs +2488 -0
- package/packages/wacando/xmpp.mjs +48 -0
- package/plugins/wacando/.codex-plugin/plugin.json +1 -1
- package/plugins/wacando-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/wacando-cursor/.cursor-plugin/plugin.json +1 -1
- package/provenance.json +126 -266
package/docs/AGENT_TOOLS.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
package/docs/MIGRATION-2.0.md
CHANGED
|
@@ -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
|
-
(
|
|
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`
|
|
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,
|
package/docs/ONBOARDING.md
CHANGED
|
@@ -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`,
|
|
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, `
|
|
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,
|
|
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,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
package/docs/WACANDO_PACKAGES.md
CHANGED
|
@@ -1,25 +1,73 @@
|
|
|
1
|
-
# Wacando
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
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
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"@
|
|
50
|
-
"@xmpp/
|
|
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
|
|
2
|
+
export const pluginVersion = '2.1.0';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { NS_OMEMO } from '
|
|
2
|
-
import { NS_WACANDO_POLICY, toPolicySubmission } from '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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 '
|
|
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
|
+
};
|