@indexnetwork/protocol 6.3.0-rc.382.1 → 6.5.0-rc.384.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/CHANGELOG.md CHANGED
@@ -13,6 +13,7 @@ See [STABILITY.md](./STABILITY.md) for the public-contract and tier definitions.
13
13
  ## [Unreleased]
14
14
 
15
15
  ### Added
16
+ - Read-only `reporter` chat persona for Agent-surface activity reporting (IND-476 PR1), with an exact positive allowlist, self-only narrowing, aggregate-only opportunity reporting, grounded `report_agent_activity` metrics, and a public briefing kickoff marker. Mutation, discovery, negotiation, memory, question-answering, scraping, and counterparty identity surfaces remain unavailable.
16
17
  - Restricted `signal` chat persona for the main-web cutover (IND-449), built on the existing persona-neutral runtime with a custom signals/profile prompt, an exact positive allowlist, proposal hallucination recovery, and the discovery-coupled create-intent callback disabled. Signal-specific wrappers clamp focused intent/network reads to owned active intents and current memberships, prohibit other-user membership enumeration, and validate live membership before forwarding network-scoped proposals. Shared orchestrator, MCP, and direct-tool registries are unchanged.
17
18
  - `RawEvidenceOwnerAnswer` is now re-exported from the root barrel alongside the other Lens C negotiation-evidence segment types, so API-side projections (IND-465 slice 2) can type owner-answer evidence without deep imports. Type-only, additive; no runtime change.
18
19
  - Default-off `POOL_QUESTIONS_VISIT_TRIGGER` accessor plus the shared 6h `POOL_VISIT_MINING_DEBOUNCE_MS` debounce window for visit-triggered pool mining: the flag only adds a *when* for the existing mining hook — every mining/question gate (`POOL_QUESTIONS_MODE`, k-anonymity floor, VoI threshold, per-intent budgets, freshness fingerprints, push budgets) applies unchanged (IND-439 visibility-audit slice).
