@indexnetwork/protocol 22.0.0-rc.494.1 → 22.0.0-rc.495.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/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/negotiations/negotiation.agent.js +10 -1
- package/dist/negotiations/negotiation.graph.turn.d.ts +2 -0
- package/dist/negotiations/negotiation.graph.turn.js +9 -0
- package/dist/negotiations/negotiation.module.d.ts +1 -1
- package/dist/negotiations/negotiation.module.js +1 -1
- package/dist/negotiations/negotiation.question-safety.d.ts +0 -1
- package/dist/negotiations/negotiation.question-safety.js +0 -1
- package/dist/negotiations/negotiation.tools.js +83 -31
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -127,6 +127,6 @@ export { isNegotiationTurnCapReached, expectedNegotiationSpeaker, negotiationSco
|
|
|
127
127
|
export type { NegotiationSpeakerParticipants, NegotiationSpeakerMessage, NegotiationScopeMetadata } from "./negotiations/negotiation.module.js";
|
|
128
128
|
export { assessConsultationEligibility, consultationPromptFor, negotiationConsultationPolicyMode } from "./negotiations/negotiation.module.js";
|
|
129
129
|
export type { ConsultationEligibility, ConsultationEligibilityInput, NegotiationConsultationPolicyMode } from "./negotiations/negotiation.module.js";
|
|
130
|
-
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK,
|
|
130
|
+
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK, negotiationQuestionSettlementId, } from "./negotiations/negotiation.module.js";
|
|
131
131
|
export { classifyParkedNegotiation, consumeQuestionBlockAnswers, negotiationParkAnswerId, resumeParkedNegotiation, routeAnswerRef, } from "./negotiations/negotiation.module.js";
|
|
132
132
|
export type { AnswerRoute, InflightAnswerSettlementInput, InflightAnswerSettlementResult, NegotiationAnswerConsumptionPorts, NegotiationAnswerInput, NegotiationAnswerResumeOutcome, ParkClassification, QuestionBlockAnswerConsumptionInput, QuestionBlockAnswerConsumptionResult, RoutedAnswer, } from "./negotiations/negotiation.module.js";
|
package/dist/index.js
CHANGED
|
@@ -113,6 +113,6 @@ export { HERMES_OWNER_DIRECTIVE, HermesNegotiationResponseSchema, allowedHermesA
|
|
|
113
113
|
// ─── Negotiation seat rules (v2 client-advocate protocol) ───────────────────
|
|
114
114
|
export { isNegotiationTurnCapReached, expectedNegotiationSpeaker, negotiationScopeKey, readNegotiationMessages, allowedActionsFor, askUserAnswerWindowMs, configuredAskUserEnabled, isTerminalAction, isRejectLikeAction, readProtocolVersion, resolveSeat, seatViolationMessage } from "./negotiations/negotiation.module.js";
|
|
115
115
|
export { assessConsultationEligibility, consultationPromptFor, negotiationConsultationPolicyMode } from "./negotiations/negotiation.module.js";
|
|
116
|
-
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK,
|
|
116
|
+
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK, negotiationQuestionSettlementId, } from "./negotiations/negotiation.module.js";
|
|
117
117
|
// ─── Negotiation answer consumption (conversational questions) ──────────────
|
|
118
118
|
export { classifyParkedNegotiation, consumeQuestionBlockAnswers, negotiationParkAnswerId, resumeParkedNegotiation, routeAnswerRef, } from "./negotiations/negotiation.module.js";
|
|
@@ -238,9 +238,18 @@ ${stanceQuerySatisfiedRule(stance, otherName, userName)}`
|
|
|
238
238
|
// worth having. What it may not do is stand in for this negotiation's own
|
|
239
239
|
// exchange, so the policy line differs by whether this one has opened.
|
|
240
240
|
const hasPriorDialogue = hasAttributedDialogue || input.history.length > 0;
|
|
241
|
+
// Three states, not two. `isContinuation` only says whether this
|
|
242
|
+
// negotiation spoke in an EARLIER session; on any turn after the opening
|
|
243
|
+
// it is still false while this negotiation is visibly mid-exchange. Under
|
|
244
|
+
// the old two-way split such a turn was told the signal was new and to
|
|
245
|
+
// "make your own case for it" — which, for the initiator seat, reads as
|
|
246
|
+
// an instruction to re-open, and produced a fresh outreach on every one
|
|
247
|
+
// of its turns instead of a reply.
|
|
241
248
|
const priorDialoguePolicy = input.isContinuation
|
|
242
249
|
? 'Policy: You are continuing a prior dialogue. If this signal is materially the same as one you previously evaluated, you may resolve quickly. If materially different, evaluate on its own merits.'
|
|
243
|
-
:
|
|
250
|
+
: input.history.length > 0
|
|
251
|
+
? 'Policy: This negotiation is already under way — the turns above under the current opportunity are THIS exchange. Respond to the counterparty\'s latest turn; do not restate or re-pitch your opening.'
|
|
252
|
+
: 'Policy: This signal is NEW — you have not negotiated it before. The dialogue above concluded on other signals and is background only. Evaluate this one on its own merits and make your own case for it.';
|
|
244
253
|
const priorDialogueContext = hasPriorDialogue
|
|
245
254
|
? `\n\n--- Prior dialogue with this counterparty ---\n${attributionPreamble}${priorDialogueBody}\n\n--- New signal under evaluation ---\n${input.discoveryQuery
|
|
246
255
|
? `Discovery query: "${input.discoveryQuery}"`
|
|
@@ -45,6 +45,7 @@ export declare function turnNode(state: NegotiationState, deps: NegotiationGraph
|
|
|
45
45
|
role: "agent";
|
|
46
46
|
parts: unknown;
|
|
47
47
|
createdAt: Date;
|
|
48
|
+
taskId: string;
|
|
48
49
|
}[];
|
|
49
50
|
turnCount: number;
|
|
50
51
|
lastTurn: {
|
|
@@ -82,6 +83,7 @@ export declare function turnNode(state: NegotiationState, deps: NegotiationGraph
|
|
|
82
83
|
role: "agent";
|
|
83
84
|
parts: unknown;
|
|
84
85
|
createdAt: Date;
|
|
86
|
+
taskId: string;
|
|
85
87
|
}[];
|
|
86
88
|
turnCount: number;
|
|
87
89
|
currentSpeaker: "source" | "candidate";
|
|
@@ -495,6 +495,7 @@ export async function turnNode(state, deps) {
|
|
|
495
495
|
role: "agent",
|
|
496
496
|
parts: message.parts,
|
|
497
497
|
createdAt: message.createdAt,
|
|
498
|
+
taskId: state.taskId,
|
|
498
499
|
}],
|
|
499
500
|
turnCount: state.turnCount + 1,
|
|
500
501
|
lastTurn: turn,
|
|
@@ -518,12 +519,20 @@ export async function turnNode(state, deps) {
|
|
|
518
519
|
});
|
|
519
520
|
}
|
|
520
521
|
return {
|
|
522
|
+
// `taskId` is what makes this turn part of THIS session's block in the
|
|
523
|
+
// attributed prior dialogue (IND-569). Persisted with the current task
|
|
524
|
+
// id above; carried onto the state message so `buildAttributedDialogue`
|
|
525
|
+
// can match it. Dropping it silently emptied the "[Current opportunity —
|
|
526
|
+
// under negotiation now]" block, which — because the attributed
|
|
527
|
+
// rendering replaces the flat history in the prompt — left every turn
|
|
528
|
+
// after the opening blind to the exchange it was answering.
|
|
521
529
|
messages: [{
|
|
522
530
|
id: message.id,
|
|
523
531
|
senderId: message.senderId,
|
|
524
532
|
role: "agent",
|
|
525
533
|
parts: message.parts,
|
|
526
534
|
createdAt: message.createdAt,
|
|
535
|
+
taskId: state.taskId,
|
|
527
536
|
}],
|
|
528
537
|
turnCount: state.turnCount + 1,
|
|
529
538
|
currentSpeaker: (isSource ? "candidate" : "source"),
|
|
@@ -24,7 +24,7 @@ export { DEFAULT_NEGOTIATION_MAX_TURNS, isNegotiationTurnCapReached } from "./ne
|
|
|
24
24
|
export { expectedNegotiationSpeaker } from "./negotiation.expected-speaker.js";
|
|
25
25
|
export { negotiationScopeKey, readNegotiationMessages } from "./negotiation.scope.js";
|
|
26
26
|
export { assessConsultationEligibility, consultationPromptFor, negotiationConsultationPolicyMode, } from "./negotiation.consultation-policy.js";
|
|
27
|
-
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK,
|
|
27
|
+
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK, isSafeAuthoredNegotiationQuestion, isSafeNegotiationQuestionText, negotiationQuestionSettlementId, validateInflightAskUserFields, } from "./negotiation.question-safety.js";
|
|
28
28
|
export { renderNegotiatorChatMemorySection } from "./negotiation.memory.js";
|
|
29
29
|
export type { ConsultationEligibility, ConsultationEligibilityInput, NegotiationConsultationPolicyMode, NegotiationConsultationReason, } from "./negotiation.consultation-policy.js";
|
|
30
30
|
export type { HermesNegotiationAction, HermesNegotiationResponse, HermesOwnerDirective, HermesRoleAlignment, } from "./negotiation.hermes-contract.js";
|
|
@@ -19,5 +19,5 @@ export { DEFAULT_NEGOTIATION_MAX_TURNS, isNegotiationTurnCapReached } from "./ne
|
|
|
19
19
|
export { expectedNegotiationSpeaker } from "./negotiation.expected-speaker.js";
|
|
20
20
|
export { negotiationScopeKey, readNegotiationMessages } from "./negotiation.scope.js";
|
|
21
21
|
export { assessConsultationEligibility, consultationPromptFor, negotiationConsultationPolicyMode, } from "./negotiation.consultation-policy.js";
|
|
22
|
-
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK,
|
|
22
|
+
export { NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY, NEGOTIATION_QUESTION_GENERIC_NETWORK, isSafeAuthoredNegotiationQuestion, isSafeNegotiationQuestionText, negotiationQuestionSettlementId, validateInflightAskUserFields, } from "./negotiation.question-safety.js";
|
|
23
23
|
export { renderNegotiatorChatMemorySection } from "./negotiation.memory.js";
|
|
@@ -2,7 +2,6 @@ import type { StructuredQuestion } from '../shared/schemas/structured-question.s
|
|
|
2
2
|
/** Fixed prompt-safe labels; producers must not replace them with raw network/counterparty text. */
|
|
3
3
|
export declare const NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY = "the other participant";
|
|
4
4
|
export declare const NEGOTIATION_QUESTION_GENERIC_NETWORK = "the selected network";
|
|
5
|
-
export declare const NEGOTIATION_QUESTION_GENERIC_UPTAKE_ACTIVITY = "a potential collaboration that may require clarification before you decide";
|
|
6
5
|
/**
|
|
7
6
|
* Deterministically reject question context copied from private negotiation inputs.
|
|
8
7
|
* This guard never rewrites content: unsafe or ambiguous text yields no card while
|
|
@@ -2,7 +2,6 @@ import { hasUnsupportedOpportunityClaim } from '../shared/utils/claim-safety.js'
|
|
|
2
2
|
/** Fixed prompt-safe labels; producers must not replace them with raw network/counterparty text. */
|
|
3
3
|
export const NEGOTIATION_QUESTION_GENERIC_COUNTERPARTY = 'the other participant';
|
|
4
4
|
export const NEGOTIATION_QUESTION_GENERIC_NETWORK = 'the selected network';
|
|
5
|
-
export const NEGOTIATION_QUESTION_GENERIC_UPTAKE_ACTIVITY = 'a potential collaboration that may require clarification before you decide';
|
|
6
5
|
const INTERNAL_ID_PATTERN = /\b(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|(?:task|intent|network|opportunity|user|match)[_-]?id)\b/i;
|
|
7
6
|
const PRIVATE_SOURCE_PATTERN = /\b(?:private transcript|raw transcript|assessment(?:\.reasoning)?|seed assessment|evaluator reasoning|match reason|matchReason|internal metadata|counterparty profile)\b/i;
|
|
8
7
|
function normalize(value) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { success, error } from '../shared/agent/tool.helpers.js';
|
|
3
3
|
import { IndexNegotiator } from './negotiation.agent.js';
|
|
4
|
-
import { allowedActionsFor, isTerminalAction, readProtocolVersion, rejectActionFor, resolveSeat, seatViolationMessage } from './negotiation.protocol.js';
|
|
4
|
+
import { allowedActionsFor, isRejectLikeAction, isTerminalAction, readProtocolVersion, rejectActionFor, resolveSeat, seatViolationMessage } from './negotiation.protocol.js';
|
|
5
5
|
import { NEGOTIATION_ACTIONS } from '../shared/schemas/negotiation-state.schema.js';
|
|
6
6
|
import { protocolLogger } from '../shared/observability/protocol.logger.js';
|
|
7
7
|
import { focusedIntentId, focusedNetworkId } from '../shared/agent/tool.scope.js';
|
|
@@ -53,6 +53,58 @@ async function readOpportunityLifecycles(database, opportunityIds, ownerUserId)
|
|
|
53
53
|
return {};
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Terminal opportunity status for a negotiation concluded through this tool.
|
|
58
|
+
* Version-independent mapping, identical to the graph's finalize node:
|
|
59
|
+
* `accept` → `pending` (the owner gate is still ahead), reject-like →
|
|
60
|
+
* `rejected`, anything else (the turn cap) → `stalled`.
|
|
61
|
+
*/
|
|
62
|
+
function terminalOpportunityStatus(lastAction) {
|
|
63
|
+
if (lastAction === 'accept')
|
|
64
|
+
return 'pending';
|
|
65
|
+
return isRejectLikeAction(lastAction) ? 'rejected' : 'stalled';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Conclude a negotiation reached through `respond_to_negotiation`: complete
|
|
69
|
+
* the task, write the outcome artifact, and advance the opportunity out of
|
|
70
|
+
* `negotiating`.
|
|
71
|
+
*
|
|
72
|
+
* The status write goes through `updateOpportunityStatus` — the same waist the
|
|
73
|
+
* graph's finalize node uses — because that method is what carries the
|
|
74
|
+
* post-commit opportunity-transition emit. Writing the column any other way
|
|
75
|
+
* (or, as this path did before, not at all) leaves the transition hook blind:
|
|
76
|
+
* neither side's question-message is regenerated or pruned, radar buckets keep
|
|
77
|
+
* counting a finished negotiation, and the expiry sweep still sees a phantom
|
|
78
|
+
* active opportunity.
|
|
79
|
+
*
|
|
80
|
+
* The status write is best-effort, mirroring finalize: the task is already
|
|
81
|
+
* completed and the artifact written, so a failed status flip is logged rather
|
|
82
|
+
* than turned into a tool error the caller would read as "the conclude did not
|
|
83
|
+
* happen".
|
|
84
|
+
*/
|
|
85
|
+
async function concludeNegotiation(database, input) {
|
|
86
|
+
await database.updateTaskState(input.taskId, 'completed');
|
|
87
|
+
await database.createArtifact({
|
|
88
|
+
taskId: input.taskId,
|
|
89
|
+
name: 'negotiation-outcome',
|
|
90
|
+
parts: [{ kind: 'data', data: input.outcome }],
|
|
91
|
+
metadata: { hasOpportunity: input.outcome.hasOpportunity, turnCount: input.turnCount },
|
|
92
|
+
});
|
|
93
|
+
if (!input.opportunityId)
|
|
94
|
+
return;
|
|
95
|
+
const status = terminalOpportunityStatus(input.lastAction);
|
|
96
|
+
try {
|
|
97
|
+
await database.updateOpportunityStatus(input.opportunityId, status);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
logger.error('Failed to update opportunity status on MCP conclude', {
|
|
101
|
+
taskId: input.taskId,
|
|
102
|
+
opportunityId: input.opportunityId,
|
|
103
|
+
status,
|
|
104
|
+
err,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
56
108
|
/** Extracts the ordered NegotiationTurn list from A2A message data parts. */
|
|
57
109
|
function turnsFromMessages(messages) {
|
|
58
110
|
return messages
|
|
@@ -437,12 +489,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
437
489
|
const history = turnsFromMessages(allMessages);
|
|
438
490
|
const nextSpeaker = currentSpeaker === 'source' ? 'candidate' : 'source';
|
|
439
491
|
const outcome = buildNegotiationOutcome(history, newTurnCount, query.action, meta.sourceUserId, meta.candidateUserId, nextSpeaker);
|
|
440
|
-
await negotiationDatabase
|
|
441
|
-
await negotiationDatabase.createArtifact({
|
|
492
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
442
493
|
taskId: task.id,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
494
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
495
|
+
lastAction: query.action,
|
|
496
|
+
outcome,
|
|
497
|
+
turnCount: newTurnCount,
|
|
446
498
|
});
|
|
447
499
|
return success({
|
|
448
500
|
message: query.action === 'accept'
|
|
@@ -465,12 +517,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
465
517
|
const history = turnsFromMessages(allMessages);
|
|
466
518
|
const nextSpeakerForCap = currentSpeaker === 'source' ? 'candidate' : 'source';
|
|
467
519
|
const outcome = buildNegotiationOutcome(history, newTurnCount, 'counter', meta.sourceUserId, meta.candidateUserId, nextSpeakerForCap);
|
|
468
|
-
await negotiationDatabase
|
|
469
|
-
await negotiationDatabase.createArtifact({
|
|
520
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
470
521
|
taskId: task.id,
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
522
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
523
|
+
lastAction: 'counter',
|
|
524
|
+
outcome,
|
|
525
|
+
turnCount: newTurnCount,
|
|
474
526
|
});
|
|
475
527
|
return success({
|
|
476
528
|
message: 'Maximum turns reached. Negotiation finalized without opportunity.',
|
|
@@ -586,12 +638,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
586
638
|
if (isTerminalAction(aiTurn.action)) {
|
|
587
639
|
const fullHistory = [...historyForDispatch, aiTurn];
|
|
588
640
|
const outcome = buildNegotiationOutcome(fullHistory, finalTurnCount, aiTurn.action, meta.sourceUserId, meta.candidateUserId, counterpartySpeaker === 'source' ? 'candidate' : 'source');
|
|
589
|
-
await negotiationDatabase
|
|
590
|
-
await negotiationDatabase.createArtifact({
|
|
641
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
591
642
|
taskId: task.id,
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
643
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
644
|
+
lastAction: aiTurn.action,
|
|
645
|
+
outcome,
|
|
646
|
+
turnCount: finalTurnCount,
|
|
595
647
|
});
|
|
596
648
|
return success({
|
|
597
649
|
message: `${query.action === 'question' ? 'Question' : query.action === 'propose' ? 'Proposal' : 'Counter'} submitted. Counterparty responded with ${aiTurn.action}.`,
|
|
@@ -606,12 +658,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
606
658
|
if (isNegotiationTurnCapReached(finalTurnCount, meta.maxTurns)) {
|
|
607
659
|
const fullHistory = [...historyForDispatch, aiTurn];
|
|
608
660
|
const outcome = buildNegotiationOutcome(fullHistory, finalTurnCount, 'counter', meta.sourceUserId, meta.candidateUserId, counterpartySpeaker === 'source' ? 'candidate' : 'source');
|
|
609
|
-
await negotiationDatabase
|
|
610
|
-
await negotiationDatabase.createArtifact({
|
|
661
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
611
662
|
taskId: task.id,
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
663
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
664
|
+
lastAction: 'counter',
|
|
665
|
+
outcome,
|
|
666
|
+
turnCount: finalTurnCount,
|
|
615
667
|
});
|
|
616
668
|
return success({
|
|
617
669
|
message: 'Counterparty responded but max turns reached. Negotiation finalized.',
|
|
@@ -683,12 +735,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
683
735
|
const fullHistory = [...historyForDispatch, aiTurn, userAgentTurn];
|
|
684
736
|
const userSpeaker = isSource ? 'source' : 'candidate';
|
|
685
737
|
const outcome = buildNegotiationOutcome(fullHistory, userTurnCount, userAgentTurn.action, meta.sourceUserId, meta.candidateUserId, userSpeaker === 'source' ? 'candidate' : 'source');
|
|
686
|
-
await negotiationDatabase
|
|
687
|
-
await negotiationDatabase.createArtifact({
|
|
738
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
688
739
|
taskId: task.id,
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
740
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
741
|
+
lastAction: userAgentTurn.action,
|
|
742
|
+
outcome,
|
|
743
|
+
turnCount: userTurnCount,
|
|
692
744
|
});
|
|
693
745
|
return success({
|
|
694
746
|
message: `Your agent ${userAgentTurn.action}ed the counterparty's response.`,
|
|
@@ -702,12 +754,12 @@ export function createNegotiationTools(defineTool, deps) {
|
|
|
702
754
|
if (isNegotiationTurnCapReached(userTurnCount, meta.maxTurns)) {
|
|
703
755
|
const fullHistory = [...historyForDispatch, aiTurn, userAgentTurn];
|
|
704
756
|
const outcome = buildNegotiationOutcome(fullHistory, userTurnCount, 'counter', meta.sourceUserId, meta.candidateUserId, isSource ? 'candidate' : 'source');
|
|
705
|
-
await negotiationDatabase
|
|
706
|
-
await negotiationDatabase.createArtifact({
|
|
757
|
+
await concludeNegotiation(negotiationDatabase, {
|
|
707
758
|
taskId: task.id,
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
759
|
+
...(meta.opportunityId ? { opportunityId: meta.opportunityId } : {}),
|
|
760
|
+
lastAction: 'counter',
|
|
761
|
+
outcome,
|
|
762
|
+
turnCount: userTurnCount,
|
|
711
763
|
});
|
|
712
764
|
return success({
|
|
713
765
|
message: 'Your agent responded but max turns reached. Negotiation finalized.',
|