@ouro.bot/cli 0.1.0-alpha.847 → 0.1.0-alpha.848

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/changelog.json CHANGED
@@ -1,6 +1,17 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.848",
6
+ "changes": [
7
+ "Delegated commands: an A2A message whose signed envelope carries onBehalfOf \"principal\" (ouro a2a message --delegated) runs with the owner's sanctuary-owner authority only when the sender's friend record holds an operator-set delegationGrant and the sender is active family. The server posts a notice in the owner's Telegram chat first and refuses the command if that fails (JSON-RPC -32004). steward_policy_manage accepts the admitted command and records the source as \"<owner> via <peer> (delegated)\". Host execute stays Telegram-only.",
8
+ "Telegram: sendOwnerNotice / sendTelegramOwnerNotice post an idempotent Butler notice to the owner's chat and record it in the owner session.",
9
+ "Depend on @ouro.bot/friends 0.1.0-alpha.14.",
10
+ "Delegated commands: an A2A message whose signed envelope carries onBehalfOf \"principal\" (ouro a2a message --delegated) runs with the owner's sanctuary-owner authority only when the sender's friend record holds an operator-set delegationGrant and the sender is active family. Each message gets a fresh command id. The server posts a notice in the owner's Telegram chat first (30 s timeout) and refuses the command if that fails (JSON-RPC -32004). steward_policy_manage accepts the admitted command, records the source as \"<owner> via <peer> (delegated)\", and stamps delegatedVia (peer friend id, DID, notice id) on every audit row. Host execute stays Telegram-only.",
11
+ "A2A: a peer that types the delegated-command banner itself gets it marked as unverified in its turn.",
12
+ "Release gates: docs/audit-exceptions.json accepts a reviewed, dated exception per advisory URL (first entry: node-forge GHSA-86w9-cpqp-85rv via @bitwarden/cli, which has no patched release, due 2026-11-15); multer is overridden to 2.4.0; the mailroom raw-orphan debt is re-dated to 2026-10-31 and tracked as its own task."
13
+ ]
14
+ },
4
15
  {
5
16
  "version": "0.1.0-alpha.847",
6
17
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.847",
2
+ "runtimeVersion": "0.1.0-alpha.848",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>ouro-butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.847</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.848</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -213,6 +213,7 @@ async function sendSealedA2AChat(input) {
213
213
  recipientEd25519Pub: parsed.ed25519Pub,
214
214
  text: input.text,
215
215
  conversationId,
216
+ ...(input.onBehalfOf ? { onBehalfOf: input.onBehalfOf } : {}),
216
217
  });
