@indexnetwork/protocol 13.2.1-rc.467.1 → 14.0.0-rc.468.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.
@@ -1,4 +1,3 @@
1
- import { focusedIntentId } from "../shared/agent/tool.scope.js";
2
1
  // ═══════════════════════════════════════════════════════════════════════════════
3
2
  // EXTRACTION
4
3
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -7,7 +6,7 @@ import { focusedIntentId } from "../shared/agent/tool.scope.js";
7
6
  *
8
7
  * Scans backwards to find the last HumanMessage, then collects all tool calls
9
8
  * from AIMessages after that point. This ensures multi-iteration tool history
10
- * is available for module resolution within a single user turn.
9
+ * is available for prompt construction within a single user turn.
11
10
  *
12
11
  * @param messages - The current conversation message array
13
12
  * @returns Flattened array of tool name + args from the current agent turn
@@ -39,236 +38,3 @@ export function extractRecentToolCalls(messages) {
39
38
  }
40
39
  return toolCalls;
41
40
  }
42
- // ═══════════════════════════════════════════════════════════════════════════════
43
- // HELPERS
44
- // ═══════════════════════════════════════════════════════════════════════════════
45
- /**
46
- * Checks whether recent tool calls include discover_opportunities with
47
- * introduction-specific arguments (partyUserIds or introTargetUserId).
48
- */
49
- function hasIntroductionArgs(recentTools) {
50
- return recentTools.some((t) => t.name === "discover_opportunities" &&
51
- (t.args.partyUserIds || t.args.introTargetUserId));
52
- }
53
- // ═══════════════════════════════════════════════════════════════════════════════
54
- // MODULE DEFINITIONS
55
- // ═══════════════════════════════════════════════════════════════════════════════
56
- const intentCreationModule = {
57
- id: "intent-creation",
58
- triggers: ["create_intent"],
59
- content: () => `
60
- ### 2. User explicitly wants to create or save an intent
61
-
62
- **YOU decide if it's specific enough. The tool proposes — the user confirms.**
63
-
64
- \`\`\`
65
- IF description is vague ("find a job", "meet people", "learn something"):
66
- 1. read_user_contexts() → get their background
67
- 2. read_intents() → see existing intents for context
68
- 3. THINK: given their profile and existing intents, suggest a refined version
69
- 4. Reply: "Based on your background in X, did you mean something like 'Y'?"
70
- 5. Wait for confirmation
71
- 6. On "yes" → create_intent(description=exact_refined_text)
72
-
73
- IF description is specific enough ("contribute to an open-source LLM project"):
74
- → create_intent(description=...) directly
75
- \`\`\`
76
-
77
- **CRITICAL: Never write a \`\`\`intent_proposal block yourself.** To propose an intent you MUST call create_intent(description=...). The tool returns a \`\`\`intent_proposal code block (with proposalId and description). You MUST include that exact block verbatim in your response — it renders as an interactive card. Do not summarize or invent the block; only the tool provides a valid one. Add a brief explanation that creating this intent will let the system look for relevant people in the background.
78
-
79
- Specificity test: Does it contain a concrete domain, action, or scope? If just a single generic verb+noun ("find a job"), it's vague. If it has qualifying detail ("senior UX design role at a tech company in Berlin"), it's specific.
80
- `,
81
- };
82
- const intentManagementModule = {
83
- id: "intent-management",
84
- triggers: ["update_intent", "delete_intent"],
85
- content: () => `
86
- ### 4. Update or delete an intent
87
-
88
- **YOU look up the ID first.**
89
-
90
- \`\`\`
91
- 1. read_intents() → get current intents with IDs
92
- 2. Match user's request to the right intent
93
- 3. update_intent(intentId=exact_id, description=...) or delete_intent(intentId=exact_id)
94
- \`\`\`
95
- `,
96
- };
97
- const personLookupModule = {
98
- id: "person-lookup",
99
- triggers: ["read_user_contexts"],
100
- content: () => `
101
- ### 0. User asks about a specific person by name
102
-
103
- When the user mentions a specific person by name ("find [name]", "look up [name]", "who is [name]?", "tell me about [name]"), look them up by name first — do NOT use discovery.
104
-
105
- - Call \`read_user_contexts(query="the name")\` — this finds members by name across the user's indexes
106
- - If one match: the result already includes their full profile; present it naturally
107
- - If multiple matches: present the list and ask the user to clarify which person
108
- - If no matches: tell the user you couldn't find anyone by that name in their network
109
- - If the user then asks for semantic discovery (e.g. "find people like them"), use Pattern 1.
110
- - If the user wants to connect with this specific person (e.g. "yes, connect us", "what can I do with them", "I'd like to reach out"), use Pattern 1a.
111
- `,
112
- };
113
- const urlScrapingModule = {
114
- id: "url-scraping",
115
- triggers: ["scrape_url"],
116
- regex: /(https?:\/\/)/i,
117
- content: (ctx) => `
118
- ### 3. User includes a URL
119
-
120
- **YOU handle scraping before ${focusedIntentId(ctx) ? "updating the selected intent" : "intent creation"}.**
121
-
122
- \`\`\`
123
- 1. scrape_url(url, objective="Extract key details for an intent")
124
- 2. Synthesize a conceptual description from scraped content
125
- 3. ${focusedIntentId(ctx) ? `update_intent(intentId="${focusedIntentId(ctx)}", description=synthesized_summary)` : "create_intent(description=synthesized_summary)"}
126
- \`\`\`
127
-
128
- Exception: for profile creation, pass URLs directly to create_user_context (it handles scraping internally).
129
-
130
- If the user pastes or types a profile URL (e.g. linkedin.com/..., github.com/...) to create or update their profile, you MUST pass that exact URL in the corresponding parameter (e.g. linkedinUrl, githubUrl, twitterUrl) to create_user_context, or use scrape_url with that URL then update_user_context; do not use the user's stored social links for that request.
131
- `,
132
- };
133
- const communityModule = {
134
- id: "community",
135
- triggers: ["read_networks", "create_network", "create_network_membership", "update_network", "delete_network", "delete_network_membership"],
136
- content: () => `
137
- ### 8. Explore what a community is about
138
-
139
- \`\`\`
140
- 0. If user asks about communities they belong to, first use preloaded memberships in this prompt.
141
- 1. read_networks() → get network details (title, prompt)
142
- 2. read_intents(networkId=X) → what members are looking for
143
- 3. read_network_memberships(networkId=X) → who's in it
144
- 4. Synthesize: community purpose, active needs, member composition
145
- \`\`\`
146
-
147
- ### When to mention community/index
148
- Index and community membership is background: handle it without talking about indexes unless the user asks or it's sign-up, leave, or owner settings. Do not proactively mention "your indexes", "your communities", "which index", "in your current communities", or similar. Only mention indexes (or communities, lists) when: (i) post-onboarding sign-up to a community, (ii) user explicitly asked about their indexes/communities, (iii) user wants to leave one, (iv) owner is changing index/community settings. Otherwise use neutral language ("where you're connected", "people you're connected with") and do not narrate "your indexes", "your current communities", "in this network", etc.
149
- `,
150
- };
151
- const contactsModule = {
152
- id: "contacts",
153
- triggers: ["import_gmail_contacts", "add_contact", "list_contacts", "remove_contact"],
154
- // Gate on the CONTACTS_ENABLED flag (fail-closed: only `true` enables). When
155
- // contacts are disabled the import/add tools are de-registered, so this module
156
- // must not be injected — otherwise the orchestrator keeps advertising Gmail
157
- // import / add_contact and offers an action that then fails as "Unknown tool".
158
- triggerFilter: (iterCtx) => iterCtx.ctx.contactsEnabled === true,
159
- content: () => `
160
- ### 9. Import contacts from Gmail
161
-
162
- **Single-step workflow:**
163
-
164
- \`\`\`
165
- import_gmail_contacts()
166
- → If not connected: returns { requiresAuth: true, authUrl: "..." } — share the URL with the user
167
- → If connected: imports contacts directly and returns stats { imported, skipped, newContacts, existingContacts }
168
- \`\`\`
169
-
170
- Ghost users are contacts without accounts — they're enriched with public data (LinkedIn, GitHub, X) and can appear in opportunity discovery once enriched.
171
-
172
- ### 10. Add or manage contacts manually
173
-
174
- \`\`\`
175
- # Add a single contact
176
- add_contact(email="alice@example.com", name="Alice Smith")
177
-
178
- # List user's network
179
- list_contacts() → returns contacts with names, emails, and whether they're ghost users
180
-
181
- # Remove a contact
182
- remove_contact(contactId=X)
183
- \`\`\`
184
- `,
185
- };
186
- const sharedContextModule = {
187
- id: "shared-context",
188
- triggers: ["read_network_memberships"],
189
- content: () => `
190
- ### 5. Find shared context between two users
191
-
192
- \`\`\`
193
- 1. read_network_memberships(userId=me) → my networks
194
- 2. read_network_memberships(userId=other) → their networks
195
- 3. Intersect networkIds
196
- 4. For each shared network: read_intents(networkId=shared)
197
- 5. read_user_contexts(userId=other)
198
- 6. Synthesize: what overlaps, where they could collaborate
199
- \`\`\`
200
- `,
201
- };
202
- const mentionsModule = {
203
- id: "mentions",
204
- triggers: [],
205
- regex: /@\[.*?\]\(.*?\)/,
206
- content: () => `- Messages may contain \`@[Display Name](userId)\` markup. The value in parentheses is the userId.
207
- `,
208
- };
209
- // ═══════════════════════════════════════════════════════════════════════════════
210
- // MODULE REGISTRY
211
- // ═══════════════════════════════════════════════════════════════════════════════
212
- /** All registered prompt modules. */
213
- export const PROMPT_MODULES = [
214
- intentCreationModule,
215
- intentManagementModule,
216
- personLookupModule,
217
- urlScrapingModule,
218
- communityModule,
219
- contactsModule,
220
- sharedContextModule,
221
- mentionsModule,
222
- ];
223
- // ═══════════════════════════════════════════════════════════════════════════════
224
- // RESOLUTION
225
- // ═══════════════════════════════════════════════════════════════════════════════
226
- /**
227
- * Resolves which prompt modules should be injected for the current iteration.
228
- *
229
- * Phase 1: Skip all modules when onboarding is active (early exit).
230
- * Phase 2: Collect candidate modules by checking triggers and regex.
231
- * Phase 3: Apply exclusions (unidirectional — the excluding module stays).
232
- *
233
- * @param iterCtx - Current iteration context (tool history, user message, resolved context)
234
- * @returns Concatenated prompt text from all matched modules
235
- */
236
- export function resolveModules(iterCtx) {
237
- // Phase 1 (early exit): Skip all modules during onboarding
238
- if (iterCtx.ctx.isOnboarding) {
239
- return "";
240
- }
241
- const toolNames = new Set(iterCtx.recentTools.map((t) => t.name));
242
- // Phase 2: Collect candidates
243
- const candidates = new Map();
244
- for (const mod of PROMPT_MODULES) {
245
- let matched = false;
246
- // Check tool triggers (with optional filter for arg-based disambiguation)
247
- if (mod.triggers.length > 0 && mod.triggers.some((t) => toolNames.has(t))) {
248
- matched = mod.triggerFilter ? mod.triggerFilter(iterCtx) : true;
249
- }
250
- // Check regex trigger
251
- if (!matched && mod.regex && iterCtx.currentMessage && mod.regex.test(iterCtx.currentMessage)) {
252
- matched = true;
253
- }
254
- if (matched) {
255
- candidates.set(mod.id, mod);
256
- }
257
- }
258
- // Phase 3: Apply exclusions (skip self-exclusion)
259
- for (const mod of candidates.values()) {
260
- if (mod.excludes) {
261
- for (const excludedId of mod.excludes) {
262
- if (excludedId !== mod.id) {
263
- candidates.delete(excludedId);
264
- }
265
- }
266
- }
267
- }
268
- // Build output
269
- const sections = [];
270
- for (const mod of candidates.values()) {
271
- sections.push(mod.content(iterCtx.ctx));
272
- }
273
- return sections.join("\n");
274
- }
@@ -1,6 +1,6 @@
1
1
  import { AIMessage, HumanMessage } from "@langchain/core/messages";
