@indexnetwork/protocol 8.6.1-rc.429.1 → 8.8.0-rc.431.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.
@@ -15,5 +15,5 @@ export type { IntentIndexerOutput } from "../signals/application/intent.indexer.
15
15
  export { createIntentTools } from "../signals/application/intent.tools.js";
16
16
  export type { IntentToolDeps } from "./signals.tools.port.js";
17
17
  export { SignalIntakePackGenerator, normalizeIntakePack, type IntakePack, type IntakePackInput, type IntakePackQuestion, type IntakePackQuestionOption, } from "../signals/application/intake.pack.generator.js";
18
- export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, type IntakeAnswer, type SynthesisInput, type SynthesisResult, } from "../signals/application/intake.orchestrator.js";
18
+ export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, type IntakeAnswer, type IntakeRound, type FollowUpPlan, type FollowUpPlanInput, type SynthesisInput, type SynthesisResult, } from "../signals/application/intake.orchestrator.js";
19
19
  export { normalizeIntentDescription } from "../signals/domain/intent.proposal.js";
package/dist/index.d.ts CHANGED
@@ -69,7 +69,7 @@ export type { IntentIndexerOutput } from "./capabilities/signals.facade.js";
69
69
  export { SignalIntakePackGenerator, normalizeIntakePack } from "./capabilities/signals.facade.js";
70
70
  export type { IntakePack, IntakePackInput, IntakePackQuestion, IntakePackQuestionOption, } from "./capabilities/signals.facade.js";
71
71
  export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, } from "./capabilities/signals.facade.js";
72
- export type { IntakeAnswer, SynthesisInput, SynthesisResult, } from "./capabilities/signals.facade.js";
72
+ export type { IntakeAnswer, IntakeRound, FollowUpPlan, FollowUpPlanInput, SynthesisInput, SynthesisResult, } from "./capabilities/signals.facade.js";
73
73
  export { normalizeIntentDescription } from "./capabilities/signals.facade.js";
74
74
  export { LensInferrer } from "./capabilities/participant-context.facade.js";
75
75
  export { NegotiationInsightsGenerator } from "./capabilities/negotiation.facade.js";
@@ -18,4 +18,4 @@ export { IntentReconciler, type IntentReconcilerOutput, type NormalizedIntentAct
18
18
  export { IntentIndexer, IntentIndexerOutputSchema, type IntentIndexerOutput } from "./intent.indexer.js";
19
19
  export { createIntentTools, describeIntentUpdateFailure } from "./intent.tools.js";
20
20
  export { SignalIntakePackGenerator, normalizeIntakePack, type IntakePack, type IntakePackInput, type IntakePackQuestion, type IntakePackQuestionOption, } from "./intake.pack.generator.js";
21
- export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, type IntakeAnswer, type SynthesisInput, type SynthesisResult, } from "./intake.orchestrator.js";
21
+ export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, type IntakeAnswer, type IntakeRound, type FollowUpPlan, type FollowUpPlanInput, type SynthesisInput, type SynthesisResult, } from "./intake.orchestrator.js";
@@ -8,18 +8,23 @@
8
8
  */
9
9
  import type { BaseLanguageModelInput } from "@langchain/core/language_models/base";
10
10
  import type { Runnable } from "@langchain/core/runnables";
11
+ import { z } from "zod";
11
12
  import { type IntakePackQuestion } from "./intake.pack.generator.js";
12
13
  /** One answered intake round. */
13
14
  export interface IntakeAnswer {
14
15
  selectedOptions: string[];
15
16
  freeText?: string;
16
17
  }
18
+ /** One answered intake round, in order (round 1 first). */
19
+ export interface IntakeRound {
20
+ prompt: string;
21
+ answer: IntakeAnswer;
22
+ }
17
23
  /** Everything synthesis needs to write the signal. */
18
24
  export interface SynthesisInput {
19
25
  brief: string;
20
- whoAnswer: IntakeAnswer;
21
- bringAnswer: IntakeAnswer;
22
- /** Free-text place/community constraint from round 3. */
26
+ rounds: IntakeRound[];
27
+ /** Free-text place/community constraint from the where round. */
23
28
  whereText?: string;
24
29
  /**
25
30
  * Free-text correction the user typed against a draft they already saw.
@@ -28,12 +33,83 @@ export interface SynthesisInput {
28
33
  */
29
34
  feedback?: string;
30
35
  }
