@indexnetwork/protocol 6.14.0-rc.406.1 → 8.0.1-rc.417.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 (114) hide show
  1. package/CHANGELOG.md +307 -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/capabilities/signals.tools.port.d.ts +1 -1
  6. package/dist/chat/chat-streaming.types.d.ts +4 -5
  7. package/dist/chat/chat.agent.d.ts +0 -19
  8. package/dist/chat/chat.agent.js +1 -70
  9. package/dist/chat/chat.persona.d.ts +0 -6
  10. package/dist/chat/chat.persona.js +0 -1
  11. package/dist/chat/chat.prompt.js +10 -10
  12. package/dist/chat/chat.prompt.modules.js +0 -104
  13. package/dist/chat/chat.tools.js +11 -7
  14. package/dist/chat/negotiator.persona.js +0 -1
  15. package/dist/chat/onboarding.persona.js +0 -1
  16. package/dist/chat/reporter.persona.d.ts +1 -1
  17. package/dist/chat/reporter.persona.js +1 -2
  18. package/dist/chat/reporter.prompt.js +5 -5
  19. package/dist/chat/signal.persona.js +0 -1
  20. package/dist/communities/application/network.tools.js +48 -27
  21. package/dist/contacts/application/contact.tools.js +9 -9
  22. package/dist/index.d.ts +9 -2
  23. package/dist/index.js +5 -0
  24. package/dist/maintenance/maintenance.graph.js +7 -0
  25. package/dist/mcp/mcp.authorization-policy.d.ts +250 -0
  26. package/dist/mcp/mcp.authorization-policy.js +522 -0
  27. package/dist/mcp/mcp.server.d.ts +6 -29
  28. package/dist/mcp/mcp.server.js +202 -228
  29. package/dist/negotiation/application/negotiation.agent.js +29 -5
  30. package/dist/negotiation/application/negotiation.graph.d.ts +2 -5
  31. package/dist/negotiation/application/negotiation.graph.js +53 -23
  32. package/dist/negotiation/domain/index.d.ts +3 -0
  33. package/dist/negotiation/domain/index.js +3 -0
  34. package/dist/negotiation/domain/negotiation.deadlock.d.ts +7 -1
  35. package/dist/negotiation/domain/negotiation.deadlock.js +48 -1
  36. package/dist/negotiation/domain/negotiation.stance.contracts.d.ts +85 -0
  37. package/dist/negotiation/domain/negotiation.stance.contracts.js +140 -0
  38. package/dist/negotiation/domain/negotiation.state.d.ts +2 -3
  39. package/dist/negotiation/domain/negotiation.state.js +2 -3
  40. package/dist/negotiation/public/index.d.ts +2 -0
  41. package/dist/negotiation/public/index.js +2 -0
  42. package/dist/opportunity/application/index.d.ts +0 -3
  43. package/dist/opportunity/application/index.js +0 -3
  44. package/dist/opportunity/application/opportunity.graph.d.ts +0 -43
  45. package/dist/opportunity/application/opportunity.graph.js +7 -113
  46. package/dist/opportunity/application/opportunity.introducer-feature.d.ts +2 -0
  47. package/dist/opportunity/application/opportunity.introducer-feature.js +4 -0
  48. package/dist/opportunity/application/opportunity.owner-approval.d.ts +118 -0
  49. package/dist/opportunity/application/opportunity.owner-approval.js +34 -0
  50. package/dist/opportunity/application/opportunity.owner-provenance.d.ts +6 -0
  51. package/dist/opportunity/application/opportunity.owner-provenance.js +28 -0
  52. package/dist/opportunity/application/opportunity.tools.d.ts +3 -3
  53. package/dist/opportunity/application/opportunity.tools.js +77 -870
  54. package/dist/opportunity/discriminator/discriminator.shadow.js +17 -10
  55. package/dist/opportunity/discriminator/discriminator.types.d.ts +2 -0
  56. package/dist/opportunity/domain/index.d.ts +0 -2
  57. package/dist/opportunity/domain/index.js +0 -2
  58. package/dist/opportunity/domain/opportunity.state.d.ts +2 -70
  59. package/dist/opportunity/domain/opportunity.state.js +2 -55
  60. package/dist/opportunity/opportunity.owner-approval.d.ts +5 -0
  61. package/dist/opportunity/opportunity.owner-approval.js +5 -0
  62. package/dist/opportunity/ports/opportunity.tools.port.d.ts +12 -1
  63. package/dist/opportunity/public/index.d.ts +3 -3
  64. package/dist/opportunity/public/index.js +2 -1
  65. package/dist/participant-agents/application/agent.tools.d.ts +1 -1
  66. package/dist/participant-agents/application/agent.tools.js +44 -6
  67. package/dist/participant-agents/application/index.d.ts +5 -3
  68. package/dist/participant-agents/application/index.js +5 -3
  69. package/dist/questions/application/question.tools.js +31 -2
  70. package/dist/runtime/foreground/composition/tool.factory.d.ts +2 -1
  71. package/dist/runtime/foreground/composition/tool.factory.js +10 -2
  72. package/dist/runtime/foreground/composition/tool.registry.d.ts +16 -2
  73. package/dist/runtime/foreground/composition/tool.registry.js +41 -38
  74. package/dist/shared/agent/activity-projection.d.ts +271 -0
  75. package/dist/shared/agent/activity-projection.js +248 -0
  76. package/dist/shared/agent/canonical-guidance.d.ts +30 -0
  77. package/dist/shared/agent/canonical-guidance.js +249 -0
  78. package/dist/shared/agent/tool.helpers.d.ts +13 -17
  79. package/dist/shared/agent/tool.runtime.js +1 -3
  80. package/dist/shared/agent/utility.tools.d.ts +11 -1
  81. package/dist/shared/agent/utility.tools.js +126 -90
  82. package/dist/shared/interfaces/auth.interface.d.ts +2 -8
  83. package/dist/shared/interfaces/chat-session.interface.js +5 -0
  84. package/dist/shared/interfaces/database.interface.d.ts +12 -5
  85. package/dist/shared/observability/request-context.d.ts +4 -40
  86. package/dist/shared/schemas/mcp-auth.schema.d.ts +87 -15
  87. package/dist/shared/schemas/mcp-auth.schema.js +49 -1
  88. package/dist/signals/application/intent.graph.js +4 -15
  89. package/dist/signals/application/intent.tools.js +34 -14
  90. package/dist/signals/domain/intent.proposal.d.ts +35 -0
  91. package/dist/signals/domain/intent.proposal.js +12 -0
  92. package/package.json +10 -9
  93. package/dist/opportunity/application/opportunity.discover.d.ts +0 -215
  94. package/dist/opportunity/application/opportunity.discover.js +0 -793
  95. package/dist/opportunity/application/opportunity.discovery-continuation-finalization.d.ts +0 -34
  96. package/dist/opportunity/application/opportunity.discovery-continuation-finalization.js +0 -74
  97. package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -44
  98. package/dist/opportunity/application/opportunity.discovery-mcp-lifecycle-finalization.js +0 -92
  99. package/dist/opportunity/domain/opportunity.discovery-run-coalescing.d.ts +0 -33
  100. package/dist/opportunity/domain/opportunity.discovery-run-coalescing.js +0 -33
  101. package/dist/opportunity/domain/opportunity.discovery.contracts.d.ts +0 -45
  102. package/dist/opportunity/domain/opportunity.discovery.contracts.js +0 -1
  103. package/dist/opportunity/opportunity.discover.d.ts +0 -5
  104. package/dist/opportunity/opportunity.discover.js +0 -5
  105. package/dist/opportunity/opportunity.discovery-continuation-finalization.d.ts +0 -5
  106. package/dist/opportunity/opportunity.discovery-continuation-finalization.js +0 -5
  107. package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.d.ts +0 -5
  108. package/dist/opportunity/opportunity.discovery-mcp-lifecycle-finalization.js +0 -5
  109. package/dist/opportunity/opportunity.discovery-run-coalescing.d.ts +0 -5
  110. package/dist/opportunity/opportunity.discovery-run-coalescing.js +0 -5
  111. package/dist/opportunity/opportunity.discovery.contracts.d.ts +0 -5
  112. package/dist/opportunity/opportunity.discovery.contracts.js +0 -5
  113. package/dist/shared/interfaces/discovery-run.interface.d.ts +0 -70
  114. package/dist/shared/interfaces/discovery-run.interface.js +0 -1