2
2
  import { protocolLogger } from "../shared/observability/protocol.logger.js";
3
- import { createAgentEndEvent, createAgentStartEvent, createDebugMetaEvent, createDecisionQuestionsEvent, createErrorEvent, createGraphEndEvent, createPhaseStartEvent, createPhaseEndEvent, createGraphStartEvent, createIterationStartEvent, createLlmStartEvent, createLlmEndEvent, createResponseCompleteEvent, createResponseResetEvent, createHallucinationDetectedEvent, createStatusEvent, createTokenEvent, createToolActivityEvent, createChatSummarizerStartEvent, createChatSummarizerEndEvent, createQuestionGeneratorStartEvent, createQuestionGeneratorEndEvent, createUserQuestionEvent } from "./chat-streaming.types.js";
3
+ import { createAgentEndEvent, createAgentStartEvent, createDebugMetaEvent, createDecisionQuestionsEvent, createErrorEvent, createGraphEndEvent, createPhaseStartEvent, createPhaseEndEvent, createGraphStartEvent, createIterationStartEvent, createLlmStartEvent, createLlmEndEvent, createResponseCompleteEvent, createResponseResetEvent, createHallucinationDetectedEvent, createStatusEvent, createTokenEvent, createToolActivityEvent, createChatSummarizerStartEvent, createChatSummarizerEndEvent, createUserQuestionEvent } from "./chat-streaming.types.js";
4
4
  const logger = protocolLogger("ChatStreamer");
