@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
@@ -14,17 +14,9 @@ import { createQuestionerTools } from '../../../capabilities/questions.facade.js
14
14
  import { protocolLogger } from '../../../shared/observability/protocol.logger.js';
15
15
  import { requestContext } from '../../../shared/observability/request-context.js';
16
16
  const logger = protocolLogger('ToolRegistry');
17
- /**
18
- * Creates a tool registry containing all tool handlers indexed by name.
19
- * Handlers are raw async functions (not LangChain tool() wrappers) that
20
- * accept { context, query } and return a JSON string.
21
- *
22
- * @param deps - Shared tool dependencies (graphs, database, embedder, etc.)
23
- * @param context - Resolved user context for this request.
24
- * @returns Map of tool name to raw tool definition.
25
- */
26
- export function createToolRegistry(deps) {
17
+ export function createToolRegistry(deps, options = {}) {
27
18
  const registry = new Map();
19
+ const isMcpSurface = options.surface === 'mcp';
28
20
  // defineTool that captures raw handlers into the registry
29
21
  function defineTool(opts) {
30
22
  const entry = {
@@ -65,9 +57,17 @@ export function createToolRegistry(deps) {
65
57
  createIntentTools(dt, deps);
66
58
  createNetworkTools(dt, deps);
67
59
  createOpportunityTools(dt, deps);
68
- createUtilityTools(dt, deps);
69
- createIntegrationTools(dt, deps);
70
- createContactTools(dt, deps);
60
+ // Utility tools always register read_docs + read_activity_summary; on the
61
+ // MCP surface scrape_url is omitted and read_docs guidance is sanitized
62
+ // (IND-597). The retired report_agent_activity name retains no alias on
63
+ // either surface (IND-605).
64
+ createUtilityTools(dt, deps, { surface: isMcpSurface ? 'mcp' : 'rest' });
65
+ // Contact/Gmail import tools are omitted from the MCP surface (IND-596). Their
66
+ // implementations remain available to the REST Tool API and chat agent.
67
+ if (!isMcpSurface) {
68
+ createIntegrationTools(dt, deps);
69
+ createContactTools(dt, deps);
70
+ }
71
71
  createAgentTools(dt, deps);
72
72
  createNegotiationTools(dt, deps);
73
73
  createPremiseTools(dt, deps);
@@ -75,32 +75,35 @@ export function createToolRegistry(deps) {
75
75
  if (deps.chatSession) {
76
76
  createChatTools(dt, deps);
77
77
  }
78
- // Deprecated tool-name aliases (IND-371). The canonical implementations are now
79
- // registered under their *_user_context / *_enrichment_run names; we expose the
80
- // legacy *_user_profile / *_profile_run names as thin aliases that delegate to the
81
- // exact same handler + schema, so existing MCP clients keep working while they
82
- // migrate. The old names are removed in IND-373.
83
- const DEPRECATED_TOOL_ALIASES = [
84
- ["read_user_profiles", "read_user_contexts"],
85
- ["create_user_profile", "create_user_context"],
86
- ["update_user_profile", "update_user_context"],
87
- ["confirm_user_profile", "confirm_user_context"],
88
- ["preview_user_profile", "preview_user_context"],
89
- ["get_profile_run", "get_enrichment_run"],
90
- ["cancel_profile_run", "cancel_enrichment_run"],
91
- ];
92
- for (const [oldName, canonicalName] of DEPRECATED_TOOL_ALIASES) {
93
- const canonical = registry.get(canonicalName);
94
- if (!canonical) {
95
- logger.warn('Cannot register deprecated alias: canonical tool not found', { alias: oldName, canonicalName });
96
- continue;
78
+ // Deprecated tool-name aliases (IND-371). The canonical implementations are
79
+ // registered under their *_user_context / *_enrichment_run names; the legacy
80
+ // *_user_profile / *_profile_run names are retained as thin aliases that
81
+ // delegate to the exact same handler + schema so existing direct HTTP Tool
82
+ // API clients keep working. These aliases are NOT exposed on the MCP surface
83
+ // (IND-598) MCP callers must use the canonical names.
84
+ if (!isMcpSurface) {
85
+ const DEPRECATED_TOOL_ALIASES = [
86
+ ["read_user_profiles", "read_user_contexts"],
87
+ ["create_user_profile", "create_user_context"],
88
+ ["update_user_profile", "update_user_context"],
89
+ ["confirm_user_profile", "confirm_user_context"],
90
+ ["preview_user_profile", "preview_user_context"],
91
+ ["get_profile_run", "get_enrichment_run"],
92
+ ["cancel_profile_run", "cancel_enrichment_run"],
93
+ ];
94
+ for (const [oldName, canonicalName] of DEPRECATED_TOOL_ALIASES) {
95
+ const canonical = registry.get(canonicalName);
96
+ if (!canonical) {
97
+ logger.warn('Cannot register deprecated alias: canonical tool not found', { alias: oldName, canonicalName });
98
+ continue;
99
+ }
100
+ registry.set(oldName, {
101
+ ...canonical,
102
+ name: oldName,
103
+ description: `[DEPRECATED — use \`${canonicalName}\` instead; this alias is retained for backward compatibility and will be removed.] ${canonical.description}`,
104
+ });
97
105
  }
98
- registry.set(oldName, {
99
- ...canonical,
100
- name: oldName,
101
- description: `[DEPRECATED — use \`${canonicalName}\` instead; this alias is retained for backward compatibility and will be removed.] ${canonical.description}`,
102
- });
103
106
  }
104
- logger.verbose('Tool registry created', { toolCount: registry.size });
107
+ logger.verbose('Tool registry created', { toolCount: registry.size, surface: options.surface ?? 'rest' });
105
108
  return registry;
106
109
  }
@@ -0,0 +1,271 @@
1
+ import { z } from "zod";
2
+ import type { AgentActivitySummary } from "../interfaces/database.interface.js";
3
+ /**
4
+ * Centralized activity-summary permission projection (IND-605).
5
+ *
6
+ * `read_activity_summary` is the only public name for grounded, aggregate-only
7
+ * agent activity reporting. The handler passes a typed resolved caller context
8
+ * into this one projection:
9
+ *
10
+ * - Human owners (REST/chat surfaces and session-authenticated MCP callers)
11
+ * receive every domain.
12
+ * - Registered agents receive only the domains their permissions authorize.
13
+ * Signal IDs/titles (`opportunitiesBySignal`) require `manage:intents`.
14
+ * - Question counts are meta-network: they are never narrowed by a network
15
+ * binding, but each count inherits the permission of the domain the
16
+ * question AFFECTS. A caller sees a domain's question counts only when it
17
+ * holds that domain's permission; conversational (`chat`-mode) and
18
+ * unrecognized modes are human-owner-only. There is deliberately no
19
+ * any-of all-question count shortcut.
20
+ * - Network agents additionally narrow network-bound aggregates to their bound
21
+ * community — exposed here as `activitySummaryNetworkId` so the narrowing
22
+ * happens in the query/adapter layer, never as transport-local JSON
23
+ * filtering.
24
+ *
25
+ * The projection can only drop fields. It never fabricates data and it never
26
+ * exposes counterparty identities, chats, turns, transcripts, or private
27
+ * content; the strict response schema below is the explicit output contract.
28
+ */
29
+ /** Canonical public tool name for aggregate activity reporting. */
30
+ export declare const READ_ACTIVITY_SUMMARY_TOOL_NAME = "read_activity_summary";
31
+ /** Activity summary domains, in stable projection order. */
32
+ export declare const ActivitySummaryDomainSchema: z.ZodEnum<["signals", "opportunities", "questions", "negotiations"]>;
33
+ export type ActivitySummaryDomain = z.infer<typeof ActivitySummaryDomainSchema>;
34
+ /**
35
+ * Domains a question can affect. `'chat'` covers conversational questions
36
+ * (and, fail-closed, any mode added to the protocol enum later): they have no
37
+ * agent-permission domain and are visible to human owners only.
38
+ */
39
+ export declare const ActivityQuestionDomainSchema: z.ZodEnum<["identity", "premises", "intents", "opportunities", "negotiations", "chat"]>;
40
+ export type ActivityQuestionDomain = z.infer<typeof ActivityQuestionDomainSchema>;
41
+ /**
42
+ * Maps every current `QuestionMode` to the domain the question affects.
43
+ * Modes missing from this table fail closed to the human-only `'chat'`
44
+ * bucket so a future protocol mode can never leak to an agent caller.
45
+ */
46
+ export declare const QUESTION_MODE_TO_DOMAIN: Readonly<Record<string, ActivityQuestionDomain>>;
47
+ /**
48
+ * Explicit per-domain question counts. Each key is present only when the
49
+ * caller is authorized for that domain and the count is non-zero.
50
+ */
51
+ export declare const ActivityQuestionCountsSchema: z.ZodObject<{
52
+ identity: z.ZodOptional<z.ZodNumber>;
53
+ premises: z.ZodOptional<z.ZodNumber>;
54
+ intents: z.ZodOptional<z.ZodNumber>;
55
+ opportunities: z.ZodOptional<z.ZodNumber>;
56
+ negotiations: z.ZodOptional<z.ZodNumber>;
57
+ /** Human-owner-only conversational (or unrecognized) question counts. */
58
+ chat: z.ZodOptional<z.ZodNumber>;
59
+ }, "strict", z.ZodTypeAny, {
60
+ chat?: number | undefined;
61
+ identity?: number | undefined;
62
+ intents?: number | undefined;
63
+ premises?: number | undefined;
64
+ opportunities?: number | undefined;
65
+ negotiations?: number | undefined;
66
+ }, {
67
+ chat?: number | undefined;
68
+ identity?: number | undefined;
69
+ intents?: number | undefined;
70
+ premises?: number | undefined;
71
+ opportunities?: number | undefined;
72
+ negotiations?: number | undefined;
73
+ }>;
74
+ export type ActivityQuestionCounts = z.infer<typeof ActivityQuestionCountsSchema>;
75
+ /**
76
+ * Typed resolved MCP caller context consumed by the projection. Derived from
77
+ * the MCP capability subject at the transport boundary (see
78
+ * `resolveMcpActivityCaller` in mcp.authorization-policy.ts); absent on
79
+ * REST/chat surfaces, which are owner-trusted.
80
+ */
81
+ export declare const McpActivityCallerSchema: z.ZodObject<{
82
+ /** `'human'` callers own the summarized data and see every domain. */
83
+ kind: z.ZodEnum<["human", "agent"]>;
84
+ /** Canonical `manage:*` permission actions granted to the caller. */
85
+ permissions: z.ZodArray<z.ZodString, "many">;
86
+ /** A network agent's bound community; always null for humans. */
87
+ networkScopeId: z.ZodNullable<z.ZodString>;
88
+ }, "strict", z.ZodTypeAny, {
89
+ kind: "human" | "agent";
90
+ permissions: string[];
91
+ networkScopeId: string | null;
92
+ }, {
93
+ kind: "human" | "agent";
94
+ permissions: string[];
95
+ networkScopeId: string | null;
96
+ }>;
97
+ export type McpActivityCaller = z.infer<typeof McpActivityCallerSchema>;
98
+ /**
99
+ * Canonical affected-domain permission an agent must hold to read or answer a
100
+ * question of the given mode. Returns `null` for human-owner-only questions
101
+ * (`chat`, and any unrecognized mode, which fail closed to the human-only
102
+ * bucket) — no agent permission can release those.
103
+ *
104
+ * This is the single shared mode → affected-domain → permission mapping, reusing
105
+ * `QUESTION_MODE_TO_DOMAIN` and `QUESTION_DOMAIN_PERMISSIONS` so question
106
+ * read/answer inheritance is identical to the `read_activity_summary`
107
+ * projection. It keys on `mode` only: the canonical question model does not vary
108
+ * a question's owning domain by `purpose` (purpose refines generation and
109
+ * provenance, not the affected domain), so purpose is intentionally not a
110
+ * discriminant here rather than being guessed at.
111
+ */
112
+ export declare function questionModeRequiredPermission(mode: string): string | null;
113
+ /**
114
+ * Whether an MCP caller may read or answer a question of the given mode. Human
115
+ * (owner) callers see every mode; agents are confined to modes backed by one of
116
+ * their exact affected-domain permissions. A network agent's binding is applied
117
+ * separately by the caller (network/intent clamp); this is purely the
118
+ * affected-domain permission gate.
119
+ */
120
+ export declare function callerMayAccessQuestionMode(caller: McpActivityCaller, mode: string): boolean;
121
+ /**
122
+ * Explicit response contract for `read_activity_summary`. Every domain field
123
+ * is optional because agent callers receive only their authorized domains.
124
+ * Strict: no counterparty or unspecified field can pass validation.
125
+ */
126
+ export declare const ActivitySummaryResponseSchema: z.ZodObject<{
127
+ /** The reporting window actually used, in hours. */
128
+ sinceHours: z.ZodNumber;
129
+ /** signals domain — liveSignalsWatched and per-signal opportunity counts. */
130
+ liveSignalsWatched: z.ZodOptional<z.ZodNumber>;
131
+ /** opportunities domain. */
132
+ opportunitiesSurfaced: z.ZodOptional<z.ZodNumber>;
133
+ /** signals domain — signal IDs/titles, authorized by manage:intents only. */
134
+ opportunitiesBySignal: z.ZodOptional<z.ZodArray<z.ZodObject<{
135
+ intentId: z.ZodString;
136
+ title: z.ZodString;
137
+ count: z.ZodNumber;
138
+ }, "strict", z.ZodTypeAny, {
139
+ title: string;
140
+ intentId: string;
141
+ count: number;
142
+ }, {
143
+ title: string;
144
+ intentId: string;
145
+ count: number;
146
+ }>, "many">>;
147
+ /** questions domain (meta-network) — counts grouped by affected domain. */
148
+ pendingQuestionsByDomain: z.ZodOptional<z.ZodObject<{
149
+ identity: z.ZodOptional<z.ZodNumber>;
150
+ premises: z.ZodOptional<z.ZodNumber>;
151
+ intents: z.ZodOptional<z.ZodNumber>;
152
+ opportunities: z.ZodOptional<z.ZodNumber>;
153
+ negotiations: z.ZodOptional<z.ZodNumber>;
154
+ /** Human-owner-only conversational (or unrecognized) question counts. */
155
+ chat: z.ZodOptional<z.ZodNumber>;
156
+ }, "strict", z.ZodTypeAny, {
157
+ chat?: number | undefined;
158
+ identity?: number | undefined;
159
+ intents?: number | undefined;
160
+ premises?: number | undefined;
161
+ opportunities?: number | undefined;
162
+ negotiations?: number | undefined;
163
+ }, {
164
+ chat?: number | undefined;
165
+ identity?: number | undefined;
166
+ intents?: number | undefined;
167
+ premises?: number | undefined;
168
+ opportunities?: number | undefined;
169
+ negotiations?: number | undefined;
170
+ }>>;
171
+ answeredQuestionsByDomain: z.ZodOptional<z.ZodObject<{
172
+ identity: z.ZodOptional<z.ZodNumber>;
173
+ premises: z.ZodOptional<z.ZodNumber>;
174
+ intents: z.ZodOptional<z.ZodNumber>;
175
+ opportunities: z.ZodOptional<z.ZodNumber>;
176
+ negotiations: z.ZodOptional<z.ZodNumber>;
177
+ /** Human-owner-only conversational (or unrecognized) question counts. */
178
+ chat: z.ZodOptional<z.ZodNumber>;
179
+ }, "strict", z.ZodTypeAny, {
180
+ chat?: number | undefined;
181
+ identity?: number | undefined;
182
+ intents?: number | undefined;
183
+ premises?: number | undefined;
184
+ opportunities?: number | undefined;
185
+ negotiations?: number | undefined;
186
+ }, {
187
+ chat?: number | undefined;
188
+ identity?: number | undefined;
189
+ intents?: number | undefined;
190
+ premises?: number | undefined;
191
+ opportunities?: number | undefined;
192
+ negotiations?: number | undefined;
193
+ }>>;
194
+ /** negotiations domain. */
195
+ negotiationsStarted: z.ZodOptional<z.ZodNumber>;
196
+ negotiationsCompleted: z.ZodOptional<z.ZodNumber>;
197
+ }, "strict", z.ZodTypeAny, {
198
+ sinceHours: number;
199
+ liveSignalsWatched?: number | undefined;
200
+ opportunitiesSurfaced?: number | undefined;
201
+ opportunitiesBySignal?: {
202
+ title: string;
203
+ intentId: string;
204
+ count: number;
205
+ }[] | undefined;
206
+ pendingQuestionsByDomain?: {
207
+ chat?: number | undefined;
208
+ identity?: number | undefined;
209
+ intents?: number | undefined;
210
+ premises?: number | undefined;
211
+ opportunities?: number | undefined;
212
+ negotiations?: number | undefined;
213
+ } | undefined;
214
+ answeredQuestionsByDomain?: {
215
+ chat?: number | undefined;
216
+ identity?: number | undefined;
217
+ intents?: number | undefined;
218
+ premises?: number | undefined;
219
+ opportunities?: number | undefined;
220
+ negotiations?: number | undefined;
221
+ } | undefined;
222
+ negotiationsStarted?: number | undefined;
223
+ negotiationsCompleted?: number | undefined;
224
+ }, {
225
+ sinceHours: number;
226
+ liveSignalsWatched?: number | undefined;
227
+ opportunitiesSurfaced?: number | undefined;
228
+ opportunitiesBySignal?: {
229
+ title: string;
230
+ intentId: string;
231
+ count: number;
232
+ }[] | undefined;
233
+ pendingQuestionsByDomain?: {
234
+ chat?: number | undefined;
235
+ identity?: number | undefined;
236
+ intents?: number | undefined;
237
+ premises?: number | undefined;
238
+ opportunities?: number | undefined;
239
+ negotiations?: number | undefined;
240
+ } | undefined;
241
+ answeredQuestionsByDomain?: {
242
+ chat?: number | undefined;
243
+ identity?: number | undefined;
244
+ intents?: number | undefined;
245
+ premises?: number | undefined;
246
+ opportunities?: number | undefined;
247
+ negotiations?: number | undefined;
248
+ } | undefined;
249
+ negotiationsStarted?: number | undefined;
250
+ negotiationsCompleted?: number | undefined;
251
+ }>;
252
+ export type ProjectedActivitySummary = z.infer<typeof ActivitySummaryResponseSchema>;
253
+ /**
254
+ * Resolves the summary domains a caller may see. Human owners receive every
255
+ * domain; agents receive only domains backed by one of their permissions.
256
+ * The `questions` domain only flags that at least one affected-domain count
257
+ * may be visible — each count is still filtered per affected domain in
258
+ * `projectActivitySummary`.
259
+ */
260
+ export declare function resolveActivitySummaryDomains(caller: McpActivityCaller): ActivitySummaryDomain[];
261
+ /**
262
+ * Returns the bound community a network agent's network-bound aggregates must
263
+ * be narrowed to in the query/adapter layer. Undefined for human callers and
264
+ * global agents, whose aggregates span the owner's networks.
265
+ */
266
+ export declare function activitySummaryNetworkId(caller: McpActivityCaller): string | undefined;
267
+ /**
268
+ * Projects a full owner-scoped summary down to the caller's authorized
269
+ * domains. Pure field selection — no data is transformed or fabricated.
270
+ */
271
+ export declare function projectActivitySummary(caller: McpActivityCaller, summary: AgentActivitySummary): ProjectedActivitySummary;
@@ -0,0 +1,248 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Centralized activity-summary permission projection (IND-605).
4
+ *
5
+ * `read_activity_summary` is the only public name for grounded, aggregate-only
6
+ * agent activity reporting. The handler passes a typed resolved caller context
7
+ * into this one projection:
8
+ *
9
+ * - Human owners (REST/chat surfaces and session-authenticated MCP callers)
10
+ * receive every domain.
11
+ * - Registered agents receive only the domains their permissions authorize.
12
+ * Signal IDs/titles (`opportunitiesBySignal`) require `manage:intents`.
13
+ * - Question counts are meta-network: they are never narrowed by a network
14
+ * binding, but each count inherits the permission of the domain the
15
+ * question AFFECTS. A caller sees a domain's question counts only when it
16
+ * holds that domain's permission; conversational (`chat`-mode) and
17
+ * unrecognized modes are human-owner-only. There is deliberately no
18
+ * any-of all-question count shortcut.
19
+ * - Network agents additionally narrow network-bound aggregates to their bound
20
+ * community — exposed here as `activitySummaryNetworkId` so the narrowing
21
+ * happens in the query/adapter layer, never as transport-local JSON
22
+ * filtering.
23
+ *
24
+ * The projection can only drop fields. It never fabricates data and it never
25
+ * exposes counterparty identities, chats, turns, transcripts, or private
26
+ * content; the strict response schema below is the explicit output contract.
27
+ */
28
+ /** Canonical public tool name for aggregate activity reporting. */
29
+ export const READ_ACTIVITY_SUMMARY_TOOL_NAME = "read_activity_summary";
30
+ /** Activity summary domains, in stable projection order. */
31
+ export const ActivitySummaryDomainSchema = z.enum([
32
+ "signals",
33
+ "opportunities",
34
+ "questions",
35
+ "negotiations",
36
+ ]);
37
+ /**
38
+ * Domains a question can affect. `'chat'` covers conversational questions
39
+ * (and, fail-closed, any mode added to the protocol enum later): they have no
40
+ * agent-permission domain and are visible to human owners only.
41
+ */
42
+ export const ActivityQuestionDomainSchema = z.enum([
43
+ "identity",
44
+ "premises",
45
+ "intents",
46
+ "opportunities",
47
+ "negotiations",
48
+ "chat",
49
+ ]);
50
+ /**
51
+ * Maps every current `QuestionMode` to the domain the question affects.
52
+ * Modes missing from this table fail closed to the human-only `'chat'`
53
+ * bucket so a future protocol mode can never leak to an agent caller.
54
+ */
55
+ export const QUESTION_MODE_TO_DOMAIN = {
56
+ // Enrichment answers run the PremiseGraph lifecycle (createPremiseFromAnswer →
57
+ // PremiseEvents.onCreated), so enrichment questions affect the premises domain.
58
+ enrichment: "premises",
59
+ intent: "intents",
60
+ discovery: "opportunities",
61
+ pool_discovery: "opportunities",
62
+ negotiation: "negotiations",
63
+ negotiation_inflight: "negotiations",
64
+ chat: "chat",
65
+ };
66
+ /**
67
+ * Explicit per-domain question counts. Each key is present only when the
68
+ * caller is authorized for that domain and the count is non-zero.
69
+ */
70
+ export const ActivityQuestionCountsSchema = z.object({
71
+ identity: z.number().int().optional(),
72
+ premises: z.number().int().optional(),
73
+ intents: z.number().int().optional(),
74
+ opportunities: z.number().int().optional(),
75
+ negotiations: z.number().int().optional(),
76
+ /** Human-owner-only conversational (or unrecognized) question counts. */
77
+ chat: z.number().int().optional(),
78
+ }).strict();
79
+ /**
80
+ * Typed resolved MCP caller context consumed by the projection. Derived from
81
+ * the MCP capability subject at the transport boundary (see
82
+ * `resolveMcpActivityCaller` in mcp.authorization-policy.ts); absent on
83
+ * REST/chat surfaces, which are owner-trusted.
84
+ */
85
+ export const McpActivityCallerSchema = z.object({
86
+ /** `'human'` callers own the summarized data and see every domain. */
87
+ kind: z.enum(["human", "agent"]),
88
+ /** Canonical `manage:*` permission actions granted to the caller. */
89
+ permissions: z.array(z.string()),
90
+ /** A network agent's bound community; always null for humans. */
91
+ networkScopeId: z.string().min(1).nullable(),
92
+ }).strict();
93
+ /** Summary domain → authorizing permission action for agent callers. */
94
+ const DOMAIN_PERMISSIONS = {
95
+ signals: "manage:intents",
96
+ opportunities: "manage:opportunities",
97
+ negotiations: "manage:negotiations",
98
+ };
99
+ /**
100
+ * Affected question domain → authorizing permission action. `'chat'` is
101
+ * deliberately absent: conversational counts are human-owner-only and no
102
+ * agent permission releases them.
103
+ */
104
+ const QUESTION_DOMAIN_PERMISSIONS = {
105
+ identity: "manage:identity",
106
+ premises: "manage:premises",
107
+ intents: "manage:intents",
108
+ opportunities: "manage:opportunities",
109
+ negotiations: "manage:negotiations",
110
+ };
111
+ /**
112
+ * Canonical affected-domain permission an agent must hold to read or answer a
113
+ * question of the given mode. Returns `null` for human-owner-only questions
114
+ * (`chat`, and any unrecognized mode, which fail closed to the human-only
115
+ * bucket) — no agent permission can release those.
116
+ *
117
+ * This is the single shared mode → affected-domain → permission mapping, reusing
118
+ * `QUESTION_MODE_TO_DOMAIN` and `QUESTION_DOMAIN_PERMISSIONS` so question
119
+ * read/answer inheritance is identical to the `read_activity_summary`
120
+ * projection. It keys on `mode` only: the canonical question model does not vary
121
+ * a question's owning domain by `purpose` (purpose refines generation and
122
+ * provenance, not the affected domain), so purpose is intentionally not a
123
+ * discriminant here rather than being guessed at.
124
+ */
125
+ export function questionModeRequiredPermission(mode) {
126
+ const domain = QUESTION_MODE_TO_DOMAIN[mode] ?? "chat";
127
+ return QUESTION_DOMAIN_PERMISSIONS[domain] ?? null;
128
+ }
129
+ /**
130
+ * Whether an MCP caller may read or answer a question of the given mode. Human
131
+ * (owner) callers see every mode; agents are confined to modes backed by one of
132
+ * their exact affected-domain permissions. A network agent's binding is applied
133
+ * separately by the caller (network/intent clamp); this is purely the
134
+ * affected-domain permission gate.
135
+ */
136
+ export function callerMayAccessQuestionMode(caller, mode) {
137
+ if (caller.kind === "human")
138
+ return true;
139
+ const required = questionModeRequiredPermission(mode);
140
+ return required !== null && caller.permissions.includes(required);
141
+ }
142
+ /**
143
+ * Explicit response contract for `read_activity_summary`. Every domain field
144
+ * is optional because agent callers receive only their authorized domains.
145
+ * Strict: no counterparty or unspecified field can pass validation.
146
+ */
147
+ export const ActivitySummaryResponseSchema = z.object({
148
+ /** The reporting window actually used, in hours. */
149
+ sinceHours: z.number().int(),
150
+ /** signals domain — liveSignalsWatched and per-signal opportunity counts. */
151
+ liveSignalsWatched: z.number().int().optional(),
152
+ /** opportunities domain. */
153
+ opportunitiesSurfaced: z.number().int().optional(),
154
+ /** signals domain — signal IDs/titles, authorized by manage:intents only. */
155
+ opportunitiesBySignal: z.array(z.object({
156
+ intentId: z.string(),
157
+ title: z.string(),
158
+ count: z.number().int(),
159
+ }).strict()).optional(),
160
+ /** questions domain (meta-network) — counts grouped by affected domain. */
161
+ pendingQuestionsByDomain: ActivityQuestionCountsSchema.optional(),
162
+ answeredQuestionsByDomain: ActivityQuestionCountsSchema.optional(),
163
+ /** negotiations domain. */
164
+ negotiationsStarted: z.number().int().optional(),
165
+ negotiationsCompleted: z.number().int().optional(),
166
+ }).strict();
167
+ /**
168
+ * Resolves the summary domains a caller may see. Human owners receive every
169
+ * domain; agents receive only domains backed by one of their permissions.
170
+ * The `questions` domain only flags that at least one affected-domain count
171
+ * may be visible — each count is still filtered per affected domain in
172
+ * `projectActivitySummary`.
173
+ */
174
+ export function resolveActivitySummaryDomains(caller) {
175
+ if (caller.kind === "human") {
176
+ return ["signals", "opportunities", "questions", "negotiations"];
177
+ }
178
+ const granted = new Set(caller.permissions);
179
+ const domains = [];
180
+ if (granted.has(DOMAIN_PERMISSIONS.signals))
181
+ domains.push("signals");
182
+ if (granted.has(DOMAIN_PERMISSIONS.opportunities))
183
+ domains.push("opportunities");
184
+ if (Object.values(QUESTION_DOMAIN_PERMISSIONS).some((action) => granted.has(action))) {
185
+ domains.push("questions");
186
+ }
187
+ if (granted.has(DOMAIN_PERMISSIONS.negotiations))
188
+ domains.push("negotiations");
189
+ return domains;
190
+ }
191
+ /**
192
+ * Returns the bound community a network agent's network-bound aggregates must
193
+ * be narrowed to in the query/adapter layer. Undefined for human callers and
194
+ * global agents, whose aggregates span the owner's networks.
195
+ */
196
+ export function activitySummaryNetworkId(caller) {
197
+ if (caller.kind !== "agent")
198
+ return undefined;
199
+ return caller.networkScopeId ?? undefined;
200
+ }
201
+ /**
202
+ * Aggregates raw per-mode question counts into per-affected-domain counts,
203
+ * releasing only the domains the caller is authorized for. Meta-network:
204
+ * domain permissions are the only boundary — never a network filter.
205
+ */
206
+ function projectQuestionCounts(caller, byMode) {
207
+ const granted = new Set(caller.permissions);
208
+ const counts = {};
209
+ for (const [mode, count] of Object.entries(byMode)) {
210
+ // Unrecognized modes fail closed to the human-only bucket.
211
+ const domain = QUESTION_MODE_TO_DOMAIN[mode] ?? "chat";
212
+ if (caller.kind === "agent") {
213
+ const permission = QUESTION_DOMAIN_PERMISSIONS[domain];
214
+ if (!permission || !granted.has(permission))
215
+ continue;
216
+ }
217
+ counts[domain] = (counts[domain] ?? 0) + count;
218
+ }
219
+ return Object.keys(counts).length > 0 ? counts : undefined;
220
+ }
221
+ /**
222
+ * Projects a full owner-scoped summary down to the caller's authorized
223
+ * domains. Pure field selection — no data is transformed or fabricated.
224
+ */
225
+ export function projectActivitySummary(caller, summary) {
226
+ const domains = new Set(resolveActivitySummaryDomains(caller));
227
+ const projected = { sinceHours: summary.sinceHours };
228
+ if (domains.has("signals")) {
229
+ projected.liveSignalsWatched = summary.liveSignalsWatched;
230
+ projected.opportunitiesBySignal = summary.opportunitiesBySignal;
231
+ }
232
+ if (domains.has("opportunities")) {
233
+ projected.opportunitiesSurfaced = summary.opportunitiesSurfaced;
234
+ }
235
+ if (domains.has("questions")) {
236
+ const pending = projectQuestionCounts(caller, summary.pendingQuestionsByMode);
237
+ if (pending)
238
+ projected.pendingQuestionsByDomain = pending;
239
+ const answered = projectQuestionCounts(caller, summary.answeredQuestionsByMode);
240
+ if (answered)
241
+ projected.answeredQuestionsByDomain = answered;
242
+ }
243
+ if (domains.has("negotiations")) {
244
+ projected.negotiationsStarted = summary.negotiationsStarted;
245
+ projected.negotiationsCompleted = summary.negotiationsCompleted;
246
+ }
247
+ return projected;
248
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Canonical guidance for Index Network protocol operations.
3
+ *
4
+ * This is the single normative source for:
5
+ * - Entity model and identity/context definitions
6
+ * - Premise, signal, community, network, and opportunity concepts
7
+ * - Negotiation semantics (owner approval vs A2A acceptance)
8
+ * - H2A and A2A collaboration (H2H is never exposed)
9
+ * - Retired vocabulary (no contact/Gmail/scrape/profile/ghost-user guidance)
10
+ *
11
+ * Wired into:
12
+ * - MCP_INSTRUCTIONS (packages/protocol/src/mcp/mcp.server.ts)
13
+ * - read_docs tool (packages/protocol/src/shared/agent/utility.tools.ts)
14
+ *
15
+ * IND-602, IND-603
16
+ */
17
+ export declare const CANONICAL_GUIDANCE_TOPICS: readonly ["identity-context", "premises", "signals", "communities-networks", "opportunities", "negotiations", "workflows"];
18
+ export type CanonicalGuidanceTopic = (typeof CANONICAL_GUIDANCE_TOPICS)[number];
19
+ /**
20
+ * Summary of canonical entity model and protocol semantics.
21
+ * Used in MCP_INSTRUCTIONS and as read_docs summary across all surfaces.
22
+ * Never mentions retired contact/Gmail/scrape/profile/ghost-user guidance.
23
+ * Optimized to ~1900 chars to leave room for MCP_INSTRUCTIONS context budget.
24
+ */
25
+ export declare const CANONICAL_GUIDANCE_SUMMARY = "# Index Network Protocol\n\nIndex Network is a private, intent-driven discovery protocol. Users express signals (intents), agents find matches within shared networks, and decisions proceed through clear gates.\n\n## Core Concepts\n\n**Identity & Context** \u2014 User profile (name, bio, skills, interests, location) and current scope (networks, intents, stage).\n\n**Premises** \u2014 Foundational facts: background, experience, stage, timeline, constraints.\n\n**Signals** \u2014 What users seek (intents, opportunities). Drive semantic matching.\n\n**Communities & Networks** \u2014 Private groups where members share signals and discover connections via shared membership.\n\n**Opportunities** \u2014 Discovered matches between users. Lifecycle: draft \u2192 pending \u2192 accepted/rejected/expired.\n\n**Negotiations** \u2014 Agents coordinate, users approve. **A2A acceptance is not owner approval.** These are separate gates.\n\n**Workflows** \u2014 H2A (users express signals \u2192 agents discover) and A2A (agents coordinate) over MCP. Further escalation via native surfaces (human-to-human threads do not cross MCP).\n\n## Canonical Topics\n\nRefer to these for detailed entity facts and lifecycle:\n\n- **identity-context** \u2014 Profile structure, context scoping\n- **premises** \u2014 Background, stage, constraints\n- **signals** \u2014 Intent inference, classification\n- **communities-networks** \u2014 Membership, auto-assign\n- **opportunities** \u2014 Discovery, roles, reasoning\n- **negotiations** \u2014 Owner approval vs A2A, acceptance gates\n- **workflows** \u2014 H2A and A2A tool sequences";
26
+ /**
27
+ * Detailed canonical topic content for read_docs.
28
+ * Indexed by topic name; each contains current entity, capability, and lifecycle facts.
29
+ */
30
+ export declare const CANONICAL_GUIDANCE_TOPICS_CONTENT: Record<CanonicalGuidanceTopic, string>;