@sharpee/character 0.9.106 → 0.9.107
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/apply.d.ts +29 -7
- package/apply.d.ts.map +1 -1
- package/apply.js +15 -7
- package/apply.js.map +1 -1
- package/character-builder.d.ts +67 -4
- package/character-builder.d.ts.map +1 -1
- package/character-builder.js +116 -7
- package/character-builder.js.map +1 -1
- package/conversation/acl.d.ts +62 -0
- package/conversation/acl.d.ts.map +1 -0
- package/conversation/acl.js +117 -0
- package/conversation/acl.js.map +1 -0
- package/conversation/builder.d.ts +247 -0
- package/conversation/builder.d.ts.map +1 -0
- package/conversation/builder.js +324 -0
- package/conversation/builder.js.map +1 -0
- package/conversation/constraint-evaluator.d.ts +147 -0
- package/conversation/constraint-evaluator.d.ts.map +1 -0
- package/conversation/constraint-evaluator.js +247 -0
- package/conversation/constraint-evaluator.js.map +1 -0
- package/conversation/conversation-messages.d.ts +56 -0
- package/conversation/conversation-messages.d.ts.map +1 -0
- package/conversation/conversation-messages.js +69 -0
- package/conversation/conversation-messages.js.map +1 -0
- package/conversation/dialogue-extension.d.ts +87 -0
- package/conversation/dialogue-extension.d.ts.map +1 -0
- package/conversation/dialogue-extension.js +302 -0
- package/conversation/dialogue-extension.js.map +1 -0
- package/conversation/dialogue-types.d.ts +71 -0
- package/conversation/dialogue-types.d.ts.map +1 -0
- package/conversation/dialogue-types.js +13 -0
- package/conversation/dialogue-types.js.map +1 -0
- package/conversation/index.d.ts +19 -0
- package/conversation/index.d.ts.map +1 -0
- package/conversation/index.js +37 -0
- package/conversation/index.js.map +1 -0
- package/conversation/lifecycle.d.ts +212 -0
- package/conversation/lifecycle.d.ts.map +1 -0
- package/conversation/lifecycle.js +348 -0
- package/conversation/lifecycle.js.map +1 -0
- package/conversation/response-types.d.ts +98 -0
- package/conversation/response-types.d.ts.map +1 -0
- package/conversation/response-types.js +30 -0
- package/conversation/response-types.js.map +1 -0
- package/conversation/topic-registry.d.ts +130 -0
- package/conversation/topic-registry.d.ts.map +1 -0
- package/conversation/topic-registry.js +187 -0
- package/conversation/topic-registry.js.map +1 -0
- package/goals/builder.d.ts +135 -0
- package/goals/builder.d.ts.map +1 -0
- package/goals/builder.js +193 -0
- package/goals/builder.js.map +1 -0
- package/goals/goal-activation.d.ts +99 -0
- package/goals/goal-activation.d.ts.map +1 -0
- package/goals/goal-activation.js +212 -0
- package/goals/goal-activation.js.map +1 -0
- package/goals/goal-types.d.ts +138 -0
- package/goals/goal-types.d.ts.map +1 -0
- package/goals/goal-types.js +20 -0
- package/goals/goal-types.js.map +1 -0
- package/goals/index.d.ts +15 -0
- package/goals/index.d.ts.map +1 -0
- package/goals/index.js +24 -0
- package/goals/index.js.map +1 -0
- package/goals/pathfinding.d.ts +67 -0
- package/goals/pathfinding.d.ts.map +1 -0
- package/goals/pathfinding.js +134 -0
- package/goals/pathfinding.js.map +1 -0
- package/goals/step-evaluator.d.ts +52 -0
- package/goals/step-evaluator.d.ts.map +1 -0
- package/goals/step-evaluator.js +153 -0
- package/goals/step-evaluator.js.map +1 -0
- package/index.d.ts +6 -1
- package/index.d.ts.map +1 -1
- package/index.js +60 -1
- package/index.js.map +1 -1
- package/influence/builder.d.ts +136 -0
- package/influence/builder.d.ts.map +1 -0
- package/influence/builder.js +198 -0
- package/influence/builder.js.map +1 -0
- package/influence/index.d.ts +16 -0
- package/influence/index.d.ts.map +1 -0
- package/influence/index.js +25 -0
- package/influence/index.js.map +1 -0
- package/influence/influence-duration.d.ts +82 -0
- package/influence/influence-duration.d.ts.map +1 -0
- package/influence/influence-duration.js +148 -0
- package/influence/influence-duration.js.map +1 -0
- package/influence/influence-evaluator.d.ts +55 -0
- package/influence/influence-evaluator.d.ts.map +1 -0
- package/influence/influence-evaluator.js +183 -0
- package/influence/influence-evaluator.js.map +1 -0
- package/influence/influence-messages.d.ts +30 -0
- package/influence/influence-messages.d.ts.map +1 -0
- package/influence/influence-messages.js +40 -0
- package/influence/influence-messages.js.map +1 -0
- package/influence/influence-types.d.ts +123 -0
- package/influence/influence-types.d.ts.map +1 -0
- package/influence/influence-types.js +12 -0
- package/influence/influence-types.js.map +1 -0
- package/influence/pc-influence.d.ts +38 -0
- package/influence/pc-influence.d.ts.map +1 -0
- package/influence/pc-influence.js +56 -0
- package/influence/pc-influence.js.map +1 -0
- package/package.json +4 -3
- package/propagation/builder.d.ts +47 -0
- package/propagation/builder.d.ts.map +1 -0
- package/propagation/builder.js +46 -0
- package/propagation/builder.js.map +1 -0
- package/propagation/fact-transfer.d.ts +47 -0
- package/propagation/fact-transfer.d.ts.map +1 -0
- package/propagation/fact-transfer.js +76 -0
- package/propagation/fact-transfer.js.map +1 -0
- package/propagation/index.d.ts +16 -0
- package/propagation/index.d.ts.map +1 -0
- package/propagation/index.js +28 -0
- package/propagation/index.js.map +1 -0
- package/propagation/propagation-evaluator.d.ts +53 -0
- package/propagation/propagation-evaluator.d.ts.map +1 -0
- package/propagation/propagation-evaluator.js +231 -0
- package/propagation/propagation-evaluator.js.map +1 -0
- package/propagation/propagation-messages.d.ts +30 -0
- package/propagation/propagation-messages.d.ts.map +1 -0
- package/propagation/propagation-messages.js +37 -0
- package/propagation/propagation-messages.js.map +1 -0
- package/propagation/propagation-types.d.ts +100 -0
- package/propagation/propagation-types.d.ts.map +1 -0
- package/propagation/propagation-types.js +68 -0
- package/propagation/propagation-types.js.map +1 -0
- package/propagation/visibility.d.ts +46 -0
- package/propagation/visibility.d.ts.map +1 -0
- package/propagation/visibility.js +79 -0
- package/propagation/visibility.js.map +1 -0
- package/tick-phases.d.ts +93 -0
- package/tick-phases.d.ts.map +1 -0
- package/tick-phases.js +433 -0
- package/tick-phases.js.map +1 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation lifecycle and attention management (ADR-142)
|
|
3
|
+
*
|
|
4
|
+
* A conversation is an active state that persists across non-conversation
|
|
5
|
+
* actions and competes for the player's attention. NPC intent and strength
|
|
6
|
+
* drive between-turn commentary and determine how aggressively the NPC
|
|
7
|
+
* holds the player's focus.
|
|
8
|
+
*
|
|
9
|
+
* Public interface: ConversationIntent, ConversationStrength, ConversationContext,
|
|
10
|
+
* ContinuationEntry, InitiativeTrigger, ConversationLifecycle.
|
|
11
|
+
* Owner context: @sharpee/character / conversation
|
|
12
|
+
*/
|
|
13
|
+
/** How the NPC feels about continuing the conversation. */
|
|
14
|
+
export type ConversationIntent = 'eager' | 'reluctant' | 'hostile' | 'confessing' | 'neutral';
|
|
15
|
+
/** How aggressively the NPC holds the player's attention. */
|
|
16
|
+
export type ConversationStrength = 'passive' | 'assertive' | 'blocking';
|
|
17
|
+
/** Result of attempting to redirect attention away from the current NPC. */
|
|
18
|
+
export type RedirectResult = 'yields' | 'protests' | 'blocks';
|
|
19
|
+
/**
|
|
20
|
+
* Default number of non-conversation turns before a conversation decays,
|
|
21
|
+
* keyed by intent. Authors can override per conversation context.
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_DECAY_THRESHOLDS: Record<ConversationIntent, number>;
|
|
24
|
+
/** A scheduled NPC continuation message within a conversation context. */
|
|
25
|
+
export interface ContinuationEntry {
|
|
26
|
+
/** Turns after context was set before this continuation fires. */
|
|
27
|
+
afterTurns: number;
|
|
28
|
+
/** Message ID for the continuation. */
|
|
29
|
+
messageId: string;
|
|
30
|
+
}
|
|
31
|
+
/** An NPC initiative trigger — the NPC starts a conversation proactively. */
|
|
32
|
+
export interface InitiativeTrigger {
|
|
33
|
+
/** Predicate conditions that must all be satisfied. */
|
|
34
|
+
conditions: string[];
|
|
35
|
+
/** Message ID when the NPC initiates. */
|
|
36
|
+
messageId: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Platform default between-turn commentary message IDs.
|
|
40
|
+
* Keyed by `${intent}.${turnBucket}` where turnBucket is '1', '3+', or 'decay'.
|
|
41
|
+
* Authors override per conversation context for character-specific flavor.
|
|
42
|
+
*/
|
|
43
|
+
export declare const BETWEEN_TURN_DEFAULTS: Record<string, string>;
|
|
44
|
+
/** The persistent state of an active conversation. */
|
|
45
|
+
export interface ConversationContext {
|
|
46
|
+
/** Entity ID of the NPC in this conversation. */
|
|
47
|
+
npcId: string;
|
|
48
|
+
/** Current conversation intent. */
|
|
49
|
+
intent: ConversationIntent;
|
|
50
|
+
/** Current conversation strength. */
|
|
51
|
+
strength: ConversationStrength;
|
|
52
|
+
/** Decay threshold (non-conversation turns before conversation ends). */
|
|
53
|
+
decayThreshold: number;
|
|
54
|
+
/** Number of non-conversation turns elapsed since last conversation action. */
|
|
55
|
+
nonConversationTurns: number;
|
|
56
|
+
/** Optional context label (e.g., 'confessing', 'caught'). */
|
|
57
|
+
contextLabel?: string;
|
|
58
|
+
/** Scheduled continuation messages within this context. */
|
|
59
|
+
continuations: ContinuationEntry[];
|
|
60
|
+
/** Author-overridden between-turn messages. Keyed by turn number. */
|
|
61
|
+
betweenTurnOverrides: Map<number, string>;
|
|
62
|
+
/** Author-overridden leave-attempt message. */
|
|
63
|
+
onLeaveAttemptMessage?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Manages the lifecycle of an active conversation between the player
|
|
67
|
+
* and a single NPC. Tracks intent, strength, decay, attention shifts,
|
|
68
|
+
* NPC continuation scheduling, and NPC initiative triggers.
|
|
69
|
+
*
|
|
70
|
+
* One instance per game session. The active conversation is singular —
|
|
71
|
+
* the player can only be in one conversation at a time.
|
|
72
|
+
*/
|
|
73
|
+
export declare class ConversationLifecycle {
|
|
74
|
+
/** The currently active conversation, or null if none. */
|
|
75
|
+
private context;
|
|
76
|
+
/** Registered NPC initiative triggers. Keyed by NPC entity ID. */
|
|
77
|
+
private readonly initiativeTriggers;
|
|
78
|
+
/**
|
|
79
|
+
* Begin a conversation with an NPC.
|
|
80
|
+
* If a conversation is already active, it is ended first.
|
|
81
|
+
*
|
|
82
|
+
* @param npcId - The NPC entity ID
|
|
83
|
+
* @param intent - The NPC's conversation intent
|
|
84
|
+
* @param strength - The NPC's conversation strength
|
|
85
|
+
*/
|
|
86
|
+
begin(npcId: string, intent?: ConversationIntent, strength?: ConversationStrength): void;
|
|
87
|
+
/**
|
|
88
|
+
* End the current conversation.
|
|
89
|
+
* No-op if no conversation is active.
|
|
90
|
+
*/
|
|
91
|
+
end(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Update the conversation context mid-conversation.
|
|
94
|
+
* Used when an NPC response changes the conversation's tone
|
|
95
|
+
* (e.g., a confess response shifts intent to 'eager' and strength to 'assertive').
|
|
96
|
+
*
|
|
97
|
+
* @param label - Context label
|
|
98
|
+
* @param intent - New intent (or keep current)
|
|
99
|
+
* @param strength - New strength (or keep current)
|
|
100
|
+
* @param decayThreshold - New decay threshold (or derive from intent)
|
|
101
|
+
*/
|
|
102
|
+
setContext(label: string, intent?: ConversationIntent, strength?: ConversationStrength, decayThreshold?: number): void;
|
|
103
|
+
/** Whether a conversation is currently active. */
|
|
104
|
+
isActive(): boolean;
|
|
105
|
+
/** Whether the active conversation is blocking. */
|
|
106
|
+
isBlocking(): boolean;
|
|
107
|
+
/** Get the active conversation context, or null. */
|
|
108
|
+
getContext(): Readonly<ConversationContext> | null;
|
|
109
|
+
/** Get the NPC ID of the active conversation, or null. */
|
|
110
|
+
getActiveNpcId(): string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Record that a non-conversation turn occurred.
|
|
113
|
+
* Increments the decay counter and returns whether the conversation
|
|
114
|
+
* should end (decay threshold reached).
|
|
115
|
+
*
|
|
116
|
+
* @returns True if the conversation decayed and ended
|
|
117
|
+
*/
|
|
118
|
+
recordNonConversationTurn(): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Get the between-turn commentary message ID for the current state.
|
|
121
|
+
* Returns author override if set, otherwise the platform default.
|
|
122
|
+
*
|
|
123
|
+
* @returns Message ID for between-turn commentary, or undefined if no conversation
|
|
124
|
+
*/
|
|
125
|
+
getBetweenTurnMessage(): string | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* Register an author-overridden between-turn message for the active context.
|
|
128
|
+
*
|
|
129
|
+
* @param turnNumber - Which non-conversation turn this fires on
|
|
130
|
+
* @param messageId - The message ID
|
|
131
|
+
*/
|
|
132
|
+
setBetweenTurnOverride(turnNumber: number, messageId: string): void;
|
|
133
|
+
/**
|
|
134
|
+
* Set the leave-attempt message for a blocking conversation.
|
|
135
|
+
*
|
|
136
|
+
* @param messageId - The message ID when the player tries to leave
|
|
137
|
+
*/
|
|
138
|
+
setOnLeaveAttemptMessage(messageId: string): void;
|
|
139
|
+
/**
|
|
140
|
+
* Get the leave-attempt message ID, if any.
|
|
141
|
+
*
|
|
142
|
+
* @returns Message ID, or undefined
|
|
143
|
+
*/
|
|
144
|
+
getOnLeaveAttemptMessage(): string | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Attempt to redirect the player's attention to a different NPC.
|
|
147
|
+
* The result depends on the current conversation's strength:
|
|
148
|
+
* - passive: yields immediately
|
|
149
|
+
* - assertive: protests but yields (conversation ends)
|
|
150
|
+
* - blocking: blocks the redirect (conversation stays active)
|
|
151
|
+
*
|
|
152
|
+
* @param _toNpcId - The NPC the player is trying to talk to
|
|
153
|
+
* @returns The result of the redirect attempt
|
|
154
|
+
*/
|
|
155
|
+
attemptRedirect(_toNpcId: string): RedirectResult;
|
|
156
|
+
/**
|
|
157
|
+
* Attempt to leave the room during an active conversation.
|
|
158
|
+
* Same strength-based rules as redirect.
|
|
159
|
+
*
|
|
160
|
+
* @returns The result of the leave attempt
|
|
161
|
+
*/
|
|
162
|
+
attemptLeave(): RedirectResult;
|
|
163
|
+
/**
|
|
164
|
+
* Resolve an attention challenge (redirect or leave) against conversation strength.
|
|
165
|
+
* Passive and assertive conversations end; blocking conversations persist.
|
|
166
|
+
*
|
|
167
|
+
* @returns The redirect result based on current conversation strength
|
|
168
|
+
*/
|
|
169
|
+
private resolveStrengthCheck;
|
|
170
|
+
/**
|
|
171
|
+
* Schedule a continuation message for N turns after context was set.
|
|
172
|
+
*
|
|
173
|
+
* @param afterTurns - Number of turns after context was set
|
|
174
|
+
* @param messageId - The message ID
|
|
175
|
+
*/
|
|
176
|
+
scheduleAfter(afterTurns: number, messageId: string): void;
|
|
177
|
+
/**
|
|
178
|
+
* Get the continuation message for the current turn count, if any.
|
|
179
|
+
* Continuations fire based on non-conversation turns elapsed.
|
|
180
|
+
*
|
|
181
|
+
* @returns Message ID if a continuation is scheduled for this turn, or undefined
|
|
182
|
+
*/
|
|
183
|
+
getContinuationMessage(): string | undefined;
|
|
184
|
+
/**
|
|
185
|
+
* Register an initiative trigger for an NPC.
|
|
186
|
+
* The NPC will initiate conversation when conditions are met.
|
|
187
|
+
*
|
|
188
|
+
* @param npcId - The NPC entity ID
|
|
189
|
+
* @param conditions - Predicate conditions that must be satisfied
|
|
190
|
+
* @param messageId - The message ID when the NPC initiates
|
|
191
|
+
*/
|
|
192
|
+
registerInitiative(npcId: string, conditions: string[], messageId: string): void;
|
|
193
|
+
/**
|
|
194
|
+
* Get initiative triggers for an NPC.
|
|
195
|
+
*
|
|
196
|
+
* @param npcId - The NPC entity ID
|
|
197
|
+
* @returns Array of initiative triggers, or empty array
|
|
198
|
+
*/
|
|
199
|
+
getInitiativeTriggers(npcId: string): InitiativeTrigger[];
|
|
200
|
+
/** Export lifecycle state for save/restore. */
|
|
201
|
+
toJSON(): ConversationLifecycleState;
|
|
202
|
+
/** Restore lifecycle state from serialized data. */
|
|
203
|
+
static fromJSON(state: ConversationLifecycleState): ConversationLifecycle;
|
|
204
|
+
}
|
|
205
|
+
/** Serialized lifecycle state. */
|
|
206
|
+
export interface ConversationLifecycleState {
|
|
207
|
+
context: (Omit<ConversationContext, 'betweenTurnOverrides'> & {
|
|
208
|
+
betweenTurnOverrides: Record<string, string>;
|
|
209
|
+
}) | null;
|
|
210
|
+
initiativeTriggers: Record<string, InitiativeTrigger[]>;
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../repos/sharpee/packages/character/src/conversation/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Conversation lifecycle and attention management (ADR-142)
|
|
4
|
+
*
|
|
5
|
+
* A conversation is an active state that persists across non-conversation
|
|
6
|
+
* actions and competes for the player's attention. NPC intent and strength
|
|
7
|
+
* drive between-turn commentary and determine how aggressively the NPC
|
|
8
|
+
* holds the player's focus.
|
|
9
|
+
*
|
|
10
|
+
* Public interface: ConversationIntent, ConversationStrength, ConversationContext,
|
|
11
|
+
* ContinuationEntry, InitiativeTrigger, ConversationLifecycle.
|
|
12
|
+
* Owner context: @sharpee/character / conversation
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ConversationLifecycle = exports.BETWEEN_TURN_DEFAULTS = exports.DEFAULT_DECAY_THRESHOLDS = void 0;
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Default decay thresholds per intent
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Default number of non-conversation turns before a conversation decays,
|
|
21
|
+
* keyed by intent. Authors can override per conversation context.
|
|
22
|
+
*/
|
|
23
|
+
exports.DEFAULT_DECAY_THRESHOLDS = {
|
|
24
|
+
eager: 5,
|
|
25
|
+
reluctant: 2,
|
|
26
|
+
hostile: 3,
|
|
27
|
+
confessing: 6,
|
|
28
|
+
neutral: 4,
|
|
29
|
+
};
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Between-turn defaults
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Platform default between-turn commentary message IDs.
|
|
35
|
+
* Keyed by `${intent}.${turnBucket}` where turnBucket is '1', '3+', or 'decay'.
|
|
36
|
+
* Authors override per conversation context for character-specific flavor.
|
|
37
|
+
*/
|
|
38
|
+
exports.BETWEEN_TURN_DEFAULTS = {
|
|
39
|
+
// Eager
|
|
40
|
+
'eager.1': 'character.conversation.eager.watches',
|
|
41
|
+
'eager.3+': 'character.conversation.eager.tugs',
|
|
42
|
+
'eager.decay': 'character.conversation.eager.sighs',
|
|
43
|
+
// Reluctant
|
|
44
|
+
'reluctant.1': 'character.conversation.reluctant.silence',
|
|
45
|
+
'reluctant.3+': 'character.conversation.reluctant.ends',
|
|
46
|
+
'reluctant.decay': 'character.conversation.reluctant.relief',
|
|
47
|
+
// Hostile
|
|
48
|
+
'hostile.1': 'character.conversation.hostile.glares',
|
|
49
|
+
'hostile.3+': 'character.conversation.hostile.turns-away',
|
|
50
|
+
'hostile.decay': 'character.conversation.hostile.dismisses',
|
|
51
|
+
// Confessing
|
|
52
|
+
'confessing.1': 'character.conversation.confessing.pauses',
|
|
53
|
+
'confessing.3+': 'character.conversation.confessing.whispers',
|
|
54
|
+
'confessing.decay': 'character.conversation.confessing.trails-off',
|
|
55
|
+
// Neutral
|
|
56
|
+
'neutral.1': 'character.conversation.neutral.silence',
|
|
57
|
+
'neutral.3+': 'character.conversation.neutral.waits',
|
|
58
|
+
'neutral.decay': 'character.conversation.neutral.drifts',
|
|
59
|
+
};
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Conversation lifecycle
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Manages the lifecycle of an active conversation between the player
|
|
65
|
+
* and a single NPC. Tracks intent, strength, decay, attention shifts,
|
|
66
|
+
* NPC continuation scheduling, and NPC initiative triggers.
|
|
67
|
+
*
|
|
68
|
+
* One instance per game session. The active conversation is singular —
|
|
69
|
+
* the player can only be in one conversation at a time.
|
|
70
|
+
*/
|
|
71
|
+
class ConversationLifecycle {
|
|
72
|
+
/** The currently active conversation, or null if none. */
|
|
73
|
+
context = null;
|
|
74
|
+
/** Registered NPC initiative triggers. Keyed by NPC entity ID. */
|
|
75
|
+
initiativeTriggers = new Map();
|
|
76
|
+
// =========================================================================
|
|
77
|
+
// Lifecycle transitions
|
|
78
|
+
// =========================================================================
|
|
79
|
+
/**
|
|
80
|
+
* Begin a conversation with an NPC.
|
|
81
|
+
* If a conversation is already active, it is ended first.
|
|
82
|
+
*
|
|
83
|
+
* @param npcId - The NPC entity ID
|
|
84
|
+
* @param intent - The NPC's conversation intent
|
|
85
|
+
* @param strength - The NPC's conversation strength
|
|
86
|
+
*/
|
|
87
|
+
begin(npcId, intent = 'neutral', strength = 'passive') {
|
|
88
|
+
this.context = {
|
|
89
|
+
npcId,
|
|
90
|
+
intent,
|
|
91
|
+
strength,
|
|
92
|
+
decayThreshold: exports.DEFAULT_DECAY_THRESHOLDS[intent],
|
|
93
|
+
nonConversationTurns: 0,
|
|
94
|
+
continuations: [],
|
|
95
|
+
betweenTurnOverrides: new Map(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* End the current conversation.
|
|
100
|
+
* No-op if no conversation is active.
|
|
101
|
+
*/
|
|
102
|
+
end() {
|
|
103
|
+
this.context = null;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update the conversation context mid-conversation.
|
|
107
|
+
* Used when an NPC response changes the conversation's tone
|
|
108
|
+
* (e.g., a confess response shifts intent to 'eager' and strength to 'assertive').
|
|
109
|
+
*
|
|
110
|
+
* @param label - Context label
|
|
111
|
+
* @param intent - New intent (or keep current)
|
|
112
|
+
* @param strength - New strength (or keep current)
|
|
113
|
+
* @param decayThreshold - New decay threshold (or derive from intent)
|
|
114
|
+
*/
|
|
115
|
+
setContext(label, intent, strength, decayThreshold) {
|
|
116
|
+
if (!this.context)
|
|
117
|
+
return;
|
|
118
|
+
this.context.contextLabel = label;
|
|
119
|
+
if (intent !== undefined) {
|
|
120
|
+
this.context.intent = intent;
|
|
121
|
+
// Reset decay threshold to match new intent unless explicitly overridden
|
|
122
|
+
this.context.decayThreshold = decayThreshold ?? exports.DEFAULT_DECAY_THRESHOLDS[intent];
|
|
123
|
+
}
|
|
124
|
+
else if (decayThreshold !== undefined) {
|
|
125
|
+
this.context.decayThreshold = decayThreshold;
|
|
126
|
+
}
|
|
127
|
+
if (strength !== undefined) {
|
|
128
|
+
this.context.strength = strength;
|
|
129
|
+
}
|
|
130
|
+
// Reset non-conversation turn counter on context change
|
|
131
|
+
this.context.nonConversationTurns = 0;
|
|
132
|
+
}
|
|
133
|
+
// =========================================================================
|
|
134
|
+
// State queries
|
|
135
|
+
// =========================================================================
|
|
136
|
+
/** Whether a conversation is currently active. */
|
|
137
|
+
isActive() {
|
|
138
|
+
return this.context !== null;
|
|
139
|
+
}
|
|
140
|
+
/** Whether the active conversation is blocking. */
|
|
141
|
+
isBlocking() {
|
|
142
|
+
return this.context?.strength === 'blocking';
|
|
143
|
+
}
|
|
144
|
+
/** Get the active conversation context, or null. */
|
|
145
|
+
getContext() {
|
|
146
|
+
return this.context;
|
|
147
|
+
}
|
|
148
|
+
/** Get the NPC ID of the active conversation, or null. */
|
|
149
|
+
getActiveNpcId() {
|
|
150
|
+
return this.context?.npcId ?? null;
|
|
151
|
+
}
|
|
152
|
+
// =========================================================================
|
|
153
|
+
// Non-conversation turn tracking and decay
|
|
154
|
+
// =========================================================================
|
|
155
|
+
/**
|
|
156
|
+
* Record that a non-conversation turn occurred.
|
|
157
|
+
* Increments the decay counter and returns whether the conversation
|
|
158
|
+
* should end (decay threshold reached).
|
|
159
|
+
*
|
|
160
|
+
* @returns True if the conversation decayed and ended
|
|
161
|
+
*/
|
|
162
|
+
recordNonConversationTurn() {
|
|
163
|
+
if (!this.context)
|
|
164
|
+
return false;
|
|
165
|
+
this.context.nonConversationTurns++;
|
|
166
|
+
if (this.context.nonConversationTurns >= this.context.decayThreshold) {
|
|
167
|
+
this.end();
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get the between-turn commentary message ID for the current state.
|
|
174
|
+
* Returns author override if set, otherwise the platform default.
|
|
175
|
+
*
|
|
176
|
+
* @returns Message ID for between-turn commentary, or undefined if no conversation
|
|
177
|
+
*/
|
|
178
|
+
getBetweenTurnMessage() {
|
|
179
|
+
if (!this.context)
|
|
180
|
+
return undefined;
|
|
181
|
+
const turns = this.context.nonConversationTurns;
|
|
182
|
+
// Check author overrides first
|
|
183
|
+
const override = this.context.betweenTurnOverrides.get(turns);
|
|
184
|
+
if (override)
|
|
185
|
+
return override;
|
|
186
|
+
// Platform defaults — bucket into '1', '3+', or 'decay'
|
|
187
|
+
const bucket = turns >= 3 ? '3+' : String(turns);
|
|
188
|
+
const key = `${this.context.intent}.${bucket}`;
|
|
189
|
+
return exports.BETWEEN_TURN_DEFAULTS[key];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Register an author-overridden between-turn message for the active context.
|
|
193
|
+
*
|
|
194
|
+
* @param turnNumber - Which non-conversation turn this fires on
|
|
195
|
+
* @param messageId - The message ID
|
|
196
|
+
*/
|
|
197
|
+
setBetweenTurnOverride(turnNumber, messageId) {
|
|
198
|
+
if (!this.context)
|
|
199
|
+
return;
|
|
200
|
+
this.context.betweenTurnOverrides.set(turnNumber, messageId);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Set the leave-attempt message for a blocking conversation.
|
|
204
|
+
*
|
|
205
|
+
* @param messageId - The message ID when the player tries to leave
|
|
206
|
+
*/
|
|
207
|
+
setOnLeaveAttemptMessage(messageId) {
|
|
208
|
+
if (!this.context)
|
|
209
|
+
return;
|
|
210
|
+
this.context.onLeaveAttemptMessage = messageId;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get the leave-attempt message ID, if any.
|
|
214
|
+
*
|
|
215
|
+
* @returns Message ID, or undefined
|
|
216
|
+
*/
|
|
217
|
+
getOnLeaveAttemptMessage() {
|
|
218
|
+
return this.context?.onLeaveAttemptMessage;
|
|
219
|
+
}
|
|
220
|
+
// =========================================================================
|
|
221
|
+
// Attention management
|
|
222
|
+
// =========================================================================
|
|
223
|
+
/**
|
|
224
|
+
* Attempt to redirect the player's attention to a different NPC.
|
|
225
|
+
* The result depends on the current conversation's strength:
|
|
226
|
+
* - passive: yields immediately
|
|
227
|
+
* - assertive: protests but yields (conversation ends)
|
|
228
|
+
* - blocking: blocks the redirect (conversation stays active)
|
|
229
|
+
*
|
|
230
|
+
* @param _toNpcId - The NPC the player is trying to talk to
|
|
231
|
+
* @returns The result of the redirect attempt
|
|
232
|
+
*/
|
|
233
|
+
attemptRedirect(_toNpcId) {
|
|
234
|
+
return this.resolveStrengthCheck();
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Attempt to leave the room during an active conversation.
|
|
238
|
+
* Same strength-based rules as redirect.
|
|
239
|
+
*
|
|
240
|
+
* @returns The result of the leave attempt
|
|
241
|
+
*/
|
|
242
|
+
attemptLeave() {
|
|
243
|
+
return this.resolveStrengthCheck();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Resolve an attention challenge (redirect or leave) against conversation strength.
|
|
247
|
+
* Passive and assertive conversations end; blocking conversations persist.
|
|
248
|
+
*
|
|
249
|
+
* @returns The redirect result based on current conversation strength
|
|
250
|
+
*/
|
|
251
|
+
resolveStrengthCheck() {
|
|
252
|
+
if (!this.context)
|
|
253
|
+
return 'yields';
|
|
254
|
+
switch (this.context.strength) {
|
|
255
|
+
case 'passive':
|
|
256
|
+
this.end();
|
|
257
|
+
return 'yields';
|
|
258
|
+
case 'assertive':
|
|
259
|
+
this.end();
|
|
260
|
+
return 'protests';
|
|
261
|
+
case 'blocking':
|
|
262
|
+
return 'blocks';
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// =========================================================================
|
|
266
|
+
// NPC continuation scheduling
|
|
267
|
+
// =========================================================================
|
|
268
|
+
/**
|
|
269
|
+
* Schedule a continuation message for N turns after context was set.
|
|
270
|
+
*
|
|
271
|
+
* @param afterTurns - Number of turns after context was set
|
|
272
|
+
* @param messageId - The message ID
|
|
273
|
+
*/
|
|
274
|
+
scheduleAfter(afterTurns, messageId) {
|
|
275
|
+
if (!this.context)
|
|
276
|
+
return;
|
|
277
|
+
this.context.continuations.push({ afterTurns, messageId });
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the continuation message for the current turn count, if any.
|
|
281
|
+
* Continuations fire based on non-conversation turns elapsed.
|
|
282
|
+
*
|
|
283
|
+
* @returns Message ID if a continuation is scheduled for this turn, or undefined
|
|
284
|
+
*/
|
|
285
|
+
getContinuationMessage() {
|
|
286
|
+
if (!this.context)
|
|
287
|
+
return undefined;
|
|
288
|
+
const turns = this.context.nonConversationTurns;
|
|
289
|
+
const entry = this.context.continuations.find(c => c.afterTurns === turns);
|
|
290
|
+
return entry?.messageId;
|
|
291
|
+
}
|
|
292
|
+
// =========================================================================
|
|
293
|
+
// NPC initiative triggers
|
|
294
|
+
// =========================================================================
|
|
295
|
+
/**
|
|
296
|
+
* Register an initiative trigger for an NPC.
|
|
297
|
+
* The NPC will initiate conversation when conditions are met.
|
|
298
|
+
*
|
|
299
|
+
* @param npcId - The NPC entity ID
|
|
300
|
+
* @param conditions - Predicate conditions that must be satisfied
|
|
301
|
+
* @param messageId - The message ID when the NPC initiates
|
|
302
|
+
*/
|
|
303
|
+
registerInitiative(npcId, conditions, messageId) {
|
|
304
|
+
const triggers = this.initiativeTriggers.get(npcId) ?? [];
|
|
305
|
+
triggers.push({ conditions, messageId });
|
|
306
|
+
this.initiativeTriggers.set(npcId, triggers);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get initiative triggers for an NPC.
|
|
310
|
+
*
|
|
311
|
+
* @param npcId - The NPC entity ID
|
|
312
|
+
* @returns Array of initiative triggers, or empty array
|
|
313
|
+
*/
|
|
314
|
+
getInitiativeTriggers(npcId) {
|
|
315
|
+
return this.initiativeTriggers.get(npcId) ?? [];
|
|
316
|
+
}
|
|
317
|
+
// =========================================================================
|
|
318
|
+
// Serialization
|
|
319
|
+
// =========================================================================
|
|
320
|
+
/** Export lifecycle state for save/restore. */
|
|
321
|
+
toJSON() {
|
|
322
|
+
return {
|
|
323
|
+
context: this.context
|
|
324
|
+
? {
|
|
325
|
+
...this.context,
|
|
326
|
+
betweenTurnOverrides: Object.fromEntries(this.context.betweenTurnOverrides),
|
|
327
|
+
}
|
|
328
|
+
: null,
|
|
329
|
+
initiativeTriggers: Object.fromEntries(Array.from(this.initiativeTriggers.entries()).map(([npcId, triggers]) => [npcId, triggers])),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
/** Restore lifecycle state from serialized data. */
|
|
333
|
+
static fromJSON(state) {
|
|
334
|
+
const lifecycle = new ConversationLifecycle();
|
|
335
|
+
if (state.context) {
|
|
336
|
+
lifecycle.context = {
|
|
337
|
+
...state.context,
|
|
338
|
+
betweenTurnOverrides: new Map(Object.entries(state.context.betweenTurnOverrides).map(([k, v]) => [Number(k), v])),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
for (const [npcId, triggers] of Object.entries(state.initiativeTriggers)) {
|
|
342
|
+
lifecycle.initiativeTriggers.set(npcId, triggers);
|
|
343
|
+
}
|
|
344
|
+
return lifecycle;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
exports.ConversationLifecycle = ConversationLifecycle;
|
|
348
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../../../../../repos/sharpee/packages/character/src/conversation/lifecycle.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAuBH,8EAA8E;AAC9E,sCAAsC;AACtC,8EAA8E;AAE9E;;;GAGG;AACU,QAAA,wBAAwB,GAAuC;IAC1E,KAAK,EAAE,CAAC;IACR,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,CAAC;CACX,CAAC;AAwBF,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;GAIG;AACU,QAAA,qBAAqB,GAA2B;IAC3D,QAAQ;IACR,SAAS,EAAM,sCAAsC;IACrD,UAAU,EAAK,mCAAmC;IAClD,aAAa,EAAE,oCAAoC;IAEnD,YAAY;IACZ,aAAa,EAAM,0CAA0C;IAC7D,cAAc,EAAK,uCAAuC;IAC1D,iBAAiB,EAAE,yCAAyC;IAE5D,UAAU;IACV,WAAW,EAAM,uCAAuC;IACxD,YAAY,EAAK,2CAA2C;IAC5D,eAAe,EAAE,0CAA0C;IAE3D,aAAa;IACb,cAAc,EAAM,0CAA0C;IAC9D,eAAe,EAAK,4CAA4C;IAChE,kBAAkB,EAAE,8CAA8C;IAElE,UAAU;IACV,WAAW,EAAM,wCAAwC;IACzD,YAAY,EAAK,sCAAsC;IACvD,eAAe,EAAE,uCAAuC;CACzD,CAAC;AAoCF,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAa,qBAAqB;IAChC,0DAA0D;IAClD,OAAO,GAA+B,IAAI,CAAC;IAEnD,kEAAkE;IACjD,kBAAkB,GAAqC,IAAI,GAAG,EAAE,CAAC;IAElF,4EAA4E;IAC5E,wBAAwB;IACxB,4EAA4E;IAE5E;;;;;;;OAOG;IACH,KAAK,CACH,KAAa,EACb,SAA6B,SAAS,EACtC,WAAiC,SAAS;QAE1C,IAAI,CAAC,OAAO,GAAG;YACb,KAAK;YACL,MAAM;YACN,QAAQ;YACR,cAAc,EAAE,gCAAwB,CAAC,MAAM,CAAC;YAChD,oBAAoB,EAAE,CAAC;YACvB,aAAa,EAAE,EAAE;YACjB,oBAAoB,EAAE,IAAI,GAAG,EAAE;SAChC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,GAAG;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED;;;;;;;;;OASG;IACH,UAAU,CACR,KAAa,EACb,MAA2B,EAC3B,QAA+B,EAC/B,cAAuB;QAEvB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAE1B,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,KAAK,CAAC;QAElC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;YAC7B,yEAAyE;YACzE,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,cAAc,IAAI,gCAAwB,CAAC,MAAM,CAAC,CAAC;QACnF,CAAC;aAAM,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,cAAc,CAAC;QAC/C,CAAC;QAED,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACnC,CAAC;QAED,wDAAwD;QACxD,IAAI,CAAC,OAAO,CAAC,oBAAoB,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,4EAA4E;IAC5E,gBAAgB;IAChB,4EAA4E;IAE5E,kDAAkD;IAClD,QAAQ;QACN,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;IAC/B,CAAC;IAED,mDAAmD;IACnD,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,KAAK,UAAU,CAAC;IAC/C,CAAC;IAED,oDAAoD;IACpD,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,0DAA0D;IAC1D,cAAc;QACZ,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,4EAA4E;IAC5E,2CAA2C;IAC3C,4EAA4E;IAE5E;;;;;;OAMG;IACH,yBAAyB;QACvB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAEhC,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QAEpC,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,IAAI,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;YACrE,IAAI,CAAC,GAAG,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,qBAAqB;QACnB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAEhD,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE9B,wDAAwD;QACxD,MAAM,MAAM,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC;QAC/C,OAAO,6BAAqB,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IAED;;;;;OAKG;IACH,sBAAsB,CAAC,UAAkB,EAAE,SAAiB;QAC1D,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC/D,CAAC;IAED;;;;OAIG;IACH,wBAAwB,CAAC,SAAiB;QACxC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACH,wBAAwB;QACtB,OAAO,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC;IAC7C,CAAC;IAED,4EAA4E;IAC5E,uBAAuB;IACvB,4EAA4E;IAE5E;;;;;;;;;OASG;IACH,eAAe,CAAC,QAAgB;QAC9B,OAAO,IAAI,CAAC,oBAAoB,EAAE,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,oBAAoB,EAAE,CAAC;IACrC,CAAC;IAED;;;;;OAKG;IACK,oBAAoB;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,QAAQ,CAAC;QAEnC,QAAQ,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC9B,KAAK,SAAS;gBACZ,IAAI,CAAC,GAAG,EAAE,CAAC;gBACX,OAAO,QAAQ,CAAC;YAElB,KAAK,WAAW;gBACd,IAAI,CAAC,GAAG,EAAE,CAAC;gBACX,OAAO,UAAU,CAAC;YAEpB,KAAK,UAAU;gBACb,OAAO,QAAQ,CAAC;QACpB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,8BAA8B;IAC9B,4EAA4E;IAE5E;;;;;OAKG;IACH,aAAa,CAAC,UAAkB,EAAE,SAAiB;QACjD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED;;;;;OAKG;IACH,sBAAsB;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,KAAK,CAAC,CAAC;QAC3E,OAAO,KAAK,EAAE,SAAS,CAAC;IAC1B,CAAC;IAED,4EAA4E;IAC5E,0BAA0B;IAC1B,4EAA4E;IAE5E;;;;;;;OAOG;IACH,kBAAkB,CAAC,KAAa,EAAE,UAAoB,EAAE,SAAiB;QACvE,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC1D,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAED;;;;;OAKG;IACH,qBAAqB,CAAC,KAAa;QACjC,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;IAClD,CAAC;IAED,4EAA4E;IAC5E,gBAAgB;IAChB,4EAA4E;IAE5E,+CAA+C;IAC/C,MAAM;QACJ,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;gBACnB,CAAC,CAAC;oBACE,GAAG,IAAI,CAAC,OAAO;oBACf,oBAAoB,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC;iBAC5E;gBACH,CAAC,CAAC,IAAI;YACR,kBAAkB,EAAE,MAAM,CAAC,WAAW,CACpC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAC/C,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,CACzC,CACF;SACF,CAAC;IACJ,CAAC;IAED,oDAAoD;IACpD,MAAM,CAAC,QAAQ,CAAC,KAAiC;QAC/C,MAAM,SAAS,GAAG,IAAI,qBAAqB,EAAE,CAAC;QAE9C,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,SAAS,CAAC,OAAO,GAAG;gBAClB,GAAG,KAAK,CAAC,OAAO;gBAChB,oBAAoB,EAAE,IAAI,GAAG,CAC3B,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC,GAAG,CACpD,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAqB,CAC/C,CACF;aACF,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACzE,SAAS,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACpD,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAxUD,sDAwUC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation response types (ADR-142)
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for constraint-based response selection:
|
|
5
|
+
* ResponseAction, ResponseCandidate, ResponseIntent, and
|
|
6
|
+
* conversation/evidence tracking records.
|
|
7
|
+
*
|
|
8
|
+
* Public interface: ResponseAction, ResponseCandidate, ResponseIntent,
|
|
9
|
+
* ConversationRecord, ConversationEntry, EvidenceRecord, EvidenceEntry.
|
|
10
|
+
* Owner context: @sharpee/character / conversation
|
|
11
|
+
*/
|
|
12
|
+
import { Mood, Coherence } from "../../world-model/index";
|
|
13
|
+
/**
|
|
14
|
+
* The semantic action an NPC takes when responding to a topic.
|
|
15
|
+
* Drives both conversation history tracking and language layer variant selection.
|
|
16
|
+
*/
|
|
17
|
+
export type ResponseAction = 'tell' | 'omit' | 'lie' | 'deflect' | 'refuse' | 'ask back' | 'confess' | 'confabulate';
|
|
18
|
+
/**
|
|
19
|
+
* A single authored response option for a topic.
|
|
20
|
+
* The constraint evaluator selects among candidates using first-match-wins.
|
|
21
|
+
*/
|
|
22
|
+
export interface ResponseCandidate {
|
|
23
|
+
/** The response action type. */
|
|
24
|
+
action: ResponseAction;
|
|
25
|
+
/** Language-layer message ID for this response. */
|
|
26
|
+
messageId: string;
|
|
27
|
+
/**
|
|
28
|
+
* Predicate names that must all be satisfied for this candidate to match.
|
|
29
|
+
* Empty array means "always matches" (used for .otherwise() fallback).
|
|
30
|
+
*/
|
|
31
|
+
constraints: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Author-defined parameters resolved at render time.
|
|
34
|
+
* Keys are param names; values are resolver functions.
|
|
35
|
+
*/
|
|
36
|
+
params?: Record<string, () => unknown>;
|
|
37
|
+
/**
|
|
38
|
+
* State mutations to apply when this response is selected.
|
|
39
|
+
* Keys are state dimensions; values are target values.
|
|
40
|
+
*/
|
|
41
|
+
stateMutations?: Record<string, unknown>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The structured output of constraint evaluation, consumed by the ACL
|
|
45
|
+
* and ultimately the language layer. Contains everything needed to
|
|
46
|
+
* produce prose without coupling to the character model.
|
|
47
|
+
*/
|
|
48
|
+
export interface ResponseIntent {
|
|
49
|
+
/** Which response action was taken. */
|
|
50
|
+
action: ResponseAction;
|
|
51
|
+
/** The topic being discussed. */
|
|
52
|
+
topic: string;
|
|
53
|
+
/** Author-assigned message ID. */
|
|
54
|
+
messageId: string;
|
|
55
|
+
/** Current NPC mood (for tone selection in language layer). */
|
|
56
|
+
mood: Mood;
|
|
57
|
+
/** Current NPC coherence (for sentence structure in language layer). */
|
|
58
|
+
coherence: Coherence;
|
|
59
|
+
/** Active conversation context label, if any. */
|
|
60
|
+
context?: string;
|
|
61
|
+
/** Resolved parameter values for the language layer. */
|
|
62
|
+
params?: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
/** A single entry in the conversation record for one topic. */
|
|
65
|
+
export interface ConversationEntry {
|
|
66
|
+
/** Which response action the NPC took. */
|
|
67
|
+
action: ResponseAction;
|
|
68
|
+
/** The turn number when this response was given. */
|
|
69
|
+
turn: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Per-NPC record of which topics have been discussed and what action
|
|
73
|
+
* was taken. Keyed by topic name, stores the most recent response.
|
|
74
|
+
* Previous responses are kept in a history array for contradiction detection.
|
|
75
|
+
*/
|
|
76
|
+
export interface ConversationRecord {
|
|
77
|
+
/** Most recent response per topic. */
|
|
78
|
+
responses: Map<string, ConversationEntry>;
|
|
79
|
+
/** Full history per topic (for contradiction detection). */
|
|
80
|
+
history: Map<string, ConversationEntry[]>;
|
|
81
|
+
}
|
|
82
|
+
/** A single record of evidence the player presented to an NPC. */
|
|
83
|
+
export interface EvidenceEntry {
|
|
84
|
+
/** The topic/evidence the player presented. */
|
|
85
|
+
topic: string;
|
|
86
|
+
/** The turn number when the evidence was presented. */
|
|
87
|
+
turn: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Per-NPC record of what evidence the player has presented.
|
|
91
|
+
* Keyed by NPC entity ID.
|
|
92
|
+
*/
|
|
93
|
+
export type EvidenceRecord = Map<string, EvidenceEntry[]>;
|
|
94
|
+
/** Create an empty ConversationRecord. */
|
|
95
|
+
export declare function createConversationRecord(): ConversationRecord;
|
|
96
|
+
/** Create an empty EvidenceRecord. */
|
|
97
|
+
export declare function createEvidenceRecord(): EvidenceRecord;
|
|
98
|
+
//# sourceMappingURL=response-types.d.ts.map
|