@indexnetwork/protocol 8.0.3-rc.420.1 → 8.1.1-rc.422.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.
package/CHANGELOG.md CHANGED
@@ -10,8 +10,45 @@ See [STABILITY.md](./STABILITY.md) for the public-contract and tier definitions.
10
10
  > itemized. From `2.0.0` onward, keep this file updated as part of every release
11
11
  > (bump `package.json` and the `[Unreleased]` section before promoting to `main`).
12
12
 
13
+ ## Release model
14
+
15
+ Every push to `dev` publishes `<package.json version>-rc.<run>.<attempt>` under
16
+ the npm `rc` tag; `latest` moves only when `main` is promoted, and only if that
17
+ exact version is not already on npm. **Stable releases are therefore sparse and
18
+ skip versions on purpose** — most versions only ever exist as an `rc`. `latest`
19
+ went 6.7.1 → 8.0.2 with no 7.x in between because the whole 7.x line shipped as
20
+ prereleases between the two promotions. To track every change, read `rc`; to
21
+ pin a supported release, use `latest`.
22
+
13
23
  ## [Unreleased]
14
24
 
25
+ ### Added
26
+ - Deterministic fast signal intake (#1307; 8.1.0). `SignalIntakePackGenerator`
27
+ precomputes a per-user intake brief plus round-1 question, and
28
+ `SignalIntakeOrchestrator` drives the funnel as a deterministic state machine
29
+ on flash instead of sequential pro turns, with synthesis speculated during a
30
+ deterministic community picker. New stable exports from the `signals` facade:
31
+ `SignalIntakePackGenerator`, `normalizeIntakePack`, `SignalIntakeOrchestrator`,
32
+ `answerLabel`, `FALLBACK_WHO_QUESTION`, `FALLBACK_BRING_QUESTION`, and the
33
+ `IntakePack` / `IntakePackInput` / `IntakePackQuestion` /
34
+ `IntakePackQuestionOption` / `IntakeAnswer` / `SynthesisInput` /
35
+ `SynthesisResult` types. Minor bump: additive surface only.
36
+
37
+ ### Fixed
38
+ - `architecture:cycles` graphs runtime edges only (8.0.3). It counted `import
39
+ type` / `export type` edges, which TypeScript erases, so it reported a
40
+ 7-module negotiation/questions cycle that no runtime can observe — penalizing
41
+ the capability-facade pattern of depending on a port *type* instead of an
42
+ implementation. Tooling only; no source or public-surface change. The full
43
+ `architecture:check` suite now passes and runs in CI.
44
+
45
+ ## [8.0.2] — 2026-07-30
46
+
47
+ Promoted to npm `latest` on 2026-07-30, carrying the whole 7.6.0 → 8.0.2 line.
48
+ Those intermediate versions were published as `-rc` prereleases from `dev`
49
+ only, so `latest` moved 6.7.1 → 8.0.2 in one step; see **Release model**
50
+ above. Entries below keep the version they were developed under.
51
+
15
52
  ### Added
16
53
  - Configurable negotiator stance `NEGOTIATOR_STANCE` (IND-611; 7.11.0), shipped
17
54
  dark. `advocate` (default) | `evaluator` | `skeptic`, resolved by the new
@@ -39,12 +76,6 @@ See [STABILITY.md](./STABILITY.md) for the public-contract and tier definitions.
39
76
  with that change; this entry and the regenerated export inventory record it.
40
77
 
41
78
  ### Fixed
42
- - `architecture:cycles` graphs runtime edges only (8.0.3). It counted `import
43
- type` / `export type` edges, which TypeScript erases, so it reported a
44
- 7-module negotiation/questions cycle that no runtime can observe — penalizing
45
- the capability-facade pattern of depending on a port *type* instead of an
46
- implementation. Tooling only; no source or public-surface change. The full
47
- `architecture:check` suite now passes and runs in CI.
48
79
  - Stop force-rewriting an opening-move refusal (IND-611 prerequisite; 7.11.0):
49
80
  `negotiation.graph.ts` ran the turn-0 opening force *before* the IND-564
50
81
  opening-withdraw guard, so a v2 initiator that judged a match not worth making
@@ -710,7 +741,16 @@ the matching/opportunity/premise eval harnesses, premise source tracking and
710
741
  cascade retraction, network-scoped agents, and the agent registry. Reconstructed
711
742
  from git history; not itemized.
712
743
 
713
- [Unreleased]: https://github.com/indexnetwork/protocol/compare/v4.3.0...HEAD
744
+ <!--
745
+ Release tags stopped being created when publishing moved to the automated
746
+ subtree workflow: only v0.2.1 and v0.3.0 still exist in indexnetwork/protocol,
747
+ so every `compare/vX.Y.Z` link below 404s. They are kept for historical intent.
748
+ New entries link to the npm release instead, and [Unreleased] compares the
749
+ branches that actually define it.
750
+ -->
751
+
752
+ [Unreleased]: https://github.com/indexnetwork/protocol/compare/main...dev
753
+ [8.0.2]: https://www.npmjs.com/package/@indexnetwork/protocol/v/8.0.2
714
754
  [4.3.0]: https://github.com/indexnetwork/protocol/compare/v4.2.0...v4.3.0
715
755
  [4.2.0]: https://github.com/indexnetwork/protocol/compare/v4.1.0...v4.2.0
716
756
  [4.1.0]: https://github.com/indexnetwork/protocol/compare/v4.0.0...v4.1.0
@@ -14,3 +14,6 @@ export { IntentIndexer } from "../signals/application/intent.indexer.js";
14
14
  export type { IntentIndexerOutput } from "../signals/application/intent.indexer.js";
15
15
  export { createIntentTools } from "../signals/application/intent.tools.js";
16
16
  export type { IntentToolDeps } from "./signals.tools.port.js";
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";
19
+ export { normalizeIntentDescription } from "../signals/domain/intent.proposal.js";
@@ -12,3 +12,6 @@ export { IntentGraphFactory } from "../signals/application/intent.graph.js";
12
12
  export { SemanticVerifier } from "../signals/application/intent.verifier.js";
13
13
  export { IntentIndexer } from "../signals/application/intent.indexer.js";
14
14
  export { createIntentTools } from "../signals/application/intent.tools.js";
15
+ export { SignalIntakePackGenerator, normalizeIntakePack, } from "../signals/application/intake.pack.generator.js";
16
+ export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, } from "../signals/application/intake.orchestrator.js";
17
+ export { normalizeIntentDescription } from "../signals/domain/intent.proposal.js";
package/dist/index.d.ts CHANGED
@@ -66,6 +66,11 @@ export { SuggestionGenerator } from "./capabilities/participant-agents.facade.js
66
66
  export { generateInviteMessage } from "./capabilities/contacts.facade.js";