217
218
  const request = {
218
219
  jsonrpc: "2.0",
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DELEGATED_BANNER = void 0;
4
+ exports.delegatedCommandNotice = delegatedCommandNotice;
5
+ exports.admitDelegatedCommand = admitDelegatedCommand;
6
+ const runtime_1 = require("../nerves/runtime");
7
+ const relationship_authorization_1 = require("../repertoire/relationship-authorization");
8
+ const NOTICE_EXCERPT_CHARS = 200;
9
+ /** Matches the banner only the server may put in front of an admitted command. */
10
+ exports.DELEGATED_BANNER = /^\s*\[\s*delegated command/iu;
11
+ function delegatedCommandNotice(input) {
12
+ const flat = input.text.replace(/\s+/gu, " ").trim();
13
+ const excerpt = flat.length > NOTICE_EXCERPT_CHARS ? `${flat.slice(0, NOTICE_EXCERPT_CHARS)}…` : flat;
14
+ return `Delegated command from you via ${input.delegateName}: "${excerpt}". If this wasn't you, say so here: it is on record, and the grant can be revoked.`;
15
+ }
16
+ function hasPrincipalGrant(friend) {
17
+ return friend.delegationGrant?.scope === "principal_commands";
18
+ }
19
+ async function resolvePrincipal(store, profileId) {
20
+ const friends = await store.listAll?.() ?? [];
21
+ const matches = friends.filter((friend) => friend.capabilityProfileId === profileId
22
+ && friend.trustLevel === "family" && friend.admissionState === "active");
23
+ return matches.length === 1 ? matches[0] : null;
24
+ }
25
+ async function admitDelegatedCommand(input) {
26
+ const refuse = (reason) => {
27
+ (0, runtime_1.emitNervesEvent)({
28
+ level: "warn",
29
+ component: "senses",
30
+ event: "senses.a2a_delegated_command_refused",
31
+ message: "refused a delegated A2A command",
32
+ meta: { reason, delegateFriendId: input.friend.id, commandId: input.commandId },
33
+ });
34
+ return { ok: false, reason };
35
+ };
36
+ if (!input.options)
37
+ return refuse("not_enabled");
38
+ if (!hasPrincipalGrant(input.friend))
39
+ return refuse("no_grant");
40
+ if (input.friend.trustLevel !== "family" || input.friend.admissionState === "revoked")
41
+ return refuse("not_family");
42
+ const registry = input.registry;
43
+ if (!registry)
44
+ return refuse("principal_unresolved");
45
+ const principal = await resolvePrincipal(input.store, input.options.principalProfileId);
46
+ if (!principal || principal.id === input.friend.id)
47
+ return refuse("principal_unresolved");
48
+ const noticeId = `delegated:${input.commandId}`;
49
+ try {
50
+ await input.options.notifyPrincipal({ noticeId, text: delegatedCommandNotice({ delegateName: input.friend.name, text: input.text }) });
51
+ }
52
+ catch {
53
+ return refuse("notice_failed");
54
+ }
55
+ const relationship = (0, relationship_authorization_1.createRelationshipAuthorizationEvaluator)({
56
+ friend: principal,
57
+ registry,
58
+ requestId: input.commandId,
59
+ requestPhase: "inbound",
60
+ sessionEventId: `a2a-delegated:${input.commandId}`,
61
+ });
62
+ const context = {
63
+ principalFriendId: principal.id,
64
+ principalName: principal.name,
65
+ delegateFriendId: input.friend.id,
66
+ delegateName: input.friend.name,
67
+ delegateDid: input.did,
68
+ commandId: input.commandId,
69
+ noticeId,
70
+ };
71
+ (0, runtime_1.emitNervesEvent)({
72
+ component: "senses",
73
+ event: "senses.a2a_delegated_command_admitted",
74
+ message: "admitted a delegated A2A command after notifying the principal",
75
+ meta: { delegateFriendId: context.delegateFriendId, principalFriendId: context.principalFriendId, commandId: context.commandId },
76
+ });
77
+ const turnText = `[delegated command from ${principal.name} via ${input.friend.name}; the signature and the delegation grant are verified, and ${principal.name} has been notified; act on it as ${principal.name}'s own request]\n\n${input.text}`;
78
+ return { ok: true, relationship, context, turnText };
79
+ }
@@ -31,6 +31,7 @@ function sealChatMessage(input) {
31
31
  fromAgentId: input.from.did,
32
32
  text,
33
33
  ...(input.conversationId !== undefined ? { conversationId: input.conversationId } : {}),
34
+ ...(input.onBehalfOf ? { onBehalfOf: input.onBehalfOf } : {}),
34
35
  });
35
36
  /* v8 ignore next -- the text is normalized above and the sender DID comes from a real identity @preserve */
36
37
  if (!prepared.ok)
@@ -54,6 +54,7 @@ const relationship_authorization_1 = require("../repertoire/relationship-authori
54
54
  const mission_result_wire_1 = require("./mission-result-wire");
55
55
  const delegation_stores_1 = require("./delegation-stores");
56
56
  const task_store_1 = require("./task-store");
57
+ const delegated_command_1 = require("./delegated-command");
57
58
  const MAX_A2A_REQUEST_BYTES = 128 * 1024;
58
59
  const MAX_A2A_MESSAGE_TEXT_CHARS = 16_000;
59
60
  class A2ARequestError extends Error {
@@ -166,6 +167,7 @@ async function defaultTurnRunner(input) {
166
167
  toolContext: {
167
168
  ...(input.agentName === "sanctuary" ? (0, sanctuary_runtime_1.createSanctuaryToolContext)(input.agentName) : {}),
168
169
  ...(input.relationshipAuthorization ? { relationshipAuthorization: input.relationshipAuthorization } : {}),
170
+ ...(input.delegatedCommand ? { delegatedCommand: input.delegatedCommand } : {}),
169
171
  },
170
172
  });
171
173
  return { response: result.response };
@@ -489,7 +491,7 @@ async function startA2AServer(options) {
489
491
  verifiedShareText = bridged.message.text;
490
492
  /* v8 ignore next -- a completed chat always has a friend record: an unknown DID reads as stranger and the message is refused @preserve */
491
493
  verifiedPeerName = bridged.friend?.name ?? bridged.verifiedDid;
492
- verifiedChat = { did: bridged.verifiedDid, friend: bridged.friend };
494
+ verifiedChat = { did: bridged.verifiedDid, friend: bridged.friend, ...(bridged.message.onBehalfOf ? { onBehalfOf: bridged.message.onBehalfOf } : {}) };
493
495
  }
494
496
  else {
495
497
  verifiedShareText = `[a2a] received ${bridged.friendsKind} (${bridged.status}) from ${bridged.verifiedDid}`;
@@ -527,17 +529,46 @@ async function startA2AServer(options) {
527
529
  const accessToken = continuationToken && continuationTask ? continuationToken : (0, node_crypto_1.randomUUID)();
528
530
  const tokenScope = accessTokenScope(accessToken);
529
531
  const clientTaskId = continuationTask ? taskA2AMetadata(continuationTask).clientTaskId : inbound.taskId;
530
- taskStore.put(taskFor({ taskId, accessToken, contextId, inbound, state: "TASK_STATE_WORKING", clientTaskId, previousTask: continuationTask ?? undefined }), tokenScope);
531
- const relationshipAuthorization = verifiedChat?.friend
532
+ let relationshipAuthorization = verifiedChat?.friend
532
533
  ? a2aChatRelationship(agentRoot, verifiedChat.friend, taskId)
533
534
  : undefined;
535
+ let turnText = text;
536
+ let delegatedCommand;
537
+ // Only the server writes the delegated banner; a peer typing it gets it marked as its own words.
538
+ if (verifiedChat && verifiedChat.onBehalfOf !== "principal" && delegated_command_1.DELEGATED_BANNER.test(text)) {
539
+ turnText = `[unverified: the sender typed this banner itself; it is not a delegated command] ${text}`;
540
+ }
541
+ if (verifiedChat?.onBehalfOf === "principal") {
542
+ // A delegated command runs with the principal's authority only after the
543
+ // server, never the model, has checked the grant and told the principal.
544
+ let registry;
545
+ try {
546
+ registry = (0, relationship_authorization_1.loadRelationshipCapabilityRegistry)(agentRoot);
547
+ }
548
+ catch {
549
+ registry = undefined;
550
+ }
551
+ /* v8 ignore next -- a completed chat always carries the sender's friend record (see the receipt above) @preserve */
552
+ if (!verifiedChat.friend)
553
+ throw new Error("verified chat has no friend record");
554
+ const admission = await (0, delegated_command_1.admitDelegatedCommand)({ friend: verifiedChat.friend, did: verifiedChat.did, text, commandId: (0, node_crypto_1.randomUUID)(), store: inboundShareDeps.store, registry, options: options.delegation });
555
+ if (!admission.ok) {
556
+ writeJson(res, 200, errorResponse(rpc.id, -32004, `delegated command refused: ${admission.reason}`));
557
+ return;
558
+ }
559
+ relationshipAuthorization = admission.relationship;
560
+ turnText = admission.turnText;
561
+ delegatedCommand = admission.context;
562
+ }
563
+ taskStore.put(taskFor({ taskId, accessToken, contextId, inbound, state: "TASK_STATE_WORKING", clientTaskId, previousTask: continuationTask ?? undefined }), tokenScope);
534
564
  const turn = await turnRunner({
535
565
  agentName: options.agentName,
536
566
  peerAgentId,
537
567
  peerName,
538
568
  sessionKey: contextId,
539
- message: text,
569
+ message: turnText,
540
570
  ...(relationshipAuthorization ? { relationshipAuthorization } : {}),
571
+ ...(delegatedCommand ? { delegatedCommand } : {}),
541
572
  });
542
573
  let responseParts;
543
574
  if (verifiedChat) {
@@ -6103,6 +6103,7 @@ async function executeA2AClientCommand(command, deps) {
6103
6103
  cardUrl: command.to,
6104
6104
  text: command.text,
6105
6105
  ...(command.conversationId ? { conversationId: command.conversationId } : {}),
6106
+ ...(command.delegated ? { onBehalfOf: "principal" } : {}),
6106
6107
  identity,
6107
6108
  /* v8 ignore next -- production CLI uses global fetch; tests inject fetch for a hermetic peer @preserve */
6108
6109
  ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),
@@ -1512,17 +1512,22 @@ function parseA2ACommand(args) {
1512
1512
  return { kind: "a2a.identity", ...(identityFile ? { identityFile } : {}), ...(json ? { json: true } : {}) };
1513
1513
  }
1514
1514
  if (sub === "message") {
1515
- const usageText = "Usage: ouro a2a message --to <card-url> --text <text> [--context <id>] [--identity-file <path>] [--json]";
1515
+ const usageText = "Usage: ouro a2a message --to <card-url> --text <text> [--context <id>] [--delegated] [--identity-file <path>] [--json]";
1516
1516
  let to;
1517
1517
  let text;
1518
1518
  let conversationId;
1519
1519
  let identityFile;
1520
1520
  let json = false;
1521
+ let delegated = false;
1521
1522
  for (let i = 0; i < rest.length; i += 1) {
1522
1523
  if (rest[i] === "--to" && rest[i + 1]) {
1523
1524
  to = rest[++i];
1524
1525
  continue;
1525
1526
  }
1527
+ if (rest[i] === "--delegated") {
1528
+ delegated = true;
1529
+ continue;
1530
+ }
1526
1531
  if (rest[i] === "--text" && rest[i + 1]) {
1527
1532
  text = rest[++i];
1528
1533
  continue;
@@ -1543,7 +1548,7 @@ function parseA2ACommand(args) {
1543
1548
  }
1544
1549
  if (!to || !text)
1545
1550
  throw new Error(usageText);
1546
- return { kind: "a2a.message", to, text, ...(conversationId ? { conversationId } : {}), ...(identityFile ? { identityFile } : {}), ...(json ? { json: true } : {}) };
1551
+ return { kind: "a2a.message", to, text, ...(conversationId ? { conversationId } : {}), ...(delegated ? { delegated: true } : {}), ...(identityFile ? { identityFile } : {}), ...(json ? { json: true } : {}) };
1547
1552
  }
1548
1553
  if (sub === "onboard") {
1549
1554
  let cardUrl;
@@ -77,6 +77,10 @@ function ownerAuthorization(value) {
77
77
  && value.profileId === "sanctuary-owner" && positiveInteger(value.profileVersion)
78
78
  && text(value.requestId) && text(value.sessionKey) && text(value.receiptId);
79
79
  }
80
+ function delegation(value) {
81
+ return record(value) && exactKeys(value, ["delegateFriendId", "delegateDid", "noticeId"])
82
+ && text(value.delegateFriendId) && text(value.delegateDid) && text(value.noticeId);
83
+ }
80
84
  function desiredEntry(value, version) {
81
85
  return record(value) && exactKeys(value, ["value", "provenance", "version", "source"], ["expiresAt"])
82
86
  && text(value.value) && text(value.source) && positiveInteger(value.version) && value.version <= version
@@ -126,9 +130,10 @@ function operationIdentity(issuer, event, requestId, kind, key) {
126
130
  return JSON.stringify([issuer, event, requestId, kind, key]);
127
131
  }
128
132
  function auditRow(value) {
129
- if (!record(value) || !exactKeys(value, ["schemaVersion", "transactionId", "precedingBytesSha256", "mutationKind", "key", "mutationFingerprint", "affectedKeyResult", "affectedKeyResultSha256", "issuer", "authorizingSessionEvent", "authorization", "preimage", "preimageVersion", "preimageSha256", "postimage", "postimageVersion", "postimageSha256", "at"])
133
+ if (!record(value) || !exactKeys(value, ["schemaVersion", "transactionId", "precedingBytesSha256", "mutationKind", "key", "mutationFingerprint", "affectedKeyResult", "affectedKeyResultSha256", "issuer", "authorizingSessionEvent", "authorization", "preimage", "preimageVersion", "preimageSha256", "postimage", "postimageVersion", "postimageSha256", "at"], ["delegatedVia"])
130
134
  || value.schemaVersion !== 2 || (value.mutationKind !== "set_desired_state" && value.mutationKind !== "grant_routine_action")
131
135
  || !text(value.key) || !text(value.issuer) || !text(value.authorizingSessionEvent) || !ownerAuthorization(value.authorization)
136
+ || (value.delegatedVia !== undefined && !delegation(value.delegatedVia))
132
137
  || typeof value.preimage !== "string" || typeof value.postimage !== "string" || !canonicalTime(value.at))
133
138
  return false;
134
139
  const before = policyImage(value.preimage);
@@ -315,6 +320,8 @@ function updateStewardPolicy(agentRoot, input) {
315
320
  const authorization = input.actor.authorization;
316
321
  if (!ownerAuthorization(authorization))
317
322
  throw new Error("steward policy mutation requires current owner authorization");
323
+ if (input.actor.delegatedVia !== undefined && !delegation(input.actor.delegatedVia))
324
+ throw new Error("steward policy delegation is invalid");
318
325
  if (input.mutation.kind !== "set_desired_state" && input.mutation.kind !== "grant_routine_action")
319
326
  throw new Error("steward policy mutation kind is invalid");
320
327
  return (0, session_transaction_1.withImmediateSessionTurnLease)(policyPath(agentRoot), (lease) => {
@@ -382,6 +389,7 @@ function updateStewardPolicy(agentRoot, input) {
382
389
  schemaVersion: 2, transactionId: sha256(JSON.stringify([identity, fingerprint])), precedingBytesSha256: sha256(snapshot.auditBytes),
383
390
  mutationKind: input.mutation.kind, key, mutationFingerprint: fingerprint, affectedKeyResult, affectedKeyResultSha256: sha256(JSON.stringify(affectedKeyResult)),
384
391
  issuer, authorizingSessionEvent, authorization: { ...authorization },
392
+ ...(input.actor.delegatedVia ? { delegatedVia: { ...input.actor.delegatedVia } } : {}),
385
393
  preimage: snapshot.bytes, preimageVersion: current.version, preimageSha256: snapshot.revision,
386
394
  postimage, postimageVersion: version, postimageSha256: sha256(postimage), at: now,
387
395
  };
@@ -55,11 +55,24 @@ exports.stewardPolicyToolDefinition = {
55
55
  const { friendId, trustLevel, sessionEventId } = actor;
56
56
  const { profileId, requestId } = relationship;
57
57
  const sessionKey = ctx.currentSession?.key;
58
+ const delegated = ctx.delegatedCommand;
59
+ // Two ways to hold owner authority: the owner's own Telegram turn, or a delegated
60
+ // command the A2A server admitted (grant checked, owner already notified).
61
+ const sessionMatches = () => delegated
62
+ ? ctx.currentSession?.channel === "a2a" && ctx.delegatedCommand === delegated
63
+ && friendId === delegated.principalFriendId && requestId === delegated.commandId
64
+ && sessionEventId === `a2a-delegated:${delegated.commandId}`
65
+ : ctx.currentSession?.friendId === friendId && ctx.currentSession.channel === "telegram";
58
66
  if (trustLevel !== "family" || profileId !== "sanctuary-owner" || typeof requestId !== "string" || !requestId.trim()
59
67
  || typeof sessionKey !== "string" || !sessionKey.trim() || !sessionEventId.trim()
60
- || ctx.currentSession?.friendId !== friendId || ctx.currentSession.channel !== "telegram" || ctx.currentExternalEvent) {
61
- throw new Error("steward policy mutation requires a current authenticated owner Telegram request and session");
68
+ || !sessionMatches() || ctx.currentExternalEvent) {
69
+ throw new Error(delegated
70
+ ? "steward policy mutation requires a current admitted delegated owner command"
71
+ : "steward policy mutation requires a current authenticated owner Telegram request and session");
62
72
  }
73
+ const sourceFor = (raw) => delegated
74
+ ? `${delegated.principalName} via ${delegated.delegateName} (delegated)${raw?.trim() ? `: ${raw.trim()}` : ""}`
75
+ : raw ?? "";
63
76
  const expectedVersion = args.expectedVersion === undefined ? undefined : Number(args.expectedVersion);
64
77
  if (expectedVersion !== undefined && (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0))
65
78
  throw new Error("expectedVersion must be a nonnegative integer");
@@ -67,7 +80,7 @@ exports.stewardPolicyToolDefinition = {
67
80
  if (args.action === "set_desired_state") {
68
81
  if (args.provenance !== "stated" && args.provenance !== "observed" && args.provenance !== "default")
69
82
  throw new Error("desired state provenance is invalid");
70
- mutation = { kind: "set_desired_state", key: args.key ?? "", value: args.value ?? "", provenance: args.provenance, source: args.source ?? "", ...(args.expiresAt ? { expiresAt: args.expiresAt } : {}) };
83
+ mutation = { kind: "set_desired_state", key: args.key ?? "", value: args.value ?? "", provenance: args.provenance, source: sourceFor(args.source), ...(args.expiresAt ? { expiresAt: args.expiresAt } : {}) };
71
84
  }
72
85
  else if (args.action === "grant_routine_action") {
73
86
  if (args.provenance !== "stated" && args.provenance !== "installed_explicit_policy")
@@ -101,12 +114,16 @@ exports.stewardPolicyToolDefinition = {
101
114
  }
102
115
  if (ctx.relationshipAuthorization !== relationship || relationship.profileId !== profileId || relationship.requestId !== requestId
103
116
  || relationship.actor?.friendId !== friendId || relationship.actor.trustLevel !== trustLevel || relationship.actor.sessionEventId !== sessionEventId
104
- || ctx.currentSession?.friendId !== friendId || ctx.currentSession.key !== sessionKey || ctx.currentSession.channel !== "telegram" || ctx.currentExternalEvent) {
117
+ || ctx.currentSession?.key !== sessionKey || !sessionMatches() || ctx.currentExternalEvent) {
105
118
  throw new Error("steward policy owner authorization or session changed before mutation");
106
119
  }
107
120
  const result = (0, steward_policy_1.updateStewardPolicy)(agentRoot, {
108
121
  expectedVersion: expectedVersion ?? (0, steward_policy_1.readStewardPolicy)(agentRoot).version,
109
- actor: { friendId, trustLevel, sessionEventId, authorization: { profileId, requestId, sessionKey, receiptId: authorization.receiptId, profileVersion: authorization.profileVersion } },
122
+ actor: {
123
+ friendId, trustLevel, sessionEventId,
124
+ authorization: { profileId, requestId, sessionKey, receiptId: authorization.receiptId, profileVersion: authorization.profileVersion },
125
+ ...(delegated ? { delegatedVia: { delegateFriendId: delegated.delegateFriendId, delegateDid: delegated.delegateDid, noticeId: delegated.noticeId } } : {}),
126
+ },
110
127
  mutation,
111
128
  });
112
129
  (0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.steward_policy_tool_call", message: "updated steward policy", meta: { action: args.action } });
@@ -95,6 +95,17 @@ Promise.all([
95
95
  await startA2AServer({
96
96
  agentName,
97
97
  identity,
98
+ // The Butler accepts delegated commands from explicitly granted peers; each
99
+ // one is announced in the owner's Telegram chat before it runs.
100
+ ...(agentName === "sanctuary" ? {
101
+ delegation: {
102
+ principalProfileId: "sanctuary-owner",
103
+ notifyPrincipal: async (notice) => {
104
+ const { sendTelegramOwnerNotice } = await Promise.resolve().then(() => __importStar(require("./telegram")));
105
+ await sendTelegramOwnerNotice(agentName, { ...notice, signal: AbortSignal.timeout(30_000) });
106
+ },
107
+ },
108
+ } : {}),
98
109
  ...(argValue("--host") ? { host: argValue("--host") } : {}),
99
110
  ...(Number.isInteger(port) ? { port } : {}),
100
111
  ...(argValue("--base-url") ? { baseUrl: argValue("--base-url") } : {}),
@@ -51,6 +51,7 @@ exports.loadTelegramSenseCredentials = loadTelegramSenseCredentials;
51
51
  exports.createProductionTelegramRelationshipComposition = createProductionTelegramRelationshipComposition;
52
52
  exports.startTelegramSenseApp = startTelegramSenseApp;
53
53
  exports.sendTelegramExternalEventDecision = sendTelegramExternalEventDecision;
54
+ exports.sendTelegramOwnerNotice = sendTelegramOwnerNotice;
54
55
  exports.createProactiveRepetitionGuard = createProactiveRepetitionGuard;
55
56
  exports.withTelegramTypingIndicator = withTelegramTypingIndicator;
56
57
  exports.sendTelegramAwaitFollowUp = sendTelegramAwaitFollowUp;
@@ -1917,6 +1918,16 @@ function createTelegramSenseApp(options) {
1917
1918
  async sendAwaitFollowUp(request) {
1918
1919
  return runWithAcceptanceAuditOwner(() => deliverAwaitFollowUp(request));
1919
1920
  },
1921
+ async sendOwnerNotice(input) {
1922
+ const noticeId = requiredText(input.noticeId, "owner notice id");
1923
+ if (Buffer.byteLength(noticeId) > 512)
1924
+ throw new Error("Telegram owner notice id is invalid");
1925
+ await runWithAcceptanceAuditOwner(async () => {
1926
+ const effect = await deliverButlerEffect(requiredText(input.text, "owner notice"), `owner-notice:${noticeId}`, input.signal);
1927
+ const sessionPath = (0, shared_turn_1.getSenseSessionPath)(options.agentName, configuredOwnerFriendId, "telegram", configuredOwnerSessionKey, agentRoot);
1928
+ await recordAcceptedEffects(sessionPath, [effect]);
1929
+ });
1930
+ },
1920
1931
  stop() {
1921
1932
  if (stopPromise)
1922
1933
  return stopPromise;
@@ -2252,6 +2263,15 @@ async function sendTelegramExternalEventDecision(agentName, input) {
2252
2263
  await app.stop();
2253
2264
  }
2254
2265
  }
2266
+ async function sendTelegramOwnerNotice(agentName, input) {
2267
+ const app = await startTelegramSenseApp(agentName);
2268
+ try {
2269
+ await app.sendOwnerNotice(input);
2270
+ }
2271
+ finally {
2272
+ await app.stop();
2273
+ }
2274
+ }
2255
2275
  /**
2256
2276
  * Suppresses a proactive message that repeats one sent recently.
2257
2277
  *
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.847",
3
+ "version": "0.1.0-alpha.848",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.847",
9
+ "version": "0.1.0-alpha.848",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
@@ -18,7 +18,7 @@
18
18
  "@microsoft/teams.common": "2.0.11",
19
19
  "@microsoft/teams.dev": "2.0.11",
20
20
  "@microsoft/teams.graph": "2.0.11",
21
- "@ouro.bot/friends": "0.1.0-alpha.12",
21
+ "@ouro.bot/friends": "0.1.0-alpha.14",
22
22
  "@types/react": "^17.0.91",
23
23
  "@types/ws": "^8.18.1",
24
24
  "ajv": "^8.20.0",
@@ -1357,9 +1357,9 @@
1357
1357
  }
1358
1358
  },
1359
1359
  "node_modules/@ouro.bot/friends": {
1360
- "version": "0.1.0-alpha.12",
1361
- "resolved": "https://registry.npmjs.org/@ouro.bot/friends/-/friends-0.1.0-alpha.12.tgz",
1362
- "integrity": "sha512-pzV2PQvi1eUkcog3iDDHIvwSBDwpsB8/ykrJpoZQ/wB84qQa0xGKJSDnTt45r8H4j/68Cc5LU6vcjlwI5GG47g==",
1360
+ "version": "0.1.0-alpha.14",
1361
+ "resolved": "https://registry.npmjs.org/@ouro.bot/friends/-/friends-0.1.0-alpha.14.tgz",
1362
+ "integrity": "sha512-ikb633DfL8Mz4jgNJtIoMZ9F3hqYngc9o8VzVb9dSlMdwp15bvlVXUvnF/uWqQ7gzmX0GeTx54LQn8AtYFOmsg==",
1363
1363
  "license": "Apache-2.0",
1364
1364
  "dependencies": {
1365
1365
  "libsodium-wrappers": "0.8.4"
@@ -2257,14 +2257,14 @@
2257
2257
  }
2258
2258
  },
2259
2259
  "node_modules/@zone-eu/mailsplit": {
2260
- "version": "5.4.16",
2261
- "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.16.tgz",
2262
- "integrity": "sha512-zQ9iXvlT3Wi/hazeC1MdI4rQc1UJwJ6IQ6QzSZ5KDxLZZWQSazWLOzImLFluXadKShJ9WJvI1xH+AyVS8b9azg==",
2260
+ "version": "5.4.19",
2261
+ "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.19.tgz",
2262
+ "integrity": "sha512-nDWzw+X3tqfwd7DpDThAjg3KqtrNA6bG2gkrf6m2j1JBaGyyKAZzu1TguzB4GEco+HYpXAbXZ7kbl4Sn8H1vXw==",
2263
2263
  "license": "(MIT OR EUPL-1.1+)",
2264
2264
  "dependencies": {
2265
- "libbase64": "1.3.0",
2266
- "libmime": "5.4.3",
2267
- "libqp": "2.1.1"
2265
+ "libbase64": "1.3.1",
2266
+ "libmime": "5.4.6",
2267
+ "libqp": "2.1.2"
2268
2268
  }
2269
2269
  },
2270
2270
  "node_modules/accepts": {
@@ -2460,13 +2460,13 @@
2460
2460
  }
2461
2461
  },
2462
2462
  "node_modules/axios": {
2463
- "version": "1.18.1",
2464
- "resolved": "https://ms-feed-25.pkgs.visualstudio.com/1es-public/_packaging/npm-public/npm/registry/axios/-/axios-1.18.1.tgz",
2465
- "integrity": "sha1-1j+YY7zYk4gVyG+eKr04AYnZbf4=",
2463
+ "version": "1.20.0",
2464
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz",
2465
+ "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==",
2466
2466
  "license": "MIT",
2467
2467
  "dependencies": {
2468
2468
  "follow-redirects": "^1.16.0",
2469
- "form-data": "^4.0.5",
2469
+ "form-data": "^4.0.6",
2470
2470
  "https-proxy-agent": "^5.0.1",
2471
2471
  "proxy-from-env": "^2.1.0"
2472
2472
  }
@@ -2621,9 +2621,9 @@
2621
2621
  }
2622
2622
  },
2623
2623
  "node_modules/brace-expansion": {
2624
- "version": "5.0.9",
2625
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
2626
- "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
2624
+ "version": "5.0.12",
2625
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
2626
+ "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
2627
2627
  "dev": true,
2628
2628
  "license": "MIT",
2629
2629
  "dependencies": {
@@ -2681,12 +2681,6 @@
2681
2681
  "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
2682
2682
  "license": "BSD-3-Clause"
2683
2683
  },
2684
- "node_modules/buffer-from": {
2685
- "version": "1.1.2",
2686
- "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
2687
- "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
2688
- "license": "MIT"
2689
- },
2690
2684
  "node_modules/bundle-name": {
2691
2685
  "version": "4.1.0",
2692
2686
  "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@@ -2975,35 +2969,6 @@
2975
2969
  "node": ">=20"
2976
2970
  }
2977
2971
  },
2978
- "node_modules/concat-stream": {
2979
- "version": "2.0.0",
2980
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
2981
- "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
2982
- "engines": [
2983
- "node >= 6.0"
2984
- ],
2985
- "license": "MIT",
2986
- "dependencies": {
2987
- "buffer-from": "^1.0.0",
2988
- "inherits": "^2.0.3",
2989
- "readable-stream": "^3.0.2",
2990
- "typedarray": "^0.0.6"
2991
- }
2992
- },
2993
- "node_modules/concat-stream/node_modules/readable-stream": {
2994
- "version": "3.6.2",
2995
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
2996
- "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
2997
- "license": "MIT",
2998
- "dependencies": {
2999
- "inherits": "^2.0.3",
3000
- "string_decoder": "^1.1.1",
3001
- "util-deprecate": "^1.0.1"
3002
- },
3003
- "engines": {
3004
- "node": ">= 6"
3005
- }
3006
- },
3007
2972
  "node_modules/content-disposition": {
3008
2973
  "version": "1.1.0",
3009
2974
  "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
@@ -3567,9 +3532,9 @@
3567
3532
  }
3568
3533
  },
3569
3534
  "node_modules/encoding-japanese": {
3570
- "version": "2.3.0",
3571
- "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.3.0.tgz",
3572
- "integrity": "sha512-eQyh1vzHz13DUkZcJO+0IOAoKXRQwKV5IBffeuYsWZyRLGiSzfzXObCqWvqFXdX0UU8qOk+lBXbkUhMCpdJe4Q==",
3535
+ "version": "2.4.0",
3536
+ "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.4.0.tgz",
3537
+ "integrity": "sha512-PE0KjlsDy9JSsoU4C2Q8NXb1kC4c+bZRac9ZvHXUE4q1Ht63gScKMdEy9jst9+Fi3M4eDkqzkEhBZvix4blP2A==",
3573
3538
  "license": "MIT",
3574
3539
  "engines": {
3575
3540
  "node": ">=18.0.0"
@@ -4015,9 +3980,9 @@
4015
3980
  "license": "MIT"
4016
3981
  },
4017
3982
  "node_modules/fast-uri": {
4018
- "version": "3.1.7",
4019
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz",
4020
- "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==",
3983
+ "version": "3.1.8",
3984
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz",
3985
+ "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==",
4021
3986
  "funding": [
4022
3987
  {
4023
3988
  "type": "github",
@@ -5360,27 +5325,27 @@
5360
5325
  }
5361
5326
  },
5362
5327
  "node_modules/libbase64": {
5363
- "version": "1.3.0",
5364
- "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz",
5365
- "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==",
5328
+ "version": "1.3.1",
5329
+ "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.1.tgz",
5330
+ "integrity": "sha512-1b9LXEjEtXj35YKOR9RqNKBootlb37qbIzmqiw4MsP52cjOsfR6i/AYU1Y8qN4kV2TJQmLJ5aiXn45qI6h+Szw==",
5366
5331
  "license": "MIT"
5367
5332
  },
5368
5333
  "node_modules/libmime": {
5369
- "version": "5.4.3",
5370
- "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.4.3.tgz",
5371
- "integrity": "sha512-di9BoDabBUMqjeD/wGj+hHpSgdqAph5ui7w6OdY6NpzU6O6VFLQsMOg9tqCjm/zf9OHzAM9EZxSOF7uIb8O8Hw==",
5334
+ "version": "5.4.6",
5335
+ "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.4.6.tgz",
5336
+ "integrity": "sha512-Cgx1vHd76GTwDaLNsl0K44CjNFfhkX/1bMcoruoWr8Ugcl0/zMw7x/8NpZERIOcpGyzlQo10tOEw9goGsv2Wlg==",
5372
5337
  "license": "MIT",
5373
5338
  "dependencies": {
5374
- "encoding-japanese": "2.3.0",
5339
+ "encoding-japanese": "2.4.0",
5375
5340
  "iconv-lite": "0.7.3",
5376
- "libbase64": "1.3.0",
5377
- "libqp": "2.1.1"
5341
+ "libbase64": "1.3.1",
5342
+ "libqp": "2.1.2"
5378
5343
  }
5379
5344
  },
5380
5345
  "node_modules/libqp": {
5381
- "version": "2.1.1",
5382
- "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz",
5383
- "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==",
5346
+ "version": "2.1.2",
5347
+ "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.2.tgz",
5348
+ "integrity": "sha512-rp+U80tl9Os4K7GnVbPzPRmghPu6eqB8rcF+ontvNAnoga0JhMFG/bkTD/reC8yoznKMgOc5/ttZHJasiWeFPw==",
5384
5349
  "license": "MIT"
5385
5350
  },
5386
5351
  "node_modules/libsodium": {
@@ -5869,19 +5834,19 @@
5869
5834
  }
5870
5835
  },
5871
5836
  "node_modules/mailparser": {
5872
- "version": "3.9.23",
5873
- "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.23.tgz",
5874
- "integrity": "sha512-5jpsKltHt9oudhMu6mXxiwWMWV/CILfly+m9xhKiKW6cdmfOVvcAZ/NCuChV6QlADJqx1e5E2hwDAkP7lTP+Lg==",
5837
+ "version": "3.9.33",
5838
+ "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.33.tgz",
5839
+ "integrity": "sha512-+ZYhApN4f7B8+jokb7VZeml8lxYHixVJ3tJTB5iPG9/MJpy8YmBHGdy+AWJ9dmtUX6t0xlu4rxwj1b7eWCvbDA==",
5875
5840
  "license": "MIT",
5876
5841
  "dependencies": {
5877
- "@zone-eu/mailsplit": "5.4.16",
5878
- "encoding-japanese": "2.3.0",
5842
+ "@zone-eu/mailsplit": "5.4.19",
5843
+ "encoding-japanese": "2.4.0",
5879
5844
  "he": "1.2.0",
5880
5845
  "html-to-text": "10.0.1",
5881
5846
  "iconv-lite": "0.7.3",
5882
- "libmime": "5.4.3",
5847
+ "libmime": "5.4.6",
5883
5848
  "linkify-it": "5.0.2",
5884
- "nodemailer": "10.0.1",
5849
+ "nodemailer": "10.0.13",
5885
5850
  "punycode.js": "2.3.1",
5886
5851
  "tlds": "1.261.0"
5887
5852
  },
@@ -6029,14 +5994,13 @@
6029
5994
  "license": "MIT"
6030
5995
  },
6031
5996
  "node_modules/multer": {
6032
- "version": "2.3.0",
6033
- "resolved": "https://registry.npmjs.org/multer/-/multer-2.3.0.tgz",
6034
- "integrity": "sha512-cjNbm3sttszgZeGfJR124D+jFEfkXCVAsoPBmFn9X7UxmDSFHWqE2CoEj0vrmSpuAFnqWR1Szcm9QTsiHr60Xw==",
5997
+ "version": "2.4.0",
5998
+ "resolved": "https://registry.npmjs.org/multer/-/multer-2.4.0.tgz",
5999
+ "integrity": "sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==",
6035
6000
  "license": "MIT",
6036
6001
  "dependencies": {
6037
6002
  "append-field": "^1.0.0",
6038
6003
  "busboy": "^1.6.0",
6039
- "concat-stream": "^2.0.0",
6040
6004
  "type-is": "^1.6.18"
6041
6005
  },
6042
6006
  "engines": {
@@ -6171,9 +6135,9 @@
6171
6135
  }
6172
6136
  },
6173
6137
  "node_modules/nodemailer": {
6174
- "version": "10.0.1",
6175
- "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-10.0.1.tgz",
6176
- "integrity": "sha512-c+gU9cL9HLDax3vjxL88kW+6NOgdtEUWaZ+AUtxdJR6LLhf0kGdCLExof7yiKW7zdO9EfXCSIgmhGyFmUM0mYQ==",
6138
+ "version": "10.0.13",
6139
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-10.0.13.tgz",
6140
+ "integrity": "sha512-SzG86OlvcW/NNhUFC6uROMwRTL4n7MswfQqC/T8mhkmnY1YVa23zUEMYi4ijSeXSl9GLz9ZeTJDUatEDuY5FeQ==",
6177
6141
  "license": "MIT-0",
6178
6142
  "engines": {
6179
6143
  "node": ">=20.0.0"
@@ -7177,13 +7141,13 @@
7177
7141
  }
7178
7142
  },
7179
7143
  "node_modules/smtp-server": {
7180
- "version": "3.19.9",
7181
- "resolved": "https://registry.npmjs.org/smtp-server/-/smtp-server-3.19.9.tgz",
7182
- "integrity": "sha512-ljndWZ9km1qI/1fUj+VTQwG0ts5NRX+Lb95NPttoUZRvaOMUSPKdph9YoHpxpQfOomafzWvNHJcZkFL2v6OXYw==",
7144
+ "version": "3.19.16",
7145
+ "resolved": "https://registry.npmjs.org/smtp-server/-/smtp-server-3.19.16.tgz",
7146
+ "integrity": "sha512-EAO7FiexdU+s/IiW92MIRDSOaVTGF2skmXEpSvl+A+NDMDLnv+NnoYpmEh+7kmARU1F1NTTe3am35IBMEEnN1w==",
7183
7147
  "license": "MIT-0",
7184
7148
  "dependencies": {
7185
7149
  "ipv6-normalize": "1.0.1",
7186
- "nodemailer": "10.0.1",
7150
+ "nodemailer": "10.0.13",
7187
7151
  "punycode.js": "2.3.1"
7188
7152
  },
7189
7153
  "engines": {
@@ -7607,12 +7571,6 @@
7607
7571
  "url": "https://opencollective.com/express"
7608
7572
  }
7609
7573
  },
7610
- "node_modules/typedarray": {
7611
- "version": "0.0.6",
7612
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
7613
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
7614
- "license": "MIT"
7615
- },
7616
7574
  "node_modules/typescript": {
7617
7575
  "version": "5.9.3",
7618
7576
  "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -7658,9 +7616,9 @@
7658
7616
  "license": "MIT"
7659
7617
  },
7660
7618
  "node_modules/undici": {
7661
- "version": "7.29.0",
7662
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
7663
- "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
7619
+ "version": "7.30.0",
7620
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz",
7621
+ "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==",
7664
7622
  "dev": true,
7665
7623
  "license": "MIT",
7666
7624
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.847",
3
+ "version": "0.1.0-alpha.848",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },
@@ -65,7 +65,7 @@
65
65
  "@microsoft/teams.common": "2.0.11",
66
66
  "@microsoft/teams.dev": "2.0.11",
67
67
  "@microsoft/teams.graph": "2.0.11",
68
- "@ouro.bot/friends": "0.1.0-alpha.12",
68
+ "@ouro.bot/friends": "0.1.0-alpha.14",
69
69
  "@types/react": "^17.0.91",
70
70
  "@types/ws": "^8.18.1",
71
71
  "ajv": "^8.20.0",
@@ -100,7 +100,7 @@
100
100
  "overrides": {
101
101
  "deepmerge-ts": "^8.0.1",
102
102
  "form-data": "4.0.6",
103
- "multer": "2.3.0",
103
+ "multer": "2.4.0",
104
104
  "tmp": "0.2.7",
105
105
  "@microsoft/teams.apps": {
106
106
  "@azure/msal-node": "^5.2.2"