@ouro.bot/cli 0.1.0-alpha.50 → 0.1.0-alpha.52
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 +18 -0
- package/dist/heart/active-work.js +32 -11
- package/dist/heart/core.js +29 -4
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/model-capabilities.js +40 -0
- package/dist/heart/providers/anthropic.js +56 -5
- package/dist/heart/providers/azure.js +8 -1
- package/dist/heart/providers/minimax.js +4 -0
- package/dist/heart/providers/openai-codex.js +10 -1
- package/dist/heart/streaming.js +4 -1
- package/dist/heart/target-resolution.js +123 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/prompt.js +37 -1
- package/dist/repertoire/tools-base.js +162 -8
- package/dist/repertoire/tools.js +44 -22
- package/dist/senses/bluebubbles.js +43 -8
- package/dist/senses/pipeline.js +22 -0
- package/dist/senses/teams.js +107 -78
- package/package.json +1 -1
package/dist/repertoire/tools.js
CHANGED
|
@@ -35,9 +35,11 @@ function blockedLocalToolMessage() {
|
|
|
35
35
|
return "I can't do that because my trust level with you isn't high enough for local shell/file operations. Ask me for a remote-safe alternative (Graph/ADO/web), or run that operation from CLI.";
|
|
36
36
|
}
|
|
37
37
|
function baseToolsForCapabilities(capabilities, context) {
|
|
38
|
+
// Use baseToolDefinitions at call time so dynamically-added tools are included
|
|
39
|
+
const currentTools = tools_base_1.baseToolDefinitions.map((d) => d.tool);
|
|
38
40
|
if (!shouldBlockLocalTools(capabilities, context))
|
|
39
|
-
return
|
|
40
|
-
return
|
|
41
|
+
return currentTools;
|
|
42
|
+
return currentTools.filter((tool) => !exports.REMOTE_BLOCKED_LOCAL_TOOLS.has(tool.function.name));
|
|
41
43
|
}
|
|
42
44
|
// Apply a single tool preference to a tool schema, returning a new object.
|
|
43
45
|
function applyPreference(tool, pref) {
|
|
@@ -49,37 +51,55 @@ function applyPreference(tool, pref) {
|
|
|
49
51
|
},
|
|
50
52
|
};
|
|
51
53
|
}
|
|
54
|
+
// Filter out tools whose requiredCapability is not in the provider's capability set.
|
|
55
|
+
// Uses baseToolDefinitions at call time so dynamically-added tools are included.
|
|
56
|
+
// Only base tools can have requiredCapability (integration tools do not).
|
|
57
|
+
function filterByCapability(toolList, providerCapabilities) {
|
|
58
|
+
return toolList.filter((tool) => {
|
|
59
|
+
const def = tools_base_1.baseToolDefinitions.find((d) => d.tool.function.name === tool.function.name);
|
|
60
|
+
if (!def?.requiredCapability)
|
|
61
|
+
return true;
|
|
62
|
+
return providerCapabilities?.has(def.requiredCapability) === true;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
52
65
|
// Return the appropriate tools list based on channel capabilities.
|
|
53
66
|
// Base tools (no integration) are always included.
|
|
54
67
|
// Teams/integration tools are included only if their integration is in availableIntegrations.
|
|
55
68
|
// When toolPreferences is provided, matching preferences are appended to tool descriptions.
|
|
56
|
-
|
|
69
|
+
// When providerCapabilities is provided, tools with requiredCapability are filtered.
|
|
70
|
+
function getToolsForChannel(capabilities, toolPreferences, context, providerCapabilities) {
|
|
57
71
|
const baseTools = baseToolsForCapabilities(capabilities, context);
|
|
58
72
|
const bluebubblesTools = capabilities?.channel === "bluebubbles"
|
|
59
73
|
? tools_bluebubbles_1.bluebubblesToolDefinitions.map((d) => d.tool)
|
|
60
74
|
: [];
|
|
75
|
+
let result;
|
|
61
76
|
if (!capabilities || capabilities.availableIntegrations.length === 0) {
|
|
62
|
-
|
|
77
|
+
result = [...baseTools, ...bluebubblesTools];
|
|
63
78
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
else {
|
|
80
|
+
const available = new Set(capabilities.availableIntegrations);
|
|
81
|
+
const channelDefs = [...tools_teams_1.teamsToolDefinitions, ...ado_semantic_1.adoSemanticToolDefinitions, ...tools_github_1.githubToolDefinitions];
|
|
82
|
+
// Include tools whose integration is available, plus channel tools with no integration gate (e.g. teams_send_message)
|
|
83
|
+
const integrationDefs = channelDefs.filter((d) => d.integration ? available.has(d.integration) : capabilities.channel === "teams");
|
|
84
|
+
if (!toolPreferences || Object.keys(toolPreferences).length === 0) {
|
|
85
|
+
result = [...baseTools, ...bluebubblesTools, ...integrationDefs.map((d) => d.tool)];
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
// Build a map of integration -> preference text for fast lookup
|
|
89
|
+
const prefMap = new Map();
|
|
90
|
+
for (const [key, value] of Object.entries(toolPreferences)) {
|
|
91
|
+
prefMap.set(key, value);
|
|
92
|
+
}
|
|
93
|
+
// Apply preferences to matching integration tools (new objects, no mutation)
|
|
94
|
+
// d.integration is guaranteed truthy -- integrationDefs are pre-filtered above
|
|
95
|
+
const enrichedIntegrationTools = integrationDefs.map((d) => {
|
|
96
|
+
const pref = prefMap.get(d.integration);
|
|
97
|
+
return pref ? applyPreference(d.tool, pref) : d.tool;
|
|
98
|
+
});
|
|
99
|
+
result = [...baseTools, ...bluebubblesTools, ...enrichedIntegrationTools];
|
|
100
|
+
}
|
|
70
101
|
}
|
|
71
|
-
|
|
72
|
-
const prefMap = new Map();
|
|
73
|
-
for (const [key, value] of Object.entries(toolPreferences)) {
|
|
74
|
-
prefMap.set(key, value);
|
|
75
|
-
}
|
|
76
|
-
// Apply preferences to matching integration tools (new objects, no mutation)
|
|
77
|
-
// d.integration is guaranteed truthy -- integrationDefs are pre-filtered above
|
|
78
|
-
const enrichedIntegrationTools = integrationDefs.map((d) => {
|
|
79
|
-
const pref = prefMap.get(d.integration);
|
|
80
|
-
return pref ? applyPreference(d.tool, pref) : d.tool;
|
|
81
|
-
});
|
|
82
|
-
return [...baseTools, ...bluebubblesTools, ...enrichedIntegrationTools];
|
|
102
|
+
return filterByCapability(result, providerCapabilities);
|
|
83
103
|
}
|
|
84
104
|
// Check whether a tool requires user confirmation before execution.
|
|
85
105
|
// Reads from ToolDefinition.confirmationRequired instead of a separate Set.
|
|
@@ -192,6 +212,8 @@ function summarizeArgs(name, args) {
|
|
|
192
212
|
return summarizeKeyValues(args, ["sessionId"]);
|
|
193
213
|
if (name === "bluebubbles_set_reply_target")
|
|
194
214
|
return summarizeKeyValues(args, ["target", "threadOriginatorGuid"]);
|
|
215
|
+
if (name === "set_reasoning_effort")
|
|
216
|
+
return summarizeKeyValues(args, ["level"]);
|
|
195
217
|
if (name === "claude")
|
|
196
218
|
return summarizeKeyValues(args, ["prompt"]);
|
|
197
219
|
if (name === "web_search")
|
|
@@ -48,6 +48,7 @@ const identity_1 = require("../heart/identity");
|
|
|
48
48
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
49
49
|
const context_1 = require("../mind/context");
|
|
50
50
|
const tokens_1 = require("../mind/friends/tokens");
|
|
51
|
+
const group_context_1 = require("../mind/friends/group-context");
|
|
51
52
|
const resolver_1 = require("../mind/friends/resolver");
|
|
52
53
|
const store_file_1 = require("../mind/friends/store-file");
|
|
53
54
|
const types_1 = require("../mind/friends/types");
|
|
@@ -97,6 +98,10 @@ function resolveFriendParams(event) {
|
|
|
97
98
|
channel: "bluebubbles",
|
|
98
99
|
};
|
|
99
100
|
}
|
|
101
|
+
function resolveGroupExternalId(event) {
|
|
102
|
+
const groupKey = event.chat.chatGuid ?? event.chat.chatIdentifier ?? event.sender.externalId;
|
|
103
|
+
return `group:${groupKey}`;
|
|
104
|
+
}
|
|
100
105
|
/**
|
|
101
106
|
* Check if any participant in a group chat is a known family member.
|
|
102
107
|
* Looks up each participant handle in the friend store.
|
|
@@ -585,6 +590,16 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
|
|
|
585
590
|
return { handled: true, notifiedAgent: false, kind: event.kind, reason: "already_processed" };
|
|
586
591
|
}
|
|
587
592
|
}
|
|
593
|
+
if (event.kind === "message" && event.chat.isGroup) {
|
|
594
|
+
await (0, group_context_1.upsertGroupContextParticipants)({
|
|
595
|
+
store,
|
|
596
|
+
participants: (event.chat.participantHandles ?? []).map((externalId) => ({
|
|
597
|
+
provider: "imessage-handle",
|
|
598
|
+
externalId,
|
|
599
|
+
})),
|
|
600
|
+
groupExternalId: resolveGroupExternalId(event),
|
|
601
|
+
});
|
|
602
|
+
}
|
|
588
603
|
// Build inbound user message (adapter concern: BB-specific content formatting)
|
|
589
604
|
const userMessage = {
|
|
590
605
|
role: "user",
|
|
@@ -842,21 +857,33 @@ function findImessageHandle(friend) {
|
|
|
842
857
|
}
|
|
843
858
|
return undefined;
|
|
844
859
|
}
|
|
860
|
+
function normalizeBlueBubblesSessionKey(sessionKey) {
|
|
861
|
+
const trimmed = sessionKey.trim();
|
|
862
|
+
if (trimmed.startsWith("chat_identifier_")) {
|
|
863
|
+
return `chat_identifier:${trimmed.slice("chat_identifier_".length)}`;
|
|
864
|
+
}
|
|
865
|
+
if (trimmed.startsWith("chat_")) {
|
|
866
|
+
return `chat:${trimmed.slice("chat_".length)}`;
|
|
867
|
+
}
|
|
868
|
+
return trimmed;
|
|
869
|
+
}
|
|
845
870
|
function extractChatIdentifierFromSessionKey(sessionKey) {
|
|
846
|
-
|
|
847
|
-
|
|
871
|
+
const normalizedKey = normalizeBlueBubblesSessionKey(sessionKey);
|
|
872
|
+
if (normalizedKey.startsWith("chat:")) {
|
|
873
|
+
const chatGuid = normalizedKey.slice("chat:".length).trim();
|
|
848
874
|
const parts = chatGuid.split(";");
|
|
849
875
|
return parts.length >= 3 ? parts[2]?.trim() || undefined : undefined;
|
|
850
876
|
}
|
|
851
|
-
if (
|
|
852
|
-
const identifier =
|
|
877
|
+
if (normalizedKey.startsWith("chat_identifier:")) {
|
|
878
|
+
const identifier = normalizedKey.slice("chat_identifier:".length).trim();
|
|
853
879
|
return identifier || undefined;
|
|
854
880
|
}
|
|
855
881
|
return undefined;
|
|
856
882
|
}
|
|
857
883
|
function buildChatRefForSessionKey(friend, sessionKey) {
|
|
858
|
-
|
|
859
|
-
|
|
884
|
+
const normalizedKey = normalizeBlueBubblesSessionKey(sessionKey);
|
|
885
|
+
if (normalizedKey.startsWith("chat:")) {
|
|
886
|
+
const chatGuid = normalizedKey.slice("chat:".length).trim();
|
|
860
887
|
if (!chatGuid)
|
|
861
888
|
return null;
|
|
862
889
|
return {
|
|
@@ -900,12 +927,20 @@ async function sendProactiveBlueBubblesMessageToSession(params, deps = {}) {
|
|
|
900
927
|
});
|
|
901
928
|
return { delivered: false, reason: "friend_not_found" };
|
|
902
929
|
}
|
|
903
|
-
|
|
930
|
+
const explicitCrossChatAuthorized = params.intent === "explicit_cross_chat"
|
|
931
|
+
&& types_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
|
|
932
|
+
if (!explicitCrossChatAuthorized && !types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
904
933
|
(0, runtime_1.emitNervesEvent)({
|
|
905
934
|
component: "senses",
|
|
906
935
|
event: "senses.bluebubbles_proactive_trust_skip",
|
|
907
936
|
message: "proactive send skipped: trust level not allowed",
|
|
908
|
-
meta: {
|
|
937
|
+
meta: {
|
|
938
|
+
friendId: params.friendId,
|
|
939
|
+
sessionKey: params.sessionKey,
|
|
940
|
+
trustLevel: friend.trustLevel ?? "unknown",
|
|
941
|
+
intent: params.intent ?? "generic_outreach",
|
|
942
|
+
authorizingTrustLevel: params.authorizingSession?.trustLevel ?? null,
|
|
943
|
+
},
|
|
909
944
|
});
|
|
910
945
|
return { delivered: false, reason: "trust_skip" };
|
|
911
946
|
}
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -14,6 +14,7 @@ const tasks_1 = require("../repertoire/tasks");
|
|
|
14
14
|
const session_activity_1 = require("../heart/session-activity");
|
|
15
15
|
const active_work_1 = require("../heart/active-work");
|
|
16
16
|
const delegation_1 = require("../heart/delegation");
|
|
17
|
+
const target_resolution_1 = require("../heart/target-resolution");
|
|
17
18
|
const thoughts_1 = require("../heart/daemon/thoughts");
|
|
18
19
|
const pending_1 = require("../mind/pending");
|
|
19
20
|
function emptyTaskBoard() {
|
|
@@ -137,6 +138,26 @@ async function handleInboundTurn(input) {
|
|
|
137
138
|
catch {
|
|
138
139
|
sessionActivity = [];
|
|
139
140
|
}
|
|
141
|
+
let targetCandidates = [];
|
|
142
|
+
try {
|
|
143
|
+
if (input.channel !== "inner") {
|
|
144
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
145
|
+
targetCandidates = await (0, target_resolution_1.listTargetSessionCandidates)({
|
|
146
|
+
sessionsDir: `${agentRoot}/state/sessions`,
|
|
147
|
+
friendsDir: `${agentRoot}/friends`,
|
|
148
|
+
agentName: (0, identity_1.getAgentName)(),
|
|
149
|
+
currentSession: {
|
|
150
|
+
friendId: currentSession.friendId,
|
|
151
|
+
channel: currentSession.channel,
|
|
152
|
+
key: currentSession.key,
|
|
153
|
+
},
|
|
154
|
+
friendStore: input.friendStore,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
targetCandidates = [];
|
|
160
|
+
}
|
|
140
161
|
const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
|
|
141
162
|
currentSession,
|
|
142
163
|
currentObligation,
|
|
@@ -152,6 +173,7 @@ async function handleInboundTurn(input) {
|
|
|
152
173
|
}
|
|
153
174
|
})(),
|
|
154
175
|
friendActivity: sessionActivity,
|
|
176
|
+
targetCandidates,
|
|
155
177
|
});
|
|
156
178
|
const delegationDecision = (0, delegation_1.decideDelegation)({
|
|
157
179
|
channel: input.channel,
|
package/dist/senses/teams.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.createTeamsCallbacks = createTeamsCallbacks;
|
|
|
40
40
|
exports.resolvePendingConfirmation = resolvePendingConfirmation;
|
|
41
41
|
exports.withConversationLock = withConversationLock;
|
|
42
42
|
exports.handleTeamsMessage = handleTeamsMessage;
|
|
43
|
+
exports.sendProactiveTeamsMessageToSession = sendProactiveTeamsMessageToSession;
|
|
43
44
|
exports.drainAndSendPendingTeams = drainAndSendPendingTeams;
|
|
44
45
|
exports.startTeamsApp = startTeamsApp;
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
@@ -867,6 +868,98 @@ function findAadObjectId(friend) {
|
|
|
867
868
|
}
|
|
868
869
|
return undefined;
|
|
869
870
|
}
|
|
871
|
+
function resolveTeamsFriendStore(deps) {
|
|
872
|
+
return deps.store
|
|
873
|
+
?? deps.createFriendStore?.()
|
|
874
|
+
?? new store_file_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
|
|
875
|
+
}
|
|
876
|
+
function getTeamsConversations(botApi) {
|
|
877
|
+
return botApi.conversations;
|
|
878
|
+
}
|
|
879
|
+
function hasExplicitCrossChatAuthorization(params) {
|
|
880
|
+
return params.intent === "explicit_cross_chat"
|
|
881
|
+
&& types_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
|
|
882
|
+
}
|
|
883
|
+
async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
884
|
+
const store = resolveTeamsFriendStore(deps);
|
|
885
|
+
const conversations = getTeamsConversations(deps.botApi);
|
|
886
|
+
let friend;
|
|
887
|
+
try {
|
|
888
|
+
friend = await store.get(params.friendId);
|
|
889
|
+
}
|
|
890
|
+
catch {
|
|
891
|
+
friend = null;
|
|
892
|
+
}
|
|
893
|
+
if (!friend) {
|
|
894
|
+
(0, runtime_1.emitNervesEvent)({
|
|
895
|
+
level: "warn",
|
|
896
|
+
component: "senses",
|
|
897
|
+
event: "senses.teams_proactive_no_friend",
|
|
898
|
+
message: "proactive send skipped: friend not found",
|
|
899
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
900
|
+
});
|
|
901
|
+
return { delivered: false, reason: "friend_not_found" };
|
|
902
|
+
}
|
|
903
|
+
if (!hasExplicitCrossChatAuthorization(params) && !types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
904
|
+
(0, runtime_1.emitNervesEvent)({
|
|
905
|
+
component: "senses",
|
|
906
|
+
event: "senses.teams_proactive_trust_skip",
|
|
907
|
+
message: "proactive send skipped: trust level not allowed",
|
|
908
|
+
meta: {
|
|
909
|
+
friendId: params.friendId,
|
|
910
|
+
trustLevel: friend.trustLevel ?? "unknown",
|
|
911
|
+
intent: params.intent ?? "generic_outreach",
|
|
912
|
+
authorizingTrustLevel: params.authorizingSession?.trustLevel ?? null,
|
|
913
|
+
},
|
|
914
|
+
});
|
|
915
|
+
return { delivered: false, reason: "trust_skip" };
|
|
916
|
+
}
|
|
917
|
+
const aadInfo = findAadObjectId(friend);
|
|
918
|
+
if (!aadInfo) {
|
|
919
|
+
(0, runtime_1.emitNervesEvent)({
|
|
920
|
+
level: "warn",
|
|
921
|
+
component: "senses",
|
|
922
|
+
event: "senses.teams_proactive_no_aad_id",
|
|
923
|
+
message: "proactive send skipped: no AAD object ID found",
|
|
924
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
925
|
+
});
|
|
926
|
+
return { delivered: false, reason: "missing_target" };
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
const conversation = await conversations.create({
|
|
930
|
+
bot: { id: deps.botApi.id },
|
|
931
|
+
members: [{ id: aadInfo.aadObjectId, role: "user", name: friend.name || aadInfo.aadObjectId }],
|
|
932
|
+
tenantId: aadInfo.tenantId,
|
|
933
|
+
isGroup: false,
|
|
934
|
+
});
|
|
935
|
+
await conversations.activities(conversation.id).create({
|
|
936
|
+
type: "message",
|
|
937
|
+
text: params.text,
|
|
938
|
+
});
|
|
939
|
+
(0, runtime_1.emitNervesEvent)({
|
|
940
|
+
component: "senses",
|
|
941
|
+
event: "senses.teams_proactive_sent",
|
|
942
|
+
message: "proactive teams message sent",
|
|
943
|
+
meta: { friendId: params.friendId, aadObjectId: aadInfo.aadObjectId, sessionKey: params.sessionKey },
|
|
944
|
+
});
|
|
945
|
+
return { delivered: true };
|
|
946
|
+
}
|
|
947
|
+
catch (error) {
|
|
948
|
+
(0, runtime_1.emitNervesEvent)({
|
|
949
|
+
level: "error",
|
|
950
|
+
component: "senses",
|
|
951
|
+
event: "senses.teams_proactive_send_error",
|
|
952
|
+
message: "proactive teams send failed",
|
|
953
|
+
meta: {
|
|
954
|
+
friendId: params.friendId,
|
|
955
|
+
aadObjectId: aadInfo.aadObjectId,
|
|
956
|
+
sessionKey: params.sessionKey,
|
|
957
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
958
|
+
},
|
|
959
|
+
});
|
|
960
|
+
return { delivered: false, reason: "send_error" };
|
|
961
|
+
}
|
|
962
|
+
}
|
|
870
963
|
function scanPendingTeamsFiles(pendingRoot) {
|
|
871
964
|
const results = [];
|
|
872
965
|
let friendIds;
|
|
@@ -912,8 +1005,7 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
912
1005
|
const root = pendingRoot ?? path.join((0, identity_1.getAgentRoot)(), "state", "pending");
|
|
913
1006
|
const pendingFiles = scanPendingTeamsFiles(root);
|
|
914
1007
|
const result = { sent: 0, skipped: 0, failed: 0 };
|
|
915
|
-
const
|
|
916
|
-
for (const { friendId, filePath, content } of pendingFiles) {
|
|
1008
|
+
for (const { friendId, key, filePath, content } of pendingFiles) {
|
|
917
1009
|
let parsed;
|
|
918
1010
|
try {
|
|
919
1011
|
parsed = JSON.parse(content);
|
|
@@ -935,95 +1027,32 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
935
1027
|
catch { /* ignore */ }
|
|
936
1028
|
continue;
|
|
937
1029
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
catch { /* ignore */ }
|
|
951
|
-
(0, runtime_1.emitNervesEvent)({
|
|
952
|
-
level: "warn",
|
|
953
|
-
component: "senses",
|
|
954
|
-
event: "senses.teams_proactive_no_friend",
|
|
955
|
-
message: "proactive send skipped: friend not found",
|
|
956
|
-
meta: { friendId },
|
|
957
|
-
});
|
|
958
|
-
continue;
|
|
959
|
-
}
|
|
960
|
-
if (!types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
961
|
-
result.skipped++;
|
|
1030
|
+
const sendResult = await sendProactiveTeamsMessageToSession({
|
|
1031
|
+
friendId,
|
|
1032
|
+
sessionKey: key,
|
|
1033
|
+
text: messageText,
|
|
1034
|
+
intent: "generic_outreach",
|
|
1035
|
+
}, {
|
|
1036
|
+
botApi,
|
|
1037
|
+
store,
|
|
1038
|
+
});
|
|
1039
|
+
if (sendResult.delivered) {
|
|
1040
|
+
result.sent++;
|
|
962
1041
|
try {
|
|
963
1042
|
fs.unlinkSync(filePath);
|
|
964
1043
|
}
|
|
965
1044
|
catch { /* ignore */ }
|
|
966
|
-
(0, runtime_1.emitNervesEvent)({
|
|
967
|
-
component: "senses",
|
|
968
|
-
event: "senses.teams_proactive_trust_skip",
|
|
969
|
-
message: "proactive send skipped: trust level not allowed",
|
|
970
|
-
meta: { friendId, trustLevel: friend.trustLevel ?? "unknown" },
|
|
971
|
-
});
|
|
972
1045
|
continue;
|
|
973
1046
|
}
|
|
974
|
-
|
|
975
|
-
if (!aadInfo) {
|
|
1047
|
+
if (sendResult.reason === "friend_not_found" || sendResult.reason === "trust_skip" || sendResult.reason === "missing_target") {
|
|
976
1048
|
result.skipped++;
|
|
977
1049
|
try {
|
|
978
1050
|
fs.unlinkSync(filePath);
|
|
979
1051
|
}
|
|
980
1052
|
catch { /* ignore */ }
|
|
981
|
-
(0, runtime_1.emitNervesEvent)({
|
|
982
|
-
level: "warn",
|
|
983
|
-
component: "senses",
|
|
984
|
-
event: "senses.teams_proactive_no_aad_id",
|
|
985
|
-
message: "proactive send skipped: no AAD object ID found",
|
|
986
|
-
meta: { friendId },
|
|
987
|
-
});
|
|
988
1053
|
continue;
|
|
989
1054
|
}
|
|
990
|
-
|
|
991
|
-
const conversation = await conversations.create({
|
|
992
|
-
bot: { id: botApi.id },
|
|
993
|
-
members: [{ id: aadInfo.aadObjectId, role: "user", name: friend.name || aadInfo.aadObjectId }],
|
|
994
|
-
tenantId: aadInfo.tenantId,
|
|
995
|
-
isGroup: false,
|
|
996
|
-
});
|
|
997
|
-
await conversations.activities(conversation.id).create({
|
|
998
|
-
type: "message",
|
|
999
|
-
text: messageText,
|
|
1000
|
-
});
|
|
1001
|
-
result.sent++;
|
|
1002
|
-
try {
|
|
1003
|
-
fs.unlinkSync(filePath);
|
|
1004
|
-
}
|
|
1005
|
-
catch { /* ignore */ }
|
|
1006
|
-
(0, runtime_1.emitNervesEvent)({
|
|
1007
|
-
component: "senses",
|
|
1008
|
-
event: "senses.teams_proactive_sent",
|
|
1009
|
-
message: "proactive teams message sent",
|
|
1010
|
-
meta: { friendId, aadObjectId: aadInfo.aadObjectId },
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
catch (error) {
|
|
1014
|
-
result.failed++;
|
|
1015
|
-
(0, runtime_1.emitNervesEvent)({
|
|
1016
|
-
level: "error",
|
|
1017
|
-
component: "senses",
|
|
1018
|
-
event: "senses.teams_proactive_send_error",
|
|
1019
|
-
message: "proactive teams send failed",
|
|
1020
|
-
meta: {
|
|
1021
|
-
friendId,
|
|
1022
|
-
aadObjectId: aadInfo.aadObjectId,
|
|
1023
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
1024
|
-
},
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1055
|
+
result.failed++;
|
|
1027
1056
|
}
|
|
1028
1057
|
if (result.sent > 0 || result.skipped > 0 || result.failed > 0) {
|
|
1029
1058
|
(0, runtime_1.emitNervesEvent)({
|