67
67
  export { IntentIndexer } from "./capabilities/signals.facade.js";
68
68
  export type { IntentIndexerOutput } from "./capabilities/signals.facade.js";
69
+ export { SignalIntakePackGenerator, normalizeIntakePack } from "./capabilities/signals.facade.js";
70
+ export type { IntakePack, IntakePackInput, IntakePackQuestion, IntakePackQuestionOption, } from "./capabilities/signals.facade.js";
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";
73
+ export { normalizeIntentDescription } from "./capabilities/signals.facade.js";
69
74
  export { LensInferrer } from "./capabilities/participant-context.facade.js";
70
75
  export { NegotiationInsightsGenerator } from "./capabilities/negotiation.facade.js";
71
76
  export type { NegotiationDigest } from "./capabilities/negotiation.facade.js";
package/dist/index.js CHANGED
@@ -58,6 +58,9 @@ export { HydeGenerator } from "./capabilities/participant-context.facade.js";
58
58
  export { SuggestionGenerator } from "./capabilities/participant-agents.facade.js";
59
59
  export { generateInviteMessage } from "./capabilities/contacts.facade.js";
60
60
  export { IntentIndexer } from "./capabilities/signals.facade.js";
61
+ export { SignalIntakePackGenerator, normalizeIntakePack } from "./capabilities/signals.facade.js";
62
+ export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, } from "./capabilities/signals.facade.js";
63
+ export { normalizeIntentDescription } from "./capabilities/signals.facade.js";
61
64
  export { LensInferrer } from "./capabilities/participant-context.facade.js";
62
65
  export { NegotiationInsightsGenerator } from "./capabilities/negotiation.facade.js";