package/CHANGELOG.md CHANGED
@@ -12,6 +12,313 @@ See [STABILITY.md](./STABILITY.md) for the public-contract and tier definitions.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ### Added
16
+ - Configurable negotiator stance `NEGOTIATOR_STANCE` (IND-611; 7.11.0), shipped
17
+ dark. `advocate` (default) | `evaluator` | `skeptic`, resolved by the new
18
+ domain contract `configuredNegotiatorStance()`
19
+ (`negotiation/domain/negotiation.stance.contracts.ts`, mirroring
20
+ `configuredScreenMode()`); unset or unrecognized falls back to `advocate`.
21
+ `evaluator` adds an opportunity-cost value bar, asks the agent to assess
22
+ before advocating, and makes discovery-query satisfaction a precondition for
23
+ continuing to evaluate rather than a mandate to connect; `skeptic` adds the
24
+ prior that most candidate matches are not worth making and resolves a
25
+ detected deadlock as a stalemate instead of by concessions.
26
+ **Prompt-only** — seat vocabularies (`allowedActionsFor`), turn schemas, and
27
+ graph routing (including the continuation-screen bypass) are identical under
28
+ all three stances. **`advocate` renders byte-identical prompts** to 7.10.1,
29
+ pinned by an external golden fixture in
30
+ `negotiation/tests/negotiation.stance.spec.ts`. New live eval harness
31
+ `bun run eval:stance` measures decline rate on low-value versus high-value
32
+ fixtures per stance.
33
+
34
+ ### Fixed
35
+ - Stop force-rewriting an opening-move refusal (IND-611 prerequisite; 7.11.0):
36
+ `negotiation.graph.ts` ran the turn-0 opening force *before* the IND-564
37
+ opening-withdraw guard, so a v2 initiator that judged a match not worth making
38
+ had its `withdraw` rewritten to `outreach` while its reasoning survived —
39
+ sending the counterparty an outreach that argued against the match — and made
40
+ the guard below it dead code on turn 0. The guard now runs first: a turn-0
41
+ refusal stands and flows into the existing quiet `screened_out` outcome with
42
+ no message persisted, while a genuinely malformed turn-0 opening (e.g.
43
+ `counter`) is still coerced to the opening action.
44
+ - Attribute `outcome.reasoning` to whoever actually decided (IND-611; 7.11.1):
45
+ `screened_out` now has two routes — the screen node, and an opening-turn
46
+ refusal. The finalize node preferred `screenDecision.reasoning` for both,
47
+ which is wrong on the new route when the gate returned `reach_out`: the
48
+ outcome would carry the screen's argument *for* the match as the reason the
49
+ agent did *not* reach out (and IND-610 renders that string in the owner-only
50
+ gate-decision card). An opening-turn refusal now reports the withdrawing
51
+ turn's own reasoning; a genuine screen-node block is unchanged.
52
+ - Add canonical shared guidance source and unified MCP_INSTRUCTIONS/read_docs
53
+ (IND-602/603; 7.10.0): The single normative `CANONICAL_GUIDANCE_SUMMARY`
54
+ (1,555 chars, under the 4,500-char MCP context budget) covers Index Network
55
+ entity model (identity/context, premises, signals, communities/networks,
56
+ opportunities), negotiation semantics with the critical distinction
57
+ **"A2A acceptance is not owner approval"** (separate gates), H2A/A2A
58
+ workflows, and the boundary **"H2H (human-to-human) never exposed; escalation
59
+ to native surfaces (web, Telegram) is outside MCP scope."** Seven detailed
60
+ canonical topics (identity-context, premises, signals, communities-networks,
61
+ opportunities, negotiations, workflows) are published via read_docs on both
62
+ MCP and REST/chat surfaces. MCP surface read_docs serves canonical guidance
63
+ only; REST/chat retains legacy supplemental topics for backwards compatibility.
64
+ New internal shared constants (packages/protocol/src/shared/agent/canonical-guidance.ts):
65
+ `CANONICAL_GUIDANCE_SUMMARY`, `CANONICAL_GUIDANCE_TOPICS` (const array),
66
+ `CANONICAL_GUIDANCE_TOPICS_CONTENT` (record). Not public root protocol exports.
67
+ MCP_INSTRUCTIONS now delegates entity/lifecycle details to read_docs, dropping
68
+ verbose inline model. Published MCP guidance/read_docs contract now includes
69
+ canonical seven-topic structure and H2A/A2A/owner-approval semantics. No data,
70
+ migration, capability, permission, or runtime behavior changes.
71
+
72
+ - Add a host-injected MCP authorization-observability seam (IND-581; 7.8.0):
73
+ `McpAuthorizationObserver`, the secret-free `McpAuthorizationDenialEvent`, and
74
+ the central `buildMcpAuthorizationDenialEvent` constructor, plus an optional
75
+ fifth `authorizationObserver` parameter on `createMcpServer`. Every
76
+ `tools/call` capability denial (preliminary and resolved stages) emits one
77
+ structured event carrying ONLY the caller profile, tool name, decision
78
+ reason/reach, required permissions, and opaque `userId`/`agentId`/
79
+ `networkScopeId` — never a token, API key, bearer credential, raw header, or
80
+ tool-argument payload. The seam is fail-closed: an observer that throws is
81
+ swallowed and never alters the denial. Denials remain freshly resolved per
82
+ reconnect/session because the static tool-metadata cache holds
83
+ principal-independent registration data only; the per-principal decision is
84
+ recomputed on every fresh server resolution.
85
+
86
+ - Add the canonical `read_own_agent` MCP tool: a registered active agent's
87
+ self-read of its OWN sanitized registration record (IND-599; 7.7.0). The input
88
+ schema is empty — there is no target selector, so a caller can never name
89
+ another agent — and the handler resolves strictly the authenticated
90
+ `context.agentId` with an owner match, so a forged target argument is stripped
91
+ by the schema and never queried. The tool is classified `agent_admin` in the
92
+ canonical capability matrix and registered in the shared factory (`fast`
93
+ runtime class).
94
+
95
+ ### Changed
96
+ - Pin the generic MCP conversation surface to H2A-only in the published
97
+ contract (IND-600; 7.9.0). This is a distinct public-contract release above
98
+ the integrated 7.8.0 floor (IND-581): the `list_conversations` /
99
+ `get_conversation` descriptions now state explicitly that they expose ONLY
100
+ the caller's H2A chats with the Index agent (orchestrator-persona sessions
101
+ with the system agent as a participant): human-to-human (H2H) DMs are NEVER
102
+ exposed through these tools — including via schema-valid forged `tools/call`
103
+ requests — and A2A negotiation conversations are reachable only through the
104
+ negotiation tools (`list_negotiations` / `get_negotiation` /
105
+ `respond_to_negotiation`), which retain their `manage:negotiations`
106
+ permission, exact-participation, and bound-network-scope checks. The public
107
+ `ChatSessionReader` port contract gains the same category rule: non-H2A
108
+ session IDs behave exactly like nonexistent ones. Runtime enforcement is
109
+ unchanged — the `human_only` capability classification still denies every
110
+ non-session principal before any context DB read, scoped-deps creation, or
111
+ chat-adapter work — so this is a published contract/description
112
+ clarification on public tools, hence the minor bump.
113
+ - Split the `agent_admin` capability family by principal kind (IND-599; 7.7.0).
114
+ Registered agent principals (global/network/delivery) may now see and call
115
+ ONLY `read_own_agent` on the admin surface — `list_agents` is no longer
116
+ visible or callable by an agent (previously it was the sole agent-visible
117
+ admin tool), and every admin mutation remains denied (`agent_admin_denied`).
118
+ Session/onboarding humans retain the full owned-agent administration surface
119
+ (`register_agent`, `list_agents`, `update_agent`, `delete_agent`,
120
+ `grant_agent_permission`, `revoke_agent_permission`) but are denied the
121
+ agent-only `read_own_agent` with the new dedicated decision reason
122
+ `human_read_own_agent_denied`. Enrollment-capable unregistered keys remain
123
+ single-purpose `register_agent`-only across the entire registry, and plain
124
+ unregistered keys remain fail-closed. The `agent_admin` decision is made
125
+ BEFORE the session-human blanket allow, and denials fire before any context
126
+ DB read or scoped-deps creation. Domain, informational (`read_docs`),
127
+ permission/network-scope, and delivery capabilities for registered agents are
128
+ unchanged. Behavior tightening on published MCP tools plus a new public tool
129
+ name, hence the minor bump.
130
+ - Redact private transport connection material from every agent record
131
+ projected by the participant-agent tools (IND-599; 7.7.0).
132
+ `sanitizeAgentForOutput` now empties each transport's `config` (endpoint
133
+ secrets, auth headers/tokens) while preserving the safe response shape
134
+ (id/channel/priority/active/failureCount and permissions), covering
135
+ `read_own_agent`, `register_agent`, `list_agents`, and `update_agent` outputs.
136
+ - Require explicit owner authorization for every owner-gated `update_opportunity`
137
+ transition (send/accept/reject) at a new protocol-owned authoritative boundary
138
+ (IND-593; 7.6.0). The `OpportunityOwnerApprovalAuthority` port is injected by
139
+ the host: registered MCP agents must present an owner-issued, fresh, atomically
140
+ single-use proof bound to the exact opportunity, action, owner principal, acting
141
+ agent, and server-derived interaction — missing/stale/generic/forged/wrong-binding/
142
+ replayed proofs fail closed with stable reasons BEFORE the mutation graph runs,
143
+ and a proof-less agent call returns a fresh interaction challenge. The optional
144
+ `ownerApprovalProof` field is added to the public `update_opportunity` schema.
145
+ Non-agent calls traverse the same boundary via host attestation of typed, trusted,
146
+ server-derived interaction/surface provenance (`OpportunityOwnerInteractionProvenance`):
147
+ only a genuine direct authenticated owner session (REST or MCP) attests; chat/CLI/
148
+ H2A/A2A/mediated surfaces and caller-supplied identity, binding, or provenance
149
+ fields can never mint or attest owner authority (`untrusted_provenance`). A2A
150
+ negotiation approvals, uptake acknowledgements, agent self-acknowledgement, and
151
+ server advisory/challenge values are explicitly non-substitutable. System `expired`
152
+ transitions remain ungated. Behavior tightening on a published MCP tool plus new
153
+ public port types, hence the minor bump. No data action, migration, or deployment
154
+ change ships with this entry.
155
+
156
+ ## [7.5.0] — 2026-07-25
157
+
158
+ ### Changed
159
+ - Partition async discovery-run ownership by the exact calling MCP principal, not
160
+ only by user (IND-592). `get_discovery_run` and `cancel_discovery_run` now
161
+ reject a run whose recorded principal (session-human vs a specific agent id)
162
+ differs from the caller's, even within the same user, returning the opaque
163
+ "Discovery run not found." and never attempting cancellation. `discover_opportunities`
164
+ MCP coalescing is likewise partitioned by principal, so an agent-initiated
165
+ request never coalesces onto — and is never handed — the owner's (or another
166
+ agent's) in-flight run id or its status/results. The store lookup remains
167
+ user-scoped; this is an additional in-handler/domain narrowing with no host
168
+ interface change. Behavior tightening on published MCP tools, hence the minor bump.
169
+
170
+ ## [7.4.0] — 2026-07-25
171
+
172
+ ### Changed
173
+ - Make the public participant-agent permission INPUT schemas canonical-only:
174
+ `register_agent.permissions` and `grant_agent_permission.actions` are now a
175
+ `z.enum` of the six canonical `manage:*` actions instead of `z.array(z.string())`.
176
+ Retired `manage:profile` / `manage:contacts` strings are rejected at the schema
177
+ seam (the handler's `isValidAction` check is retained as defense in depth). This
178
+ narrows the published tool input schemas exposed via `tools/list`, hence the
179
+ minor bump. No change to the temporary stored-row compatibility projection,
180
+ which still interprets residual legacy STORED rows.
181
+
182
+ ## [7.3.0] — 2026-07-25
183
+
184
+ ### Added
185
+ - Add the injected `IntentProposalStore` host boundary so web proposal cards are
186
+ emitted only after the normalized description, optional network scope, and
187
+ complete verifier output have been durably bound to their owner.
188
+ - Add `projectStoredPermissionActions` at the MCP capability-loading boundary:
189
+ temporary rolling-data compatibility that interprets residual **stored** legacy
190
+ grant rows during a mixed-version deploy (`manage:profile` →
191
+ `manage:identity` + `manage:premises`; `manage:contacts` → no capability;
192
+ owner/scope preserved; unknown actions fail closed). Not a public alias — legacy
193
+ names remain rejected as input and absent from `tools/list`/docs. Removed only
194
+ after the post-drain final sweep and compatibility gate (see the IND-609
195
+ rollout doc).
196
+
197
+ ### Changed
198
+ - **MCP permission migration (IND-606/607).** Retire issuance of the legacy
199
+ `manage:profile` and `manage:contacts` grant actions in favor of
200
+ `manage:identity` and `manage:premises`. Issuers, defaults, validation, and the
201
+ capability policy emit/accept only the canonical action set; the durable data
202
+ migration (`services/api/drizzle/0109_migrate_agent_permission_actions.sql`)
203
+ converges existing grants (`manage:profile` → `manage:identity` + `manage:premises`;
204
+ `manage:contacts` removed). No public protocol type changes.
205
+ - **Exact question affected-domain inheritance (IND-608).** `read_pending_questions`
206
+ and `answer_pending_question` now enforce each question's exact affected-domain
207
+ permission at the handler, not merely the union that admits the tool. A global
208
+ `manage:intents` agent can no longer read or answer negotiation/enrichment/
209
+ discovery questions it does not manage; the owning human is unaffected.
210
+ - **Corrected `QUESTION_MODE_TO_DOMAIN` mapping.** `enrichment` now maps to the
211
+ `premises` domain (`manage:premises`), matching the enrichment answer pipeline
212
+ that runs the PremiseGraph lifecycle. Previously mapped to `identity`. This
213
+ changes the exported constant's `enrichment` value and the
214
+ `read_activity_summary` projection of enrichment question counts from the
215
+ identity domain to the premises domain — the deliberate public-constant change
216
+ motivating this minor bump from the 7.2.0 floor.
217
+
218
+ ## [7.2.0] — 2026-07-25
219
+
220
+ ### Added
221
+ - Add `read_activity_summary` as the single public name for grounded,
222
+ aggregate-only agent activity reporting (IND-605). The MCP capability matrix
223
+ admits any caller holding at least one activity-domain permission
224
+ (`manage:identity`/`manage:premises`/`manage:intents`/`manage:opportunities`/
225
+ `manage:negotiations`); the handler then passes the typed resolved MCP caller
226
+ context into one centralized permission projection, so global agents receive
227
+ only the domains their permissions authorize while session humans receive the
228
+ full owner view. Signal IDs/titles (`opportunitiesBySignal`) are exposed only
229
+ with `manage:intents`, and question counts are meta-network — never network
230
+ filtered — while each count inherits the permission of the domain the
231
+ question affects: the adapter groups pending/answered counts by question
232
+ mode and the projection releases only the affected-domain counts
233
+ (identity/premises/intents/opportunities/negotiations) the caller is
234
+ authorized for, with conversational `chat`-mode and unrecognized modes
235
+ human-owner-only. There is deliberately no any-of all-question count
236
+ shortcut. A network agent's network-bound aggregates (opportunity and
237
+ negotiation counts) are narrowed to its bound community inside the
238
+ query/adapter layer via the new optional `getAgentActivitySummary`
239
+ `networkId` input — never by transport-local JSON filtering. The response
240
+ never contains counterparty identities, chats, turns, transcripts, or
241
+ private content, and validates against the new strict
242
+ `ActivitySummaryResponseSchema`.
243
+ - Export the centralized activity-projection contract
244
+ (`READ_ACTIVITY_SUMMARY_TOOL_NAME`, `McpActivityCallerSchema`,
245
+ `ActivitySummaryDomainSchema`, `ActivitySummaryResponseSchema`,
246
+ `ActivityQuestionDomainSchema`, `ActivityQuestionCountsSchema`,
247
+ `QUESTION_MODE_TO_DOMAIN`, `resolveMcpActivityCaller`,
248
+ `resolveActivitySummaryDomains`, `activitySummaryNetworkId`,
249
+ `projectActivitySummary`, and their types) for host and capability
250
+ composition.
251
+
252
+ ### Changed
253
+ - The internal reporter persona now consumes the canonical
254
+ `read_activity_summary` as the same tool (no persona-specific fork);
255
+ unrelated REST/chat behavior is unchanged.
256
+ - SemVer rationale: on MCP this release is purely additive —
257
+ `report_agent_activity` was already denied as `removed` since 7.0.0, so no
258
+ working MCP integration can break. The REST/chat tool rename retires a
259
+ same-cycle (7.0.0-era) surface with no alias by deliberate product decision,
260
+ so a minor bump records the change without a major. Recorded here for
261
+ integration-owner reconciliation.
262
+
263
+ ### Removed
264
+ - `report_agent_activity` is retired on every surface with no hidden legacy
265
+ alias. It is no longer registered in either tool-registry profile and
266
+ carries no canonical access rule, so a forged MCP `tools/call` under the old
267
+ name is rejected as an unknown tool before any authorization, database, or
268
+ graph work. The `'removed'` access classification remains available in the
269
+ extension contract but no canonical tool uses it.
270
+
271
+ ## [7.1.0] — 2026-07-25
272
+
273
+ ### Changed
274
+ - Complete the MCP legacy-surface removal declared in 7.0.0 (IND-596/597/598).
275
+ `createToolRegistry` is now surface-aware: the default `'rest'` profile (direct
276
+ HTTP Tool API + chat) retains contact/Gmail-import tools, `scrape_url`, and the
277
+ deprecated `*_user_profile` / `*_profile_run` compatibility aliases, while the
278
+ restricted `'mcp'` profile omits all of them. The MCP server builds both its
279
+ `tools/list` metadata and its `tools/call` lookup from the `'mcp'` profile, so
280
+ the removed names are no longer registered on the MCP surface — a direct
281
+ `tools/call` for any of them now fails as an unknown tool before any work.
282
+ - MCP `read_docs` guidance is sanitized by the MCP surface profile (never by
283
+ `CONTACTS_ENABLED`) so it no longer advertises the removed contact/Gmail
284
+ workflows; REST/chat `read_docs` retains the full guidance.
285
+ - `CONTACTS_ENABLED` no longer shapes the MCP registry or its metadata cache key.
286
+
287
+ ### Removed
288
+ - The `add_contact`, `import_contacts`, `import_gmail_contacts`, `list_contacts`,
289
+ `remove_contact`, `search_contacts`, `scrape_url`, `read_user_profiles`,
290
+ `create_user_profile`, `update_user_profile`, `confirm_user_profile`,
291
+ `preview_user_profile`, `get_profile_run`, and `cancel_profile_run` entries are
292
+ removed from the canonical MCP capability matrix; the tools are omitted from the
293
+ MCP registry rather than classified. Their non-MCP implementations (REST Tool
294
+ API, dedicated contact REST endpoints, chat agent, shared runtime
295
+ classifications) are unchanged.
296
+
297
+ ## [7.0.0] — 2026-07-25
298
+
299
+ ### Breaking
300
+ - MCP capability discovery is now principal-aware. `tools/list` advertises only
301
+ the tools available to the resolved human, onboarding, enrollment-key,
302
+ registered-agent, network-agent, or delivery-agent profile; `tools/call`
303
+ repeats the same authorization before scoped database and handler work.
304
+ - Replace the agent permission actions `manage:profile` and `manage:contacts`
305
+ with the canonical `manage:identity` and `manage:premises` actions. The full
306
+ MCP permission vocabulary is now `manage:identity`, `manage:premises`,
307
+ `manage:intents`, `manage:networks`, `manage:opportunities`, and
308
+ `manage:negotiations`.
309
+ - Contact tools, Gmail contact import, `scrape_url`, `report_agent_activity`,
310
+ and deprecated profile/profile-run aliases are explicitly unavailable
311
+ through MCP. Their non-MCP implementations remain intact.
312
+ - Agent-administration mutations are session-human-only. Enrollment keys may
313
+ call `register_agent` only when explicitly enrollment-capable; registered
314
+ agents may list only their own sanitized registration. Opportunity delivery
315
+ confirmation is exposed only to designated delivery agents.
316
+
317
+ ### Added
318
+ - Export a runtime-validated canonical MCP tool access matrix, permission/reach
319
+ extension contracts, principal schemas, and reusable capability-policy
320
+ implementation for host and capability composition.
321
+
15
322
  ## [6.14.0] — 2026-07-25
