@indexnetwork/protocol 4.5.0-rc.335.1 → 4.5.0-rc.337.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/dist/chat/chat-streaming.types.d.ts +1 -1
- package/dist/chat/chat-streaming.types.d.ts.map +1 -1
- package/dist/chat/chat-streaming.types.js.map +1 -1
- package/dist/chat/chat.agent.d.ts +1 -1
- package/dist/chat/chat.agent.d.ts.map +1 -1
- package/dist/chat/chat.agent.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/negotiation/negotiation.agent.d.ts +9 -0
- package/dist/negotiation/negotiation.agent.d.ts.map +1 -1
- package/dist/negotiation/negotiation.agent.js +11 -3
- package/dist/negotiation/negotiation.agent.js.map +1 -1
- package/dist/negotiation/negotiation.graph.d.ts +133 -35
- package/dist/negotiation/negotiation.graph.d.ts.map +1 -1
- package/dist/negotiation/negotiation.graph.js +197 -9
- package/dist/negotiation/negotiation.graph.js.map +1 -1
- package/dist/negotiation/negotiation.protocol.d.ts +250 -2
- package/dist/negotiation/negotiation.protocol.d.ts.map +1 -1
- package/dist/negotiation/negotiation.protocol.js +57 -8
- package/dist/negotiation/negotiation.protocol.js.map +1 -1
- package/dist/negotiation/negotiation.reflect.d.ts +199 -0
- package/dist/negotiation/negotiation.reflect.d.ts.map +1 -0
- package/dist/negotiation/negotiation.reflect.js +153 -0
- package/dist/negotiation/negotiation.reflect.js.map +1 -0
- package/dist/negotiation/negotiation.state.d.ts +40 -7
- package/dist/negotiation/negotiation.state.d.ts.map +1 -1
- package/dist/negotiation/negotiation.state.js +5 -1
- package/dist/negotiation/negotiation.state.js.map +1 -1
- package/dist/opportunity/question.prompt.d.ts +1 -1
- package/dist/opportunity/question.prompt.d.ts.map +1 -1
- package/dist/opportunity/question.prompt.js.map +1 -1
- package/dist/shared/agent/model.config.d.ts +5 -0
- package/dist/shared/agent/model.config.d.ts.map +1 -1
- package/dist/shared/agent/model.config.js +1 -0
- package/dist/shared/agent/model.config.js.map +1 -1
- package/dist/shared/interfaces/negotiation-events.interface.d.ts +30 -0
- package/dist/shared/interfaces/negotiation-events.interface.d.ts.map +1 -1
- package/dist/shared/interfaces/negotiation-events.interface.js.map +1 -1
- package/dist/shared/schemas/discovery-question.schema.d.ts +8 -8
- package/dist/shared/schemas/discovery-question.schema.js +1 -1
- package/dist/shared/schemas/discovery-question.schema.js.map +1 -1
- package/dist/shared/schemas/negotiation-state.schema.d.ts +41 -4
- package/dist/shared/schemas/negotiation-state.schema.d.ts.map +1 -1
- package/dist/shared/schemas/negotiation-state.schema.js +14 -0
- package/dist/shared/schemas/negotiation-state.schema.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Memory kinds a reflection pass may distill (P5.1 `negotiator_memories.kind`).
|
|
5
|
+
* Plain text at the DB level (55P04 lesson) — adding kinds is code-only.
|
|
6
|
+
*/
|
|
7
|
+
export declare const NEGOTIATOR_MEMORY_KINDS: readonly ["playbook", "disclosure_rule", "counterparty_dossier", "threshold"];
|
|
8
|
+
export type DistilledMemoryKind = (typeof NEGOTIATOR_MEMORY_KINDS)[number];
|
|
9
|
+
/** Hard ceiling on entries distilled per reflection pass (per side). */
|
|
10
|
+
export declare const MAX_DISTILLED_MEMORIES = 3;
|
|
11
|
+
/**
|
|
12
|
+
* One distilled memory entry as produced by the reflection LLM. The caller
|
|
13
|
+
* owns persistence: it resolves `aboutCounterparty` to a `subjectUserId`,
|
|
14
|
+
* computes the embedding, and attaches provenance `sourceRefs`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DistilledMemorySchema: z.ZodObject<{
|
|
17
|
+
kind: z.ZodEnum<["playbook", "disclosure_rule", "counterparty_dossier", "threshold"]>;
|
|
18
|
+
/** Self-contained operational statement, useful without the transcript. */
|
|
19
|
+
content: z.ZodString;
|
|
20
|
+
/** Evidence strength, 0..1. Explicit client statements score high. */
|
|
21
|
+
confidence: z.ZodNumber;
|
|
22
|
+
/**
|
|
23
|
+
* True when the entry is about the counterparty (kind should be
|
|
24
|
+
* `counterparty_dossier`); false for client-side rules and playbooks.
|
|
25
|
+
*/
|
|
26
|
+
aboutCounterparty: z.ZodBoolean;
|
|
27
|
+
/** Turn indexes (0-based, into the provided transcript) evidencing this entry. */
|
|
28
|
+
turnIndexes: z.ZodDefault<z.ZodArray<z.ZodNumber, "many">>;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
31
|
+
confidence: number;
|
|
32
|
+
content: string;
|
|
33
|
+
aboutCounterparty: boolean;
|
|
34
|
+
turnIndexes: number[];
|
|
35
|
+
}, {
|
|
36
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
37
|
+
confidence: number;
|
|
38
|
+
content: string;
|
|
39
|
+
aboutCounterparty: boolean;
|
|
40
|
+
turnIndexes?: number[] | undefined;
|
|
41
|
+
}>;
|
|
42
|
+
export type DistilledMemory = z.infer<typeof DistilledMemorySchema>;
|
|
43
|
+
export declare const ReflectionResultSchema: z.ZodObject<{
|
|
44
|
+
memories: z.ZodArray<z.ZodObject<{
|
|
45
|
+
kind: z.ZodEnum<["playbook", "disclosure_rule", "counterparty_dossier", "threshold"]>;
|
|
46
|
+
/** Self-contained operational statement, useful without the transcript. */
|
|
47
|
+
content: z.ZodString;
|
|
48
|
+
/** Evidence strength, 0..1. Explicit client statements score high. */
|
|
49
|
+
confidence: z.ZodNumber;
|
|
50
|
+
/**
|
|
51
|
+
* True when the entry is about the counterparty (kind should be
|
|
52
|
+
* `counterparty_dossier`); false for client-side rules and playbooks.
|
|
53
|
+
*/
|
|
54
|
+
aboutCounterparty: z.ZodBoolean;
|
|
55
|
+
/** Turn indexes (0-based, into the provided transcript) evidencing this entry. */
|
|
56
|
+
turnIndexes: z.ZodDefault<z.ZodArray<z.ZodNumber, "many">>;
|
|
57
|
+
}, "strip", z.ZodTypeAny, {
|
|
58
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
59
|
+
confidence: number;
|
|
60
|
+
content: string;
|
|
61
|
+
aboutCounterparty: boolean;
|
|
62
|
+
turnIndexes: number[];
|
|
63
|
+
}, {
|
|
64
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
65
|
+
confidence: number;
|
|
66
|
+
content: string;
|
|
67
|
+
aboutCounterparty: boolean;
|
|
68
|
+
turnIndexes?: number[] | undefined;
|
|
69
|
+
}>, "many">;
|
|
70
|
+
}, "strip", z.ZodTypeAny, {
|
|
71
|
+
memories: {
|
|
72
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
73
|
+
confidence: number;
|
|
74
|
+
content: string;
|
|
75
|
+
aboutCounterparty: boolean;
|
|
76
|
+
turnIndexes: number[];
|
|
77
|
+
}[];
|
|
78
|
+
}, {
|
|
79
|
+
memories: {
|
|
80
|
+
kind: "threshold" | "playbook" | "disclosure_rule" | "counterparty_dossier";
|
|
81
|
+
confidence: number;
|
|
82
|
+
content: string;
|
|
83
|
+
aboutCounterparty: boolean;
|
|
84
|
+
turnIndexes?: number[] | undefined;
|
|
85
|
+
}[];
|
|
86
|
+
}>;
|
|
87
|
+
export type ReflectionResult = z.infer<typeof ReflectionResultSchema>;
|
|
88
|
+
/** A transcript row projected into the reflecting client's perspective. */
|
|
89
|
+
export interface ReflectionTranscriptEntry {
|
|
90
|
+
index: number;
|
|
91
|
+
speaker: "client" | "counterparty";
|
|
92
|
+
action: string;
|
|
93
|
+
message?: string;
|
|
94
|
+
reasoning?: string;
|
|
95
|
+
}
|
|
96
|
+
export interface NegotiationReflectionInput {
|
|
97
|
+
/** The user whose negotiator is reflecting (memories land on their agent). */
|
|
98
|
+
clientUser: {
|
|
99
|
+
id: string;
|
|
100
|
+
name?: string;
|
|
101
|
+
bio?: string;
|
|
102
|
+
};
|
|
103
|
+
counterpartyUser: {
|
|
104
|
+
id: string;
|
|
105
|
+
name?: string;
|
|
106
|
+
bio?: string;
|
|
107
|
+
};
|
|
108
|
+
/** The client's seat in this negotiation. */
|
|
109
|
+
seat: "initiator" | "counterparty";
|
|
110
|
+
outcome: {
|
|
111
|
+
hasOpportunity: boolean;
|
|
112
|
+
reasoning: string;
|
|
113
|
+
turnCount: number;
|
|
114
|
+
};
|
|
115
|
+
transcript: ReflectionTranscriptEntry[];
|
|
116
|
+
/** Network prompt for context (optional). */
|
|
117
|
+
indexContext?: string;
|
|
118
|
+
}
|
|
119
|
+
export interface ChatReflectionInput {
|
|
120
|
+
clientUser: {
|
|
121
|
+
id: string;
|
|
122
|
+
name?: string;
|
|
123
|
+
};
|
|
124
|
+
/** The negotiator DM messages, oldest first. */
|
|
125
|
+
messages: Array<{
|
|
126
|
+
role: "user" | "assistant";
|
|
127
|
+
content: string;
|
|
128
|
+
}>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Payload the finalize node hands to the injected {@link ReflectEnqueueFn}.
|
|
132
|
+
* Carries user display context so the reflect worker never re-loads profiles;
|
|
133
|
+
* turn history is loaded from the conversation by the worker (payloads stay
|
|
134
|
+
* small in Redis).
|
|
135
|
+
*/
|
|
136
|
+
export interface NegotiationReflectJobData {
|
|
137
|
+
negotiationId: string;
|
|
138
|
+
conversationId: string;
|
|
139
|
+
opportunityId?: string;
|
|
140
|
+
sourceUser: {
|
|
141
|
+
id: string;
|
|
142
|
+
name?: string;
|
|
143
|
+
bio?: string;
|
|
144
|
+
};
|
|
145
|
+
candidateUser: {
|
|
146
|
+
id: string;
|
|
147
|
+
name?: string;
|
|
148
|
+
bio?: string;
|
|
149
|
+
};
|
|
150
|
+
initiatorUserId: string;
|
|
151
|
+
outcome: {
|
|
152
|
+
hasOpportunity: boolean;
|
|
153
|
+
reasoning: string;
|
|
154
|
+
turnCount: number;
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Injected enqueue callback for post-negotiation reflection (P5.2). The
|
|
159
|
+
* protocol package has no BullMQ access — services/api wires this at its
|
|
160
|
+
* composition roots, exactly like `QuestionerEnqueueFn`. Called fire-and-
|
|
161
|
+
* forget from the finalize node: a reflection failure must never affect the
|
|
162
|
+
* negotiation outcome.
|
|
163
|
+
*/
|
|
164
|
+
export type ReflectEnqueueFn = (job: NegotiationReflectJobData) => Promise<void>;
|
|
165
|
+
export interface NegotiationReflectorConfig {
|
|
166
|
+
/** Hard ceiling on the reflection LLM round-trip, in ms (default 20000). */
|
|
167
|
+
timeoutMs?: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The memory distiller (P5.2). One structured LLM call per reflection pass,
|
|
171
|
+
* producing ≤ {@link MAX_DISTILLED_MEMORIES} private memory entries for one
|
|
172
|
+
* client's negotiator. Throws on LLM/validation failure — callers (the reflect
|
|
173
|
+
* queue worker) own the swallow-and-log policy, since reflection must never
|
|
174
|
+
* affect a negotiation outcome.
|
|
175
|
+
*/
|
|
176
|
+
export declare class NegotiationReflector {
|
|
177
|
+
private readonly timeoutMs;
|
|
178
|
+
constructor(config?: NegotiationReflectorConfig);
|
|
179
|
+
/**
|
|
180
|
+
* Distill memories from a finished negotiation, from one side's perspective.
|
|
181
|
+
* @throws When the LLM call times out or returns schema-invalid output.
|
|
182
|
+
*/
|
|
183
|
+
reflectNegotiation(input: NegotiationReflectionInput): Promise<DistilledMemory[]>;
|
|
184
|
+
/**
|
|
185
|
+
* Distill stated preferences/corrections from a client ↔ negotiator chat.
|
|
186
|
+
* @throws When the LLM call times out or returns schema-invalid output.
|
|
187
|
+
*/
|
|
188
|
+
reflectChat(input: ChatReflectionInput): Promise<DistilledMemory[]>;
|
|
189
|
+
private distill;
|
|
190
|
+
/**
|
|
191
|
+
* Raw structured-model round trip. Split out as a seam so tests can drive
|
|
192
|
+
* the schema-validation path without a live provider.
|
|
193
|
+
*/
|
|
194
|
+
protected callModel(model: ReturnType<typeof createStructuredModel>, chatMessages: Array<{
|
|
195
|
+
role: string;
|
|
196
|
+
content: string;
|
|
197
|
+
}>): Promise<unknown>;
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=negotiation.reflect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"negotiation.reflect.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.reflect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createStructuredModel } from "../shared/agent/model.config.js";
|
|
3
|
+
import { invokeWithAbortSignal } from "../shared/agent/model-signal.js";
|
|
4
|
+
import { protocolLogger } from "../shared/observability/protocol.logger.js";
|
|
5
|
+
const reflectLog = protocolLogger("NegotiationReflector");
|
|
6
|
+
/**
|
|
7
|
+
* Memory kinds a reflection pass may distill (P5.1 `negotiator_memories.kind`).
|
|
8
|
+
* Plain text at the DB level (55P04 lesson) — adding kinds is code-only.
|
|
9
|
+
*/
|
|
10
|
+
export const NEGOTIATOR_MEMORY_KINDS = [
|
|
11
|
+
"playbook",
|
|
12
|
+
"disclosure_rule",
|
|
13
|
+
"counterparty_dossier",
|
|
14
|
+
"threshold",
|
|
15
|
+
];
|
|
16
|
+
/** Hard ceiling on entries distilled per reflection pass (per side). */
|
|
17
|
+
export const MAX_DISTILLED_MEMORIES = 3;
|
|
18
|
+
/**
|
|
19
|
+
* One distilled memory entry as produced by the reflection LLM. The caller
|
|
20
|
+
* owns persistence: it resolves `aboutCounterparty` to a `subjectUserId`,
|
|
21
|
+
* computes the embedding, and attaches provenance `sourceRefs`.
|
|
22
|
+
*/
|
|
23
|
+
export const DistilledMemorySchema = z.object({
|
|
24
|
+
kind: z.enum(NEGOTIATOR_MEMORY_KINDS),
|
|
25
|
+
/** Self-contained operational statement, useful without the transcript. */
|
|
26
|
+
content: z.string().min(1),
|
|
27
|
+
/** Evidence strength, 0..1. Explicit client statements score high. */
|
|
28
|
+
confidence: z.number().min(0).max(1),
|
|
29
|
+
/**
|
|
30
|
+
* True when the entry is about the counterparty (kind should be
|
|
31
|
+
* `counterparty_dossier`); false for client-side rules and playbooks.
|
|
32
|
+
*/
|
|
33
|
+
aboutCounterparty: z.boolean(),
|
|
34
|
+
/** Turn indexes (0-based, into the provided transcript) evidencing this entry. */
|
|
35
|
+
turnIndexes: z.array(z.number().int().min(0)).default([]),
|
|
36
|
+
});
|
|
37
|
+
export const ReflectionResultSchema = z.object({
|
|
38
|
+
memories: z.array(DistilledMemorySchema).max(MAX_DISTILLED_MEMORIES),
|
|
39
|
+
});
|
|
40
|
+
const NEGOTIATION_SYSTEM_PROMPT = `You are the private post-negotiation reflection process for {clientName}'s negotiator agent. The negotiation is over; your job is to distill AT MOST ${MAX_DISTILLED_MEMORIES} durable operational memory entries that will make {clientName}'s negotiator better in FUTURE negotiations. These memories are private to {clientName}'s agent — the counterparty never sees them.
|
|
41
|
+
|
|
42
|
+
Memory kinds:
|
|
43
|
+
- "playbook": a tactic or pattern that worked or failed (e.g. "Opening with the specific shared-interest angle got engagement; generic intros stalled").
|
|
44
|
+
- "disclosure_rule": what {clientName} is or is not willing to share/commit (only when the transcript actually evidences it).
|
|
45
|
+
- "counterparty_dossier": a durable fact about the counterparty useful in future dealings with THEM specifically (set aboutCounterparty=true).
|
|
46
|
+
- "threshold": a concrete boundary observed (e.g. minimum scope, timing constraints, deal-breakers).
|
|
47
|
+
|
|
48
|
+
Rules:
|
|
49
|
+
- Record ONLY what future negotiations need. No summaries, no play-by-play, no identity facts about {clientName} (their profile already covers those).
|
|
50
|
+
- Every entry MUST cite the transcript turn indexes that evidence it in turnIndexes.
|
|
51
|
+
- Each content string must be self-contained and actionable without the transcript.
|
|
52
|
+
- Set confidence by evidence strength: explicit statements ≈ 0.8-0.9, inferred patterns ≈ 0.4-0.6.
|
|
53
|
+
- aboutCounterparty=true ONLY for counterparty_dossier entries.
|
|
54
|
+
- Return an empty memories array when nothing durable was learned — most short or failed negotiations teach nothing. Do not force entries.`;
|
|
55
|
+
const CHAT_SYSTEM_PROMPT = `You are the private reflection process for {clientName}'s negotiator agent, reviewing a direct chat between {clientName} (the client) and their negotiator. Distill AT MOST ${MAX_DISTILLED_MEMORIES} durable operational memory entries capturing the client's STATED preferences, corrections, and instructions.
|
|
56
|
+
|
|
57
|
+
Memory kinds:
|
|
58
|
+
- "playbook": how the client wants negotiations approached (style, priorities).
|
|
59
|
+
- "disclosure_rule": what the client said they will or won't share/commit.
|
|
60
|
+
- "threshold": concrete boundaries the client stated (rates, scope, timing, deal-breakers).
|
|
61
|
+
|
|
62
|
+
Rules:
|
|
63
|
+
- Only distill what the CLIENT stated or clearly confirmed — never invent preferences from the negotiator's own suggestions.
|
|
64
|
+
- Do NOT produce counterparty_dossier entries; this is a client-side conversation. Always set aboutCounterparty=false.
|
|
65
|
+
- Each content string must be self-contained and actionable.
|
|
66
|
+
- turnIndexes cite 0-based indexes into the provided message list.
|
|
67
|
+
- Set confidence by how explicit the client was (direct instruction ≈ 0.9, implied preference ≈ 0.5).
|
|
68
|
+
- Return an empty memories array when the chat contains no durable guidance — casual Q&A usually doesn't.`;
|
|
69
|
+
const DEFAULT_REFLECT_TIMEOUT_MS = 20000;
|
|
70
|
+
/**
|
|
71
|
+
* The memory distiller (P5.2). One structured LLM call per reflection pass,
|
|
72
|
+
* producing ≤ {@link MAX_DISTILLED_MEMORIES} private memory entries for one
|
|
73
|
+
* client's negotiator. Throws on LLM/validation failure — callers (the reflect
|
|
74
|
+
* queue worker) own the swallow-and-log policy, since reflection must never
|
|
75
|
+
* affect a negotiation outcome.
|
|
76
|
+
*/
|
|
77
|
+
export class NegotiationReflector {
|
|
78
|
+
constructor(config) {
|
|
79
|
+
this.timeoutMs = config?.timeoutMs && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0
|
|
80
|
+
? config.timeoutMs
|
|
81
|
+
: DEFAULT_REFLECT_TIMEOUT_MS;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Distill memories from a finished negotiation, from one side's perspective.
|
|
85
|
+
* @throws When the LLM call times out or returns schema-invalid output.
|
|
86
|
+
*/
|
|
87
|
+
async reflectNegotiation(input) {
|
|
88
|
+
const clientName = input.clientUser.name ?? "the client";
|
|
89
|
+
const counterpartyName = input.counterpartyUser.name ?? "the counterparty";
|
|
90
|
+
const systemPrompt = NEGOTIATION_SYSTEM_PROMPT.replace(/{clientName}/g, clientName);
|
|
91
|
+
const transcriptText = input.transcript.length > 0
|
|
92
|
+
? input.transcript.map((t) => {
|
|
93
|
+
const who = t.speaker === "client" ? `${clientName}'s negotiator` : `${counterpartyName}'s negotiator`;
|
|
94
|
+
const parts = [`[${t.index}] ${who} → ${t.action}`];
|
|
95
|
+
if (t.message)
|
|
96
|
+
parts.push(`message: ${t.message}`);
|
|
97
|
+
if (t.reasoning)
|
|
98
|
+
parts.push(`reasoning: ${t.reasoning}`);
|
|
99
|
+
return parts.join("\n ");
|
|
100
|
+
}).join("\n")
|
|
101
|
+
: "(no turns)";
|
|
102
|
+
const userMessage = `CLIENT: ${clientName}${input.clientUser.bio ? ` — ${input.clientUser.bio}` : ""}
|
|
103
|
+
SEAT: ${input.seat === "initiator" ? "initiator (client's negotiator reached out)" : "counterparty (client's negotiator was reached)"}
|
|
104
|
+
COUNTERPARTY: ${counterpartyName}${input.counterpartyUser.bio ? ` — ${input.counterpartyUser.bio}` : ""}
|
|
105
|
+
${input.indexContext ? `NETWORK CONTEXT: ${input.indexContext}\n` : ""}
|
|
106
|
+
OUTCOME: ${input.outcome.hasOpportunity ? "accepted" : "not accepted"} after ${input.outcome.turnCount} turn(s) — ${input.outcome.reasoning}
|
|
107
|
+
|
|
108
|
+
TRANSCRIPT:
|
|
109
|
+
${transcriptText}
|
|
110
|
+
|
|
111
|
+
Distill the durable memories (or return an empty array).`;
|
|
112
|
+
return this.distill(systemPrompt, userMessage);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Distill stated preferences/corrections from a client ↔ negotiator chat.
|
|
116
|
+
* @throws When the LLM call times out or returns schema-invalid output.
|
|
117
|
+
*/
|
|
118
|
+
async reflectChat(input) {
|
|
119
|
+
const clientName = input.clientUser.name ?? "the client";
|
|
120
|
+
const systemPrompt = CHAT_SYSTEM_PROMPT.replace(/{clientName}/g, clientName);
|
|
121
|
+
const chatText = input.messages
|
|
122
|
+
.map((m, i) => `[${i}] ${m.role === "user" ? clientName : "negotiator"}: ${m.content}`)
|
|
123
|
+
.join("\n");
|
|
124
|
+
const userMessage = `CHAT between ${clientName} and their negotiator (oldest first):
|
|
125
|
+
${chatText}
|
|
126
|
+
|
|
127
|
+
Distill the client's durable guidance (or return an empty array).`;
|
|
128
|
+
return this.distill(systemPrompt, userMessage);
|
|
129
|
+
}
|
|
130
|
+
async distill(systemPrompt, userMessage) {
|
|
131
|
+
const model = createStructuredModel("negotiationReflector", ReflectionResultSchema, { name: "negotiation_reflector" });
|
|
132
|
+
const result = await this.callModel(model, [
|
|
133
|
+
{ role: "system", content: systemPrompt },
|
|
134
|
+
{ role: "user", content: userMessage },
|
|
135
|
+
]);
|
|
136
|
+
const parsed = ReflectionResultSchema.safeParse(result);
|
|
137
|
+
if (!parsed.success) {
|
|
138
|
+
reflectLog.warn("Reflection output failed schema validation", {
|
|
139
|
+
issues: parsed.error.issues.map((i) => i.message).slice(0, 3),
|
|
140
|
+
});
|
|
141
|
+
throw new Error(`Reflection failed validation: ${parsed.error.issues[0]?.message ?? "unknown"}`);
|
|
142
|
+
}
|
|
143
|
+
return parsed.data.memories.slice(0, MAX_DISTILLED_MEMORIES);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Raw structured-model round trip. Split out as a seam so tests can drive
|
|
147
|
+
* the schema-validation path without a live provider.
|
|
148
|
+
*/
|
|
149
|
+
async callModel(model, chatMessages) {
|
|
150
|
+
return invokeWithAbortSignal(model, chatMessages, AbortSignal.timeout(this.timeoutMs));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=negotiation.reflect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"negotiation.reflect.js","sourceRoot":"/","sources":["negotiation/negotiation.reflect.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from \"zod\";\n\nimport { createStructuredModel } from \"../shared/agent/model.config.js\";\nimport { invokeWithAbortSignal } from \"../shared/agent/model-signal.js\";\nimport { protocolLogger } from \"../shared/observability/protocol.logger.js\";\n\nconst reflectLog = protocolLogger(\"NegotiationReflector\");\n\n/**\n * Memory kinds a reflection pass may distill (P5.1 `negotiator_memories.kind`).\n * Plain text at the DB level (55P04 lesson) — adding kinds is code-only.\n */\nexport const NEGOTIATOR_MEMORY_KINDS = [\n \"playbook\",\n \"disclosure_rule\",\n \"counterparty_dossier\",\n \"threshold\",\n] as const;\n\nexport type DistilledMemoryKind = (typeof NEGOTIATOR_MEMORY_KINDS)[number];\n\n/** Hard ceiling on entries distilled per reflection pass (per side). */\nexport const MAX_DISTILLED_MEMORIES = 3;\n\n/**\n * One distilled memory entry as produced by the reflection LLM. The caller\n * owns persistence: it resolves `aboutCounterparty` to a `subjectUserId`,\n * computes the embedding, and attaches provenance `sourceRefs`.\n */\nexport const DistilledMemorySchema = z.object({\n kind: z.enum(NEGOTIATOR_MEMORY_KINDS),\n /** Self-contained operational statement, useful without the transcript. */\n content: z.string().min(1),\n /** Evidence strength, 0..1. Explicit client statements score high. */\n confidence: z.number().min(0).max(1),\n /**\n * True when the entry is about the counterparty (kind should be\n * `counterparty_dossier`); false for client-side rules and playbooks.\n */\n aboutCounterparty: z.boolean(),\n /** Turn indexes (0-based, into the provided transcript) evidencing this entry. */\n turnIndexes: z.array(z.number().int().min(0)).default([]),\n});\n\nexport type DistilledMemory = z.infer<typeof DistilledMemorySchema>;\n\nexport const ReflectionResultSchema = z.object({\n memories: z.array(DistilledMemorySchema).max(MAX_DISTILLED_MEMORIES),\n});\n\nexport type ReflectionResult = z.infer<typeof ReflectionResultSchema>;\n\n/** A transcript row projected into the reflecting client's perspective. */\nexport interface ReflectionTranscriptEntry {\n index: number;\n speaker: \"client\" | \"counterparty\";\n action: string;\n message?: string;\n reasoning?: string;\n}\n\nexport interface NegotiationReflectionInput {\n /** The user whose negotiator is reflecting (memories land on their agent). */\n clientUser: { id: string; name?: string; bio?: string };\n counterpartyUser: { id: string; name?: string; bio?: string };\n /** The client's seat in this negotiation. */\n seat: \"initiator\" | \"counterparty\";\n outcome: { hasOpportunity: boolean; reasoning: string; turnCount: number };\n transcript: ReflectionTranscriptEntry[];\n /** Network prompt for context (optional). */\n indexContext?: string;\n}\n\nexport interface ChatReflectionInput {\n clientUser: { id: string; name?: string };\n /** The negotiator DM messages, oldest first. */\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n}\n\n/**\n * Payload the finalize node hands to the injected {@link ReflectEnqueueFn}.\n * Carries user display context so the reflect worker never re-loads profiles;\n * turn history is loaded from the conversation by the worker (payloads stay\n * small in Redis).\n */\nexport interface NegotiationReflectJobData {\n negotiationId: string;\n conversationId: string;\n opportunityId?: string;\n sourceUser: { id: string; name?: string; bio?: string };\n candidateUser: { id: string; name?: string; bio?: string };\n initiatorUserId: string;\n outcome: { hasOpportunity: boolean; reasoning: string; turnCount: number };\n}\n\n/**\n * Injected enqueue callback for post-negotiation reflection (P5.2). The\n * protocol package has no BullMQ access — services/api wires this at its\n * composition roots, exactly like `QuestionerEnqueueFn`. Called fire-and-\n * forget from the finalize node: a reflection failure must never affect the\n * negotiation outcome.\n */\nexport type ReflectEnqueueFn = (job: NegotiationReflectJobData) => Promise<void>;\n\nconst NEGOTIATION_SYSTEM_PROMPT = `You are the private post-negotiation reflection process for {clientName}'s negotiator agent. The negotiation is over; your job is to distill AT MOST ${MAX_DISTILLED_MEMORIES} durable operational memory entries that will make {clientName}'s negotiator better in FUTURE negotiations. These memories are private to {clientName}'s agent — the counterparty never sees them.\n\nMemory kinds:\n- \"playbook\": a tactic or pattern that worked or failed (e.g. \"Opening with the specific shared-interest angle got engagement; generic intros stalled\").\n- \"disclosure_rule\": what {clientName} is or is not willing to share/commit (only when the transcript actually evidences it).\n- \"counterparty_dossier\": a durable fact about the counterparty useful in future dealings with THEM specifically (set aboutCounterparty=true).\n- \"threshold\": a concrete boundary observed (e.g. minimum scope, timing constraints, deal-breakers).\n\nRules:\n- Record ONLY what future negotiations need. No summaries, no play-by-play, no identity facts about {clientName} (their profile already covers those).\n- Every entry MUST cite the transcript turn indexes that evidence it in turnIndexes.\n- Each content string must be self-contained and actionable without the transcript.\n- Set confidence by evidence strength: explicit statements ≈ 0.8-0.9, inferred patterns ≈ 0.4-0.6.\n- aboutCounterparty=true ONLY for counterparty_dossier entries.\n- Return an empty memories array when nothing durable was learned — most short or failed negotiations teach nothing. Do not force entries.`;\n\nconst CHAT_SYSTEM_PROMPT = `You are the private reflection process for {clientName}'s negotiator agent, reviewing a direct chat between {clientName} (the client) and their negotiator. Distill AT MOST ${MAX_DISTILLED_MEMORIES} durable operational memory entries capturing the client's STATED preferences, corrections, and instructions.\n\nMemory kinds:\n- \"playbook\": how the client wants negotiations approached (style, priorities).\n- \"disclosure_rule\": what the client said they will or won't share/commit.\n- \"threshold\": concrete boundaries the client stated (rates, scope, timing, deal-breakers).\n\nRules:\n- Only distill what the CLIENT stated or clearly confirmed — never invent preferences from the negotiator's own suggestions.\n- Do NOT produce counterparty_dossier entries; this is a client-side conversation. Always set aboutCounterparty=false.\n- Each content string must be self-contained and actionable.\n- turnIndexes cite 0-based indexes into the provided message list.\n- Set confidence by how explicit the client was (direct instruction ≈ 0.9, implied preference ≈ 0.5).\n- Return an empty memories array when the chat contains no durable guidance — casual Q&A usually doesn't.`;\n\nconst DEFAULT_REFLECT_TIMEOUT_MS = 20_000;\n\nexport interface NegotiationReflectorConfig {\n /** Hard ceiling on the reflection LLM round-trip, in ms (default 20000). */\n timeoutMs?: number;\n}\n\n/**\n * The memory distiller (P5.2). One structured LLM call per reflection pass,\n * producing ≤ {@link MAX_DISTILLED_MEMORIES} private memory entries for one\n * client's negotiator. Throws on LLM/validation failure — callers (the reflect\n * queue worker) own the swallow-and-log policy, since reflection must never\n * affect a negotiation outcome.\n */\nexport class NegotiationReflector {\n private readonly timeoutMs: number;\n\n constructor(config?: NegotiationReflectorConfig) {\n this.timeoutMs = config?.timeoutMs && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0\n ? config.timeoutMs\n : DEFAULT_REFLECT_TIMEOUT_MS;\n }\n\n /**\n * Distill memories from a finished negotiation, from one side's perspective.\n * @throws When the LLM call times out or returns schema-invalid output.\n */\n async reflectNegotiation(input: NegotiationReflectionInput): Promise<DistilledMemory[]> {\n const clientName = input.clientUser.name ?? \"the client\";\n const counterpartyName = input.counterpartyUser.name ?? \"the counterparty\";\n\n const systemPrompt = NEGOTIATION_SYSTEM_PROMPT.replace(/{clientName}/g, clientName);\n\n const transcriptText = input.transcript.length > 0\n ? input.transcript.map((t) => {\n const who = t.speaker === \"client\" ? `${clientName}'s negotiator` : `${counterpartyName}'s negotiator`;\n const parts = [`[${t.index}] ${who} → ${t.action}`];\n if (t.message) parts.push(`message: ${t.message}`);\n if (t.reasoning) parts.push(`reasoning: ${t.reasoning}`);\n return parts.join(\"\\n \");\n }).join(\"\\n\")\n : \"(no turns)\";\n\n const userMessage = `CLIENT: ${clientName}${input.clientUser.bio ? ` — ${input.clientUser.bio}` : \"\"}\nSEAT: ${input.seat === \"initiator\" ? \"initiator (client's negotiator reached out)\" : \"counterparty (client's negotiator was reached)\"}\nCOUNTERPARTY: ${counterpartyName}${input.counterpartyUser.bio ? ` — ${input.counterpartyUser.bio}` : \"\"}\n${input.indexContext ? `NETWORK CONTEXT: ${input.indexContext}\\n` : \"\"}\nOUTCOME: ${input.outcome.hasOpportunity ? \"accepted\" : \"not accepted\"} after ${input.outcome.turnCount} turn(s) — ${input.outcome.reasoning}\n\nTRANSCRIPT:\n${transcriptText}\n\nDistill the durable memories (or return an empty array).`;\n\n return this.distill(systemPrompt, userMessage);\n }\n\n /**\n * Distill stated preferences/corrections from a client ↔ negotiator chat.\n * @throws When the LLM call times out or returns schema-invalid output.\n */\n async reflectChat(input: ChatReflectionInput): Promise<DistilledMemory[]> {\n const clientName = input.clientUser.name ?? \"the client\";\n const systemPrompt = CHAT_SYSTEM_PROMPT.replace(/{clientName}/g, clientName);\n\n const chatText = input.messages\n .map((m, i) => `[${i}] ${m.role === \"user\" ? clientName : \"negotiator\"}: ${m.content}`)\n .join(\"\\n\");\n\n const userMessage = `CHAT between ${clientName} and their negotiator (oldest first):\n${chatText}\n\nDistill the client's durable guidance (or return an empty array).`;\n\n return this.distill(systemPrompt, userMessage);\n }\n\n private async distill(systemPrompt: string, userMessage: string): Promise<DistilledMemory[]> {\n const model = createStructuredModel(\"negotiationReflector\", ReflectionResultSchema, { name: \"negotiation_reflector\" });\n\n const result = await this.callModel(model, [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: userMessage },\n ]);\n\n const parsed = ReflectionResultSchema.safeParse(result);\n if (!parsed.success) {\n reflectLog.warn(\"Reflection output failed schema validation\", {\n issues: parsed.error.issues.map((i) => i.message).slice(0, 3),\n });\n throw new Error(`Reflection failed validation: ${parsed.error.issues[0]?.message ?? \"unknown\"}`);\n }\n return parsed.data.memories.slice(0, MAX_DISTILLED_MEMORIES);\n }\n\n /**\n * Raw structured-model round trip. Split out as a seam so tests can drive\n * the schema-validation path without a live provider.\n */\n protected async callModel(\n model: ReturnType<typeof createStructuredModel>,\n chatMessages: Array<{ role: string; content: string }>,\n ): Promise<unknown> {\n return invokeWithAbortSignal(model, chatMessages, AbortSignal.timeout(this.timeoutMs));\n }\n}\n"]}
|
|
@@ -8,7 +8,7 @@ import { type NegotiationProtocolVersion } from "../shared/schemas/negotiation-s
|
|
|
8
8
|
* is enforced by the seat-scoped schemas in `negotiation.protocol.ts`.
|
|
9
9
|
*/
|
|
10
10
|
export declare const NegotiationTurnSchema: z.ZodObject<{
|
|
11
|
-
action: z.ZodEnum<["propose", "accept", "reject", "counter", "question", "outreach", "withdraw", "decline"]>;
|
|
11
|
+
action: z.ZodEnum<["propose", "accept", "reject", "counter", "question", "outreach", "withdraw", "decline", "ask_user"]>;
|
|
12
12
|
assessment: z.ZodObject<{
|
|
13
13
|
reasoning: z.ZodString;
|
|
14
14
|
suggestedRoles: z.ZodObject<{
|
|
@@ -35,8 +35,19 @@ export declare const NegotiationTurnSchema: z.ZodObject<{
|
|
|
35
35
|
};
|
|
36
36
|
}>;
|
|
37
37
|
message: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
38
|
+
/** Present when action is `ask_user` (v2, P3.2). */
|
|
39
|
+
askUser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
40
|
+
disclosureSubject: z.ZodString;
|
|
41
|
+
draftQuestion: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
42
|
+
}, "strip", z.ZodTypeAny, {
|
|
43
|
+
disclosureSubject: string;
|
|
44
|
+
draftQuestion?: string | null | undefined;
|
|
45
|
+
}, {
|
|
46
|
+
disclosureSubject: string;
|
|
47
|
+
draftQuestion?: string | null | undefined;
|
|
48
|
+
}>>>;
|
|
38
49
|
}, "strip", z.ZodTypeAny, {
|
|
39
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
50
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
40
51
|
assessment: {
|
|
41
52
|
reasoning: string;
|
|
42
53
|
suggestedRoles: {
|
|
@@ -45,8 +56,12 @@ export declare const NegotiationTurnSchema: z.ZodObject<{
|
|
|
45
56
|
};
|
|
46
57
|
};
|
|
47
58
|
message?: string | null | undefined;
|
|
59
|
+
askUser?: {
|
|
60
|
+
disclosureSubject: string;
|
|
61
|
+
draftQuestion?: string | null | undefined;
|
|
62
|
+
} | null | undefined;
|
|
48
63
|
}, {
|
|
49
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
64
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
50
65
|
assessment: {
|
|
51
66
|
reasoning: string;
|
|
52
67
|
suggestedRoles: {
|
|
@@ -55,6 +70,10 @@ export declare const NegotiationTurnSchema: z.ZodObject<{
|
|
|
55
70
|
};
|
|
56
71
|
};
|
|
57
72
|
message?: string | null | undefined;
|
|
73
|
+
askUser?: {
|
|
74
|
+
disclosureSubject: string;
|
|
75
|
+
draftQuestion?: string | null | undefined;
|
|
76
|
+
} | null | undefined;
|
|
58
77
|
}>;
|
|
59
78
|
/** Restricted v1 turn schema for the system agent (no question action). */
|
|
60
79
|
export declare const SystemNegotiationTurnSchema: z.ZodObject<{
|
|
@@ -310,7 +329,7 @@ export declare const NegotiationGraphState: import("@langchain/langgraph").Annot
|
|
|
310
329
|
timeoutMs: import("@langchain/langgraph").BaseChannel<number, number | import("@langchain/langgraph").OverwriteValue<number>, unknown>;
|
|
311
330
|
currentSpeaker: import("@langchain/langgraph").BaseChannel<"source" | "candidate", "source" | "candidate" | import("@langchain/langgraph").OverwriteValue<"source" | "candidate">, unknown>;
|
|
312
331
|
lastTurn: import("@langchain/langgraph").BaseChannel<{
|
|
313
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
332
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
314
333
|
assessment: {
|
|
315
334
|
reasoning: string;
|
|
316
335
|
suggestedRoles: {
|
|
@@ -319,8 +338,12 @@ export declare const NegotiationGraphState: import("@langchain/langgraph").Annot
|
|
|
319
338
|
};
|
|
320
339
|
};
|
|
321
340
|
message?: string | null | undefined;
|
|
341
|
+
askUser?: {
|
|
342
|
+
disclosureSubject: string;
|
|
343
|
+
draftQuestion?: string | null | undefined;
|
|
344
|
+
} | null | undefined;
|
|
322
345
|
} | null, {
|
|
323
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
346
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
324
347
|
assessment: {
|
|
325
348
|
reasoning: string;
|
|
326
349
|
suggestedRoles: {
|
|
@@ -329,8 +352,12 @@ export declare const NegotiationGraphState: import("@langchain/langgraph").Annot
|
|
|
329
352
|
};
|
|
330
353
|
};
|
|
331
354
|
message?: string | null | undefined;
|
|
355
|
+
askUser?: {
|
|
356
|
+
disclosureSubject: string;
|
|
357
|
+
draftQuestion?: string | null | undefined;
|
|
358
|
+
} | null | undefined;
|
|
332
359
|
} | import("@langchain/langgraph").OverwriteValue<{
|
|
333
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
360
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
334
361
|
assessment: {
|
|
335
362
|
reasoning: string;
|
|
336
363
|
suggestedRoles: {
|
|
@@ -339,14 +366,20 @@ export declare const NegotiationGraphState: import("@langchain/langgraph").Annot
|
|
|
339
366
|
};
|
|
340
367
|
};
|
|
341
368
|
message?: string | null | undefined;
|
|
369
|
+
askUser?: {
|
|
370
|
+
disclosureSubject: string;
|
|
371
|
+
draftQuestion?: string | null | undefined;
|
|
372
|
+
} | null | undefined;
|
|
342
373
|
} | null> | null, unknown>;
|
|
343
374
|
/**
|
|
344
375
|
* Graph status.
|
|
345
376
|
* - `active` — agents are exchanging turns (default)
|
|
346
377
|
* - `waiting_for_agent` — graph suspended; awaiting external agent response or timeout
|
|
378
|
+
* - `input_required` — graph suspended on an `ask_user` pause; awaiting the
|
|
379
|
+
* negotiator's own client (answer or 24 h window expiry resumes it)
|
|
347
380
|
* - `completed` — negotiation finalized (accept/reject/turn-cap/timeout)
|
|
348
381
|
*/
|
|
349
|
-
status: import("@langchain/langgraph").BaseChannel<"completed" | "waiting_for_agent" | "active", "completed" | "waiting_for_agent" | "active" | import("@langchain/langgraph").OverwriteValue<"completed" | "waiting_for_agent" | "active">, unknown>;
|
|
382
|
+
status: import("@langchain/langgraph").BaseChannel<"completed" | "waiting_for_agent" | "active" | "input_required", "completed" | "waiting_for_agent" | "active" | "input_required" | import("@langchain/langgraph").OverwriteValue<"completed" | "waiting_for_agent" | "active" | "input_required">, unknown>;
|
|
350
383
|
/** Number of turns present in the conversation before this session started. */
|
|
351
384
|
priorTurnCount: import("@langchain/langgraph").BaseChannel<number, number | import("@langchain/langgraph").OverwriteValue<number>, unknown>;
|
|
352
385
|
/** User answers collected by the questioner between negotiation sessions. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"negotiation.state.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACxF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"negotiation.state.d.ts","sourceRoot":"/","sources":["negotiation/negotiation.state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Annotation } from "@langchain/langgraph";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { NEGOTIATION_ACTIONS } from "../shared/schemas/negotiation-state.schema.js";
|
|
3
|
+
import { AskUserPayloadSchema, NEGOTIATION_ACTIONS } from "../shared/schemas/negotiation-state.schema.js";
|
|
4
4
|
/**
|
|
5
5
|
* Zod schema for a single negotiation turn (DataPart payload in A2A message).
|
|
6
6
|
* Accepts the full v1+v2 action union — which subset is valid for a given turn
|
|
@@ -16,6 +16,8 @@ export const NegotiationTurnSchema = z.object({
|
|
|
16
16
|
}),
|
|
17
17
|
}),
|
|
18
18
|
message: z.string().nullable().optional(),
|
|
19
|
+
/** Present when action is `ask_user` (v2, P3.2). */
|
|
20
|
+
askUser: AskUserPayloadSchema.nullable().optional(),
|
|
19
21
|
});
|
|
20
22
|
/** Restricted v1 turn schema for the system agent (no question action). */
|
|
21
23
|
export const SystemNegotiationTurnSchema = z.object({
|
|
@@ -155,6 +157,8 @@ export const NegotiationGraphState = Annotation.Root({
|
|
|
155
157
|
* Graph status.
|
|
156
158
|
* - `active` — agents are exchanging turns (default)
|
|
157
159
|
* - `waiting_for_agent` — graph suspended; awaiting external agent response or timeout
|
|
160
|
+
* - `input_required` — graph suspended on an `ask_user` pause; awaiting the
|
|
161
|
+
* negotiator's own client (answer or 24 h window expiry resumes it)
|
|
158
162
|
* - `completed` — negotiation finalized (accept/reject/turn-cap/timeout)
|
|
159
163
|
*/
|
|
160
164
|
status: Annotation({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"negotiation.state.js","sourceRoot":"/","sources":["negotiation/negotiation.state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\nimport { z } from \"zod\";\nimport type { NegotiationUserAnswer } from \"../shared/interfaces/database.interface.js\";\nimport type { ScreenDecisionRecord } from \"./negotiation.screen.js\";\nimport { NEGOTIATION_ACTIONS, type NegotiationProtocolVersion } from \"../shared/schemas/negotiation-state.schema.js\";\n\n/**\n * Zod schema for a single negotiation turn (DataPart payload in A2A message).\n * Accepts the full v1+v2 action union — which subset is valid for a given turn\n * is enforced by the seat-scoped schemas in `negotiation.protocol.ts`.\n */\nexport const NegotiationTurnSchema = z.object({\n action: z.enum(NEGOTIATION_ACTIONS),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n});\n\n/** Restricted v1 turn schema for the system agent (no question action). */\nexport const SystemNegotiationTurnSchema = z.object({\n action: z.enum([\"propose\", \"accept\", \"reject\", \"counter\"]),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n});\n\n/** v1 turn schema for system agent's final allowed turn (must decide). */\nexport const FinalNegotiationTurnSchema = z.object({\n action: z.enum([\"accept\", \"reject\"]),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n});\n\nexport type NegotiationTurn = z.infer<typeof NegotiationTurnSchema>;\n\n/** Zod schema for the negotiation outcome (Artifact payload on COMPLETED task). */\nexport const NegotiationOutcomeSchema = z.object({\n hasOpportunity: z.boolean(),\n agreedRoles: z.array(z.object({\n userId: z.string(),\n role: z.enum([\"agent\", \"patient\", \"peer\"]),\n })),\n reasoning: z.string(),\n turnCount: z.number(),\n reason: z.enum([\"turn_cap\", \"timeout\"]).optional(),\n});\n\nexport type NegotiationOutcome = z.infer<typeof NegotiationOutcomeSchema>;\n\n/** Context each agent receives about its user. */\nexport interface UserNegotiationContext {\n id: string;\n intents: Array<{ id: string; title: string; description: string; confidence: number }>;\n profile: { name?: string; bio?: string; location?: string; interests?: string[]; skills?: string[] };\n}\n\n/** Seed assessment from the evaluator pre-filter. */\nexport interface SeedAssessment {\n reasoning: string;\n valencyRole: string;\n actors?: Array<{ userId: string; role: string }>;\n}\n\n/** Typed interface for a negotiation graph's invoke signature. */\nexport interface NegotiationGraphLike {\n invoke(input: {\n sourceUser: UserNegotiationContext;\n candidateUser: UserNegotiationContext;\n indexContext: { networkId: string; prompt: string };\n seedAssessment: Omit<SeedAssessment, \"actors\">;\n discoveryQuery?: string;\n opportunityId?: string;\n maxTurns?: number;\n timeoutMs?: number;\n /**\n * The user who holds the initiating seat for this match (v2 client-advocate\n * protocol). Stamped into task metadata by the init node. When omitted, the\n * init node resolves it: inherit from the prior task for the same\n * opportunity → conversation-scoped tie-break → fall back to sourceUser.id.\n */\n initiatorUserId?: string;\n }): Promise<{\n outcome: NegotiationOutcome | null;\n messages?: NegotiationMessage[];\n conversationId?: string;\n isContinuation?: boolean;\n priorTurnCount?: number;\n }>;\n}\n\n/** A2A message record shape (matches messages table). */\nexport interface NegotiationMessage {\n id: string;\n senderId: string;\n role: \"agent\";\n parts: unknown[];\n createdAt: Date;\n}\n\n/** LangGraph state annotation for the negotiation graph. */\nexport const NegotiationGraphState = Annotation.Root({\n sourceUser: Annotation<UserNegotiationContext>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ id: \"\", intents: [], profile: {} }),\n }),\n candidateUser: Annotation<UserNegotiationContext>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ id: \"\", intents: [], profile: {} }),\n }),\n indexContext: Annotation<{ networkId: string; prompt: string }>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ networkId: \"\", prompt: \"\" }),\n }),\n seedAssessment: Annotation<SeedAssessment>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ reasoning: \"\", valencyRole: \"\" }),\n }),\n\n /**\n * Explicit initiator seat for this match (purely additive metadata — no seat\n * rules attach to it yet). Resolution when unset happens in the init node;\n * the resolved value is written back to state and into task metadata.\n */\n initiatorUserId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** The explicit search query that triggered discovery (if any). */\n discoveryQuery: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n /**\n * Negotiation protocol version for this session's task. Resolved by the\n * init node: inherited from the prior task on the conversation when one\n * exists (never re-stamped — a v1 conversation stays v1 mid-flight), else\n * stamped from `NEGOTIATION_PROTOCOL_VERSION` for genuinely fresh runs.\n */\n protocolVersion: Annotation<NegotiationProtocolVersion>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"v1\" as const,\n }),\n\n /**\n * Screen-gate decision for this fresh run (P2.1 shadow mode). Written by the\n * screen node; null when the gate is off, on continuations, or before the\n * node runs. Mirrors `tasks.metadata.screenDecision`.\n */\n screenDecision: Annotation<ScreenDecisionRecord | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** Whether this run is continuing a prior conversation with the same pair. */\n isContinuation: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n opportunityId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n conversationId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n taskId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n messages: Annotation<NegotiationMessage[]>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n turnCount: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 0,\n }),\n maxTurns: Annotation<number | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n /**\n * Park-window budget in milliseconds. Ambient callers pass `AMBIENT_PARK_WINDOW_MS`\n * (5 minutes); orchestrator callers pass a shorter window. This annotation default\n * is a safety net for any caller that omits the field — keep it aligned with\n * `AMBIENT_PARK_WINDOW_MS` in packages/protocol/src/negotiation/negotiation.tools.ts.\n * Inlined rather than imported to avoid a state↔tools cycle.\n */\n timeoutMs: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 5 * 60 * 1000,\n }),\n\n currentSpeaker: Annotation<\"source\" | \"candidate\">({\n reducer: (curr, next) => next ?? curr,\n default: () => \"source\" as const,\n }),\n lastTurn: Annotation<NegotiationTurn | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /**\n * Graph status.\n * - `active` — agents are exchanging turns (default)\n * - `waiting_for_agent` — graph suspended; awaiting external agent response or timeout\n * - `completed` — negotiation finalized (accept/reject/turn-cap/timeout)\n */\n status: Annotation<'active' | 'waiting_for_agent' | 'completed'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'active' as const,\n }),\n\n /** Number of turns present in the conversation before this session started. */\n priorTurnCount: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 0,\n }),\n\n /** User answers collected by the questioner between negotiation sessions. */\n userAnswers: Annotation<NegotiationUserAnswer[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n outcome: Annotation<NegotiationOutcome | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n error: Annotation<string | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n});\n"]}
|
|
1
|
+
{"version":3,"file":"negotiation.state.js","sourceRoot":"/","sources":["negotiation/negotiation.state.ts"],"names":[],"mappings":"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","sourcesContent":["import { Annotation } from \"@langchain/langgraph\";\nimport { z } from \"zod\";\nimport type { NegotiationUserAnswer } from \"../shared/interfaces/database.interface.js\";\nimport type { ScreenDecisionRecord } from \"./negotiation.screen.js\";\nimport { AskUserPayloadSchema, NEGOTIATION_ACTIONS, type NegotiationProtocolVersion } from \"../shared/schemas/negotiation-state.schema.js\";\n\n/**\n * Zod schema for a single negotiation turn (DataPart payload in A2A message).\n * Accepts the full v1+v2 action union — which subset is valid for a given turn\n * is enforced by the seat-scoped schemas in `negotiation.protocol.ts`.\n */\nexport const NegotiationTurnSchema = z.object({\n action: z.enum(NEGOTIATION_ACTIONS),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n /** Present when action is `ask_user` (v2, P3.2). */\n askUser: AskUserPayloadSchema.nullable().optional(),\n});\n\n/** Restricted v1 turn schema for the system agent (no question action). */\nexport const SystemNegotiationTurnSchema = z.object({\n action: z.enum([\"propose\", \"accept\", \"reject\", \"counter\"]),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n});\n\n/** v1 turn schema for system agent's final allowed turn (must decide). */\nexport const FinalNegotiationTurnSchema = z.object({\n action: z.enum([\"accept\", \"reject\"]),\n assessment: z.object({\n reasoning: z.string(),\n suggestedRoles: z.object({\n ownUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n otherUser: z.enum([\"agent\", \"patient\", \"peer\"]),\n }),\n }),\n message: z.string().nullable().optional(),\n});\n\nexport type NegotiationTurn = z.infer<typeof NegotiationTurnSchema>;\n\n/** Zod schema for the negotiation outcome (Artifact payload on COMPLETED task). */\nexport const NegotiationOutcomeSchema = z.object({\n hasOpportunity: z.boolean(),\n agreedRoles: z.array(z.object({\n userId: z.string(),\n role: z.enum([\"agent\", \"patient\", \"peer\"]),\n })),\n reasoning: z.string(),\n turnCount: z.number(),\n reason: z.enum([\"turn_cap\", \"timeout\"]).optional(),\n});\n\nexport type NegotiationOutcome = z.infer<typeof NegotiationOutcomeSchema>;\n\n/** Context each agent receives about its user. */\nexport interface UserNegotiationContext {\n id: string;\n intents: Array<{ id: string; title: string; description: string; confidence: number }>;\n profile: { name?: string; bio?: string; location?: string; interests?: string[]; skills?: string[] };\n}\n\n/** Seed assessment from the evaluator pre-filter. */\nexport interface SeedAssessment {\n reasoning: string;\n valencyRole: string;\n actors?: Array<{ userId: string; role: string }>;\n}\n\n/** Typed interface for a negotiation graph's invoke signature. */\nexport interface NegotiationGraphLike {\n invoke(input: {\n sourceUser: UserNegotiationContext;\n candidateUser: UserNegotiationContext;\n indexContext: { networkId: string; prompt: string };\n seedAssessment: Omit<SeedAssessment, \"actors\">;\n discoveryQuery?: string;\n opportunityId?: string;\n maxTurns?: number;\n timeoutMs?: number;\n /**\n * The user who holds the initiating seat for this match (v2 client-advocate\n * protocol). Stamped into task metadata by the init node. When omitted, the\n * init node resolves it: inherit from the prior task for the same\n * opportunity → conversation-scoped tie-break → fall back to sourceUser.id.\n */\n initiatorUserId?: string;\n }): Promise<{\n outcome: NegotiationOutcome | null;\n messages?: NegotiationMessage[];\n conversationId?: string;\n isContinuation?: boolean;\n priorTurnCount?: number;\n }>;\n}\n\n/** A2A message record shape (matches messages table). */\nexport interface NegotiationMessage {\n id: string;\n senderId: string;\n role: \"agent\";\n parts: unknown[];\n createdAt: Date;\n}\n\n/** LangGraph state annotation for the negotiation graph. */\nexport const NegotiationGraphState = Annotation.Root({\n sourceUser: Annotation<UserNegotiationContext>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ id: \"\", intents: [], profile: {} }),\n }),\n candidateUser: Annotation<UserNegotiationContext>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ id: \"\", intents: [], profile: {} }),\n }),\n indexContext: Annotation<{ networkId: string; prompt: string }>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ networkId: \"\", prompt: \"\" }),\n }),\n seedAssessment: Annotation<SeedAssessment>({\n reducer: (curr, next) => next ?? curr,\n default: () => ({ reasoning: \"\", valencyRole: \"\" }),\n }),\n\n /**\n * Explicit initiator seat for this match (purely additive metadata — no seat\n * rules attach to it yet). Resolution when unset happens in the init node;\n * the resolved value is written back to state and into task metadata.\n */\n initiatorUserId: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n\n /** The explicit search query that triggered discovery (if any). */\n discoveryQuery: Annotation<string | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n /**\n * Negotiation protocol version for this session's task. Resolved by the\n * init node: inherited from the prior task on the conversation when one\n * exists (never re-stamped — a v1 conversation stays v1 mid-flight), else\n * stamped from `NEGOTIATION_PROTOCOL_VERSION` for genuinely fresh runs.\n */\n protocolVersion: Annotation<NegotiationProtocolVersion>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"v1\" as const,\n }),\n\n /**\n * Screen-gate decision for this fresh run (P2.1 shadow mode). Written by the\n * screen node; null when the gate is off, on continuations, or before the\n * node runs. Mirrors `tasks.metadata.screenDecision`.\n */\n screenDecision: Annotation<ScreenDecisionRecord | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /** Whether this run is continuing a prior conversation with the same pair. */\n isContinuation: Annotation<boolean>({\n reducer: (curr, next) => next ?? curr,\n default: () => false,\n }),\n opportunityId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n conversationId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n taskId: Annotation<string>({\n reducer: (curr, next) => next ?? curr,\n default: () => \"\",\n }),\n messages: Annotation<NegotiationMessage[]>({\n reducer: (curr, next) => [...curr, ...(next || [])],\n default: () => [],\n }),\n turnCount: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 0,\n }),\n maxTurns: Annotation<number | undefined>({\n reducer: (curr, next) => next ?? curr,\n default: () => undefined,\n }),\n /**\n * Park-window budget in milliseconds. Ambient callers pass `AMBIENT_PARK_WINDOW_MS`\n * (5 minutes); orchestrator callers pass a shorter window. This annotation default\n * is a safety net for any caller that omits the field — keep it aligned with\n * `AMBIENT_PARK_WINDOW_MS` in packages/protocol/src/negotiation/negotiation.tools.ts.\n * Inlined rather than imported to avoid a state↔tools cycle.\n */\n timeoutMs: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 5 * 60 * 1000,\n }),\n\n currentSpeaker: Annotation<\"source\" | \"candidate\">({\n reducer: (curr, next) => next ?? curr,\n default: () => \"source\" as const,\n }),\n lastTurn: Annotation<NegotiationTurn | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n\n /**\n * Graph status.\n * - `active` — agents are exchanging turns (default)\n * - `waiting_for_agent` — graph suspended; awaiting external agent response or timeout\n * - `input_required` — graph suspended on an `ask_user` pause; awaiting the\n * negotiator's own client (answer or 24 h window expiry resumes it)\n * - `completed` — negotiation finalized (accept/reject/turn-cap/timeout)\n */\n status: Annotation<'active' | 'waiting_for_agent' | 'input_required' | 'completed'>({\n reducer: (curr, next) => next ?? curr,\n default: () => 'active' as const,\n }),\n\n /** Number of turns present in the conversation before this session started. */\n priorTurnCount: Annotation<number>({\n reducer: (curr, next) => next ?? curr,\n default: () => 0,\n }),\n\n /** User answers collected by the questioner between negotiation sessions. */\n userAnswers: Annotation<NegotiationUserAnswer[]>({\n reducer: (curr, next) => next ?? curr,\n default: () => [],\n }),\n\n outcome: Annotation<NegotiationOutcome | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n error: Annotation<string | null>({\n reducer: (curr, next) => next ?? curr,\n default: () => null,\n }),\n});\n"]}
|
|
@@ -14,7 +14,7 @@ import type { DiscoveryNegotiationDigest } from "../shared/schemas/negotiation-d
|
|
|
14
14
|
export type NegotiationRole = "agent" | "patient" | "peer";
|
|
15
15
|
/** One turn within a negotiation. */
|
|
16
16
|
export interface DiscoveryTurn {
|
|
17
|
-
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline";
|
|
17
|
+
action: "propose" | "accept" | "reject" | "counter" | "question" | "outreach" | "withdraw" | "decline" | "ask_user";
|
|
18
18
|
reasoning: string;
|
|
19
19
|
suggestedRoles: {
|
|
20
20
|
ownUser: NegotiationRole;
|