@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.
Files changed (137) hide show
  1. package/apply.d.ts +29 -7
  2. package/apply.d.ts.map +1 -1
  3. package/apply.js +15 -7
  4. package/apply.js.map +1 -1
  5. package/character-builder.d.ts +67 -4
  6. package/character-builder.d.ts.map +1 -1
  7. package/character-builder.js +116 -7
  8. package/character-builder.js.map +1 -1
  9. package/conversation/acl.d.ts +62 -0
  10. package/conversation/acl.d.ts.map +1 -0
  11. package/conversation/acl.js +117 -0
  12. package/conversation/acl.js.map +1 -0
  13. package/conversation/builder.d.ts +247 -0
  14. package/conversation/builder.d.ts.map +1 -0
  15. package/conversation/builder.js +324 -0
  16. package/conversation/builder.js.map +1 -0
  17. package/conversation/constraint-evaluator.d.ts +147 -0
  18. package/conversation/constraint-evaluator.d.ts.map +1 -0
  19. package/conversation/constraint-evaluator.js +247 -0
  20. package/conversation/constraint-evaluator.js.map +1 -0
  21. package/conversation/conversation-messages.d.ts +56 -0
  22. package/conversation/conversation-messages.d.ts.map +1 -0
  23. package/conversation/conversation-messages.js +69 -0
  24. package/conversation/conversation-messages.js.map +1 -0
  25. package/conversation/dialogue-extension.d.ts +87 -0
  26. package/conversation/dialogue-extension.d.ts.map +1 -0
  27. package/conversation/dialogue-extension.js +302 -0
  28. package/conversation/dialogue-extension.js.map +1 -0
  29. package/conversation/dialogue-types.d.ts +71 -0
  30. package/conversation/dialogue-types.d.ts.map +1 -0
  31. package/conversation/dialogue-types.js +13 -0
  32. package/conversation/dialogue-types.js.map +1 -0
  33. package/conversation/index.d.ts +19 -0
  34. package/conversation/index.d.ts.map +1 -0
  35. package/conversation/index.js +37 -0
  36. package/conversation/index.js.map +1 -0
  37. package/conversation/lifecycle.d.ts +212 -0
  38. package/conversation/lifecycle.d.ts.map +1 -0
  39. package/conversation/lifecycle.js +348 -0
  40. package/conversation/lifecycle.js.map +1 -0
  41. package/conversation/response-types.d.ts +98 -0
  42. package/conversation/response-types.d.ts.map +1 -0
  43. package/conversation/response-types.js +30 -0
  44. package/conversation/response-types.js.map +1 -0
  45. package/conversation/topic-registry.d.ts +130 -0
  46. package/conversation/topic-registry.d.ts.map +1 -0
  47. package/conversation/topic-registry.js +187 -0
  48. package/conversation/topic-registry.js.map +1 -0
  49. package/goals/builder.d.ts +135 -0
  50. package/goals/builder.d.ts.map +1 -0
  51. package/goals/builder.js +193 -0
  52. package/goals/builder.js.map +1 -0
  53. package/goals/goal-activation.d.ts +99 -0
  54. package/goals/goal-activation.d.ts.map +1 -0
  55. package/goals/goal-activation.js +212 -0
  56. package/goals/goal-activation.js.map +1 -0
  57. package/goals/goal-types.d.ts +138 -0
  58. package/goals/goal-types.d.ts.map +1 -0
  59. package/goals/goal-types.js +20 -0
  60. package/goals/goal-types.js.map +1 -0
  61. package/goals/index.d.ts +15 -0
  62. package/goals/index.d.ts.map +1 -0
  63. package/goals/index.js +24 -0
  64. package/goals/index.js.map +1 -0
  65. package/goals/pathfinding.d.ts +67 -0
  66. package/goals/pathfinding.d.ts.map +1 -0
  67. package/goals/pathfinding.js +134 -0
  68. package/goals/pathfinding.js.map +1 -0
  69. package/goals/step-evaluator.d.ts +52 -0
  70. package/goals/step-evaluator.d.ts.map +1 -0
  71. package/goals/step-evaluator.js +153 -0
  72. package/goals/step-evaluator.js.map +1 -0
  73. package/index.d.ts +6 -1
  74. package/index.d.ts.map +1 -1
  75. package/index.js +60 -1
  76. package/index.js.map +1 -1
  77. package/influence/builder.d.ts +136 -0
  78. package/influence/builder.d.ts.map +1 -0
  79. package/influence/builder.js +198 -0
  80. package/influence/builder.js.map +1 -0
  81. package/influence/index.d.ts +16 -0
  82. package/influence/index.d.ts.map +1 -0
  83. package/influence/index.js +25 -0
  84. package/influence/index.js.map +1 -0
  85. package/influence/influence-duration.d.ts +82 -0
  86. package/influence/influence-duration.d.ts.map +1 -0
  87. package/influence/influence-duration.js +148 -0
  88. package/influence/influence-duration.js.map +1 -0
  89. package/influence/influence-evaluator.d.ts +55 -0
  90. package/influence/influence-evaluator.d.ts.map +1 -0
  91. package/influence/influence-evaluator.js +183 -0
  92. package/influence/influence-evaluator.js.map +1 -0
  93. package/influence/influence-messages.d.ts +30 -0
  94. package/influence/influence-messages.d.ts.map +1 -0
  95. package/influence/influence-messages.js +40 -0
  96. package/influence/influence-messages.js.map +1 -0
  97. package/influence/influence-types.d.ts +123 -0
  98. package/influence/influence-types.d.ts.map +1 -0
  99. package/influence/influence-types.js +12 -0
  100. package/influence/influence-types.js.map +1 -0
  101. package/influence/pc-influence.d.ts +38 -0
  102. package/influence/pc-influence.d.ts.map +1 -0
  103. package/influence/pc-influence.js +56 -0
  104. package/influence/pc-influence.js.map +1 -0
  105. package/package.json +4 -3
  106. package/propagation/builder.d.ts +47 -0
  107. package/propagation/builder.d.ts.map +1 -0
  108. package/propagation/builder.js +46 -0
  109. package/propagation/builder.js.map +1 -0
  110. package/propagation/fact-transfer.d.ts +47 -0
  111. package/propagation/fact-transfer.d.ts.map +1 -0
  112. package/propagation/fact-transfer.js +76 -0
  113. package/propagation/fact-transfer.js.map +1 -0
  114. package/propagation/index.d.ts +16 -0
  115. package/propagation/index.d.ts.map +1 -0
  116. package/propagation/index.js +28 -0
  117. package/propagation/index.js.map +1 -0
  118. package/propagation/propagation-evaluator.d.ts +53 -0
  119. package/propagation/propagation-evaluator.d.ts.map +1 -0
  120. package/propagation/propagation-evaluator.js +231 -0
  121. package/propagation/propagation-evaluator.js.map +1 -0
  122. package/propagation/propagation-messages.d.ts +30 -0
  123. package/propagation/propagation-messages.d.ts.map +1 -0
  124. package/propagation/propagation-messages.js +37 -0
  125. package/propagation/propagation-messages.js.map +1 -0
  126. package/propagation/propagation-types.d.ts +100 -0
  127. package/propagation/propagation-types.d.ts.map +1 -0
  128. package/propagation/propagation-types.js +68 -0
  129. package/propagation/propagation-types.js.map +1 -0
  130. package/propagation/visibility.d.ts +46 -0
  131. package/propagation/visibility.d.ts.map +1 -0
  132. package/propagation/visibility.js +79 -0
  133. package/propagation/visibility.js.map +1 -0
  134. package/tick-phases.d.ts +93 -0
  135. package/tick-phases.d.ts.map +1 -0
  136. package/tick-phases.js +433 -0
  137. 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":"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"}
@@ -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