@@ -0,0 +1,32 @@
1
+ import { type ChatTools, type ResolvedToolContext, type ToolContext } from "../shared/agent/tool.factory.js";
2
+ import type { UserDatabase } from "../shared/interfaces/database.interface.js";
3
+ import type { ChatPersonaConfig } from "./chat.persona.js";
4
+ /** Public kickoff marker used by the Agent surface to request its opening briefing. */
5
+ export { REPORTER_BRIEFING_KICKOFF } from "./reporter.prompt.js";
6
+ /** Stable persona id persisted for read-only Agent reporting sessions. */
7
+ export declare const REPORTER_PERSONA_ID = "reporter";
8
+ /**
9
+ * Exact positive allowlist for the reporter persona. New shared tools remain
10
+ * unavailable until they are explicitly reviewed here.
11
+ */
12
+ export declare const REPORTER_TOOL_NAMES: readonly ["read_intents", "search_intents", "read_user_contexts", "preview_user_context", "read_premises", "read_networks", "read_network_memberships", "read_pending_questions", "list_opportunities", "report_agent_activity"];
13
+ interface ReporterToolBoundary {
14
+ context: ResolvedToolContext;
15
+ userDb: UserDatabase;
16
+ findPendingQuestions?: ToolContext["findPendingQuestions"];
17
+ }
18
+ /** Filters a shared registry through the reporter's positive allowlist. */
19
+ export declare function filterReporterTools<T extends {
20
+ name: string;
21
+ }>(tools: T[]): T[];
22
+ /**
23
+ * Replaces shared tools whose normal modes can enumerate other users with
24
+ * reporter-safe, self-only read contracts. Opportunity listing is deliberately
25
+ * aggregate-only: it never returns a counterpart name, row, or explanation.
26
+ */
27
+ export declare function narrowReporterTools(allowed: ChatTools, boundary: ReporterToolBoundary): ChatTools;
28
+ /** Creates the reporter's context-bound, allowlisted toolset. */
29
+ export declare function createReporterTools(deps: ToolContext, preResolvedContext?: ResolvedToolContext): Promise<ChatTools>;
30
+ /** Restricted read-only Agent reporter persona. */
31
+ export declare const REPORTER_PERSONA: ChatPersonaConfig;
32
+ //# sourceMappingURL=reporter.persona.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.persona.d.ts","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9H,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,uFAAuF;AACvF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,kOAWtB,CAAC;AAIX,UAAU,oBAAoB;IAC5B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,oBAAoB,CAAC,EAAE,WAAW,CAAC,sBAAsB,CAAC,CAAC;CAC5D;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE/E;AAMD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,oBAAoB,GAC7B,SAAS,CAkKX;AAED,iEAAiE;AACjE,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,mBAAmB,GACvC,OAAO,CAAC,SAAS,CAAC,CAwBpB;AAED,mDAAmD;AACnD,eAAO,MAAM,gBAAgB,EAAE,iBAQ9B,CAAC"}
@@ -0,0 +1,205 @@
1
+ import { tool } from "@langchain/core/tools";
2
+ import { z } from "zod";
3
+ import { createChatTools } from "../shared/agent/tool.factory.js";
4
+ import { error, resolveChatContext, success } from "../shared/agent/tool.helpers.js";
5
+ import { focusedNetworkId, scopeFromNetworkId } from "../shared/agent/tool.scope.js";
6
+ import { buildReporterSystemContent } from "./reporter.prompt.js";
7
+ /** Public kickoff marker used by the Agent surface to request its opening briefing. */
8
+ export { REPORTER_BRIEFING_KICKOFF } from "./reporter.prompt.js";
9
+ /** Stable persona id persisted for read-only Agent reporting sessions. */
10
+ export const REPORTER_PERSONA_ID = "reporter";
11
+ /**
12
+ * Exact positive allowlist for the reporter persona. New shared tools remain
13
+ * unavailable until they are explicitly reviewed here.
14
+ */
15
+ export const REPORTER_TOOL_NAMES = [
16
+ "read_intents",
17
+ "search_intents",
18
+ "read_user_contexts",
19
+ "preview_user_context",
20
+ "read_premises",
21
+ "read_networks",
22
+ "read_network_memberships",
23
+ "read_pending_questions",
24
+ "list_opportunities",
25
+ "report_agent_activity",
26
+ ];
27
+ const REPORTER_TOOL_ALLOWLIST = new Set(REPORTER_TOOL_NAMES);
28
+ /** Filters a shared registry through the reporter's positive allowlist. */
29
+ export function filterReporterTools(tools) {
30
+ return tools.filter((candidate) => REPORTER_TOOL_ALLOWLIST.has(candidate.name));
31
+ }
32
+ function invokeSharedTool(sharedTool, input) {
33
+ return sharedTool.invoke(input);
34
+ }
35
+ /**
36
+ * Replaces shared tools whose normal modes can enumerate other users with
37
+ * reporter-safe, self-only read contracts. Opportunity listing is deliberately
38
+ * aggregate-only: it never returns a counterpart name, row, or explanation.
39
+ */
40
+ export function narrowReporterTools(allowed, boundary) {
41
+ const { context, userDb } = boundary;
42
+ return allowed.map((sharedTool) => {
43
+ if (sharedTool.name === "read_intents") {
44
+ return tool(async (query) => {
45
+ const limit = query.limit ?? 100;
46
+ const page = query.page ?? 1;
47
+ const intents = await userDb.getActiveIntents();
48
+ const start = (page - 1) * limit;
49
+ return success({
50
+ intents: intents.slice(start, start + limit),
51
+ page,
52
+ limit,
53
+ total: intents.length,
54
+ });
55
+ }, {
56
+ name: "read_intents",
57
+ description: "Read the authenticated user's own active signals only.",
58
+ schema: z.object({
59
+ limit: z.number().int().min(1).max(100).optional(),
60
+ page: z.number().int().min(1).optional(),
61
+ }).strict(),
62
+ });
63
+ }
64
+ if (sharedTool.name === "search_intents") {
65
+ return tool(async (query) => success({
66
+ intents: await userDb.searchOwnIntents(query.query, query.limit ?? 20),
67
+ }), {
68
+ name: "search_intents",
69
+ description: "Search the authenticated user's own active signals only.",
70
+ schema: z.object({
71
+ query: z.string().trim().min(1),
72
+ limit: z.number().int().min(1).max(100).optional(),
73
+ }).strict(),
74
+ });
75
+ }
76
+ if (sharedTool.name === "read_user_contexts") {
77
+ return tool(async () => invokeSharedTool(sharedTool, {}), {
78
+ name: "read_user_contexts",
79
+ description: "Read the authenticated user's own identity and global context only.",
80
+ schema: z.object({}).strict(),
81
+ });
82
+ }
83
+ if (sharedTool.name === "read_premises") {
84
+ return tool(async (query) => invokeSharedTool(sharedTool, {
85
+ includeRetracted: query.includeRetracted ?? false,
86
+ userId: context.userId,
87
+ }), {
88
+ name: "read_premises",
89
+ description: "Read the authenticated user's own premises only.",
90
+ schema: z.object({ includeRetracted: z.boolean().optional() }).strict(),
91
+ });
92
+ }
93
+ if (sharedTool.name === "read_network_memberships") {
94
+ return tool(async () => invokeSharedTool(sharedTool, {}), {
95
+ name: "read_network_memberships",
96
+ description: "Read the authenticated user's own network memberships only.",
97
+ schema: z.object({}).strict(),
98
+ });
99
+ }
100
+ if (sharedTool.name === "read_networks") {
101
+ return tool(async () => invokeSharedTool(sharedTool, { userId: context.userId }), {
102
+ name: "read_networks",
103
+ description: "Read networks available to the authenticated user.",
104
+ schema: z.object({}).strict(),
105
+ });
106
+ }
107
+ if (sharedTool.name === "read_pending_questions") {
108
+ return tool(async (query) => {
109
+ if (!boundary.findPendingQuestions)
110
+ return error("Question lookup is not available.");
111
+ const questions = await boundary.findPendingQuestions(context.userId, {
112
+ modes: ["enrichment", "intent", "discovery"],
113
+ limit: query.limit ?? 10,
114
+ });
115
+ return success({ questions: questions.slice(0, query.limit ?? 10) });
116
+ }, {
117
+ name: "read_pending_questions",
118
+ description: "Read the user's own non-negotiation pending questions; answering is unavailable here.",
119
+ schema: z.object({ limit: z.number().int().min(1).max(10).optional() }).strict(),
120
+ });
121
+ }
122
+ if (sharedTool.name === "list_opportunities") {
123
+ return tool(async (query) => {
124
+ const scopedNetworkId = focusedNetworkId(context);
125
+ if (scopedNetworkId && query.networkId && query.networkId !== scopedNetworkId) {
126
+ return error("This chat is scoped to a different network.");
127
+ }
128
+ const activeIntents = await userDb.getActiveIntents();
129
+ const intentById = new Map(activeIntents.map((intent) => [intent.id, intent]));
130
+ const opportunities = await userDb.getOpportunitiesForUser({
131
+ ...(query.networkId || scopedNetworkId ? { networkId: query.networkId || scopedNetworkId } : {}),
132
+ statuses: ["draft", "pending", "latent"],
133
+ limit: 100,
134
+ });
135
+ const counts = new Map();
136
+ const seen = new Set();
137
+ for (const opportunity of opportunities) {
138
+ const ownIntentIds = new Set(opportunity.actors
139
+ .filter((actor) => actor.userId === context.userId && actor.intent && intentById.has(actor.intent))
140
+ .map((actor) => actor.intent));
141
+ if (ownIntentIds.size === 0)
142
+ continue;
143
+ seen.add(opportunity.id);
144
+ for (const intentId of ownIntentIds) {
145
+ const intent = intentById.get(intentId);
146
+ if (!intent)
147
+ continue;
148
+ const existing = counts.get(intentId) ?? {
149
+ intentId,
150
+ title: intent.summary?.trim() || intent.payload,
151
+ count: 0,
152
+ };
153
+ existing.count += 1;
154
+ counts.set(intentId, existing);
155
+ }
156
+ }
157
+ return success({
158
+ found: seen.size > 0,
159
+ count: seen.size,
160
+ bySignal: [...counts.values()],
161
+ });
162
+ }, {
163
+ name: "list_opportunities",
164
+ description: "Report current opportunity counts by the user's own signal, without counterpart identities or rows.",
165
+ schema: z.object({ networkId: z.string().uuid().optional() }).strict(),
166
+ });
167
+ }
168
+ return sharedTool;
169
+ });
170
+ }
171
+ /** Creates the reporter's context-bound, allowlisted toolset. */
172
+ export async function createReporterTools(deps, preResolvedContext) {
173
+ const explicitScope = deps.scopeType && deps.scopeId
174
+ ? { scopeType: deps.scopeType, scopeId: deps.scopeId }
175
+ : scopeFromNetworkId(deps.networkId);
176
+ const resolvedContext = preResolvedContext ?? await resolveChatContext({
177
+ database: deps.database,
178
+ userId: deps.userId,
179
+ networkId: explicitScope.scopeType === "network" ? explicitScope.scopeId : deps.networkId,
180
+ sessionId: deps.sessionId,
181
+ contactsEnabled: deps.contactsEnabled,
182
+ });
183
+ if (explicitScope.scopeType && explicitScope.scopeId) {
184
+ resolvedContext.scopeType = explicitScope.scopeType;
185
+ resolvedContext.scopeId = explicitScope.scopeId;
186
+ }
187
+ const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);
188
+ const allowed = filterReporterTools(await createChatTools(deps, resolvedContext));
189
+ return narrowReporterTools(allowed, {
190
+ context: resolvedContext,
191
+ userDb,
192
+ findPendingQuestions: deps.findPendingQuestions,
193
+ });
194
+ }
195
+ /** Restricted read-only Agent reporter persona. */
196
+ export const REPORTER_PERSONA = {
197
+ id: REPORTER_PERSONA_ID,
198
+ buildSystemContent: (ctx, iterCtx) => buildReporterSystemContent(ctx, iterCtx),
199
+ createTools: (deps, preResolvedContext) => createReporterTools(deps, preResolvedContext),
200
+ loopBehaviors: {
201
+ createIntentCallback: false,
202
+ hallucinationRecovery: false,
203
+ },
204
+ };
205
+ //# sourceMappingURL=reporter.persona.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.persona.js","sourceRoot":"/","sources":["chat/reporter.persona.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport { createChatTools, type ChatTools, type ResolvedToolContext, type ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { error, resolveChatContext, success } from \"../shared/agent/tool.helpers.js\";\nimport type { UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { focusedNetworkId, scopeFromNetworkId } from \"../shared/agent/tool.scope.js\";\nimport type { ChatPersonaConfig } from \"./chat.persona.js\";\nimport { buildReporterSystemContent } from \"./reporter.prompt.js\";\n\n/** Public kickoff marker used by the Agent surface to request its opening briefing. */\nexport { REPORTER_BRIEFING_KICKOFF } from \"./reporter.prompt.js\";\n\n/** Stable persona id persisted for read-only Agent reporting sessions. */\nexport const REPORTER_PERSONA_ID = \"reporter\";\n\n/**\n * Exact positive allowlist for the reporter persona. New shared tools remain\n * unavailable until they are explicitly reviewed here.\n */\nexport const REPORTER_TOOL_NAMES = [\n \"read_intents\",\n \"search_intents\",\n \"read_user_contexts\",\n \"preview_user_context\",\n \"read_premises\",\n \"read_networks\",\n \"read_network_memberships\",\n \"read_pending_questions\",\n \"list_opportunities\",\n \"report_agent_activity\",\n] as const;\n\nconst REPORTER_TOOL_ALLOWLIST: ReadonlySet<string> = new Set(REPORTER_TOOL_NAMES);\n\ninterface ReporterToolBoundary {\n context: ResolvedToolContext;\n userDb: UserDatabase;\n findPendingQuestions?: ToolContext[\"findPendingQuestions\"];\n}\n\n/** Filters a shared registry through the reporter's positive allowlist. */\nexport function filterReporterTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((candidate) => REPORTER_TOOL_ALLOWLIST.has(candidate.name));\n}\n\nfunction invokeSharedTool(sharedTool: { invoke: (input: unknown) => unknown }, input: unknown): Promise<string> {\n return sharedTool.invoke(input) as Promise<string>;\n}\n\n/**\n * Replaces shared tools whose normal modes can enumerate other users with\n * reporter-safe, self-only read contracts. Opportunity listing is deliberately\n * aggregate-only: it never returns a counterpart name, row, or explanation.\n */\nexport function narrowReporterTools(\n allowed: ChatTools,\n boundary: ReporterToolBoundary,\n): ChatTools {\n const { context, userDb } = boundary;\n\n return allowed.map((sharedTool) => {\n if (sharedTool.name === \"read_intents\") {\n return tool(\n async (query: { limit?: number; page?: number }) => {\n const limit = query.limit ?? 100;\n const page = query.page ?? 1;\n const intents = await userDb.getActiveIntents();\n const start = (page - 1) * limit;\n return success({\n intents: intents.slice(start, start + limit),\n page,\n limit,\n total: intents.length,\n });\n },\n {\n name: \"read_intents\",\n description: \"Read the authenticated user's own active signals only.\",\n schema: z.object({\n limit: z.number().int().min(1).max(100).optional(),\n page: z.number().int().min(1).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"search_intents\") {\n return tool(\n async (query: { query: string; limit?: number }) => success({\n intents: await userDb.searchOwnIntents(query.query, query.limit ?? 20),\n }),\n {\n name: \"search_intents\",\n description: \"Search the authenticated user's own active signals only.\",\n schema: z.object({\n query: z.string().trim().min(1),\n limit: z.number().int().min(1).max(100).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_user_contexts\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_user_contexts\",\n description: \"Read the authenticated user's own identity and global context only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_premises\") {\n return tool(\n async (query: { includeRetracted?: boolean }) => invokeSharedTool(sharedTool, {\n includeRetracted: query.includeRetracted ?? false,\n userId: context.userId,\n }),\n {\n name: \"read_premises\",\n description: \"Read the authenticated user's own premises only.\",\n schema: z.object({ includeRetracted: z.boolean().optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_network_memberships\") {\n return tool(\n async () => invokeSharedTool(sharedTool, {}),\n {\n name: \"read_network_memberships\",\n description: \"Read the authenticated user's own network memberships only.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_networks\") {\n return tool(\n async () => invokeSharedTool(sharedTool, { userId: context.userId }),\n {\n name: \"read_networks\",\n description: \"Read networks available to the authenticated user.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_pending_questions\") {\n return tool(\n async (query: { limit?: number }) => {\n if (!boundary.findPendingQuestions) return error(\"Question lookup is not available.\");\n const questions = await boundary.findPendingQuestions(context.userId, {\n modes: [\"enrichment\", \"intent\", \"discovery\"],\n limit: query.limit ?? 10,\n });\n return success({ questions: questions.slice(0, query.limit ?? 10) });\n },\n {\n name: \"read_pending_questions\",\n description: \"Read the user's own non-negotiation pending questions; answering is unavailable here.\",\n schema: z.object({ limit: z.number().int().min(1).max(10).optional() }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"list_opportunities\") {\n return tool(\n async (query: { networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n if (scopedNetworkId && query.networkId && query.networkId !== scopedNetworkId) {\n return error(\"This chat is scoped to a different network.\");\n }\n const activeIntents = await userDb.getActiveIntents();\n const intentById = new Map(activeIntents.map((intent) => [intent.id, intent]));\n const opportunities = await userDb.getOpportunitiesForUser({\n ...(query.networkId || scopedNetworkId ? { networkId: query.networkId || scopedNetworkId } : {}),\n statuses: [\"draft\", \"pending\", \"latent\"],\n limit: 100,\n });\n const counts = new Map<string, { intentId: string; title: string; count: number }>();\n const seen = new Set<string>();\n for (const opportunity of opportunities) {\n const ownIntentIds = new Set(\n opportunity.actors\n .filter((actor) => actor.userId === context.userId && actor.intent && intentById.has(actor.intent))\n .map((actor) => actor.intent as string),\n );\n if (ownIntentIds.size === 0) continue;\n seen.add(opportunity.id);\n for (const intentId of ownIntentIds) {\n const intent = intentById.get(intentId);\n if (!intent) continue;\n const existing = counts.get(intentId) ?? {\n intentId,\n title: intent.summary?.trim() || intent.payload,\n count: 0,\n };\n existing.count += 1;\n counts.set(intentId, existing);\n }\n }\n return success({\n found: seen.size > 0,\n count: seen.size,\n bySignal: [...counts.values()],\n });\n },\n {\n name: \"list_opportunities\",\n description: \"Report current opportunity counts by the user's own signal, without counterpart identities or rows.\",\n schema: z.object({ networkId: z.string().uuid().optional() }).strict(),\n },\n );\n }\n\n return sharedTool;\n }) as ChatTools;\n}\n\n/** Creates the reporter's context-bound, allowlisted toolset. */\nexport async function createReporterTools(\n deps: ToolContext,\n preResolvedContext?: ResolvedToolContext,\n): Promise<ChatTools> {\n const explicitScope = deps.scopeType && deps.scopeId\n ? { scopeType: deps.scopeType, scopeId: deps.scopeId }\n : scopeFromNetworkId(deps.networkId);\n const resolvedContext = preResolvedContext ?? await resolveChatContext({\n database: deps.database,\n userId: deps.userId,\n networkId: explicitScope.scopeType === \"network\" ? explicitScope.scopeId : deps.networkId,\n sessionId: deps.sessionId,\n contactsEnabled: deps.contactsEnabled,\n });\n if (explicitScope.scopeType && explicitScope.scopeId) {\n resolvedContext.scopeType = explicitScope.scopeType;\n resolvedContext.scopeId = explicitScope.scopeId;\n }\n\n const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);\n const allowed = filterReporterTools(await createChatTools(deps, resolvedContext)) as ChatTools;\n\n return narrowReporterTools(allowed, {\n context: resolvedContext,\n userDb,\n findPendingQuestions: deps.findPendingQuestions,\n });\n}\n\n/** Restricted read-only Agent reporter persona. */\nexport const REPORTER_PERSONA: ChatPersonaConfig = {\n id: REPORTER_PERSONA_ID,\n buildSystemContent: (ctx, iterCtx) => buildReporterSystemContent(ctx, iterCtx),\n createTools: (deps, preResolvedContext) => createReporterTools(deps, preResolvedContext),\n loopBehaviors: {\n createIntentCallback: false,\n hallucinationRecovery: false,\n },\n};\n"]}
@@ -0,0 +1,21 @@
1
+ import type { ResolvedToolContext } from "../shared/agent/tool.factory.js";
2
+ import type { IterationContext } from "./chat.prompt.modules.js";
3
+ /** Stable marker used by the Agent surface to request its opening briefing. */
4
+ export declare const REPORTER_BRIEFING_KICKOFF = "reporter-briefing-kickoff";
5
+ /**
6
+ * Recognizes the explicit opening briefing marker without putting ordinary
7
+ * reporter conversations into briefing mode accidentally.
8
+ *
9
+ * @param message - Latest user message in the current turn
10
+ * @returns Whether this turn is the Agent-surface briefing kickoff
11
+ */
12
+ export declare function isReporterBriefingKickoff(message?: string): boolean;
13
+ /**
14
+ * Builds the read-only reporter persona prompt.
15
+ *
16
+ * @param ctx - Resolved authenticated user context
17
+ * @param iterCtx - Current agent-loop context used for briefing kickoff
18
+ * @returns Complete reporter system content
19
+ */
20
+ export declare function buildReporterSystemContent(ctx: ResolvedToolContext, iterCtx?: IterationContext): string;
21
+ //# sourceMappingURL=reporter.prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.prompt.d.ts","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,+EAA+E;AAC/E,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAYnE;AAgBD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,mBAAmB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,CA8DR"}
@@ -0,0 +1,101 @@
1
+ /** Stable marker used by the Agent surface to request its opening briefing. */
2
+ export const REPORTER_BRIEFING_KICKOFF = "reporter-briefing-kickoff";
3
+ /**
4
+ * Recognizes the explicit opening briefing marker without putting ordinary
5
+ * reporter conversations into briefing mode accidentally.
6
+ *
7
+ * @param message - Latest user message in the current turn
8
+ * @returns Whether this turn is the Agent-surface briefing kickoff
9
+ */
10
+ export function isReporterBriefingKickoff(message) {
11
+ const normalized = message?.trim().toLocaleLowerCase()
12
+ .replace(/[–—]/g, "-")
13
+ .replace(/^_+|_+$/g, "");
14
+ if (!normalized)
15
+ return false;
16
+ return new Set([
17
+ REPORTER_BRIEFING_KICKOFF,
18
+ "reporter-briefing",
19
+ "reporter briefing",
20
+ "agent briefing",
21
+ ]).has(normalized);
22
+ }
23
+ function buildBriefingGuidance(iterCtx) {
24
+ if (!isReporterBriefingKickoff(iterCtx?.currentMessage))
25
+ return "";
26
+ return `
27
+
28
+ ## Opening briefing
29
+ This is the Agent-surface briefing kickoff. Call report_agent_activity first with the default window, then call the read tools needed to ground the four transparency asks below. Present one concise briefing covering:
30
+ 1. summarize all my signals;
31
+ 2. what did you do today?;
32
+ 3. how do I look to others?;
33
+ 4. what should I sharpen?
34
+ Do not claim a metric unless it appears in a tool result from this turn. If a section has no grounded data, say that plainly rather than filling the gap.`;
35
+ }
36
+ /**
37
+ * Builds the read-only reporter persona prompt.
38
+ *
39
+ * @param ctx - Resolved authenticated user context
40
+ * @param iterCtx - Current agent-loop context used for briefing kickoff
41
+ * @returns Complete reporter system content
42
+ */
43
+ export function buildReporterSystemContent(ctx, iterCtx) {
44
+ const userContext = JSON.stringify(ctx.user, null, 2);
45
+ const profileContext = ctx.userProfile
46
+ ? JSON.stringify(ctx.userProfile, null, 2)
47
+ : "null";
48
+ const membershipContext = JSON.stringify(ctx.userNetworks.map((network) => ({
49
+ id: network.networkId,
50
+ title: network.networkTitle,
51
+ isPersonal: network.isPersonal,
52
+ })), null, 2);
53
+ return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.
54
+
55
+ Your role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.
56
+
57
+ ## Hard rules
58
+ - Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.
59
+ - Use report_agent_activity for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.
60
+ - Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.
61
+ - Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.
62
+ - Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.
63
+ - If opportunity information is relevant, use only the restricted list_opportunities result or report_agent_activity result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.
64
+ - Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.
65
+
66
+ ## Four transparency asks
67
+ Be ready to answer:
68
+ - “summarize all my signals” — read the user's own signals and describe their current themes without inventing a synthesis.
69
+ - “what did you do today?” — report only grounded activity counts from the requested window.
70
+ - “how do I look to others?” — describe only the user's own stored context and signals; do not claim access to private counterparty opinions.
71
+ - “what should I sharpen?” — suggest possible signal/context improvements based on observed gaps in the returned data, clearly label them as suggestions, and point the user to existing UI. Never apply them.
72
+
73
+ ## Allowed capabilities
74
+ - Own signal reads: read_intents, search_intents.
75
+ - Own context reads: read_user_contexts, preview_user_context, read_premises.
76
+ - Own community context: read_networks, read_network_memberships.
77
+ - Own pending-question reads: read_pending_questions (never answer them).
78
+ - Aggregate activity reporting: report_agent_activity.
79
+ - Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).
80
+
81
+ ## Session identity (preloaded)
82
+ - User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}
83
+
84
+ ### User record
85
+ \`\`\`json
86
+ ${userContext}
87
+ \`\`\`
88
+
89
+ ### User context
90
+ \`\`\`json
91
+ ${profileContext}
92
+ \`\`\`
93
+
94
+ ### Current memberships
95
+ \`\`\`json
96
+ ${membershipContext}
97
+ \`\`\`
98
+
99
+ Only the identity, context, and membership metadata above are preloaded. Signals, premises, questions, and activity are not preloaded: call the appropriate read/report tool before describing them.${buildBriefingGuidance(iterCtx)}`;
100
+ }
101
+ //# sourceMappingURL=reporter.prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.prompt.js","sourceRoot":"/","sources":["chat/reporter.prompt.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAgB;IACxD,MAAM,UAAU,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,iBAAiB,EAAE;SACnD,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE9B,OAAO,IAAI,GAAG,CAAC;QACb,yBAAyB;QACzB,mBAAmB;QACnB,mBAAmB;QACnB,gBAAgB;KACjB,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACrB,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA0B;IACvD,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,cAAc,CAAC;QAAE,OAAO,EAAE,CAAC;IAEnE,OAAO;;;;;;;;0JAQiJ,CAAC;AAC3J,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,GAAwB,EACxB,OAA0B;IAE1B,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,WAAW;QACpC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CACtC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjC,EAAE,EAAE,OAAO,CAAC,SAAS;QACrB,KAAK,EAAE,OAAO,CAAC,YAAY;QAC3B,UAAU,EAAE,OAAO,CAAC,UAAU;KAC/B,CAAC,CAAC,EACH,IAAI,EACJ,CAAC,CACF,CAAC;IAEF,OAAO,qEAAqE,GAAG,CAAC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA6BhF,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,SAAS,UAAU,GAAG,CAAC,MAAM;;;;EAI1D,WAAW;;;;;EAKX,cAAc;;;;;EAKd,iBAAiB;;;sMAGmL,qBAAqB,CAAC,OAAO,CAAC,EAAE,CAAC;AACvO,CAAC","sourcesContent":["import type { ResolvedToolContext } from \"../shared/agent/tool.factory.js\";\nimport type { IterationContext } from \"./chat.prompt.modules.js\";\n\n/** Stable marker used by the Agent surface to request its opening briefing. */\nexport const REPORTER_BRIEFING_KICKOFF = \"reporter-briefing-kickoff\";\n\n/**\n * Recognizes the explicit opening briefing marker without putting ordinary\n * reporter conversations into briefing mode accidentally.\n *\n * @param message - Latest user message in the current turn\n * @returns Whether this turn is the Agent-surface briefing kickoff\n */\nexport function isReporterBriefingKickoff(message?: string): boolean {\n const normalized = message?.trim().toLocaleLowerCase()\n .replace(/[–—]/g, \"-\")\n .replace(/^_+|_+$/g, \"\");\n if (!normalized) return false;\n\n return new Set([\n REPORTER_BRIEFING_KICKOFF,\n \"reporter-briefing\",\n \"reporter briefing\",\n \"agent briefing\",\n ]).has(normalized);\n}\n\nfunction buildBriefingGuidance(iterCtx?: IterationContext): string {\n if (!isReporterBriefingKickoff(iterCtx?.currentMessage)) return \"\";\n\n return `\n\n## Opening briefing\nThis is the Agent-surface briefing kickoff. Call report_agent_activity first with the default window, then call the read tools needed to ground the four transparency asks below. Present one concise briefing covering:\n1. summarize all my signals;\n2. what did you do today?;\n3. how do I look to others?;\n4. what should I sharpen?\nDo not claim a metric unless it appears in a tool result from this turn. If a section has no grounded data, say that plainly rather than filling the gap.`;\n}\n\n/**\n * Builds the read-only reporter persona prompt.\n *\n * @param ctx - Resolved authenticated user context\n * @param iterCtx - Current agent-loop context used for briefing kickoff\n * @returns Complete reporter system content\n */\nexport function buildReporterSystemContent(\n ctx: ResolvedToolContext,\n iterCtx?: IterationContext,\n): string {\n const userContext = JSON.stringify(ctx.user, null, 2);\n const profileContext = ctx.userProfile\n ? JSON.stringify(ctx.userProfile, null, 2)\n : \"null\";\n const membershipContext = JSON.stringify(\n ctx.userNetworks.map((network) => ({\n id: network.networkId,\n title: network.networkTitle,\n isPersonal: network.isPersonal,\n })),\n null,\n 2,\n );\n\n return `You are Agent, the user's private read-only activity reporter for ${ctx.userName}.\n\nYour role is to report what the user's Index agent has done and what the user's own signals currently communicate. You observe; you never change anything. Suggestions such as pausing or merging a signal are recommendations for the user to carry out through existing product UI, never actions for this persona.\n\n## Hard rules\n- Every factual claim, number, status, or trend must come from a tool result in the current turn. Never invent, estimate, or reuse an unverified metric.\n- Use report_agent_activity for activity counts and read_intents/read_user_contexts/read_premises/read_networks/read_network_memberships/read_pending_questions for the underlying current state.\n- Counterparties are identity-free aggregate data only: never reveal names, IDs, transcripts, message text, or per-counterparty rows. Do not infer what another person thinks from a match or negotiation.\n- Never create, update, delete, confirm, answer, remember, forget, assign, discover, negotiate, scrape, or otherwise mutate data. Do not ask the user a question through a tool.\n- Do not write observed behavior back as a preference or premise. The user decides whether to act on a suggestion.\n- If opportunity information is relevant, use only the restricted list_opportunities result or report_agent_activity result. Do not expose raw evaluator reasoning, matchReason, or internal JSON. Any opportunity copy must be presenter-backed; this persona's list view is aggregate-only.\n- Be transparent about missing data and the reporting window. Keep the response concise, calm, and useful without hype.\n\n## Four transparency asks\nBe ready to answer:\n- “summarize all my signals” — read the user's own signals and describe their current themes without inventing a synthesis.\n- “what did you do today?” — report only grounded activity counts from the requested window.\n- “how do I look to others?” — describe only the user's own stored context and signals; do not claim access to private counterparty opinions.\n- “what should I sharpen?” — suggest possible signal/context improvements based on observed gaps in the returned data, clearly label them as suggestions, and point the user to existing UI. Never apply them.\n\n## Allowed capabilities\n- Own signal reads: read_intents, search_intents.\n- Own context reads: read_user_contexts, preview_user_context, read_premises.\n- Own community context: read_networks, read_network_memberships.\n- Own pending-question reads: read_pending_questions (never answer them).\n- Aggregate activity reporting: report_agent_activity.\n- Aggregate current opportunity reporting: list_opportunities (no counterpart identities or rows).\n\n## Session identity (preloaded)\n- User: ${ctx.userName} (${ctx.userEmail}), id: ${ctx.userId}\n\n### User record\n\\`\\`\\`json\n${userContext}\n\\`\\`\\`\n\n### User context\n\\`\\`\\`json\n${profileContext}\n\\`\\`\\`\n\n### Current memberships\n\\`\\`\\`json\n${membershipContext}\n\\`\\`\\`\n\nOnly the identity, context, and membership metadata above are preloaded. Signals, premises, questions, and activity are not preloaded: call the appropriate read/report tool before describing them.${buildBriefingGuidance(iterCtx)}`;\n}\n"]}
@@ -1,6 +1,8 @@
1
1
  import { type ChatTools, type ResolvedToolContext, type ToolContext } from "../shared/agent/tool.factory.js";