63
66
  export { IndexNegotiator } from "./capabilities/negotiation.facade.js";
@@ -108,6 +108,11 @@ declare function getModelConfig(config?: ModelConfig): {
108
108
  readonly temperature: 0.5;
109
109
  readonly maxTokens: 1024;
110
110
  };
111
+ readonly signalIntakePack: {
112
+ readonly model: "google/gemini-2.5-flash";
113
+ readonly temperature: 0.3;
114
+ readonly maxTokens: 1024;
115
+ };
111
116
  readonly negotiationSummarizer: {
112
117
  readonly model: "google/gemini-2.5-flash";
113
118
  readonly temperature: 0.2;
@@ -21,6 +21,7 @@ function getModelConfig(config) {
21
21
  negotiationInsights: { model: "google/gemini-2.5-flash", temperature: 0.4, maxTokens: 512 },
22
22
  chatContextSummarizer: { model: "google/gemini-2.5-flash", temperature: 0.2, maxTokens: 512 },
23
23
  questioner: { model: "google/gemini-2.5-flash", temperature: 0.5, maxTokens: 1024 },
24
+ signalIntakePack: { model: "google/gemini-2.5-flash", temperature: 0.3, maxTokens: 1024 },
24
25
  negotiationSummarizer: { model: "google/gemini-2.5-flash", temperature: 0.2, maxTokens: 256 },
25
26
  poolDiscriminatorMiner: { model: "google/gemini-2.5-flash", temperature: 0.2, maxTokens: 4096 },
26
27
  poolDiscriminatorAssigner: { model: "google/gemini-2.5-flash", temperature: 0.1, maxTokens: 16384 },
@@ -17,3 +17,5 @@ export { SemanticVerifier, type SemanticVerifierOutput } from "./intent.verifier
17
17
  export { IntentReconciler, type IntentReconcilerOutput, type NormalizedIntentAction } from "./intent.reconciler.js";
18
18
  export { IntentIndexer, IntentIndexerOutputSchema, type IntentIndexerOutput } from "./intent.indexer.js";
19
19
  export { createIntentTools, describeIntentUpdateFailure } from "./intent.tools.js";
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";
@@ -20,3 +20,7 @@ export { IntentReconciler } from "./intent.reconciler.js";
20
20
  export { IntentIndexer, IntentIndexerOutputSchema } from "./intent.indexer.js";
21
21
  // ── Tool factory (signals layer) ──────────────────────────────────────────────
22
22
  export { createIntentTools, describeIntentUpdateFailure } from "./intent.tools.js";
23
+ // ── Fast-intake pack ──────────────────────────────────────────────────────────
24
+ export { SignalIntakePackGenerator, normalizeIntakePack, } from "./intake.pack.generator.js";
25
+ // ── Fast-intake orchestrator ──────────────────────────────────────────────────
26
+ export { SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, } from "./intake.orchestrator.js";
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Deterministic signal-intake stage logic.
3
+ *
4
+ * The fast intake path is a fixed funnel, so stages are driven here rather than
5
+ * by an agent loop: round 1 comes from the precomputed pack, round 2 is one
6
+ * structured call, round 3 is a deterministic client-side picker, and synthesis
7
+ * is one structured call. This module owns no I/O.
8
+ */
9
+ import type { BaseLanguageModelInput } from "@langchain/core/language_models/base";
10
+ import type { Runnable } from "@langchain/core/runnables";
11
+ import { type IntakePackQuestion } from "./intake.pack.generator.js";
12
+ /** One answered intake round. */
13
+ export interface IntakeAnswer {
14
+ selectedOptions: string[];
15
+ freeText?: string;
16
+ }
17
+ /** Everything synthesis needs to write the signal. */
18
+ export interface SynthesisInput {
19
+ brief: string;
20
+ whoAnswer: IntakeAnswer;
21
+ bringAnswer: IntakeAnswer;
22
+ /** Free-text place/community constraint from round 3. */
23
+ whereText?: string;
24
+ /**
25
+ * Free-text correction the user typed against a draft they already saw.
26
+ * Distinct from {@link SynthesisInput.whereText}: feedback rewrites the
27
+ * signal, it does not constrain where to look.
28
+ */
29
+ feedback?: string;
30
+ }
31
+ /** Synthesized signal text plus its summary fields. */
32
+ export interface SynthesisResult {
33
+ description: string;
34
+ lookingFor: string;
35
+ youBring: string;
36
+ }
37
+ /** Static round-1 question used only when pack generation fails outright. */
38
+ export declare const FALLBACK_WHO_QUESTION: IntakePackQuestion;
39
+ /** Static round-2 question used when live generation fails. */
40
+ export declare const FALLBACK_BRING_QUESTION: IntakePackQuestion;
41
+ /**
42
+ * Render an answer as a human-readable label.
43
+ *
44
+ * @param answer - Selected options plus optional free text
45
+ * @returns Comma-joined non-empty parts
46
+ */
47
+ export declare function answerLabel(answer: IntakeAnswer): string;
48
+ /** Runs the two live stages of the fast intake funnel. */
49
+ export declare class SignalIntakeOrchestrator {
50
+ private readonly questionModel;
51
+ private readonly synthesisModel;
52
+ /**
53
+ * @param models - Optional injected structured models. Tests pass stubs.
54
+ */
55
+ constructor(models?: {
56
+ question?: Runnable<BaseLanguageModelInput, IntakePackQuestion>;
57
+ synthesis?: Runnable<BaseLanguageModelInput, SynthesisResult>;
58
+ });
59
+ /**
60
+ * Generate round 2 from the brief and the round-1 answer.
61
+ *
62
+ * @param input - Brief plus the answered round-1 question
63
+ * @returns A renderable question; the static fallback when generation fails
64
+ */
65
+ nextQuestion(input: {
66
+ brief: string;
67
+ whoAnswer: IntakeAnswer;
68
+ }): Promise<IntakePackQuestion>;
69
+ /**
70
+ * Write the signal from both answers, any where-constraint, and any
71
+ * revision feedback.
72
+ *
73
+ * @param input - Brief, both answers, optional where constraint, optional feedback
74
+ * @returns Description plus card summary fields
75
+ * @throws Propagates model failure so the caller can mark the run failed
76
+ */
77
+ synthesize(input: SynthesisInput): Promise<SynthesisResult>;
78
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Deterministic signal-intake stage logic.
3
+ *
4
+ * The fast intake path is a fixed funnel, so stages are driven here rather than
5
+ * by an agent loop: round 1 comes from the precomputed pack, round 2 is one
6
+ * structured call, round 3 is a deterministic client-side picker, and synthesis
7
+ * is one structured call. This module owns no I/O.
8
+ */
9
+ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
10
+ import { z } from "zod";
11
+ import { createStructuredModel } from "../../shared/agent/model.config.js";
12
+ import { normalizeIntakePack } from "./intake.pack.generator.js";
13
+ const questionSchema = z.object({
14
+ title: z.string(),
15
+ prompt: z.string().min(1),
16
+ options: z.array(z.object({ label: z.string().min(1), description: z.string() })),
17
+ multiSelect: z.boolean(),
18
+ });
19
+ const synthesisSchema = z.object({
20
+ description: z.string().min(1),
21
+ lookingFor: z.string().min(1),
22
+ youBring: z.string().min(1),
23
+ });
24
+ /** Static round-1 question used only when pack generation fails outright. */
25
+ export const FALLBACK_WHO_QUESTION = {
26
+ title: "Question 1",
27
+ prompt: "Who do you want to meet right now?",
28
+ options: [
29
+ { label: "A collaborator", description: "Someone to build or work on something with" },
30
+ { label: "A customer or user", description: "Someone who has the problem you solve" },
31
+ { label: "An expert", description: "Someone who has done this before" },
32
+ { label: "A peer", description: "Someone at a similar stage to compare notes with" },
33
+ ],
34
+ multiSelect: false,
35
+ };
36
+ /** Static round-2 question used when live generation fails. */
37
+ export const FALLBACK_BRING_QUESTION = {
38
+ title: "Question 2",
39
+ prompt: "What would you bring, and what gap should they fill?",
40
+ options: [
41
+ { label: "Hands-on expertise", description: "You can do the work with them" },
42
+ { label: "Introductions and reach", description: "You can open doors for them" },
43
+ { label: "Funding or resources", description: "You can back what they are doing" },
44
+ { label: "A mutual exchange", description: "You each cover the other's gap" },
45
+ ],
46
+ multiSelect: false,
47
+ };
48
+ const QUESTION_SYSTEM_PROMPT = `You write one intake question for a networking product.
49
+
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.`;
55
+ const SYNTHESIS_SYSTEM_PROMPT = `You write one clear signal for a networking product.
56
+
57
+ Combine the brief and the person's answers into a specific description of who they
58
+ want to meet, what they bring or need, and any stated constraint. Write it in the
59
+ person's own voice, first person, 1-3 sentences, concrete and free of hype. Also
60
+ return short "lookingFor" and "youBring" summaries for the confirmation card.
61
+ When revision feedback is present, it is the person correcting a draft they
62
+ already read: apply it to the whole signal rather than treating it as a place or
63
+ community constraint. Never invent facts beyond the brief and the answers.`;
64
+ /**
65
+ * Render an answer as a human-readable label.
66
+ *
67
+ * @param answer - Selected options plus optional free text
68
+ * @returns Comma-joined non-empty parts
69
+ */
70
+ export function answerLabel(answer) {
71
+ return [...answer.selectedOptions, answer.freeText?.trim() ?? ""].filter(Boolean).join(", ");
72
+ }
73
+ /** Runs the two live stages of the fast intake funnel. */
74
+ export class SignalIntakeOrchestrator {
75
+ /**
76
+ * @param models - Optional injected structured models. Tests pass stubs.
77
+ */
78
+ constructor(models) {
79
+ this.questionModel = models?.question
80
+ ?? createStructuredModel("signalIntakePack", questionSchema);
81
+ this.synthesisModel = models?.synthesis
82
+ ?? createStructuredModel("signalIntakePack", synthesisSchema);
83
+ }
84
+ /**
85
+ * Generate round 2 from the brief and the round-1 answer.
86
+ *
87
+ * @param input - Brief plus the answered round-1 question
88
+ * @returns A renderable question; the static fallback when generation fails
89
+ */
90
+ async nextQuestion(input) {
91
+ 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.`),
95
+ ]);
96
+ return normalizeIntakePack({ brief: input.brief, question: raw }).question;
97
+ }
98
+ catch {
99
+ return FALLBACK_BRING_QUESTION;
100
+ }
101
+ }
102
+ /**
103
+ * Write the signal from both answers, any where-constraint, and any
104
+ * revision feedback.
105
+ *
106
+ * @param input - Brief, both answers, optional where constraint, optional feedback
107
+ * @returns Description plus card summary fields
108
+ * @throws Propagates model failure so the caller can mark the run failed
109
+ */
110
+ async synthesize(input) {
111
+ const whereLine = input.whereText?.trim()
112
+ ? `\n\nWhere constraint: ${input.whereText.trim()}`
113
+ : "";
114
+ const feedbackLine = input.feedback?.trim()
115
+ ? `\n\nRevision feedback on the previous draft: ${input.feedback.trim()}`
116
+ : "";
117
+ const result = await this.synthesisModel.invoke([
118
+ 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.`),
120
+ ]);
121
+ return {
122
+ description: result.description.trim(),
123
+ lookingFor: result.lookingFor.trim(),
124
+ youBring: result.youBring.trim(),
125
+ };
126
+ }
127
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Signal intake pack generator.
3
+ *
4
+ * Produces the offline half of the fast intake path: a prose brief written for
5
+ * the intake task and a ready round-1 question. Both are stored per user and
6
+ * refreshed in the background, so `/i/new` can render round 1 with no model call.
7
+ */
8
+ import type { BaseLanguageModelInput } from "@langchain/core/language_models/base";
9
+ import type { Runnable } from "@langchain/core/runnables";
10
+ /** One selectable option on the round-1 question. */
11
+ export interface IntakePackQuestionOption {
12
+ label: string;
13
+ description: string;
14
+ }
15
+ /** Round-1 question, shaped exactly like the frontend `QuestionPayload`. */
16
+ export interface IntakePackQuestion {
17
+ title: string;
18
+ prompt: string;
19
+ options: IntakePackQuestionOption[];
20
+ multiSelect: boolean;
21
+ }
22
+ /** Everything the generator needs about a user. */
23
+ export interface IntakePackInput {
24
+ premises: Array<{
25
+ text: string;
26
+ }>;
27
+ networkTitles: string[];
28
+ globalContext: string | null;
29
+ }
30
+ /** Stored artifact: the brief plus the ready round-1 question. */
31
+ export interface IntakePack {
32
+ brief: string;
33
+ question: IntakePackQuestion;
34
+ }
35
+ /** Generates and normalizes the per-user intake pack. */
36
+ export declare class SignalIntakePackGenerator {
37
+ private readonly model;
38
+ /**
39
+ * @param model - Optional injected structured model. Tests pass a stub.
40
+ */
41
+ constructor(model?: Runnable<BaseLanguageModelInput, IntakePack>);
42
+ /**
43
+ * Generate the intake pack for one user.
44
+ *
45
+ * @param input - Active premises, membership titles, and the global context paragraph
46
+ * @returns Normalized brief and round-1 question
47
+ */
48
+ generate(input: IntakePackInput): Promise<IntakePack>;
49
+ }
50
+ /**
51
+ * Clamp a generated pack into the shape the frontend can render.
52
+ *
53
+ * @param pack - Raw model output
54
+ * @returns Normalized pack
55
+ * @throws When the question has fewer than 2 usable options
56
+ */
57
+ export declare function normalizeIntakePack(pack: IntakePack): IntakePack;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Signal intake pack generator.
3
+ *
4
+ * Produces the offline half of the fast intake path: a prose brief written for
5
+ * the intake task and a ready round-1 question. Both are stored per user and
6
+ * refreshed in the background, so `/i/new` can render round 1 with no model call.
7
+ */
8
+ import { HumanMessage, SystemMessage } from "@langchain/core/messages";
9
+ import { z } from "zod";
10
+ import { createStructuredModel } from "../../shared/agent/model.config.js";
11
+ const packSchema = z.object({
12
+ brief: z.string().min(1),
13
+ question: z.object({
14
+ title: z.string(),
15
+ prompt: z.string().min(1),
16
+ options: z.array(z.object({ label: z.string().min(1), description: z.string() })),
17
+ multiSelect: z.boolean(),
18
+ }),
19
+ });
20
+ const SYSTEM_PROMPT = `You prepare a person's signal-intake pack for a networking product.
21
+
22
+ Produce two things:
23
+
24
+ 1. "brief": 4-8 sentences of prose, third person, written specifically to help a
25
+ small model run an intake interview with this person. Cover who they are, what
26
+ they plausibly need from a connection, what they can offer in return, and which
27
+ communities they belong to. Be concrete. Never invent facts beyond the input.
28
+
29
+ 2. "question": the opening intake question asking who they want to meet right now.
30
+ Give 3-4 concrete, distinct recipient profiles grounded in the person's actual
31
+ background (for example a design partner, a technical co-founder, an early
32
+ customer, a specific expertise gap) — never generic choices like "anyone".
33
+ Each option needs a short label and a one-line description. Set multiSelect false.
34
+
35
+ Never expose raw JSON, IDs, or internal vocabulary in either field.`;
36
+ /** Generates and normalizes the per-user intake pack. */
37
+ export class SignalIntakePackGenerator {
38
+ /**
39
+ * @param model - Optional injected structured model. Tests pass a stub.
40
+ */
41
+ constructor(model) {
42
+ this.model = model ?? createStructuredModel("signalIntakePack", packSchema);
43
+ }
44
+ /**
45
+ * Generate the intake pack for one user.
46
+ *
47
+ * @param input - Active premises, membership titles, and the global context paragraph
48
+ * @returns Normalized brief and round-1 question
49
+ */
50
+ async generate(input) {
51
+ const premiseBlock = input.premises.map((p) => `- ${p.text}`).join("\n");
52
+ const networkBlock = input.networkTitles.length > 0
53
+ ? input.networkTitles.join(", ")
54
+ : "none";
55
+ const contextBlock = input.globalContext?.trim() ? input.globalContext.trim() : "none";
56
+ const raw = await this.model.invoke([
57
+ new SystemMessage(SYSTEM_PROMPT),
58
+ new HumanMessage(`Communities: ${networkBlock}\n\nGlobal context:\n${contextBlock}\n\nPremises:\n${premiseBlock}\n\nWrite the intake pack.`),
59
+ ]);
60
+ return normalizeIntakePack(raw);
61
+ }
62
+ }
63
+ /**
64
+ * Clamp a generated pack into the shape the frontend can render.
65
+ *
66
+ * @param pack - Raw model output
67
+ * @returns Normalized pack
68
+ * @throws When the question has fewer than 2 usable options
69
+ */
70
+ export function normalizeIntakePack(pack) {
71
+ const options = pack.question.options
72
+ .filter((option) => option.label.trim().length > 0)
73
+ .slice(0, 4)
74
+ .map((option) => ({
75
+ label: option.label.trim().slice(0, 120),
76
+ description: option.description.trim().slice(0, 280),
77
+ }));
78
+ if (options.length < 2) {
79
+ throw new Error("Intake pack question needs at least 2 options.");
80
+ }
81
+ return {
82
+ brief: pack.brief.trim(),
83
+ question: {
84
+ title: pack.question.title.trim() || "Question 1",
85
+ prompt: pack.question.prompt.trim().slice(0, 400),
86
+ options,
87
+ multiSelect: pack.question.multiSelect,
88
+ },
89
+ };
90
+ }
@@ -11,5 +11,5 @@
11
11
  * host implementations.
12
12
  */
13
13
  export { type VerifiedIntent, type IntentValidationFailureCategory, type IntentValidationFailure, type ExecutionResult, IntentGraphState, DEFAULT_SPECIFICITY_WARNING, } from "../domain/index.js";
14
- export { IntentGraphFactory, enforceIntentActionBoundary, buildExplicitUpdateActions, SemanticVerifier, type SemanticVerifierOutput, ExplicitIntentInferrer, type InferredIntent, type InferrerOptions, IntentClarifier, IntentReconciler, type IntentReconcilerOutput, type NormalizedIntentAction, IntentIndexer, IntentIndexerOutputSchema, type IntentIndexerOutput, describeIntentUpdateFailure, } from "../application/index.js";
14
+ export { IntentGraphFactory, enforceIntentActionBoundary, buildExplicitUpdateActions, SemanticVerifier, type SemanticVerifierOutput, ExplicitIntentInferrer, type InferredIntent, type InferrerOptions, IntentClarifier, IntentReconciler, type IntentReconcilerOutput, type NormalizedIntentAction, IntentIndexer, IntentIndexerOutputSchema, type IntentIndexerOutput, describeIntentUpdateFailure, SignalIntakePackGenerator, normalizeIntakePack, type IntakePack, type IntakePackInput, type IntakePackQuestion, type IntakePackQuestionOption, SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, type IntakeAnswer, type SynthesisInput, type SynthesisResult, } from "../application/index.js";
15
15
  export type { IntentGraphDatabase, EmbeddingGenerator, IntentGraphQueue } from "../ports/index.js";
@@ -13,4 +13,4 @@
13
13
  // ── Domain contracts ──────────────────────────────────────────────────────────
14
14
  export { IntentGraphState, DEFAULT_SPECIFICITY_WARNING, } from "../domain/index.js";
15
15
  // ── Application seams ─────────────────────────────────────────────────────────
16
- export { IntentGraphFactory, enforceIntentActionBoundary, buildExplicitUpdateActions, SemanticVerifier, ExplicitIntentInferrer, IntentClarifier, IntentReconciler, IntentIndexer, IntentIndexerOutputSchema, describeIntentUpdateFailure, } from "../application/index.js";
16
+ export { IntentGraphFactory, enforceIntentActionBoundary, buildExplicitUpdateActions, SemanticVerifier, ExplicitIntentInferrer, IntentClarifier, IntentReconciler, IntentIndexer, IntentIndexerOutputSchema, describeIntentUpdateFailure, SignalIntakePackGenerator, normalizeIntakePack, SignalIntakeOrchestrator, answerLabel, FALLBACK_WHO_QUESTION, FALLBACK_BRING_QUESTION, } from "../application/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indexnetwork/protocol",
3
- "version": "8.0.3-rc.420.1",
3
+ "version": "8.1.1-rc.422.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",