36
+ /** Planning input for one follow-up generation call. */
37
+ export interface FollowUpPlanInput {
38
+ brief: string;
39
+ /** Answered rounds in order (round 1 first). */
40
+ rounds: IntakeRound[];
41
+ /** Hard cap on questions returned by THIS call. */
42
+ maxFollowUps: number;
43
+ /** Locked interview plan on continuation calls; echoed unchanged. */
44
+ plannedFollowUpCount?: number;
45
+ }
46
+ /** The model's follow-up batch plus its total follow-up plan. */
47
+ export interface FollowUpPlan {
48
+ questions: IntakePackQuestion[];
49
+ plannedFollowUpCount: number;
50
+ }
31
51
  /** Synthesized signal text plus its summary fields. */
32
52
  export interface SynthesisResult {
33
53
  description: string;
34
54
  lookingFor: string;
35
55
  youBring: string;
36
56
  }
57
+ declare const followUpPlanSchema: z.ZodObject<{
58
+ questions: z.ZodArray<z.ZodObject<{
59
+ title: z.ZodString;
60
+ prompt: z.ZodString;
61
+ options: z.ZodArray<z.ZodObject<{
62
+ label: z.ZodString;
63
+ description: z.ZodString;
64
+ }, "strip", z.ZodTypeAny, {
65
+ label: string;
66
+ description: string;
67
+ }, {
68
+ label: string;
69
+ description: string;
70
+ }>, "many">;
71
+ multiSelect: z.ZodBoolean;
72
+ }, "strip", z.ZodTypeAny, {
73
+ prompt: string;
74
+ options: {
75
+ label: string;
76
+ description: string;
77
+ }[];
78
+ title: string;
79
+ multiSelect: boolean;
80
+ }, {
81
+ prompt: string;
82
+ options: {
83
+ label: string;
84
+ description: string;
85
+ }[];
86
+ title: string;
87
+ multiSelect: boolean;
88
+ }>, "many">;
89
+ plannedFollowUpCount: z.ZodNumber;
90
+ }, "strip", z.ZodTypeAny, {
91
+ questions: {
92
+ prompt: string;
93
+ options: {
94
+ label: string;
95
+ description: string;
96
+ }[];
97
+ title: string;
98
+ multiSelect: boolean;
99
+ }[];
100
+ plannedFollowUpCount: number;
101
+ }, {
102
+ questions: {
103
+ prompt: string;
104
+ options: {
105
+ label: string;
106
+ description: string;
107
+ }[];
108
+ title: string;
109
+ multiSelect: boolean;
110
+ }[];
111
+ plannedFollowUpCount: number;
112
+ }>;
37
113
  /** Static round-1 question used only when pack generation fails outright. */
38
114
  export declare const FALLBACK_WHO_QUESTION: IntakePackQuestion;
39
115
  /** Static round-2 question used when live generation fails. */
@@ -45,34 +121,33 @@ export declare const FALLBACK_BRING_QUESTION: IntakePackQuestion;
45
121
  * @returns Comma-joined non-empty parts
46
122
  */
47
123
  export declare function answerLabel(answer: IntakeAnswer): string;
48
- /** Runs the two live stages of the fast intake funnel. */
124
+ /** Runs the two live stages of the fast intake funnel: follow-up planning and synthesis. */
49
125
  export declare class SignalIntakeOrchestrator {
50
- private readonly questionModel;
126
+ private readonly plannerModel;
51
127
  private readonly synthesisModel;
52
128
  /**
53
129
  * @param models - Optional injected structured models. Tests pass stubs.
54
130
  */
55
131
  constructor(models?: {
56
- question?: Runnable<BaseLanguageModelInput, IntakePackQuestion>;
132
+ planner?: Runnable<BaseLanguageModelInput, z.infer<typeof followUpPlanSchema>>;
57
133
  synthesis?: Runnable<BaseLanguageModelInput, SynthesisResult>;
58
134
  });
59
135
  /**
60
- * Generate round 2 from the brief and the round-1 answer.
136
+ * Plan and write follow-up questions from the brief and answered rounds.
61
137
  *
62
- * @param input - Brief plus the answered round-1 question
63
- * @returns A renderable question; the static fallback when generation fails
138
+ * @param input - Brief, answered rounds, per-call cap, and any locked plan
139
+ * @returns Up to `maxFollowUps` renderable questions plus the total plan;
140
+ * the static fallback question with count 1 when generation fails
64
141
  */
65
- nextQuestion(input: {
66
- brief: string;
67
- whoAnswer: IntakeAnswer;
68
- }): Promise<IntakePackQuestion>;
142
+ generateFollowUps(input: FollowUpPlanInput): Promise<FollowUpPlan>;
69
143
  /**
70
- * Write the signal from both answers, any where-constraint, and any
144
+ * Write the signal from every answered round, any where-constraint, and any
71
145
  * revision feedback.
72
146
  *
73
- * @param input - Brief, both answers, optional where constraint, optional feedback
147
+ * @param input - Brief, ordered rounds, optional where constraint, optional feedback
74
148
  * @returns Description plus card summary fields
75
149
  * @throws Propagates model failure so the caller can mark the run failed
76
150
  */
77
151
  synthesize(input: SynthesisInput): Promise<SynthesisResult>;
78
152
  }