16
323
 
17
324
  ### Added
package/IMPLEMENTATION.md CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  This guide covers the technical package surface for implementers embedding the canonical Index Network Protocol implementation. For the public protocol overview, see [README.md](./README.md).
4
4
 
5
- The package implements LangGraph-based workflows for signal processing, opportunity discovery, negotiation, and chat — decoupled from any specific infrastructure via adapter injection.
6
5
 
7
6
  ## Stability & versioning
8
7
 
@@ -227,7 +226,12 @@ On every tool call the server:
227
226
 
228
227
  1. Extracts the HTTP request from the MCP `ServerContext`.
229
228
  2. Calls `authResolver.resolveIdentity(req)` to get `{ userId, agentId }`.
230
- 3. Gates access: MCP callers without a resolved `agentId` are blocked from every tool except `register_agent`, `read_docs`, and `scrape_url` until they register.
229
+ 3. Gates access through the canonical capability policy (`mcp/mcp.authorization-policy.ts`), decided per resolved principal BEFORE any context read or scoped-deps creation:
230
+ - **Enrollment-capable unregistered API keys** may see and call only `register_agent` — single-purpose across the entire registry.
231
+ - **Plain unregistered API keys** fail closed on every tool.
232
+ - **Registered active agents** retain their canonical permission- and network-scope-authorized domain tools, `read_docs`, and (for designated delivery agents) `confirm_opportunity_delivery`; within the agent-administration family (`MCP_AGENT_ADMIN_TOOLS`) they may see and call only `read_own_agent`, which returns the caller's own sanitized record and accepts no target.
233
+ - **Session humans** administer their owned agents (`register_agent`, `list_agents`, `update_agent`, `delete_agent`, `grant_agent_permission`, `revoke_agent_permission`) but are never offered the agent-only `read_own_agent`.
234
+ - Contact/Gmail-import tools, `scrape_url`, and the deprecated `*_user_profile`/`*_profile_run` aliases are not registered on the MCP surface at all (IND-596/597/598).
231
235
  4. Builds per-request scoped databases via `scopedDepsFactory` and invokes the tool handler through the shared runtime.
