@ouro.bot/cli 0.1.0-alpha.669 → 0.1.0-alpha.670
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 +6 -0
- package/dist/a2a/onboarding.js +2 -2
- package/dist/heart/core.js +3 -3
- package/dist/heart/cross-chat-delivery.js +2 -2
- package/dist/heart/daemon/cli-exec.js +3 -3
- package/dist/heart/daemon/cli-parse.js +2 -2
- package/dist/heart/daemon/daemon.js +2 -2
- package/dist/heart/daemon/runtime-logging.js +6 -0
- package/dist/heart/target-resolution.js +2 -2
- package/dist/mind/prompt.js +12 -14
- package/dist/mind/provenance-trust.js +2 -2
- package/dist/repertoire/guardrails.js +3 -3
- package/dist/repertoire/tools-a2a.js +6 -7
- package/dist/repertoire/tools-mail.js +2 -2
- package/dist/repertoire/tools-record.js +2 -2
- package/dist/repertoire/tools-trip.js +2 -2
- package/dist/senses/bluebubbles/index.js +9 -14
- package/dist/senses/cli.js +5 -8
- package/dist/senses/inner-dialog-worker.js +2 -2
- package/dist/senses/inner-dialog.js +3 -4
- package/dist/senses/shared-turn.js +5 -8
- package/dist/senses/teams.js +10 -14
- package/dist/senses/trust-gate.js +2 -2
- package/dist/senses/voice/outbound.js +2 -2
- package/dist/senses/voice/twilio-phone.js +9 -11
- package/package.json +2 -1
- package/dist/mind/friends/channel.js +0 -132
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -187
- package/dist/mind/friends/store-file.js +0 -247
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
|
@@ -51,12 +51,9 @@ const config_1 = require("../heart/config");
|
|
|
51
51
|
const session_events_1 = require("../heart/session-events");
|
|
52
52
|
const context_1 = require("../mind/context");
|
|
53
53
|
const prompt_1 = require("../mind/prompt");
|
|
54
|
-
const
|
|
55
|
-
const resolver_1 = require("../mind/friends/resolver");
|
|
56
|
-
const store_file_1 = require("../mind/friends/store-file");
|
|
54
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
57
55
|
const pending_1 = require("../mind/pending");
|
|
58
56
|
const context_2 = require("../mind/context");
|
|
59
|
-
const tokens_1 = require("../mind/friends/tokens");
|
|
60
57
|
const trust_gate_1 = require("./trust-gate");
|
|
61
58
|
const pipeline_1 = require("./pipeline");
|
|
62
59
|
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
@@ -193,8 +190,8 @@ async function runSenseTurn(options) {
|
|
|
193
190
|
// Resolve context
|
|
194
191
|
const agentRoot = (0, identity_1.getAgentRoot)(agentName);
|
|
195
192
|
const friendsPath = path.join(agentRoot, "friends");
|
|
196
|
-
const friendStore = new
|
|
197
|
-
const capabilities = (0,
|
|
193
|
+
const friendStore = new friends_1.FileFriendStore(friendsPath);
|
|
194
|
+
const capabilities = (0, friends_1.getChannelCapabilities)(channel);
|
|
198
195
|
// If friendId looks like a UUID, look up the friend record directly and use its identity.
|
|
199
196
|
// Otherwise, resolve as a local user (same pattern as CLI sense).
|
|
200
197
|
const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-/.test(friendId);
|
|
@@ -229,7 +226,7 @@ async function runSenseTurn(options) {
|
|
|
229
226
|
const username = os.userInfo().username;
|
|
230
227
|
resolverParams = { provider: "local", externalId: username, displayName: username, channel };
|
|
231
228
|
}
|
|
232
|
-
const resolver = new
|
|
229
|
+
const resolver = new friends_1.FriendResolver(friendStore, resolverParams);
|
|
233
230
|
// Initialize MCP manager so MCP tools appear as first-class tools in the agent's tool list.
|
|
234
231
|
// Runtime MCP servers (e.g. Workbench's ouro_workbench) are passed per-turn for THIS agent only.
|
|
235
232
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)(options.runtimeMcpServers ? { runtimeServers: options.runtimeMcpServers } : undefined) ?? undefined;
|
|
@@ -351,7 +348,7 @@ async function runSenseTurn(options) {
|
|
|
351
348
|
persistPromise = (0, context_2.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
352
349
|
},
|
|
353
350
|
/* v8 ignore stop */
|
|
354
|
-
accumulateFriendTokens:
|
|
351
|
+
accumulateFriendTokens: friends_1.accumulateFriendTokens,
|
|
355
352
|
});
|
|
356
353
|
if (turnResult.gateResult && !turnResult.gateResult.allowed) {
|
|
357
354
|
const blockedResponse = "autoReply" in turnResult.gateResult
|
package/dist/senses/teams.js
CHANGED
|
@@ -51,7 +51,7 @@ const teams_apps_1 = require("@microsoft/teams.apps");
|
|
|
51
51
|
const teams_dev_1 = require("@microsoft/teams.dev");
|
|
52
52
|
const core_1 = require("../heart/core");
|
|
53
53
|
const tools_1 = require("../repertoire/tools");
|
|
54
|
-
const
|
|
54
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
55
55
|
const config_1 = require("../heart/config");
|
|
56
56
|
const prompt_1 = require("../mind/prompt");
|
|
57
57
|
const phrases_1 = require("../mind/phrases");
|
|
@@ -62,10 +62,6 @@ const commands_1 = require("./commands");
|
|
|
62
62
|
const nerves_1 = require("../nerves");
|
|
63
63
|
const runtime_1 = require("../nerves/runtime");
|
|
64
64
|
const proactive_content_guard_1 = require("./proactive-content-guard");
|
|
65
|
-
const store_file_1 = require("../mind/friends/store-file");
|
|
66
|
-
const types_1 = require("../mind/friends/types");
|
|
67
|
-
const resolver_1 = require("../mind/friends/resolver");
|
|
68
|
-
const tokens_1 = require("../mind/friends/tokens");
|
|
69
65
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
70
66
|
const identity_1 = require("../heart/identity");
|
|
71
67
|
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
@@ -610,7 +606,7 @@ async function withConversationLock(convId, fn) {
|
|
|
610
606
|
// are deleted while the process is alive.
|
|
611
607
|
function getFriendStore() {
|
|
612
608
|
const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
|
|
613
|
-
return new
|
|
609
|
+
return new friends_1.FileFriendStore(friendsPath);
|
|
614
610
|
}
|
|
615
611
|
function createTeamsCommandRegistry() {
|
|
616
612
|
const registry = (0, commands_1.createCommandRegistry)();
|
|
@@ -660,7 +656,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
660
656
|
const provider = teamsContext?.aadObjectId ? "aad" : "teams-conversation";
|
|
661
657
|
const externalId = teamsContext?.aadObjectId || conversationId;
|
|
662
658
|
// Build FriendResolver for the pipeline
|
|
663
|
-
const resolver = new
|
|
659
|
+
const resolver = new friends_1.FriendResolver(store, {
|
|
664
660
|
provider,
|
|
665
661
|
externalId,
|
|
666
662
|
tenantId: teamsContext?.tenantId,
|
|
@@ -676,7 +672,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
676
672
|
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs, ...(reactionOverrides?.suppressEmptyStreamMessage ? { suppressEmptyStreamMessage: true } : {}) });
|
|
677
673
|
const traceId = (0, nerves_1.createTraceId)();
|
|
678
674
|
const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
|
|
679
|
-
const teamsCapabilities = (0,
|
|
675
|
+
const teamsCapabilities = (0, friends_1.getChannelCapabilities)("teams");
|
|
680
676
|
const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, "teams", conversationId);
|
|
681
677
|
// Build Teams-specific toolContext fields for injection into the pipeline
|
|
682
678
|
const teamsToolContext = teamsContext ? {
|
|
@@ -773,7 +769,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
773
769
|
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
774
770
|
(0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
775
771
|
},
|
|
776
|
-
accumulateFriendTokens:
|
|
772
|
+
accumulateFriendTokens: friends_1.accumulateFriendTokens,
|
|
777
773
|
signal: controller.signal,
|
|
778
774
|
runAgentOptions: agentOptions,
|
|
779
775
|
failoverState: teamsFailoverState,
|
|
@@ -1024,7 +1020,7 @@ function registerBotHandlers(app, label) {
|
|
|
1024
1020
|
const commandStore = getFriendStore();
|
|
1025
1021
|
const commandProvider = activity.from?.aadObjectId ? "aad" : "teams-conversation";
|
|
1026
1022
|
const commandExternalId = activity.from?.aadObjectId || convId;
|
|
1027
|
-
const commandResolver = new
|
|
1023
|
+
const commandResolver = new friends_1.FriendResolver(commandStore, {
|
|
1028
1024
|
provider: commandProvider,
|
|
1029
1025
|
externalId: commandExternalId,
|
|
1030
1026
|
tenantId: activity.conversation?.tenantId,
|
|
@@ -1139,14 +1135,14 @@ function findAadObjectId(friend) {
|
|
|
1139
1135
|
function resolveTeamsFriendStore(deps) {
|
|
1140
1136
|
return deps.store
|
|
1141
1137
|
?? deps.createFriendStore?.()
|
|
1142
|
-
?? new
|
|
1138
|
+
?? new friends_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
|
|
1143
1139
|
}
|
|
1144
1140
|
function getTeamsConversations(botApi) {
|
|
1145
1141
|
return botApi.conversations;
|
|
1146
1142
|
}
|
|
1147
1143
|
function hasExplicitCrossChatAuthorization(params) {
|
|
1148
1144
|
return params.intent === "explicit_cross_chat"
|
|
1149
|
-
&&
|
|
1145
|
+
&& friends_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
|
|
1150
1146
|
}
|
|
1151
1147
|
async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
1152
1148
|
const store = resolveTeamsFriendStore(deps);
|
|
@@ -1168,7 +1164,7 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
1168
1164
|
});
|
|
1169
1165
|
return { delivered: false, reason: "friend_not_found" };
|
|
1170
1166
|
}
|
|
1171
|
-
if (!hasExplicitCrossChatAuthorization(params) && !
|
|
1167
|
+
if (!hasExplicitCrossChatAuthorization(params) && !friends_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
1172
1168
|
(0, runtime_1.emitNervesEvent)({
|
|
1173
1169
|
component: "senses",
|
|
1174
1170
|
event: "senses.teams_proactive_trust_skip",
|
|
@@ -1380,7 +1376,7 @@ function startTeamsApp() {
|
|
|
1380
1376
|
const port = process.env.PORT ? Number(process.env.PORT) : (0, config_2.getTeamsChannelConfig)().port;
|
|
1381
1377
|
app.start(port);
|
|
1382
1378
|
// Diagnostic: log tool count at startup to verify deploy
|
|
1383
|
-
const startupTools = (0, tools_1.getToolsForChannel)((0,
|
|
1379
|
+
const startupTools = (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("teams"));
|
|
1384
1380
|
const toolNames = startupTools.map((t) => t.function.name);
|
|
1385
1381
|
(0, runtime_1.emitNervesEvent)({ level: "info", event: "channel.app_started", component: "channels", message: `Teams bot started on port ${port} with ${mode} (chunked streaming)`, meta: { port, mode, toolCount: toolNames.length, hasProactive: toolNames.includes("teams_send_message") } });
|
|
1386
1382
|
// --- Secondary bot (dual-bot support) ---
|
|
@@ -39,7 +39,7 @@ const fs = __importStar(require("fs"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const identity_1 = require("../heart/identity");
|
|
41
41
|
const runtime_1 = require("../nerves/runtime");
|
|
42
|
-
const
|
|
42
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
43
43
|
const pending_1 = require("../mind/pending");
|
|
44
44
|
// Canned reply; eventually agents should compose their own first-contact message
|
|
45
45
|
exports.STRANGER_AUTO_REPLY = "I'm sorry, I'm not allowed to talk to strangers";
|
|
@@ -205,7 +205,7 @@ function enforceTrustGate(input) {
|
|
|
205
205
|
}
|
|
206
206
|
const trustLevel = input.friend.trustLevel ?? "friend";
|
|
207
207
|
// Family and friend — always allow on open
|
|
208
|
-
if ((0,
|
|
208
|
+
if ((0, friends_1.isTrustedLevel)(trustLevel)) {
|
|
209
209
|
return { allowed: true };
|
|
210
210
|
}
|
|
211
211
|
const bundleRoot = input.bundleRoot ?? (0, identity_1.getAgentRoot)();
|
|
@@ -39,7 +39,7 @@ exports.voiceOutboundPhoneNumberForFriend = voiceOutboundPhoneNumberForFriend;
|
|
|
39
39
|
exports.placeTrustedFriendVoiceOutboundCall = placeTrustedFriendVoiceOutboundCall;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
-
const
|
|
42
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
43
43
|
const runtime_1 = require("../../nerves/runtime");
|
|
44
44
|
const phone_1 = require("./phone");
|
|
45
45
|
function readVoiceOutboundFriendRecordById(friendsDir, friendId) {
|
|
@@ -122,7 +122,7 @@ async function placeTrustedFriendVoiceOutboundCall(request) {
|
|
|
122
122
|
});
|
|
123
123
|
return { status: "blocked", detail: "voice call requires a known friend record" };
|
|
124
124
|
}
|
|
125
|
-
if (!(0,
|
|
125
|
+
if (!(0, friends_1.isTrustedLevel)(friend.trustLevel)) {
|
|
126
126
|
(0, runtime_1.emitNervesEvent)({
|
|
127
127
|
component: "senses",
|
|
128
128
|
event: "senses.voice_outbound_call_blocked",
|
|
@@ -65,9 +65,7 @@ const http = __importStar(require("http"));
|
|
|
65
65
|
const path = __importStar(require("path"));
|
|
66
66
|
const ws_1 = require("ws");
|
|
67
67
|
const context_1 = require("../../mind/context");
|
|
68
|
-
const
|
|
69
|
-
const resolver_1 = require("../../mind/friends/resolver");
|
|
70
|
-
const store_file_1 = require("../../mind/friends/store-file");
|
|
68
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
71
69
|
const identity_1 = require("../../heart/identity");
|
|
72
70
|
const mcp_manager_1 = require("../../repertoire/mcp-manager");
|
|
73
71
|
const tools_1 = require("../../repertoire/tools");
|
|
@@ -359,7 +357,7 @@ function voiceFriendId(options, from, callSid) {
|
|
|
359
357
|
}
|
|
360
358
|
async function resolveVoiceFriendContext(options, input) {
|
|
361
359
|
const agentRoot = options.agentRoot ?? (0, identity_1.getAgentRoot)(options.agentName);
|
|
362
|
-
const friendStore = new
|
|
360
|
+
const friendStore = new friends_1.FileFriendStore(path.join(agentRoot, "friends"));
|
|
363
361
|
const explicitFriendId = input.friendId?.trim();
|
|
364
362
|
if (explicitFriendId) {
|
|
365
363
|
const existing = await friendStore.get(explicitFriendId);
|
|
@@ -367,14 +365,14 @@ async function resolveVoiceFriendContext(options, input) {
|
|
|
367
365
|
return {
|
|
368
366
|
friendId: existing.id,
|
|
369
367
|
friendStore,
|
|
370
|
-
resolved: { friend: existing, channel: (0,
|
|
368
|
+
resolved: { friend: existing, channel: (0, friends_1.getChannelCapabilities)("voice") },
|
|
371
369
|
};
|
|
372
370
|
}
|
|
373
371
|
}
|
|
374
372
|
const remotePhone = (0, phone_1.normalizeTwilioE164PhoneNumber)(input.remotePhone);
|
|
375
373
|
const provider = remotePhone ? "imessage-handle" : "local";
|
|
376
374
|
const externalId = remotePhone || explicitFriendId || voiceFriendId(options, input.remotePhone ?? "", input.callSid);
|
|
377
|
-
const resolver = new
|
|
375
|
+
const resolver = new friends_1.FriendResolver(friendStore, {
|
|
378
376
|
provider,
|
|
379
377
|
externalId,
|
|
380
378
|
displayName: remotePhone || externalId,
|
|
@@ -1492,7 +1490,7 @@ function realtimeBootstrapInstructions(agentName, voiceStyle) {
|
|
|
1492
1490
|
].filter(Boolean).join(" ");
|
|
1493
1491
|
}
|
|
1494
1492
|
function realtimeBootstrapTools() {
|
|
1495
|
-
return realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0,
|
|
1493
|
+
return realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("voice")));
|
|
1496
1494
|
}
|
|
1497
1495
|
function timeoutAfter(ms) {
|
|
1498
1496
|
return new Promise((resolve) => {
|
|
@@ -1861,14 +1859,14 @@ class TwilioOpenAIRealtimeMediaStreamSession {
|
|
|
1861
1859
|
},
|
|
1862
1860
|
};
|
|
1863
1861
|
void this.refreshRealtimeToolsWithMcp(resolved);
|
|
1864
|
-
return (0, tools_1.getToolsForChannel)((0,
|
|
1862
|
+
return (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("voice"), resolved.friend.toolPreferences, resolved, undefined, undefined);
|
|
1865
1863
|
}
|
|
1866
1864
|
async refreshRealtimeToolsWithMcp(resolved) {
|
|
1867
1865
|
try {
|
|
1868
1866
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
1869
1867
|
if (!mcpManager || this.closed)
|
|
1870
1868
|
return;
|
|
1871
|
-
const tools = realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0,
|
|
1869
|
+
const tools = realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("voice"), resolved.friend.toolPreferences, resolved, undefined, mcpManager));
|
|
1872
1870
|
this.sendOpenAI({
|
|
1873
1871
|
type: "session.update",
|
|
1874
1872
|
session: {
|
|
@@ -2922,7 +2920,7 @@ class OpenAISipPhoneSession {
|
|
|
2922
2920
|
},
|
|
2923
2921
|
};
|
|
2924
2922
|
void this.refreshRealtimeToolsWithMcp(resolved);
|
|
2925
|
-
return (0, tools_1.getToolsForChannel)((0,
|
|
2923
|
+
return (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("voice"), resolved.friend.toolPreferences, resolved, undefined, undefined);
|
|
2926
2924
|
}
|
|
2927
2925
|
playRealtimeAudioCue(request) {
|
|
2928
2926
|
const source = request.source ?? "tone";
|
|
@@ -2971,7 +2969,7 @@ class OpenAISipPhoneSession {
|
|
|
2971
2969
|
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
2972
2970
|
if (!mcpManager || this.closed)
|
|
2973
2971
|
return;
|
|
2974
|
-
const tools = realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0,
|
|
2972
|
+
const tools = realtimeToolsFromChatTools((0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("voice"), resolved.friend.toolPreferences, resolved, undefined, mcpManager), OPENAI_SIP_UNSUPPORTED_TOOL_NAMES);
|
|
2975
2973
|
this.sendOpenAI({
|
|
2976
2974
|
type: "session.update",
|
|
2977
2975
|
session: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.670",
|
|
4
4
|
"main": "dist/heart/daemon/ouro-entry.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cli": "dist/heart/daemon/ouro-bot-entry.js",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
53
|
+
"@ouro.bot/friends": "git+https://github.com/ourostack/friends.git#0095316eed6876a27f36a26ddc47ad9da800a652",
|
|
53
54
|
"@azure/identity": "^4.13.0",
|
|
54
55
|
"@azure/storage-blob": "^12.31.0",
|
|
55
56
|
"@microsoft/teams.api": "2.0.11",
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Channel capabilities -- hardcoded const map keyed by channel identifier.
|
|
3
|
-
// Pure lookup, no I/O, cannot fail. Unknown channel gets minimal defaults.
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.channelToFacing = channelToFacing;
|
|
6
|
-
exports.getChannelCapabilities = getChannelCapabilities;
|
|
7
|
-
exports.isRemoteChannel = isRemoteChannel;
|
|
8
|
-
exports.getAlwaysOnSenseNames = getAlwaysOnSenseNames;
|
|
9
|
-
const runtime_1 = require("../../nerves/runtime");
|
|
10
|
-
const AGENT_FACING_CHANNELS = new Set(["inner", "mcp", "a2a"]);
|
|
11
|
-
function channelToFacing(channel) {
|
|
12
|
-
const facing = channel && AGENT_FACING_CHANNELS.has(channel) ? "agent" : "human";
|
|
13
|
-
(0, runtime_1.emitNervesEvent)({
|
|
14
|
-
component: "channels",
|
|
15
|
-
event: "channel.facing_lookup",
|
|
16
|
-
message: "channel facing lookup",
|
|
17
|
-
meta: { channel: channel ?? "undefined", facing },
|
|
18
|
-
});
|
|
19
|
-
return facing;
|
|
20
|
-
}
|
|
21
|
-
const CHANNEL_CAPABILITIES = {
|
|
22
|
-
cli: {
|
|
23
|
-
channel: "cli",
|
|
24
|
-
senseType: "local",
|
|
25
|
-
availableIntegrations: [],
|
|
26
|
-
supportsMarkdown: false,
|
|
27
|
-
supportsStreaming: true,
|
|
28
|
-
supportsRichCards: false,
|
|
29
|
-
maxMessageLength: Infinity,
|
|
30
|
-
},
|
|
31
|
-
teams: {
|
|
32
|
-
channel: "teams",
|
|
33
|
-
senseType: "closed",
|
|
34
|
-
availableIntegrations: ["ado", "graph", "github"],
|
|
35
|
-
supportsMarkdown: true,
|
|
36
|
-
supportsStreaming: true,
|
|
37
|
-
supportsRichCards: true,
|
|
38
|
-
maxMessageLength: Infinity,
|
|
39
|
-
},
|
|
40
|
-
bluebubbles: {
|
|
41
|
-
channel: "bluebubbles",
|
|
42
|
-
senseType: "open",
|
|
43
|
-
availableIntegrations: [],
|
|
44
|
-
supportsMarkdown: false,
|
|
45
|
-
supportsStreaming: false,
|
|
46
|
-
supportsRichCards: false,
|
|
47
|
-
maxMessageLength: Infinity,
|
|
48
|
-
},
|
|
49
|
-
mail: {
|
|
50
|
-
channel: "mail",
|
|
51
|
-
senseType: "open",
|
|
52
|
-
availableIntegrations: [],
|
|
53
|
-
supportsMarkdown: false,
|
|
54
|
-
supportsStreaming: false,
|
|
55
|
-
supportsRichCards: false,
|
|
56
|
-
maxMessageLength: Infinity,
|
|
57
|
-
},
|
|
58
|
-
voice: {
|
|
59
|
-
channel: "voice",
|
|
60
|
-
senseType: "local",
|
|
61
|
-
availableIntegrations: [],
|
|
62
|
-
supportsMarkdown: false,
|
|
63
|
-
supportsStreaming: true,
|
|
64
|
-
supportsRichCards: false,
|
|
65
|
-
maxMessageLength: Infinity,
|
|
66
|
-
},
|
|
67
|
-
a2a: {
|
|
68
|
-
channel: "a2a",
|
|
69
|
-
senseType: "open",
|
|
70
|
-
availableIntegrations: [],
|
|
71
|
-
supportsMarkdown: true,
|
|
72
|
-
supportsStreaming: false,
|
|
73
|
-
supportsRichCards: false,
|
|
74
|
-
maxMessageLength: Infinity,
|
|
75
|
-
},
|
|
76
|
-
inner: {
|
|
77
|
-
channel: "inner",
|
|
78
|
-
senseType: "internal",
|
|
79
|
-
availableIntegrations: [],
|
|
80
|
-
supportsMarkdown: false,
|
|
81
|
-
supportsStreaming: true,
|
|
82
|
-
supportsRichCards: false,
|
|
83
|
-
maxMessageLength: Infinity,
|
|
84
|
-
},
|
|
85
|
-
mcp: {
|
|
86
|
-
channel: "mcp",
|
|
87
|
-
senseType: "local",
|
|
88
|
-
availableIntegrations: [],
|
|
89
|
-
supportsMarkdown: true,
|
|
90
|
-
supportsStreaming: false,
|
|
91
|
-
supportsRichCards: false,
|
|
92
|
-
maxMessageLength: Infinity,
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
const DEFAULT_CAPABILITIES = {
|
|
96
|
-
channel: "cli",
|
|
97
|
-
senseType: "local",
|
|
98
|
-
availableIntegrations: [],
|
|
99
|
-
supportsMarkdown: false,
|
|
100
|
-
supportsStreaming: false,
|
|
101
|
-
supportsRichCards: false,
|
|
102
|
-
maxMessageLength: Infinity,
|
|
103
|
-
};
|
|
104
|
-
function getChannelCapabilities(channel) {
|
|
105
|
-
(0, runtime_1.emitNervesEvent)({
|
|
106
|
-
component: "channels",
|
|
107
|
-
event: "channel.capabilities_lookup",
|
|
108
|
-
message: "channel capabilities lookup",
|
|
109
|
-
meta: { channel },
|
|
110
|
-
});
|
|
111
|
-
return CHANNEL_CAPABILITIES[channel] ?? DEFAULT_CAPABILITIES;
|
|
112
|
-
}
|
|
113
|
-
/** Whether the channel is remote (open or closed) vs local/internal. */
|
|
114
|
-
function isRemoteChannel(capabilities) {
|
|
115
|
-
const senseType = capabilities?.senseType;
|
|
116
|
-
return senseType !== undefined && senseType !== "local" && senseType !== "internal";
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Returns channel names whose senseType is "open" or "closed" -- i.e. channels
|
|
120
|
-
* that are always-on (daemon-managed) rather than interactive or internal.
|
|
121
|
-
*/
|
|
122
|
-
function getAlwaysOnSenseNames() {
|
|
123
|
-
(0, runtime_1.emitNervesEvent)({
|
|
124
|
-
component: "channels",
|
|
125
|
-
event: "channel.always_on_lookup",
|
|
126
|
-
message: "always-on sense names lookup",
|
|
127
|
-
meta: {},
|
|
128
|
-
});
|
|
129
|
-
return Object.entries(CHANNEL_CAPABILITIES)
|
|
130
|
-
.filter(([, cap]) => cap.senseType === "open" || cap.senseType === "closed")
|
|
131
|
-
.map(([channel]) => channel);
|
|
132
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.upsertGroupContextParticipants = upsertGroupContextParticipants;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
const runtime_1 = require("../../nerves/runtime");
|
|
6
|
-
const CURRENT_SCHEMA_VERSION = 1;
|
|
7
|
-
function normalizeDisplayName(externalId, displayName) {
|
|
8
|
-
const trimmed = displayName?.trim();
|
|
9
|
-
return trimmed && trimmed.length > 0 ? trimmed : externalId;
|
|
10
|
-
}
|
|
11
|
-
function buildNameNotes(name, now) {
|
|
12
|
-
return name !== "Unknown"
|
|
13
|
-
? { name: { value: name, savedAt: now } }
|
|
14
|
-
: {};
|
|
15
|
-
}
|
|
16
|
-
function dedupeParticipants(participants) {
|
|
17
|
-
const deduped = new Map();
|
|
18
|
-
for (const participant of participants) {
|
|
19
|
-
const externalId = participant.externalId.trim();
|
|
20
|
-
if (!externalId)
|
|
21
|
-
continue;
|
|
22
|
-
const key = `${participant.provider}:${externalId}`;
|
|
23
|
-
if (!deduped.has(key)) {
|
|
24
|
-
deduped.set(key, {
|
|
25
|
-
...participant,
|
|
26
|
-
externalId,
|
|
27
|
-
displayName: participant.displayName?.trim() || undefined,
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return Array.from(deduped.values());
|
|
32
|
-
}
|
|
33
|
-
function createGroupExternalId(provider, groupExternalId, linkedAt) {
|
|
34
|
-
return {
|
|
35
|
-
provider,
|
|
36
|
-
externalId: groupExternalId,
|
|
37
|
-
linkedAt,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function shouldPromoteToAcquaintance(friend) {
|
|
41
|
-
return (friend.trustLevel ?? "stranger") === "stranger";
|
|
42
|
-
}
|
|
43
|
-
function createAcquaintanceRecord(participant, groupExternalId, linkedAt) {
|
|
44
|
-
const name = normalizeDisplayName(participant.externalId, participant.displayName);
|
|
45
|
-
return {
|
|
46
|
-
id: (0, node_crypto_1.randomUUID)(),
|
|
47
|
-
name,
|
|
48
|
-
role: "acquaintance",
|
|
49
|
-
trustLevel: "acquaintance",
|
|
50
|
-
connections: [],
|
|
51
|
-
externalIds: [
|
|
52
|
-
{
|
|
53
|
-
provider: participant.provider,
|
|
54
|
-
externalId: participant.externalId,
|
|
55
|
-
linkedAt,
|
|
56
|
-
},
|
|
57
|
-
createGroupExternalId(participant.provider, groupExternalId, linkedAt),
|
|
58
|
-
],
|
|
59
|
-
tenantMemberships: [],
|
|
60
|
-
toolPreferences: {},
|
|
61
|
-
notes: buildNameNotes(name, linkedAt),
|
|
62
|
-
totalTokens: 0,
|
|
63
|
-
createdAt: linkedAt,
|
|
64
|
-
updatedAt: linkedAt,
|
|
65
|
-
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
async function upsertGroupContextParticipants(input) {
|
|
69
|
-
(0, runtime_1.emitNervesEvent)({
|
|
70
|
-
component: "friends",
|
|
71
|
-
event: "friends.group_context_upsert_start",
|
|
72
|
-
message: "upserting shared-group participant context",
|
|
73
|
-
meta: {
|
|
74
|
-
participantCount: input.participants.length,
|
|
75
|
-
hasGroupExternalId: input.groupExternalId.trim().length > 0,
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
const groupExternalId = input.groupExternalId.trim();
|
|
79
|
-
if (!groupExternalId) {
|
|
80
|
-
return [];
|
|
81
|
-
}
|
|
82
|
-
const now = input.now ?? (() => new Date().toISOString());
|
|
83
|
-
const participants = dedupeParticipants(input.participants);
|
|
84
|
-
const results = [];
|
|
85
|
-
for (const participant of participants) {
|
|
86
|
-
const linkedAt = now();
|
|
87
|
-
const existing = await input.store.findByExternalId(participant.provider, participant.externalId);
|
|
88
|
-
if (!existing) {
|
|
89
|
-
const created = createAcquaintanceRecord(participant, groupExternalId, linkedAt);
|
|
90
|
-
await input.store.put(created.id, created);
|
|
91
|
-
results.push({
|
|
92
|
-
friendId: created.id,
|
|
93
|
-
name: created.name,
|
|
94
|
-
trustLevel: "acquaintance",
|
|
95
|
-
created: true,
|
|
96
|
-
updated: false,
|
|
97
|
-
addedGroupExternalId: true,
|
|
98
|
-
});
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
const hasGroupExternalId = existing.externalIds.some((externalId) => externalId.externalId === groupExternalId);
|
|
102
|
-
const promoteToAcquaintance = shouldPromoteToAcquaintance(existing);
|
|
103
|
-
const trustLevel = promoteToAcquaintance
|
|
104
|
-
? "acquaintance"
|
|
105
|
-
: existing.trustLevel;
|
|
106
|
-
const role = promoteToAcquaintance
|
|
107
|
-
? "acquaintance"
|
|
108
|
-
: existing.role;
|
|
109
|
-
const updatedExternalIds = hasGroupExternalId
|
|
110
|
-
? existing.externalIds
|
|
111
|
-
: [...existing.externalIds, createGroupExternalId(participant.provider, groupExternalId, linkedAt)];
|
|
112
|
-
const updated = promoteToAcquaintance || !hasGroupExternalId;
|
|
113
|
-
const record = updated
|
|
114
|
-
? {
|
|
115
|
-
...existing,
|
|
116
|
-
role,
|
|
117
|
-
trustLevel,
|
|
118
|
-
externalIds: updatedExternalIds,
|
|
119
|
-
updatedAt: linkedAt,
|
|
120
|
-
}
|
|
121
|
-
: existing;
|
|
122
|
-
if (updated) {
|
|
123
|
-
await input.store.put(record.id, record);
|
|
124
|
-
}
|
|
125
|
-
results.push({
|
|
126
|
-
friendId: record.id,
|
|
127
|
-
name: record.name,
|
|
128
|
-
trustLevel,
|
|
129
|
-
created: false,
|
|
130
|
-
updated,
|
|
131
|
-
addedGroupExternalId: !hasGroupExternalId,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
(0, runtime_1.emitNervesEvent)({
|
|
135
|
-
component: "friends",
|
|
136
|
-
event: "friends.group_context_upsert_end",
|
|
137
|
-
message: "upserted shared-group participant context",
|
|
138
|
-
meta: {
|
|
139
|
-
participantCount: participants.length,
|
|
140
|
-
updatedCount: results.filter((result) => result.created || result.updated).length,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
return results;
|
|
144
|
-
}
|