153
+ export {};
@@ -16,6 +16,10 @@ const questionSchema = z.object({
16
16
  options: z.array(z.object({ label: z.string().min(1), description: z.string() })),
17
17
  multiSelect: z.boolean(),
18
18
  });
19
+ const followUpPlanSchema = z.object({
20
+ questions: z.array(questionSchema),
21
+ plannedFollowUpCount: z.number().int().min(0),
22
+ });
19
23
  const synthesisSchema = z.object({
20
24
  description: z.string().min(1),
21
25
  lookingFor: z.string().min(1),
@@ -45,13 +49,18 @@ export const FALLBACK_BRING_QUESTION = {
45
49
  ],
46
50
  multiSelect: false,
47
51
  };
48
- const QUESTION_SYSTEM_PROMPT = `You write one intake question for a networking product.
52
+ const PLAN_SYSTEM_PROMPT = `You plan and write follow-up intake questions for a networking product.
49
53
 
50
- Given a brief about the person and who they said they want to meet, ask what they
51
- would bring to that connection and what gap the other side should fill. Give 3-4
52
- concrete options grounded in the brief, each with a short label and a one-line
53
- description. Include an option for mutual exchange when both sides matter. Set
54
- multiSelect false. Never expose raw JSON, IDs, or internal vocabulary.`;
54
+ Given a brief about the person and the intake rounds they already answered, decide
55
+ how many further questions (up to the stated maximum) would make their signal
56
+ specific enough to match on, and write them. Each question is one concise prompt
57
+ with 3-4 concrete options grounded in the brief and the previous answers; each
58
+ option has a short label and a one-line description. Set multiSelect true only
59
+ when several options can genuinely apply together. Never re-ask a dimension that
60
+ is already answered; skip a dimension the brief already covers. Never expose raw
61
+ JSON, IDs, or internal vocabulary. plannedFollowUpCount is the TOTAL number of
62
+ follow-up questions the interview should contain, including any returned now;
63
+ when the input already fixes it, echo that value unchanged.`;
55
64
  const SYNTHESIS_SYSTEM_PROMPT = `You write one clear signal for a networking product.
56
65
 
57
66
  Combine the brief and the person's answers into a specific description of who they
@@ -70,44 +79,74 @@ community constraint. Never invent facts beyond the brief and the answers.`;
70
79
  export function answerLabel(answer) {
71
80
  return [...answer.selectedOptions, answer.freeText?.trim() ?? ""].filter(Boolean).join(", ");
72
81
  }
73
- /** Runs the two live stages of the fast intake funnel. */
82
+ /** Runs the two live stages of the fast intake funnel: follow-up planning and synthesis. */
74
83
  export class SignalIntakeOrchestrator {
75
84
  /**
76
85
  * @param models - Optional injected structured models. Tests pass stubs.
77
86
  */
78
87
  constructor(models) {
79
- this.questionModel = models?.question
80
- ?? createStructuredModel("signalIntakePack", questionSchema);
88
+ this.plannerModel = models?.planner
89
+ ?? createStructuredModel("signalIntakePack", followUpPlanSchema);
81
90
  this.synthesisModel = models?.synthesis
82
91
  ?? createStructuredModel("signalIntakePack", synthesisSchema);
83
92
  }
84
93
  /**
85
- * Generate round 2 from the brief and the round-1 answer.
94
+ * Plan and write follow-up questions from the brief and answered rounds.
86
95
  *
87
- * @param input - Brief plus the answered round-1 question
88
- * @returns A renderable question; the static fallback when generation fails
96
+ * @param input - Brief, answered rounds, per-call cap, and any locked plan
97
+ * @returns Up to `maxFollowUps` renderable questions plus the total plan;
98
+ * the static fallback question with count 1 when generation fails
89
99
  */
90
- async nextQuestion(input) {
100
+ async generateFollowUps(input) {
101
+ const roundsText = input.rounds
102
+ .map((round, index) => `Round ${index + 1} — Q: ${round.prompt}\nA: ${answerLabel(round.answer)}`)
103
+ .join("\n\n");
104
+ const lockedLine = input.plannedFollowUpCount !== undefined
105
+ ? `\n\nThe interview plan is fixed at ${input.plannedFollowUpCount} follow-up question(s) in total; ${input.rounds.length - 1} already asked. Echo that count unchanged.`
106
+ : "";
91
107
  try {
92
- const raw = await this.questionModel.invoke([
93
- new SystemMessage(QUESTION_SYSTEM_PROMPT),
94
- new HumanMessage(`Brief:\n${input.brief}\n\nThey want to meet: ${answerLabel(input.whoAnswer)}\n\nWrite the question.`),
108
+ const raw = await this.plannerModel.invoke([
109
+ new SystemMessage(PLAN_SYSTEM_PROMPT),
110
+ new HumanMessage(`Brief:\n${input.brief}\n\n${roundsText}\n\nWrite up to ${input.maxFollowUps} follow-up question(s).${lockedLine}`),
95
111
  ]);
96
- return normalizeIntakePack({ brief: input.brief, question: raw }).question;
112
+ const questions = raw.questions
113
+ .slice(0, input.maxFollowUps)
114
+ .map((q) => normalizeIntakePack({ brief: input.brief, question: q }).question);
115
+ // Backstop: while budget remains, a successful empty plan must not
116
+ // silently shrink the interview; serve the static fallback instead.
117
+ if (questions.length === 0 && input.maxFollowUps > 0) {
118
+ return {
119
+ questions: [FALLBACK_BRING_QUESTION],
120
+ plannedFollowUpCount: input.plannedFollowUpCount ?? 1,
121
+ };
122
+ }
123
+ return {
124
+ questions,
125
+ plannedFollowUpCount: input.plannedFollowUpCount
126
+ ?? Math.max(raw.plannedFollowUpCount, questions.length),
127
+ };
97
128
  }
98
129
  catch {
99
- return FALLBACK_BRING_QUESTION;
130
+ if (input.maxFollowUps <= 0)
131
+ return { questions: [], plannedFollowUpCount: 0 };
132
+ return {
133
+ questions: [FALLBACK_BRING_QUESTION],
134
+ plannedFollowUpCount: input.plannedFollowUpCount ?? 1,
135
+ };
100
136
  }
101
137
  }
102
138
  /**
103
- * Write the signal from both answers, any where-constraint, and any
139
+ * Write the signal from every answered round, any where-constraint, and any
104
140
  * revision feedback.
105
141
  *
106
- * @param input - Brief, both answers, optional where constraint, optional feedback
142
+ * @param input - Brief, ordered rounds, optional where constraint, optional feedback
107
143
  * @returns Description plus card summary fields
108
144
  * @throws Propagates model failure so the caller can mark the run failed
109
145
  */
110
146
  async synthesize(input) {
147
+ const roundsText = input.rounds
148
+ .map((round) => `Q: ${round.prompt}\nA: ${answerLabel(round.answer)}`)
149
+ .join("\n\n");
111
150
  const whereLine = input.whereText?.trim()
112
151
  ? `\n\nWhere constraint: ${input.whereText.trim()}`
113
152
  : "";
@@ -116,7 +155,7 @@ export class SignalIntakeOrchestrator {
116
155
  : "";
117
156
  const result = await this.synthesisModel.invoke([
118
157
  new SystemMessage(SYNTHESIS_SYSTEM_PROMPT),
119
- new HumanMessage(`Brief:\n${input.brief}\n\nThey want to meet: ${answerLabel(input.whoAnswer)}\n\nThey bring: ${answerLabel(input.bringAnswer)}${whereLine}${feedbackLine}\n\nWrite the signal.`),
158
+ new HumanMessage(`Brief:\n${input.brief}\n\n${roundsText}${whereLine}${feedbackLine}\n\nWrite the signal.`),
120
159
  ]);
121
160
  return {
122
161
  description: result.description.trim(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/protocol",
3
- "version": "8.6.1-rc.429.1",
3
+ "version": "8.8.0-rc.431.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",