232
236
 
233
237
  ### Runtime controls
@@ -240,9 +244,8 @@ MCP tools are bounded by `ToolInvocationRuntime`:
240
244
  | `bounded_slow` | 45 s | `MCP_TOOL_TIMEOUT_BOUNDED_SLOW_MS` |
241
245
  | `async_candidate` | 50 s | `MCP_TOOL_TIMEOUT_ASYNC_CANDIDATE_MS` |
242
246
 
243
- Per-tool timeout overrides use `MCP_TOOL_TIMEOUT_<TOOL_NAME>_MS`, such as `MCP_TOOL_TIMEOUT_DISCOVER_OPPORTUNITIES_MS`. Tool outputs are capped by `MCP_TOOL_MAX_OUTPUT_BYTES` (default `1000000`) or `MCP_TOOL_MAX_OUTPUT_<TOOL_NAME>_BYTES`; inbound MCP request bodies are capped by the backend with `MCP_MAX_REQUEST_BYTES` (default `1000000`). Runtime failures return JSON text envelopes with stable `code` values: `TOOL_TIMEOUT`, `TOOL_CANCELLED`, or `TOOL_OUTPUT_TOO_LARGE`.
247
+ Per-tool timeout overrides use `MCP_TOOL_TIMEOUT_<TOOL_NAME>_MS`. Tool outputs are capped by `MCP_TOOL_MAX_OUTPUT_BYTES` (default `1000000`) or `MCP_TOOL_MAX_OUTPUT_<TOOL_NAME>_BYTES`; inbound MCP request bodies are capped by the backend with `MCP_MAX_REQUEST_BYTES` (default `1000000`). Runtime failures return JSON text envelopes with stable `code` values: `TOOL_TIMEOUT`, `TOOL_CANCELLED`, or `TOOL_OUTPUT_TOO_LARGE`.
244
248
 
