@mindot/will 0.4.0 → 0.5.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/cli.js CHANGED
@@ -3862,6 +3862,11 @@ var MODEL_PRICING = {
3862
3862
  // Legacy aliases kept for backward compat
3863
3863
  "anthropic/claude-haiku-4": { input: 1, output: 5 },
3864
3864
  "anthropic/claude-opus-4": { input: 5, output: 25 },
3865
+ // Z.ai (GLM-5 family). `glm-5.2[1m]` is the same model asking for its 1M
3866
+ // context window — same rate, so it gets its own row rather than relying on
3867
+ // the normalizer (a future long-context tier would price differently).
3868
+ "glm/glm-5.2": { input: 1.4, output: 4.4 },
3869
+ "glm/glm-5.2[1m]": { input: 1.4, output: 4.4 },
3865
3870
  // Google
3866
3871
  "google/gemini-2.0-flash": { input: 0.1, output: 0.4 },
3867
3872
  "google/gemini-2.0-pro": { input: 1.25, output: 5 },
@@ -13153,6 +13158,38 @@ async function withGate(fn, label, gate = llmGate) {
13153
13158
  }
13154
13159
 
13155
13160
  // src/llm/index.ts
13161
+ var ANTHROPIC_WIRE = /* @__PURE__ */ new Set(["anthropic", "glm"]);
13162
+ function speaksAnthropicWire(provider) {
13163
+ return ANTHROPIC_WIRE.has(provider);
13164
+ }
13165
+ function defaultBaseFor(provider) {
13166
+ switch (provider) {
13167
+ case "anthropic":
13168
+ return "https://api.anthropic.com/v1";
13169
+ // Z.ai documents the base as `…/api/anthropic` because the Anthropic SDK
13170
+ // appends `/v1/messages`; this client appends `/messages`, so the version
13171
+ // segment belongs here — verified against the live endpoint.
13172
+ case "glm":
13173
+ return "https://api.z.ai/api/anthropic/v1";
13174
+ case "openai":
13175
+ return "https://api.openai.com/v1";
13176
+ case "deepseek":
13177
+ return "https://api.deepseek.com/v1";
13178
+ case "google":
13179
+ return "https://generativelanguage.googleapis.com/v1beta";
13180
+ }
13181
+ }
13182
+ function defaultModelFor(provider) {
13183
+ return provider === "glm" ? "glm-5.2" : "claude-sonnet-4-5-20250929";
13184
+ }
13185
+ function anthropicWireHeaders(provider, apiKey) {
13186
+ return {
13187
+ "Content-Type": "application/json",
13188
+ "anthropic-version": "2023-06-01",
13189
+ "x-api-key": apiKey,
13190
+ ...provider === "glm" ? { Authorization: `Bearer ${apiKey}` } : {}
13191
+ };
13192
+ }
13156
13193
  var DEFAULT_CALL_META = { category: "executive", attribute: "master", function: "decision" };
13157
13194
  var LLMDirector = class {
13158
13195
  _willId;
@@ -13255,7 +13292,7 @@ var LLMDirector = class {
13255
13292
  this._recordCompletion(systemPrompt, userMessage, tick, result2, Date.now() - start, true);
13256
13293
  return result2;
13257
13294
  }
13258
- const result = this._provider === "anthropic" ? await this._callAnthropicStream(systemPrompt, userMessage, onChunk, temperature) : await (async () => {
13295
+ const result = speaksAnthropicWire(this._provider) ? await this._callAnthropicStream(systemPrompt, userMessage, onChunk, temperature) : await (async () => {
13259
13296
  const r = await this._callProvider(systemPrompt, userMessage, temperature);
13260
13297
  onChunk(r.text);
13261
13298
  return r;
@@ -13335,11 +13372,7 @@ var LLMDirector = class {
13335
13372
  try {
13336
13373
  res = await fetch(`${this._resolvedBase()}/messages`, {
13337
13374
  method: "POST",
13338
- headers: {
13339
- "Content-Type": "application/json",
13340
- "anthropic-version": "2023-06-01",
13341
- "x-api-key": this._apiKey
13342
- },
13375
+ headers: anthropicWireHeaders(this._provider, this._apiKey),
13343
13376
  body: JSON.stringify({
13344
13377
  model: this._model,
13345
13378
  max_tokens: this._maxOutputTokens,
@@ -13414,7 +13447,7 @@ var LLMDirector = class {
13414
13447
  return result2;
13415
13448
  }
13416
13449
  const result = await withGate(
13417
- () => this._provider === "anthropic" ? this._callAnthropicStream(systemPrompt, userMessage, () => {
13450
+ () => speaksAnthropicWire(this._provider) ? this._callAnthropicStream(systemPrompt, userMessage, () => {
13418
13451
  }, temperature) : this._callProvider(systemPrompt, userMessage, temperature),
13419
13452
  "executive/direct"
13420
13453
  );
@@ -13426,6 +13459,8 @@ var LLMDirector = class {
13426
13459
  switch (this._provider) {
13427
13460
  case "anthropic":
13428
13461
  return this._callAnthropic(systemPrompt, userMessage, temperature);
13462
+ case "glm":
13463
+ return this._callAnthropic(systemPrompt, userMessage, temperature);
13429
13464
  case "deepseek":
13430
13465
  return this._callOpenAI(systemPrompt, userMessage, temperature);
13431
13466
  case "openai":
@@ -13438,16 +13473,7 @@ var LLMDirector = class {
13438
13473
  }
13439
13474
  /** Default API base URL (including version segment) for a provider. */
13440
13475
  _baseFor(provider) {
13441
- switch (provider) {
13442
- case "anthropic":
13443
- return "https://api.anthropic.com/v1";
13444
- case "openai":
13445
- return "https://api.openai.com/v1";
13446
- case "deepseek":
13447
- return "https://api.deepseek.com/v1";
13448
- case "google":
13449
- return "https://generativelanguage.googleapis.com/v1beta";
13450
- }
13476
+ return defaultBaseFor(provider);
13451
13477
  }
13452
13478
  /** Resolved API base: explicit override wins, else the provider default. */
13453
13479
  _resolvedBase() {
@@ -13487,15 +13513,11 @@ var LLMDirector = class {
13487
13513
  };
13488
13514
  const res = await this._fetchWithTimeout(`${this._resolvedBase()}/messages`, {
13489
13515
  method: "POST",
13490
- headers: {
13491
- "Content-Type": "application/json",
13492
- "anthropic-version": "2023-06-01",
13493
- "x-api-key": this._apiKey
13494
- },
13516
+ headers: anthropicWireHeaders(this._provider, this._apiKey),
13495
13517
  body: JSON.stringify(body)
13496
13518
  });
13497
13519
  if (!res.ok)
13498
- throw new Error(`Anthropic API ${res.status}: ${(await res.text()).slice(0, 300)}`);
13520
+ throw new Error(`${this._provider} API ${res.status}: ${(await res.text()).slice(0, 300)}`);
13499
13521
  const data = await res.json(), text = data.content.find((b) => b.type === "text")?.text ?? "";
13500
13522
  return {
13501
13523
  text,
@@ -14275,7 +14297,7 @@ var ExecutiveEngine = class extends AsyncEngine {
14275
14297
  this._gatingState.executiveInterval = rtConfig.executiveInterval;
14276
14298
  this._gatingState.cooldownTicks = rtConfig.cooldownTicks;
14277
14299
  if (!this._llmDirector && this._willId) {
14278
- const execModel = this._models.executive ?? process.env.WILL_LLM_MODEL ?? "claude-sonnet-4-5-20250929";
14300
+ const execModel = this._models.executive ?? process.env.WILL_LLM_MODEL ?? defaultModelFor(this._llm?.provider ?? process.env.WILL_LLM_PROVIDER ?? "anthropic");
14279
14301
  this._llmDirector = this._directorFor(execModel);
14280
14302
  this._summarizer?.attachLLMDirector(this._directorFor(this._models.summarizer ?? execModel));
14281
14303
  }
@@ -22198,12 +22220,13 @@ async function checkIdentityCoherence(input, reviewer) {
22198
22220
  return { ok: !issues.some((i) => i.severity === "error"), ran: true, issues, raw: text };
22199
22221
  }
22200
22222
  async function reviewIdentityCoherence(input, opts = {}) {
22223
+ const provider = process.env.WILL_LLM_PROVIDER ?? "anthropic";
22201
22224
  const director = new LLMDirector({
22202
22225
  willId: opts.willId ?? "identity-coherence",
22203
- model: process.env.WILL_LLM_MODEL ?? "claude-sonnet-4-5-20250929",
22226
+ model: process.env.WILL_LLM_MODEL ?? defaultModelFor(provider),
22204
22227
  maxOutputTokens: 512,
22205
22228
  apiKey: process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY ?? "",
22206
- provider: process.env.WILL_LLM_PROVIDER ?? "anthropic",
22229
+ provider,
22207
22230
  sessionLogger: null,
22208
22231
  baseUrl: process.env.WILL_LLM_BASE_URL ?? process.env.OPENAI_BASE_URL,
22209
22232
  // When a per-Will tracker is supplied, the creation-time review records under
@@ -25760,7 +25783,9 @@ var Will = class _Will {
25760
25783
  }
25761
25784
  // ── Internals ──────────────────────────────────────────────
25762
25785
  _buildConfig(id, opts) {
25763
- const useMock = (opts.llm ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : "mock")) === "mock";
25786
+ const mode = opts.llm ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : process.env.ZAI_API_KEY ? "glm" : "mock");
25787
+ const useMock = mode === "mock";
25788
+ const llmConfig = mode === "glm" ? { provider: "glm", ...opts.llmConfig } : opts.llmConfig;
25764
25789
  return {
25765
25790
  id,
25766
25791
  name: opts.name,
@@ -25772,7 +25797,7 @@ var Will = class _Will {
25772
25797
  },
25773
25798
  anatomy: opts.anatomy ?? "mind",
25774
25799
  model: opts.model,
25775
- llm: opts.llmConfig,
25800
+ llm: llmConfig,
25776
25801
  testMode: useMock,
25777
25802
  persistentMemory: opts.persist ?? false,
25778
25803
  snapshotInterval: 100,
@@ -25955,21 +25980,32 @@ function routeLogsToStderr() {
25955
25980
  function slug2(s) {
25956
25981
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "will";
25957
25982
  }
25983
+ function resolveLlmMode() {
25984
+ const explicit = process.env.WILL_LLM;
25985
+ if (explicit) return explicit;
25986
+ if (process.env.ANTHROPIC_API_KEY) return "anthropic";
25987
+ if (process.env.ZAI_API_KEY) return "glm";
25988
+ return "mock";
25989
+ }
25990
+ function resolveLlmKey(mode) {
25991
+ return process.env.WILL_LLM_API_KEY ?? (mode === "glm" ? process.env.ZAI_API_KEY : process.env.ANTHROPIC_API_KEY);
25992
+ }
25958
25993
  async function preflightLLM(anatomy) {
25959
- const mode = process.env.WILL_LLM ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : "mock");
25994
+ const mode = resolveLlmMode();
25960
25995
  if (mode === "mock" || anatomy === "reflex") return;
25961
- const key = process.env.WILL_LLM_API_KEY ?? process.env.ANTHROPIC_API_KEY;
25996
+ const key = resolveLlmKey(mode);
25962
25997
  if (!key) {
25963
- console.error("[will] WILL_LLM=anthropic but no ANTHROPIC_API_KEY / WILL_LLM_API_KEY is set.");
25998
+ const expected = mode === "glm" ? "ZAI_API_KEY" : "ANTHROPIC_API_KEY";
25999
+ console.error(`[will] WILL_LLM=${mode} but no ${expected} / WILL_LLM_API_KEY is set.`);
25964
26000
  console.error("[will] The Will would boot, perceive, and never speak. Set a key, or run keyless with WILL_LLM=mock.");
25965
26001
  process.exit(2);
25966
26002
  }
25967
- const base = process.env.WILL_LLM_BASE_URL ?? "https://api.anthropic.com/v1";
25968
- const model = process.env.WILL_LLM_MODEL ?? "claude-haiku-4-5-20251001";
26003
+ const base = process.env.WILL_LLM_BASE_URL ?? defaultBaseFor(mode);
26004
+ const model = process.env.WILL_LLM_MODEL ?? (mode === "glm" ? defaultModelFor("glm") : "claude-haiku-4-5-20251001");
25969
26005
  try {
25970
26006
  const res = await fetch(`${base}/messages`, {
25971
26007
  method: "POST",
25972
- headers: { "content-type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": key },
26008
+ headers: anthropicWireHeaders(mode, key),
25973
26009
  body: JSON.stringify({ model, max_tokens: 1, messages: [{ role: "user", content: "ping" }] }),
25974
26010
  signal: AbortSignal.timeout(2e4)
25975
26011
  });
@@ -26474,21 +26510,200 @@ async function createDiscordClient() {
26474
26510
  });
26475
26511
  }
26476
26512
 
26513
+ // src/channels/whatsapp.ts
26514
+ var WHATSAPP_MESSAGE_LIMIT = 65536;
26515
+ var isGroupJid = (jid) => jid.endsWith("@g.us");
26516
+ var bareId = (jid) => jid.split("@")[0].split(":")[0];
26517
+ var dmJidFor = (userId) => `${userId}@s.whatsapp.net`;
26518
+ function textOf(m) {
26519
+ const msg = m.message;
26520
+ return msg?.conversation ?? msg?.extendedTextMessage?.text ?? msg?.imageMessage?.caption ?? msg?.videoMessage?.caption ?? "";
26521
+ }
26522
+ async function connectWhatsApp(will, opts = {}) {
26523
+ const log = opts.log ?? ((m) => console.error(`[will:whatsapp] ${m}`));
26524
+ const roster = new ChannelRoster(opts.rosterPath ?? `.will/${will.id}.whatsapp.json`);
26525
+ const allowed = opts.chats?.length ? new Set(opts.chats) : null;
26526
+ let closed = false;
26527
+ const socket = opts.socket ?? await createWhatsAppSocket({
26528
+ authPath: opts.authPath ?? `.will/${will.id}.wa-auth`,
26529
+ log,
26530
+ stillOpen: () => !closed
26531
+ });
26532
+ let lastActiveChatId = opts.homeChatId ?? null;
26533
+ socket.ev.on("messages.upsert", ({ messages, type }) => {
26534
+ if (type && type !== "notify") return;
26535
+ for (const m of messages) void onMessage(m);
26536
+ });
26537
+ async function onMessage(m) {
26538
+ const jid = m.key.remoteJid;
26539
+ if (!jid || m.key.fromMe || !m.message || m.messageStubType) return;
26540
+ if (jid.endsWith("@broadcast") || jid.endsWith("@newsletter")) return;
26541
+ if (allowed && !allowed.has(jid)) return;
26542
+ const isGroup = isGroupJid(jid);
26543
+ const senderJid = isGroup ? m.key.participant : jid;
26544
+ if (!senderJid) return;
26545
+ const selfId = socket.user ? bareId(socket.user.id) : null;
26546
+ const mentioned = m.message.extendedTextMessage?.contextInfo?.mentionedJid ?? [];
26547
+ const addressed = !isGroup || selfId != null && mentioned.some((j) => bareId(j) === selfId);
26548
+ if (opts.mentionOnly && !addressed) return;
26549
+ const userId = bareId(senderJid);
26550
+ const entityId = `whatsapp:${userId}`;
26551
+ const speaker = m.pushName ?? void 0;
26552
+ roster.record({
26553
+ entityId,
26554
+ userId,
26555
+ ...speaker ? { displayName: speaker } : {},
26556
+ ...isGroup ? { lastChannelId: jid } : { dmChannelId: jid }
26557
+ });
26558
+ if (isGroup) lastActiveChatId = jid;
26559
+ if (addressed) await socket.sendPresenceUpdate?.("composing", jid).catch(() => {
26560
+ });
26561
+ const text = textOf(m);
26562
+ if (!text.trim()) return;
26563
+ await will.perceive({
26564
+ text,
26565
+ from: entityId,
26566
+ thread: `whatsapp:${jid}`,
26567
+ ...speaker ? { speaker } : {}
26568
+ });
26569
+ }
26570
+ will.on("message", (m) => {
26571
+ if (!closed) void deliver(m);
26572
+ });
26573
+ async function deliver(m) {
26574
+ const peer = m.to ? roster.resolve(m.to) : void 0;
26575
+ const chunks = chunkText(m.content, WHATSAPP_MESSAGE_LIMIT);
26576
+ const derivedDm = m.to?.startsWith("whatsapp:") ? dmJidFor(m.to.slice("whatsapp:".length)) : void 0;
26577
+ const targets = [peer?.lastChannelId, peer?.dmChannelId, derivedDm, opts.homeChatId ?? void 0, lastActiveChatId ?? void 0];
26578
+ for (const jid of targets) {
26579
+ if (!jid) continue;
26580
+ try {
26581
+ for (const chunk of chunks) await socket.sendMessage(jid, { text: chunk });
26582
+ return;
26583
+ } catch {
26584
+ }
26585
+ }
26586
+ log(`no route for utterance to '${m.to}' \u2014 dropped (${m.content.length} chars)`);
26587
+ }
26588
+ return {
26589
+ kind: "whatsapp",
26590
+ async start() {
26591
+ log(`${will.name} is present on WhatsApp${socket.user ? ` as ${bareId(socket.user.id)}` : ""}`);
26592
+ },
26593
+ async close() {
26594
+ if (closed) return;
26595
+ closed = true;
26596
+ roster.flush();
26597
+ try {
26598
+ socket.end?.();
26599
+ } catch {
26600
+ }
26601
+ }
26602
+ };
26603
+ }
26604
+ async function createWhatsAppSocket(o) {
26605
+ let baileys;
26606
+ try {
26607
+ baileys = await import('baileys');
26608
+ } catch {
26609
+ throw new Error("baileys is not installed (it is an optionalDependency) \u2014 run `bun add baileys` / `npm i baileys` and retry.");
26610
+ }
26611
+ const { default: makeWASocket, useMultiFileAuthState, DisconnectReason } = baileys;
26612
+ const noop = () => {
26613
+ };
26614
+ const logger2 = {
26615
+ level: "silent",
26616
+ child() {
26617
+ return logger2;
26618
+ },
26619
+ trace: noop,
26620
+ debug: noop,
26621
+ info: noop,
26622
+ warn: noop,
26623
+ error: noop,
26624
+ fatal: noop
26625
+ };
26626
+ const subscribers = [];
26627
+ let inner = null;
26628
+ const facade = {
26629
+ get user() {
26630
+ return inner?.user ? { id: inner.user.id, name: inner.user.name ?? void 0 } : null;
26631
+ },
26632
+ ev: { on: (_e, fn) => {
26633
+ subscribers.push(fn);
26634
+ } },
26635
+ sendMessage: (jid, content) => {
26636
+ if (!inner) return Promise.reject(new Error("whatsapp socket not connected"));
26637
+ return inner.sendMessage(jid, content);
26638
+ },
26639
+ sendPresenceUpdate: (state2, jid) => inner?.sendPresenceUpdate(state2, jid) ?? Promise.resolve(),
26640
+ end: () => inner?.end(void 0)
26641
+ };
26642
+ const { state, saveCreds } = await useMultiFileAuthState(o.authPath);
26643
+ await new Promise((resolveOpen, rejectOpen) => {
26644
+ let opened = false;
26645
+ function connect2() {
26646
+ const sock = makeWASocket({ auth: state, logger: logger2 });
26647
+ inner = sock;
26648
+ sock.ev.on("creds.update", saveCreds);
26649
+ sock.ev.on("messages.upsert", (u) => {
26650
+ for (const fn of subscribers) fn(u);
26651
+ });
26652
+ sock.ev.on("connection.update", (update) => {
26653
+ const { connection, lastDisconnect, qr } = update;
26654
+ if (qr) void printQr(qr, o.log);
26655
+ if (connection === "open" && !opened) {
26656
+ opened = true;
26657
+ resolveOpen();
26658
+ }
26659
+ if (connection === "close") {
26660
+ const code = lastDisconnect?.error?.output?.statusCode;
26661
+ if (code === DisconnectReason.loggedOut) {
26662
+ const err = new Error("WhatsApp unlinked this device (logged out) \u2014 delete the auth dir and pair again.");
26663
+ o.log(err.message);
26664
+ if (!opened) rejectOpen(err);
26665
+ return;
26666
+ }
26667
+ if (o.stillOpen()) {
26668
+ o.log(`connection closed (status ${code ?? "?"}) \u2014 reconnecting\u2026`);
26669
+ setTimeout(connect2, 3e3);
26670
+ }
26671
+ }
26672
+ });
26673
+ }
26674
+ connect2();
26675
+ });
26676
+ return facade;
26677
+ }
26678
+ async function printQr(qr, log) {
26679
+ log("pair this device: WhatsApp \u2192 Settings \u2192 Linked devices \u2192 Link a device");
26680
+ try {
26681
+ const qrt = await import('qrcode-terminal');
26682
+ (qrt.default ?? qrt).generate(qr, { small: true });
26683
+ } catch {
26684
+ log(`qrcode-terminal not installed \u2014 raw pairing code:
26685
+ ${qr}`);
26686
+ }
26687
+ }
26688
+
26477
26689
  // src/cli.ts
26478
26690
  routeLogsToStderr();
26479
- var USAGE = `usage: will <mcp | serve | discord>
26691
+ var USAGE = `usage: will <mcp | serve | discord | whatsapp>
26480
26692
 
26481
- mcp host a persistent mind over MCP stdio (Claude Desktop / Claude Code)
26482
- serve host a persistent mind over HTTP (any language; WILL_PORT, default 7777)
26483
- discord put a persistent mind in a Discord server (DISCORD_BOT_TOKEN; optional
26484
- WILL_DISCORD_CHANNELS, WILL_DISCORD_MENTION_ONLY, WILL_DISCORD_HOME_CHANNEL)
26693
+ mcp host a persistent mind over MCP stdio (Claude Desktop / Claude Code)
26694
+ serve host a persistent mind over HTTP (any language; WILL_PORT, default 7777)
26695
+ discord put a persistent mind in a Discord server (DISCORD_BOT_TOKEN; optional
26696
+ WILL_DISCORD_CHANNELS, WILL_DISCORD_MENTION_ONLY, WILL_DISCORD_HOME_CHANNEL)
26697
+ whatsapp put a persistent mind on WhatsApp \u2014 QR-pairs as a linked device; no token.
26698
+ UNOFFICIAL protocol (ban risk; use a spare number \u2014 docs/channels/whatsapp.md).
26699
+ Optional WILL_WHATSAPP_CHATS, WILL_WHATSAPP_MENTION_ONLY, WILL_WHATSAPP_HOME_CHAT
26485
26700
 
26486
26701
  Shared env: WILL_NAME, WILL_IDENTITY, WILL_TIER, WILL_LLM, WILL_TICK_MS,
26487
26702
  WILL_SEED, WILL_PMA_PATH, WILL_MCP_SERVERS. The mind persists across runs via
26488
26703
  its PMA artifact.`;
26489
26704
  async function main() {
26490
26705
  const sub = process.argv[2];
26491
- if (sub !== "mcp" && sub !== "serve" && sub !== "discord") {
26706
+ if (sub !== "mcp" && sub !== "serve" && sub !== "discord" && sub !== "whatsapp") {
26492
26707
  console.error(sub ? `unknown subcommand: ${sub}
26493
26708
 
26494
26709
  ${USAGE}` : USAGE);
@@ -26520,6 +26735,21 @@ ${USAGE}` : USAGE);
26520
26735
  console.error(`[will] ${name} is present on Discord (tick ${tickMs}ms, anatomy ${anatomy}) \u2014 it speaks when it decides to.`);
26521
26736
  return;
26522
26737
  }
26738
+ if (sub === "whatsapp") {
26739
+ const csv = (v) => v?.split(",").map((s) => s.trim()).filter(Boolean);
26740
+ const stem = pmaPath.replace(/(\.pma)?\.json$/, "");
26741
+ const bridge = await connectWhatsApp(will, {
26742
+ chats: csv(process.env.WILL_WHATSAPP_CHATS),
26743
+ mentionOnly: /^(1|true|yes)$/i.test(process.env.WILL_WHATSAPP_MENTION_ONLY ?? ""),
26744
+ homeChatId: process.env.WILL_WHATSAPP_HOME_CHAT,
26745
+ authPath: stem + ".wa-auth",
26746
+ rosterPath: stem + ".whatsapp.json"
26747
+ });
26748
+ onCleanup(() => bridge.close());
26749
+ await bridge.start();
26750
+ console.error(`[will] ${name} is present on WhatsApp (tick ${tickMs}ms, anatomy ${anatomy}) \u2014 it speaks when it decides to.`);
26751
+ return;
26752
+ }
26523
26753
  const port = parseInt(process.env.WILL_PORT ?? "7777");
26524
26754
  const host = process.env.WILL_HOST ?? "127.0.0.1";
26525
26755
  const server = buildWillHttpServer(will, { pmaPath });