@agentcomms/events-daemon 0.27.1 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -123,6 +123,12 @@ approval preview then shows exactly what changes.
123
123
  - `doctor`, a content-free health report.
124
124
 
125
125
  Every command takes `--json`. The [CLI reference](../../docs/reference/events-daemon-cli.md) lists them all.
126
+ - **What it delivered:** from 0.29.0, what each delivery came to (delivered, failed, or dropped before it was
127
+ delivered) is a line in agent-communications' private activity log, under the event service's own session. A line
128
+ names the rule, the account the events came from, how many events, the target and the outcome. It never holds the
129
+ event, its sender or its subject, nor a webhook's URL. `agentcomms activity sessions` lists the session, and
130
+ `agentcomms activity record off` stops the recording
131
+ ([activity](../../README.md#what-your-agents-did-activity)).
126
132
 
127
133
  ## Agents and approvals
128
134
 
@@ -852,7 +852,7 @@ async function removeTarget(targetId, options = {}) {
852
852
  //#endregion
853
853
  //#region src/version.ts
854
854
  /** The package version, read from package.json at build time. */
855
- const VERSION = "0.27.1";
855
+ const VERSION = "0.29.0";
856
856
  //#endregion
857
857
  //#region src/mcp/server.ts
858
858
  function reply(data) {
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as addSubscriber, $t as isControlHello, A as normaliseSourceOptions, At as updateJudge, B as canonicalJudgeKindEnablement, Bt as deliveriesList, C as normaliseActivationDocument, Ct as judgeKindsList, D as normaliseGmailSourceOptions, Dt as removeJudge, E as classifySourceOptionChange, Et as judgesList, F as assertCreatableLayaJudge, Ft as doctor, G as registerLayaEnablementInputs, Gt as catalogueShow, H as layaEnablementInputsFor, Ht as heldList, I as assertLayaJudgeKindAvailable, It as ACTIVATION_FAILURE, J as addTarget, Jt as probeControl, K as createEventsMcpServer, Kt as approvalView, L as assertLayaRuleJudgeOffered, Lt as isActivationFailureCode, M as APPROVED_TYPESAFE_MODELS, Mt as assertDoctorReportV1, N as JUDGE_BUDGET_ID, Nt as assertDoctorReportV2, O as normaliseResendSourceOptions, Ot as removeJudgeModel, P as JUDGE_KIND_DEFINITIONS, Pt as assertDoctorReportV3, Q as targetTest, Qt as encodeControlFrame, R as canonicalJudge, Rt as disableAll, S as disclosureBindingFor, St as enableJudgeKind, T as sseDeliveryTarget, Tt as judgeTest, U as layaJudgeKindDefinitionFor, Ut as retryDelivery, V as judgeKindDefinition, Vt as dropDelivery, W as layaJudgeTermsFor, Wt as catalogueList, X as targetsList, Xt as ControlFrameReader, Y as removeTarget$1, Yt as CONTROL_PROTOCOL_VERSIONS, Z as updateTarget, Zt as controlFailure, _ as isOwnerControlWireOperation, _t as pause, an as readInstance, at as loadSqlite, b as canonicalSubscriber, bt as budgetShow, cn as assertControlSupported, ct as createRule$1, d as B3_SECRET_TERMINAL_ORIGIN, dn as verifyControlSocket, dt as removeRule, en as isControlRequest, et as removeSubscriber, f as isB3ControlOperation, fn as verifyPrivateSocketDirectory, ft as ruleShow$1, g as OWNER_CONTROL_WIRES, gn as eventPaths, gt as ruleTestRetained, h as layaJudgePreviewFor, hn as ensureOwnerOnlyFile, ht as ruleTest, in as processIsLive, it as status, j as normaliseWhatsAppSourceOptions, jt as enableAll, k as normaliseSlackSourceOptions, kt as updateBudget, ln as assertSocketPathFits, lt as disableRule$1, m as renderB3ApprovalPreviewV1, mn as ensureEventSocketDirectory, mt as updateRule, nn as mayRecoverStaleInstance, nt as updateSubscriber, on as removeInstance, ot as sourceShow$1, p as isB3SecretOperation, pn as ensureEventPaths, pt as rulesList, q as VERSION, qt as EventControlClient, rn as newInstanceRecord, rt as stop, sn as writeInstance, st as sourcesList, tn as negotiateVersion, tt as subscribersList, un as controlEndpoint, ut as enableRule, v as activationDocumentDigest, vt as resume, w as canonicalSseSubscriber, wt as judgeModelStatus, x as canonicalTarget, xt as disableJudgeKind, y as canonicalFullRuleDocument, yt as addJudge, z as canonicalJudgeBudget, zt as decideHeld } from "./b3-contract-D1Zy-xLG.mjs";
2
+ import { $ as addSubscriber, $t as isControlHello, A as normaliseSourceOptions, At as updateJudge, B as canonicalJudgeKindEnablement, Bt as deliveriesList, C as normaliseActivationDocument, Ct as judgeKindsList, D as normaliseGmailSourceOptions, Dt as removeJudge, E as classifySourceOptionChange, Et as judgesList, F as assertCreatableLayaJudge, Ft as doctor, G as registerLayaEnablementInputs, Gt as catalogueShow, H as layaEnablementInputsFor, Ht as heldList, I as assertLayaJudgeKindAvailable, It as ACTIVATION_FAILURE, J as addTarget, Jt as probeControl, K as createEventsMcpServer, Kt as approvalView, L as assertLayaRuleJudgeOffered, Lt as isActivationFailureCode, M as APPROVED_TYPESAFE_MODELS, Mt as assertDoctorReportV1, N as JUDGE_BUDGET_ID, Nt as assertDoctorReportV2, O as normaliseResendSourceOptions, Ot as removeJudgeModel, P as JUDGE_KIND_DEFINITIONS, Pt as assertDoctorReportV3, Q as targetTest, Qt as encodeControlFrame, R as canonicalJudge, Rt as disableAll, S as disclosureBindingFor, St as enableJudgeKind, T as sseDeliveryTarget, Tt as judgeTest, U as layaJudgeKindDefinitionFor, Ut as retryDelivery, V as judgeKindDefinition, Vt as dropDelivery, W as layaJudgeTermsFor, Wt as catalogueList, X as targetsList, Xt as ControlFrameReader, Y as removeTarget$1, Yt as CONTROL_PROTOCOL_VERSIONS, Z as updateTarget, Zt as controlFailure, _ as isOwnerControlWireOperation, _t as pause, an as readInstance, at as loadSqlite, b as canonicalSubscriber, bt as budgetShow, cn as assertControlSupported, ct as createRule$1, d as B3_SECRET_TERMINAL_ORIGIN, dn as verifyControlSocket, dt as removeRule, en as isControlRequest, et as removeSubscriber, f as isB3ControlOperation, fn as verifyPrivateSocketDirectory, ft as ruleShow$1, g as OWNER_CONTROL_WIRES, gn as eventPaths, gt as ruleTestRetained, h as layaJudgePreviewFor, hn as ensureOwnerOnlyFile, ht as ruleTest, in as processIsLive, it as status, j as normaliseWhatsAppSourceOptions, jt as enableAll, k as normaliseSlackSourceOptions, kt as updateBudget, ln as assertSocketPathFits, lt as disableRule$1, m as renderB3ApprovalPreviewV1, mn as ensureEventSocketDirectory, mt as updateRule, nn as mayRecoverStaleInstance, nt as updateSubscriber, on as removeInstance, ot as sourceShow$1, p as isB3SecretOperation, pn as ensureEventPaths, pt as rulesList, q as VERSION, qt as EventControlClient, rn as newInstanceRecord, rt as stop, sn as writeInstance, st as sourcesList, tn as negotiateVersion, tt as subscribersList, un as controlEndpoint, ut as enableRule, v as activationDocumentDigest, vt as resume, w as canonicalSseSubscriber, wt as judgeModelStatus, x as canonicalTarget, xt as disableJudgeKind, y as canonicalFullRuleDocument, yt as addJudge, z as canonicalJudgeBudget, zt as decideHeld } from "./b3-contract-BmdhGHkq.mjs";
3
3
  import { a as createLayaWorkerBoundary, d as encodeWorkerFrame, h as isLayaTemplateId, l as WorkerFrameParser, u as createWorkerRequestId, v as installErrorSink } from "./worker-boundary-probe-CFN6EsyK.mjs";
4
4
  import { _ as JUDGE_KIND_TYPESAFE, c as validateLocalEndpoint, d as assertApprovedAddressSet, f as parseIpLiteral, h as JUDGE_KIND_LAYA, i as webhookDeliveryTarget, l as AddressPolicyError, m as JUDGE_KINDS, n as canonicalWebhookTarget, p as EventDomainError, r as secretWebhookUrlDescriptor, t as canonicalSecretWebhookUrl, u as assertAddressAllowed, v as assertJudgeKindEnabled, y as isJudgeKind } from "./webhook-target-EzLIFpIY.mjs";
5
5
  import { c as migrateEventSecrets, i as RecordStorageError, l as openEventSecretStore, n as LayaModelRepository, o as WriterQuiescer, s as withEventWriterLease, t as EventRecordCipher } from "./records-BRl5wEbC.mjs";
@@ -7,7 +7,7 @@ import process$1 from "node:process";
7
7
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
8
8
  import { appendFile, chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, rename, rm, statfs, writeFile } from "node:fs/promises";
9
9
  import net, { createServer } from "node:net";
10
- import { CommsError, TaintCollector, agentMarker, canPrompt, canonicalJson, escapeForDisplay, hashChallenge, isCommsError, isDangerous, neutralise, newBoundary, openCore, renderFencedBody, resolvePaths, sanitizeHtmlToText, sanitizePlainText, sha256Hex, stripInvisible, wrapUntrusted } from "@agentcomms/core";
10
+ import { CommsError, TaintCollector, agentMarker, canPrompt, canonicalJson, escapeForDisplay, hashChallenge, isCommsError, isDangerous, neutralise, newBoundary, openCore, recordActivity, renderFencedBody, resolvePaths, sanitizeHtmlToText, sanitizePlainText, sessionIdentity, sha256Hex, stripInvisible, wrapUntrusted } from "@agentcomms/core";
11
11
  import fs, { constants, createReadStream } from "node:fs";
12
12
  import path, { basename, dirname, join, resolve, sep } from "node:path";
13
13
  import { CATALOGUE, TEST_CLOUD_EVENT, TEST_CLOUD_EVENT_BYTES, applyRepresentation, buildCloudEvent, canonicalJson as canonicalJson$1, catalogueEntry, classifyMapped, cloudEventBytes, compileMapping, conditionPointers, evaluateCondition, evaluateMapping, eventId, eventIdPreimage, expandPattern, getPointer, gmailMessageLabelledV1, gmailMessageReceivedV1, gmailMessageSentV1, parsePointer, validateEvent, whatsappMessageKey } from "@agentcomms/events";
@@ -14893,6 +14893,189 @@ var TargetResumeControl = class {
14893
14893
  }
14894
14894
  };
14895
14895
  //#endregion
14896
+ //#region src/runtime/delivery-activity.ts
14897
+ /**
14898
+ * The event service's own "told" lines in agent-communications' activity log (activity design S1, §8 Task 2c): one
14899
+ * line for what each delivery came to — delivered, failed, or dropped — naming the rule, the account its events came
14900
+ * from, how many events, the target, and the outcome.
14901
+ *
14902
+ * - **Never content.** A line is built only from a delivery's rule id, account id, target id and closed terminal
14903
+ * state. No payload, CloudEvent, sender, subject, event id or provider error text is read, so none can be written
14904
+ * (events design D7, D8 and Appendix A keep every payload out of every log). A target is named by its id, never its
14905
+ * URL or a secret.
14906
+ * - **After the outcome is durable.** The outcome is read back from the committed `deliveries` row; every delivery
14907
+ * transaction is synchronous, so no read here sees one half-done, and a pass inside an open transaction waits for
14908
+ * the next.
14909
+ * - **Once.** Each delivery is counted in exactly one line: the first terminal state it is seen in. A dead letter
14910
+ * whose payload is later purged, or dropped, is not counted again. Deliveries already finished when this process
14911
+ * started were counted by the process that finished them.
14912
+ * - **Grouped.** Outcomes seen in one pass with the same rule, account, target and outcome are one line, whose count
14913
+ * is how many events they were ("Delivered 3 events from david-on-slack to my-hook").
14914
+ * - **Best effort, as every activity line is (§4.6).** Recording never changes or delays a delivery, never throws, and
14915
+ * a line lost to a crash between a delivery's commit and the next pass is not written later: the event database
14916
+ * stays the record of what was delivered.
14917
+ */
14918
+ /** The activity log's operation for a delivery outcome. Not a capability: no CLI or MCP call makes it. */
14919
+ const DELIVERY_ACTIVITY_OPERATION = "events.delivery";
14920
+ /** The channel word a delivery outcome is recorded under: the event service's. */
14921
+ const DELIVERY_ACTIVITY_CHANNEL = "events";
14922
+ /** How often the owner records the outcomes reached since its last pass, by default. */
14923
+ const DELIVERY_ACTIVITY_INTERVAL_MS = 5e3;
14924
+ /** Deliveries that may still be delivered; every other state is an outcome. */
14925
+ const OPEN_STATES = "('queued', 'retryable', 'disclosing')";
14926
+ /**
14927
+ * Each terminal delivery state as an activity outcome:
14928
+ * - `ok`: delivered;
14929
+ * - `failed`: every attempt failed, its deadline passed first, or its stored copy could not be read;
14930
+ * - `refused`: dropped before it was delivered, because its rule, target, subscriber or account was removed, collection
14931
+ * was switched off, or its chat was hidden.
14932
+ */
14933
+ const OUTCOMES = Object.freeze({
14934
+ delivered: "ok",
14935
+ "dead-lettered": "failed",
14936
+ "retention-expired": "failed",
14937
+ "content-unreadable": "failed",
14938
+ cancelled: "refused",
14939
+ "in-flight-at-disable": "refused",
14940
+ "in-flight-at-account-removal": "refused"
14941
+ });
14942
+ var DeliveryActivity = class {
14943
+ #store;
14944
+ #core;
14945
+ #config;
14946
+ #sessionOf;
14947
+ #session;
14948
+ /** The highest delivery row seen; every row above it is new since the last pass. */
14949
+ #high;
14950
+ /** Rows seen while they could still be delivered. */
14951
+ #open = /* @__PURE__ */ new Set();
14952
+ #timer;
14953
+ #writing = Promise.resolve();
14954
+ #stopped = false;
14955
+ /** Set when the baseline could not be read: this process then records nothing. */
14956
+ #blind = false;
14957
+ /** Takes the baseline: what is already finished was counted by the process that finished it. */
14958
+ constructor(options) {
14959
+ this.#store = options.store;
14960
+ this.#core = options.core;
14961
+ this.#config = options.config;
14962
+ this.#sessionOf = options.session ?? (() => sessionIdentity("daemon"));
14963
+ try {
14964
+ const database = this.#store.database;
14965
+ this.#high = Number(database.prepare("SELECT coalesce(max(rowid), 0) AS high FROM deliveries").get().high);
14966
+ for (const row of database.prepare(`SELECT rowid AS seq FROM deliveries WHERE state IN ${OPEN_STATES}`).all()) this.#open.add(Number(row.seq));
14967
+ } catch {
14968
+ this.#high = 0;
14969
+ this.#blind = true;
14970
+ }
14971
+ }
14972
+ /** Records the outcomes reached since the last pass every `intervalMs`, until `stop`. */
14973
+ start(intervalMs = DELIVERY_ACTIVITY_INTERVAL_MS) {
14974
+ if (this.#timer !== void 0 || this.#stopped || this.#blind) return;
14975
+ this.#timer = setInterval(() => {
14976
+ this.record();
14977
+ }, intervalMs);
14978
+ this.#timer.unref?.();
14979
+ }
14980
+ /**
14981
+ * Records every outcome reached since the last pass, and resolves once its lines are written. Never throws. The
14982
+ * rows are read and marked counted at once, before anything awaits, so two passes never count one delivery twice.
14983
+ */
14984
+ record() {
14985
+ if (this.#blind) return this.#writing;
14986
+ let settled;
14987
+ try {
14988
+ settled = this.#settle();
14989
+ } catch {
14990
+ settled = [];
14991
+ }
14992
+ if (settled.length > 0) this.#writing = this.#writing.then(() => this.#write(settled));
14993
+ return this.#writing;
14994
+ }
14995
+ /** Stops the timer and records what the last work finished. Call it before the event database closes. */
14996
+ async stop() {
14997
+ if (this.#timer !== void 0) clearInterval(this.#timer);
14998
+ this.#timer = void 0;
14999
+ if (this.#stopped) return this.#writing;
15000
+ this.#stopped = true;
15001
+ return this.record();
15002
+ }
15003
+ #settle() {
15004
+ const database = this.#store.database;
15005
+ if (database.isTransaction) return [];
15006
+ const columns = "d.rowid AS seq, d.rule_id, d.account_id, d.target_id, d.state, d.dead_lettered_at";
15007
+ const watched = this.#open.size === 0 ? [] : database.prepare(`SELECT ${columns} FROM json_each(?) AS watching JOIN deliveries d ON d.rowid = watching.value`).all(JSON.stringify([...this.#open]));
15008
+ const fresh = database.prepare(`SELECT ${columns} FROM deliveries d WHERE d.rowid > ? ORDER BY d.rowid`).all(this.#high);
15009
+ const seen = /* @__PURE__ */ new Set();
15010
+ const settled = [];
15011
+ for (const row of [...watched, ...fresh]) {
15012
+ const seq = Number(row.seq);
15013
+ if (seen.has(seq)) continue;
15014
+ seen.add(seq);
15015
+ if (seq > this.#high) this.#high = seq;
15016
+ const outcome = OUTCOMES[row.state];
15017
+ if (outcome === void 0) {
15018
+ this.#open.add(seq);
15019
+ continue;
15020
+ }
15021
+ this.#open.delete(seq);
15022
+ settled.push({
15023
+ rule: row.rule_id,
15024
+ accountId: row.account_id,
15025
+ target: row.target_id,
15026
+ outcome,
15027
+ reason: outcome === "ok" ? void 0 : row.dead_lettered_at !== null ? "dead-lettered" : row.state
15028
+ });
15029
+ }
15030
+ for (const seq of this.#open) if (!seen.has(seq)) this.#open.delete(seq);
15031
+ return settled;
15032
+ }
15033
+ async #write(settled) {
15034
+ try {
15035
+ const groups = /* @__PURE__ */ new Map();
15036
+ for (const one of settled) {
15037
+ const key = JSON.stringify([
15038
+ one.rule,
15039
+ one.accountId,
15040
+ one.target,
15041
+ one.outcome,
15042
+ one.reason ?? null
15043
+ ]);
15044
+ const group = groups.get(key);
15045
+ if (group === void 0) groups.set(key, {
15046
+ ...one,
15047
+ count: 1
15048
+ });
15049
+ else group.count += 1;
15050
+ }
15051
+ const aliases = /* @__PURE__ */ new Map();
15052
+ try {
15053
+ for (const account of await connectedEventAccounts(this.#config)) aliases.set(account.accountId, account.account);
15054
+ } catch {}
15055
+ this.#session ??= this.#sessionOf();
15056
+ const session = await this.#session;
15057
+ for (const group of groups.values()) await recordActivity(this.#core, {
15058
+ channel: DELIVERY_ACTIVITY_CHANNEL,
15059
+ account: {
15060
+ id: group.accountId,
15061
+ alias: aliases.get(group.accountId) ?? ""
15062
+ },
15063
+ operation: DELIVERY_ACTIVITY_OPERATION,
15064
+ kind: "told",
15065
+ outcome: group.outcome,
15066
+ ...group.reason === void 0 ? {} : { reason: group.reason },
15067
+ session,
15068
+ ms: 0,
15069
+ count: group.count,
15070
+ told: {
15071
+ rule: group.rule,
15072
+ target: group.target
15073
+ }
15074
+ });
15075
+ } catch {}
15076
+ }
15077
+ };
15078
+ //#endregion
14896
15079
  //#region src/runtime/expiry.ts
14897
15080
  /** Runs every source-specific decrypted expiry before any adapter can inspect a retained source record. */
14898
15081
  var SourceStageExpiryGroup = class {
@@ -23727,6 +23910,11 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
23727
23910
  }),
23728
23911
  assertObjectSecretReady: b3Ports.assertObjectSecretReady
23729
23912
  });
23913
+ const deliveryActivity = new DeliveryActivity({
23914
+ store: database,
23915
+ core,
23916
+ config: core.config
23917
+ });
23730
23918
  const gmailSourceFor = async (accountId) => {
23731
23919
  const config = await core.config.load();
23732
23920
  const alias = Object.entries(config.inboxes).find(([, inbox]) => inbox.id === accountId && inbox.provider === "gmail")?.[0];
@@ -24166,6 +24354,7 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
24166
24354
  unregisterLayaWorker();
24167
24355
  unregisterLayaInputs();
24168
24356
  unregisterLayaPurge();
24357
+ await deliveryActivity.stop();
24169
24358
  database.close();
24170
24359
  await removeInstance(paths, instance.instanceId);
24171
24360
  await rm(paths.controlToken, { force: true });
@@ -24202,6 +24391,7 @@ async function startOwnerWithLock(options, stateDir, paths, lock, started, onDat
24202
24391
  }
24203
24392
  await writeInstance(paths, instance);
24204
24393
  scheduler.start();
24394
+ deliveryActivity.start(options.tickMs ?? 5e3);
24205
24395
  return owner;
24206
24396
  } catch (error) {
24207
24397
  await owner.stop();
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { t as B3_APPROVAL_VIEW_VARIANTS, n as B3_CAPABILITIES, r as B3_CONTROL_CONTRACT, i as B3_CONTROL_ERROR_CODES, a as B3_CONTROL_V1_OPERATION_SPECS, o as B3_CONTROL_V1_RESULT_OPERATIONS, s as B3_CONTROL_V1_SCHEMA_VERSION, c as B3_CONTROL_V1_SUCCESSOR_REQUIREMENT, l as B3_DESKTOP_CONTROL_OPERATIONS, u as B3_JUDGE_UNAVAILABLE_VARIANTS, d as B3_SECRET_TERMINAL_ORIGIN, K as createEventsMcpServer, m as renderB3ApprovalPreviewV1, it as status } from "./b3-contract-D1Zy-xLG.mjs";
1
+ export { t as B3_APPROVAL_VIEW_VARIANTS, n as B3_CAPABILITIES, r as B3_CONTROL_CONTRACT, i as B3_CONTROL_ERROR_CODES, a as B3_CONTROL_V1_OPERATION_SPECS, o as B3_CONTROL_V1_RESULT_OPERATIONS, s as B3_CONTROL_V1_SCHEMA_VERSION, c as B3_CONTROL_V1_SUCCESSOR_REQUIREMENT, l as B3_DESKTOP_CONTROL_OPERATIONS, u as B3_JUDGE_UNAVAILABLE_VARIANTS, d as B3_SECRET_TERMINAL_ORIGIN, K as createEventsMcpServer, m as renderB3ApprovalPreviewV1, it as status } from "./b3-contract-BmdhGHkq.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentcomms/events-daemon",
3
- "version": "0.27.1",
3
+ "version": "0.29.0",
4
4
  "description": "The local event service for agent-communications: rules you approve deliver Gmail, Slack, Resend and WhatsApp events to a signed webhook, a local live stream or a dry-run log.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,12 +37,12 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@agentcomms/core": "0.27.1",
41
- "@agentcomms/events": "0.27.1",
42
- "@agentcomms/gmail": "0.27.1",
43
- "@agentcomms/resend": "0.27.1",
44
- "@agentcomms/whatsapp": "0.27.1",
45
- "@agentcomms/slack": "0.27.1"
40
+ "@agentcomms/core": "0.29.0",
41
+ "@agentcomms/slack": "0.29.0",
42
+ "@agentcomms/whatsapp": "0.29.0",
43
+ "@agentcomms/resend": "0.29.0",
44
+ "@agentcomms/gmail": "0.29.0",
45
+ "@agentcomms/events": "0.29.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@modelcontextprotocol/client": "2.3.1",
@@ -73,5 +73,5 @@
73
73
  "typecheck": "tsc -p tsconfig.json",
74
74
  "test": "node --import ../../test/helpers/loopback-seal-preload.mjs --experimental-strip-types --disable-warning=ExperimentalWarning --test \"test/**/*.test.ts\" test/laya-runtime-package.test.mjs"
75
75
  },
76
- "gitHead": "a8f295c394ffaff3ca32c61865573240fba1473c"
76
+ "gitHead": "762fe2c8369643c09059cc6dc1f2a782ba835b46"
77
77
  }