5
5
  // ══════════════════════════════════════════════════════════════════════════════
6
6
  // CHAT STREAMER (Streaming Narration Architecture)
@@ -176,12 +176,6 @@ export class ChatStreamer {
176
176
  if (event.type === "chat_summarizer_end") {
177
177
  yield createChatSummarizerEndEvent(sessionId, event.payload);
178
178
  }
179
- if (event.type === "question_generator_start") {
180
- yield createQuestionGeneratorStartEvent(sessionId, event.payload);
181
- }
182
- if (event.type === "question_generator_end") {
183
- yield createQuestionGeneratorEndEvent(sessionId, event.payload);
184
- }
185
179
  }
186
180
  // ─────────────────────────────────────────────────────────────────
187
181
  // UPDATES: final state from agentLoopNode
@@ -206,7 +200,7 @@ export class ChatStreamer {
206
200
  if (debugMeta?.graph != null &&
207
201
  typeof debugMeta.iterations === "number") {
208
202
  const llmFallback = { calls: 0, totalDurationMs: 0, resets: [], hallucinations: [] };
209
- yield createDebugMetaEvent(sessionId, debugMeta.graph, debugMeta.iterations, Array.isArray(debugMeta.tools) ? debugMeta.tools : [], debugMeta.llm ?? llmFallback, debugMeta.orchestratorNegotiations, debugMeta.discoveryQuestions);
203
+ yield createDebugMetaEvent(sessionId, debugMeta.graph, debugMeta.iterations, Array.isArray(debugMeta.tools) ? debugMeta.tools : [], debugMeta.llm ?? llmFallback, debugMeta.orchestratorNegotiations);
210
204
  }
211
205
  logger.verbose("Agent loop complete (updates)", {
212
206
  responseLength: responseText.length,
package/dist/index.d.ts CHANGED
@@ -34,7 +34,7 @@ export type { NetworkAssignmentMetadata } from "./shared/schemas/network-assignm
34
34
  export { DEFAULT_NETWORK_ASSIGNMENT_THRESHOLD, resolveAssignmentNetworkScope, buildNetworkAssignmentDecision } from "./shared/assignment/network-assignment.policy.js";
35
35
  export { buildCandidateEvidence } from "./capabilities/opportunities.facade.js";
36
36
  export { ChatGraphFactory } from "./capabilities/participant-agents.facade.js";
37
- export { ORCHESTRATOR_PERSONA_ID, type ChatPersonaConfig } from "./capabilities/participant-agents.facade.js";
37
+ export { type ChatPersonaConfig } from "./capabilities/participant-agents.facade.js";
38
38
  export { NEGOTIATOR_PERSONA_ID, createNegotiatorPersona } from "./capabilities/participant-agents.facade.js";
39
39
  export { SIGNAL_PERSONA_ID, SIGNAL_PERSONA, SIGNAL_NEW_SIGNAL_KICKOFF, SIGNAL_TOOL_NAMES, createSignalTools, filterSignalTools, narrowSignalTools, } from "./capabilities/participant-agents.facade.js";
40
40
  export { REPORTER_PERSONA_ID, REPORTER_PERSONA, REPORTER_BRIEFING_KICKOFF, REPORTER_TOOL_NAMES, createReporterTools, filterReporterTools, narrowReporterTools, } from "./capabilities/participant-agents.facade.js";
package/dist/index.js CHANGED
@@ -30,7 +30,6 @@ export { DEFAULT_NETWORK_ASSIGNMENT_THRESHOLD, resolveAssignmentNetworkScope, bu
30
30
  export { buildCandidateEvidence } from "./capabilities/opportunities.facade.js";
31
31
  // ─── Graph factories ──────────────────────────────────────────────────────────
32
32
  export { ChatGraphFactory } from "./capabilities/participant-agents.facade.js";
33
- export { ORCHESTRATOR_PERSONA_ID } from "./capabilities/participant-agents.facade.js";
34
33
  export { NEGOTIATOR_PERSONA_ID, createNegotiatorPersona } from "./capabilities/participant-agents.facade.js";
35
34
  export { SIGNAL_PERSONA_ID, SIGNAL_PERSONA, SIGNAL_NEW_SIGNAL_KICKOFF, SIGNAL_TOOL_NAMES, createSignalTools, filterSignalTools, narrowSignalTools, } from "./capabilities/participant-agents.facade.js";
36
35
  export { REPORTER_PERSONA_ID, REPORTER_PERSONA, REPORTER_BRIEFING_KICKOFF, REPORTER_TOOL_NAMES, createReporterTools, filterReporterTools, narrowReporterTools, } from "./capabilities/participant-agents.facade.js";
@@ -17,7 +17,7 @@
17
17
  * The following are application-internal or runtime-owned:
18
18
  * - ChatGraphFactory, ChatPersonaConfig, ChatPersona — runtime graph wiring;
19
19
  * consumers should import via capabilities/participant-agents.facade.ts.
20
- * - Persona constants (ORCHESTRATOR_PERSONA_ID, SIGNAL_PERSONA, etc.) — chat
20
+ * - Persona constants (SIGNAL_PERSONA, REPORTER_PERSONA, etc.) — chat
21
21
  * runtime internals; import from capabilities/participant-agents.facade.ts.
22
22
  * - ChatTitleGenerator, ChatSummarizer, etc. — runtime tool composition; import
23
23
  * via capabilities/participant-agents.facade.ts.
@@ -17,7 +17,7 @@
17
17
  * The following are application-internal or runtime-owned:
18
18
  * - ChatGraphFactory, ChatPersonaConfig, ChatPersona — runtime graph wiring;
19
19
  * consumers should import via capabilities/participant-agents.facade.ts.
20
- * - Persona constants (ORCHESTRATOR_PERSONA_ID, SIGNAL_PERSONA, etc.) — chat
20
+ * - Persona constants (SIGNAL_PERSONA, REPORTER_PERSONA, etc.) — chat
21
21
  * runtime internals; import from capabilities/participant-agents.facade.ts.
22
22
  * - ChatTitleGenerator, ChatSummarizer, etc. — runtime tool composition; import
23
23
  * via capabilities/participant-agents.facade.ts.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/protocol",
3
- "version": "13.2.1-rc.467.1",
3
+ "version": "14.0.0-rc.468.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",