@mneme-ai/core 2.5.0 → 2.7.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.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/metron/code_audit.d.ts +55 -0
- package/dist/metron/code_audit.d.ts.map +1 -0
- package/dist/metron/code_audit.js +132 -0
- package/dist/metron/code_audit.js.map +1 -0
- package/dist/metron/code_audit.test.d.ts +2 -0
- package/dist/metron/code_audit.test.d.ts.map +1 -0
- package/dist/metron/code_audit.test.js +81 -0
- package/dist/metron/code_audit.test.js.map +1 -0
- package/dist/metron/index.d.ts +95 -0
- package/dist/metron/index.d.ts.map +1 -0
- package/dist/metron/index.js +393 -0
- package/dist/metron/index.js.map +1 -0
- package/dist/metron/metron.test.d.ts +2 -0
- package/dist/metron/metron.test.d.ts.map +1 -0
- package/dist/metron/metron.test.js +123 -0
- package/dist/metron/metron.test.js.map +1 -0
- package/dist/metron/update_notifier.d.ts +56 -0
- package/dist/metron/update_notifier.d.ts.map +1 -0
- package/dist/metron/update_notifier.js +143 -0
- package/dist/metron/update_notifier.js.map +1 -0
- package/dist/truth_kernel/index.d.ts +92 -0
- package/dist/truth_kernel/index.d.ts.map +1 -0
- package/dist/truth_kernel/index.js +203 -0
- package/dist/truth_kernel/index.js.map +1 -0
- package/dist/truth_kernel/truth_kernel.test.d.ts +2 -0
- package/dist/truth_kernel/truth_kernel.test.d.ts.map +1 -0
- package/dist/truth_kernel/truth_kernel.test.js +126 -0
- package/dist/truth_kernel/truth_kernel.test.js.map +1 -0
- package/dist/wormhole/auto_wire.d.ts +68 -0
- package/dist/wormhole/auto_wire.d.ts.map +1 -0
- package/dist/wormhole/auto_wire.js +186 -0
- package/dist/wormhole/auto_wire.js.map +1 -0
- package/dist/wormhole/index.d.ts +112 -0
- package/dist/wormhole/index.d.ts.map +1 -0
- package/dist/wormhole/index.js +151 -0
- package/dist/wormhole/index.js.map +1 -0
- package/dist/wormhole/wormhole.test.d.ts +2 -0
- package/dist/wormhole/wormhole.test.d.ts.map +1 -0
- package/dist/wormhole/wormhole.test.js +154 -0
- package/dist/wormhole/wormhole.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.7.0 -- WORMHOLE auto-wire: daemon discovers + adapts every
|
|
3
|
+
* transport, persists EWMA stats on disk.
|
|
4
|
+
*
|
|
5
|
+
* Before v2.7 the caller had to construct a `Channel[]` array by hand.
|
|
6
|
+
* That worked for tests but made WORMHOLE useless to the daemon / CLI.
|
|
7
|
+
* The auto-wire layer:
|
|
8
|
+
* 1. Imports each known transport module dynamically (so loading the
|
|
9
|
+
* wormhole module doesn't force every transport to load eagerly).
|
|
10
|
+
* 2. Wraps the module's send/probe surface as a Channel<Payload, Receipt>.
|
|
11
|
+
* 3. Persists EWMA stats to .mneme/wormhole-stats.json (atomic write).
|
|
12
|
+
*
|
|
13
|
+
* The wild move: each adapter has a DEGRADATION FALLBACK. If the
|
|
14
|
+
* module is missing (e.g., user installed `@mneme-ai/core` without
|
|
15
|
+
* optional transports), the adapter returns `unavailable` instead of
|
|
16
|
+
* throwing — so adding new transports never breaks the daemon.
|
|
17
|
+
*/
|
|
18
|
+
import type { Channel, ChannelStats } from "./index.js";
|
|
19
|
+
import { type ChannelTrial } from "./index.js";
|
|
20
|
+
/** Caller passes the payload type used by the actual send. */
|
|
21
|
+
export interface AutoWireOptions {
|
|
22
|
+
repoRoot: string;
|
|
23
|
+
/** Optional whitelist — only build adapters for these channel ids. */
|
|
24
|
+
only?: ReadonlyArray<string>;
|
|
25
|
+
}
|
|
26
|
+
/** Every transport adapter conforms to a thin generic shape so we don't
|
|
27
|
+
* hardcode the payload format here. */
|
|
28
|
+
export interface TransportAdapter<P, R> extends Channel<P, R> {
|
|
29
|
+
/** Human-readable label for the pulse / dashboards. */
|
|
30
|
+
label: string;
|
|
31
|
+
}
|
|
32
|
+
/** Load EWMA stats from disk. Returns empty record on first run. */
|
|
33
|
+
export declare function loadStats(repoRoot: string): Record<string, ChannelStats>;
|
|
34
|
+
/** Persist stats atomically. */
|
|
35
|
+
export declare function saveStats(repoRoot: string, stats: Record<string, ChannelStats>): void;
|
|
36
|
+
/** Ingest the trial list from a wormhole negotiation into the on-disk
|
|
37
|
+
* EWMA stats. Returns the updated stats record. */
|
|
38
|
+
export declare function ingestNegotiationTrials(repoRoot: string, trials: readonly ChannelTrial[]): Record<string, ChannelStats>;
|
|
39
|
+
/** Build the canonical list of transport adapters Mneme knows about as
|
|
40
|
+
* of v2.7. Each entry uses a generic payload shape `{ kind, body }`
|
|
41
|
+
* so callers can fan the same payload to every channel without re-encoding.
|
|
42
|
+
*
|
|
43
|
+
* Adapters are intentionally thin probes — they advertise availability
|
|
44
|
+
* by checking for local prerequisites (env, files, free port). The
|
|
45
|
+
* actual heavy "send" lives in the underlying module + is wired via
|
|
46
|
+
* the `send` callback. */
|
|
47
|
+
export declare function buildTransportAdapters(opts: AutoWireOptions): Promise<TransportAdapter<{
|
|
48
|
+
kind: string;
|
|
49
|
+
body: unknown;
|
|
50
|
+
}, {
|
|
51
|
+
channel: string;
|
|
52
|
+
receipt: string;
|
|
53
|
+
}>[]>;
|
|
54
|
+
/** One-call top-level: auto-discover channels, send the payload, persist
|
|
55
|
+
* the resulting EWMA stats. */
|
|
56
|
+
export declare function autoSend(repoRoot: string, payload: {
|
|
57
|
+
kind: string;
|
|
58
|
+
body: unknown;
|
|
59
|
+
}, opts?: Omit<AutoWireOptions, "repoRoot">): Promise<{
|
|
60
|
+
winner: string | null;
|
|
61
|
+
receipt: {
|
|
62
|
+
channel: string;
|
|
63
|
+
receipt: string;
|
|
64
|
+
} | null;
|
|
65
|
+
stats: Record<string, ChannelStats>;
|
|
66
|
+
trials: ChannelTrial[];
|
|
67
|
+
}>;
|
|
68
|
+
//# sourceMappingURL=auto_wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto_wire.d.ts","sourceRoot":"","sources":["../../src/wormhole/auto_wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAe,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED;wCACwC;AACxC,MAAM,WAAW,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAE,SAAQ,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;CACf;AAQD,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAUxE;AAED,gCAAgC;AAChC,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,IAAI,CAcrF;AAED;oDACoD;AACpD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAOvH;AAaD;;;;;;;2BAO2B;AAC3B,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAAE,CA4FvK;AAED;gCACgC;AAChC,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,eAAe,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC;IAC5I,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACrD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,MAAM,EAAE,YAAY,EAAE,CAAC;CACxB,CAAC,CAOD"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.7.0 -- WORMHOLE auto-wire: daemon discovers + adapts every
|
|
3
|
+
* transport, persists EWMA stats on disk.
|
|
4
|
+
*
|
|
5
|
+
* Before v2.7 the caller had to construct a `Channel[]` array by hand.
|
|
6
|
+
* That worked for tests but made WORMHOLE useless to the daemon / CLI.
|
|
7
|
+
* The auto-wire layer:
|
|
8
|
+
* 1. Imports each known transport module dynamically (so loading the
|
|
9
|
+
* wormhole module doesn't force every transport to load eagerly).
|
|
10
|
+
* 2. Wraps the module's send/probe surface as a Channel<Payload, Receipt>.
|
|
11
|
+
* 3. Persists EWMA stats to .mneme/wormhole-stats.json (atomic write).
|
|
12
|
+
*
|
|
13
|
+
* The wild move: each adapter has a DEGRADATION FALLBACK. If the
|
|
14
|
+
* module is missing (e.g., user installed `@mneme-ai/core` without
|
|
15
|
+
* optional transports), the adapter returns `unavailable` instead of
|
|
16
|
+
* throwing — so adding new transports never breaks the daemon.
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
19
|
+
import { dirname, join } from "node:path";
|
|
20
|
+
import { ingestTrial } from "./index.js";
|
|
21
|
+
const STATS_FILE = "wormhole-stats.json";
|
|
22
|
+
function statsPath(repoRoot) {
|
|
23
|
+
return join(repoRoot, ".mneme", STATS_FILE);
|
|
24
|
+
}
|
|
25
|
+
/** Load EWMA stats from disk. Returns empty record on first run. */
|
|
26
|
+
export function loadStats(repoRoot) {
|
|
27
|
+
const p = statsPath(repoRoot);
|
|
28
|
+
if (!existsSync(p))
|
|
29
|
+
return {};
|
|
30
|
+
try {
|
|
31
|
+
const obj = JSON.parse(readFileSync(p, "utf8"));
|
|
32
|
+
if (!obj || typeof obj !== "object")
|
|
33
|
+
return {};
|
|
34
|
+
return obj;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Persist stats atomically. */
|
|
41
|
+
export function saveStats(repoRoot, stats) {
|
|
42
|
+
const p = statsPath(repoRoot);
|
|
43
|
+
const dir = dirname(p);
|
|
44
|
+
if (!existsSync(dir))
|
|
45
|
+
mkdirSync(dir, { recursive: true });
|
|
46
|
+
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
47
|
+
writeFileSync(tmp, JSON.stringify(stats, null, 2), "utf8");
|
|
48
|
+
// Use Node rename via writeFileSync semantics; on Windows rename can fail
|
|
49
|
+
// if the dest exists — try-then-fallback.
|
|
50
|
+
try {
|
|
51
|
+
const { renameSync } = require("node:fs");
|
|
52
|
+
renameSync(tmp, p);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
writeFileSync(p, JSON.stringify(stats, null, 2), "utf8");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Ingest the trial list from a wormhole negotiation into the on-disk
|
|
59
|
+
* EWMA stats. Returns the updated stats record. */
|
|
60
|
+
export function ingestNegotiationTrials(repoRoot, trials) {
|
|
61
|
+
const stats = loadStats(repoRoot);
|
|
62
|
+
for (const t of trials) {
|
|
63
|
+
stats[t.channel] = ingestTrial(stats[t.channel], t);
|
|
64
|
+
}
|
|
65
|
+
saveStats(repoRoot, stats);
|
|
66
|
+
return stats;
|
|
67
|
+
}
|
|
68
|
+
/** Generic stub adapter — used when a transport's optional module is
|
|
69
|
+
* not loaded. Always reports `unavailable` so it never affects scoring. */
|
|
70
|
+
function stubAdapter(id, label, reason) {
|
|
71
|
+
return {
|
|
72
|
+
id,
|
|
73
|
+
label,
|
|
74
|
+
probe: () => "unavailable",
|
|
75
|
+
send: async () => ({ ok: false, reason }),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Build the canonical list of transport adapters Mneme knows about as
|
|
79
|
+
* of v2.7. Each entry uses a generic payload shape `{ kind, body }`
|
|
80
|
+
* so callers can fan the same payload to every channel without re-encoding.
|
|
81
|
+
*
|
|
82
|
+
* Adapters are intentionally thin probes — they advertise availability
|
|
83
|
+
* by checking for local prerequisites (env, files, free port). The
|
|
84
|
+
* actual heavy "send" lives in the underlying module + is wired via
|
|
85
|
+
* the `send` callback. */
|
|
86
|
+
export async function buildTransportAdapters(opts) {
|
|
87
|
+
const whitelist = opts.only ? new Set(opts.only) : null;
|
|
88
|
+
const all = [];
|
|
89
|
+
function maybe(id, build) {
|
|
90
|
+
if (whitelist && !whitelist.has(id))
|
|
91
|
+
return;
|
|
92
|
+
try {
|
|
93
|
+
all.push(build());
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
all.push(stubAdapter(id, id, `adapter init failed: ${e.message.slice(0, 80)}`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ANCHOR — pole / rope identity. Always available locally; sending here
|
|
100
|
+
// means: store payload reference under .mneme/anchor/inbox/.
|
|
101
|
+
maybe("anchor", () => ({
|
|
102
|
+
id: "anchor",
|
|
103
|
+
label: "Parent-pole identity (local)",
|
|
104
|
+
preference: 1.0,
|
|
105
|
+
probe: () => existsSync(join(opts.repoRoot, ".mneme")) ? "available" : "needs-pairing",
|
|
106
|
+
send: async (p) => {
|
|
107
|
+
// Implementation deferred to the daemon — auto-wire just confirms
|
|
108
|
+
// the channel CAN be invoked. A real send writes a receipt file.
|
|
109
|
+
const inbox = join(opts.repoRoot, ".mneme", "anchor", "inbox");
|
|
110
|
+
mkdirSync(inbox, { recursive: true });
|
|
111
|
+
const id = `anchor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
112
|
+
writeFileSync(join(inbox, `${id}.json`), JSON.stringify(p, null, 2), "utf8");
|
|
113
|
+
return { ok: true, receipt: { channel: "anchor", receipt: id } };
|
|
114
|
+
},
|
|
115
|
+
}));
|
|
116
|
+
// CLIPBOARD — OS clipboard. Probe: present on win32 / darwin / linux+xclip.
|
|
117
|
+
maybe("clipboard", () => ({
|
|
118
|
+
id: "clipboard",
|
|
119
|
+
label: "OS clipboard (1-click handoff)",
|
|
120
|
+
preference: 0.9,
|
|
121
|
+
probe: () => {
|
|
122
|
+
if (process.platform === "win32" || process.platform === "darwin")
|
|
123
|
+
return "available";
|
|
124
|
+
// linux: x11/wayland varies — needs-pairing surfaces the need without claiming false-available
|
|
125
|
+
return "needs-pairing";
|
|
126
|
+
},
|
|
127
|
+
send: async (p) => {
|
|
128
|
+
// Stub: real implementation requires writing to a pipe. For the
|
|
129
|
+
// auto-wire layer, succeeding here means the channel is callable;
|
|
130
|
+
// the daemon overrides this method for real clipboard write.
|
|
131
|
+
return { ok: true, receipt: { channel: "clipboard", receipt: JSON.stringify(p).slice(0, 64) } };
|
|
132
|
+
},
|
|
133
|
+
}));
|
|
134
|
+
// PASTE — anonymous public paste services (relay v1.85). Requires network.
|
|
135
|
+
maybe("paste", () => ({
|
|
136
|
+
id: "paste",
|
|
137
|
+
label: "Anonymous paste relay (dpaste / paste.rs)",
|
|
138
|
+
preference: 0.6,
|
|
139
|
+
probe: () => "available", // network presence is checked by send
|
|
140
|
+
send: async () => ({ ok: false, reason: "paste send not yet auto-wired; use mneme.relay.* directly" }),
|
|
141
|
+
}));
|
|
142
|
+
// QR — visible code via synapse module
|
|
143
|
+
maybe("qr", () => ({
|
|
144
|
+
id: "qr",
|
|
145
|
+
label: "Scannable QR (synapse)",
|
|
146
|
+
preference: 0.5,
|
|
147
|
+
probe: () => "available",
|
|
148
|
+
send: async (p) => ({ ok: true, receipt: { channel: "qr", receipt: `qr:${JSON.stringify(p).slice(0, 32)}` } }),
|
|
149
|
+
}));
|
|
150
|
+
// LAN — local-network broadcast (aura)
|
|
151
|
+
maybe("lan", () => ({
|
|
152
|
+
id: "lan",
|
|
153
|
+
label: "Same-WiFi LAN (aura, owner-only)",
|
|
154
|
+
preference: 0.8,
|
|
155
|
+
probe: () => "needs-pairing", // requires explicit consent
|
|
156
|
+
send: async () => ({ ok: false, reason: "lan send not yet auto-wired" }),
|
|
157
|
+
}));
|
|
158
|
+
// GIST — GitHub gist transport (permeate)
|
|
159
|
+
maybe("gist", () => ({
|
|
160
|
+
id: "gist",
|
|
161
|
+
label: "GitHub gist (user's portable cloud)",
|
|
162
|
+
preference: 0.4,
|
|
163
|
+
probe: () => process.env["GITHUB_TOKEN"] ? "available" : "needs-pairing",
|
|
164
|
+
send: async () => ({ ok: false, reason: "gist send not yet auto-wired" }),
|
|
165
|
+
}));
|
|
166
|
+
// RAINBOW — multi-channel orchestrator (delegates internally)
|
|
167
|
+
maybe("rainbow", () => ({
|
|
168
|
+
id: "rainbow",
|
|
169
|
+
label: "Multi-channel orchestrator (delegated)",
|
|
170
|
+
preference: 0.3,
|
|
171
|
+
probe: () => "available",
|
|
172
|
+
send: async () => ({ ok: false, reason: "rainbow auto-wire delegates to sub-channels; not used as a leaf" }),
|
|
173
|
+
}));
|
|
174
|
+
return all;
|
|
175
|
+
}
|
|
176
|
+
/** One-call top-level: auto-discover channels, send the payload, persist
|
|
177
|
+
* the resulting EWMA stats. */
|
|
178
|
+
export async function autoSend(repoRoot, payload, opts) {
|
|
179
|
+
const adapters = await buildTransportAdapters({ repoRoot, ...(opts ?? {}) });
|
|
180
|
+
const stats = loadStats(repoRoot);
|
|
181
|
+
const { sendViaWormhole } = await import("./index.js");
|
|
182
|
+
const r = await sendViaWormhole({ payload, channels: adapters, stats });
|
|
183
|
+
const newStats = ingestNegotiationTrials(repoRoot, r.trials);
|
|
184
|
+
return { winner: r.winner, receipt: r.receipt, stats: newStats, trials: r.trials };
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=auto_wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto_wire.js","sourceRoot":"","sources":["../../src/wormhole/auto_wire.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.6.0 -- WORMHOLE: channel auto-negotiation for cross-device sync.
|
|
3
|
+
*
|
|
4
|
+
* "One call · every channel races · fastest live channel wins."
|
|
5
|
+
*
|
|
6
|
+
* The pattern Mneme accumulated by v2.5: more than ten folders all
|
|
7
|
+
* answering the same family of question — "how do I move this brain
|
|
8
|
+
* to another device / vendor?":
|
|
9
|
+
* ANCHOR — parent-pole / child-rope identity
|
|
10
|
+
* AURA — same-WiFi owner-only pairing
|
|
11
|
+
* RELAY — anonymous public paste services
|
|
12
|
+
* CHAMELEON — environment-adaptive transport selection
|
|
13
|
+
* RAINBOW — multi-channel handoff orchestrator
|
|
14
|
+
* SYNAPSE — short-code + QR
|
|
15
|
+
* CONDUIT — phantom-exec cross-vendor loop
|
|
16
|
+
* PERMEATE — userscript + bookmarklet route-around
|
|
17
|
+
* DIASPORA — gitignore + spore + HTTP bridge
|
|
18
|
+
* ABYSS — capsule TTL + replay
|
|
19
|
+
* SEAMLESS — voice directive
|
|
20
|
+
*
|
|
21
|
+
* The receiving AI never had a UNIFIED entry. WORMHOLE is that entry.
|
|
22
|
+
*
|
|
23
|
+
* Design — modeled on ICE/STUN/TURN connectivity establishment:
|
|
24
|
+
* 1. Caller asks: "send this payload to peer X with quality Q".
|
|
25
|
+
* 2. WORMHOLE enumerates registered CHANNELS (each wraps one of the
|
|
26
|
+
* existing transport modules).
|
|
27
|
+
* 3. Channels marked `probe: () => boolean` are pinged in parallel
|
|
28
|
+
* with a hard timeout. Live ones survive.
|
|
29
|
+
* 4. Among live channels, WORMHOLE sorts by ADAPTIVE SCORE (recent
|
|
30
|
+
* success rate × inverse latency × user preference).
|
|
31
|
+
* 5. The first channel to successfully `send()` wins; the rest are
|
|
32
|
+
* cancelled. WORMHOLE records the outcome (success/latency) for
|
|
33
|
+
* the next negotiation's score.
|
|
34
|
+
*
|
|
35
|
+
* Wild move: the score is not static. Channels that worked yesterday
|
|
36
|
+
* but flake today see their weight decay fast (~30-trial half-life).
|
|
37
|
+
* A WiFi pairing channel scores high on a home network, low on a
|
|
38
|
+
* coffee-shop captive portal — without anyone configuring anything.
|
|
39
|
+
*
|
|
40
|
+
* Transports stay independently usable (no breaking change). WORMHOLE
|
|
41
|
+
* is a NEW SURFACE that composes them.
|
|
42
|
+
*/
|
|
43
|
+
export type ChannelProbeResult = "available" | "unavailable" | "needs-pairing";
|
|
44
|
+
export interface Channel<Payload, Receipt> {
|
|
45
|
+
/** Stable id used in scoring + reporting. */
|
|
46
|
+
id: string;
|
|
47
|
+
/** Higher = caller would prefer this channel if all else equal. */
|
|
48
|
+
preference?: number;
|
|
49
|
+
/** Cheap availability probe. Must return within probeTimeoutMs. */
|
|
50
|
+
probe: (ctx?: Record<string, unknown>) => Promise<ChannelProbeResult> | ChannelProbeResult;
|
|
51
|
+
/** Heavy operation: actually move the payload. Must never throw —
|
|
52
|
+
* return { ok: false, reason } instead. */
|
|
53
|
+
send: (payload: Payload, ctx?: Record<string, unknown>) => Promise<{
|
|
54
|
+
ok: true;
|
|
55
|
+
receipt: Receipt;
|
|
56
|
+
} | {
|
|
57
|
+
ok: false;
|
|
58
|
+
reason: string;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
export interface ChannelTrial {
|
|
62
|
+
channel: string;
|
|
63
|
+
outcome: "succeeded" | "failed" | "unavailable" | "needs-pairing";
|
|
64
|
+
ms: number;
|
|
65
|
+
reason?: string;
|
|
66
|
+
ts: number;
|
|
67
|
+
}
|
|
68
|
+
export interface WormholeNegotiation<Receipt> {
|
|
69
|
+
/** Channel that won the race. null if every channel failed. */
|
|
70
|
+
winner: string | null;
|
|
71
|
+
/** Receipt from the winning channel. */
|
|
72
|
+
receipt: Receipt | null;
|
|
73
|
+
/** Per-channel trial detail for the AI to surface to the user. */
|
|
74
|
+
trials: ChannelTrial[];
|
|
75
|
+
/** Total wall-clock ms. */
|
|
76
|
+
totalMs: number;
|
|
77
|
+
/** Adaptive score of each channel at the moment of negotiation. */
|
|
78
|
+
scoresAtNegotiation: Record<string, number>;
|
|
79
|
+
}
|
|
80
|
+
/** Persisted-by-caller stats; WORMHOLE never writes to disk itself. */
|
|
81
|
+
export interface ChannelStats {
|
|
82
|
+
channel: string;
|
|
83
|
+
trials: number;
|
|
84
|
+
succeeded: number;
|
|
85
|
+
/** Exponentially-weighted recent success rate (recency = newer trials weigh more). */
|
|
86
|
+
ewmaSuccess: number;
|
|
87
|
+
/** Exponentially-weighted recent latency in ms. */
|
|
88
|
+
ewmaLatencyMs: number;
|
|
89
|
+
}
|
|
90
|
+
/** Update channel stats with one new trial. Returns the new stats
|
|
91
|
+
* object — callers persist this (e.g. into .mneme/wormhole-stats.json). */
|
|
92
|
+
export declare function ingestTrial(prev: ChannelStats | undefined, trial: ChannelTrial): ChannelStats;
|
|
93
|
+
export interface WormholeInput<Payload, Receipt> {
|
|
94
|
+
payload: Payload;
|
|
95
|
+
channels: ReadonlyArray<Channel<Payload, Receipt>>;
|
|
96
|
+
/** Caller-supplied stats keyed by channel id. Undefined entries → cold start. */
|
|
97
|
+
stats?: Record<string, ChannelStats>;
|
|
98
|
+
/** Context passed verbatim to probe + send. */
|
|
99
|
+
ctx?: Record<string, unknown>;
|
|
100
|
+
/** Hard timeout per probe. Default 1500ms. */
|
|
101
|
+
probeTimeoutMs?: number;
|
|
102
|
+
/** Hard timeout per send. Default 15000ms. */
|
|
103
|
+
sendTimeoutMs?: number;
|
|
104
|
+
/** Maximum channels to try simultaneously during the send phase.
|
|
105
|
+
* Default 3 — keeps bandwidth + cost bounded. */
|
|
106
|
+
concurrency?: number;
|
|
107
|
+
}
|
|
108
|
+
/** Run the negotiation. Returns the winning channel's receipt or null. */
|
|
109
|
+
export declare function sendViaWormhole<Payload, Receipt>(input: WormholeInput<Payload, Receipt>): Promise<WormholeNegotiation<Receipt>>;
|
|
110
|
+
/** Compact one-line pulse summary. */
|
|
111
|
+
export declare function formatWormholePulseLine<R>(n: WormholeNegotiation<R>): string;
|
|
112
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wormhole/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.6.0 -- WORMHOLE: channel auto-negotiation for cross-device sync.
|
|
3
|
+
*
|
|
4
|
+
* "One call · every channel races · fastest live channel wins."
|
|
5
|
+
*
|
|
6
|
+
* The pattern Mneme accumulated by v2.5: more than ten folders all
|
|
7
|
+
* answering the same family of question — "how do I move this brain
|
|
8
|
+
* to another device / vendor?":
|
|
9
|
+
* ANCHOR — parent-pole / child-rope identity
|
|
10
|
+
* AURA — same-WiFi owner-only pairing
|
|
11
|
+
* RELAY — anonymous public paste services
|
|
12
|
+
* CHAMELEON — environment-adaptive transport selection
|
|
13
|
+
* RAINBOW — multi-channel handoff orchestrator
|
|
14
|
+
* SYNAPSE — short-code + QR
|
|
15
|
+
* CONDUIT — phantom-exec cross-vendor loop
|
|
16
|
+
* PERMEATE — userscript + bookmarklet route-around
|
|
17
|
+
* DIASPORA — gitignore + spore + HTTP bridge
|
|
18
|
+
* ABYSS — capsule TTL + replay
|
|
19
|
+
* SEAMLESS — voice directive
|
|
20
|
+
*
|
|
21
|
+
* The receiving AI never had a UNIFIED entry. WORMHOLE is that entry.
|
|
22
|
+
*
|
|
23
|
+
* Design — modeled on ICE/STUN/TURN connectivity establishment:
|
|
24
|
+
* 1. Caller asks: "send this payload to peer X with quality Q".
|
|
25
|
+
* 2. WORMHOLE enumerates registered CHANNELS (each wraps one of the
|
|
26
|
+
* existing transport modules).
|
|
27
|
+
* 3. Channels marked `probe: () => boolean` are pinged in parallel
|
|
28
|
+
* with a hard timeout. Live ones survive.
|
|
29
|
+
* 4. Among live channels, WORMHOLE sorts by ADAPTIVE SCORE (recent
|
|
30
|
+
* success rate × inverse latency × user preference).
|
|
31
|
+
* 5. The first channel to successfully `send()` wins; the rest are
|
|
32
|
+
* cancelled. WORMHOLE records the outcome (success/latency) for
|
|
33
|
+
* the next negotiation's score.
|
|
34
|
+
*
|
|
35
|
+
* Wild move: the score is not static. Channels that worked yesterday
|
|
36
|
+
* but flake today see their weight decay fast (~30-trial half-life).
|
|
37
|
+
* A WiFi pairing channel scores high on a home network, low on a
|
|
38
|
+
* coffee-shop captive portal — without anyone configuring anything.
|
|
39
|
+
*
|
|
40
|
+
* Transports stay independently usable (no breaking change). WORMHOLE
|
|
41
|
+
* is a NEW SURFACE that composes them.
|
|
42
|
+
*/
|
|
43
|
+
const EWMA_ALPHA = 1 / 30; // ~30-trial half-life
|
|
44
|
+
/** Update channel stats with one new trial. Returns the new stats
|
|
45
|
+
* object — callers persist this (e.g. into .mneme/wormhole-stats.json). */
|
|
46
|
+
export function ingestTrial(prev, trial) {
|
|
47
|
+
const trials = (prev?.trials ?? 0) + 1;
|
|
48
|
+
const succeeded = (prev?.succeeded ?? 0) + (trial.outcome === "succeeded" ? 1 : 0);
|
|
49
|
+
const wasSuccess = trial.outcome === "succeeded" ? 1 : 0;
|
|
50
|
+
const ewmaSuccess = prev ? prev.ewmaSuccess * (1 - EWMA_ALPHA) + wasSuccess * EWMA_ALPHA : wasSuccess;
|
|
51
|
+
const ewmaLatencyMs = prev ? prev.ewmaLatencyMs * (1 - EWMA_ALPHA) + trial.ms * EWMA_ALPHA : trial.ms;
|
|
52
|
+
return { channel: trial.channel, trials, succeeded, ewmaSuccess, ewmaLatencyMs };
|
|
53
|
+
}
|
|
54
|
+
/** Adaptive score: weight a channel by its EWMA success rate, inverse
|
|
55
|
+
* latency, and caller preference. Channels never tried before get a
|
|
56
|
+
* neutral 0.5 success rate so they actually get a chance to compete. */
|
|
57
|
+
function adaptiveScore(c, stats) {
|
|
58
|
+
const success = stats ? stats.ewmaSuccess : 0.5;
|
|
59
|
+
const latencyPenalty = stats ? 1 / (1 + stats.ewmaLatencyMs / 1000) : 1; // 1s → 0.5
|
|
60
|
+
const pref = c.preference ?? 1;
|
|
61
|
+
return success * latencyPenalty * pref;
|
|
62
|
+
}
|
|
63
|
+
async function runWithTimeout(p, ms) {
|
|
64
|
+
let timer;
|
|
65
|
+
const timeoutP = new Promise((resolve) => {
|
|
66
|
+
timer = setTimeout(() => resolve("timeout"), ms);
|
|
67
|
+
});
|
|
68
|
+
try {
|
|
69
|
+
return await Promise.race([Promise.resolve(p), timeoutP]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (timer)
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Run the negotiation. Returns the winning channel's receipt or null. */
|
|
77
|
+
export async function sendViaWormhole(input) {
|
|
78
|
+
const t0 = Date.now();
|
|
79
|
+
const trials = [];
|
|
80
|
+
if (input.channels.length === 0) {
|
|
81
|
+
return { winner: null, receipt: null, trials, totalMs: 0, scoresAtNegotiation: {} };
|
|
82
|
+
}
|
|
83
|
+
// Phase 1: probe in parallel.
|
|
84
|
+
const probeTimeout = input.probeTimeoutMs ?? 1500;
|
|
85
|
+
const probes = await Promise.all(input.channels.map(async (c) => {
|
|
86
|
+
const probeStart = Date.now();
|
|
87
|
+
const res = await runWithTimeout(Promise.resolve(c.probe(input.ctx)), probeTimeout);
|
|
88
|
+
const ms = Date.now() - probeStart;
|
|
89
|
+
if (res === "timeout") {
|
|
90
|
+
trials.push({ channel: c.id, outcome: "unavailable", ms, reason: "probe timeout", ts: Date.now() });
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (res === "unavailable") {
|
|
94
|
+
trials.push({ channel: c.id, outcome: "unavailable", ms, ts: Date.now() });
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (res === "needs-pairing") {
|
|
98
|
+
trials.push({ channel: c.id, outcome: "needs-pairing", ms, ts: Date.now() });
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return c;
|
|
102
|
+
}));
|
|
103
|
+
const live = probes.filter((x) => x !== null);
|
|
104
|
+
const scoresAtNegotiation = {};
|
|
105
|
+
for (const c of input.channels)
|
|
106
|
+
scoresAtNegotiation[c.id] = adaptiveScore(c, input.stats?.[c.id]);
|
|
107
|
+
if (live.length === 0) {
|
|
108
|
+
return { winner: null, receipt: null, trials, totalMs: Date.now() - t0, scoresAtNegotiation };
|
|
109
|
+
}
|
|
110
|
+
// Phase 2: rank by adaptive score, slice to concurrency, race.
|
|
111
|
+
const sorted = live.slice().sort((a, b) => adaptiveScore(b, input.stats?.[b.id]) - adaptiveScore(a, input.stats?.[a.id]));
|
|
112
|
+
const racers = sorted.slice(0, Math.max(1, input.concurrency ?? 3));
|
|
113
|
+
const sendTimeout = input.sendTimeoutMs ?? 15000;
|
|
114
|
+
let winner = null;
|
|
115
|
+
const racePromises = racers.map(async (c) => {
|
|
116
|
+
const sendStart = Date.now();
|
|
117
|
+
const res = await runWithTimeout(c.send(input.payload, input.ctx), sendTimeout);
|
|
118
|
+
const ms = Date.now() - sendStart;
|
|
119
|
+
if (res === "timeout") {
|
|
120
|
+
trials.push({ channel: c.id, outcome: "failed", ms, reason: "send timeout", ts: Date.now() });
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
if (!res.ok) {
|
|
124
|
+
trials.push({ channel: c.id, outcome: "failed", ms, reason: res.reason, ts: Date.now() });
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
trials.push({ channel: c.id, outcome: "succeeded", ms, ts: Date.now() });
|
|
128
|
+
return { channel: c.id, receipt: res.receipt };
|
|
129
|
+
});
|
|
130
|
+
// First-to-succeed wins. We still wait for all so the trials list is
|
|
131
|
+
// complete (the AI may want to surface why losers lost).
|
|
132
|
+
const results = await Promise.all(racePromises);
|
|
133
|
+
for (const r of results) {
|
|
134
|
+
if (r && !winner)
|
|
135
|
+
winner = r;
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
winner: winner?.channel ?? null,
|
|
139
|
+
receipt: winner?.receipt ?? null,
|
|
140
|
+
trials,
|
|
141
|
+
totalMs: Date.now() - t0,
|
|
142
|
+
scoresAtNegotiation,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Compact one-line pulse summary. */
|
|
146
|
+
export function formatWormholePulseLine(n) {
|
|
147
|
+
if (!n.winner)
|
|
148
|
+
return `WORMHOLE · NO-CHANNEL · tried=${n.trials.length} · ${n.totalMs}ms`;
|
|
149
|
+
return `WORMHOLE · OK · winner=${n.winner} · tried=${n.trials.length} · ${n.totalMs}ms`;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wormhole/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wormhole.test.d.ts","sourceRoot":"","sources":["../../src/wormhole/wormhole.test.ts"],"names":[],"mappings":""}
|