2
2
  import type { SystemDatabase, UserDatabase } from "../shared/interfaces/database.interface.js";
3
3
  import type { ChatPersonaConfig } from "./chat.persona.js";
4
+ /** Public kickoff marker used by New Signal surfaces to enter guided intake. */
5
+ export { SIGNAL_NEW_SIGNAL_KICKOFF } from "./signal.prompt.js";
4
6
  /** Stable persona id persisted for restricted Signal Agent conversations. */
5
7
  export declare const SIGNAL_PERSONA_ID = "signal";
6
8
  /**
@@ -43,5 +45,4 @@ export declare function narrowSignalTools(allowed: ChatTools, boundary: SignalTo
43
45
  export declare function createSignalTools(deps: ToolContext, preResolvedContext?: ResolvedToolContext): Promise<ChatTools>;
44
46
  /** Restricted Signal Agent persona on the persona-neutral chat runtime. */
45
47
  export declare const SIGNAL_PERSONA: ChatPersonaConfig;
46
- export {};
47
48
  //# sourceMappingURL=signal.persona.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"signal.persona.d.ts","sourceRoot":"/","sources":["chat/signal.persona.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9H,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAE/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,ibA2BpB,CAAC;AAIX,UAAU,kBAAkB;IAC1B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AA8BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,kBAAkB,GAC3B,SAAS,CAsPX;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,mBAAmB,GACvC,OAAO,CAAC,SAAS,CAAC,CA+BpB;AAED,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,iBAU5B,CAAC"}
