@agentcomms/whatsapp 0.25.0 → 0.26.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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as gatedChangeAtTerminal, A as requireAccount, B as PATH_OPTIONS, Ct as CALLER, F as expandSource, G as approveChangeAtTerminal, H as absoluteSearchPath, J as cliHandoffs, K as canPrompt, L as CHANNEL_SERVERS, M as storeIdentity, N as checkStorePath, O as PLATFORM, P as defaultStorePath, Q as exemptFromUpdateNotice, R as CommsError, S as chatRefOf, St as writeResult, Tt as VERSION, U as agentMarker, W as approveAndWaitSentence, X as commandPathOf, Y as colorEnabled, Z as escapeForDisplay, _ as WhatsAppContext, _t as truncateDisplay, a as CHAT_KINDS, at as nameAvailable, b as requireSupportedNode, bt as windowsSystemProgram, c as searchMessages, ct as paint, dt as renderPrune, et as handoffSentence, f as draftMessage, ft as resolvePaths, ht as serverPruneChange, i as probeStore, it as lookupName, j as requireNamedConfig, k as newWhatsAppAccount, l as Presenter, lt as pathOverridesFromCliOptions, m as listKey, mt as serverInstallChange, nt as installExitStatus, o as listChats, ot as nameShapeProblem, p as Visibility, pt as runCommand, q as childEnvironment, r as whatsappStatus, rt as isCommsError, s as readChat, st as openCore, t as syncAccount, tt as handoffText, u as innerText, ut as renderInstall, v as WhatsAppIndex, vt as updateNoticeAtTerminal, xt as withFileLock, z as EXIT_CODES } from "./sync-DEG2YdIm.mjs";
2
+ import { $ as gatedChangeAtTerminal, A as requireAccount, B as PATH_OPTIONS, Ct as CALLER, F as expandSource, G as approveChangeAtTerminal, H as absoluteSearchPath, J as cliHandoffs, K as canPrompt, L as CHANNEL_SERVERS, M as storeIdentity, N as checkStorePath, O as PLATFORM, P as defaultStorePath, Q as exemptFromUpdateNotice, R as CommsError, S as chatRefOf, St as writeResult, Tt as VERSION, U as agentMarker, W as approveAndWaitSentence, X as commandPathOf, Y as colorEnabled, Z as escapeForDisplay, _ as WhatsAppContext, _t as truncateDisplay, a as CHAT_KINDS, at as nameAvailable, b as requireSupportedNode, bt as windowsSystemProgram, c as searchMessages, ct as paint, dt as renderPrune, et as handoffSentence, f as draftMessage, ft as resolvePaths, ht as serverPruneChange, i as probeStore, it as lookupName, j as requireNamedConfig, k as newWhatsAppAccount, l as Presenter, lt as pathOverridesFromCliOptions, m as listKey, mt as serverInstallChange, nt as installExitStatus, o as listChats, ot as nameShapeProblem, p as Visibility, pt as runCommand, q as childEnvironment, r as whatsappStatus, rt as isCommsError, s as readChat, st as openCore, t as syncAccount, tt as handoffText, u as innerText, ut as renderInstall, v as WhatsAppIndex, vt as updateNoticeAtTerminal, xt as withFileLock, z as EXIT_CODES } from "./sync-BwJSBi-M.mjs";
3
3
  import { rm } from "node:fs/promises";
4
4
  import path, { delimiter, join } from "node:path";
5
5
  import { stripVTControlCharacters } from "node:util";
@@ -3569,7 +3569,7 @@ a macOS dialog may be waiting) · 77 permission needed (macOS privacy) · 78 con
3569
3569
  ran = true;
