@indexnetwork/protocol 4.3.3-rc.308.1 → 4.3.4-rc.310.1
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/IMPLEMENTATION.md +2 -2
- package/dist/chat/chat.agent.d.ts +1 -1
- package/dist/chat/chat.agent.d.ts.map +1 -1
- package/dist/chat/chat.agent.js +12 -2
- package/dist/chat/chat.agent.js.map +1 -1
- package/dist/chat/chat.graph.d.ts +16 -0
- package/dist/chat/chat.graph.d.ts.map +1 -1
- package/dist/chat/chat.graph.js +5 -2
- package/dist/chat/chat.graph.js.map +1 -1
- package/dist/chat/chat.prompt.d.ts.map +1 -1
- package/dist/chat/chat.prompt.js +44 -37
- package/dist/chat/chat.prompt.js.map +1 -1
- package/dist/chat/chat.prompt.modules.js +8 -8
- package/dist/chat/chat.prompt.modules.js.map +1 -1
- package/dist/chat/chat.state.d.ts +7 -4
- package/dist/chat/chat.state.d.ts.map +1 -1
- package/dist/chat/chat.state.js +12 -3
- package/dist/chat/chat.state.js.map +1 -1
- package/dist/chat/chat.streamer.d.ts +5 -0
- package/dist/chat/chat.streamer.d.ts.map +1 -1
- package/dist/chat/chat.streamer.js +13 -4
- package/dist/chat/chat.streamer.js.map +1 -1
- package/dist/chat/tests/chat.graph.mocks.d.ts +1 -1
- package/dist/chat/tests/chat.graph.mocks.js +1 -1
- package/dist/chat/tests/chat.graph.mocks.js.map +1 -1
- package/dist/contact/contact.tools.js +6 -6
- package/dist/contact/contact.tools.js.map +1 -1
- package/dist/enrichment/enrichment.tools.d.ts.map +1 -1
- package/dist/enrichment/enrichment.tools.js +23 -26
- package/dist/enrichment/enrichment.tools.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integration/integration.tools.js +1 -1
- package/dist/integration/integration.tools.js.map +1 -1
- package/dist/intent/intent.graph.d.ts.map +1 -1
- package/dist/intent/intent.graph.js +9 -9
- package/dist/intent/intent.graph.js.map +1 -1
- package/dist/intent/intent.indexer.d.ts +1 -1
- package/dist/intent/intent.indexer.js +4 -4
- package/dist/intent/intent.indexer.js.map +1 -1
- package/dist/intent/intent.state.d.ts +8 -8
- package/dist/intent/intent.state.js +8 -8
- package/dist/intent/intent.state.js.map +1 -1
- package/dist/intent/intent.tools.d.ts.map +1 -1
- package/dist/intent/intent.tools.js +87 -70
- package/dist/intent/intent.tools.js.map +1 -1
- package/dist/maintenance/maintenance.graph.d.ts +2 -2
- package/dist/maintenance/maintenance.graph.d.ts.map +1 -1
- package/dist/maintenance/maintenance.graph.js.map +1 -1
- package/dist/mcp/mcp.server.d.ts +5 -7
- package/dist/mcp/mcp.server.d.ts.map +1 -1
- package/dist/mcp/mcp.server.js +11 -20
- package/dist/mcp/mcp.server.js.map +1 -1
- package/dist/negotiation/negotiation.tools.d.ts.map +1 -1
- package/dist/negotiation/negotiation.tools.js +11 -7
- package/dist/negotiation/negotiation.tools.js.map +1 -1
- package/dist/network/indexer/indexer.graph.d.ts +2 -2
- package/dist/network/indexer/indexer.graph.d.ts.map +1 -1
- package/dist/network/indexer/indexer.graph.js +13 -13
- package/dist/network/indexer/indexer.graph.js.map +1 -1
- package/dist/network/indexer/indexer.state.d.ts +3 -3
- package/dist/network/indexer/indexer.state.d.ts.map +1 -1
- package/dist/network/indexer/indexer.state.js +4 -4
- package/dist/network/indexer/indexer.state.js.map +1 -1
- package/dist/network/membership/membership.graph.d.ts +1 -1
- package/dist/network/membership/membership.graph.js +21 -21
- package/dist/network/membership/membership.graph.js.map +1 -1
- package/dist/network/membership/membership.state.d.ts +2 -2
- package/dist/network/membership/membership.state.js +2 -2
- package/dist/network/membership/membership.state.js.map +1 -1
- package/dist/network/network.graph.d.ts +1 -1
- package/dist/network/network.graph.d.ts.map +1 -1
- package/dist/network/network.graph.js +13 -13
- package/dist/network/network.graph.js.map +1 -1
- package/dist/network/network.state.d.ts +3 -3
- package/dist/network/network.state.d.ts.map +1 -1
- package/dist/network/network.state.js +3 -3
- package/dist/network/network.state.js.map +1 -1
- package/dist/network/network.tools.js +68 -68
- package/dist/network/network.tools.js.map +1 -1
- package/dist/opportunity/opportunity.discover.d.ts +1 -1
- package/dist/opportunity/opportunity.discover.d.ts.map +1 -1
- package/dist/opportunity/opportunity.discover.js +1 -1
- package/dist/opportunity/opportunity.discover.js.map +1 -1
- package/dist/opportunity/opportunity.graph.js +11 -11
- package/dist/opportunity/opportunity.graph.js.map +1 -1
- package/dist/opportunity/opportunity.introducer.d.ts +3 -3
- package/dist/opportunity/opportunity.introducer.d.ts.map +1 -1
- package/dist/opportunity/opportunity.introducer.js +2 -2
- package/dist/opportunity/opportunity.introducer.js.map +1 -1
- package/dist/opportunity/opportunity.state.d.ts +1 -1
- package/dist/opportunity/opportunity.state.js +1 -1
- package/dist/opportunity/opportunity.state.js.map +1 -1
- package/dist/opportunity/opportunity.tools.d.ts.map +1 -1
- package/dist/opportunity/opportunity.tools.js +43 -47
- package/dist/opportunity/opportunity.tools.js.map +1 -1
- package/dist/premise/premise.indexer.d.ts +1 -1
- package/dist/premise/premise.indexer.js +5 -5
- package/dist/premise/premise.indexer.js.map +1 -1
- package/dist/questioner/questioner.tools.d.ts +1 -1
- package/dist/questioner/questioner.tools.d.ts.map +1 -1
- package/dist/questioner/questioner.tools.js +5 -6
- package/dist/questioner/questioner.tools.js.map +1 -1
- package/dist/shared/agent/tool.factory.d.ts +1 -1
- package/dist/shared/agent/tool.factory.d.ts.map +1 -1
- package/dist/shared/agent/tool.factory.js +10 -11
- package/dist/shared/agent/tool.factory.js.map +1 -1
- package/dist/shared/agent/tool.helpers.d.ts +6 -6
- package/dist/shared/agent/tool.helpers.d.ts.map +1 -1
- package/dist/shared/agent/tool.helpers.js +2 -2
- package/dist/shared/agent/tool.helpers.js.map +1 -1
- package/dist/shared/agent/tool.registry.js +1 -1
- package/dist/shared/agent/tool.registry.js.map +1 -1
- package/dist/shared/agent/tool.scope.d.ts +12 -0
- package/dist/shared/agent/tool.scope.d.ts.map +1 -1
- package/dist/shared/agent/tool.scope.js +14 -0
- package/dist/shared/agent/tool.scope.js.map +1 -1
- package/dist/shared/agent/utility.tools.js +20 -20
- package/dist/shared/agent/utility.tools.js.map +1 -1
- package/dist/shared/interfaces/auth.interface.d.ts +3 -3
- package/dist/shared/interfaces/auth.interface.js.map +1 -1
- package/dist/shared/interfaces/database.interface.d.ts +59 -59
- package/dist/shared/interfaces/database.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/database.interface.js.map +1 -1
- package/dist/shared/interfaces/discovery-run.interface.d.ts +1 -1
- package/dist/shared/interfaces/discovery-run.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/discovery-run.interface.js.map +1 -1
- package/dist/shared/interfaces/embedder.interface.d.ts +2 -2
- package/dist/shared/interfaces/embedder.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/embedder.interface.js.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/enrichment-run.interface.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent.tools.d.ts","sourceRoot":"/","sources":["intent/intent.tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"intent.tools.d.ts","sourceRoot":"/","sources":["intent/intent.tools.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AA8D5E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,qDAssBvE"}
|
|
@@ -4,6 +4,7 @@ import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
|
4
4
|
import { traceGraph } from "../shared/observability/trace.js";
|
|
5
5
|
import { success, error, UUID_REGEX } from "../shared/agent/tool.helpers.js";
|
|
6
6
|
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
7
|
+
import { deriveAllowedNetworkIds, focusedNetworkId, focusedNetworkLabel } from "../shared/agent/tool.scope.js";
|
|
7
8
|
const logger = protocolLogger("ChatTools:Intent");
|
|
8
9
|
/**
|
|
9
10
|
* Sanitize JSON string for use inside a markdown code fence (```). Escapes backticks
|
|
@@ -12,13 +13,14 @@ const logger = protocolLogger("ChatTools:Intent");
|
|
|
12
13
|
function sanitizeJsonForCodeFence(json) {
|
|
13
14
|
return json.replace(/`/g, "\\u0060");
|
|
14
15
|
}
|
|
15
|
-
/** When context is
|
|
16
|
+
/** When context is network-scoped, verifies the caller is still a member of that index. Returns error message or null. */
|
|
16
17
|
async function ensureScopedMembership(context, systemDb) {
|
|
17
|
-
|
|
18
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
19
|
+
if (!scopedNetworkId)
|
|
18
20
|
return null;
|
|
19
|
-
const isMember = await systemDb.isNetworkMember(
|
|
21
|
+
const isMember = await systemDb.isNetworkMember(scopedNetworkId, context.userId);
|
|
20
22
|
if (!isMember) {
|
|
21
|
-
return `This chat is scoped to ${context
|
|
23
|
+
return `This chat is scoped to ${focusedNetworkLabel(context)}. You are no longer a member of this community.`;
|
|
22
24
|
}
|
|
23
25
|
return null;
|
|
24
26
|
}
|
|
@@ -60,17 +62,17 @@ export function createIntentTools(defineTool, deps) {
|
|
|
60
62
|
description: "Retrieves intents (signals of interest/need, e.g. 'Looking for a React developer in Berlin'). " +
|
|
61
63
|
"Intents are the core unit of discovery — they represent what users are seeking and drive semantic matching for opportunities.\n\n" +
|
|
62
64
|
"**Usage modes:**\n" +
|
|
63
|
-
"- No parameters: returns the **caller's own** active intents. In an
|
|
65
|
+
"- No parameters: returns the **caller's own** active intents. In an network-scoped chat the result is clamped to the reachable networks (the bound network plus the user's personal network). In an unscoped chat the result spans all of the user's active intents. There is no implicit default to the scoped network — to browse the bound community's intents, pass `networkId` explicitly.\n" +
|
|
64
66
|
"- With networkId: returns **all members'** intents in that index (community browse path). Add userId to filter to one member.\n" +
|
|
65
|
-
"- With userId in an
|
|
66
|
-
"- With userId in an unscoped chat: only works for the current user (cannot read another user's global intents without an
|
|
67
|
-
"**Workflow:** To explore what members of
|
|
67
|
+
"- With userId in an network-scoped chat: reads that member's intents in the bound network. The target user must be a member of that index.\n" +
|
|
68
|
+
"- With userId in an unscoped chat: only works for the current user (cannot read another user's global intents without an network scope).\n\n" +
|
|
69
|
+
"**Workflow:** To explore what members of a network are looking for, first call read_network_memberships(networkId) to list members, " +
|
|
68
70
|
"then read_intents(networkId) to see all intents in that community. " +
|
|
69
71
|
"Each intent includes: id, description (payload), summary, confidence (0-1), inferenceType (explicit/implicit), status, and linked indexes.\n\n" +
|
|
70
72
|
"**Returns:** Paginated list of intents with count. Use the intent IDs in subsequent calls to update_intent, delete_intent, or create_intent_index.",
|
|
71
73
|
querySchema: z.object({
|
|
72
|
-
networkId: z.string().optional().describe("
|
|
73
|
-
userId: z.string().optional().describe("User ID — filters to this user's intents. In an
|
|
74
|
+
networkId: z.string().optional().describe("Network UUID — filters intents to this network (community browse path: returns all members' intents). There is no implicit default in network-scoped chats; omit to get caller-owned intents across the reachable networks, or pass the scoped network UUID to browse community members. Get network IDs from read_networks."),
|
|
75
|
+
userId: z.string().optional().describe("User ID — filters to this user's intents. In an network-scoped chat, this reads that member's intents in the bound network (no networkId required). In an unscoped chat, only the current user is allowed without networkId; cross-user reads require an network scope. Omit for caller-owned intents."),
|
|
74
76
|
limit: z.number().int().min(1).max(100).optional().describe("Page size (1-100). Defaults to returning all results if omitted."),
|
|
75
77
|
page: z.number().int().min(1).optional().describe("Page number (1-based). Only used when limit is also provided."),
|
|
76
78
|
}),
|
|
@@ -79,6 +81,8 @@ export function createIntentTools(defineTool, deps) {
|
|
|
79
81
|
if (scopeErr)
|
|
80
82
|
return error(scopeErr);
|
|
81
83
|
// Distinguish "explicit network browse" from "implicit scope-aware read"
|
|
84
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
85
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
82
86
|
const explicitNetworkId = query.networkId?.trim();
|
|
83
87
|
const explicitUserId = query.userId?.trim();
|
|
84
88
|
// Validate explicit networkId format
|
|
@@ -86,25 +90,24 @@ export function createIntentTools(defineTool, deps) {
|
|
|
86
90
|
return error("Invalid network ID format.");
|
|
87
91
|
}
|
|
88
92
|
// Strict scope enforcement: in a scoped chat, the only allowed explicit
|
|
89
|
-
// networkId is
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const isInScopedIndex = await deps.systemDb.isNetworkMember(context.networkId, explicitUserId);
|
|
93
|
+
// networkId is the envelope's scoped network. Cross-network browse must
|
|
94
|
+
// happen in a separate unscoped chat or a chat scoped to that network.
|
|
95
|
+
if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {
|
|
96
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intents from this community.`);
|
|
97
|
+
}
|
|
98
|
+
// Cross-user read in scoped chat: target user must be a member of the scoped network
|
|
99
|
+
if (scopedNetworkId && explicitUserId && explicitUserId !== context.userId) {
|
|
100
|
+
const isInScopedIndex = await deps.systemDb.isNetworkMember(scopedNetworkId, explicitUserId);
|
|
98
101
|
if (!isInScopedIndex) {
|
|
99
|
-
return error(`This chat is scoped to ${
|
|
102
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intents from members of this community.`);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
|
-
// Cross-user global read is disallowed without an
|
|
103
|
-
if (!explicitNetworkId && !
|
|
105
|
+
// Cross-user global read is disallowed without an network scope
|
|
106
|
+
if (!explicitNetworkId && !scopedNetworkId && explicitUserId && explicitUserId !== context.userId) {
|
|
104
107
|
return error("Cannot read another user's global intents. Use networkId to scope to a shared network.");
|
|
105
108
|
}
|
|
106
109
|
// Membership check for explicit cross-network reads in unscoped chats
|
|
107
|
-
if (!
|
|
110
|
+
if (!scopedNetworkId && explicitNetworkId) {
|
|
108
111
|
const callerIsMember = await deps.systemDb.isNetworkMember(explicitNetworkId, context.userId);
|
|
109
112
|
if (!callerIsMember) {
|
|
110
113
|
return error("You can only read intents from indexes you are a member of.");
|
|
@@ -126,10 +129,10 @@ export function createIntentTools(defineTool, deps) {
|
|
|
126
129
|
if (explicitUserId)
|
|
127
130
|
graphInput.queryUserId = explicitUserId;
|
|
128
131
|
}
|
|
129
|
-
else if (explicitUserId &&
|
|
132
|
+
else if (explicitUserId && scopedNetworkId) {
|
|
130
133
|
// Scoped chat + userId: implicit network is the chat's bound network.
|
|
131
134
|
// Membership of the target user was verified above.
|
|
132
|
-
graphInput.networkId =
|
|
135
|
+
graphInput.networkId = scopedNetworkId;
|
|
133
136
|
graphInput.queryUserId = explicitUserId;
|
|
134
137
|
}
|
|
135
138
|
else if (explicitUserId) {
|
|
@@ -137,9 +140,13 @@ export function createIntentTools(defineTool, deps) {
|
|
|
137
140
|
graphInput.queryUserId = explicitUserId;
|
|
138
141
|
graphInput.allUserIntents = true;
|
|
139
142
|
}
|
|
140
|
-
else if (
|
|
141
|
-
// Scoped chat, implicit read: caller-only across reachable
|
|
142
|
-
graphInput.indexScope =
|
|
143
|
+
else if (scopedNetworkId) {
|
|
144
|
+
// Scoped chat, implicit read: caller-only across reachable networks.
|
|
145
|
+
graphInput.indexScope = deriveAllowedNetworkIds({
|
|
146
|
+
memberships: context.userNetworks,
|
|
147
|
+
scopeType: 'network',
|
|
148
|
+
scopeId: scopedNetworkId,
|
|
149
|
+
});
|
|
143
150
|
}
|
|
144
151
|
else {
|
|
145
152
|
// Unscoped, implicit read: caller's global intents.
|
|
@@ -199,7 +206,7 @@ export function createIntentTools(defineTool, deps) {
|
|
|
199
206
|
"to create_user_context are handled by that tool directly — do not scrape first.",
|
|
200
207
|
querySchema: z.object({
|
|
201
208
|
description: z.string().describe("A clear, specific description of what the user is looking for. Should be concept-based, not a raw URL. If the user shared a URL, scrape it first with scrape_url and pass the synthesized content here. Vague descriptions will be rejected — include what kind, what for, and/or timeframe."),
|
|
202
|
-
networkId: z.string().optional().describe("
|
|
209
|
+
networkId: z.string().optional().describe("Network UUID to link the intent to upon creation. Defaults to the scoped network in network-scoped chats. Get network IDs from read_networks. If omitted, the system auto-assigns to relevant networks based on their prompts."),
|
|
203
210
|
autoApprove: z.boolean().optional().describe("When true, automatically persists all verified intents without returning proposal cards for manual approval. MCP agents SHOULD set this to true since there is no UI for card-based approval. Web chat agents should omit or set to false to get interactive proposal cards."),
|
|
204
211
|
}),
|
|
205
212
|
handler: async ({ context, query }) => {
|
|
@@ -209,13 +216,15 @@ export function createIntentTools(defineTool, deps) {
|
|
|
209
216
|
if (!query.description?.trim()) {
|
|
210
217
|
return error("Description is required.");
|
|
211
218
|
}
|
|
219
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
220
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
212
221
|
// Strict scope enforcement
|
|
213
|
-
if (
|
|
214
|
-
return error(`This chat is scoped to ${
|
|
222
|
+
if (scopedNetworkId && query.networkId?.trim() && query.networkId.trim() !== scopedNetworkId) {
|
|
223
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only create intents in this community.`);
|
|
215
224
|
}
|
|
216
|
-
const effectiveIndexId =
|
|
217
|
-
const scopeEnvelope =
|
|
218
|
-
? { scopeType:
|
|
225
|
+
const effectiveIndexId = scopedNetworkId || query.networkId?.trim() || undefined;
|
|
226
|
+
const scopeEnvelope = scopedNetworkId
|
|
227
|
+
? { scopeType: 'network', scopeId: scopedNetworkId }
|
|
219
228
|
: {};
|
|
220
229
|
// Fetch profile (the intent graph needs it for inference)
|
|
221
230
|
const _profileGraphStart1 = Date.now();
|
|
@@ -375,12 +384,14 @@ export function createIntentTools(defineTool, deps) {
|
|
|
375
384
|
if (intent.archivedAt) {
|
|
376
385
|
return error("This intent is archived and cannot be updated. Create a new intent instead.");
|
|
377
386
|
}
|
|
378
|
-
|
|
379
|
-
|
|
387
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
388
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
389
|
+
// Strict scope enforcement: when chat is network-scoped, verify intent is linked to that index
|
|
390
|
+
if (scopedNetworkId) {
|
|
380
391
|
const db = deps.userDb;
|
|
381
392
|
const intentNetworks = await db.getNetworkIdsForIntent(intentId);
|
|
382
|
-
if (!intentNetworks.includes(
|
|
383
|
-
return error(`This chat is scoped to ${
|
|
393
|
+
if (!intentNetworks.includes(scopedNetworkId)) {
|
|
394
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only update intents linked to this community.`);
|
|
384
395
|
}
|
|
385
396
|
}
|
|
386
397
|
const _profileGraphStart2 = Date.now();
|
|
@@ -396,8 +407,7 @@ export function createIntentTools(defineTool, deps) {
|
|
|
396
407
|
operationMode: 'update',
|
|
397
408
|
inputContent: query.description,
|
|
398
409
|
targetIntentIds: [intentId],
|
|
399
|
-
...(
|
|
400
|
-
...(context.scopeType && context.scopeId ? { scopeType: context.scopeType, scopeId: context.scopeId } : {}),
|
|
410
|
+
...(scopedNetworkId && { networkId: scopedNetworkId, scopeType: 'network', scopeId: scopedNetworkId }),
|
|
401
411
|
}));
|
|
402
412
|
const _intentGraphMs2 = Date.now() - _intentGraphStart2;
|
|
403
413
|
if (result.executionResults?.some((r) => !r.success)) {
|
|
@@ -440,12 +450,14 @@ export function createIntentTools(defineTool, deps) {
|
|
|
440
450
|
if (!intent || intent.userId !== context.userId) {
|
|
441
451
|
return error("Intent not found or you can only delete your own intents.");
|
|
442
452
|
}
|
|
443
|
-
|
|
444
|
-
|
|
453
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
454
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
455
|
+
// Strict scope enforcement: when chat is network-scoped, verify intent is linked to that index
|
|
456
|
+
if (scopedNetworkId) {
|
|
445
457
|
const db = deps.userDb;
|
|
446
458
|
const intentNetworks = await db.getNetworkIdsForIntent(intentId);
|
|
447
|
-
if (!intentNetworks.includes(
|
|
448
|
-
return error(`This chat is scoped to ${
|
|
459
|
+
if (!intentNetworks.includes(scopedNetworkId)) {
|
|
460
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only delete intents linked to this community.`);
|
|
449
461
|
}
|
|
450
462
|
}
|
|
451
463
|
const _deleteIntentGraphStart = Date.now();
|
|
@@ -454,8 +466,7 @@ export function createIntentTools(defineTool, deps) {
|
|
|
454
466
|
userProfile: "",
|
|
455
467
|
operationMode: 'delete',
|
|
456
468
|
targetIntentIds: [intentId],
|
|
457
|
-
...(
|
|
458
|
-
...(context.scopeType && context.scopeId ? { scopeType: context.scopeType, scopeId: context.scopeId } : {}),
|
|
469
|
+
...(scopedNetworkId && { networkId: scopedNetworkId, scopeType: 'network', scopeId: scopedNetworkId }),
|
|
459
470
|
}));
|
|
460
471
|
const _deleteIntentGraphMs = Date.now() - _deleteIntentGraphStart;
|
|
461
472
|
if (result.executionResults?.some((r) => !r.success)) {
|
|
@@ -472,27 +483,29 @@ export function createIntentTools(defineTool, deps) {
|
|
|
472
483
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
473
484
|
const createIntentIndex = defineTool({
|
|
474
485
|
name: "create_intent_index",
|
|
475
|
-
description: "Manually links an intent to
|
|
486
|
+
description: "Manually links an intent to a network (community), making it visible to other members and eligible for opportunity discovery within that index. " +
|
|
476
487
|
"Normally intents are auto-assigned to relevant indexes on creation, but use this to explicitly add an intent to an additional index.\n\n" +
|
|
477
488
|
"**When to use:** When the user wants to share an existing intent with a specific community they belong to, " +
|
|
478
|
-
"or when auto-assignment missed
|
|
489
|
+
"or when auto-assignment missed a network the user considers relevant.\n\n" +
|
|
479
490
|
"**Returns:** Confirmation that the link was created. The intent will now appear in that index's intent list and participate in discovery within that community.",
|
|
480
491
|
querySchema: z.object({
|
|
481
492
|
intentId: z.string().describe("The UUID of the intent to link. Get this from read_intents results."),
|
|
482
|
-
networkId: z.string().optional().describe("The UUID of the
|
|
493
|
+
networkId: z.string().optional().describe("The UUID of the network to link the intent to. Get this from read_networks. Defaults to the scoped network in network-scoped chats."),
|
|
483
494
|
}),
|
|
484
495
|
handler: async ({ context, query }) => {
|
|
485
496
|
const scopeErr = await ensureScopedMembership(context, deps.systemDb);
|
|
486
497
|
if (scopeErr)
|
|
487
498
|
return error(scopeErr);
|
|
499
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
500
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
488
501
|
const intentId = query.intentId?.trim() ?? "";
|
|
489
|
-
const networkId = query.networkId?.trim() ||
|
|
502
|
+
const networkId = query.networkId?.trim() || scopedNetworkId || "";
|
|
490
503
|
if (!UUID_REGEX.test(intentId) || !UUID_REGEX.test(networkId)) {
|
|
491
504
|
return error("Invalid ID format. Both must be UUIDs.");
|
|
492
505
|
}
|
|
493
|
-
// Strict scope enforcement: when chat is
|
|
494
|
-
if (
|
|
495
|
-
return error(`This chat is scoped to ${
|
|
506
|
+
// Strict scope enforcement: when chat is network-scoped, only allow linking to that index
|
|
507
|
+
if (scopedNetworkId && networkId !== scopedNetworkId) {
|
|
508
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only link intents to this community.`);
|
|
496
509
|
}
|
|
497
510
|
const _createIntentIndexGraphStart = Date.now();
|
|
498
511
|
const result = await traceGraph("intent_network", () => invokeWithAbortSignal(graphs.intentIndex, {
|
|
@@ -524,21 +537,23 @@ export function createIntentTools(defineTool, deps) {
|
|
|
524
537
|
"**Usage modes:**\n" +
|
|
525
538
|
"- With networkId: lists all intents linked to that index. Add userId to filter to one member's intents in that index.\n" +
|
|
526
539
|
"- With intentId + networkId: checks whether a specific intent is linked to a specific index.\n" +
|
|
527
|
-
"- intentId alone requires a networkId (the system won't reveal all
|
|
540
|
+
"- intentId alone requires a networkId (the system won't reveal all networks an intent is in).\n\n" +
|
|
528
541
|
"**When to use:** To audit which intents are active in a community, verify an intent's index assignment before unlinking, " +
|
|
529
542
|
"or check if a newly created intent was auto-assigned to the expected index.\n\n" +
|
|
530
|
-
"**Returns:** List of intent-
|
|
543
|
+
"**Returns:** List of intent-network links with relevancy scores (0-1, how well the intent fits the network's purpose).",
|
|
531
544
|
querySchema: z.object({
|
|
532
545
|
intentId: z.string().optional().describe("Intent UUID — check if this specific intent is linked to the specified index. Must be combined with networkId."),
|
|
533
|
-
networkId: z.string().optional().describe("
|
|
546
|
+
networkId: z.string().optional().describe("Network UUID — list all intents linked to this network. Get this from read_networks. Defaults to scoped network in network-scoped chats."),
|
|
534
547
|
userId: z.string().optional().describe("Filter results to this user's intents within the specified index. Omit to see all members' intents."),
|
|
535
548
|
}),
|
|
536
549
|
handler: async ({ context, query }) => {
|
|
537
550
|
const scopeErr = await ensureScopedMembership(context, deps.systemDb);
|
|
538
551
|
if (scopeErr)
|
|
539
552
|
return error(scopeErr);
|
|
553
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
554
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
540
555
|
const intentId = query.intentId?.trim() || undefined;
|
|
541
|
-
let networkId = query.networkId?.trim() ||
|
|
556
|
+
let networkId = query.networkId?.trim() || scopedNetworkId || undefined;
|
|
542
557
|
const queryUserId = query.userId?.trim() || undefined;
|
|
543
558
|
if (intentId && !UUID_REGEX.test(intentId)) {
|
|
544
559
|
return error("Invalid intent ID format.");
|
|
@@ -549,18 +564,18 @@ export function createIntentTools(defineTool, deps) {
|
|
|
549
564
|
if (!intentId && !networkId) {
|
|
550
565
|
return error("Provide networkId or intentId.");
|
|
551
566
|
}
|
|
552
|
-
// Strict scope enforcement: when chat is
|
|
553
|
-
if (
|
|
554
|
-
return error(`This chat is scoped to ${
|
|
567
|
+
// Strict scope enforcement: when chat is network-scoped, only allow querying that index
|
|
568
|
+
if (scopedNetworkId && networkId && networkId !== scopedNetworkId) {
|
|
569
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only read intent links from this community.`);
|
|
555
570
|
}
|
|
556
571
|
// When only intentId is provided, enforce scope - don't reveal all linked indexes
|
|
557
572
|
if (intentId && !networkId) {
|
|
558
|
-
if (
|
|
559
|
-
// When scoped, only check if intent is linked to the scoped
|
|
560
|
-
networkId =
|
|
573
|
+
if (scopedNetworkId) {
|
|
574
|
+
// When scoped, only check if intent is linked to the scoped network
|
|
575
|
+
networkId = scopedNetworkId;
|
|
561
576
|
}
|
|
562
577
|
else {
|
|
563
|
-
// When unscoped, still don't reveal all
|
|
578
|
+
// When unscoped, still don't reveal all networks - require explicit networkId
|
|
564
579
|
return error("Please provide a networkId to check if the intent is linked to a specific network. Listing all linked networks is not supported.");
|
|
565
580
|
}
|
|
566
581
|
}
|
|
@@ -584,27 +599,29 @@ export function createIntentTools(defineTool, deps) {
|
|
|
584
599
|
});
|
|
585
600
|
const deleteIntentIndex = defineTool({
|
|
586
601
|
name: "delete_intent_index",
|
|
587
|
-
description: "Removes the link between an intent and
|
|
602
|
+
description: "Removes the link between an intent and a network. The intent itself is NOT deleted — it just stops being visible in that community " +
|
|
588
603
|
"and no longer participates in opportunity discovery within that index. The intent may still be linked to other indexes.\n\n" +
|
|
589
604
|
"**When to use:** When the user wants to withdraw an intent from a specific community without archiving it entirely. " +
|
|
590
605
|
"Use read_intent_indexes first to verify the link exists.\n\n" +
|
|
591
606
|
"**Returns:** Confirmation that the link was removed. To fully remove an intent, use delete_intent instead.",
|
|
592
607
|
querySchema: z.object({
|
|
593
608
|
intentId: z.string().describe("The UUID of the intent to unlink. Get this from read_intents or read_intent_indexes."),
|
|
594
|
-
networkId: z.string().optional().describe("The UUID of the
|
|
609
|
+
networkId: z.string().optional().describe("The UUID of the network to unlink from. Get this from read_networks. Defaults to the scoped network in network-scoped chats."),
|
|
595
610
|
}),
|
|
596
611
|
handler: async ({ context, query }) => {
|
|
597
612
|
const scopeErr = await ensureScopedMembership(context, deps.systemDb);
|
|
598
613
|
if (scopeErr)
|
|
599
614
|
return error(scopeErr);
|
|
615
|
+
const scopedNetworkId = focusedNetworkId(context);
|
|
616
|
+
const scopedIndexLabel = focusedNetworkLabel(context);
|
|
600
617
|
const intentId = query.intentId?.trim() ?? "";
|
|
601
|
-
const networkId = query.networkId?.trim() ||
|
|
618
|
+
const networkId = query.networkId?.trim() || scopedNetworkId || "";
|
|
602
619
|
if (!UUID_REGEX.test(intentId) || !UUID_REGEX.test(networkId)) {
|
|
603
620
|
return error("Invalid ID format. Both must be UUIDs.");
|
|
604
621
|
}
|
|
605
|
-
// Strict scope enforcement: when chat is
|
|
606
|
-
if (
|
|
607
|
-
return error(`This chat is scoped to ${
|
|
622
|
+
// Strict scope enforcement: when chat is network-scoped, only allow unlinking from that index
|
|
623
|
+
if (scopedNetworkId && networkId !== scopedNetworkId) {
|
|
624
|
+
return error(`This chat is scoped to ${scopedIndexLabel}. You can only unlink intents from this community.`);
|
|
608
625
|
}
|
|
609
626
|
const _deleteIntentIndexGraphStart = Date.now();
|
|
610
627
|
const result = await traceGraph("intent_network", () => invokeWithAbortSignal(graphs.intentIndex, {
|