1
+ {"version":3,"file":"signal.persona.d.ts","sourceRoot":"/","sources":["chat/signal.persona.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,KAAK,SAAS,EAAE,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9H,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAC;AAE/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,gFAAgF;AAChF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,WAAW,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,ibA2BpB,CAAC;AAIX,UAAU,kBAAkB;IAC1B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AA8BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAE7E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,kBAAkB,GAC3B,SAAS,CAsPX;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,WAAW,EACjB,kBAAkB,CAAC,EAAE,mBAAmB,GACvC,OAAO,CAAC,SAAS,CAAC,CA+BpB;AAED,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,iBAU5B,CAAC"}
@@ -4,6 +4,8 @@ import { createChatTools } from "../shared/agent/tool.factory.js";
4
4
  import { error, resolveChatContext, success } from "../shared/agent/tool.helpers.js";
5
5
  import { deriveAllowedNetworkIds, focusedIntentId, focusedNetworkId, scopeFromNetworkId } from "../shared/agent/tool.scope.js";
6
6
  import { buildSignalSystemContent } from "./signal.prompt.js";
7
+ /** Public kickoff marker used by New Signal surfaces to enter guided intake. */
8
+ export { SIGNAL_NEW_SIGNAL_KICKOFF } from "./signal.prompt.js";
7
9
  /** Stable persona id persisted for restricted Signal Agent conversations. */
8
10
  export const SIGNAL_PERSONA_ID = "signal";
9
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"signal.persona.js","sourceRoot":"/","sources":["chat/signal.persona.ts"],"names":[],"mappings":"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","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport { createChatTools, type ChatTools, type ResolvedToolContext, type ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { error, resolveChatContext, success } from \"../shared/agent/tool.helpers.js\";\nimport type { SystemDatabase, UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { deriveAllowedNetworkIds, focusedIntentId, focusedNetworkId, scopeFromNetworkId } from \"../shared/agent/tool.scope.js\";\nimport type { ChatPersonaConfig } from \"./chat.persona.js\";\nimport { buildSignalSystemContent } from \"./signal.prompt.js\";\n\n/** Stable persona id persisted for restricted Signal Agent conversations. */\nexport const SIGNAL_PERSONA_ID = \"signal\";\n\n/**\n * Exact positive tool allowlist for Signal Agent.\n *\n * New tools added to the shared chat registry remain unavailable until they are\n * reviewed and explicitly added here.\n */\nexport const SIGNAL_TOOL_NAMES = [\n // Signals and assignment to communities the user already belongs to.\n \"read_intents\",\n \"create_intent\",\n \"update_intent\",\n \"delete_intent\",\n \"search_intents\",\n \"read_intent_indexes\",\n \"create_intent_index\",\n \"delete_intent_index\",\n // User/profile context.\n \"read_user_contexts\",\n \"preview_user_context\",\n \"confirm_user_context\",\n \"create_user_context\",\n \"update_user_context\",\n // Premise knowledge.\n \"read_premises\",\n \"create_premise\",\n \"update_premise\",\n \"retract_premise\",\n // Read-only community and membership context.\n \"read_networks\",\n \"read_network_memberships\",\n // Pasted-link reading and chat clarification.\n \"scrape_url\",\n \"ask_user_question\",\n] as const;\n\nconst SIGNAL_TOOL_ALLOWLIST: ReadonlySet<string> = new Set(SIGNAL_TOOL_NAMES);\n\ninterface SignalToolBoundary {\n context: ResolvedToolContext;\n userDb: UserDatabase;\n systemDb: SystemDatabase;\n}\n\nfunction isLiveIntent(intent: Awaited<ReturnType<UserDatabase[\"getIntent\"]>>): intent is NonNullable<typeof intent> {\n return Boolean(\n intent\n && !intent.archivedAt\n && (intent.status == null || intent.status === \"ACTIVE\"),\n );\n}\n\nasync function getOwnedLiveIntent(userDb: UserDatabase, intentId: string) {\n try {\n const intent = await userDb.getIntent(intentId);\n return isLiveIntent(intent) ? intent : null;\n } catch {\n // The context-bound adapter throws for foreign IDs. Collapse missing,\n // foreign, archived, and non-live rows to the same non-enumerating result.\n return null;\n }\n}\n\nfunction matchesIntentText(\n intent: { payload: string; summary: string | null },\n query: string,\n): boolean {\n const needle = query.trim().toLocaleLowerCase();\n return intent.payload.toLocaleLowerCase().includes(needle)\n || (intent.summary?.toLocaleLowerCase().includes(needle) ?? false);\n}\n\n/**\n * Filters shared chat tools through Signal Agent's positive allowlist.\n *\n * @param tools - Shared context-bound chat tools\n * @returns Only explicitly approved Signal Agent tools\n */\nexport function filterSignalTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((candidate) => SIGNAL_TOOL_ALLOWLIST.has(candidate.name));\n}\n\n/**\n * Narrows schemas and handlers whose shared versions expose broader modes than\n * Signal Agent is allowed to use.\n *\n * @param allowed - Name-allowlisted shared chat tools\n * @param boundary - Authoritative context-bound databases for Signal-only checks\n * @returns Signal-safe tools with self-only reads and proposal-only creation\n */\nexport function narrowSignalTools(\n allowed: ChatTools,\n boundary: SignalToolBoundary,\n): ChatTools {\n const { context, userDb, systemDb } = boundary;\n\n return allowed.map((sharedTool) => {\n if (sharedTool.name === \"create_intent\") {\n return tool(\n async (query: { description: string; networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n const scopedIntentId = focusedIntentId(context);\n const explicitNetworkId = query.networkId?.trim();\n\n if (scopedIntentId) {\n return error(\"This chat is scoped to an existing selected intent. Update that intent instead of creating a different one here.\");\n }\n if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n\n const effectiveNetworkId = scopedNetworkId ?? explicitNetworkId;\n if (effectiveNetworkId) {\n const isMember = await systemDb.isNetworkMember(effectiveNetworkId, context.userId);\n if (!isMember) {\n return error(\"You are no longer a member of this community.\");\n }\n }\n\n return sharedTool.invoke({\n description: query.description,\n ...(effectiveNetworkId ? { networkId: effectiveNetworkId } : {}),\n // Signal web chats always use the confirmation-safe proposal path.\n autoApprove: false,\n }) as Promise<string>;\n },\n {\n name: \"create_intent\",\n description:\n \"Draft a new signal for the current user. Returns an intent_proposal card that must be passed through verbatim and approved in the web UI before persistence.\",\n schema: z.object({\n description: z.string().trim().min(1).describe(\"Clear, specific signal description.\"),\n networkId: z.string().uuid().optional().describe(\"Optional existing-membership community UUID.\"),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_premises\") {\n return tool(\n async (query: { includeRetracted?: boolean }) => sharedTool.invoke({\n includeRetracted: query.includeRetracted ?? false,\n }) as Promise<string>,\n {\n name: \"read_premises\",\n description: \"Read only the current user's premises. Use before creating or updating profile knowledge.\",\n schema: z.object({\n includeRetracted: z.boolean().optional().default(false),\n }),\n },\n );\n }\n\n if (sharedTool.name === \"read_user_contexts\") {\n return tool(\n async () => sharedTool.invoke({}) as Promise<string>,\n {\n name: \"read_user_contexts\",\n description: \"Read only the current user's identity and synthesized profile context.\",\n schema: z.object({}),\n },\n );\n }\n\n if (sharedTool.name === \"read_intents\") {\n return tool(\n async (query: { limit?: number; page?: number }) => sharedTool.invoke(query) as Promise<string>,\n {\n name: \"read_intents\",\n description: \"Read the current user's own signals, optionally paginated.\",\n schema: z.object({\n limit: z.number().int().min(1).max(100).optional(),\n page: z.number().int().min(1).optional(),\n }),\n },\n );\n }\n\n if (sharedTool.name === \"search_intents\") {\n return tool(\n async (query: { query: string; limit?: number }) => {\n const limit = query.limit ?? 25;\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n\n if (scopedIntentId) {\n const intent = await getOwnedLiveIntent(userDb, scopedIntentId);\n const intents = intent && matchesIntentText(intent, query.query)\n ? [{ id: intent.id, payload: intent.payload, summary: intent.summary, createdAt: intent.createdAt }]\n : [];\n return success({ intents: intents.slice(0, limit) });\n }\n\n if (scopedNetworkId) {\n if (!(await systemDb.isNetworkMember(scopedNetworkId, context.userId))) {\n return error(\"You are no longer a member of this community.\");\n }\n const candidates = (await userDb.getActiveIntents())\n .filter((intent) => matchesIntentText(intent, query.query));\n const assignmentChecks = await Promise.all(\n candidates.map((intent) => userDb.isIntentAssignedToIndex(intent.id, scopedNetworkId)),\n );\n return success({\n intents: candidates\n .filter((_intent, index) => assignmentChecks[index])\n .slice(0, limit),\n });\n }\n\n return success({\n intents: await userDb.searchOwnIntents(query.query, limit),\n });\n },\n {\n name: \"search_intents\",\n description: \"Search the current user's own active signals by text within the selected Signal scope.\",\n schema: z.object({\n query: z.string().trim().min(1),\n limit: z.number().int().min(1).max(100).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_networks\") {\n return tool(\n async () => {\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n const memberships = await userDb.getNetworkMemberships();\n\n if (scopedNetworkId) {\n const focusedMemberships = memberships.filter(\n (membership) => membership.networkId === scopedNetworkId,\n );\n if (focusedMemberships.length === 0) {\n return error(\"You are no longer a member of this community.\");\n }\n return success({ memberOf: focusedMemberships, publicNetworks: [] });\n }\n\n if (scopedIntentId) {\n const intent = await getOwnedLiveIntent(userDb, scopedIntentId);\n if (!intent) {\n return error(\"The selected intent is not an owned active signal.\");\n }\n const assignedNetworkIds = new Set(\n await userDb.getNetworkIdsForIntent(scopedIntentId),\n );\n return success({\n memberOf: memberships.filter((membership) =>\n assignedNetworkIds.has(membership.networkId)),\n publicNetworks: [],\n });\n }\n\n return success({ memberOf: memberships, publicNetworks: [] });\n },\n {\n name: \"read_networks\",\n description: \"List only communities the current user is presently a member of. Public communities are never included.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_network_memberships\") {\n return tool(\n async (query: { networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n const explicitNetworkId = query.networkId?.trim();\n if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n const effectiveNetworkId = explicitNetworkId ?? scopedNetworkId;\n const memberships = await userDb.getNetworkMemberships();\n if (effectiveNetworkId) {\n const focusedMemberships = memberships.filter(\n (membership) => membership.networkId === effectiveNetworkId,\n );\n if (focusedMemberships.length === 0) {\n return error(\"You are no longer a member of this community.\");\n }\n return success({ userId: context.userId, memberships: focusedMemberships });\n }\n return success({ userId: context.userId, memberships });\n },\n {\n name: \"read_network_memberships\",\n description: \"Read only the current user's present community memberships. This never lists other members.\",\n schema: z.object({\n networkId: z.string().uuid().optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_intent_indexes\") {\n return tool(\n async (query: { intentId: string; networkId: string }) => {\n const intentId = query.intentId.trim();\n const networkId = query.networkId.trim();\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n\n if (scopedIntentId && intentId !== scopedIntentId) {\n return error(\"The requested intent conflicts with this chat's selected intent.\");\n }\n if (scopedNetworkId && networkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n\n const intent = await getOwnedLiveIntent(userDb, intentId);\n if (!intent) {\n return error(\"The selected intent is not an owned active signal.\");\n }\n if (!(await systemDb.isNetworkMember(networkId, context.userId))) {\n return error(\"You are no longer a member of this community.\");\n }\n\n const assigned = await userDb.isIntentAssignedToIndex(intentId, networkId);\n return success({\n isAssigned: assigned,\n links: assigned ? [{ intentId, networkId }] : [],\n });\n },\n {\n name: \"read_intent_indexes\",\n description: \"Check one exact owned active signal-to-current-membership community assignment.\",\n schema: z.object({\n intentId: z.string().uuid(),\n networkId: z.string().uuid(),\n }).strict(),\n },\n );\n }\n\n return sharedTool;\n }) as ChatTools;\n}\n\n/**\n * Creates Signal Agent's context-bound restricted toolset.\n *\n * @param deps - Shared tool dependencies\n * @param preResolvedContext - Optional authoritative resolved context\n * @returns The allowlisted and schema-narrowed Signal Agent tools\n */\nexport async function createSignalTools(\n deps: ToolContext,\n preResolvedContext?: ResolvedToolContext,\n): Promise<ChatTools> {\n const explicitScope = deps.scopeType && deps.scopeId\n ? { scopeType: deps.scopeType, scopeId: deps.scopeId }\n : scopeFromNetworkId(deps.networkId);\n const resolvedContext = preResolvedContext ?? await resolveChatContext({\n database: deps.database,\n userId: deps.userId,\n networkId: explicitScope.scopeType === \"network\" ? explicitScope.scopeId : deps.networkId,\n sessionId: deps.sessionId,\n contactsEnabled: deps.contactsEnabled,\n });\n if (explicitScope.scopeType && explicitScope.scopeId) {\n resolvedContext.scopeType = explicitScope.scopeType;\n resolvedContext.scopeId = explicitScope.scopeId;\n }\n\n const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);\n const liveMemberships = await userDb.getNetworkMemberships();\n const allowedNetworkIds = deriveAllowedNetworkIds({\n memberships: liveMemberships,\n ...(resolvedContext.scopeType && resolvedContext.scopeId\n ? { scopeType: resolvedContext.scopeType, scopeId: resolvedContext.scopeId }\n : {}),\n });\n const systemDb = deps.systemDb\n ?? deps.createSystemDatabase(deps.database, resolvedContext.userId, allowedNetworkIds, deps.embedder);\n const allowed = filterSignalTools(\n await createChatTools(deps, resolvedContext),\n ) as ChatTools;\n\n return narrowSignalTools(allowed, { context: resolvedContext, userDb, systemDb });\n}\n\n/** Restricted Signal Agent persona on the persona-neutral chat runtime. */\nexport const SIGNAL_PERSONA: ChatPersonaConfig = {\n id: SIGNAL_PERSONA_ID,\n buildSystemContent: (ctx, iterCtx) => buildSignalSystemContent(ctx, iterCtx),\n createTools: (deps, preResolvedContext) => createSignalTools(deps, preResolvedContext),\n loopBehaviors: {\n // Direct discovery is absent, so its create-intent retry callback must stay off.\n createIntentCallback: false,\n // create_intent can legitimately return proposal cards; retain recovery/stripping.\n hallucinationRecovery: true,\n },\n};\n"]}
1
+ {"version":3,"file":"signal.persona.js","sourceRoot":"/","sources":["chat/signal.persona.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,eAAe,EAA8D,MAAM,iCAAiC,CAAC;AAC9H,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAErF,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAE/H,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAE9D,gFAAgF;AAChF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAE/D,6EAA6E;AAC7E,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,qEAAqE;IACrE,cAAc;IACd,eAAe;IACf,eAAe;IACf,eAAe;IACf,gBAAgB;IAChB,qBAAqB;IACrB,qBAAqB;IACrB,qBAAqB;IACrB,wBAAwB;IACxB,oBAAoB;IACpB,sBAAsB;IACtB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,qBAAqB;IACrB,eAAe;IACf,gBAAgB;IAChB,gBAAgB;IAChB,iBAAiB;IACjB,8CAA8C;IAC9C,eAAe;IACf,0BAA0B;IAC1B,8CAA8C;IAC9C,YAAY;IACZ,mBAAmB;CACX,CAAC;AAEX,MAAM,qBAAqB,GAAwB,IAAI,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAQ9E,SAAS,YAAY,CAAC,MAAsD;IAC1E,OAAO,OAAO,CACZ,MAAM;WACH,CAAC,MAAM,CAAC,UAAU;WAClB,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CACzD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,MAAoB,EAAE,QAAgB;IACtE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAChD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,2EAA2E;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAmD,EACnD,KAAa;IAEb,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,iBAAiB,EAAE,CAAC;IAChD,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;WACrD,CAAC,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,CAAC;AACvE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAA6B,KAAU;IACtE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAkB,EAClB,QAA4B;IAE5B,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,QAAQ,CAAC;IAE/C,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;QAChC,IAAI,UAAU,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACxC,OAAO,IAAI,CACT,KAAK,EAAE,KAAkD,EAAE,EAAE;gBAC3D,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAClD,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBAChD,MAAM,iBAAiB,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;gBAElD,IAAI,cAAc,EAAE,CAAC;oBACnB,OAAO,KAAK,CAAC,kHAAkH,CAAC,CAAC;gBACnI,CAAC;gBACD,IAAI,eAAe,IAAI,iBAAiB,IAAI,iBAAiB,KAAK,eAAe,EAAE,CAAC;oBAClF,OAAO,KAAK,CAAC,qEAAqE,CAAC,CAAC;gBACtF,CAAC;gBAED,MAAM,kBAAkB,GAAG,eAAe,IAAI,iBAAiB,CAAC;gBAChE,IAAI,kBAAkB,EAAE,CAAC;oBACvB,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAC,kBAAkB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;oBACpF,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,OAAO,KAAK,CAAC,+CAA+C,CAAC,CAAC;oBAChE,CAAC;gBACH,CAAC;gBAED,OAAO,UAAU,CAAC,MAAM,CAAC;oBACvB,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAChE,mEAAmE;oBACnE,WAAW,EAAE,KAAK;iBACnB,CAAoB,CAAC;YACxB,CAAC,EACD;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EACT,8JAA8J;gBAChK,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;oBACrF,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;iBACjG,CAAC,CAAC,MAAM,EAAE;aACZ,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACxC,OAAO,IAAI,CACT,KAAK,EAAE,KAAqC,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;gBACjE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,KAAK;aAClD,CAAoB,EACrB;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,2FAA2F;gBACxG,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;iBACxD,CAAC;aACH,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YAC7C,OAAO,IAAI,CACT,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAoB,EACpD;gBACE,IAAI,EAAE,oBAAoB;gBAC1B,WAAW,EAAE,wEAAwE;gBACrF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;aACrB,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACvC,OAAO,IAAI,CACT,KAAK,EAAE,KAAwC,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAoB,EAC/F;gBACE,IAAI,EAAE,cAAc;gBACpB,WAAW,EAAE,4DAA4D;gBACzE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;oBAClD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;iBACzC,CAAC;aACH,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YACzC,OAAO,IAAI,CACT,KAAK,EAAE,KAAwC,EAAE,EAAE;gBACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;gBAChC,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBAChD,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAElD,IAAI,cAAc,EAAE,CAAC;oBACnB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;oBAChE,MAAM,OAAO,GAAG,MAAM,IAAI,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC;wBAC9D,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;wBACpG,CAAC,CAAC,EAAE,CAAC;oBACP,OAAO,OAAO,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;gBACvD,CAAC;gBAED,IAAI,eAAe,EAAE,CAAC;oBACpB,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,eAAe,CAAC,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;wBACvE,OAAO,KAAK,CAAC,+CAA+C,CAAC,CAAC;oBAChE,CAAC;oBACD,MAAM,UAAU,GAAG,CAAC,MAAM,MAAM,CAAC,gBAAgB,EAAE,CAAC;yBACjD,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;oBAC9D,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,GAAG,CACxC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC,CACvF,CAAC;oBACF,OAAO,OAAO,CAAC;wBACb,OAAO,EAAE,UAAU;6BAChB,MAAM,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;6BACnD,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;qBACnB,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,OAAO,CAAC;oBACb,OAAO,EAAE,MAAM,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC;iBAC3D,CAAC,CAAC;YACL,CAAC,EACD;gBACE,IAAI,EAAE,gBAAgB;gBACtB,WAAW,EAAE,wFAAwF;gBACrG,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC/B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;iBACnD,CAAC,CAAC,MAAM,EAAE;aACZ,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACxC,OAAO,IAAI,CACT,KAAK,IAAI,EAAE;gBACT,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBAChD,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAClD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,qBAAqB,EAAE,CAAC;gBAEzD,IAAI,eAAe,EAAE,CAAC;oBACpB,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAC3C,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,KAAK,eAAe,CACzD,CAAC;oBACF,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACpC,OAAO,KAAK,CAAC,+CAA+C,CAAC,CAAC;oBAChE,CAAC;oBACD,OAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,kBAAkB,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;gBACvE,CAAC;gBAED,IAAI,cAAc,EAAE,CAAC;oBACnB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;oBAChE,IAAI,CAAC,MAAM,EAAE,CAAC;wBACZ,OAAO,KAAK,CAAC,oDAAoD,CAAC,CAAC;oBACrE,CAAC;oBACD,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAChC,MAAM,MAAM,CAAC,sBAAsB,CAAC,cAAc,CAAC,CACpD,CAAC;oBACF,OAAO,OAAO,CAAC;wBACb,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAC1C,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;wBAC/C,cAAc,EAAE,EAAE;qBACnB,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,OAAO,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,EAAE,CAAC,CAAC;YAChE,CAAC,EACD;gBACE,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,yGAAyG;gBACtH,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE;aAC9B,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,0BAA0B,EAAE,CAAC;YACnD,OAAO,IAAI,CACT,KAAK,EAAE,KAA6B,EAAE,EAAE;gBACtC,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAClD,MAAM,iBAAiB,GAAG,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;gBAClD,IAAI,eAAe,IAAI,iBAAiB,IAAI,iBAAiB,KAAK,eAAe,EAAE,CAAC;oBAClF,OAAO,KAAK,CAAC,qEAAqE,CAAC,CAAC;gBACtF,CAAC;gBACD,MAAM,kBAAkB,GAAG,iBAAiB,IAAI,eAAe,CAAC;gBAChE,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,qBAAqB,EAAE,CAAC;gBACzD,IAAI,kBAAkB,EAAE,CAAC;oBACvB,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAC3C,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,SAAS,KAAK,kBAAkB,CAC5D,CAAC;oBACF,IAAI,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBACpC,OAAO,KAAK,CAAC,+CAA+C,CAAC,CAAC;oBAChE,CAAC;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC9E,CAAC;gBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YAC1D,CAAC,EACD;gBACE,IAAI,EAAE,0BAA0B;gBAChC,WAAW,EAAE,6FAA6F;gBAC1G,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;iBACxC,CAAC,CAAC,MAAM,EAAE;aACZ,CACF,CAAC;QACJ,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,qBAAqB,EAAE,CAAC;YAC9C,OAAO,IAAI,CACT,KAAK,EAAE,KAA8C,EAAE,EAAE;gBACvD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACvC,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBACzC,MAAM,cAAc,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBAChD,MAAM,eAAe,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;gBAElD,IAAI,cAAc,IAAI,QAAQ,KAAK,cAAc,EAAE,CAAC;oBAClD,OAAO,KAAK,CAAC,kEAAkE,CAAC,CAAC;gBACnF,CAAC;gBACD,IAAI,eAAe,IAAI,SAAS,KAAK,eAAe,EAAE,CAAC;oBACrD,OAAO,KAAK,CAAC,qEAAqE,CAAC,CAAC;gBACtF,CAAC;gBAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAC1D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,KAAK,CAAC,oDAAoD,CAAC,CAAC;gBACrE,CAAC;gBACD,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;oBACjE,OAAO,KAAK,CAAC,+CAA+C,CAAC,CAAC;gBAChE,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBAC3E,OAAO,OAAO,CAAC;oBACb,UAAU,EAAE,QAAQ;oBACpB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;iBACjD,CAAC,CAAC;YACL,CAAC,EACD;gBACE,IAAI,EAAE,qBAAqB;gBAC3B,WAAW,EAAE,iFAAiF;gBAC9F,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;oBACf,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;oBAC3B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;iBAC7B,CAAC,CAAC,MAAM,EAAE;aACZ,CACF,CAAC;QACJ,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC,CAAc,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAiB,EACjB,kBAAwC;IAExC,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO;QAClD,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;QACtD,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,eAAe,GAAG,kBAAkB,IAAI,MAAM,kBAAkB,CAAC;QACrE,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,SAAS,EAAE,aAAa,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS;QACzF,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,eAAe,EAAE,IAAI,CAAC,eAAe;KACtC,CAAC,CAAC;IACH,IAAI,aAAa,CAAC,SAAS,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QACrD,eAAe,CAAC,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC;QACpD,eAAe,CAAC,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC;IAClD,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7F,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC,qBAAqB,EAAE,CAAC;IAC7D,MAAM,iBAAiB,GAAG,uBAAuB,CAAC;QAChD,WAAW,EAAE,eAAe;QAC5B,GAAG,CAAC,eAAe,CAAC,SAAS,IAAI,eAAe,CAAC,OAAO;YACtD,CAAC,CAAC,EAAE,SAAS,EAAE,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,EAAE;YAC5E,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ;WACzB,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,MAAM,EAAE,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxG,MAAM,OAAO,GAAG,iBAAiB,CAC/B,MAAM,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,CAChC,CAAC;IAEf,OAAO,iBAAiB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAsB;IAC/C,EAAE,EAAE,iBAAiB;IACrB,kBAAkB,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC;IAC5E,WAAW,EAAE,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,CAAC;IACtF,aAAa,EAAE;QACb,iFAAiF;QACjF,oBAAoB,EAAE,KAAK;QAC3B,mFAAmF;QACnF,qBAAqB,EAAE,IAAI;KAC5B;CACF,CAAC","sourcesContent":["import { tool } from \"@langchain/core/tools\";\nimport { z } from \"zod\";\n\nimport { createChatTools, type ChatTools, type ResolvedToolContext, type ToolContext } from \"../shared/agent/tool.factory.js\";\nimport { error, resolveChatContext, success } from \"../shared/agent/tool.helpers.js\";\nimport type { SystemDatabase, UserDatabase } from \"../shared/interfaces/database.interface.js\";\nimport { deriveAllowedNetworkIds, focusedIntentId, focusedNetworkId, scopeFromNetworkId } from \"../shared/agent/tool.scope.js\";\nimport type { ChatPersonaConfig } from \"./chat.persona.js\";\nimport { buildSignalSystemContent } from \"./signal.prompt.js\";\n\n/** Public kickoff marker used by New Signal surfaces to enter guided intake. */\nexport { SIGNAL_NEW_SIGNAL_KICKOFF } from \"./signal.prompt.js\";\n\n/** Stable persona id persisted for restricted Signal Agent conversations. */\nexport const SIGNAL_PERSONA_ID = \"signal\";\n\n/**\n * Exact positive tool allowlist for Signal Agent.\n *\n * New tools added to the shared chat registry remain unavailable until they are\n * reviewed and explicitly added here.\n */\nexport const SIGNAL_TOOL_NAMES = [\n // Signals and assignment to communities the user already belongs to.\n \"read_intents\",\n \"create_intent\",\n \"update_intent\",\n \"delete_intent\",\n \"search_intents\",\n \"read_intent_indexes\",\n \"create_intent_index\",\n \"delete_intent_index\",\n // User/profile context.\n \"read_user_contexts\",\n \"preview_user_context\",\n \"confirm_user_context\",\n \"create_user_context\",\n \"update_user_context\",\n // Premise knowledge.\n \"read_premises\",\n \"create_premise\",\n \"update_premise\",\n \"retract_premise\",\n // Read-only community and membership context.\n \"read_networks\",\n \"read_network_memberships\",\n // Pasted-link reading and chat clarification.\n \"scrape_url\",\n \"ask_user_question\",\n] as const;\n\nconst SIGNAL_TOOL_ALLOWLIST: ReadonlySet<string> = new Set(SIGNAL_TOOL_NAMES);\n\ninterface SignalToolBoundary {\n context: ResolvedToolContext;\n userDb: UserDatabase;\n systemDb: SystemDatabase;\n}\n\nfunction isLiveIntent(intent: Awaited<ReturnType<UserDatabase[\"getIntent\"]>>): intent is NonNullable<typeof intent> {\n return Boolean(\n intent\n && !intent.archivedAt\n && (intent.status == null || intent.status === \"ACTIVE\"),\n );\n}\n\nasync function getOwnedLiveIntent(userDb: UserDatabase, intentId: string) {\n try {\n const intent = await userDb.getIntent(intentId);\n return isLiveIntent(intent) ? intent : null;\n } catch {\n // The context-bound adapter throws for foreign IDs. Collapse missing,\n // foreign, archived, and non-live rows to the same non-enumerating result.\n return null;\n }\n}\n\nfunction matchesIntentText(\n intent: { payload: string; summary: string | null },\n query: string,\n): boolean {\n const needle = query.trim().toLocaleLowerCase();\n return intent.payload.toLocaleLowerCase().includes(needle)\n || (intent.summary?.toLocaleLowerCase().includes(needle) ?? false);\n}\n\n/**\n * Filters shared chat tools through Signal Agent's positive allowlist.\n *\n * @param tools - Shared context-bound chat tools\n * @returns Only explicitly approved Signal Agent tools\n */\nexport function filterSignalTools<T extends { name: string }>(tools: T[]): T[] {\n return tools.filter((candidate) => SIGNAL_TOOL_ALLOWLIST.has(candidate.name));\n}\n\n/**\n * Narrows schemas and handlers whose shared versions expose broader modes than\n * Signal Agent is allowed to use.\n *\n * @param allowed - Name-allowlisted shared chat tools\n * @param boundary - Authoritative context-bound databases for Signal-only checks\n * @returns Signal-safe tools with self-only reads and proposal-only creation\n */\nexport function narrowSignalTools(\n allowed: ChatTools,\n boundary: SignalToolBoundary,\n): ChatTools {\n const { context, userDb, systemDb } = boundary;\n\n return allowed.map((sharedTool) => {\n if (sharedTool.name === \"create_intent\") {\n return tool(\n async (query: { description: string; networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n const scopedIntentId = focusedIntentId(context);\n const explicitNetworkId = query.networkId?.trim();\n\n if (scopedIntentId) {\n return error(\"This chat is scoped to an existing selected intent. Update that intent instead of creating a different one here.\");\n }\n if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n\n const effectiveNetworkId = scopedNetworkId ?? explicitNetworkId;\n if (effectiveNetworkId) {\n const isMember = await systemDb.isNetworkMember(effectiveNetworkId, context.userId);\n if (!isMember) {\n return error(\"You are no longer a member of this community.\");\n }\n }\n\n return sharedTool.invoke({\n description: query.description,\n ...(effectiveNetworkId ? { networkId: effectiveNetworkId } : {}),\n // Signal web chats always use the confirmation-safe proposal path.\n autoApprove: false,\n }) as Promise<string>;\n },\n {\n name: \"create_intent\",\n description:\n \"Draft a new signal for the current user. Returns an intent_proposal card that must be passed through verbatim and approved in the web UI before persistence.\",\n schema: z.object({\n description: z.string().trim().min(1).describe(\"Clear, specific signal description.\"),\n networkId: z.string().uuid().optional().describe(\"Optional existing-membership community UUID.\"),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_premises\") {\n return tool(\n async (query: { includeRetracted?: boolean }) => sharedTool.invoke({\n includeRetracted: query.includeRetracted ?? false,\n }) as Promise<string>,\n {\n name: \"read_premises\",\n description: \"Read only the current user's premises. Use before creating or updating profile knowledge.\",\n schema: z.object({\n includeRetracted: z.boolean().optional().default(false),\n }),\n },\n );\n }\n\n if (sharedTool.name === \"read_user_contexts\") {\n return tool(\n async () => sharedTool.invoke({}) as Promise<string>,\n {\n name: \"read_user_contexts\",\n description: \"Read only the current user's identity and synthesized profile context.\",\n schema: z.object({}),\n },\n );\n }\n\n if (sharedTool.name === \"read_intents\") {\n return tool(\n async (query: { limit?: number; page?: number }) => sharedTool.invoke(query) as Promise<string>,\n {\n name: \"read_intents\",\n description: \"Read the current user's own signals, optionally paginated.\",\n schema: z.object({\n limit: z.number().int().min(1).max(100).optional(),\n page: z.number().int().min(1).optional(),\n }),\n },\n );\n }\n\n if (sharedTool.name === \"search_intents\") {\n return tool(\n async (query: { query: string; limit?: number }) => {\n const limit = query.limit ?? 25;\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n\n if (scopedIntentId) {\n const intent = await getOwnedLiveIntent(userDb, scopedIntentId);\n const intents = intent && matchesIntentText(intent, query.query)\n ? [{ id: intent.id, payload: intent.payload, summary: intent.summary, createdAt: intent.createdAt }]\n : [];\n return success({ intents: intents.slice(0, limit) });\n }\n\n if (scopedNetworkId) {\n if (!(await systemDb.isNetworkMember(scopedNetworkId, context.userId))) {\n return error(\"You are no longer a member of this community.\");\n }\n const candidates = (await userDb.getActiveIntents())\n .filter((intent) => matchesIntentText(intent, query.query));\n const assignmentChecks = await Promise.all(\n candidates.map((intent) => userDb.isIntentAssignedToIndex(intent.id, scopedNetworkId)),\n );\n return success({\n intents: candidates\n .filter((_intent, index) => assignmentChecks[index])\n .slice(0, limit),\n });\n }\n\n return success({\n intents: await userDb.searchOwnIntents(query.query, limit),\n });\n },\n {\n name: \"search_intents\",\n description: \"Search the current user's own active signals by text within the selected Signal scope.\",\n schema: z.object({\n query: z.string().trim().min(1),\n limit: z.number().int().min(1).max(100).optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_networks\") {\n return tool(\n async () => {\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n const memberships = await userDb.getNetworkMemberships();\n\n if (scopedNetworkId) {\n const focusedMemberships = memberships.filter(\n (membership) => membership.networkId === scopedNetworkId,\n );\n if (focusedMemberships.length === 0) {\n return error(\"You are no longer a member of this community.\");\n }\n return success({ memberOf: focusedMemberships, publicNetworks: [] });\n }\n\n if (scopedIntentId) {\n const intent = await getOwnedLiveIntent(userDb, scopedIntentId);\n if (!intent) {\n return error(\"The selected intent is not an owned active signal.\");\n }\n const assignedNetworkIds = new Set(\n await userDb.getNetworkIdsForIntent(scopedIntentId),\n );\n return success({\n memberOf: memberships.filter((membership) =>\n assignedNetworkIds.has(membership.networkId)),\n publicNetworks: [],\n });\n }\n\n return success({ memberOf: memberships, publicNetworks: [] });\n },\n {\n name: \"read_networks\",\n description: \"List only communities the current user is presently a member of. Public communities are never included.\",\n schema: z.object({}).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_network_memberships\") {\n return tool(\n async (query: { networkId?: string }) => {\n const scopedNetworkId = focusedNetworkId(context);\n const explicitNetworkId = query.networkId?.trim();\n if (scopedNetworkId && explicitNetworkId && explicitNetworkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n const effectiveNetworkId = explicitNetworkId ?? scopedNetworkId;\n const memberships = await userDb.getNetworkMemberships();\n if (effectiveNetworkId) {\n const focusedMemberships = memberships.filter(\n (membership) => membership.networkId === effectiveNetworkId,\n );\n if (focusedMemberships.length === 0) {\n return error(\"You are no longer a member of this community.\");\n }\n return success({ userId: context.userId, memberships: focusedMemberships });\n }\n return success({ userId: context.userId, memberships });\n },\n {\n name: \"read_network_memberships\",\n description: \"Read only the current user's present community memberships. This never lists other members.\",\n schema: z.object({\n networkId: z.string().uuid().optional(),\n }).strict(),\n },\n );\n }\n\n if (sharedTool.name === \"read_intent_indexes\") {\n return tool(\n async (query: { intentId: string; networkId: string }) => {\n const intentId = query.intentId.trim();\n const networkId = query.networkId.trim();\n const scopedIntentId = focusedIntentId(context);\n const scopedNetworkId = focusedNetworkId(context);\n\n if (scopedIntentId && intentId !== scopedIntentId) {\n return error(\"The requested intent conflicts with this chat's selected intent.\");\n }\n if (scopedNetworkId && networkId !== scopedNetworkId) {\n return error(\"The requested network conflicts with this chat's focused community.\");\n }\n\n const intent = await getOwnedLiveIntent(userDb, intentId);\n if (!intent) {\n return error(\"The selected intent is not an owned active signal.\");\n }\n if (!(await systemDb.isNetworkMember(networkId, context.userId))) {\n return error(\"You are no longer a member of this community.\");\n }\n\n const assigned = await userDb.isIntentAssignedToIndex(intentId, networkId);\n return success({\n isAssigned: assigned,\n links: assigned ? [{ intentId, networkId }] : [],\n });\n },\n {\n name: \"read_intent_indexes\",\n description: \"Check one exact owned active signal-to-current-membership community assignment.\",\n schema: z.object({\n intentId: z.string().uuid(),\n networkId: z.string().uuid(),\n }).strict(),\n },\n );\n }\n\n return sharedTool;\n }) as ChatTools;\n}\n\n/**\n * Creates Signal Agent's context-bound restricted toolset.\n *\n * @param deps - Shared tool dependencies\n * @param preResolvedContext - Optional authoritative resolved context\n * @returns The allowlisted and schema-narrowed Signal Agent tools\n */\nexport async function createSignalTools(\n deps: ToolContext,\n preResolvedContext?: ResolvedToolContext,\n): Promise<ChatTools> {\n const explicitScope = deps.scopeType && deps.scopeId\n ? { scopeType: deps.scopeType, scopeId: deps.scopeId }\n : scopeFromNetworkId(deps.networkId);\n const resolvedContext = preResolvedContext ?? await resolveChatContext({\n database: deps.database,\n userId: deps.userId,\n networkId: explicitScope.scopeType === \"network\" ? explicitScope.scopeId : deps.networkId,\n sessionId: deps.sessionId,\n contactsEnabled: deps.contactsEnabled,\n });\n if (explicitScope.scopeType && explicitScope.scopeId) {\n resolvedContext.scopeType = explicitScope.scopeType;\n resolvedContext.scopeId = explicitScope.scopeId;\n }\n\n const userDb = deps.userDb ?? deps.createUserDatabase(deps.database, resolvedContext.userId);\n const liveMemberships = await userDb.getNetworkMemberships();\n const allowedNetworkIds = deriveAllowedNetworkIds({\n memberships: liveMemberships,\n ...(resolvedContext.scopeType && resolvedContext.scopeId\n ? { scopeType: resolvedContext.scopeType, scopeId: resolvedContext.scopeId }\n : {}),\n });\n const systemDb = deps.systemDb\n ?? deps.createSystemDatabase(deps.database, resolvedContext.userId, allowedNetworkIds, deps.embedder);\n const allowed = filterSignalTools(\n await createChatTools(deps, resolvedContext),\n ) as ChatTools;\n\n return narrowSignalTools(allowed, { context: resolvedContext, userDb, systemDb });\n}\n\n/** Restricted Signal Agent persona on the persona-neutral chat runtime. */\nexport const SIGNAL_PERSONA: ChatPersonaConfig = {\n id: SIGNAL_PERSONA_ID,\n buildSystemContent: (ctx, iterCtx) => buildSignalSystemContent(ctx, iterCtx),\n createTools: (deps, preResolvedContext) => createSignalTools(deps, preResolvedContext),\n loopBehaviors: {\n // Direct discovery is absent, so its create-intent retry callback must stay off.\n createIntentCallback: false,\n // create_intent can legitimately return proposal cards; retain recovery/stripping.\n hallucinationRecovery: true,\n },\n};\n"]}