@sharpee/text-service 0.9.60-beta

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli-renderer.d.ts +43 -0
  3. package/dist/cli-renderer.d.ts.map +1 -0
  4. package/dist/cli-renderer.js +208 -0
  5. package/dist/cli-renderer.js.map +1 -0
  6. package/dist/decoration-parser.d.ts +31 -0
  7. package/dist/decoration-parser.d.ts.map +1 -0
  8. package/dist/decoration-parser.js +176 -0
  9. package/dist/decoration-parser.js.map +1 -0
  10. package/dist/handlers/action.d.ts +19 -0
  11. package/dist/handlers/action.d.ts.map +1 -0
  12. package/dist/handlers/action.js +68 -0
  13. package/dist/handlers/action.js.map +1 -0
  14. package/dist/handlers/game.d.ts +29 -0
  15. package/dist/handlers/game.d.ts.map +1 -0
  16. package/dist/handlers/game.js +56 -0
  17. package/dist/handlers/game.js.map +1 -0
  18. package/dist/handlers/generic.d.ts +23 -0
  19. package/dist/handlers/generic.d.ts.map +1 -0
  20. package/dist/handlers/generic.js +70 -0
  21. package/dist/handlers/generic.js.map +1 -0
  22. package/dist/handlers/index.d.ts +12 -0
  23. package/dist/handlers/index.d.ts.map +1 -0
  24. package/dist/handlers/index.js +22 -0
  25. package/dist/handlers/index.js.map +1 -0
  26. package/dist/handlers/revealed.d.ts +16 -0
  27. package/dist/handlers/revealed.d.ts.map +1 -0
  28. package/dist/handlers/revealed.js +50 -0
  29. package/dist/handlers/revealed.js.map +1 -0
  30. package/dist/handlers/room.d.ts +22 -0
  31. package/dist/handlers/room.d.ts.map +1 -0
  32. package/dist/handlers/room.js +70 -0
  33. package/dist/handlers/room.js.map +1 -0
  34. package/dist/handlers/types.d.ts +40 -0
  35. package/dist/handlers/types.d.ts.map +1 -0
  36. package/dist/handlers/types.js +8 -0
  37. package/dist/handlers/types.js.map +1 -0
  38. package/dist/index.d.ts +31 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +58 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/stages/assemble.d.ts +18 -0
  43. package/dist/stages/assemble.d.ts.map +1 -0
  44. package/dist/stages/assemble.js +38 -0
  45. package/dist/stages/assemble.js.map +1 -0
  46. package/dist/stages/filter.d.ts +14 -0
  47. package/dist/stages/filter.d.ts.map +1 -0
  48. package/dist/stages/filter.js +28 -0
  49. package/dist/stages/filter.js.map +1 -0
  50. package/dist/stages/index.d.ts +9 -0
  51. package/dist/stages/index.d.ts.map +1 -0
  52. package/dist/stages/index.js +17 -0
  53. package/dist/stages/index.js.map +1 -0
  54. package/dist/stages/sort.d.ts +36 -0
  55. package/dist/stages/sort.d.ts.map +1 -0
  56. package/dist/stages/sort.js +84 -0
  57. package/dist/stages/sort.js.map +1 -0
  58. package/dist/text-service.d.ts +85 -0
  59. package/dist/text-service.d.ts.map +1 -0
  60. package/dist/text-service.js +233 -0
  61. package/dist/text-service.js.map +1 -0
  62. package/package.json +64 -0
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /**
3
+ * Event filtering stage
4
+ *
5
+ * Filters out events that should not produce text output:
6
+ * - System events (system.*)
7
+ *
8
+ * @see ADR-096 Text Service Architecture
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.filterEvents = filterEvents;
12
+ /**
13
+ * Filter events that should produce text output
14
+ */
15
+ function filterEvents(events) {
16
+ return events.filter((event) => {
17
+ // Skip system events
18
+ if (event.type.startsWith('system.')) {
19
+ return false;
20
+ }
21
+ // Skip platform events (save/restore/quit/restart requests and completions)
22
+ if (event.type.startsWith('platform.')) {
23
+ return false;
24
+ }
25
+ return true;
26
+ });
27
+ }
28
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/stages/filter.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAOH,oCAcC;AAjBD;;GAEG;AACH,SAAgB,YAAY,CAAC,MAAwB;IACnD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC7B,qBAAqB;QACrB,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,4EAA4E;QAC5E,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Pipeline stages for TextService
3
+ *
4
+ * @see ADR-096 Text Service Architecture
5
+ */
6
+ export { filterEvents } from './filter.js';
7
+ export { sortEventsForProse, getChainMetadata } from './sort.js';
8
+ export { createBlock, extractValue } from './assemble.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stages/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * Pipeline stages for TextService
4
+ *
5
+ * @see ADR-096 Text Service Architecture
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.extractValue = exports.createBlock = exports.getChainMetadata = exports.sortEventsForProse = exports.filterEvents = void 0;
9
+ var filter_js_1 = require("./filter.js");
10
+ Object.defineProperty(exports, "filterEvents", { enumerable: true, get: function () { return filter_js_1.filterEvents; } });
11
+ var sort_js_1 = require("./sort.js");
12
+ Object.defineProperty(exports, "sortEventsForProse", { enumerable: true, get: function () { return sort_js_1.sortEventsForProse; } });
13
+ Object.defineProperty(exports, "getChainMetadata", { enumerable: true, get: function () { return sort_js_1.getChainMetadata; } });
14
+ var assemble_js_1 = require("./assemble.js");
15
+ Object.defineProperty(exports, "createBlock", { enumerable: true, get: function () { return assemble_js_1.createBlock; } });
16
+ Object.defineProperty(exports, "extractValue", { enumerable: true, get: function () { return assemble_js_1.extractValue; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stages/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,yCAA2C;AAAlC,yGAAA,YAAY,OAAA;AACrB,qCAAiE;AAAxD,6GAAA,kBAAkB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAC7C,6CAA0D;AAAjD,0GAAA,WAAW,OAAA;AAAE,2GAAA,YAAY,OAAA"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Event sorting stage
3
+ *
4
+ * Sorts events within each transaction for correct prose order:
5
+ * 1. action.* events first (the main action result)
6
+ * 2. Then by chainDepth (lower depth first)
7
+ *
8
+ * Events arrive from Engine in emission order, but prose requires:
9
+ * - Action result first ("You open the chest.")
10
+ * - Then consequences ("Inside you see...")
11
+ *
12
+ * @see ADR-094 Event Chaining
13
+ * @see ADR-096 Text Service Architecture
14
+ */
15
+ import type { ISemanticEvent } from '@sharpee/core';
16
+ /**
17
+ * Event data with chain metadata (ADR-094)
18
+ */
19
+ interface ChainMetadata {
20
+ _transactionId?: string;
21
+ _chainDepth?: number;
22
+ _chainedFrom?: string;
23
+ _chainSourceId?: string;
24
+ }
25
+ /**
26
+ * Sort events for correct prose order within transactions
27
+ *
28
+ * Uses stable sort to preserve order across different transactions.
29
+ */
30
+ export declare function sortEventsForProse(events: ISemanticEvent[]): ISemanticEvent[];
31
+ /**
32
+ * Extract chain metadata from event data
33
+ */
34
+ export declare function getChainMetadata(event: ISemanticEvent): ChainMetadata;
35
+ export {};
36
+ //# sourceMappingURL=sort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.d.ts","sourceRoot":"","sources":["../../src/stages/sort.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;GAEG;AACH,UAAU,aAAa;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CA6C7E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,GAAG,aAAa,CAQrE"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * Event sorting stage
4
+ *
5
+ * Sorts events within each transaction for correct prose order:
6
+ * 1. action.* events first (the main action result)
7
+ * 2. Then by chainDepth (lower depth first)
8
+ *
9
+ * Events arrive from Engine in emission order, but prose requires:
10
+ * - Action result first ("You open the chest.")
11
+ * - Then consequences ("Inside you see...")
12
+ *
13
+ * @see ADR-094 Event Chaining
14
+ * @see ADR-096 Text Service Architecture
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.sortEventsForProse = sortEventsForProse;
18
+ exports.getChainMetadata = getChainMetadata;
19
+ /**
20
+ * Sort events for correct prose order within transactions
21
+ *
22
+ * Uses stable sort to preserve order across different transactions.
23
+ */
24
+ function sortEventsForProse(events) {
25
+ return [...events].sort((a, b) => {
26
+ const aData = a.data;
27
+ const bData = b.data;
28
+ const aTxnId = aData?._transactionId;
29
+ const bTxnId = bData?._transactionId;
30
+ // Game lifecycle events come first, before everything
31
+ // This ensures the banner displays before the first room description
32
+ // Note: Only match specific lifecycle events, NOT game.message (which is handled elsewhere)
33
+ const LIFECYCLE_EVENTS = ['game.started', 'game.starting', 'game.loading', 'game.loaded', 'game.initialized'];
34
+ const aIsGameLifecycle = LIFECYCLE_EVENTS.includes(a.type);
35
+ const bIsGameLifecycle = LIFECYCLE_EVENTS.includes(b.type);
36
+ if (aIsGameLifecycle && !bIsGameLifecycle)
37
+ return -1;
38
+ if (!aIsGameLifecycle && bIsGameLifecycle)
39
+ return 1;
40
+ // Different transactions or no transaction: maintain original order
41
+ if (aTxnId !== bTxnId)
42
+ return 0;
43
+ // Implicit take events should come first (before the main action result)
44
+ // "first taking the X" should appear before "X reads:..."
45
+ const aIsImplicitTake = a.type === 'if.event.implicit_take';
46
+ const bIsImplicitTake = b.type === 'if.event.implicit_take';
47
+ if (aIsImplicitTake && !bIsImplicitTake)
48
+ return -1;
49
+ if (!aIsImplicitTake && bIsImplicitTake)
50
+ return 1;
51
+ // Room description should come before action.success (for contents list)
52
+ // This ensures "Room Name\nDescription" appears before "You see X here."
53
+ const aIsRoomDesc = a.type === 'if.event.room.description' || a.type === 'if.event.room_description';
54
+ const bIsRoomDesc = b.type === 'if.event.room.description' || b.type === 'if.event.room_description';
55
+ if (aIsRoomDesc && !bIsRoomDesc)
56
+ return -1;
57
+ if (!aIsRoomDesc && bIsRoomDesc)
58
+ return 1;
59
+ // Same transaction: action.* first (after room description)
60
+ const aIsAction = a.type.startsWith('action.');
61
+ const bIsAction = b.type.startsWith('action.');
62
+ if (aIsAction && !bIsAction)
63
+ return -1;
64
+ if (!aIsAction && bIsAction)
65
+ return 1;
66
+ // Then by chain depth (lower depth first)
67
+ const aDepth = aData?._chainDepth ?? 0;
68
+ const bDepth = bData?._chainDepth ?? 0;
69
+ return aDepth - bDepth;
70
+ });
71
+ }
72
+ /**
73
+ * Extract chain metadata from event data
74
+ */
75
+ function getChainMetadata(event) {
76
+ const data = event.data;
77
+ return {
78
+ _transactionId: data?._transactionId,
79
+ _chainDepth: data?._chainDepth,
80
+ _chainedFrom: data?._chainedFrom,
81
+ _chainSourceId: data?._chainSourceId,
82
+ };
83
+ }
84
+ //# sourceMappingURL=sort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sort.js","sourceRoot":"","sources":["../../src/stages/sort.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG;;AAmBH,gDA6CC;AAKD,4CAQC;AA/DD;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,MAAwB;IACzD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC/B,MAAM,KAAK,GAAG,CAAC,CAAC,IAAiC,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,CAAC,IAAiC,CAAC;QAElD,MAAM,MAAM,GAAG,KAAK,EAAE,cAAc,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,EAAE,cAAc,CAAC;QAErC,sDAAsD;QACtD,qEAAqE;QACrE,4FAA4F;QAC5F,MAAM,gBAAgB,GAAG,CAAC,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,CAAC,CAAC;QAC9G,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,gBAAgB,IAAI,CAAC,gBAAgB;YAAE,OAAO,CAAC,CAAC,CAAC;QACrD,IAAI,CAAC,gBAAgB,IAAI,gBAAgB;YAAE,OAAO,CAAC,CAAC;QAEpD,oEAAoE;QACpE,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,CAAC,CAAC;QAEhC,yEAAyE;QACzE,0DAA0D;QAC1D,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,KAAK,wBAAwB,CAAC;QAC5D,MAAM,eAAe,GAAG,CAAC,CAAC,IAAI,KAAK,wBAAwB,CAAC;QAC5D,IAAI,eAAe,IAAI,CAAC,eAAe;YAAE,OAAO,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,eAAe,IAAI,eAAe;YAAE,OAAO,CAAC,CAAC;QAElD,yEAAyE;QACzE,yEAAyE;QACzE,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,KAAK,2BAA2B,IAAI,CAAC,CAAC,IAAI,KAAK,2BAA2B,CAAC;QACrG,MAAM,WAAW,GAAG,CAAC,CAAC,IAAI,KAAK,2BAA2B,IAAI,CAAC,CAAC,IAAI,KAAK,2BAA2B,CAAC;QACrG,IAAI,WAAW,IAAI,CAAC,WAAW;YAAE,OAAO,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,IAAI,WAAW;YAAE,OAAO,CAAC,CAAC;QAE1C,4DAA4D;QAC5D,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC/C,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAC/C,IAAI,SAAS,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,IAAI,SAAS;YAAE,OAAO,CAAC,CAAC;QAEtC,0CAA0C;QAC1C,MAAM,MAAM,GAAG,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,KAAK,EAAE,WAAW,IAAI,CAAC,CAAC;QACvC,OAAO,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,KAAqB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAiC,CAAC;IACrD,OAAO;QACL,cAAc,EAAE,IAAI,EAAE,cAAc;QACpC,WAAW,EAAE,IAAI,EAAE,WAAW;QAC9B,YAAY,EAAE,IAAI,EAAE,YAAY;QAChC,cAAc,EAAE,IAAI,EAAE,cAAc;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Text Service
3
+ *
4
+ * Orchestrates the text output pipeline:
5
+ * 1. Filter - remove system events
6
+ * 2. Sort - order events for prose (ADR-094)
7
+ * 3. Process - route to handlers
8
+ * 4. Assemble - create ITextBlock with decorations
9
+ *
10
+ * Stateless transformer: events in, TextBlocks out.
11
+ * Inspired by FyreVM channel I/O (2009).
12
+ *
13
+ * @see ADR-096 Text Service Architecture
14
+ */
15
+ import type { ITextBlock } from '@sharpee/text-blocks';
16
+ import type { LanguageProvider } from '@sharpee/if-domain';
17
+ import type { ISemanticEvent } from '@sharpee/core';
18
+ /**
19
+ * Text service interface (ADR-096)
20
+ *
21
+ * Stateless transformer: takes events, returns TextBlocks.
22
+ * Engine calls processTurn() after each turn completes.
23
+ */
24
+ export interface ITextService {
25
+ /**
26
+ * Process turn events and produce TextBlocks.
27
+ * Called by Engine after turn completes.
28
+ *
29
+ * @param events - All events from this turn (including chained events)
30
+ * @returns TextBlocks for client rendering
31
+ */
32
+ processTurn(events: ISemanticEvent[]): ITextBlock[];
33
+ }
34
+ /**
35
+ * TextService implementation
36
+ *
37
+ * Orchestrates the pipeline: filter → sort → process → assemble
38
+ */
39
+ export declare class TextService implements ITextService {
40
+ private readonly languageProvider;
41
+ constructor(languageProvider: LanguageProvider);
42
+ processTurn(events: ISemanticEvent[]): ITextBlock[];
43
+ /**
44
+ * Route event to appropriate handler
45
+ */
46
+ private routeToHandler;
47
+ /**
48
+ * NEW PATTERN (ADR-097): Process domain events that carry messageId directly.
49
+ *
50
+ * Returns text blocks if event has messageId and message was found.
51
+ * Returns null to fall through to legacy handling.
52
+ *
53
+ * This allows gradual migration: actions can be updated one at a time
54
+ * to emit domain events with messageId instead of separate action.success.
55
+ */
56
+ private tryProcessDomainEventMessage;
57
+ /**
58
+ * Handle if.event.implicit_take events
59
+ * Produces "(first taking the X)" message
60
+ */
61
+ private handleImplicitTake;
62
+ /**
63
+ * Handle command.failed events
64
+ * These occur when parsing or entity resolution fails
65
+ */
66
+ private handleCommandFailed;
67
+ /**
68
+ * Handle client.query events (disambiguation, confirmations, etc.)
69
+ */
70
+ private handleClientQuery;
71
+ /**
72
+ * Format a list of candidate names as natural English
73
+ * e.g., "the red ball or the blue ball" or "the sword, the axe, or the knife"
74
+ */
75
+ private formatCandidateList;
76
+ }
77
+ /**
78
+ * Create a TextService with the given LanguageProvider.
79
+ * LanguageProvider supplies templates (standard + story-registered).
80
+ *
81
+ * @param languageProvider - Provider for template resolution
82
+ * @returns Configured TextService instance
83
+ */
84
+ export declare function createTextService(languageProvider: LanguageProvider): ITextService;
85
+ //# sourceMappingURL=text-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-service.d.ts","sourceRoot":"","sources":["../src/text-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAepD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,EAAE,CAAC;CACrD;AAyBD;;;;GAIG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAmB;gBAExC,gBAAgB,EAAE,gBAAgB;IAI9C,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,UAAU,EAAE;IAmBnD;;OAEG;IACH,OAAO,CAAC,cAAc;IAgDtB;;;;;;;;OAQG;IACH,OAAO,CAAC,4BAA4B;IAqCpC;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IA4B3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;;OAGG;IACH,OAAO,CAAC,mBAAmB;CAe5B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,YAAY,CAElF"}
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ /**
3
+ * Text Service
4
+ *
5
+ * Orchestrates the text output pipeline:
6
+ * 1. Filter - remove system events
7
+ * 2. Sort - order events for prose (ADR-094)
8
+ * 3. Process - route to handlers
9
+ * 4. Assemble - create ITextBlock with decorations
10
+ *
11
+ * Stateless transformer: events in, TextBlocks out.
12
+ * Inspired by FyreVM channel I/O (2009).
13
+ *
14
+ * @see ADR-096 Text Service Architecture
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.TextService = void 0;
18
+ exports.createTextService = createTextService;
19
+ const text_blocks_1 = require("@sharpee/text-blocks");
20
+ // Pipeline stages
21
+ const filter_js_1 = require("./stages/filter.js");
22
+ const sort_js_1 = require("./stages/sort.js");
23
+ const assemble_js_1 = require("./stages/assemble.js");
24
+ const room_js_1 = require("./handlers/room.js");
25
+ const action_js_1 = require("./handlers/action.js");
26
+ const revealed_js_1 = require("./handlers/revealed.js");
27
+ const generic_js_1 = require("./handlers/generic.js");
28
+ const game_js_1 = require("./handlers/game.js");
29
+ /**
30
+ * State change events that don't produce text output (LEGACY pattern).
31
+ * The corresponding action.success event provides the message.
32
+ *
33
+ * NOTE: With the simplified event pattern (ADR-097), domain events can carry
34
+ * messageId directly. When they do, we process them and skip action.success.
35
+ * This set is only checked when events DON'T have messageId (backward compat).
36
+ *
37
+ * @deprecated Will be removed once all actions migrate to simplified pattern
38
+ */
39
+ const STATE_CHANGE_EVENTS = new Set([
40
+ 'if.event.opened',
41
+ 'if.event.closed',
42
+ 'if.event.locked',
43
+ 'if.event.unlocked',
44
+ 'if.event.switched_on',
45
+ 'if.event.switched_off',
46
+ 'if.event.taken', // State change - action.success provides message
47
+ 'if.event.dropped', // State change - action.success provides message
48
+ 'if.event.read', // State change - action.success provides message
49
+ // Note: if.event.room.description removed - specialized handler handles it
50
+ ]);
51
+ /**
52
+ * TextService implementation
53
+ *
54
+ * Orchestrates the pipeline: filter → sort → process → assemble
55
+ */
56
+ class TextService {
57
+ languageProvider;
58
+ constructor(languageProvider) {
59
+ this.languageProvider = languageProvider;
60
+ }
61
+ processTurn(events) {
62
+ // Pipeline: filter → sort → process
63
+ const filtered = (0, filter_js_1.filterEvents)(events);
64
+ const sorted = (0, sort_js_1.sortEventsForProse)(filtered);
65
+ // Process each event through handlers
66
+ const handlerContext = {
67
+ languageProvider: this.languageProvider,
68
+ };
69
+ const blocks = [];
70
+ for (const event of sorted) {
71
+ const eventBlocks = this.routeToHandler(event, handlerContext);
72
+ blocks.push(...eventBlocks);
73
+ }
74
+ return blocks;
75
+ }
76
+ /**
77
+ * Route event to appropriate handler
78
+ */
79
+ routeToHandler(event, context) {
80
+ // NEW PATTERN (ADR-097): Domain events can carry messageId directly.
81
+ // If present, look up message and return block - no action.success needed.
82
+ const result = this.tryProcessDomainEventMessage(event, context);
83
+ if (result) {
84
+ return result;
85
+ }
86
+ // LEGACY PATTERN: Skip state change events (action.success provides message)
87
+ if (STATE_CHANGE_EVENTS.has(event.type)) {
88
+ return [];
89
+ }
90
+ switch (event.type) {
91
+ case 'game.started':
92
+ return (0, game_js_1.handleGameStarted)(event, context);
93
+ case 'if.event.room_description':
94
+ case 'if.event.room.description':
95
+ return (0, room_js_1.handleRoomDescription)(event, context);
96
+ case 'action.success':
97
+ return (0, action_js_1.handleActionSuccess)(event, context);
98
+ case 'action.failure':
99
+ case 'action.blocked':
100
+ return (0, action_js_1.handleActionFailure)(event, context);
101
+ case 'game.message':
102
+ return (0, generic_js_1.handleGameMessage)(event, context);
103
+ case 'if.event.revealed':
104
+ return (0, revealed_js_1.handleRevealed)(event, context);
105
+ case 'if.event.implicit_take':
106
+ return this.handleImplicitTake(event, context);
107
+ case 'command.failed':
108
+ return this.handleCommandFailed(event, context);
109
+ case 'client.query':
110
+ return this.handleClientQuery(event, context);
111
+ default:
112
+ return (0, generic_js_1.handleGenericEvent)(event, context);
113
+ }
114
+ }
115
+ /**
116
+ * NEW PATTERN (ADR-097): Process domain events that carry messageId directly.
117
+ *
118
+ * Returns text blocks if event has messageId and message was found.
119
+ * Returns null to fall through to legacy handling.
120
+ *
121
+ * This allows gradual migration: actions can be updated one at a time
122
+ * to emit domain events with messageId instead of separate action.success.
123
+ */
124
+ tryProcessDomainEventMessage(event, context) {
125
+ const data = event.data;
126
+ // No messageId = fall through to legacy handling
127
+ if (!data?.messageId) {
128
+ return null;
129
+ }
130
+ // Skip action.success/failure/blocked - they use legacy handler
131
+ // (prevents double processing during migration)
132
+ if (event.type.startsWith('action.')) {
133
+ return null;
134
+ }
135
+ // Look up message via language provider
136
+ if (!context.languageProvider) {
137
+ return null;
138
+ }
139
+ const message = context.languageProvider.getMessage(data.messageId, data.params);
140
+ // If message wasn't found (returns the messageId), fall through to legacy
141
+ if (message === data.messageId) {
142
+ return null;
143
+ }
144
+ // Determine block key based on event type
145
+ const blockKey = event.type.includes('blocked') || event.type.includes('failure')
146
+ ? text_blocks_1.BLOCK_KEYS.ACTION_BLOCKED
147
+ : text_blocks_1.BLOCK_KEYS.ACTION_RESULT;
148
+ return [(0, assemble_js_1.createBlock)(blockKey, message)];
149
+ }
150
+ /**
151
+ * Handle if.event.implicit_take events
152
+ * Produces "(first taking the X)" message
153
+ */
154
+ handleImplicitTake(event, _context) {
155
+ const data = event.data;
156
+ const itemName = data.itemName || 'something';
157
+ return [(0, assemble_js_1.createBlock)(text_blocks_1.BLOCK_KEYS.ACTION_RESULT, `(first taking the ${itemName})`)];
158
+ }
159
+ /**
160
+ * Handle command.failed events
161
+ * These occur when parsing or entity resolution fails
162
+ */
163
+ handleCommandFailed(event, context) {
164
+ const data = event.data;
165
+ // Try to get a user-friendly message based on the reason
166
+ if (data.reason) {
167
+ // Check for specific failure reasons
168
+ if (data.reason.includes('ENTITY_NOT_FOUND') || data.reason.includes('modifiers_not_matched')) {
169
+ // Entity resolution failed - player referred to something that doesn't exist or can't be found
170
+ const message = context.languageProvider?.getMessage('core.entity_not_found')
171
+ ?? "I don't see that here.";
172
+ return [(0, assemble_js_1.createBlock)(text_blocks_1.BLOCK_KEYS.ERROR, message)];
173
+ }
174
+ // Note: AMBIGUOUS_ENTITY now uses client.query event, not command.failed
175
+ if (data.reason.includes('NO_MATCH') || data.reason.includes('parse')) {
176
+ const message = context.languageProvider?.getMessage('core.command_not_understood')
177
+ ?? "I don't understand that.";
178
+ return [(0, assemble_js_1.createBlock)(text_blocks_1.BLOCK_KEYS.ERROR, message)];
179
+ }
180
+ }
181
+ // Generic fallback
182
+ const message = context.languageProvider?.getMessage('core.command_failed')
183
+ ?? "I don't understand that.";
184
+ return [(0, assemble_js_1.createBlock)(text_blocks_1.BLOCK_KEYS.ERROR, message)];
185
+ }
186
+ /**
187
+ * Handle client.query events (disambiguation, confirmations, etc.)
188
+ */
189
+ handleClientQuery(event, context) {
190
+ const data = event.data;
191
+ // Only handle disambiguation queries here
192
+ if (data.source !== 'disambiguation') {
193
+ return [];
194
+ }
195
+ // Format candidates as natural list
196
+ const candidateNames = (data.candidates || []).map(c => c.name);
197
+ const options = this.formatCandidateList(candidateNames);
198
+ // Get message template with options
199
+ const message = context.languageProvider?.getMessage('core.disambiguation_prompt', { options })
200
+ ?? `Which do you mean: ${options}?`;
201
+ return [(0, assemble_js_1.createBlock)(text_blocks_1.BLOCK_KEYS.ERROR, message)];
202
+ }
203
+ /**
204
+ * Format a list of candidate names as natural English
205
+ * e.g., "the red ball or the blue ball" or "the sword, the axe, or the knife"
206
+ */
207
+ formatCandidateList(names) {
208
+ if (names.length === 0)
209
+ return '';
210
+ if (names.length === 1)
211
+ return `the ${names[0]}`;
212
+ // Add "the" article to each name
213
+ const withArticles = names.map(n => `the ${n}`);
214
+ if (withArticles.length === 2) {
215
+ return `${withArticles[0]} or ${withArticles[1]}`;
216
+ }
217
+ // Oxford comma style: "the X, the Y, or the Z"
218
+ const last = withArticles.pop();
219
+ return `${withArticles.join(', ')}, or ${last}`;
220
+ }
221
+ }
222
+ exports.TextService = TextService;
223
+ /**
224
+ * Create a TextService with the given LanguageProvider.
225
+ * LanguageProvider supplies templates (standard + story-registered).
226
+ *
227
+ * @param languageProvider - Provider for template resolution
228
+ * @returns Configured TextService instance
229
+ */
230
+ function createTextService(languageProvider) {
231
+ return new TextService(languageProvider);
232
+ }
233
+ //# sourceMappingURL=text-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-service.js","sourceRoot":"","sources":["../src/text-service.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@sharpee/text-service",
3
+ "version": "0.9.60-beta",
4
+ "description": "Text service for Sharpee - resolves templates and produces ITextBlock output",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "require": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@sharpee/text-blocks": "0.9.60-beta",
16
+ "@sharpee/core": "0.9.60-beta",
17
+ "@sharpee/if-services": "0.9.60-beta",
18
+ "@sharpee/if-domain": "0.9.60-beta"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^18.0.0",
22
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
23
+ "@typescript-eslint/parser": "^5.59.0",
24
+ "eslint": "^8.38.0",
25
+ "rimraf": "^5.0.0",
26
+ "typescript": "^5.0.0",
27
+ "vitest": "^1.6.0"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "keywords": [
33
+ "interactive-fiction",
34
+ "if",
35
+ "text-adventure",
36
+ "sharpee",
37
+ "text-service",
38
+ "fyrevm"
39
+ ],
40
+ "author": "Sharpee Team",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/ChicagoDave/sharpee.git",
45
+ "directory": "packages/text-service"
46
+ },
47
+ "homepage": "https://github.com/ChicagoDave/sharpee#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/ChicagoDave/sharpee/issues"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ },
57
+ "scripts": {
58
+ "build": "tsc",
59
+ "test": "vitest",
60
+ "test:ci": "vitest run",
61
+ "lint": "eslint src --ext .ts",
62
+ "clean": "rimraf dist"
63
+ }
64
+ }