holonovel 2026.8.22 → 2026.8.24

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.
@@ -0,0 +1 @@
1
+ export declare function deriveAnchor(heading: string, explicitId?: string, existing?: Set<string>): string;
@@ -0,0 +1,35 @@
1
+ // REQ-194 — deterministic anchor derivation. Anchors are derived from heading
2
+ // text: lowercase, strip `[\p{P}\p{S}]`, collapse whitespace/hyphen runs to a
3
+ // single hyphen, trim leading/trailing hyphens. Explicit `{#id}` takes
4
+ // precedence; role-scoping markers (`*Keeper only*`, `*Player only*`) are
5
+ // stripped before derivation; duplicate anchors within a file append `-1`,
6
+ // `-2`, … per GFM convention. CJK and non-ASCII word characters are preserved.
7
+ // Re-deriving the same source reproduces identical anchors (determinism).
8
+ export function deriveAnchor(heading, explicitId, existing) {
9
+ // Explicit `{#id}` takes precedence over derived anchors (REQ-194a).
10
+ const inlineId = String(heading).match(/\{#([^}]+)\}/)?.[1];
11
+ if (explicitId)
12
+ return explicitId;
13
+ if (inlineId)
14
+ return inlineId;
15
+ let text = heading
16
+ .replace(/\*[^*]+\*/g, "")
17
+ .toLowerCase()
18
+ .replace(/[\p{P}\p{S}]/gu, "")
19
+ .replace(/[\s\u00a0]+/g, "-")
20
+ .replace(/-+/g, "-")
21
+ .replace(/^-+|-+$/g, "");
22
+ if (!text)
23
+ text = "section";
24
+ if (existing) {
25
+ if (existing.has(text)) {
26
+ let n = 1;
27
+ while (existing.has(`${text}-${n}`))
28
+ n++;
29
+ text = `${text}-${n}`;
30
+ }
31
+ existing.add(text);
32
+ }
33
+ return text;
34
+ }
35
+ //# sourceMappingURL=anchors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchors.js","sourceRoot":"","sources":["../../src/core/anchors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,uEAAuE;AACvE,0EAA0E;AAC1E,2EAA2E;AAC3E,+EAA+E;AAC/E,0EAA0E;AAE1E,MAAM,UAAU,YAAY,CAC1B,OAAe,EACf,UAAmB,EACnB,QAAsB;IAEtB,qEAAqE;IACrE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAClC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,IAAI,GAAG,OAAO;SACf,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,WAAW,EAAE;SACb,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;SAC7B,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI;QAAE,IAAI,GAAG,SAAS,CAAC;IAC5B,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;gBAAE,CAAC,EAAE,CAAC;YACzC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;QACxB,CAAC;QACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -25,6 +25,10 @@ export function novelSnapshot() {
25
25
  _state.snapshot(novel, getBadge());
26
26
  }
27
27
  export function withForbiddenAudit(handler, toolName) {
28
+ // REQ-133 — forbidden-call audit: every [FORBIDDEN] is recorded with the
29
+ // active badge, tool name, and arguments, tagged `violation_type: boundary`
30
+ // (see StateManager.auditForbidden) and surfaced with a [BOUNDARY_VIOLATION]
31
+ // marker in compress_audit / audit://novel.
28
32
  return async (args, ctx) => {
29
33
  try {
30
34
  return await handler(args, ctx);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gFAAgF;AAUhF,IAAI,MAAoB,CAAC;AAEzB,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,MAAM,CAAC,WAAW,EAAE,KAAK,IAAI,MAAM,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,YAAY,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IACjC,IAAI,KAAK;QAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAoB,EACpB,QAAgB;IAEhB,OAAO,KAAK,EAAE,IAAS,EAAE,GAAY,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gFAAgF;AAUhF,IAAI,MAAoB,CAAC;AAEzB,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,MAAM,GAAG,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,MAAM,CAAC,WAAW,EAAE,KAAK,IAAI,MAAM,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,MAAM,CAAC,YAAY,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC;IACjC,IAAI,KAAK;QAAE,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,OAAoB,EACpB,QAAgB;IAEhB,yEAAyE;IACzE,4EAA4E;IAC5E,6EAA6E;IAC7E,4CAA4C;IAC5C,OAAO,KAAK,EAAE,IAAS,EAAE,GAAY,EAAE,EAAE;QACvC,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBACzC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;YACnD,CAAC;YACD,MAAM,CAAC,CAAC;QACV,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -21,6 +21,19 @@ export interface NovelEntity {
21
21
  conditions: string[];
22
22
  condition_rounds: Record<string, number>;
23
23
  stats?: Record<string, any>;
24
+ knowledge?: {
25
+ explored?: Array<{
26
+ type: "room" | "thing";
27
+ name: string;
28
+ at: string;
29
+ }>;
30
+ };
31
+ codex_source?: {
32
+ id: string;
33
+ kind: string;
34
+ imported_at: string;
35
+ codex_modified_at: string;
36
+ };
24
37
  }
25
38
  export interface NpcState {
26
39
  id: string;
@@ -28,6 +41,7 @@ export interface NpcState {
28
41
  description?: string;
29
42
  disposition?: string;
30
43
  location?: string;
44
+ room_id?: string;
31
45
  personality?: {
32
46
  description?: string;
33
47
  voice?: string;
@@ -41,6 +55,26 @@ export interface NpcState {
41
55
  }[];
42
56
  conditions: string[];
43
57
  condition_rounds: Record<string, number>;
58
+ memory?: {
59
+ witnessed_events: Array<{
60
+ event: string;
61
+ at: string;
62
+ }>;
63
+ contacts: Record<string, {
64
+ encounters: number;
65
+ first_contact: string;
66
+ last_contact: string;
67
+ last_disposition: string;
68
+ prior_dispositions: string[];
69
+ }>;
70
+ stress_markers: string[];
71
+ goals?: string[];
72
+ last_3_interactions: Array<{
73
+ entity: string;
74
+ summary: string;
75
+ at: string;
76
+ }>;
77
+ };
44
78
  }
45
79
  export interface LoreEntry {
46
80
  key: string;
@@ -52,6 +86,8 @@ export interface LoreEntry {
52
86
  sticky_remaining: number;
53
87
  enabled: boolean;
54
88
  group?: string;
89
+ source?: string;
90
+ world_target?: string;
55
91
  }
56
92
  export interface Countdown {
57
93
  name: string;
@@ -62,6 +98,15 @@ export interface Countdown {
62
98
  direction?: string;
63
99
  clock_type?: "danger" | "racing" | "linked" | "tug_of_war" | "faction" | "mission";
64
100
  on_scene_transition?: boolean;
101
+ triggers?: string[];
102
+ world_effect?: {
103
+ type: "describe" | "property" | "exit";
104
+ target: string;
105
+ direction?: string;
106
+ destination?: string;
107
+ property?: string;
108
+ value?: string;
109
+ };
65
110
  }
66
111
  export interface CombatState {
67
112
  participants: string[];
@@ -92,6 +137,8 @@ export interface StoryEntry {
92
137
  scene_anchor: string;
93
138
  entity_ids: string[];
94
139
  timestamp: string;
140
+ discovered?: boolean;
141
+ room_id?: string;
95
142
  }
96
143
  export interface FactionState {
97
144
  id: string;
@@ -102,6 +149,23 @@ export interface FactionState {
102
149
  clock: number;
103
150
  clock_max: number;
104
151
  status: string;
152
+ territory?: string[];
153
+ }
154
+ export interface StoryBeat {
155
+ beat: string;
156
+ scene_preview: string;
157
+ source?: "adventure-scaffold" | "gm";
158
+ codex_source?: {
159
+ id: string;
160
+ kind: string;
161
+ imported_at: string;
162
+ codex_modified_at: string;
163
+ };
164
+ }
165
+ export interface GoalSuggestion {
166
+ npc_id: string;
167
+ text: string;
168
+ state: "pending" | "deferred" | "dismissed" | "accepted";
105
169
  }
106
170
  export interface SecretState {
107
171
  key: string;
@@ -109,6 +173,7 @@ export interface SecretState {
109
173
  triggers: string[];
110
174
  badge_scope: "game_master" | "shared";
111
175
  known_by: string[];
176
+ world_target?: string;
112
177
  }
113
178
  export interface Relationship {
114
179
  entity_a: string;
@@ -194,6 +259,10 @@ export interface ConditionState {
194
259
  conditions: string[];
195
260
  condition_rounds: Record<string, number>;
196
261
  }
262
+ export interface WorkflowSnapshot {
263
+ timestamp: string;
264
+ state: any;
265
+ }
197
266
  export interface AutonomyState {
198
267
  level: "full" | "mechanical_prompt" | "manual";
199
268
  confirmation: "auto" | "confirm" | "prompt";
@@ -223,7 +292,9 @@ export interface NovelState {
223
292
  location?: string;
224
293
  time_of_day?: string;
225
294
  atmosphere?: string;
295
+ beat?: string;
226
296
  }[];
297
+ scene_beat: string;
227
298
  scene_type: ("combat" | "social" | "exploration" | "neutral")[];
228
299
  narrative_directive: string;
229
300
  combat: CombatState | null;
@@ -244,11 +315,13 @@ export interface NovelState {
244
315
  adventure_set: boolean;
245
316
  pending_workflow: {
246
317
  decision: string;
247
- snapshot: any;
318
+ snapshot: WorkflowSnapshot | null;
319
+ payload?: any;
248
320
  } | {
249
321
  decision: string;
250
- snapshot: any;
322
+ snapshot: WorkflowSnapshot | null;
251
323
  creation?: import("./character-creation.js").CreationWorkflowState;
324
+ payload?: any;
252
325
  } | null;
253
326
  connection_counter: number;
254
327
  pending_staleness_counter: number;
@@ -270,6 +343,20 @@ export interface NovelState {
270
343
  }[];
271
344
  synthesis_activated: Record<string, number>;
272
345
  synthesis_module_enabled: Record<string, boolean>;
346
+ player_synthesis: Record<string, Array<{
347
+ key: string;
348
+ content: string;
349
+ triggers?: string[];
350
+ badge_scope: string;
351
+ created_at: string;
352
+ }>>;
353
+ campaign_memory: Array<{
354
+ category: "npcs" | "threads" | "locations";
355
+ text: string;
356
+ at: string;
357
+ badge_scope: "gm" | "shared" | "discovered";
358
+ scene: string;
359
+ }>;
273
360
  notes: NoteEntry[];
274
361
  vows: VowState[];
275
362
  checkpoints: Checkpoint[];
@@ -290,19 +377,63 @@ export interface NovelState {
290
377
  }>;
291
378
  premise: string;
292
379
  hooks: string[];
380
+ scene_count?: number;
293
381
  } | null;
294
382
  adventure_scene_waypoint: {
295
383
  anchor: string;
296
384
  description: string;
297
385
  } | null;
298
386
  world: WorldModel;
387
+ story_beats: StoryBeat[];
388
+ pacing_counter: number;
389
+ pacing_autonomy_fired: boolean;
390
+ scene_transition_count: number;
391
+ faction_autonomous_ticks: Record<string, number>;
392
+ npc_goal_suggestions: GoalSuggestion[];
393
+ voice_corrections_this_session: number;
394
+ auto_record: boolean;
395
+ session_no_mutation_windows: string[];
396
+ state_regression: {
397
+ audit_gap: number;
398
+ timestamp_gap_ms: number;
399
+ recorded_at: string;
400
+ } | null;
401
+ last_mutation_at: string | null;
402
+ mutation_counts_by_group: Record<string, number>;
403
+ uncommitted_rolls: Array<{
404
+ roll: string;
405
+ suggested_tool: string;
406
+ at: string;
407
+ }>;
299
408
  metadata: {
300
409
  created: string;
301
410
  modified: string;
302
411
  session_count: number;
303
412
  total_combat_rounds: number;
304
413
  last_scene_anchor: string;
414
+ sessions: Array<{
415
+ session_id: string;
416
+ entry_count: number;
417
+ timespan_start: string;
418
+ timespan_end: string | null;
419
+ combat_rounds: number;
420
+ significant_roll_count: number;
421
+ scene_transitions: number;
422
+ }>;
305
423
  };
424
+ active_session_id: string | null;
425
+ pending_vow_countdown_suggestion: {
426
+ vow_name: string;
427
+ countdown_name: string;
428
+ tick_count: number;
429
+ scope: string;
430
+ } | null;
431
+ codex_sources: Array<{
432
+ id: string;
433
+ kind: string;
434
+ imported_at: string;
435
+ codex_modified_at: string;
436
+ }>;
306
437
  }
307
438
  export interface RosterEntity extends NovelEntity {
308
439
  }
@@ -323,7 +454,10 @@ export declare class StateManager {
323
454
  enriched: boolean;
324
455
  enrichmentManifest: any;
325
456
  maxLoreTokens: number | null;
326
- serverNotes: Map<string, string>;
457
+ serverNotes: Map<string, {
458
+ content: string;
459
+ narrative_tag?: string;
460
+ }>;
327
461
  codex: Map<string, CodexEntry>;
328
462
  private npcCounter;
329
463
  private entityCounter;
@@ -342,10 +476,25 @@ export declare class StateManager {
342
476
  resumeNovel(slug: string): NovelState;
343
477
  private loadNovelFromData;
344
478
  switchNovel(slug: string): NovelState;
479
+ renameNovel(novel: NovelState, newSlug: string): NovelState;
345
480
  endNovel(novel: NovelState, dispose: "yes" | "cancel"): {
346
481
  removed: boolean;
347
482
  };
483
+ archiveNovel(slug: string): {
484
+ archived: boolean;
485
+ at: string;
486
+ };
487
+ unarchiveNovel(slug: string): NovelState;
488
+ archivedNovels(): Array<{
489
+ slug: string;
490
+ archived_at: string;
491
+ }>;
348
492
  cleanupExpiredTrash(): void;
493
+ captureWorkflowSnapshot(novel: NovelState): WorkflowSnapshot;
494
+ restoreFromSnapshot(novel: NovelState, snapshot: WorkflowSnapshot | null): {
495
+ timestamp: string;
496
+ };
497
+ checkWorkflowStaleness(novel: NovelState): string | null;
349
498
  snapshot(novel: NovelState, badge: Badge): void;
350
499
  undo(novel: NovelState, badge: Badge): {
351
500
  data: any;
@@ -355,6 +504,11 @@ export declare class StateManager {
355
504
  } | null;
356
505
  audit(novel: NovelState, badge: Badge, tool: string, args: any, output_prefix?: string): void;
357
506
  auditForbidden(badge: Badge, tool: string, args: any): void;
507
+ recordMutation(novel: NovelState, tool: string, group: string): void;
508
+ closeSessionWindow(novel: NovelState, sessionId: string): void;
509
+ stateGate(): "off" | "warn" | "block";
510
+ stateDriftActive(novel: NovelState): boolean;
511
+ markStateRegression(novel: NovelState, auditEntryCountGap: number, timestampGapMs: number): void;
358
512
  verifyAuditChain(novel: NovelState): {
359
513
  valid: boolean;
360
514
  entries: number;
@@ -394,3 +548,6 @@ export declare class StateManager {
394
548
  }, stats?: Record<string, any>): NovelEntity;
395
549
  addEntity(novel: NovelState, entity: NovelEntity): void;
396
550
  }
551
+ export declare function exportNovelJSON(novel: NovelState): any;
552
+ export declare function importNovelJSON(data: any): NovelState;
553
+ export declare function applyNovelState(target: NovelState, source: NovelState): void;