@agentcomms/whatsapp 0.25.0 → 0.26.1

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.
@@ -1,4 +1,4 @@
1
- import { C as unknown, S as union, b as strictObject, c as boolean, g as object, h as number, i as _enum, l as discriminatedUnion, m as looseObject, n as datetime, p as literal, s as array, x as string, y as record } from "./iso-USuVU_sk.mjs";
1
+ import { C as unknown, S as union, b as strictObject, c as boolean, g as object, h as number, i as _enum, l as discriminatedUnion, m as looseObject, n as datetime, p as literal, s as array, x as string, y as record } from "./iso-5-9R47LC.mjs";
2
2
  import { access, chmod, constants, link, lstat, mkdir, open, readFile, readdir, readlink, realpath, rename, rm, stat, unlink, utimes } from "node:fs/promises";
3
3
  import path, { basename, delimiter, dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
4
4
  import { domainToASCII, fileURLToPath, pathToFileURL } from "node:url";
@@ -11,7 +11,7 @@ import { execFile, spawn } from "node:child_process";
11
11
  //#endregion
12
12
  //#region src/version.ts
13
13
  /** The package version, read from package.json at build time. */
14
- const VERSION$2 = "0.25.0";
14
+ const VERSION$2 = "0.26.1";
15
15
  //#endregion
16
16
  //#region src/caller.ts
17
17
  /** This package's name, as its manifest has it. */
@@ -28,7 +28,7 @@ const CALLER = Object.freeze({
28
28
  packageName: PACKAGE_NAME
29
29
  });
30
30
  //#endregion
31
- //#region ../core/dist/update-check-CMD_Te5S.mjs
31
+ //#region ../core/dist/update-check-CfybSI56.mjs
32
32
  /**
33
33
  * Every failure a user or an agent can act on carries a specific, stable code from this registry, a message, an
34
34
  * optional one-line hint and structured `details`. The code decides the process exit status; agents and skills branch
@@ -5303,8 +5303,8 @@ async function mergeIntoJsonConfig(path, client, name, entry) {
5303
5303
  }
5304
5304
  /** Starts the server exactly as a client would, and completes the handshake. */
5305
5305
  async function verifyEntry(entry, product) {
5306
- const { Client } = await import("./dist-C7DG2LdL-l0mj9_dH.mjs");
5307
- const { StdioClientTransport } = await import("./stdio-CyjIgrch-59zcjSEt.mjs");
5306
+ const { Client } = await import("./dist-C7DG2LdL-6QTVBgXp.mjs");
5307
+ const { StdioClientTransport } = await import("./stdio-CRbkOCqe-BpzkpnG6.mjs");
5308
5308
  const client = new Client({
5309
5309
  name: `${product.binary}-install-check`,
5310
5310
  version: product.version
@@ -12026,7 +12026,7 @@ function folding(platform) {
12026
12026
  return platform === "win32" ? (text) => text.toLowerCase() : (text) => text;
12027
12027
  }
12028
12028
  /** The package version, read from package.json at build time. */
12029
- const VERSION = "0.25.0";
12029
+ const VERSION = "0.26.1";
12030
12030
  /**
12031
12031
  * The command a person is told to run for one of this suite's own CLIs (design 2026-10-04, D1 and D5; CUE-403).
12032
12032
  *
@@ -13320,7 +13320,7 @@ const PER_WORKSPACE = {
13320
13320
  "agentLabel.slackStyles": "app",
13321
13321
  "agentLabel.slackIcons": "your Slack photo"
13322
13322
  };
13323
- function recordOf$1(value) {
13323
+ function recordOf$2(value) {
13324
13324
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
13325
13325
  }
13326
13326
  /**
@@ -13330,7 +13330,7 @@ function recordOf$1(value) {
13330
13330
  */
13331
13331
  function perWorkspace(field, before, after) {
13332
13332
  const none = PER_WORKSPACE[field];
13333
- const [was, now] = [recordOf$1(before ?? {}), recordOf$1(after ?? {})];
13333
+ const [was, now] = [recordOf$2(before ?? {}), recordOf$2(after ?? {})];
13334
13334
  if (none === void 0 || was === void 0 || now === void 0) return void 0;
13335
13335
  const value = (record, name) => Object.hasOwn(record, name) ? String(record[name]) : none;
13336
13336
  const moved = [.../* @__PURE__ */ new Set([...Object.keys(was), ...Object.keys(now)])].filter((name) => value(was, name) !== value(now, name)).sort();
@@ -13343,7 +13343,7 @@ function perWorkspace(field, before, after) {
13343
13343
  * commas. A level one side does not list allows everything there. Undefined for a value of another shape.
13344
13344
  */
13345
13345
  function perLevel(field, before, after) {
13346
- const [was, now] = [recordOf$1(before ?? {}), recordOf$1(after ?? {})];
13346
+ const [was, now] = [recordOf$2(before ?? {}), recordOf$2(after ?? {})];
13347
13347
  if (field !== "behaviour.rules" || was === void 0 || now === void 0) return void 0;
13348
13348
  const value = (record, level) => Object.hasOwn(record, level) ? String(record[level]) : "everything";
13349
13349
  const levels = [.../* @__PURE__ */ new Set([...Object.keys(was), ...Object.keys(now)])].sort();
@@ -13918,7 +13918,7 @@ const OUTCOMES = [
13918
13918
  "unknown"
13919
13919
  ];
13920
13920
  const isString = (value) => typeof value === "string";
13921
- const isTime$2 = (value) => isString(value) && ISO.test(value) && !Number.isNaN(Date.parse(value));
13921
+ const isTime$3 = (value) => isString(value) && ISO.test(value) && !Number.isNaN(Date.parse(value));
13922
13922
  /** One line as an entry, or null when it is not one this release wrote. */
13923
13923
  function entryOf$2(line) {
13924
13924
  let raw;
@@ -13929,7 +13929,7 @@ function entryOf$2(line) {
13929
13929
  }
13930
13930
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
13931
13931
  const entry = raw;
13932
- if (!isTime$2(entry.at)) return null;
13932
+ if (!isTime$3(entry.at)) return null;
13933
13933
  if (entry.kind === "use") {
13934
13934
  if (![
13935
13935
  "standingId",
@@ -13961,7 +13961,7 @@ function entryOf$2(line) {
13961
13961
  if (entry.kind === "outcome") {
13962
13962
  if (!isString(entry.childId) || !isString(entry.outcome) || !OUTCOMES.includes(entry.outcome)) return null;
13963
13963
  if (!isString(entry.notice) || entry.link !== void 0 && !isString(entry.link)) return null;
13964
- if (entry.actAt !== void 0 && !isTime$2(entry.actAt)) return null;
13964
+ if (entry.actAt !== void 0 && !isTime$3(entry.actAt)) return null;
13965
13965
  return {
13966
13966
  kind: "outcome",
13967
13967
  at: entry.at,
@@ -14406,7 +14406,7 @@ function updateJobSupported(platform) {
14406
14406
  function updateJobMarkerPath(stateDir) {
14407
14407
  return join(stateDir, "update-job.json");
14408
14408
  }
14409
- const isTime$1 = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
14409
+ const isTime$2 = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
14410
14410
  /** The record, or null when there is none — or none this release can read, which is treated as none. */
14411
14411
  async function readUpdateJobMarker(stateDir) {
14412
14412
  let parsed;
@@ -14421,11 +14421,11 @@ async function readUpdateJobMarker(stateDir) {
14421
14421
  platform: record.platform,
14422
14422
  installedAt: record.installedAt,
14423
14423
  label: record.label,
14424
- ...isTime$1(record.lastRun) ? { lastRun: record.lastRun } : {}
14424
+ ...isTime$2(record.lastRun) ? { lastRun: record.lastRun } : {}
14425
14425
  };
14426
14426
  }
14427
14427
  function within$1(at, now, intervalMs) {
14428
- if (at === void 0 || !isTime$1(at)) return false;
14428
+ if (at === void 0 || !isTime$2(at)) return false;
14429
14429
  const since = now.getTime() - Date.parse(at);
14430
14430
  return since >= -3e5 && since < intervalMs;
14431
14431
  }
@@ -14506,7 +14506,7 @@ const NOTICE_OUTCOMES = [
14506
14506
  function readBackgroundNotice(value) {
14507
14507
  if (typeof value !== "object" || value === null) return null;
14508
14508
  const { at, outcome, version, text } = value;
14509
- if (!isTime(at) || !NOTICE_OUTCOMES.includes(outcome) || typeof text !== "string") return null;
14509
+ if (!isTime$1(at) || !NOTICE_OUTCOMES.includes(outcome) || typeof text !== "string") return null;
14510
14510
  return {
14511
14511
  at,
14512
14512
  outcome,
@@ -14535,7 +14535,7 @@ const EMPTY_UPDATE_CHECK = Object.freeze({
14535
14535
  function updateCheckPath(stateDir) {
14536
14536
  return join(stateDir, UPDATE_CHECK_FILE);
14537
14537
  }
14538
- const isTime = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
14538
+ const isTime$1 = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
14539
14539
  const isWords = (value) => Array.isArray(value) && value.every((word) => typeof word === "string");
14540
14540
  function readRelease(value) {
14541
14541
  if (typeof value !== "object" || value === null) return null;
@@ -14568,17 +14568,17 @@ async function readUpdateCheck(stateDir) {
14568
14568
  }
14569
14569
  const held = typeof parsed === "object" && parsed !== null ? parsed : {};
14570
14570
  return {
14571
- lastChecked: isTime(held.lastChecked) ? held.lastChecked : null,
14571
+ lastChecked: isTime$1(held.lastChecked) ? held.lastChecked : null,
14572
14572
  latest: isPlainVersion(held.latest) ? held.latest : null,
14573
14573
  behind: typeof held.behind === "boolean" ? held.behind : null,
14574
14574
  current: readCurrent(held.current),
14575
14575
  lastError: typeof held.lastError === "string" ? held.lastError.slice(0, 300) : null,
14576
- snoozedUntil: isTime(held.snoozedUntil) ? held.snoozedUntil : null,
14577
- checking: isTime(held.checking) ? held.checking : null,
14578
- commandNoticeAt: isTime(held.commandNoticeAt) ? held.commandNoticeAt : null,
14579
- noticeAt: isTime(held.noticeAt) ? held.noticeAt : null,
14580
- pausedAt: isTime(held.pausedAt) ? held.pausedAt : null,
14581
- pausedUntil: isTime(held.pausedUntil) ? held.pausedUntil : null,
14576
+ snoozedUntil: isTime$1(held.snoozedUntil) ? held.snoozedUntil : null,
14577
+ checking: isTime$1(held.checking) ? held.checking : null,
14578
+ commandNoticeAt: isTime$1(held.commandNoticeAt) ? held.commandNoticeAt : null,
14579
+ noticeAt: isTime$1(held.noticeAt) ? held.noticeAt : null,
14580
+ pausedAt: isTime$1(held.pausedAt) ? held.pausedAt : null,
14581
+ pausedUntil: isTime$1(held.pausedUntil) ? held.pausedUntil : null,
14582
14582
  release: readRelease(held.release),
14583
14583
  securityAsked: isPlainVersion(held.securityAsked) ? held.securityAsked : null,
14584
14584
  securityShown: readSecurityShown(held.securityShown),
@@ -14587,11 +14587,41 @@ async function readUpdateCheck(stateDir) {
14587
14587
  };
14588
14588
  }
14589
14589
  /**
14590
+ * How every writer of the record takes the lock beside it: 2 s for each holder, and 15 s in all (design 2026-10-22, R2).
14591
+ *
14592
+ * Every server's tool call takes this lock for its refresh, so on a busy machine a writer often waits behind several
14593
+ * holders in turn, each holding it for a moment. Counted from the start of the wait, 2 s was a budget for that whole
14594
+ * queue, and a writer at the back of it gave up while the lock was being handed on exactly as it should be: a
14595
+ * person's update was installed and its record dropped. Counted per holder, a writer gives up only on one holder that
14596
+ * kept the lock for 2 s, which is a stuck lock; 15 s bounds a queue that never empties, as config writes are bounded.
14597
+ */
14598
+ const UPDATE_CHECK_LOCK = Object.freeze({
14599
+ timeoutPerHolder: true,
14600
+ timeoutMs: 2e3,
14601
+ maxWaitMs: 15e3
14602
+ });
14603
+ /**
14604
+ * How a notice takes the lock: 2 s in all, counted from the start of the wait, however often it changes hands meanwhile.
14605
+ *
14606
+ * A notice is best effort. Deciding a tool call's or a command's line, the security line in a preview, and recording
14607
+ * the person's answer to that line take the lock only for what is said, and each runs while a person or an agent
14608
+ * waits. Waited out holder by holder, as `UPDATE_CHECK_LOCK` waits, a busy machine's queue of refreshes held them for
14609
+ * up to 15 s. Busy past 2 s, a notice decides nothing this time, and a later call says it.
14610
+ *
14611
+ * Claiming the day's check is best effort too: busy past 2 s, nothing is claimed, and a later process claims it (0.26.0
14612
+ * review B). The claim's function is not named here: this comment ships in WhatsApp's bundle, which carries none of the
14613
+ * checker, not even its name (`packages/whatsapp/test/update-gate.test.ts`).
14614
+ */
14615
+ const UPDATE_NOTICE_LOCK = Object.freeze({ timeoutMs: 2e3 });
14616
+ /**
14590
14617
  * Reads the record, changes it and writes it back, under a lock beside it, atomically — so a server recording a check
14591
14618
  * and a person's "not now" arriving at the same moment each keep what the other wrote. Returns what `change` returned
14592
14619
  * alongside the record written; `change` returning `null` writes nothing.
14620
+ *
14621
+ * `lock` is how long to wait for the lock: `UPDATE_CHECK_LOCK`, for a record that matters, unless a notice or the
14622
+ * day's claim passes `UPDATE_NOTICE_LOCK`. Busy past it, this throws LOCK_TIMEOUT, and nothing is written.
14593
14623
  */
14594
- async function changeUpdateCheck(stateDir, change) {
14624
+ async function changeUpdateCheck(stateDir, change, lock = UPDATE_CHECK_LOCK) {
14595
14625
  return withFileLock(join(stateDir, ".update-check.lock"), async () => {
14596
14626
  const current = await readUpdateCheck(stateDir);
14597
14627
  const next = change(current);
@@ -14606,7 +14636,7 @@ async function changeUpdateCheck(stateDir, change) {
14606
14636
  result: next.result,
14607
14637
  written: true
14608
14638
  };
14609
- }, { timeoutMs: 2e3 });
14639
+ }, lock);
14610
14640
  }
14611
14641
  /**
14612
14642
  * Whether the check is due: never asked, asked a day or more ago, or — a clock that went back — asked more than a few
@@ -14845,7 +14875,7 @@ async function securityAnswered(stateDir, approvalId) {
14845
14875
  securityAsked: record.securityShown.version,
14846
14876
  securityShown: null
14847
14877
  } };
14848
- });
14878
+ }, UPDATE_NOTICE_LOCK);
14849
14879
  } catch {}
14850
14880
  }
14851
14881
  /** A local time as a person reads it in a preview: `2026-09-29 00:00`. */
@@ -15510,6 +15540,7 @@ function serverInstallChange(core, env, request, own, deps = {}) {
15510
15540
  });
15511
15541
  const unverifiedOwn = fetches && deps.verifyRelease === void 0 && running !== void 0 && running.packageName === product.packageName && running.version === version;
15512
15542
  const verified = fetches && !unverifiedOwn || checksDisk ? (await verifyReleaseSet(from, /* @__PURE__ */ new Map([[product.packageName, version]]), "install", from.registry === null || from.verify === null ? verifyInCore() : void 0)).get(product.packageName) : void 0;
15543
+ if (request.print !== true && launcher === "npx" && target.writes && deps.verifyRelease !== void 0) await verifyReleaseSet(from, /* @__PURE__ */ new Map([[facts.npxPackage, version]]), "install");
15513
15544
  planned = {
15514
15545
  product,
15515
15546
  install: plannedInstall(preflight),
@@ -15689,7 +15720,7 @@ function serverPruneChange(core, env, request, own) {
15689
15720
  };
15690
15721
  const { packageName, version } = ownProduct(request.channel, own) ?? {
15691
15722
  packageName: serverFacts(request.channel).packageName,
15692
- version: "0.25.0"
15723
+ version: "0.26.1"
15693
15724
  };
15694
15725
  const product = {
15695
15726
  packageName,
@@ -15971,7 +16002,7 @@ async function decideNotice(options, where) {
15971
16002
  };
15972
16003
  };
15973
16004
  if (due(await readUpdateCheck(options.core.paths.stateDir), now()) === null) return null;
15974
- return (await changeUpdateCheck(options.core.paths.stateDir, (record) => {
16005
+ const decide = (record) => {
15975
16006
  const at = now();
15976
16007
  const decision = due(record, at);
15977
16008
  if (decision === null) return null;
@@ -15995,7 +16026,8 @@ async function decideNotice(options, where) {
15995
16026
  }
15996
16027
  }
15997
16028
  };
15998
- })).result ?? null;
16029
+ };
16030
+ return (await changeUpdateCheck(options.core.paths.stateDir, decide, UPDATE_NOTICE_LOCK)).result ?? null;
15999
16031
  }
16000
16032
  /**
16001
16033
  * What a background run left, when it is this process's to say: an install only to a process running a release before
@@ -16228,7 +16260,7 @@ function commandDecisionLine(core, decision, channel, platform) {
16228
16260
  }
16229
16261
  channelServer("core").packageName;
16230
16262
  //#endregion
16231
- //#region ../core/dist/tool-arguments-BW3WUyxT.mjs
16263
+ //#region ../core/dist/tool-arguments-CJ4RQpx4.mjs
16232
16264
  const APPLIED = Symbol.for("agentcomms.strictToolArguments");
16233
16265
  const TARGET = "draft-2020-12";
16234
16266
  /**
@@ -16664,8 +16696,6 @@ new Set([
16664
16696
  "cseuckr csksc56011987 euc-kr iso-ir-149 korean ks_c_5601-1987 ks_c_5601-1989 ksc5601 ksc_5601",
16665
16697
  "windows-949",
16666
16698
  "csiso2022kr hz-gb-2312 iso-2022-cn iso-2022-cn-ext iso-2022-kr replacement",
16667
- "unicodefffe utf-16be",
16668
- "csunicode iso-10646-ucs-2 ucs-2 unicode unicodefeff utf-16 utf-16le",
16669
16699
  "x-user-defined"
16670
16700
  ].join(" ").split(" "));
16671
16701
  new TextDecoder();
@@ -16673,6 +16703,10 @@ const ENCODED_WORD_PATTERN = "=\\?([^?\\s]+)\\?([QqBb])\\?([^?]*)\\?=";
16673
16703
  new RegExp(ENCODED_WORD_PATTERN, "g");
16674
16704
  new RegExp(`^(?:${ENCODED_WORD_PATTERN}\\s*)+$`);
16675
16705
  new TextDecoder("utf-8", { ignoreBOM: true });
16706
+ //#endregion
16707
+ //#region ../../node_modules/.pnpm/postal-mime@4.1.4/node_modules/postal-mime/dist/esm/text-format.js
16708
+ const hexByte = (value) => value.toString(16).toUpperCase().padStart(2, "0");
16709
+ Array.from({ length: 256 }, (_, byte) => "%" + hexByte(byte));
16676
16710
  new Uint8Array([10]);
16677
16711
  //#endregion
16678
16712
  //#region ../../node_modules/.pnpm/deepmerge-ts@8.0.2/node_modules/deepmerge-ts/dist/index.mjs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcomms/whatsapp",
3
- "version": "0.25.0",
3
+ "version": "0.26.1",
4
4
  "description": "WhatsApp for coding agents, read-only: list, read and search the chats WhatsApp for Mac keeps on this Mac, and draft replies as links the person sends. No network client; it never sends.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,13 +34,13 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@agentcomms/core": "0.25.0"
37
+ "@agentcomms/core": "0.26.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@modelcontextprotocol/client": "2.3.1",
41
41
  "@modelcontextprotocol/server": "^2.3.1",
42
42
  "commander": "^15.0.0",
43
- "zod": "^4.6.5"
43
+ "zod": "^4.6.7"
44
44
  },
45
45
  "agentcomms": {
46
46
  "contract": 1,
@@ -98,5 +98,5 @@
98
98
  "typecheck": "tsc -p tsconfig.json",
99
99
  "test": "node --experimental-strip-types --disable-warning=ExperimentalWarning --test --test-timeout=600000 \"test/**/*.test.ts\""
100
100
  },
101
- "gitHead": "1aca79f8c2674add5086fe4f05a52c8fb2719657"
101
+ "gitHead": "70247de711a9ae1aac5bdaea40acfc3e474817f7"
102
102
  }