3570
3570
  exitCode = await runCommand(output(), async () => {
3571
3571
  requireSupportedNode();
3572
- await (deps.startMcp ?? (await import("./stdio-entry-CYFb_dWj.mjs")).startWhatsAppStdioServer)({
3572
+ await (deps.startMcp ?? (await import("./stdio-entry-DQ1RgYGk.mjs")).startWhatsAppStdioServer)({
3573
3573
  env,
3574
3574
  platform,
3575
3575
  ...deps,
package/dist/index.d.mts CHANGED
@@ -4277,7 +4277,7 @@ declare namespace external_d_exports {
4277
4277
  export { $brand, $input, $output, BRAND, CompileOptions, GlobalMeta, INVALID, Infer$1 as Infer, IssueData, JSONType, NEVER, $RefinementCtx as RefinementCtx, SafeExtendShape, Schema, TimePrecision, TypeOf, ZodAny, ZodArray, ZodBase64, ZodBase64URL, ZodBigInt, ZodBigIntFormat, ZodBoolean, ZodCIDRv4, ZodCIDRv6, ZodCUID, ZodCUID2, ZodCatch, ZodCodec, ZodCoercedBigInt, ZodCoercedBoolean, ZodCoercedDate, ZodCoercedNumber, ZodCoercedString, ZodCompileAsyncError, ZodCompileUnsupportedError, ZodCreditCard, ZodCustom, ZodCustomStringFormat, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodE164, ZodEmail, ZodEmoji, ZodEnum, ZodError, ZodErrorMap, ZodExactOptional, ZodFile, ZodFirstPartySchemaTypes, ZodFirstPartyTypeKind, ZodFlattenedError, ZodFloat32, ZodFloat64, ZodFormattedError, ZodFunction, ZodGUID, ZodIBAN, ZodIPv4, ZodIPv6, ZodISODate, ZodISODateTime, ZodISODuration, ZodISOTime, ZodInstanceOf, ZodInt, ZodInt32, ZodInt64, ZodIntersection, ZodIssue, ZodIssueCode, ZodJSONSchema, ZodJSONSchemaInternals, ZodJWT, ZodKSUID, ZodLazy, ZodLiteral, ZodMAC, ZodMap, ZodNaN, ZodNanoID, ZodNever, ZodNonOptional, ZodNull, ZodNullable, ZodNumber, ZodNumberFormat, ZodObject, ZodOptional, ZodPipe, ZodPrefault, ZodPreprocess, ZodPromise, ZodRawShape$1 as ZodRawShape, ZodReadonly, ZodRealError, ZodRecord, ZodSafeParseError, ZodSafeParseResult, ZodSafeParseSuccess, ZodSchema, ZodSet, ZodStandardSchemaWithJSON, ZodString, ZodStringFormat, ZodSuccess, ZodSymbol, ZodTemplateLiteral, ZodTransform, ZodTuple, ZodType, ZodTypeAny, ZodUInt32, ZodUInt64, ZodULID, ZodURL, ZodUUID, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, ZodXID, ZodXor, _ZodBigInt, _ZodBoolean, _ZodDate, _ZodNumber, _ZodString, _ZodType, _default, _function, any, array, base64, base64url, bigint$1 as bigint, boolean$1 as boolean, _catch as catch, check, cidrv4, cidrv6, clone, codec, coerce_d_exports as coerce, compile, config, index_d_exports$1 as core, creditCard, cuid, cuid2, currencyCode, custom, date$2 as date, decode, decodeAsync, deepPartial, describe, discriminatedUnion, e164, email, emoji, encode, encodeAsync, _endsWith as endsWith, _enum as enum, exactOptional, file, flattenError, float32, float64, formatError, fromJSONSchema, _function as function, getDiscriminatedOption, getErrorMap, globalRegistry, _gt as gt, _gte as gte, guid, hash, hex, hostname, httpUrl, iban, _includes as includes, output$1 as infer, inferFlattenedErrors, inferFormattedError, input, _instanceof as instanceof, int, int32, int64, intersection, invertCodec, ipv4, ipv6, iso_d_exports as iso, json, jwt, keyof, ksuid, lazy, _length as length, literal, index_d_exports$2 as locales, looseObject, looseRecord, _lowercase as lowercase, _lt as lt, _lte as lte, mac, map, _maxLength as maxLength, _maxSize as maxSize, memoizer, meta, _mime as mime, _minLength as minLength, _minSize as minSize, _multipleOf as multipleOf, nan, nanoid, nativeEnum, _negative as negative, never, _nonnegative as nonnegative, nonoptional, _nonpositive as nonpositive, _normalize as normalize, _null as null, nullable, nullish, number$1 as number, object, optional, output, _overwrite as overwrite, parse, parseAsync, partialRecord, pipe, _positive as positive, prefault, preprocess, prettifyError, promise, _properties as properties, _property as property, readonly, record, refine, _regex as regex, regexes_d_exports as regexes, registry, safeDecode, safeDecodeAsync, safeEncode, safeEncodeAsync, safeParse, safeParseAsync, set, setErrorMap, _size as size, _slugify as slugify, _startsWith as startsWith, strictObject, string$1 as string, stringFormat, stringbool, success, superRefine, symbol, templateLiteral, toJSONSchema, _toLowerCase as toLowerCase, _toUpperCase as toUpperCase, toZod, transform, treeifyError, _trim as trim, tuple, uint32, uint64, ulid, _undefined as undefined, union, unknown, _uppercase as uppercase, url, util_d_exports as util, uuid, uuidv4, uuidv6, uuidv7, validate, validateAsync, _void as void, withParser, xid, xor };
4278
4278
  }
4279
4279
  //#endregion
4280
- //#region ../core/dist/launcher-B8pYW7eG.d.mts
4280
+ //#region ../core/dist/launcher-PMDWJUz9.d.mts
4281
4281
  interface RegisteredServer {
4282
4282
  client: string;
4283
4283
  path: string;
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { C as chatKindOf, D as text, E as numeric, R as CommsError, T as kindOf, Tt as VERSION, _ as WhatsAppContext, d as composeDraft, g as visibilityUnitKey, h as statusAuthor, m as listKey, n as withCheckedCopy, p as Visibility, w as coreDataToIso, wt as PACKAGE_NAME, x as inspectSchema, y as rebuildIndex } from "./sync-DEG2YdIm.mjs";
2
- import { t as createWhatsAppMcpServer } from "./server-DtDXnPRw.mjs";
1
+ import { C as chatKindOf, D as text, E as numeric, R as CommsError, T as kindOf, Tt as VERSION, _ as WhatsAppContext, d as composeDraft, g as visibilityUnitKey, h as statusAuthor, m as listKey, n as withCheckedCopy, p as Visibility, w as coreDataToIso, wt as PACKAGE_NAME, x as inspectSchema, y as rebuildIndex } from "./sync-BwJSBi-M.mjs";
2
+ import { t as createWhatsAppMcpServer } from "./server-ubl3VinK.mjs";
3
3
  //#region src/source/event-reader.ts
4
4
  function rawFromMe(value) {
5
5
  const number = numeric(value);
@@ -1,4 +1,4 @@
1
- import { I as strictToolArguments, Tt as VERSION, V as UNTRUSTED_NOTICE, _ as WhatsAppContext, a as CHAT_KINDS, c as searchMessages, f as draftMessage, gt as toCommsError, o as listChats, r as whatsappStatus, s as readChat, t as syncAccount, yt as updateToolNotice } from "./sync-DEG2YdIm.mjs";
1
+ import { I as strictToolArguments, Tt as VERSION, V as UNTRUSTED_NOTICE, _ as WhatsAppContext, a as CHAT_KINDS, c as searchMessages, f as draftMessage, gt as toCommsError, o as listChats, r as whatsappStatus, s as readChat, t as syncAccount, yt as updateToolNotice } from "./sync-BwJSBi-M.mjs";
2
2
  import { c as boolean, h as number, x as string } from "./iso-USuVU_sk.mjs";
3
3
  import { t as McpServer } from "./mcp-DIH4cS6P-CIWWZQbz.mjs";
4
4
  //#region src/mcp/server.ts
@@ -1,4 +1,4 @@
1
- import { t as createWhatsAppMcpServer } from "./server-DtDXnPRw.mjs";
1
+ import { t as createWhatsAppMcpServer } from "./server-ubl3VinK.mjs";
2
2
  //#region src/mcp/stdio-entry.ts
3
3
  /**
4
4
  * Starting the stdio server. On stdio, stdout is the protocol, so the console is redirected to stderr first — the
@@ -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.0";
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-C3j1VxDg.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
@@ -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.0";
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.0"
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-C8Oy4uOx.mjs
16232
16264
  const APPLIED = Symbol.for("agentcomms.strictToolArguments");
16233
16265
  const TARGET = "draft-2020-12";
16234
16266
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcomms/whatsapp",
3
- "version": "0.25.0",
3
+ "version": "0.26.0",
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,7 +34,7 @@
34
34
  "access": "public"
35
35
  },
36
36
  "dependencies": {
37
- "@agentcomms/core": "0.25.0"
37
+ "@agentcomms/core": "0.26.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@modelcontextprotocol/client": "2.3.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": "4a096cb13ea1444f1301137f9f91d07b8401aaa2"
102
102
  }