245
- For MCP callers, `discover_opportunities` is async: it returns a `discoveryRunId` immediately, and clients poll `get_discovery_run` or request cancellation with `cancel_discovery_run`. Non-MCP chat/web paths stay synchronous.
246
249
 
247
250
  ### `MCP_INSTRUCTIONS`
248
251
 
@@ -39,9 +39,13 @@ export { canUserSeeOpportunity, isActionableForViewer, validateOpportunityActors
39
39
  export { getPrimaryActionLabel } from "../opportunity/domain/opportunity.labels.js";
40
40
  export { computeFeedHealth } from "../opportunity/feed/feed.health.js";
41
41
  export type { FeedHealthInput } from "../opportunity/feed/feed.health.js";
42
+ export { isIntroducerDiscoveryEnabled } from "../opportunity/application/opportunity.introducer-feature.js";
42
43
  export { selectContactsForDiscovery, shouldRunIntroducerDiscovery, runIntroducerDiscovery, MAX_CONTACTS_PER_CYCLE, MAX_CANDIDATES_PER_CONTACT, INTRODUCER_DISCOVERY_SOURCE } from "../opportunity/application/opportunity.introducer.js";
43
44
  export type { IntroducerDiscoveryDatabase, IntroducerDiscoveryQueue, ContactWithIntents } from "../opportunity/application/opportunity.introducer.js";
44
45
  export { persistOpportunities } from "../opportunity/application/opportunity.persist.js";
46
+ export { opportunityOwnerActionForStatus } from "../opportunity/application/opportunity.owner-approval.js";
47
+ export { bindOwnerApprovalProvenance } from "../opportunity/application/opportunity.owner-provenance.js";
48
+ export type { OpportunityOwnerAction, OpportunityOwnerApprovalAttestation, OpportunityOwnerApprovalAuthority, OpportunityOwnerApprovalBinding, OpportunityOwnerApprovalChallenge, OpportunityOwnerApprovalDenialReason, OpportunityOwnerApprovalVerdict, OpportunityOwnerInteractionProvenance, OpportunityOwnerInteractionSurface, } from "../opportunity/application/opportunity.owner-approval.js";
45
49
  export { presentOpportunity, stripUuids, truncateAtBoundary } from "../opportunity/domain/opportunity.presentation.js";
46
50
  export type { UserInfo } from "../opportunity/domain/opportunity.presentation.js";
47
51
  export { stripUnsupportedOpportunityClaims as stripUnsupportedOpportunityClaimsText } from "../opportunity/domain/opportunity.claim-safety.js";
@@ -39,9 +39,13 @@ export { canUserSeeOpportunity, isActionableForViewer, validateOpportunityActors
39
39
  export { getPrimaryActionLabel } from "../opportunity/domain/opportunity.labels.js";
40
40
  export { computeFeedHealth } from "../opportunity/feed/feed.health.js";
41
41
  // ── Introducer / ambient discovery ───────────────────────────────────────────
42
+ export { isIntroducerDiscoveryEnabled } from "../opportunity/application/opportunity.introducer-feature.js";
42
43
  export { selectContactsForDiscovery, shouldRunIntroducerDiscovery, runIntroducerDiscovery, MAX_CONTACTS_PER_CYCLE, MAX_CANDIDATES_PER_CONTACT, INTRODUCER_DISCOVERY_SOURCE } from "../opportunity/application/opportunity.introducer.js";
43
44
  // ── Persistence ───────────────────────────────────────────────────────────────
44
45
  export { persistOpportunities } from "../opportunity/application/opportunity.persist.js";
46
+ // ── Owner-approval boundary (IND-593) ─────────────────────────────────
47
+ export { opportunityOwnerActionForStatus } from "../opportunity/application/opportunity.owner-approval.js";
48
+ export { bindOwnerApprovalProvenance } from "../opportunity/application/opportunity.owner-provenance.js";
45
49
  // ── Presentation utilities ────────────────────────────────────────────────────
46
50
  export { presentOpportunity, stripUuids, truncateAtBoundary } from "../opportunity/domain/opportunity.presentation.js";
47
51
  export { stripUnsupportedOpportunityClaims as stripUnsupportedOpportunityClaimsText } from "../opportunity/domain/opportunity.claim-safety.js";
@@ -1,5 +1,5 @@
1
1
  import type { ToolRegistryCompositionDeps } from "../shared/agent/tool.helpers.js";
2
2
  /** Host capabilities consumed by signal and intent tools. */
3
- export type IntentToolDeps = Pick<ToolRegistryCompositionDeps, "userDb" | "systemDb"> & {
3
+ export type IntentToolDeps = Pick<ToolRegistryCompositionDeps, "userDb" | "systemDb"> & Pick<ToolRegistryCompositionDeps, "intentProposalStore"> & {
4
4
  graphs: Pick<ToolRegistryCompositionDeps["graphs"], "intent" | "intentIndex" | "profile">;
5
5
  };
@@ -324,14 +324,14 @@ export interface DebugMetaLlm {
324
324
  }>;
325
325
  }
326
326
  /**
327
- * Negotiation sessions initiated by the orchestrator during this turn.
327
+ * Negotiation sessions recorded during this turn.
328
328
  */
329
329
  export interface DebugMetaOrchestratorNegotiations {
330
330
  /** Opportunity IDs for which a negotiation_session_start was emitted. */
331
331
  opportunityIds: string[];
332
332
  }
333
333
  /**
334
- * Decision-question generation debug data (orchestrator path only).
334
+ * Decision-question generation debug data.
335
335
  */
336
336
  export interface DebugMetaDiscoveryQuestions {
337
337
  inputMode: "transcripts" | "insights";
@@ -349,7 +349,7 @@ export interface DebugMetaEvent extends ChatStreamEventBase {
349
349
  tools: DebugMetaToolCall[];
350
350
  llm: DebugMetaLlm;
351
351
  orchestratorNegotiations?: DebugMetaOrchestratorNegotiations;
352
- /** Decision-question generation debug data (orchestrator path only). */
352
+ /** Decision-question generation debug data. */
353
353
  discoveryQuestions?: DebugMetaDiscoveryQuestions;
354
354
  }
355
355
  /** Graph start event — emitted when a LangGraph sub-graph begins inside a tool. */
@@ -392,7 +392,7 @@ export interface AgentEndEvent extends ChatStreamEventBase {
392
392
  /** Structured outcome summary, e.g. "5 of 12 passed" or "3 intents extracted". */
393
393
  summary: string;
394
394
  }
395
- /** Orchestrator per-candidate negotiation wrapper emitted from `negotiateCandidates`. */
395
+ /** Per-candidate negotiation wrapper emitted from `negotiateCandidates`. */
396
396
  export interface NegotiationSessionStartEvent extends ChatStreamEventBase {
397
397
  type: "negotiation_session_start";
398
398
  opportunityId: string;
@@ -402,7 +402,6 @@ export interface NegotiationSessionStartEvent extends ChatStreamEventBase {
402
402
  /** The user holding the initiating seat for this match (v2 stamp). */
403
403
  initiatorUserId?: string;
404
404
  candidateName?: string;
405
- trigger: "orchestrator" | "ambient";
406
405
  startedAt: number;
407
406
  }
408
407
  export interface NegotiationSessionEndEvent extends ChatStreamEventBase {
@@ -3,7 +3,6 @@ import type { ToolContext } from "../shared/agent/tool.factory.js";
3
3
  import { type ChatPersonaConfig } from "./chat.persona.js";
4
4
  import type { DebugMetaToolCall, DebugMetaLlm, DebugMetaOrchestratorNegotiations, DebugMetaDiscoveryQuestions } from "./chat-streaming.types.js";
5
5
  import type { Question, QuestionStrategy } from "../shared/schemas/question.schema.js";
6
- import type { Opportunity } from "../shared/interfaces/database.interface.js";
7
6
  export { ITERATION_NUDGE } from "./chat.prompt.js";
8
7
  /**
9
8
  * Writer callback for streaming custom data out of the graph node.
@@ -22,8 +21,6 @@ export type StreamWriter = (data: unknown) => void;
22
21
  * - `graph_end` — a LangGraph sub-graph completes
23
22
  * - `agent_start` — an LLM agent begins inside a graph node
24
23
  * - `agent_end` — an LLM agent completes
25
- * - `opportunity_draft_ready` — an orchestrator-triggered negotiation finalized
26
- * to `draft` and the card is ready to render inline
27
24
  */
28
25
  export type AgentStreamEvent = {
29
26
  type: "iteration_start";
@@ -83,16 +80,6 @@ export type AgentStreamEvent = {
83
80
  name: string;
84
81
  durationMs: number;
85
82
  summary: string;
86
- } | {
87
- type: "opportunity_draft_ready";
88
- opportunityId: string;
89
- opportunity: Opportunity;
90
- /** Viewer-centric summary derived from interpretation.reasoning via viewerCentricCardSummary. */
91
- personalizedSummary?: string;
92
- counterparty: {
93
- userId: string;
94
- name?: string;
95
- };
96
83
  } | {
97
84
  type: "negotiation_session_start";
98
85
  opportunityId: string;
@@ -100,7 +87,6 @@ export type AgentStreamEvent = {
100
87
  sourceUserId: string;
101
88
  candidateUserId: string;
102
89
  candidateName?: string;
103
- trigger: "orchestrator" | "ambient";
104
90
  startedAt: number;
105
91
  } | {
106
92
  type: "negotiation_session_end";
@@ -255,11 +241,6 @@ export declare class ChatAgent {
255
241
  * Execute tool calls, potentially in parallel.
256
242
  */
257
243
  private executeToolCalls;
258
- /**
259
- * When discover_opportunities returned createIntentSuggested, call create_intent then discover_opportunities.
260
- * Returns the new discover_opportunities result string or null if no callback / create_intent failed.
261
- */
262
- private handleCreateIntentCallback;
263
244
  /**
264
245
  * Check whether any tool call produced valid opportunity blocks.
265
246
  * Both `discover_opportunities` and `list_opportunities` can return
@@ -330,13 +330,6 @@ export class ChatAgent {
330
330
  logger.verbose("Executing tool", { name: tc.name, args: tc.args });
331
331
  let result = await tool.invoke(tc.args);
332
332
  let resultStr = typeof result === "string" ? result : JSON.stringify(result);
333
- if (tc.name === "discover_opportunities" && this.persona.loopBehaviors.createIntentCallback) {
334
- const newResult = await this.handleCreateIntentCallback(resultStr, tc.args);
335
- if (newResult !== null) {
336
- resultStr = newResult;
337
- result = newResult;
338
- }
339
- }
340
333
  logger.debug("Tool response", { name: tc.name, result: resultStr });
341
334
  logger.verbose("Tool completed", {
342
335
  name: tc.name,
@@ -365,59 +358,13 @@ export class ChatAgent {
365
358
  }));
366
359
  return results;
367
360
  }
368
- /**
369
- * When discover_opportunities returned createIntentSuggested, call create_intent then discover_opportunities.
370
- * Returns the new discover_opportunities result string or null if no callback / create_intent failed.
371
- */
372
- async handleCreateIntentCallback(resultStr, originalArgs) {
373
- let parsed;
374
- try {
375
- parsed = JSON.parse(resultStr);
376
- }
377
- catch {
378
- return null;
379
- }
380
- if (!parsed?.data?.createIntentSuggested ||
381
- typeof parsed.data.suggestedIntentDescription !== "string") {
382
- return null;
383
- }
384
- // Never auto-create intents during introducer flows — signals are personal
385
- if (originalArgs.introTargetUserId) {
386
- return null;
387
- }
388
- const createIntentTool = this.toolsByName.get("create_intent");
389
- const discoverOpportunitiesTool = this.toolsByName.get("discover_opportunities");
390
- if (!createIntentTool || !discoverOpportunitiesTool)
391
- return null;
392
- logger.verbose("Create-intent signal: auto-calling create_intent then discover_opportunities");
393
- const createIntentResult = await createIntentTool.invoke({
394
- description: parsed.data.suggestedIntentDescription,
395
- networkId: originalArgs.networkId,
396
- });
397
- const createIntentStr = typeof createIntentResult === "string" ? createIntentResult : JSON.stringify(createIntentResult);
398
- let createIntentParsed;
399
- try {
400
- createIntentParsed = JSON.parse(createIntentStr);
401
- }
402
- catch {
403
- createIntentParsed = {};
404
- }
405
- if (createIntentParsed.success === false) {
406
- logger.warn("Create-intent failed; not re-running discover_opportunities", {
407
- error: createIntentParsed.error,
408
- });
409
- return null;
410
- }
411
- const newResult = await discoverOpportunitiesTool.invoke(originalArgs);
412
- return typeof newResult === "string" ? newResult : JSON.stringify(newResult);
413
- }
414
361
  /**
415
362
  * Check whether any tool call produced valid opportunity blocks.
416
363
  * Both `discover_opportunities` and `list_opportunities` can return
417
364
  * ```opportunity code blocks — either one counts as a valid source.
418
365
  */
419
366
  static hasOpportunitySource(toolsUsed) {
420
- return toolsUsed.some((t) => (t.name === "discover_opportunities" || t.name === "list_opportunities") &&
367
+ return toolsUsed.some((t) => t.name === "list_opportunities" &&
421
368
  t.success &&
422
369
  !t.resultSummary?.startsWith("Found 0") &&
423
370
  !t.resultSummary?.startsWith("No matches") &&
@@ -441,15 +388,6 @@ export class ChatAgent {
441
388
  return { type: "intent_proposal", tool: "create_intent", description: match[1] };
442
389
  }
443
390
  }
444
- // Check for hallucinated opportunity blocks
445
- if (text.includes("```opportunity") && !ChatAgent.hasOpportunitySource(toolsUsed)) {
446
- // Use the user's original message as the search query — NOT fields from the
447
- // hallucinated JSON. The model fabricates person names and reasoning that have
448
- // nothing to do with the user's actual request, leading to wrong results and
449
- // the model re-calling the tool with the correct query (doubling negotiation cost).
450
- const description = userMessage?.trim() || "find connections";
451
- return { type: "opportunity", tool: "discover_opportunities", description };
452
- }
453
391
  return null;
454
392
  }
455
393
  /**
@@ -501,13 +439,6 @@ export class ChatAgent {
501
439
  */
502
440
  async normalizeToolResult(toolName, resultStr, toolArgs) {
503
441
  let normalized = resultStr;
504
- // Run create_intent callback for discover_opportunities results (orchestrator behavior)
505
- if (toolName === "discover_opportunities" && this.persona.loopBehaviors.createIntentCallback) {
506
- const callbackResult = await this.handleCreateIntentCallback(normalized, toolArgs);
507
- if (callbackResult !== null) {
508
- normalized = callbackResult;
509
- }
510
- }
511
442
  let summary = "Done";
512
443
  let debugSteps;
513
444
  let graphTimings;
@@ -5,12 +5,6 @@ import type { IterationContext } from "./chat.prompt.modules.js";
5
5
  * (not in the prompt or toolset). Each persona opts in explicitly.
6
6
  */
7
7
  export interface ChatPersonaLoopBehaviors {
8
- /**
9
- * When `discover_opportunities` returns `createIntentSuggested`, auto-invoke
10
- * `create_intent` and then re-run `discover_opportunities` with the original
11
- * arguments.
12
- */
13
- createIntentCallback: boolean;
14
8
  /**
15
9
  * Detect hallucinated ```opportunity / ```intent_proposal code blocks in model
16
10
  * text, auto-invoke the corresponding tool, and strip unbacked blocks from the
@@ -19,7 +19,6 @@ export const ORCHESTRATOR_PERSONA = {
19
19
  buildSystemContent: (ctx, iterCtx) => buildSystemContent(ctx, iterCtx),
20
20
  createTools: (deps, preResolvedContext) => createChatTools(deps, preResolvedContext),
21
21
  loopBehaviors: {
22
- createIntentCallback: true,
23
22
  hallucinationRecovery: true,
24
23
  },
25
24
  };