@indexnetwork/protocol 6.15.0-rc.407.1 → 8.0.2-rc.418.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.
Files changed (110) hide show
  1. package/CHANGELOG.md +309 -0
  2. package/IMPLEMENTATION.md +7 -4
  3. package/dist/capabilities/opportunities.facade.d.ts +4 -0
  4. package/dist/capabilities/opportunities.facade.js +4 -0
  5. package/dist/chat/chat-streaming.types.d.ts +4 -5
  6. package/dist/chat/chat.agent.d.ts +0 -19
  7. package/dist/chat/chat.agent.js +1 -70
  8. package/dist/chat/chat.persona.d.ts +0 -6
  9. package/dist/chat/chat.persona.js +0 -1
  10. package/dist/chat/chat.prompt.js +10 -10
  11. package/dist/chat/chat.prompt.modules.js +0 -104
  12. package/dist/chat/chat.tools.js +11 -7
  13. package/dist/chat/negotiator.persona.js +0 -1
  14. package/dist/chat/onboarding.persona.js +0 -1
  15. package/dist/chat/reporter.persona.d.ts +1 -1
  16. package/dist/chat/reporter.persona.js +1 -2
  17. package/dist/chat/reporter.prompt.js +5 -5
  18. package/dist/chat/signal.persona.js +0 -1
  19. package/dist/communities/application/network.tools.js +48 -27
  20. package/dist/contacts/application/contact.tools.js +9 -9
  21. package/dist/index.d.ts +9 -2
  22. package/dist/index.js +5 -0
  23. package/dist/maintenance/maintenance.graph.js +7 -0
  24. package/dist/mcp/mcp.authorization-policy.d.ts +250 -0
  25. package/dist/mcp/mcp.authorization-policy.js +522 -0
  26. package/dist/mcp/mcp.server.d.ts +6 -29
  27. package/dist/mcp/mcp.server.js +202 -228
  28. package/dist/negotiation/application/negotiation.agent.js +29 -5
  29. package/dist/negotiation/application/negotiation.graph.d.ts +2 -5
  30. package/dist/negotiation/application/negotiation.graph.js +53 -23
  31. package/dist/negotiation/domain/index.d.ts +3 -0
  32. package/dist/negotiation/domain/index.js +3 -0
  33. package/dist/negotiation/domain/negotiation.deadlock.d.ts +7 -1
  34. package/dist/negotiation/domain/negotiation.deadlock.js +48 -1
  35. package/dist/negotiation/domain/negotiation.stance.contracts.d.ts +85 -0
  36. package/dist/negotiation/domain/negotiation.stance.contracts.js +140 -0
  37. package/dist/negotiation/domain/negotiation.state.d.ts +2 -3
  38. package/dist/negotiation/domain/negotiation.state.js +2 -3
  39. package/dist/negotiation/public/index.d.ts +2 -0
  40. package/dist/negotiation/public/index.js +2 -0
  41. package/dist/opportunity/application/index.d.ts +0 -3
  42. package/dist/opportunity/application/index.js +0 -3
  43. package/dist/opportunity/application/opportunity.graph.d.ts +0 -43
  44. package/dist/opportunity/application/opportunity.graph.js +7 -113
  45. package/dist/opportunity/application/opportunity.introducer-feature.d.ts +2 -0
  46. package/dist/opportunity/application/opportunity.introducer-feature.js +4 -0
  47. package/dist/opportunity/application/opportunity.owner-approval.d.ts +118 -0
  48. package/dist/opportunity/application/opportunity.owner-approval.js +34 -0
  49. package/dist/opportunity/application/opportunity.owner-provenance.d.ts +6 -0
  50. package/dist/opportunity/application/opportunity.owner-provenance.js +28 -0
  51. package/dist/opportunity/application/opportunity.tools.d.ts +3 -3
  52. package/dist/opportunity/application/opportunity.tools.js +77 -870
  53. package/dist/opportunity/discriminator/discriminator.shadow.js +17 -10
  54. package/dist/opportunity/discriminator/discriminator.types.d.ts +2 -0
  55. package/dist/opportunity/domain/index.d.ts +0 -2
  56. package/dist/opportunity/domain/index.js +0 -2
  57. package/dist/opportunity/domain/opportunity.state.d.ts +2 -70
  58. package/dist/opportunity/domain/opportunity.state.js +2 -55
  59. package/dist/opportunity/opportunity.owner-approval.d.ts +5 -0
  60. package/dist/opportunity/opportunity.owner-approval.js +5 -0
  61. package/dist/opportunity/ports/opportunity.tools.port.d.ts +12 -1
  62. package/dist/opportunity/public/index.d.ts +3 -3
  63. package/dist/opportunity/public/index.js +2 -1
  64. package/dist/participant-agents/application/agent.tools.d.ts +1 -1
  65. package/dist/participant-agents/application/agent.tools.js +44 -6
  66. package/dist/participant-agents/application/index.d.ts +5 -3
  67. package/dist/participant-agents/application/index.js +5 -3
  68. package/dist/questions/application/question.tools.js +31 -2
  69. package/dist/runtime/foreground/composition/tool.factory.d.ts +2 -1
  70. package/dist/runtime/foreground/composition/tool.factory.js +9 -2
  71. package/dist/runtime/foreground/composition/tool.registry.d.ts +16 -2
  72. package/dist/runtime/foreground/composition/tool.registry.js +41 -38
  73. package/dist/shared/agent/activity-projection.d.ts +271 -0
  74. package/dist/shared/agent/activity-projection.js +248 -0
  75. package/dist/shared/agent/canonical-guidance.d.ts +30 -0
  76. package/dist/shared/agent/canonical-guidance.js +249 -0
  77. package/dist/shared/agent/tool.helpers.d.ts +9 -17
  78. package/dist/shared/agent/tool.runtime.js +1 -3
  79. package/dist/shared/agent/utility.tools.d.ts +11 -1
  80. package/dist/shared/agent/utility.tools.js +126 -90
  81. package/dist/shared/interfaces/auth.interface.d.ts +2 -8
  82. package/dist/shared/interfaces/chat-session.interface.js +5 -0
  83. package/dist/shared/interfaces/database.interface.d.ts +12 -5
  84. package/dist/shared/observability/request-context.d.ts +4 -40
  85. package/dist/shared/schemas/mcp-auth.schema.d.ts +87 -15
  86. package/dist/shared/schemas/mcp-auth.schema.js +49 -1
  87. package/dist/signals/application/intent.tools.js +7 -8
  88. package/package.json +10 -9
  89. package/dist/opportunity/application/opportunity.discover.d.ts +0 -215
  90. package/dist/opportunity/application/opportunity.discover.js +0 -793
  91. package/dist/opportunity/application/opportunity.discovery-continuation-finalization.d.ts +0 -34
  92. package/dist/opportunity/application/opportunity.discovery-continuation-finalization.js +0 -74
  93. package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -44
  94. package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.js +0 -92
  95. package/dist/opportunity/domain/opportunity.discovery-run-coalescing.d.ts +0 -33
  96. package/dist/opportunity/domain/opportunity.discovery-run-coalescing.js +0 -33
  97. package/dist/opportunity/domain/opportunity.discovery.contracts.d.ts +0 -45
  98. package/dist/opportunity/domain/opportunity.discovery.contracts.js +0 -1
  99. package/dist/opportunity/opportunity.discover.d.ts +0 -5
  100. package/dist/opportunity/opportunity.discover.js +0 -5
  101. package/dist/opportunity/opportunity.discovery-continuation-finalization.d.ts +0 -5
  102. package/dist/opportunity/opportunity.discovery-continuation-finalization.js +0 -5
  103. package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -5
  104. package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.js +0 -5
  105. package/dist/opportunity/opportunity.discovery-run-coalescing.d.ts +0 -5
  106. package/dist/opportunity/opportunity.discovery-run-coalescing.js +0 -5
  107. package/dist/opportunity/opportunity.discovery.contracts.d.ts +0 -5
  108. package/dist/opportunity/opportunity.discovery.contracts.js +0 -5
  109. package/dist/shared/interfaces/discovery-run.interface.d.ts +0 -70
  110. package/dist/shared/interfaces/discovery-run.interface.js +0 -1
