@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
@@ -6,10 +6,8 @@
6
6
  import { protocolLogger } from "../../shared/observability/protocol.logger.js";
7
7
  import { computeNovelty, scoreDiscriminator } from "./discriminator.scorer.js";
8
8
  const logger = protocolLogger("PoolDiscriminatorShadow");
9
- /** Max text/vector references compared for novelty. */
9
+ /** Max ordinary current-intent/premise text references compared for novelty. */
10
10
  const MAX_REFERENCE_TEXTS = 24;
11
- const MAX_PRIOR_REFERENCE_TEXTS = 24;
12
- const MAX_REFERENCE_EMBEDDINGS = 24;
13
11
  /** Text embedded per discriminator for the novelty comparison. */
14
12
  function discriminatorEmbeddingText(d) {
15
13
  return `${d.label} — ${d.questionSeed}`;
@@ -31,17 +29,16 @@ export async function runPoolDiscriminatorShadow(input) {
31
29
  .slice(0, MAX_REFERENCE_TEXTS);
32
30
  const priorReferenceTexts = (input.priorReferenceTexts ?? [])
33
31
  .map((text) => text.trim())
34
- .filter((text) => text.length > 0)
35
- .slice(0, MAX_PRIOR_REFERENCE_TEXTS);
32
+ .filter((text) => text.length > 0);
36
33
  const priorReferenceEmbeddings = (input.priorReferenceEmbeddings ?? [])
37
- .filter((embedding) => embedding.length > 0)
38
- .slice(0, MAX_REFERENCE_EMBEDDINGS);
34
+ .filter((embedding) => embedding.length > 0);
35
+ const hasPriorReferenceHistory = priorReferenceTexts.length > 0 || priorReferenceEmbeddings.length > 0;
39
36
  let novelties = mined.map(() => 1);
40
37
  let discriminatorEmbeddings = null;
38
+ let priorReferenceComparisonUnavailable = false;
41
39
  const shouldEmbed = input.retainEmbeddings
42
40
  || references.length > 0
43
- || priorReferenceTexts.length > 0
44
- || priorReferenceEmbeddings.length > 0;
41
+ || hasPriorReferenceHistory;
45
42
  if (shouldEmbed) {
46
43
  try {
47
44
  const texts = [
@@ -53,6 +50,11 @@ export async function runPoolDiscriminatorShadow(input) {
53
50
  if (embeddings.length !== texts.length || embeddings.some((embedding) => !Array.isArray(embedding) || embedding.length === 0)) {
54
51
  throw new Error("Embedding provider returned an invalid batch");
55
52
  }
53
+ const comparisonEmbeddings = [...embeddings, ...priorReferenceEmbeddings];
54
+ const dimension = comparisonEmbeddings[0]?.length;
55
+ if (dimension !== undefined && comparisonEmbeddings.some((embedding) => embedding.length !== dimension)) {
56
+ throw new Error("Embedding comparison batch contains incompatible vector dimensions");
57
+ }
56
58
  discriminatorEmbeddings = embeddings.slice(0, mined.length);
57
59
  const referenceEmbeddings = [
58
60
  ...priorReferenceEmbeddings,
@@ -64,6 +66,7 @@ export async function runPoolDiscriminatorShadow(input) {
64
66
  logger.warn("Novelty embedding failed; defaulting novelty to 1", {
65
67
  error: err instanceof Error ? err.message : String(err),
66
68
  });
69
+ priorReferenceComparisonUnavailable = hasPriorReferenceHistory;
67
70
  }
68
71
  }
69
72
  const discriminators = mined
@@ -79,5 +82,9 @@ export async function runPoolDiscriminatorShadow(input) {
79
82
  : scored;
80
83
  })
81
84
  .sort((a, b) => b.voi - a.voi);
82
- return { poolSize: candidates.length, discriminators };
85
+ return {
86
+ poolSize: candidates.length,
87
+ discriminators,
88
+ ...(priorReferenceComparisonUnavailable ? { priorReferenceComparisonUnavailable: true } : {}),
89
+ };
83
90
  }
@@ -88,4 +88,6 @@ export interface DiscriminatorShadowResult {
88
88
  poolSize: number;
89
89
  /** Scored discriminators, sorted by VoI descending. */
90
90
  discriminators: ScoredDiscriminator[];
91
+ /** Historical-axis comparison could not be completed due to invalid embeddings. */
92
+ priorReferenceComparisonUnavailable?: boolean;
91
93
  }
@@ -25,7 +25,6 @@
25
25
  * IND-551: canonical domain layer for the opportunities capability.
26
26
  */
27
27
  export * from "./opportunity.state.js";
28
- export * from "./opportunity.discovery.contracts.js";
29
28
  export * from "./opportunity.labels.js";
30
29
  export * from "./opportunity.utils.js";
31
30
  export * from "./opportunity.actor.js";
@@ -34,7 +33,6 @@ export * from "./opportunity.claim-safety.js";
34
33
  export * from "./opportunity.presentation.js";
35
34
  export * from "./opportunity.safe-presentation.js";
36
35
  export * from "./opportunity.presentation-cache.js";
37
- export * from "./opportunity.discovery-run-coalescing.js";
38
36
  export * from "./negotiation-summary.builder.js";
39
37
  export * from "./discovery-question.helper.js";
40
38
  export type { PoolCandidate, DiscriminatorMiningInput, MinedDiscriminator, ScoredDiscriminator, VerifiedAssignment, DiscriminatorShadowResult, } from "../discriminator/discriminator.types.js";
@@ -26,7 +26,6 @@
26
26
  */
27
27
  // ── Flat domain files ─────────────────────────────────────────────────────────
28
28
  export * from "./opportunity.state.js";
29
- export * from "./opportunity.discovery.contracts.js";
30
29
  export * from "./opportunity.labels.js";
31
30
  export * from "./opportunity.utils.js";
32
31
  export * from "./opportunity.actor.js";
@@ -35,7 +34,6 @@ export * from "./opportunity.claim-safety.js";
35
34
  export * from "./opportunity.presentation.js";
36
35
  export * from "./opportunity.safe-presentation.js";
37
36
  export * from "./opportunity.presentation-cache.js";
38
- export * from "./opportunity.discovery-run-coalescing.js";
39
37
  export * from "./negotiation-summary.builder.js";
40
38
  export * from "./discovery-question.helper.js";
41
39
  export { poolQuestionsMiningMode, poolQuestionsMode, poolQuestionsPushMode, poolQuestionsStampNewborn, POOL_DISCRIMINATOR_MIN_POOL_SIZE, POOL_DISCRIMINATOR_MAX_CANDIDATES, POOL_DISCRIMINATOR_MAX_PUBLIC_CONTEXT_CHARS, POOL_QUESTION_MIN_VOI, POOL_QUESTION_MAX_PENDING_PER_INTENT, poolQuestionsRanking, POOL_RERUN_DEBOUNCE_MS, poolQuestionsVisitTrigger, POOL_VISIT_MINING_DEBOUNCE_MS, } from "../discriminator/discriminator.env.js";
@@ -108,40 +108,6 @@ export interface OpportunityPersistenceOutcome {
108
108
  crossTriggerAllowedCount: number;
109
109
  finalAtomicConflictCount: number;
110
110
  }
111
- /**
112
- * Which flow triggered this graph invocation. Determines initial persist status,
113
- * park-window timeout, streaming behavior, and whether AbortSignal is honored.
114
- *
115
- * - 'ambient' (default): queue-driven. Persists at the trigger default of
116
- * `pending` unless `options.initialStatus` overrides (the queue worker
117
- * passes `'latent'`, chat-bound ambient discovery passes `'draft'`). 5-min
118
- * park window, no streaming, ignores abort.
119
- * - 'orchestrator': chat-driven. Persists at the trigger default of
120
- * `negotiating` unless `options.initialStatus` overrides. 60s park window,
121
- * streams `opportunity_draft_ready` events, honors abort.
122
- *
123
- * See {@link resolveInitialStatus} for the exact fallback used when
124
- * `options.initialStatus` is undefined.
125
- */
126
- export type OpportunityTrigger = 'ambient' | 'orchestrator';
127
- /**
128
- * Resolves the initial status for opportunities created in the persist node.
129
- *
130
- * Explicit `options.initialStatus` always wins (callers like the chat tool or
131
- * maintenance scripts override per-call). When the caller leaves it
132
- * undefined, the trigger drives the default:
133
- * - 'orchestrator' → 'negotiating' (chat-driven; negotiations run before the
134
- * user sees a draft card).
135
- * - 'ambient' (or any other trigger) → 'pending' (long-standing default for
136
- * queue- and intent-driven discovery).
137
- *
138
- * Lives here rather than in opportunity.graph.ts so unit tests can exercise
139
- * it without pulling in the full graph (and the evaluator's LLM requirements).
140
- *
141
- * @param trigger - The graph invocation's trigger
142
- * @param explicit - Caller-supplied initial status from options.initialStatus
143
- */
144
- export declare function resolveInitialStatus(trigger: OpportunityTrigger, explicit: OpportunityStatus | undefined): OpportunityStatus;
145
111
  /**
146
112
  * Options passed to the graph
147
113
  */
@@ -194,37 +160,6 @@ export declare const OpportunityGraphState: import("@langchain/langgraph").Annot
194
160
  /** Optional: discover on behalf of this user (introducer flow). When set, prep/eval use this user's profile/intents; userId becomes the introducer. */
195
161
  onBehalfOfUserId: import("@langchain/langgraph").BaseChannel<Id<"users"> | undefined, Id<"users"> | import("@langchain/langgraph").OverwriteValue<Id<"users"> | undefined> | undefined, unknown>;
196
162
  options: import("@langchain/langgraph").BaseChannel<OpportunityGraphOptions, OpportunityGraphOptions | import("@langchain/langgraph").OverwriteValue<OpportunityGraphOptions>, unknown>;
197
- /**
198
- * Which flow triggered this graph invocation. See {@link OpportunityTrigger}
199
- * for the exact branch behavior and {@link resolveInitialStatus} for the
200
- * persist default when `options.initialStatus` is unset.
201
- *
202
- * - 'ambient' (default): queue-driven, persist default `pending`, 5-min
203
- * park window, no streaming, ignores abort.
204
- * - 'orchestrator': chat-driven, persist default `negotiating`, 60s park
205
- * window, streams `opportunity_draft_ready` events, honors abort.
206
- */
207
- trigger: import("@langchain/langgraph").BaseChannel<OpportunityTrigger, OpportunityTrigger | import("@langchain/langgraph").OverwriteValue<OpportunityTrigger>, unknown>;
208
- /**
209
- * Accepted opportunities the persist node discovered between the discoverer
210
- * and a candidate actor (same pair, status='accepted'). The orchestrator
211
- * branch populates this so the discover_opportunities tool (Task 7) can tell
212
- * the LLM "these pairs are already connected, surface the existing chat
213
- * rather than creating a new draft". Always empty for the ambient trigger.
214
- *
215
- * Left intentionally minimal — conversationId/URL resolution happens at
216
- * Start Chat time (Task 8), not here.
217
- */
218
- dedupAlreadyAccepted: import("@langchain/langgraph").BaseChannel<{
219
- opportunityId: string;
220
- counterpartyUserId: string;
221
- }[], {
222
- opportunityId: string;
223
- counterpartyUserId: string;
224
- }[] | import("@langchain/langgraph").OverwriteValue<{
225
- opportunityId: string;
226
- counterpartyUserId: string;
227
- }[]>, unknown>;
228
163
  /**
229
164
  * Operation mode controls graph flow:
230
165
  * - 'create': Existing discover pipeline (Prep → Scope → Discovery → Evaluation → Ranking → Persist)
@@ -441,11 +376,8 @@ export declare const OpportunityGraphState: import("@langchain/langgraph").Annot
441
376
  /**
442
377
  * Per-candidate negotiation records captured by `negotiateNode`. Populated
443
378
  * regardless of accept/reject so the question generator sees a complete
444
- * picture. Populated for ALL triggers (ambient + orchestrator) since the
445
- * negotiate node's `onCandidateResolved` hook is unconditional; only the
446
- * orchestrator streaming side-effects (opportunity_draft_ready emission)
447
- * are trigger-gated. Empty when the negotiate node was skipped (no
448
- * opportunities to negotiate).
379
+ * picture. Empty when the negotiate node was skipped (no opportunities to
380
+ * negotiate).
449
381
  */
450
382
  discoveryNegotiations: import("@langchain/langgraph").BaseChannel<{
451
383
  counterpartyHint: string;
@@ -1,26 +1,4 @@
1
1
  import { Annotation } from "@langchain/langgraph";
2
- /**
3
- * Resolves the initial status for opportunities created in the persist node.
4
- *
5
- * Explicit `options.initialStatus` always wins (callers like the chat tool or
6
- * maintenance scripts override per-call). When the caller leaves it
7
- * undefined, the trigger drives the default:
8
- * - 'orchestrator' → 'negotiating' (chat-driven; negotiations run before the
9
- * user sees a draft card).
10
- * - 'ambient' (or any other trigger) → 'pending' (long-standing default for
11
- * queue- and intent-driven discovery).
12
- *
13
- * Lives here rather than in opportunity.graph.ts so unit tests can exercise
14
- * it without pulling in the full graph (and the evaluator's LLM requirements).
15
- *
16
- * @param trigger - The graph invocation's trigger
17
- * @param explicit - Caller-supplied initial status from options.initialStatus
18
- */
19
- export function resolveInitialStatus(trigger, explicit) {
20
- if (explicit !== undefined)
21
- return explicit;
22
- return trigger === 'orchestrator' ? 'negotiating' : 'pending';
23
- }
24
2
  /**
25
3
  * Opportunity Graph State Annotation
26
4
  */
@@ -68,34 +46,6 @@ export const OpportunityGraphState = Annotation.Root({
68
46
  reducer: (curr, next) => next ?? curr,
69
47
  default: () => ({}),
70
48
  }),
71
- /**
72
- * Which flow triggered this graph invocation. See {@link OpportunityTrigger}
73
- * for the exact branch behavior and {@link resolveInitialStatus} for the
74
- * persist default when `options.initialStatus` is unset.
75
- *
76
- * - 'ambient' (default): queue-driven, persist default `pending`, 5-min
77
- * park window, no streaming, ignores abort.
78
- * - 'orchestrator': chat-driven, persist default `negotiating`, 60s park
79
- * window, streams `opportunity_draft_ready` events, honors abort.
80
- */
81
- trigger: Annotation({
82
- reducer: (curr, next) => next ?? curr,
83
- default: () => 'ambient',
84
- }),
85
- /**
86
- * Accepted opportunities the persist node discovered between the discoverer
87
- * and a candidate actor (same pair, status='accepted'). The orchestrator
88
- * branch populates this so the discover_opportunities tool (Task 7) can tell
89
- * the LLM "these pairs are already connected, surface the existing chat
90
- * rather than creating a new draft". Always empty for the ambient trigger.
91
- *
92
- * Left intentionally minimal — conversationId/URL resolution happens at
93
- * Start Chat time (Task 8), not here.
94
- */
95
- dedupAlreadyAccepted: Annotation({
96
- reducer: (curr, next) => next ?? curr,
97
- default: () => [],
98
- }),
99
49
  /**
100
50
  * Operation mode controls graph flow:
101
51
  * - 'create': Existing discover pipeline (Prep → Scope → Discovery → Evaluation → Ranking → Persist)
@@ -291,11 +241,8 @@ export const OpportunityGraphState = Annotation.Root({
291
241
  /**
292
242
  * Per-candidate negotiation records captured by `negotiateNode`. Populated
293
243
  * regardless of accept/reject so the question generator sees a complete
294
- * picture. Populated for ALL triggers (ambient + orchestrator) since the
295
- * negotiate node's `onCandidateResolved` hook is unconditional; only the
296
- * orchestrator streaming side-effects (opportunity_draft_ready emission)
297
- * are trigger-gated. Empty when the negotiate node was skipped (no
298
- * opportunities to negotiate).
244
+ * picture. Empty when the negotiate node was skipped (no opportunities to
245
+ * negotiate).
299
246
  */
300
247
  discoveryNegotiations: Annotation({
301
248
  reducer: (curr, next) => [...curr, ...(next || [])],
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Thin backward-compat shim — IND-551.
3
+ * Canonical location: opportunity/application/opportunity.owner-approval.ts
4
+ */
5
+ export * from "./application/opportunity.owner-approval.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Thin backward-compat shim — IND-551.
3
+ * Canonical location: opportunity/application/opportunity.owner-approval.ts
4
+ */
5
+ export * from "./application/opportunity.owner-approval.js";
@@ -1,5 +1,16 @@
1
1
  import type { ToolRegistryCompositionDeps } from "../../shared/agent/tool.helpers.js";
2
+ import type { OpportunityOwnerApprovalAuthority } from "../application/opportunity.owner-approval.js";
3
+ /**
4
+ * IND-593 host authority consumed only by the opportunity mutation boundary.
5
+ *
6
+ * This stays capability-local rather than extending the shared registry shape:
7
+ * the shared helper participates in the negotiation/question architecture
8
+ * cycle, while owner approval belongs to the opportunity composition seam.
9
+ */
10
+ export interface OpportunityOwnerApprovalDeps {
11
+ opportunityOwnerApproval?: OpportunityOwnerApprovalAuthority;
12
+ }
2
13
  /** Host capabilities consumed by opportunity discovery, delivery, and presentation tools. */
3
- export type OpportunityToolDeps = Pick<ToolRegistryCompositionDeps, "database" | "userDb" | "systemDb" | "cache" | "chatSummary" | "opportunityDiscovery" | "opportunityPresentation" | "questionGenerator" | "questionerEnqueue" | "findPendingQuestions" | "negotiationSummary" | "negotiationDatabase" | "deliveryLedger" | "discoveryRuns" | "discoveryRunQueue" | "mintConnectLink" | "frontendUrl" | "stampNewbornOpportunities" | "reportToolError"> & {
14
+ export type OpportunityToolDeps = Pick<ToolRegistryCompositionDeps, "database" | "userDb" | "systemDb" | "cache" | "chatSummary" | "opportunityPresentation" | "questionGenerator" | "questionerEnqueue" | "findPendingQuestions" | "negotiationSummary" | "negotiationDatabase" | "deliveryLedger" | "mintConnectLink" | "frontendUrl" | "stampNewbornOpportunities" | "reportToolError"> & OpportunityOwnerApprovalDeps & {
4
15
  graphs: Pick<ToolRegistryCompositionDeps["graphs"], "index" | "networkMembership" | "opportunity">;
5
16
  };
@@ -7,9 +7,8 @@
7
7
  *
8
8
  * IND-551: canonical public surface for the opportunities capability.
9
9
  */
10
- export { OpportunityGraphState, resolveInitialStatus, } from "../domain/opportunity.state.js";
11
- export type { SourceProfileData, IndexedIntent, TargetNetwork, CandidateMatch, EvaluatedCandidate, EvaluatedOpportunityActor, EvaluatedOpportunity, OpportunityPersistenceOutcome, OpportunityTrigger, OpportunityGraphOptions, } from "../domain/opportunity.state.js";
12
- export type { DiscoverDebugStep, DiscoveryResultContract, } from "../domain/opportunity.discovery.contracts.js";
10
+ export { OpportunityGraphState, } from "../domain/opportunity.state.js";
11
+ export type { SourceProfileData, IndexedIntent, TargetNetwork, CandidateMatch, EvaluatedCandidate, EvaluatedOpportunityActor, EvaluatedOpportunity, OpportunityPersistenceOutcome, OpportunityGraphOptions, } from "../domain/opportunity.state.js";
13
12
  export { MINIMAL_MAIN_TEXT_MAX_CHARS, PRIMARY_ACTION_LABEL_INTRODUCER, PRIMARY_ACTION_LABEL_DEFAULT, SECONDARY_ACTION_LABEL, getPrimaryActionLabel, } from "../domain/opportunity.labels.js";
14
13
  export { deriveRolesFromCorpus, validateOpportunityActors, canUserSeeOpportunity, isActionableForViewer, classifyOpportunity, selectByComposition, deduplicateByPerson, selectDigestCandidates, FEED_SOFT_TARGETS, DIGEST_REDELIVERY_COOLDOWN_DAYS, } from "../domain/opportunity.utils.js";
15
14
  export type { OpportunityActorRole, DerivedRoles, FeedCategory, DigestDeliveredRow, } from "../domain/opportunity.utils.js";
@@ -35,6 +34,7 @@ export type { OutcomeLabel, OutcomeExample, OutcomeShadowResult, } from "../outc
35
34
  export { isOutcomeQuestionsActivated, OUTCOME_MIN_INDEPENDENT_EXAMPLES, OUTCOME_MAX_CANDIDATES, OUTCOME_MAX_PUBLIC_CONTEXT_CHARS, } from "../outcome/outcome.env.js";
36
35
  export { computeFeedHealth } from "../feed/feed.health.js";
37
36
  export type { FeedHealthInput } from "../feed/feed.health.js";
37
+ export { isIntroducerDiscoveryEnabled } from "../application/opportunity.introducer-feature.js";
38
38
  export { OpportunityGraphFactory } from "../application/opportunity.graph.js";
39
39
  export type { StampNewbornOpportunitiesFn, StampNewbornOpportunitiesInput, } from "../application/opportunity.newborn-stamping.js";
40
40
  export { OpportunityEvaluator } from "../application/opportunity.evaluator.js";
@@ -10,7 +10,7 @@
10
10
  // ── Domain exports ────────────────────────────────────────────────────────────
11
11
  export {
12
12
  // state
13
- OpportunityGraphState, resolveInitialStatus, } from "../domain/opportunity.state.js";
13
+ OpportunityGraphState, } from "../domain/opportunity.state.js";
14
14
  export { MINIMAL_MAIN_TEXT_MAX_CHARS, PRIMARY_ACTION_LABEL_INTRODUCER, PRIMARY_ACTION_LABEL_DEFAULT, SECONDARY_ACTION_LABEL, getPrimaryActionLabel, } from "../domain/opportunity.labels.js";
15
15
  export { deriveRolesFromCorpus, validateOpportunityActors, canUserSeeOpportunity, isActionableForViewer, classifyOpportunity, selectByComposition, deduplicateByPerson, selectDigestCandidates, FEED_SOFT_TARGETS, DIGEST_REDELIVERY_COOLDOWN_DAYS, } from "../domain/opportunity.utils.js";
16
16
  export { normalizeOpportunityActorIntent, resolveOpportunityActorIntent, normalizeOpportunityActors, normalizeCreateOpportunityActorIntents, } from "../domain/opportunity.actor.js";
@@ -29,6 +29,7 @@ export { isOutcomeQuestionsActivated, OUTCOME_MIN_INDEPENDENT_EXAMPLES, OUTCOME_
29
29
  // feed domain
30
30
  export { computeFeedHealth } from "../feed/feed.health.js";
31
31
  // ── Application exports ───────────────────────────────────────────────────────
32
+ export { isIntroducerDiscoveryEnabled } from "../application/opportunity.introducer-feature.js";
32
33
  export { OpportunityGraphFactory } from "../application/opportunity.graph.js";
33
34
  export { OpportunityEvaluator } from "../application/opportunity.evaluator.js";
34
35
  export { OpportunityPresenter, gatherPresenterContext, } from "../application/opportunity.presenter.js";
@@ -13,4 +13,4 @@
13
13
  */
14
14
  import type { DefineTool } from '../../shared/agent/tool.helpers.js';
15
15
  import type { AgentToolDeps } from '../ports/index.js';
16
- export declare function createAgentTools(defineTool: DefineTool, deps: AgentToolDeps): never[] | readonly [any, any, any, any, any, any];
16
+ export declare function createAgentTools(defineTool: DefineTool, deps: AgentToolDeps): never[] | readonly [any, any, any, any, any, any, any];
@@ -16,13 +16,19 @@ import { error, success } from '../../shared/agent/tool.helpers.js';
16
16
  import { protocolLogger } from '../../shared/observability/protocol.logger.js';
17
17
  const logger = protocolLogger('ChatTools:Agent');
18
18
  const AGENT_ACTIONS = [
19
- 'manage:profile',
19
+ 'manage:identity',
20
+ 'manage:premises',
20
21
  'manage:intents',
21
22
  'manage:networks',
22
- 'manage:contacts',
23
23
  'manage:opportunities',
24
24
  'manage:negotiations',
25
25
  ];
26
+ /**
27
+ * Canonical permission-action enum for the public tool INPUT schemas. Retired
28
+ * `manage:profile` / `manage:contacts` strings are rejected at the schema seam
29
+ * (defense-in-depth: the handler still re-validates via {@link isValidAction}).
30
+ */
31
+ const AgentPermissionActionSchema = z.enum(AGENT_ACTIONS);
26
32
  function invalidActionMessage(action) {
27
33
  return `Invalid action: ${action}. Valid actions: ${AGENT_ACTIONS.join(', ')}`;
28
34
  }
@@ -44,7 +50,11 @@ function ensureAgentScopedAccess(context, requestedAgentId) {
44
50
  function sanitizeAgentForOutput(agent) {
45
51
  return {
46
52
  ...agent,
47
- transports: agent.transports,
53
+ // Transport config carries private connection material (endpoint secrets,
54
+ // auth headers/tokens). It is never projected to ANY MCP caller — including
55
+ // the agent reading its own record via read_own_agent (IND-599). Channel,
56
+ // priority, and health metadata remain visible; config is fully redacted.
57
+ transports: agent.transports?.map((transport) => ({ ...transport, config: {} })),
48
58
  };
49
59
  }
50
60
  function sanitizeAgentName(name) {
@@ -66,7 +76,7 @@ export function createAgentTools(defineTool, deps) {
66
76
  querySchema: z.object({
67
77
  name: z.string().min(1).describe('Display name for the agent.'),
68
78
  description: z.string().optional().describe('What the agent does.'),
69
- permissions: z.array(z.string()).optional().describe('Optional initial permission actions to grant.'),
79
+ permissions: z.array(AgentPermissionActionSchema).optional().describe('Optional initial permission actions to grant. Valid values: manage:identity, manage:premises, manage:intents, manage:networks, manage:opportunities, manage:negotiations.'),
70
80
  }),
71
81
  handler: async ({ context, query }) => {
72
82
  if (context.agentId) {
@@ -121,6 +131,33 @@ export function createAgentTools(defineTool, deps) {
121
131
  }
122
132
  },
123
133
  });
134
+ const readOwnAgent = defineTool({
135
+ name: 'read_own_agent',
136
+ description: "Read the calling agent's own registration record \u2014 its identity, " +
137
+ 'transports, and granted permissions. Returns only the authenticated ' +
138
+ 'agent\u2019s own record; no other agent can be named or targeted. Use this ' +
139
+ 'when an agent needs to inspect its own configuration.',
140
+ querySchema: z.object({}),
141
+ handler: async ({ context }) => {
142
+ // Defense-in-depth: the capability policy only admits registered active
143
+ // agent principals here, but never trust the caller — require an agent
144
+ // context and resolve strictly the caller's OWN record (no target input).
145
+ if (!context.agentId) {
146
+ return error('read_own_agent is only available to a registered agent principal.');
147
+ }
148
+ try {
149
+ const agent = await agentDb.getAgentWithRelations(context.agentId);
150
+ if (!agent || agent.ownerId !== context.userId) {
151
+ return error('Agent not found');
152
+ }
153
+ return success({ agent: sanitizeAgentForOutput(agent) });
154
+ }
155
+ catch (err) {
156
+ logger.error('Failed to read own agent', { err });
157
+ return error('Failed to read agent. Please try again.');
158
+ }
159
+ },
160
+ });
124
161
  const listAgents = defineTool({
125
162
  name: 'list_agents',
126
163
  description: 'List all agents the current user owns or has authorized.',
@@ -228,10 +265,10 @@ export function createAgentTools(defineTool, deps) {
228
265
  const grantAgentPermission = defineTool({
229
266
  name: 'grant_agent_permission',
230
267
  description: 'Grant one or more permissions to an agent for the current user. ' +
231
- 'Valid actions: manage:profile, manage:intents, manage:networks, manage:contacts, manage:opportunities, manage:negotiations.',
268
+ 'Valid actions: manage:identity, manage:premises, manage:intents, manage:networks, manage:opportunities, manage:negotiations.',
232
269
  querySchema: z.object({
233
270
  agent_id: z.string().min(1).describe('The agent ID to grant permissions to.'),
234
- actions: z.array(z.string()).min(1).describe('Permission actions to grant. Valid values: manage:profile, manage:intents, manage:networks, manage:contacts, manage:opportunities, manage:negotiations.'),
271
+ actions: z.array(AgentPermissionActionSchema).min(1).describe('Permission actions to grant. Valid values: manage:identity, manage:premises, manage:intents, manage:networks, manage:opportunities, manage:negotiations.'),
235
272
  scope: z.enum(['global', 'node', 'network']).optional().describe('Optional permission scope.'),
236
273
  scope_id: z.string().optional().describe('Scope target ID for node/network scopes.'),
237
274
  }),
@@ -303,6 +340,7 @@ export function createAgentTools(defineTool, deps) {
303
340
  },
304
341
  });
305
342
  return [
343
+ readOwnAgent,
306
344
  registerAgent,
307
345
  listAgents,
308
346
  updateAgent,
@@ -6,9 +6,11 @@
6
6
  *
7
7
  * ## Foreground adapters (participant-directed, authenticated)
8
8
  *
9
- * - {@link createAgentTools} — `register_agent`, `list_agents`, `update_agent`,
10
- * `delete_agent`, `grant_agent_permission`, `revoke_agent_permission` MCP
11
- * tools for the authenticated registration and permission management path.
9
+ * - {@link createAgentTools} — `read_own_agent`, `register_agent`,
10
+ * `list_agents`, `update_agent`, `delete_agent`, `grant_agent_permission`,
11
+ * `revoke_agent_permission` MCP tools for the authenticated registration
12
+ * and permission management path (IND-599: `read_own_agent` is the
13
+ * agent-principal self-read; the rest are human owner/admin actions).
12
14
  *
13
15
  * ## Boundary
14
16
  *
@@ -6,9 +6,11 @@
6
6
  *
7
7
  * ## Foreground adapters (participant-directed, authenticated)
8
8
  *
9
- * - {@link createAgentTools} — `register_agent`, `list_agents`, `update_agent`,
10
- * `delete_agent`, `grant_agent_permission`, `revoke_agent_permission` MCP
11
- * tools for the authenticated registration and permission management path.
9
+ * - {@link createAgentTools} — `read_own_agent`, `register_agent`,
10
+ * `list_agents`, `update_agent`, `delete_agent`, `grant_agent_permission`,
11
+ * `revoke_agent_permission` MCP tools for the authenticated registration
12
+ * and permission management path (IND-599: `read_own_agent` is the
13
+ * agent-principal self-read; the rest are human owner/admin actions).
12
14
  *
13
15
  * ## Boundary
14
16
  *
@@ -16,6 +16,7 @@
16
16
  import { z } from "zod";
17
17
  import { error, success } from "../../shared/agent/tool.helpers.js";
18
18
  import { focusedIntentId, focusedNetworkId, focusedNetworkLabel } from "../../shared/agent/tool.scope.js";
19
+ import { callerMayAccessQuestionMode } from "../../shared/agent/activity-projection.js";
19
20
  /**
20
21
  * Detection modes whose questions derive solely from the caller's own data
21
22
  * (profile gaps, own intents, own discovery sessions). Negotiation-mode
@@ -27,6 +28,23 @@ const SELF_OWNED_MODES = ["enrichment", "intent", "discovery"];
27
28
  function isVisibleInScopedNetwork(question, userId, networkId) {
28
29
  return question.actors?.some((actor) => actor.userId === userId && actor.networkId === networkId) === true;
29
30
  }
31
+ /**
32
+ * Exact affected-domain permission gate for a single question mode.
33
+ *
34
+ * The canonical MCP matrix admits both question tools with a UNION of domain
35
+ * actions (so any question-holding agent can reach the handler), but exact
36
+ * inheritance must be enforced here: a global `manage:intents` agent must not
37
+ * read or answer a `negotiation` question. Reuses the shared
38
+ * `callerMayAccessQuestionMode` mapping so this matches the read_activity_summary
39
+ * projection. A caller context without an `mcpCaller` (REST/chat surfaces) is
40
+ * owner-trusted and passes; MCP humans own their data and also pass.
41
+ */
42
+ function callerMayAccessQuestion(context, mode) {
43
+ const caller = context.mcpCaller;
44
+ if (!caller)
45
+ return true;
46
+ return callerMayAccessQuestionMode(caller, mode);
47
+ }
30
48
  function stripInternalQuestionFields(question) {
31
49
  const { actors: _actors, purpose: _purpose, ...publicQuestion } = question;
32
50
  return publicQuestion;
@@ -80,10 +98,14 @@ export function createQuestionerTools(defineTool, deps) {
80
98
  ...(isIntentScoped ? { scopeType: 'intent', scopeId: scopedIntentId } : {}),
81
99
  limit,
82
100
  });
101
+ // Exact affected-domain permission projection: an agent sees only the
102
+ // question modes backed by one of its permissions (a global intents-only
103
+ // agent never sees negotiation questions). The owning human passes.
104
+ const permitted = fetched.filter((q) => callerMayAccessQuestion(context, q.mode));
83
105
  const visible = isNetworkScoped
84
- ? fetched.filter((q) => SELF_OWNED_MODES.includes(q.mode) &&
106
+ ? permitted.filter((q) => SELF_OWNED_MODES.includes(q.mode) &&
85
107
  isVisibleInScopedNetwork(q, context.userId, scopedNetworkId))
86
- : fetched;
108
+ : permitted;
87
109
  const limited = visible.slice(0, limit).map(stripInternalQuestionFields);
88
110
  if (isIntentScoped) {
89
111
  return success({
@@ -163,6 +185,13 @@ export function createQuestionerTools(defineTool, deps) {
163
185
  if (!target) {
164
186
  return error("Question not found among the client's pending questions — it may already be answered or dismissed. Re-check with read_pending_questions.");
165
187
  }
188
+ // Enforce exact affected-domain inheritance on the resolved target BEFORE
189
+ // any write: the union admission that let this tool run does not grant
190
+ // cross-domain answering (e.g. a manage:intents agent cannot answer a
191
+ // negotiation question). Fail closed with nothing persisted.
192
+ if (!callerMayAccessQuestion(context, target.mode)) {
193
+ return error("You are not authorized to answer this question — it belongs to a domain your agent key does not manage.");
194
+ }
166
195
  const answered = await deps.answerPendingQuestion(context.userId, query.questionId, {
167
196
  selectedOptions,
168
197
  ...(freeText ? { freeText } : {}),
@@ -1,4 +1,5 @@
1
1
  import { type ToolContext, type ResolvedToolContext } from "../../../shared/agent/tool.helpers.js";
2
+ import type { OpportunityOwnerApprovalDeps } from "../../../opportunity/ports/opportunity.tools.port.js";
2
3
  export type { ToolContext, ResolvedToolContext, ProtocolDeps } from "../../../shared/agent/tool.helpers.js";
3
4
  export type { ToolDeps } from "../../../shared/agent/tool.helpers.js";
4
5
  /**
@@ -9,7 +10,7 @@ export type { ToolDeps } from "../../../shared/agent/tool.helpers.js";
9
10
  * All external dependencies (cache, integration, queue, etc.) are provided
10
11
  * via the `deps` parameter — the protocol lib never imports concrete adapters.
11
12
  */
12
- export declare function createChatTools(deps: ToolContext, preResolvedContext?: ResolvedToolContext): Promise<any[]>;
13
+ export declare function createChatTools(deps: ToolContext & OpportunityOwnerApprovalDeps, preResolvedContext?: ResolvedToolContext): Promise<any[]>;
13
14
  /**
14
15
  * Type for the tools array returned by createChatTools.
15
16
  */
@@ -24,6 +24,7 @@ import { createAgentTools } from "../../../capabilities/participant-agents.tools
24
24
  import { createNegotiationTools } from "../../../capabilities/negotiation.facade.js";
25
25
  import { createPremiseTools } from "../../../premise/premise.tools.js";
26
26
  import { createQuestionerTools, createAskUserQuestionTools } from "../../../capabilities/questions.facade.js";
27
+ import { bindOwnerApprovalProvenance } from "../../../opportunity/application/opportunity.owner-provenance.js";
27
28
  const logger = protocolLogger("ChatTools");
28
29
  // ═══════════════════════════════════════════════════════════════════════════════
29
30
  // TOOL FACTORY
@@ -57,6 +58,13 @@ export async function createChatTools(deps, preResolvedContext) {
57
58
  resolvedContext.scopeType = explicitScope.scopeType;
58
59
  resolvedContext.scopeId = explicitScope.scopeId;
59
60
  }
61
+ // This factory is an orchestrated chat surface, never a direct owner request.
62
+ // Bind that fact at composition time rather than inferring it from a session
63
+ // id or a caller-controlled tool argument inside the opportunity handler.
64
+ bindOwnerApprovalProvenance(resolvedContext, {
65
+ surface: 'chat',
66
+ sessionAuthenticated: false,
67
+ });
60
68
  const allowedNetworkIds = deriveAllowedNetworkIds({
61
69
  memberships: resolvedContext.userNetworks,
62
70
  ...(resolvedContext.scopeType && resolvedContext.scopeId
@@ -155,8 +163,7 @@ export async function createChatTools(deps, preResolvedContext) {
155
163
  agentDispatcher: deps.agentDispatcher,
156
164
  stampNewbornOpportunities: deps.stampNewbornOpportunities,
157
165
  deliveryLedger: deps.deliveryLedger,
158
- discoveryRuns: deps.discoveryRuns,
159
- discoveryRunQueue: deps.discoveryRunQueue,
166
+ ...(deps.opportunityOwnerApproval && { opportunityOwnerApproval: deps.opportunityOwnerApproval }),
160
167
  enrichmentRuns: deps.enrichmentRuns,
161
168
  enrichmentRunQueue: deps.enrichmentRunQueue,
162
169
  mintConnectToken: deps.mintConnectToken,
@@ -172,6 +179,7 @@ export async function createChatTools(deps, preResolvedContext) {
172
179
  ...(deps.chatQuestions && { chatQuestions: deps.chatQuestions }),
173
180
  ...(deps.chatSession && { chatSession: deps.chatSession }),
174
181
  ...(deps.getUserContextText && { getUserContextText: deps.getUserContextText }),
182
+ ...(deps.intentProposalStore && { intentProposalStore: deps.intentProposalStore }),
175
183
  graphs: {
176
184
  profile: profileGraph,
177
185
  intent: intentGraph,
@@ -1,11 +1,25 @@
1
1
  import type { ToolDeps, ToolRegistry } from '../../../shared/agent/tool.helpers.js';
2
+ import type { ToolSurface } from '../../../shared/agent/utility.tools.js';
3
+ import type { OpportunityOwnerApprovalDeps } from '../../../opportunity/ports/opportunity.tools.port.js';
4
+ export interface CreateToolRegistryOptions {
5
+ /**
6
+ * Tool-surface profile. The default `'rest'` profile (direct HTTP Tool API)
7
+ * exposes the full tool set — contact/Gmail tools, `scrape_url`, and the
8
+ * deprecated profile/profile-run compatibility aliases. The restricted
9
+ * `'mcp'` profile omits exactly those surfaces (IND-596/597/598); their
10
+ * non-MCP implementations remain intact for REST and chat.
11
+ */
12
+ surface?: ToolSurface;
13
+ }
2
14
  /**
3
15
  * Creates a tool registry containing all tool handlers indexed by name.
4
16
  * Handlers are raw async functions (not LangChain tool() wrappers) that
5
17
  * accept { context, query } and return a JSON string.
6
18
  *
7
19
  * @param deps - Shared tool dependencies (graphs, database, embedder, etc.)
8
- * @param context - Resolved user context for this request.
20
+ * @param options - Surface profile selecting the MCP-restricted or full REST set.
9
21
  * @returns Map of tool name to raw tool definition.
10
22
  */
11
- export declare function createToolRegistry(deps: ToolDeps): ToolRegistry;
23
+ /** Complete registry composition with the opportunity-local owner-proof port. */
24
+ export type ToolRegistryDeps = ToolDeps & OpportunityOwnerApprovalDeps;
25
+ export declare function createToolRegistry(deps: ToolRegistryDeps, options?: CreateToolRegistryOptions): ToolRegistry;