@@ -1,9 +1,20 @@
1
1
  import { z } from "zod";
2
2
  import { requestContext } from "../observability/request-context.js";
3
+ import { ActivitySummaryResponseSchema, McpActivityCallerSchema, activitySummaryNetworkId, projectActivitySummary } from "./activity-projection.js";
4
+ import { CANONICAL_GUIDANCE_SUMMARY, CANONICAL_GUIDANCE_TOPICS, CANONICAL_GUIDANCE_TOPICS_CONTENT } from "./canonical-guidance.js";
3
5
  import { success, error, normalizeUrl } from "./tool.helpers.js";
4
- export function createUtilityTools(defineTool, deps) {
6
+ /** Owner-trusted surfaces (REST/chat) receive the full owner view. */
7
+ const HUMAN_OWNER_CALLER = {
8
+ kind: "human",
9
+ permissions: [],
10
+ networkScopeId: null,
11
+ };
12
+ export function createUtilityTools(defineTool, deps, options = {}) {
5
13
  const { scraper } = deps;
6
- const scrapeUrl = defineTool({
14
+ const isMcpSurface = options.surface === "mcp";
15
+ // scrape_url is omitted from the MCP tool surface (IND-597). It remains
16
+ // available via the direct HTTP Tool API and the chat agent.
17
+ const scrapeUrl = isMcpSurface ? null : defineTool({
7
18
  name: "scrape_url",
8
19
  description: "Extracts text content from a web URL — articles, LinkedIn/GitHub profiles, documentation, project pages, etc. " +
9
20
  "Returns the page's text content (up to 10,000 characters) for use in subsequent tool calls.\n\n" +
@@ -44,20 +55,65 @@ export function createUtilityTools(defineTool, deps) {
44
55
  name: "read_docs",
45
56
  description: "Returns comprehensive documentation about the Index Network protocol — entity model, workflows, tool usage guidance, and domain concepts. " +
46
57
  "This is the primary way for an external agent to bootstrap understanding of the system.\n\n" +
47
- "**When to use:** Call this FIRST when starting a new session. MCP agents MUST call read_docs(topic='mcp_agent_guide') at the start of every conversation to learn proper output formatting and workflow rules.\n" +
48
- "Also call when you need to understand:\n" +
49
- "- What entities exist and how they relate (intents, indexes, opportunities, profiles, contacts)\n" +
50
- "- The discovery workflow (how intents become opportunities)\n" +
51
- "- Which tools to call in what order for common tasks\n" +
52
- "- Authentication and API patterns\n\n" +
53
- "**Returns:** Markdown documentation. Pass `topic` to get a specific section, or omit for the full reference.\n\n" +
54
- "**Available topics:** 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', 'authentication', 'mcp_agent_guide'",
58
+ "**When to use:** Call this FIRST when starting a new session.\n" +
59
+ "Also call when you need to understand identity, context, premises, signals, communities, networks, opportunities, or negotiations.\n\n" +
60
+ "**Returns:** Markdown documentation. Pass `topic` to get a specific section, or omit for the summary.\n\n" +
61
+ `**Available canonical topics:** ${CANONICAL_GUIDANCE_TOPICS.join(", ")}`,
55
62
  querySchema: z.object({
56
- topic: z.string().optional().describe("Narrow to a specific topic: 'entities', 'intents', 'opportunities', 'indexes', 'profiles', 'contacts', 'discovery', 'workflows', or 'authentication'. Omit to get the full documentation."),
63
+ topic: z.string().optional().describe(`Narrow to a canonical topic: ${CANONICAL_GUIDANCE_TOPICS.join(", ")}. Omit to get the summary.`),
57
64
  }),
58
65
  handler: async ({ context: _context, query }) => {
59
66
  const topic = query.topic?.trim().toLowerCase();
67
+ // Canonical guidance is the MCP read_docs foundation. When on MCP surface,
68
+ // legacy supplemental topics (entities, intents, opportunities, etc.) are
69
+ // omitted to avoid repeating the canonical source (IND-602/603).
70
+ // REST/chat surfaces retain full topic coverage for backwards compatibility.
71
+ if (isMcpSurface) {
72
+ // MCP surface: use canonical guidance only.
73
+ if (!topic) {
74
+ // Return summary of canonical guidance
75
+ return success({ content: CANONICAL_GUIDANCE_SUMMARY });
76
+ }
77
+ // Try to match canonical topic
78
+ const normalizedTopic = topic.replace(/_/g, "-").toLowerCase();
79
+ for (const canonicalTopic of CANONICAL_GUIDANCE_TOPICS) {
80
+ if (canonicalTopic === normalizedTopic || normalizedTopic.includes(canonicalTopic.split("-")[0])) {
81
+ return success({ topic: canonicalTopic, content: CANONICAL_GUIDANCE_TOPICS_CONTENT[canonicalTopic] });
82
+ }
83
+ }
84
+ // Unknown topic on MCP surface
85
+ return success({
86
+ content: `Unknown canonical topic "${topic}". Available topics: ${CANONICAL_GUIDANCE_TOPICS.join(", ")}. Request summary for full canonical guidance.`,
87
+ });
88
+ }
89
+ // REST/chat surfaces: provide full legacy documentation alongside canonical.
90
+ // Contact management tools are not exposed on the MCP surface (IND-596), so
91
+ // full guidance includes contact workflows. This is for REST/chat only.
92
+ const contactsSection = `## Contact Management
93
+
94
+ Contacts are people in a user's personal network, stored as members of their personal network with 'contact' permission.
95
+
96
+ - **Adding contacts**: Via import_contacts (bulk), add_contact (single email), or import_gmail_contacts (Google integration).
97
+ - **Ghost users**: When a contact email doesn't match an existing account, a ghost user is created. Ghost users are enriched with public profile data and participate in opportunity matching — they can be discovered even before joining the platform.
98
+ - **Personal network scope**: Approved signals from personal-network contacts are matched in the background. Use list_opportunities only to review persisted results.
99
+ - **Contact data**: Each contact has userId, name, email, avatar, and isGhost flag.
100
+
101
+ ### Contact Workflow
102
+ 1. import_contacts or import_gmail_contacts → bulk add to network
103
+ 2. list_contacts → view all contacts with userId
104
+ 3. create_intent or update_intent → create or refine an approved signal for background matching
105
+ 4. list_opportunities → review persisted results when background processing finds matches
106
+ 5. add_contact(email) → add individual contact
107
+ 6. remove_contact(contactUserId) → remove from network`;
108
+ const managingContactsWorkflow = `### Managing Contacts
109
+ 1. import_gmail_contacts() or import_contacts([...]) → add contacts
110
+ 2. list_contacts() → view network
111
+ 3. create_intent or update_intent → create or refine an approved signal for background matching
112
+ 4. list_opportunities → review persisted results
113
+
114
+ `;
60
115
  const sections = {
116
+ // Legacy topics (REST/chat only)
61
117
  entities: `## Entity Model & Relationships
62
118
 
63
119
  - **Users**: People on the platform. Authenticated via API key (X-API-Key header) for MCP/external agents, or session-based (Better Auth) for the web app.
@@ -98,7 +154,7 @@ Intents are the core unit of discovery — they represent what users are seeking
98
154
 
99
155
  Opportunities represent discovered connections between users — potential matches worth pursuing.
100
156
 
101
- 1. **Detection** (discover_opportunities): The opportunity graph finds users whose intents semantically complement each other within shared networks. Uses HyDE embeddings for retrieval and an LLM evaluator for scoring.
157
+ 1. **Background matching**: The opportunity graph evaluates approved signals whose intents semantically complement each other within shared networks. It uses HyDE embeddings for retrieval and an LLM evaluator for scoring.
102
158
  2. **Roles**: Each opportunity assigns roles to actors:
103
159
  - **introducer**: The person who triggered the introduction (may be the system or another user)
104
160
  - **party**: The people being connected (typically 2)
@@ -108,16 +164,15 @@ Opportunities represent discovered connections between users — potential match
108
164
  - **accepted**: Both parties agreed to connect.
109
165
  - **rejected**: One party declined.
110
166
  - **expired**: Timed out without response.
111
- 4. **Creation Modes**:
112
- - **Discovery**: Automatic — system finds matches based on intent overlap (discover_opportunities with searchQuery)
113
- - **Introduction**: Manual — a user introduces two specific people (discover_opportunities with partyUserIds + entities)
114
- - **Direct**: One-to-one — connect with a specific person (discover_opportunities with targetUserId)
167
+ 4. **Creation**: Opportunities are created by background matching after approved signals are created or refined. list_opportunities only reviews persisted cards; it never starts matching or targets a person.
115
168
  5. **Presentation**: Each opportunity includes personalized match reasoning, confidence score, and suggested next action.
116
169
 
117
170
  ### Opportunity Workflow
118
- 1. discover_opportunities(searchQuery="AI engineers") → returns draft opportunity cards
119
- 2. update_opportunity(opportunityId, status="pending")sends to other party
120
- 3. Other party sees opportunity calls update_opportunity(status="accepted" or "rejected")`,
171
+ 1. create_intent(description) or update_intent(intentId, description) → create or refine an approved signal
172
+ 2. background matchingpersists opportunity cards when matches are found
173
+ 3. list_opportunities()review persisted cards
174
+ 4. update_opportunity(opportunityId, status="pending") → sends to other party
175
+ 5. Other party sees opportunity → calls update_opportunity(status="accepted" or "rejected")`,
121
176
  indexes: `## Index Mechanics
122
177
 
123
178
  Indexes (also called "networks") are communities where members share what they're looking for and the system discovers connections between them.
@@ -133,7 +188,7 @@ Indexes (also called "networks") are communities where members share what they'r
133
188
  1. create_network(title, prompt) → creates new community, you become owner
134
189
  2. create_network_membership(networkId, userId) → invite members
135
190
  3. Members create intents → auto-assigned to the index based on prompt
136
- 4. discover_opportunities(networkId) discover matches within this community`,
191
+ 4. Members' approved signals are matched in the background; list_opportunities only reviews persisted results`,
137
192
  profiles: `## Profile System
138
193
 
139
194
  Profiles are the user's identity on the platform, used for semantic matching in opportunity discovery.
@@ -152,33 +207,19 @@ Profiles are the user's identity on the platform, used for semantic matching in
152
207
  - Richer profiles produce better opportunity matches
153
208
  - Social links enable enrichment — encourage users to add LinkedIn/GitHub
154
209
  - Profiles are recalculated when updated, which may surface new matches`,
155
- contacts: `## Contact Management
156
-
157
- Contacts are people in a user's personal network, stored as members of their personal network with 'contact' permission.
158
-
159
- - **Adding contacts**: Via import_contacts (bulk), add_contact (single email), or import_gmail_contacts (Google integration).
160
- - **Ghost users**: When a contact email doesn't match an existing account, a ghost user is created. Ghost users are enriched with public profile data and participate in opportunity matching — they can be discovered even before joining the platform.
161
- - **Personal network scope**: Pass the personal network networkId to discover_opportunities to scope discovery to just the user's contacts.
162
- - **Contact data**: Each contact has userId, name, email, avatar, and isGhost flag.
163
-
164
- ### Contact Workflow
165
- 1. import_contacts or import_gmail_contacts → bulk add to network
166
- 2. list_contacts → view all contacts with userId
167
- 3. discover_opportunities(networkId=personalIndexId) → find matches among contacts
168
- 4. add_contact(email) → add individual contact
169
- 5. remove_contact(contactUserId) → remove from network`,
210
+ contacts: contactsSection,
170
211
  discovery: `## Discovery Mechanics
171
212
 
172
213
  Discovery is the process of finding meaningful connections between users based on their intents and profiles.
173
214
 
174
215
  ### How Discovery Works
175
- 1. **Trigger**: Runs automatically when an intent is created, or explicitly when discover_opportunities is called.
216
+ 1. **Trigger**: Runs automatically when an approved signal is created or refined.
176
217
  2. **Pipeline**: Preparation (gather user context) → Scope (determine which indexes to search) → Candidate retrieval (semantic matching via HyDE embeddings) → Evaluation (LLM scores relevance and complementarity) → Ranking → Persist as opportunities.
177
218
  3. **Semantic matching**: Uses HyDE (Hypothetical Document Embeddings) to find candidate intents that complement the source. This goes beyond keyword matching — it understands conceptual relationships.
178
219
  4. **Evaluation**: An LLM evaluator agent scores each candidate match on relevance, complementarity, and actionability. Low-scoring matches are filtered out.
179
220
  5. **Results**: Persisted as draft opportunities with roles, reasoning, and confidence scores.
180
221
  6. **Background processing**: After intent creation, a queue job continues looking for matches asynchronously.
181
- 7. **Pagination**: Large result sets are paginated. Use continueFrom with the discoveryId to evaluate more candidates.
222
+ 7. **Review**: Use list_opportunities to review persisted actionable cards; it does not run matching.
182
223
 
183
224
  ### Discovery Best Practices
184
225
  - More specific intents produce more relevant matches
@@ -193,29 +234,25 @@ Discovery is the process of finding meaningful connections between users based o
193
234
  3. read_networks() → see available communities
194
235
  4. create_network_membership(networkId) → join a community
195
236
  5. create_intent(description) → post what you're looking for
196
- 6. discover_opportunities(searchQuery) find matches
237
+ 6. Background matching evaluates the approved signal; list_opportunities reviews persisted results
197
238
 
198
239
  ### Finding Connections
199
240
  1. read_networks() → list user's communities (get networkId)
200
- 2. discover_opportunities(searchQuery, networkId) → discover matches
201
- 3. Review opportunity cards update_opportunity(opportunityId, status="pending") to send
241
+ 2. create_intent(description) or update_intent(intentId, description) → create or refine an approved signal in the relevant network
242
+ 3. Background matching persists eligible cards; list_opportunities reviews them
243
+ 4. update_opportunity(opportunityId, status="pending") → send a persisted card
202
244
 
203
- ### Making an Introduction
204
- 1. read_network_memberships(networkId) → find members in shared community
205
- 2. read_user_contexts(userId) → get profiles of both parties
206
- 3. read_intents(networkId, userId) get intents of both parties
207
- 4. discover_opportunities(partyUserIds=[id1,id2], entities=[...], hint="reason") → create introduction
245
+ ### Helping Connections Emerge
246
+ 1. read_network_memberships(networkId) → understand the shared community
247
+ 2. create_intent(description) or update_intent(intentId, description) capture or refine an approved signal
248
+ 3. Background matching evaluates approved signals
249
+ 4. list_opportunities() → review persisted results
208
250
 
209
- ### Managing Contacts
210
- 1. import_gmail_contacts() or import_contacts([...]) → add contacts
211
- 2. list_contacts() → view network
212
- 3. discover_opportunities(networkId=personalIndexId) → find matches among contacts
213
-
214
- ### Creating a Community
251
+ ${managingContactsWorkflow}### Creating a Community
215
252
  1. create_network(title, prompt) → create network
216
253
  2. create_network_membership(networkId, userId) → invite members
217
254
  3. Members create intents → auto-indexed
218
- 4. discover_opportunities(networkId) discover connections within community`,
255
+ 4. Members' approved signals are matched in the background; list_opportunities only reviews persisted results`,
219
256
  authentication: `## Authentication & API Access
220
257
 
221
258
  ### For External AI Agents (MCP)
@@ -235,57 +272,56 @@ Discovery is the process of finding meaningful connections between users based o
235
272
  - Avoid unnecessary read_intents/read_networks calls — cache results within a conversation
236
273
  - Use pagination (limit/page) for large result sets
237
274
  - Call read_docs once at the start to understand the domain`,
238
- mcp_agent_guide: `## MCP Agent Integration Guide
239
-
240
- **IMPORTANT: Read this section if you are an AI agent accessing Index Network via MCP tools.**
241
-
242
- ### Output Formatting
243
- - Tool results often contain structured JSON data (proposals, opportunities, cards). **Do NOT dump raw JSON to the user.** Parse the JSON and present information in natural language with clear formatting.
244
- - Some tool results contain interactive card markup (code blocks with \`intent_proposal\`, \`opportunity_card\` language tags). These are designed for the Index Network web UI. **As an MCP agent, ignore card markup.** Instead, extract the meaningful data from the JSON and present it conversationally.
245
- - When presenting opportunities or intents, use bullet points or short paragraphs — not raw JSON objects.
246
-
247
- ### Intent Creation Workflow
248
- - **Always pass \`autoApprove: true\` when calling \`create_intent\`.** This persists intents directly without returning proposal cards that require manual UI approval.
249
- - The tool will return a list of created intents with their descriptions and confidence scores. Present these in natural language.
250
- - Do not tell the user to "click on cards" or "approve above" — there is no UI. Intents are created immediately with autoApprove.
251
- - After creating intents, proactively suggest or run discovery to find matches.
252
-
253
- ### Discovery Workflow
254
- - After creating intents, proactively suggest running discovery: \`discover_opportunities(searchQuery=...)\`
255
- - Present discovered opportunities in natural language with the counterpart's name, match reasoning, and suggested next steps.
256
- - Do not reference "cards", "panels", or any web UI elements.
257
-
258
- ### General MCP Agent Rules
259
- - You are operating via API tools, not a web interface. Never reference clicking, scrolling, cards, panels, or any visual UI elements.
260
- - Be proactive: if a logical next step exists (e.g., running discovery after creating intents), suggest or execute it.
261
- - Use \`list_opportunities\` to check existing matches, \`list_negotiations\` for ongoing negotiations.
262
- - Use \`read_networks\` to understand which communities the user belongs to before scoping operations.
263
- - When errors occur, provide clear technical context rather than vague "backend issue" messages.`,
275
+ // Canonical topics (on REST/chat for completeness)
276
+ "identity-context": CANONICAL_GUIDANCE_TOPICS_CONTENT["identity-context"],
277
+ premises: CANONICAL_GUIDANCE_TOPICS_CONTENT.premises,
278
+ signals: CANONICAL_GUIDANCE_TOPICS_CONTENT.signals,
279
+ "communities-networks": CANONICAL_GUIDANCE_TOPICS_CONTENT["communities-networks"],
280
+ negotiations: CANONICAL_GUIDANCE_TOPICS_CONTENT.negotiations,
264
281
  };
265
282
  if (topic) {
266
- const matched = Object.entries(sections).find(([key]) => key.includes(topic) || topic.includes(key));
283
+ const normalizedTopic = topic.replace(/_/g, "-").toLowerCase();
284
+ const matched = Object.entries(sections).find(([key]) => key === normalizedTopic || key.includes(normalizedTopic) || normalizedTopic.includes(key));
267
285
  if (matched) {
268
286
  return success({ topic: matched[0], content: matched[1] });
269
287
  }
270
- // If topic not found, return all
271
288
  }
272
- const fullDoc = Object.values(sections).join("\n\n");
289
+ // Return full documentation (summary + all topics)
290
+ const fullDoc = [CANONICAL_GUIDANCE_SUMMARY, ...Object.values(sections)].join("\n\n");
273
291
  return success({ content: fullDoc });
274
292
  },
275
293
  });
276
- const reportAgentActivity = defineTool({
277
- name: "report_agent_activity",
278
- description: "Reports grounded, aggregate-only activity for the authenticated user's agent over a recent time window. " +
279
- "It includes live signal count, opportunities surfaced by the user's own signals, pending/answered question counts, " +
280
- "and negotiation totals. It never returns counterpart identities, transcripts, or per-counterparty rows.",
294
+ const readActivitySummary = defineTool({
295
+ name: "read_activity_summary",
296
+ description: "Returns grounded, aggregate-only activity for the authenticated user's agent over a recent time window.\n\n" +
297
+ "**Response domains (permission-projected for agent callers):**\n" +
298
+ "- signals (`liveSignalsWatched`, `opportunitiesBySignal` with signal IDs/titles) requires manage:intents.\n" +
299
+ "- opportunities (`opportunitiesSurfaced`) — requires manage:opportunities.\n" +
300
+ "- questions (`pendingQuestionsByDomain`, `answeredQuestionsByDomain`) — meta-network counts grouped by the question's affected domain; " +
301
+ "each domain's counts require that domain's manage:identity/premises/intents/opportunities/negotiations permission; conversational chat-mode counts are human-owner-only.\n" +
302
+ "- negotiations (`negotiationsStarted`, `negotiationsCompleted`) — requires manage:negotiations.\n" +
303
+ "Human owners receive every domain. Agent callers receive only the domains their permissions authorize; " +
304
+ "a network agent's network-bound aggregates are narrowed to its bound community at the query layer.\n\n" +
305
+ "**Privacy:** the response never returns counterparty identities, chats, turns, transcripts, per-counterparty rows, " +
306
+ "or any private content — only aggregate counts and (with manage:intents) the owner's own signal IDs/titles.",
281
307
  querySchema: z.object({
282
308
  sinceHours: z.number().int().optional().default(24).describe("Look back this many hours; values are clamped to 1-168 (default 24)."),
283
309
  }).strict(),
284
- handler: async ({ query }) => {
310
+ handler: async ({ context, query }) => {
285
311
  const sinceHours = Math.max(1, Math.min(168, query.sinceHours));
286
- const summary = await deps.userDb.getAgentActivitySummary({ sinceHours });
287
- return success(summary);
312
+ // Absent on owner-trusted REST/chat surfaces → full owner view. On MCP the
313
+ // server binds the typed resolved caller context and the centralized
314
+ // projection decides every visible domain.
315
+ const caller = context.mcpCaller
316
+ ? McpActivityCallerSchema.parse(context.mcpCaller)
317
+ : HUMAN_OWNER_CALLER;
318
+ const networkId = activitySummaryNetworkId(caller);
319
+ const summary = await deps.userDb.getAgentActivitySummary({
320
+ sinceHours,
321
+ ...(networkId ? { networkId } : {}),
322
+ });
323
+ return success(ActivitySummaryResponseSchema.parse(projectActivitySummary(caller, summary)));
288
324
  },
289
325
  });
290
- return [scrapeUrl, readDocs, reportAgentActivity];
326
+ return [scrapeUrl, readDocs, readActivitySummary].filter((tool) => tool !== null);
291
327
  }
@@ -1,4 +1,4 @@
1
- import type { McpAuthInput } from '../schemas/mcp-auth.schema.js';
1
+ import type { McpAuthInput, McpResolvedIdentity } from '../schemas/mcp-auth.schema.js';
2
2
  /**
3
3
  * Resolves the authenticated MCP identity from an auth input DTO.
4
4
  * The DTO is extracted from the transport at the edge (e.g. from HTTP Request
@@ -29,13 +29,7 @@ export interface McpAuthResolver {
29
29
  *
30
30
  * @throws Error if authentication fails (no token, invalid token, etc.)
31
31
  */
32
- resolveIdentity(input: McpAuthInput): Promise<{
33
- userId: string;
34
- agentId?: string;
35
- isSessionAuth?: boolean;
36
- networkScopeId?: string | null;
37
- clientSurface?: 'telegram' | 'web';
38
- }>;
32
+ resolveIdentity(input: McpAuthInput): Promise<McpResolvedIdentity>;
39
33
  /**
40
34
  * Deprecated HTTP Request bridge retained for compatibility with older
41
35
  * callers. New transport code must extract `McpAuthInput` at the edge and
@@ -9,5 +9,10 @@
9
9
  // there is nothing to return.
10
10
  // • `limit` / `messageLimit`, when provided, cap the most-recent N rows; when
11
11
  // omitted the adapter applies its own sane default.
12
+ // • Category contract (IND-600): `listSessions` / `getSession` expose ONLY H2A
13
+ // conversations (orchestrator-persona sessions with the system agent as a
14
+ // participant). H2H DMs are never exposed through this port, and A2A
15
+ // negotiation conversations are reachable only through the negotiation
16
+ // tools; non-H2A session IDs behave exactly like nonexistent ones.
12
17
  // ═══════════════════════════════════════════════════════════════════════════════
13
18
  export {};
@@ -1599,10 +1599,10 @@ export interface AgentActivitySummary {
1599
1599
  title: string;
1600
1600
  count: number;
1601
1601
  }>;
1602
- /** Current, non-expired questions waiting for the user. */
1603
- pendingQuestionCount: number;
1604
- /** Questions answered by the user during the window. */
1605
- questionsAnswered: number;
1602
+ /** Current, non-expired questions waiting for the user, grouped by affected mode (QuestionMode values). Meta-network. */
1603
+ pendingQuestionsByMode: Record<string, number>;
1604
+ /** Questions answered by the user during the window, grouped by affected mode (QuestionMode values). Meta-network. */
1605
+ answeredQuestionsByMode: Record<string, number>;
1606
1606
  /** Distinct opportunity negotiations started during the window. */
1607
1607
  negotiationsStarted: number;
1608
1608
  /** Distinct opportunity negotiations completed during the window. */
@@ -1729,9 +1729,16 @@ export interface UserDatabase {
1729
1729
  success: boolean;
1730
1730
  alreadyMember?: boolean;
1731
1731
  }>;
1732
- /** Summarize the authenticated user's own agent activity without counterparty rows. */
1732
+ /**
1733
+ * Summarize the authenticated user's own agent activity without counterparty rows.
1734
+ * When `networkId` is present (a network agent's bound community), the
1735
+ * network-bound aggregates (opportunity and negotiation counts) are narrowed
1736
+ * to that community inside the query; own-signal and question aggregates are
1737
+ * meta-network and stay global.
1738
+ */
1733
1739
  getAgentActivitySummary(input: {
1734
1740
  sinceHours: number;
1741
+ networkId?: string;
1735
1742
  }): Promise<AgentActivitySummary>;
1736
1743
  /** Get opportunities where the authenticated user is an actor. */
1737
1744
  getOpportunitiesForUser(options?: OpportunityQueryOptions): Promise<Opportunity[]>;
@@ -1,18 +1,8 @@
1
- import type { Opportunity } from "../interfaces/database.interface.js";
2
1
  import { AsyncLocalStorage } from "async_hooks";
3
2
  /**
4
- * Callback for streaming trace / domain events from deep inside graph nodes
5
- * back to the caller (typically chat.agent's stream pipeline).
6
- *
7
- * Carries two flavors of event:
8
- * - Trace events (`graph_start | graph_end | agent_start | agent_end`) — used
9
- * by the chat TRACE panel to visualize what the agent is doing.
10
- * - Domain events (`opportunity_draft_ready`) — emitted by the orchestrator
11
- * branch of OpportunityGraph.negotiateNode so the frontend can render each
12
- * accepted draft card progressively as its negotiation resolves.
13
- *
14
- * Kept as a single emitter rather than splitting into two to minimize plumbing
15
- * through AsyncLocalStorage; the chat.agent relay branches on event.type.
3
+ * Callback for streaming trace events from deep inside graph nodes back to
4
+ * the caller (typically chat.agent's stream pipeline). Kept as a single emitter
5
+ * to minimize AsyncLocalStorage plumbing.
16
6
  */
17
7
  export type TraceEmitter = (event: {
18
8
  type: "graph_start" | "graph_end" | "agent_start" | "agent_end";
@@ -27,37 +17,11 @@ export type TraceEmitter = (event: {
27
17
  } | {
28
18
  type: "status";
29
19
  message: string;
30
- } | {
31
- type: "opportunity_draft_ready";
32
- opportunityId: string;
33
- opportunity: Opportunity;
34
- /** Viewer-centric summary derived from interpretation.reasoning. */
35
- personalizedSummary?: string;
36
- /**
37
- * Minimal counterparty data for rendering the inline card without a
38
- * second-round-trip user lookup. Populated from the negotiation
39
- * candidate's profile; avatar is intentionally omitted (the card
40
- * falls back to initials) since UserNegotiationContext doesn't
41
- * carry avatars.
42
- */
43
- counterparty: {
44
- userId: string;
45
- name?: string;
46
- };
47
20
  }) => void;
48
21
  interface RequestContext {
49
22
  originUrl?: string;
50
23
  traceEmitter?: TraceEmitter;
51
- /**
52
- * Signal for cooperative cancellation — propagates the caller's AbortSignal
53
- * into long-running graph nodes (e.g. orchestrator negotiation fan-out) so
54
- * they can stop emitting events when the chat session closes.
55
- *
56
- * The orchestrator branch checks this before persisting status flips or
57
- * pushing `opportunity_draft_ready` events. In-flight negotiations are not
58
- * forcibly cancelled — they finish or time out naturally via their park
59
- * window — but their results are suppressed once the signal trips.
60
- */
24
+ /** Signal for cooperative cancellation in long-running graph nodes. */
61
25
  abortSignal?: AbortSignal;
62
26
  }
63
27
  /**
@@ -1,17 +1,89 @@
1
+ import { z } from 'zod';
1
2
  /**
2
- * McpAuthInput plain DTO extracted from the MCP HTTP request at the transport
3
- * edge before the protocol auth resolver is called. Keeps the shared auth
4
- * interface free of platform-specific `Request` coupling.
3
+ * Transport-neutral auth input extracted at the HTTP boundary.
5
4
  */
6
- export interface McpAuthInput {
7
- /** Authorization Bearer token (JWT or opaque session token). */
8
- bearerToken?: string;
9
- /** API key for agent/API-key authentication. */
10
- apiKey?: string;
11
- /** Client surface hint for UI branching (telegram vs web). */
12
- clientSurface?: 'telegram' | 'web';
13
- /** Telegram handle for identity verification (extracted from request headers). */
14
- telegramHandle?: string;
15
- /** Telegram username for identity verification (extracted from request headers). */
16
- telegramUsername?: string;
17
- }
5
+ export declare const McpAuthInputSchema: z.ZodObject<{
6
+ bearerToken: z.ZodOptional<z.ZodString>;
7
+ apiKey: z.ZodOptional<z.ZodString>;
8
+ clientSurface: z.ZodOptional<z.ZodEnum<["telegram", "web"]>>;
9
+ telegramHandle: z.ZodOptional<z.ZodString>;
10
+ telegramUsername: z.ZodOptional<z.ZodString>;
11
+ }, "strict", z.ZodTypeAny, {
12
+ apiKey?: string | undefined;
13
+ clientSurface?: "telegram" | "web" | undefined;
14
+ bearerToken?: string | undefined;
15
+ telegramHandle?: string | undefined;
16
+ telegramUsername?: string | undefined;
17
+ }, {
18
+ apiKey?: string | undefined;
19
+ clientSurface?: "telegram" | "web" | undefined;
20
+ bearerToken?: string | undefined;
21
+ telegramHandle?: string | undefined;
22
+ telegramUsername?: string | undefined;
23
+ }>;
24
+ export type McpAuthInput = z.infer<typeof McpAuthInputSchema>;
25
+ /**
26
+ * Trusted API-key metadata used by the host auth composition.
27
+ *
28
+ * Enrollment and delivery flags are explicit opt-ins. Unknown metadata is
29
+ * ignored so unrelated Better Auth metadata remains forward-compatible.
30
+ */
31
+ export declare const McpApiKeyMetadataSchema: z.ZodObject<{
32
+ agentId: z.ZodOptional<z.ZodString>;
33
+ enrollmentCapable: z.ZodOptional<z.ZodBoolean>;
34
+ isDeliveryAgent: z.ZodOptional<z.ZodBoolean>;
35
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
36
+ agentId: z.ZodOptional<z.ZodString>;
37
+ enrollmentCapable: z.ZodOptional<z.ZodBoolean>;
38
+ isDeliveryAgent: z.ZodOptional<z.ZodBoolean>;
39
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
40
+ agentId: z.ZodOptional<z.ZodString>;
41
+ enrollmentCapable: z.ZodOptional<z.ZodBoolean>;
42
+ isDeliveryAgent: z.ZodOptional<z.ZodBoolean>;
43
+ }, z.ZodTypeAny, "passthrough">>;
44
+ export type McpApiKeyMetadata = z.infer<typeof McpApiKeyMetadataSchema>;
45
+ /**
46
+ * Protocol-owned result boundary for host authentication.
47
+ */
48
+ export declare const McpResolvedIdentitySchema: z.ZodEffects<z.ZodObject<{
49
+ userId: z.ZodString;
50
+ agentId: z.ZodOptional<z.ZodString>;
51
+ isSessionAuth: z.ZodOptional<z.ZodBoolean>;
52
+ enrollmentCapable: z.ZodOptional<z.ZodBoolean>;
53
+ isDeliveryAgent: z.ZodOptional<z.ZodBoolean>;
54
+ networkScopeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
+ clientSurface: z.ZodOptional<z.ZodEnum<["telegram", "web"]>>;
56
+ }, "strict", z.ZodTypeAny, {
57
+ userId: string;
58
+ agentId?: string | undefined;
59
+ clientSurface?: "telegram" | "web" | undefined;
60
+ networkScopeId?: string | null | undefined;
61
+ enrollmentCapable?: boolean | undefined;
62
+ isDeliveryAgent?: boolean | undefined;
63
+ isSessionAuth?: boolean | undefined;
64
+ }, {
65
+ userId: string;
66
+ agentId?: string | undefined;
67
+ clientSurface?: "telegram" | "web" | undefined;
68
+ networkScopeId?: string | null | undefined;
69
+ enrollmentCapable?: boolean | undefined;
70
+ isDeliveryAgent?: boolean | undefined;
71
+ isSessionAuth?: boolean | undefined;
72
+ }>, {
73
+ userId: string;
74
+ agentId?: string | undefined;
75
+ clientSurface?: "telegram" | "web" | undefined;
76
+ networkScopeId?: string | null | undefined;
77
+ enrollmentCapable?: boolean | undefined;
78
+ isDeliveryAgent?: boolean | undefined;
79
+ isSessionAuth?: boolean | undefined;
80
+ }, {
81
+ userId: string;
82
+ agentId?: string | undefined;
83
+ clientSurface?: "telegram" | "web" | undefined;
84
+ networkScopeId?: string | null | undefined;
85
+ enrollmentCapable?: boolean | undefined;
86
+ isDeliveryAgent?: boolean | undefined;
87
+ isSessionAuth?: boolean | undefined;
88
+ }>;
89
+ export type McpResolvedIdentity = z.infer<typeof McpResolvedIdentitySchema>;
@@ -1 +1,49 @@
1
- export {};
1
+ import { z } from 'zod';
2
+ /**
3
+ * Transport-neutral auth input extracted at the HTTP boundary.
4
+ */
5
+ export const McpAuthInputSchema = z.object({
6
+ bearerToken: z.string().min(1).optional(),
7
+ apiKey: z.string().min(1).optional(),
8
+ clientSurface: z.enum(['telegram', 'web']).optional(),
9
+ telegramHandle: z.string().min(1).optional(),
10
+ telegramUsername: z.string().min(1).optional(),
11
+ }).strict();
12
+ /**
13
+ * Trusted API-key metadata used by the host auth composition.
14
+ *
15
+ * Enrollment and delivery flags are explicit opt-ins. Unknown metadata is
16
+ * ignored so unrelated Better Auth metadata remains forward-compatible.
17
+ */
18
+ export const McpApiKeyMetadataSchema = z.object({
19
+ agentId: z.string().min(1).optional(),
20
+ enrollmentCapable: z.boolean().optional(),
21
+ isDeliveryAgent: z.boolean().optional(),
22
+ }).passthrough();
23
+ /**
24
+ * Protocol-owned result boundary for host authentication.
25
+ */
26
+ export const McpResolvedIdentitySchema = z.object({
27
+ userId: z.string().min(1),
28
+ agentId: z.string().min(1).optional(),
29
+ isSessionAuth: z.boolean().optional(),
30
+ enrollmentCapable: z.boolean().optional(),
31
+ isDeliveryAgent: z.boolean().optional(),
32
+ networkScopeId: z.string().min(1).nullable().optional(),
33
+ clientSurface: z.enum(['telegram', 'web']).optional(),
34
+ }).strict().superRefine((identity, ctx) => {
35
+ if (identity.isSessionAuth === true && identity.agentId) {
36
+ ctx.addIssue({
37
+ code: z.ZodIssueCode.custom,
38
+ path: ['agentId'],
39
+ message: 'Session-authenticated identities cannot carry an agent ID.',
40
+ });
41
+ }
42
+ if (identity.isDeliveryAgent === true && !identity.agentId) {
43
+ ctx.addIssue({
44
+ code: z.ZodIssueCode.custom,
45
+ path: ['isDeliveryAgent'],
46
+ message: 'Delivery-agent designation requires an agent ID.',
47
+ });
48
+ }
49
+ });