@dsh-agentic-roleplay/state 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -66,7 +66,7 @@ The package owns how a story is remembered, not only where it is stored:
66
66
  - `session.records(scope)` reads the records back in the same wire format.
67
67
  - `session.recall({ characterId, situation, turn, policy })` is what a character is reminded of: events it took in and things it believes, by who the situation mentions, shared words and tags, importance and story-time recency; with `policy.flaky` the older and less important are told as faded or vague, or are gone. Age is elapsed story time from `session.clock`, never turn count; an unimportant event has a six-story-day half-life. It counts what it brings back as recalled, which makes it last longer.
68
68
  - `session.recallStored(query, { queryVector? })` adds the session's search index to the cues: texts that match the situation by words and, with the vector of the situation, by meaning. `session.searchEvents({ text, vector?, fromTurn, toTurn, minImportance, limit })` finds what happened by the same two searches, merged by reciprocal rank.
69
- - `session.recallRanked(query, signal, { queryVector? })` is the call for anything that reminds a character of something: `recall`, ranked by meaning when a plugin has registered a ranker with `roleplayState.rankMemories(ranker)` (an embedding model, a reranker; see `docs/capabilities.md`). `session.recallCandidates(query)` gives what could be recalled, with the text to match, for a ranker.
69
+ - `session.recallRanked(query, signal, { queryVector? })` is the call for anything that reminds a character of something: `recall`, ranked by meaning when a plugin has registered a ranker with `roleplayState.rankMemories(ranker)` (an embedding model, a reranker; see `docs/capabilities.md`). `session.recallCandidates(query)` gives what could be recalled, with the text to match, for a ranker. With `explain: true` in the query each recalled thing carries `why`: the cues of the moment (people named, words shared, place), what each search scored it, how much it matters, how long ago it was, how much of it is left in mind and what it was ranked by.
70
70
  - `session.knowledge` is the closed-world awareness graph for locations, items and directed exits (`learn`, `knows`, `get`, `list`, `forget`). Directional relationships are the corresponding character-to-character edges. Awareness grants identity only, not current state or whereabouts. `session.planKnownMove` plans through only a character's known places and exits; `planMove` remains the authoritative world planner.
71
71
  - `session.factions`, `factionStates`, `factionMemberships`, `factionRelationships` and `factionIntents` store persistent organisations separately from characters that act in scenes. Membership is many-to-many; relationships involving factions are directional and double as closed-world awareness edges.
72
72
  - `roleplayState.bind(resolver)` tells the tools which roleplay session an agent works in (the plugin that runs the agents supplies it, with the turn being played and the memory policy).
@@ -153,6 +153,12 @@ once released: a change is a new entry in `src/sqlite/schema.ts`, and a test
153
153
  pins the migrations of each release. A database from a newer version is refused
154
154
  rather than damaged.
155
155
 
156
+ Before a file that is behind the latest schema is upgraded, it must pass SQLite's
157
+ quick integrity check (a damaged file is refused with `CorruptDataError`, untouched)
158
+ and a copy of it as it was is written to `migration-backups/schema-<version>.sqlite`
159
+ beside the database, once per old version. `migrationBackupPath(location, version)`
160
+ says where.
161
+
156
162
  ## Errors
157
163
 
158
164
  | Error | When |
package/lib/index.d.ts CHANGED
@@ -2502,6 +2502,16 @@ interface MemoryPolicy {
2502
2502
  * reranker judges, and it also gives importance and recency something to choose between. At least 1.
2503
2503
  */
2504
2504
  overRetrieve?: number;
2505
+ /**
2506
+ * How many more things come to mind because they are strongly tied to what the moment recalled (the same people together with the same
2507
+ * place or tags) and matter enough. 0 or absent: none.
2508
+ */
2509
+ associate?: number;
2510
+ /**
2511
+ * How much a memory that says what one already chosen says counts against it, from 0 (the best by score, repeats
2512
+ * included) to 1 (only what adds something new): maximal marginal relevance over the pool. 0 or absent: none.
2513
+ */
2514
+ diversity?: number;
2505
2515
  }
2506
2516
  declare const DEFAULT_MEMORY_POLICY: MemoryPolicy;
2507
2517
  /** How much of a memory is left: what the character is told of it. */
@@ -2519,6 +2529,38 @@ interface Recalled {
2519
2529
  kind: 'perception' | 'memory';
2520
2530
  id: number;
2521
2531
  };
2532
+ /** It did not match the moment itself: it came to mind with something that did ({@link MemoryPolicy.associate}). */
2533
+ linked?: true;
2534
+ /** Why it came back, when the query asked to be told ({@link RecallQuery.explain}). */
2535
+ why?: RecallWhy;
2536
+ }
2537
+ /** What made one thing come back, for finding out why a character was or was not reminded of something. */
2538
+ interface RecallWhy {
2539
+ /** What the cues of the moment gave, in the units of a cue: people named (a name counts 3, more for social weight), words and tags shared (at most 6), being where it happened. */
2540
+ cues: {
2541
+ people: number;
2542
+ words: number;
2543
+ place: number;
2544
+ };
2545
+ /** What each search scored it, from 0 to 1; absent when that search did not run or did not find it. */
2546
+ lexical?: number;
2547
+ dense?: number;
2548
+ rerank?: number;
2549
+ /** What it counted for in the end: the fused retrieval score, blended with the reranker's when there was one. */
2550
+ relevance: number;
2551
+ importance: number;
2552
+ /** How long ago it was in story days; 0 when the story has no clock. */
2553
+ ageDays: number;
2554
+ /** How much of it is left in mind, from 1 to 0, when memories fade. */
2555
+ retention?: number;
2556
+ /** What it was ranked by, together with how much it matters and how recent it is. */
2557
+ score: number;
2558
+ /** For something that came to mind with another: what it shares with the closest of the things recalled for the moment. */
2559
+ linked?: {
2560
+ people: number;
2561
+ tags: number;
2562
+ place: boolean;
2563
+ };
2522
2564
  }
2523
2565
  interface RecallQuery {
2524
2566
  characterId: CharacterId;
@@ -2530,6 +2572,10 @@ interface RecallQuery {
2530
2572
  locationId?: number;
2531
2573
  turn: number;
2532
2574
  policy: MemoryPolicy;
2575
+ /** Say why each thing came back ({@link Recalled.why}). */
2576
+ explain?: boolean;
2577
+ /** Only what involves or is about one of these people comes back: "what do I know about her", which a name in the words would not reliably find. */
2578
+ about?: readonly CharacterId[];
2533
2579
  }
2534
2580
  /** What recall reads: the events a character took in and what it believes. */
2535
2581
  interface RecallSource {
@@ -2556,14 +2602,22 @@ declare function clarityAt(value: number, importance: number): Clarity | undefin
2556
2602
  declare function tokens(text: string): Set<string>;
2557
2603
  /** The characters a text mentions by name or alias. */
2558
2604
  declare function mentioned(text: string, characters: readonly Character[]): Set<number>;
2605
+ /** What ties a candidate to others: the people it involves (other than the one remembering), its tags and where it happened. */
2606
+ interface Links {
2607
+ people: readonly number[];
2608
+ tags: readonly string[];
2609
+ place?: number;
2610
+ }
2559
2611
  interface Candidate {
2560
2612
  key: string;
2561
- recalled: Omit<Recalled, 'clarity' | 'text'> & {
2613
+ links: Links;
2614
+ recalled: Omit<Recalled, 'clarity' | 'text' | 'why'> & {
2562
2615
  clear: string;
2563
2616
  faded: string;
2564
2617
  vague: string;
2565
2618
  };
2566
2619
  relevance: number;
2620
+ cues: RecallWhy['cues'];
2567
2621
  importance: number;
2568
2622
  ageDays: number;
2569
2623
  recalls: number;
@@ -2592,7 +2646,7 @@ interface RecallEvidence {
2592
2646
  rerank?: ReadonlyMap<string, number>;
2593
2647
  }
2594
2648
  /** Everything the character could be reminded of, for a caller that ranks by meaning (an embedding, a reranker) before asking for the recall. */
2595
- declare function recallCandidates(source: RecallSource, query: Pick<RecallQuery, 'characterId' | 'characters' | 'situation' | 'turn' | 'locationId'>): RecallCandidate[];
2649
+ declare function recallCandidates(source: RecallSource, query: Pick<RecallQuery, 'characterId' | 'characters' | 'situation' | 'turn' | 'locationId' | 'about'>): RecallCandidate[];
2596
2650
  /** Fuse scores from 0 to 1 into one, per key: the weighted average over the searches that ran (those with any score above 0). */
2597
2651
  declare function fuseScores(searches: readonly {
2598
2652
  weight: number;
@@ -2602,11 +2656,18 @@ interface PoolEntry {
2602
2656
  candidate: Candidate;
2603
2657
  /** How well retrieval alone matches the moment, from 0 to 1. */
2604
2658
  fused: number;
2659
+ /** What each search scored it, when it was found by it. */
2660
+ found: {
2661
+ lexical?: number;
2662
+ dense?: number;
2663
+ };
2605
2664
  }
2606
2665
  /** What retrieval over-retrieved for one recall, waiting for a reranker's judgement and the final choice. */
2607
2666
  interface RecallPool {
2608
2667
  entries: readonly PoolEntry[];
2609
- query: Pick<RecallQuery, 'policy'>;
2668
+ /** What retrieval left out, which an association may still bring to mind. */
2669
+ others: readonly Candidate[];
2670
+ query: Pick<RecallQuery, 'policy' | 'explain'>;
2610
2671
  }
2611
2672
  /**
2612
2673
  * The first stage of reminding a character of what the moment calls to mind: retrieve, by the cues
@@ -2619,7 +2680,7 @@ declare function recallPool(source: RecallSource, query: RecallQuery, evidence?:
2619
2680
  /** What a reranker is asked to judge: every candidate of the pool, with the text it would be recalled by. */
2620
2681
  declare function poolCandidates(pool: RecallPool): RecallCandidate[];
2621
2682
  /**
2622
- * The last stage: choose the `policy.recall` best of the pool. With a reranker's judgement each
2683
+ * The last stage: choose the `policy.recall` best of the pool, spread by `policy.diversity`. With a reranker's judgement each
2623
2684
  * candidate's relevance is mostly that (a candidate it left out counts as 0), and the rest is what
2624
2685
  * retrieval found; without, it is what retrieval found.
2625
2686
  */
@@ -2963,6 +3024,10 @@ declare function createSessionStore(location: string, session: NewSession, optio
2963
3024
  * @throws {SchemaError} when the file is not a session database, or is from a newer version.
2964
3025
  */
2965
3026
  declare function openSessionStore(location: string, options?: StoreOptions): SessionStore;
3027
+ /** Folder, beside the database, holding the copies made before a schema upgrade. */
3028
+ declare const MIGRATION_BACKUP_FOLDER = "migration-backups";
3029
+ /** Where the copy of a database as it was at schema `version` is kept. */
3030
+ declare function migrationBackupPath(location: string, version: number): string;
2966
3031
  //#endregion
2967
3032
  //#region src/sessions.d.ts
2968
3033
  declare const SESSION_DATABASE_FILE = "session.sqlite";
@@ -3308,5 +3373,5 @@ type LintTarget = Pick<RecordsTarget, 'characters' | 'locations' | 'placements'
3308
3373
  */
3309
3374
  declare function lintWorld(target: LintTarget): LintFinding[];
3310
3375
  //#endregion
3311
- export { ACCURACIES, Accuracy, type Agenda, type AgendaEngagement, type AgendaOptions, type AgentLink, type AgentResolver, type Announcement, type BeliefEdit, CHARACTER_CONTROLS, CHARACTER_KINDS, CHARACTER_ROLES, CHARACTER_TIERS, CLOCK_MODES, COMPARISONS, CONSOLIDATION_KINDS, CONTAINER_STATES, Character, CharacterControl, CharacterDefinition, CharacterFilter, CharacterId, CharacterIntent, CharacterKind, CharacterMemory, CharacterMemoryPatch, type CharacterMove, CharacterPatch, type CharacterRepository, CharacterRole, CharacterSpeech, CharacterStart, CharacterState, CharacterStatePatch, type CharacterStateRepository, CharacterTier, type Clarity, type Clock, ClockMode, ClockState, Comparison, Config, ConflictError, ConsolidationItem, ConsolidationItemId, ConsolidationKind, type ConsolidationRepository, ContainerSpec, ContainerState, CorruptDataError, DAY, DAY_PARTS, DEFAULT_MEMORY_POLICY, DEFAULT_SLOTS, type DayPart, EMOTION_VARIABLES, ENGAGEMENT_GRACE, ENGAGEMENT_STATUSES, EXIT_STATES, type EditChecker, type EditResult, EmbeddingIndexInfo, EmotionStart, EmotionVector, Engagement, EngagementFilter, EngagementId, EngagementPatch, EngagementStatus, EntityKnowledge, type EventEdit, EventId, type EventRepository, Exit, type ExitChange, ExitDefinition, ExitId, ExitOpener, ExitPatch, ExitState, Faction, FactionDefinition, FactionId, FactionIntent, type FactionIntentRepository, FactionMembership, FactionMembershipDefinition, type FactionMembershipRepository, FactionPatch, FactionRelationship, FactionRelationshipDefinition, type FactionRelationshipRepository, type FactionRepository, FactionState, FactionStatePatch, type FactionStateRepository, type FiredTrigger, HOUR, Holder, Holding, IMPORTANCE_MAX, IMPORTANCE_MIN, INTENT_HORIZONS, INTENT_STATUSES, INTENT_TYPES, ITEM_ACTIONS, Intent, IntentFilter, IntentHorizon, IntentId, IntentPatch, type IntentRepository, IntentStatus, IntentType, InvalidEditError, InvalidMoveError, InvalidValueError, Item, type ItemAction, type ItemChange, type ItemChangeSpec, type ItemContext, ItemDefinition, type ItemEditSpec, type ItemEffect, ItemId, ItemPatch, type ItemPlan, type ItemRepository, JsonValue, KNOWLEDGE_KINDS, KnowledgeDefinition, KnowledgeKind, type KnowledgeRepository, type KnownMoveContext, LORE_KINDS, LORE_SUBJECT_KINDS, type LintFinding, type LintSeverity, type LintTarget, type LocalTime, Location, LocationDefinition, LocationFeature, LocationId, LocationPatch, type LocationRepository, Lore, LoreDefinition, LoreId, LoreKind, LorePatch, LoreSubject, LoreSubjectDefinition, LoreSubjectKind, MAX_LOCATION_FEATURES, MAX_ROUTINE_WINDOWS, MAX_SHOWN, MAX_TRIGGERS, MAX_VARIABLES, MEMBERSHIP_STATUSES, MEMORY_KINDS, MINUTE, MembershipPatch, MembershipStatus, MemoryId, MemoryKind, type MemoryPolicy, type MemoryRepository, type MetadataRepository, Move, type MoveContext, MovePlan, type NewBelief, NewCharacter, NewCharacterMemory, type NewCharacterSpec, NewCharacterState, NewEngagement, type NewEvent, NewExit, type NewExitBetween, type NewExit$1 as NewExitEdit, NewFaction, NewFactionState, NewItem, type NewItemSpec, NewLocation, NewLore, NewPerception, type NewPlace, NewPlacement, NewRelationship, NewSession, NewStoryEvent, NewSummary, NewTriggerNote, NotFoundError, type Occurrence, type OpenOptions, PERCEPTION_MODES, PLACEABLE_KINDS, PLOT_NUDGE_MODES, PLOT_STATUSES, PendingCommit, type PendingCommitRepository, PendingEmbedding, type PerceivedBy, PerceivedEvent, Perception, type PerceptionAddition, type PerceptionEdit, PerceptionId, PerceptionMode, PerceptionPatch, type PlaceEdit, PlaceableKind, Placement, type PlacementRepository, PlotDefinition, PlotDevelopment, PlotNudgeMode, PlotProgress, PlotStatus, RECALL_ITEM_KINDS, type RankedGoal, type RecallCandidate, type RecallEvidence, RecallHit, type RecallIndexRepository, RecallItemKind, type RecallPool, type RecallQuery, type RecallSearch, type RecallSource, type Recalled, type RecordsEdit, type RecordsScope, type RecordsTarget, type RecordsView, Relationship, RelationshipDefinition, RelationshipId, RelationshipPatch, type RelationshipRepository, type RelationshipRetraction, type RelationshipSet, RoleplayState, RoutineDefinition, RoutineWindow, type RunResult, LATEST_VERSION as SCHEMA_VERSION, SECOND, SESSION_DATABASE_FILE, SLOT_ID, SUMMARY_LEVELS, SchemaError, type SemanticRanker, type Session, type SessionInit, type SessionListing, SessionMetadata, type SessionStore, type ShownVariable, Slot, type SlotRepository, SocialSubjectKind, type SqlDatabase, type SqlDriver, type SqlValue, StateError, type StatePatch, type StoreOptions, StoredRoutineWindow, StoredVariable, StoryEvent, StoryEventFilter, StoryEventPatch, StoryThread, Summary, SummaryId, SummaryLevel, type SummaryRepository, THREAD_STATUSES, TIME_CHANGE_KINDS, TRIGGER_NOTE_KINDS, TRIGGER_STATUSES, ThreadId, type ThreadRepository, ThreadStatus, TimeChange, TimeChangeFilter, TimeChangeKind, Trigger, TriggerAnnouncement, TriggerAudience, type TriggerClock, TriggerCondition, TriggerDefinition, TriggerEffect, TriggerId, TriggerNote, TriggerNoteKind, type TriggerRun, type TriggerRunOptions, TriggerStatus, VARIABLE_KEY, VARIABLE_KINDS, VARIABLE_SUBJECTS, VARIABLE_VISIBILITIES, VISUAL_SUBJECT_KINDS, type VariableAudience, VariableBand, type VariableChangeSpec, type VariableContext, VariableDefinition, VariableDefinitionInput, type VariableEffect, VariableKind, type VariablePlan, VariableSubject, VariableValue, VariableVisibility, type VectorSearch, type VectorSupport, VisualIdentity, VisualIdentityInput, VisualIdentityPatch, type VisualIdentityRepository, VisualSubjectKind, WEEKDAY_NAMES, WorldReference, addLocalDays, agendaAt, announcements, apply, applyItemPlan, applyRecordsEdit, audienceIds, catchUpPlan, changeItem, changeVariable, characterKey, clarityAt, clockTime, conditionHolds, createCharacterTools, createSession, createSessionStore, createTools, dayPart, deleteSession, denseScore, describeDuration, describeHolder, describeInstant, describeValue, finishRecall, fuseScores, inAudience, inWindow, isSessionId, isTimeZone, itemKey, jitter, judgedStatements, lapseNotes, lintWorld, listSessions, localIso, localTime, locationKey, mentioned, moveCharacter, name, nextOccurrence, nextTimeOfDay, nodeSqliteDriver, occurrenceAt, openSession, openSessionStore, overlap, parseInstant, parseRecordsEdit, parseTimeOfDay, parseTriggerDefinition, planItemChange, planKnownMove, planMove, planVariableChange, poolCandidates, rankGoals, readRecords, readTriggerDefinition, recall, recallCandidates, recallPool, recordsEditSchema, retention, runTriggers, seededRoll, sessionDatabase, sessionFolder, shownVariables, subjectOf, systemClock, systemTimeZone, tokens, triggerReferenceProblems, valueIssue, zonedInstant };
3376
+ export { ACCURACIES, Accuracy, type Agenda, type AgendaEngagement, type AgendaOptions, type AgentLink, type AgentResolver, type Announcement, type BeliefEdit, CHARACTER_CONTROLS, CHARACTER_KINDS, CHARACTER_ROLES, CHARACTER_TIERS, CLOCK_MODES, COMPARISONS, CONSOLIDATION_KINDS, CONTAINER_STATES, Character, CharacterControl, CharacterDefinition, CharacterFilter, CharacterId, CharacterIntent, CharacterKind, CharacterMemory, CharacterMemoryPatch, type CharacterMove, CharacterPatch, type CharacterRepository, CharacterRole, CharacterSpeech, CharacterStart, CharacterState, CharacterStatePatch, type CharacterStateRepository, CharacterTier, type Clarity, type Clock, ClockMode, ClockState, Comparison, Config, ConflictError, ConsolidationItem, ConsolidationItemId, ConsolidationKind, type ConsolidationRepository, ContainerSpec, ContainerState, CorruptDataError, DAY, DAY_PARTS, DEFAULT_MEMORY_POLICY, DEFAULT_SLOTS, type DayPart, EMOTION_VARIABLES, ENGAGEMENT_GRACE, ENGAGEMENT_STATUSES, EXIT_STATES, type EditChecker, type EditResult, EmbeddingIndexInfo, EmotionStart, EmotionVector, Engagement, EngagementFilter, EngagementId, EngagementPatch, EngagementStatus, EntityKnowledge, type EventEdit, EventId, type EventRepository, Exit, type ExitChange, ExitDefinition, ExitId, ExitOpener, ExitPatch, ExitState, Faction, FactionDefinition, FactionId, FactionIntent, type FactionIntentRepository, FactionMembership, FactionMembershipDefinition, type FactionMembershipRepository, FactionPatch, FactionRelationship, FactionRelationshipDefinition, type FactionRelationshipRepository, type FactionRepository, FactionState, FactionStatePatch, type FactionStateRepository, type FiredTrigger, HOUR, Holder, Holding, IMPORTANCE_MAX, IMPORTANCE_MIN, INTENT_HORIZONS, INTENT_STATUSES, INTENT_TYPES, ITEM_ACTIONS, Intent, IntentFilter, IntentHorizon, IntentId, IntentPatch, type IntentRepository, IntentStatus, IntentType, InvalidEditError, InvalidMoveError, InvalidValueError, Item, type ItemAction, type ItemChange, type ItemChangeSpec, type ItemContext, ItemDefinition, type ItemEditSpec, type ItemEffect, ItemId, ItemPatch, type ItemPlan, type ItemRepository, JsonValue, KNOWLEDGE_KINDS, KnowledgeDefinition, KnowledgeKind, type KnowledgeRepository, type KnownMoveContext, LORE_KINDS, LORE_SUBJECT_KINDS, type LintFinding, type LintSeverity, type LintTarget, type LocalTime, Location, LocationDefinition, LocationFeature, LocationId, LocationPatch, type LocationRepository, Lore, LoreDefinition, LoreId, LoreKind, LorePatch, LoreSubject, LoreSubjectDefinition, LoreSubjectKind, MAX_LOCATION_FEATURES, MAX_ROUTINE_WINDOWS, MAX_SHOWN, MAX_TRIGGERS, MAX_VARIABLES, MEMBERSHIP_STATUSES, MEMORY_KINDS, MIGRATION_BACKUP_FOLDER, MINUTE, MembershipPatch, MembershipStatus, MemoryId, MemoryKind, type MemoryPolicy, type MemoryRepository, type MetadataRepository, Move, type MoveContext, MovePlan, type NewBelief, NewCharacter, NewCharacterMemory, type NewCharacterSpec, NewCharacterState, NewEngagement, type NewEvent, NewExit, type NewExitBetween, type NewExit$1 as NewExitEdit, NewFaction, NewFactionState, NewItem, type NewItemSpec, NewLocation, NewLore, NewPerception, type NewPlace, NewPlacement, NewRelationship, NewSession, NewStoryEvent, NewSummary, NewTriggerNote, NotFoundError, type Occurrence, type OpenOptions, PERCEPTION_MODES, PLACEABLE_KINDS, PLOT_NUDGE_MODES, PLOT_STATUSES, PendingCommit, type PendingCommitRepository, PendingEmbedding, type PerceivedBy, PerceivedEvent, Perception, type PerceptionAddition, type PerceptionEdit, PerceptionId, PerceptionMode, PerceptionPatch, type PlaceEdit, PlaceableKind, Placement, type PlacementRepository, PlotDefinition, PlotDevelopment, PlotNudgeMode, PlotProgress, PlotStatus, RECALL_ITEM_KINDS, type RankedGoal, type RecallCandidate, type RecallEvidence, RecallHit, type RecallIndexRepository, RecallItemKind, type RecallPool, type RecallQuery, type RecallSearch, type RecallSource, type RecallWhy, type Recalled, type RecordsEdit, type RecordsScope, type RecordsTarget, type RecordsView, Relationship, RelationshipDefinition, RelationshipId, RelationshipPatch, type RelationshipRepository, type RelationshipRetraction, type RelationshipSet, RoleplayState, RoutineDefinition, RoutineWindow, type RunResult, LATEST_VERSION as SCHEMA_VERSION, SECOND, SESSION_DATABASE_FILE, SLOT_ID, SUMMARY_LEVELS, SchemaError, type SemanticRanker, type Session, type SessionInit, type SessionListing, SessionMetadata, type SessionStore, type ShownVariable, Slot, type SlotRepository, SocialSubjectKind, type SqlDatabase, type SqlDriver, type SqlValue, StateError, type StatePatch, type StoreOptions, StoredRoutineWindow, StoredVariable, StoryEvent, StoryEventFilter, StoryEventPatch, StoryThread, Summary, SummaryId, SummaryLevel, type SummaryRepository, THREAD_STATUSES, TIME_CHANGE_KINDS, TRIGGER_NOTE_KINDS, TRIGGER_STATUSES, ThreadId, type ThreadRepository, ThreadStatus, TimeChange, TimeChangeFilter, TimeChangeKind, Trigger, TriggerAnnouncement, TriggerAudience, type TriggerClock, TriggerCondition, TriggerDefinition, TriggerEffect, TriggerId, TriggerNote, TriggerNoteKind, type TriggerRun, type TriggerRunOptions, TriggerStatus, VARIABLE_KEY, VARIABLE_KINDS, VARIABLE_SUBJECTS, VARIABLE_VISIBILITIES, VISUAL_SUBJECT_KINDS, type VariableAudience, VariableBand, type VariableChangeSpec, type VariableContext, VariableDefinition, VariableDefinitionInput, type VariableEffect, VariableKind, type VariablePlan, VariableSubject, VariableValue, VariableVisibility, type VectorSearch, type VectorSupport, VisualIdentity, VisualIdentityInput, VisualIdentityPatch, type VisualIdentityRepository, VisualSubjectKind, WEEKDAY_NAMES, WorldReference, addLocalDays, agendaAt, announcements, apply, applyItemPlan, applyRecordsEdit, audienceIds, catchUpPlan, changeItem, changeVariable, characterKey, clarityAt, clockTime, conditionHolds, createCharacterTools, createSession, createSessionStore, createTools, dayPart, deleteSession, denseScore, describeDuration, describeHolder, describeInstant, describeValue, finishRecall, fuseScores, inAudience, inWindow, isSessionId, isTimeZone, itemKey, jitter, judgedStatements, lapseNotes, lintWorld, listSessions, localIso, localTime, locationKey, mentioned, migrationBackupPath, moveCharacter, name, nextOccurrence, nextTimeOfDay, nodeSqliteDriver, occurrenceAt, openSession, openSessionStore, overlap, parseInstant, parseRecordsEdit, parseTimeOfDay, parseTriggerDefinition, planItemChange, planKnownMove, planMove, planVariableChange, poolCandidates, rankGoals, readRecords, readTriggerDefinition, recall, recallCandidates, recallPool, recordsEditSchema, retention, runTriggers, seededRoll, sessionDatabase, sessionFolder, shownVariables, subjectOf, systemClock, systemTimeZone, tokens, triggerReferenceProblems, valueIssue, zonedInstant };
3312
3377
  //# sourceMappingURL=index.d.ts.map
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { DatabaseSync } from "node:sqlite";
3
- import { join, resolve } from "node:path";
3
+ import { dirname, join, resolve } from "node:path";
4
4
  import { Service } from "@deepseek-ai/cordis";
5
5
  import z from "@deepseek-ai/schemastery";
6
6
  import { randomUUID } from "node:crypto";
@@ -1922,9 +1922,15 @@ function describeValue(def, value) {
1922
1922
  const band = def.bands.find((candidate) => value <= candidate.upTo) ?? def.bands.at(-1);
1923
1923
  return band === void 0 ? shortNumber(value) : `${band.label} (${shortNumber(value)})`;
1924
1924
  }
1925
+ /** A variable written by its name or in other letter case ("Valence" for `valence`), when exactly one variable has that name or key. */
1926
+ function namedVariable(variables, written) {
1927
+ const wanted = written.trim().normalize("NFKC").toLowerCase();
1928
+ const found = wanted === "" ? [] : variables.defs().filter((def) => def.key.normalize("NFKC").toLowerCase() === wanted || def.name.normalize("NFKC").toLowerCase() === wanted);
1929
+ return found.length === 1 ? found[0] : void 0;
1930
+ }
1925
1931
  /** Whether a change can be made, and what it would come to. Nothing is changed. */
1926
1932
  function planVariableChange(context, spec) {
1927
- const def = context.variables.def(spec.variable);
1933
+ const def = context.variables.def(spec.variable) ?? namedVariable(context.variables, spec.variable);
1928
1934
  if (def === void 0) return {
1929
1935
  ok: false,
1930
1936
  problems: [`there is no variable "${spec.variable}" (list_variables shows them)`]
@@ -5210,7 +5216,9 @@ const DAY$1 = 864e5;
5210
5216
  const DEFAULT_MEMORY_POLICY = {
5211
5217
  flaky: true,
5212
5218
  recall: 12,
5213
- overRetrieve: 4
5219
+ overRetrieve: 4,
5220
+ associate: 2,
5221
+ diversity: .3
5214
5222
  };
5215
5223
  /** Story days for the memory of an unimportant event to halve; each step of importance doubles it. */
5216
5224
  const HALF_LIFE_DAYS = 6;
@@ -5341,7 +5349,7 @@ const DENSE_RANGE = .5;
5341
5349
  const denseScore = (similarity) => Math.min(1, Math.max(0, (similarity - DENSE_FLOOR) / DENSE_RANGE));
5342
5350
  /** Being where something happened calls it to mind, about as much as sharing two words with it. */
5343
5351
  const PLACE_CUE = 2;
5344
- function gather(source, { characterId, characters, situation, locationId }) {
5352
+ function gather(source, { characterId, characters, situation, locationId, about }) {
5345
5353
  const names = new Map(characters.map((character) => [character.id, character.name]));
5346
5354
  const peers = mentioned(situation, characters);
5347
5355
  peers.delete(characterId);
@@ -5359,24 +5367,46 @@ function gather(source, { characterId, characters, situation, locationId }) {
5359
5367
  }
5360
5368
  const atTurn = (eventTurn) => turns.get(eventTurn) ?? start;
5361
5369
  const ageDays = (at) => now === void 0 || at === void 0 ? 0 : Math.max(0, now - at) / DAY$1;
5362
- return [...source.events.perceivedBy(characterId).map((seen) => {
5370
+ const concerns = (ids) => about === void 0 || about.length === 0 || ids.some((id) => about.includes(id));
5371
+ return [...source.events.perceivedBy(characterId).filter((seen) => concerns(seen.event.involves)).map((seen) => {
5363
5372
  const at = rounds.get(`${seen.event.turn}:${seen.event.round}`) ?? atTurn(seen.event.turn);
5373
+ const cue = {
5374
+ people: peopleCue(seen.event.involves),
5375
+ words: overlap(cues, `${seen.perception.content} ${seen.event.text}`, seen.event.tags),
5376
+ place: locationId !== void 0 && seen.event.locationId === locationId ? PLACE_CUE : 0
5377
+ };
5364
5378
  return {
5365
5379
  key: `p:${seen.perception.id}`,
5380
+ links: {
5381
+ people: seen.event.involves.filter((id) => id !== characterId),
5382
+ tags: seen.event.tags.map((tag) => tag.toLowerCase()),
5383
+ ...seen.event.locationId === void 0 ? {} : { place: seen.event.locationId }
5384
+ },
5366
5385
  recalled: describeEvent(seen, names, at),
5367
- relevance: peopleCue(seen.event.involves) + overlap(cues, `${seen.perception.content} ${seen.event.text}`, seen.event.tags) + (locationId !== void 0 && seen.event.locationId === locationId ? PLACE_CUE : 0),
5386
+ cues: cue,
5387
+ relevance: cue.people + cue.words + cue.place,
5368
5388
  importance: seen.event.importance,
5369
5389
  ageDays: ageDays(at),
5370
5390
  recalls: seen.perception.recalls,
5371
5391
  seed: seen.perception.id,
5372
5392
  durability: 1
5373
5393
  };
5374
- }), ...source.memories.list(characterId).map((belief) => {
5394
+ }), ...source.memories.list(characterId).filter((belief) => concerns(belief.about)).map((belief) => {
5375
5395
  const at = atTurn(belief.turn);
5396
+ const cue = {
5397
+ people: peopleCue(belief.about),
5398
+ words: overlap(cues, belief.text, []),
5399
+ place: 0
5400
+ };
5376
5401
  return {
5377
5402
  key: `m:${belief.id}`,
5403
+ links: {
5404
+ people: belief.about.filter((id) => id !== characterId),
5405
+ tags: []
5406
+ },
5378
5407
  recalled: describeBelief(belief, at),
5379
- relevance: peopleCue(belief.about) + overlap(cues, belief.text, []),
5408
+ cues: cue,
5409
+ relevance: cue.people + cue.words,
5380
5410
  importance: belief.importance,
5381
5411
  ageDays: ageDays(at),
5382
5412
  recalls: belief.recalls,
@@ -5405,20 +5435,25 @@ function fuseScores(searches) {
5405
5435
  /** How well a candidate stands once its relevance is known: what the moment calls to mind, how much it matters and how recent it is. */
5406
5436
  function judged(candidate, relevance, policy) {
5407
5437
  let clarity = "clear";
5408
- if (policy.flaky) clarity = clarityAt(retention({
5409
- ageDays: candidate.ageDays,
5410
- importance: candidate.importance,
5411
- recalls: candidate.recalls,
5412
- seed: candidate.seed,
5413
- durability: candidate.durability,
5414
- cue: relevance >= CUE_RELEVANCE
5415
- }), candidate.importance);
5438
+ let kept;
5439
+ if (policy.flaky) {
5440
+ kept = retention({
5441
+ ageDays: candidate.ageDays,
5442
+ importance: candidate.importance,
5443
+ recalls: candidate.recalls,
5444
+ seed: candidate.seed,
5445
+ durability: candidate.durability,
5446
+ cue: relevance >= CUE_RELEVANCE
5447
+ });
5448
+ clarity = clarityAt(kept, candidate.importance);
5449
+ }
5416
5450
  if (clarity === void 0) return void 0;
5417
5451
  const score = RELEVANCE_SCALE * relevance + .6 * candidate.importance + 2 / (1 + Math.max(0, candidate.ageDays)) + (clarity === "vague" ? -1 : 0);
5418
5452
  return {
5419
5453
  clarity,
5420
5454
  score,
5421
- text: candidate.recalled[clarity === "clear" ? "clear" : clarity === "faded" ? "faded" : "vague"]
5455
+ text: candidate.recalled[clarity === "clear" ? "clear" : clarity === "faded" ? "faded" : "vague"],
5456
+ ...kept === void 0 ? {} : { retention: kept }
5422
5457
  };
5423
5458
  }
5424
5459
  /**
@@ -5429,7 +5464,7 @@ function judged(candidate, relevance, policy) {
5429
5464
  * is, and not what has faded from memory altogether). A reranker may then judge that pool.
5430
5465
  */
5431
5466
  function recallPool(source, query, evidence = {}) {
5432
- const { policy } = query;
5467
+ const { policy, explain } = query;
5433
5468
  const candidates = gather(source, query);
5434
5469
  const fused = fuseScores([
5435
5470
  {
@@ -5446,18 +5481,28 @@ function recallPool(source, query, evidence = {}) {
5446
5481
  }
5447
5482
  ]);
5448
5483
  const width = Math.min(MAX_POOL, Math.max(policy.recall, Math.ceil(policy.recall * Math.max(1, policy.overRetrieve ?? 1))));
5484
+ const entries = candidates.map((candidate) => ({
5485
+ candidate,
5486
+ fused: fused.get(candidate.key) ?? 0,
5487
+ found: {
5488
+ ...evidence.lexical?.get(candidate.key) === void 0 ? {} : { lexical: evidence.lexical.get(candidate.key) },
5489
+ ...evidence.dense?.get(candidate.key) === void 0 ? {} : { dense: evidence.dense.get(candidate.key) }
5490
+ }
5491
+ })).flatMap((entry) => {
5492
+ const standing = judged(entry.candidate, entry.fused, policy);
5493
+ return standing === void 0 ? [] : [{
5494
+ entry,
5495
+ score: standing.score
5496
+ }];
5497
+ }).sort((a, b) => b.score - a.score || b.entry.candidate.recalled.turn - a.entry.candidate.recalled.turn).map(({ entry }) => entry).slice(0, width);
5498
+ const kept = new Set(entries.map((entry) => entry.candidate.key));
5449
5499
  return {
5450
- entries: candidates.map((candidate) => ({
5451
- candidate,
5452
- fused: fused.get(candidate.key) ?? 0
5453
- })).flatMap((entry) => {
5454
- const standing = judged(entry.candidate, entry.fused, policy);
5455
- return standing === void 0 ? [] : [{
5456
- entry,
5457
- score: standing.score
5458
- }];
5459
- }).sort((a, b) => b.score - a.score || b.entry.candidate.recalled.turn - a.entry.candidate.recalled.turn).slice(0, width).map(({ entry }) => entry),
5460
- query: { policy }
5500
+ entries,
5501
+ others: candidates.filter((candidate) => !kept.has(candidate.key)),
5502
+ query: {
5503
+ policy,
5504
+ ...explain === true ? { explain } : {}
5505
+ }
5461
5506
  };
5462
5507
  }
5463
5508
  /** What a reranker is asked to judge: every candidate of the pool, with the text it would be recalled by. */
@@ -5469,26 +5514,129 @@ function poolCandidates(pool) {
5469
5514
  importance: candidate.importance
5470
5515
  }));
5471
5516
  }
5517
+ /** Two things are tied by the same people, the same tags and the same place; this is how strongly (people count most, tags and place add a little). */
5518
+ function tie(a, b) {
5519
+ const people = a.people.filter((id) => b.people.includes(id)).length;
5520
+ const tags = a.tags.filter((tag) => b.tags.includes(tag)).length;
5521
+ const place = a.place !== void 0 && a.place === b.place;
5522
+ return {
5523
+ strength: 2 * Math.min(people, 2) + Math.min(tags, 2) + (place ? 1 : 0),
5524
+ people,
5525
+ tags,
5526
+ place
5527
+ };
5528
+ }
5529
+ /** The least a tie has to be for one thing to bring the other to mind. */
5530
+ const STRONG_TIE = 3;
5531
+ /** What does not matter much is not brought back by association. */
5532
+ const ASSOCIABLE_IMPORTANCE = 3;
5533
+ /** How much two texts say the same, from 0 to 1: the share of their words (or pairs of characters) they have in common. */
5534
+ function resemblance(a, b) {
5535
+ if (a.size === 0 || b.size === 0) return 0;
5536
+ let shared = 0;
5537
+ for (const word of a) if (b.has(word)) shared++;
5538
+ return shared / (a.size + b.size - shared);
5539
+ }
5540
+ /**
5541
+ * Choose `count` of the ranked entries by maximal marginal relevance: each pick is the one with the best score
5542
+ * discounted by how much it resembles what is already chosen, so that three memories of the same moment do not
5543
+ * crowd out one of another. `diversity` 0 keeps the ranking as it is.
5544
+ */
5545
+ function diversified(ranked, count, diversity) {
5546
+ const weight = Math.min(1, Math.max(0, diversity));
5547
+ if (weight === 0 || ranked.length <= 1) return ranked.slice(0, count);
5548
+ const top = Math.max(...ranked.map((entry) => entry.score), Number.EPSILON);
5549
+ const words = new Map(ranked.map((entry) => [entry, tokens(entry.candidate.recalled.clear)]));
5550
+ const left = [...ranked];
5551
+ const chosen = [];
5552
+ while (chosen.length < count && left.length > 0) {
5553
+ let best = 0;
5554
+ let bestValue = -Infinity;
5555
+ left.forEach((entry, index) => {
5556
+ const repeat = Math.max(0, ...chosen.map((picked) => resemblance(words.get(entry), words.get(picked))));
5557
+ const value = (1 - weight) * Math.max(0, entry.score) / top - weight * repeat;
5558
+ if (value > bestValue) {
5559
+ best = index;
5560
+ bestValue = value;
5561
+ }
5562
+ });
5563
+ chosen.push(left.splice(best, 1)[0]);
5564
+ }
5565
+ return chosen;
5566
+ }
5472
5567
  /**
5473
- * The last stage: choose the `policy.recall` best of the pool. With a reranker's judgement each
5568
+ * The last stage: choose the `policy.recall` best of the pool, spread by `policy.diversity`. With a reranker's judgement each
5474
5569
  * candidate's relevance is mostly that (a candidate it left out counts as 0), and the rest is what
5475
5570
  * retrieval found; without, it is what retrieval found.
5476
5571
  */
5477
5572
  function finishRecall(pool, rerank) {
5478
5573
  const { policy } = pool.query;
5479
- return pool.entries.flatMap(({ candidate, fused }) => {
5480
- const standing = judged(candidate, rerank === void 0 ? fused : RERANK_BLEND * Math.min(1, Math.max(0, rerank.get(candidate.key) ?? 0)) + .30000000000000004 * fused, policy);
5481
- return standing === void 0 ? [] : [{
5574
+ const standing = pool.entries.flatMap(({ candidate, fused, found }) => {
5575
+ const relevance = rerank === void 0 ? fused : RERANK_BLEND * Math.min(1, Math.max(0, rerank.get(candidate.key) ?? 0)) + .30000000000000004 * fused;
5576
+ const judgement = judged(candidate, relevance, policy);
5577
+ if (judgement === void 0) return [];
5578
+ const why = pool.query.explain === true ? {
5579
+ cues: candidate.cues,
5580
+ ...found,
5581
+ ...rerank === void 0 ? {} : { rerank: rerank.get(candidate.key) ?? 0 },
5582
+ relevance,
5583
+ importance: candidate.importance,
5584
+ ageDays: candidate.ageDays,
5585
+ ...judgement.retention === void 0 ? {} : { retention: judgement.retention },
5586
+ score: judgement.score
5587
+ } : void 0;
5588
+ return [{
5482
5589
  candidate,
5483
- ...standing
5590
+ relevance,
5591
+ ...judgement,
5592
+ why
5484
5593
  }];
5485
- }).sort((a, b) => b.score - a.score || b.candidate.recalled.turn - a.candidate.recalled.turn).slice(0, policy.recall).sort((a, b) => a.candidate.recalled.turn - b.candidate.recalled.turn).map(({ candidate, clarity, text }) => ({
5594
+ }).sort((a, b) => b.score - a.score || b.candidate.recalled.turn - a.candidate.recalled.turn);
5595
+ const chosen = diversified(standing, policy.recall, policy.diversity ?? 0);
5596
+ const result = chosen.map(({ candidate, clarity, text, why }) => ({
5486
5597
  clarity,
5487
5598
  text,
5488
5599
  turn: candidate.recalled.turn,
5489
5600
  ...candidate.recalled.at === void 0 ? {} : { at: candidate.recalled.at },
5490
- source: candidate.recalled.source
5601
+ source: candidate.recalled.source,
5602
+ ...why === void 0 ? {} : { why }
5491
5603
  }));
5604
+ const anchors = chosen.filter((entry) => entry.relevance >= CUE_RELEVANCE);
5605
+ const wanted = anchors.length === 0 ? 0 : Math.max(0, policy.associate ?? 0);
5606
+ if (wanted > 0) {
5607
+ const taken = new Set(chosen.map((entry) => entry.candidate.key));
5608
+ const tied = [...standing.filter((entry) => !chosen.includes(entry)).map((entry) => entry.candidate), ...pool.others].filter((candidate) => !taken.has(candidate.key) && candidate.importance >= ASSOCIABLE_IMPORTANCE).flatMap((candidate) => {
5609
+ const best = anchors.map((anchor) => tie(anchor.candidate.links, candidate.links)).sort((a, b) => b.strength - a.strength)[0];
5610
+ const judgement = best === void 0 || best.strength < STRONG_TIE ? void 0 : judged(candidate, 0, policy);
5611
+ return best === void 0 || judgement === void 0 || judgement.clarity === "vague" ? [] : [{
5612
+ candidate,
5613
+ best,
5614
+ judgement
5615
+ }];
5616
+ }).sort((a, b) => b.best.strength - a.best.strength || b.candidate.importance - a.candidate.importance || b.candidate.recalled.turn - a.candidate.recalled.turn).slice(0, wanted);
5617
+ for (const { candidate, best, judgement } of tied) result.push({
5618
+ clarity: judgement.clarity,
5619
+ text: judgement.text,
5620
+ turn: candidate.recalled.turn,
5621
+ ...candidate.recalled.at === void 0 ? {} : { at: candidate.recalled.at },
5622
+ source: candidate.recalled.source,
5623
+ linked: true,
5624
+ ...pool.query.explain === true ? { why: {
5625
+ cues: candidate.cues,
5626
+ relevance: 0,
5627
+ importance: candidate.importance,
5628
+ ageDays: candidate.ageDays,
5629
+ ...judgement.retention === void 0 ? {} : { retention: judgement.retention },
5630
+ score: judgement.score,
5631
+ linked: {
5632
+ people: best.people,
5633
+ tags: best.tags,
5634
+ place: best.place
5635
+ }
5636
+ } } : {}
5637
+ });
5638
+ }
5639
+ return result.sort((a, b) => a.turn - b.turn);
5492
5640
  }
5493
5641
  /** Remind a character of what the moment calls to mind, in one call: {@link recallPool}, then {@link finishRecall} with the `rerank` evidence if there is any. */
5494
5642
  function recall(source, query, evidence = {}) {
@@ -9169,6 +9317,7 @@ function openSessionStore(location, options = {}) {
9169
9317
  try {
9170
9318
  const state = inspect(db);
9171
9319
  if (state.version === 0 || state.applicationId !== 1146245971) throw new SchemaError(`${location} is not a session database`);
9320
+ if (state.version < LATEST_VERSION && location !== ":memory:") backUpBeforeMigrating(db, location, state.version);
9172
9321
  migrate(db);
9173
9322
  return new SqliteSessionStore(db, options.clock ?? systemClock, vectors);
9174
9323
  } catch (error) {
@@ -9176,6 +9325,24 @@ function openSessionStore(location, options = {}) {
9176
9325
  throw error;
9177
9326
  }
9178
9327
  }
9328
+ /** Folder, beside the database, holding the copies made before a schema upgrade. */
9329
+ const MIGRATION_BACKUP_FOLDER = "migration-backups";
9330
+ /** Where the copy of a database as it was at schema `version` is kept. */
9331
+ function migrationBackupPath(location, version) {
9332
+ return join(dirname(location), MIGRATION_BACKUP_FOLDER, `schema-${version}.sqlite`);
9333
+ }
9334
+ /**
9335
+ * A released session cannot be rebuilt, so an upgrade starts from a copy that passed the integrity check; one copy per old version is kept.
9336
+ * @throws {CorruptDataError} when the database is damaged, before anything is changed.
9337
+ */
9338
+ function backUpBeforeMigrating(db, location, version) {
9339
+ const check = db.get("PRAGMA quick_check")?.quick_check;
9340
+ if (check !== "ok") throw new CorruptDataError(`${location} failed its integrity check, so it was not upgraded: ${check ?? "no result"}`);
9341
+ const target = migrationBackupPath(location, version);
9342
+ if (existsSync(target)) return;
9343
+ mkdirSync(dirname(target), { recursive: true });
9344
+ db.run("VACUUM INTO ?", [target]);
9345
+ }
9179
9346
  //#endregion
9180
9347
  //#region src/sessions.ts
9181
9348
  const SESSION_DATABASE_FILE = "session.sqlite";
@@ -11192,11 +11359,17 @@ function createCharacterTools(link, characterId) {
11192
11359
  defineTool({
11193
11360
  name: "recall_my_memories",
11194
11361
  description: "Ask yourself what you remember about something: a person, an object, a place or an earlier moment. You get your own version of it, what you took in yourself and what you believe, and what has faded comes back as hazy or not at all. Use it only when you need a memory that the situation does not already give you.",
11195
- parameters: { about: {
11196
- type: "string",
11197
- required: true,
11198
- description: "What to remember: a name, an object, a topic or a short description of the moment."
11199
- } },
11362
+ parameters: {
11363
+ about: {
11364
+ type: "string",
11365
+ required: true,
11366
+ description: "What to remember: a name, an object, a topic or a short description of the moment."
11367
+ },
11368
+ person: {
11369
+ type: "string",
11370
+ description: "Only what concerns this person (a name or alias you know): everything you remember of them."
11371
+ }
11372
+ },
11200
11373
  output: {
11201
11374
  schema: {
11202
11375
  type: "array",
@@ -11221,20 +11394,24 @@ function createCharacterTools(link, characterId) {
11221
11394
  },
11222
11395
  render: (_args, value) => [{
11223
11396
  type: "text",
11224
- text: value.length === 0 ? "You do not remember anything about that." : value.map((entry) => `- (${entry.clarity === "clear" ? "" : entry.clarity === "faded" ? "the details are fuzzy" : "you barely remember"}) ${entry.text}`.replace("- () ", "- ")).join("\n")
11397
+ text: value.length === 0 ? "You do not remember anything about that, and there is nothing more to find: go on with what you know." : value.map((entry) => `- (${entry.clarity === "clear" ? "" : entry.clarity === "faded" ? "the details are fuzzy" : "you barely remember"}) ${entry.text}`.replace("- () ", "- ")).join("\n")
11225
11398
  }]
11226
11399
  },
11227
11400
  isConcurrencySafe: () => false,
11228
11401
  async execute(args, exec) {
11229
11402
  const agent = link();
11230
11403
  const { session, turn, policy } = agent;
11404
+ const who = args.person === void 0 || args.person.trim() === "" ? [] : [...mentioned(args.person, session.characters.list())].filter((id) => id !== characterId);
11405
+ if (args.person !== void 0 && args.person.trim() !== "" && who.length === 0) return [];
11406
+ const situation = who.length === 0 ? args.about : `${args.about} ${args.person}`;
11231
11407
  return (await session.recallRanked({
11232
11408
  characterId,
11233
- situation: args.about,
11409
+ situation,
11234
11410
  turn,
11235
11411
  policy,
11412
+ ...who.length === 0 ? {} : { about: who },
11236
11413
  ...placeOf(session, characterId)
11237
- }, exec.signal, await vectorOf(agent, args.about, exec.signal))).map((entry) => ({
11414
+ }, exec.signal, await vectorOf(agent, situation, exec.signal))).map((entry) => ({
11238
11415
  clarity: entry.clarity,
11239
11416
  turn: entry.turn,
11240
11417
  text: entry.text
@@ -11272,7 +11449,7 @@ function createCharacterTools(link, characterId) {
11272
11449
  },
11273
11450
  render: (_args, value) => [{
11274
11451
  type: "text",
11275
- text: value.length === 0 ? "You know of nothing matching that." : value.map((entry) => `- ${entry.kind}: ${entry.name} (${entry.key})`).join("\n")
11452
+ text: value.length === 0 ? "You know of nothing matching that." : value.map((entry) => `- ${entry.kind}: ${entry.name}`).join("\n")
11276
11453
  }]
11277
11454
  },
11278
11455
  isConcurrencySafe: () => true,
@@ -11383,6 +11560,6 @@ function createCharacterTools(link, characterId) {
11383
11560
  ];
11384
11561
  }
11385
11562
  //#endregion
11386
- export { ACCURACIES, CHARACTER_CONTROLS, CHARACTER_KINDS, CHARACTER_ROLES, CHARACTER_TIERS, CLOCK_MODES, COMPARISONS, CONSOLIDATION_KINDS, CONTAINER_STATES, Config, ConflictError, CorruptDataError, DAY, DAY_PARTS, DEFAULT_MEMORY_POLICY, DEFAULT_SLOTS, EMOTION_VARIABLES, ENGAGEMENT_GRACE, ENGAGEMENT_STATUSES, EXIT_STATES, HOUR, IMPORTANCE_MAX, IMPORTANCE_MIN, INTENT_HORIZONS, INTENT_STATUSES, INTENT_TYPES, ITEM_ACTIONS, InvalidEditError, InvalidMoveError, InvalidValueError, KNOWLEDGE_KINDS, LORE_KINDS, LORE_SUBJECT_KINDS, MAX_LOCATION_FEATURES, MAX_ROUTINE_WINDOWS, MAX_SHOWN, MAX_TRIGGERS, MAX_VARIABLES, MEMBERSHIP_STATUSES, MEMORY_KINDS, MINUTE, NotFoundError, PERCEPTION_MODES, PLACEABLE_KINDS, PLOT_NUDGE_MODES, PLOT_STATUSES, RECALL_ITEM_KINDS, RoleplayState, LATEST_VERSION as SCHEMA_VERSION, SECOND, SESSION_DATABASE_FILE, SLOT_ID, SUMMARY_LEVELS, SchemaError, StateError, THREAD_STATUSES, TIME_CHANGE_KINDS, TRIGGER_NOTE_KINDS, TRIGGER_STATUSES, VARIABLE_KEY, VARIABLE_KINDS, VARIABLE_SUBJECTS, VARIABLE_VISIBILITIES, VISUAL_SUBJECT_KINDS, WEEKDAY_NAMES, addLocalDays, agendaAt, announcements, apply, applyItemPlan, applyRecordsEdit, audienceIds, catchUpPlan, changeItem, changeVariable, characterKey, clarityAt, clockTime, conditionHolds, createCharacterTools, createSession, createSessionStore, createTools, dayPart, deleteSession, denseScore, describeDuration, describeHolder, describeInstant, describeValue, finishRecall, fuseScores, inAudience, inWindow, isSessionId, isTimeZone, itemKey, jitter, judgedStatements, lapseNotes, lintWorld, listSessions, localIso, localTime, locationKey, mentioned, moveCharacter, name, nextOccurrence, nextTimeOfDay, nodeSqliteDriver, occurrenceAt, openSession, openSessionStore, overlap, parseInstant, parseRecordsEdit, parseTimeOfDay, parseTriggerDefinition, planItemChange, planKnownMove, planMove, planVariableChange, poolCandidates, rankGoals, readRecords, readTriggerDefinition, recall, recallCandidates, recallPool, recordsEditSchema, retention, runTriggers, seededRoll, sessionDatabase, sessionFolder, shownVariables, subjectOf, systemClock, systemTimeZone, tokens, triggerReferenceProblems, valueIssue, zonedInstant };
11563
+ export { ACCURACIES, CHARACTER_CONTROLS, CHARACTER_KINDS, CHARACTER_ROLES, CHARACTER_TIERS, CLOCK_MODES, COMPARISONS, CONSOLIDATION_KINDS, CONTAINER_STATES, Config, ConflictError, CorruptDataError, DAY, DAY_PARTS, DEFAULT_MEMORY_POLICY, DEFAULT_SLOTS, EMOTION_VARIABLES, ENGAGEMENT_GRACE, ENGAGEMENT_STATUSES, EXIT_STATES, HOUR, IMPORTANCE_MAX, IMPORTANCE_MIN, INTENT_HORIZONS, INTENT_STATUSES, INTENT_TYPES, ITEM_ACTIONS, InvalidEditError, InvalidMoveError, InvalidValueError, KNOWLEDGE_KINDS, LORE_KINDS, LORE_SUBJECT_KINDS, MAX_LOCATION_FEATURES, MAX_ROUTINE_WINDOWS, MAX_SHOWN, MAX_TRIGGERS, MAX_VARIABLES, MEMBERSHIP_STATUSES, MEMORY_KINDS, MIGRATION_BACKUP_FOLDER, MINUTE, NotFoundError, PERCEPTION_MODES, PLACEABLE_KINDS, PLOT_NUDGE_MODES, PLOT_STATUSES, RECALL_ITEM_KINDS, RoleplayState, LATEST_VERSION as SCHEMA_VERSION, SECOND, SESSION_DATABASE_FILE, SLOT_ID, SUMMARY_LEVELS, SchemaError, StateError, THREAD_STATUSES, TIME_CHANGE_KINDS, TRIGGER_NOTE_KINDS, TRIGGER_STATUSES, VARIABLE_KEY, VARIABLE_KINDS, VARIABLE_SUBJECTS, VARIABLE_VISIBILITIES, VISUAL_SUBJECT_KINDS, WEEKDAY_NAMES, addLocalDays, agendaAt, announcements, apply, applyItemPlan, applyRecordsEdit, audienceIds, catchUpPlan, changeItem, changeVariable, characterKey, clarityAt, clockTime, conditionHolds, createCharacterTools, createSession, createSessionStore, createTools, dayPart, deleteSession, denseScore, describeDuration, describeHolder, describeInstant, describeValue, finishRecall, fuseScores, inAudience, inWindow, isSessionId, isTimeZone, itemKey, jitter, judgedStatements, lapseNotes, lintWorld, listSessions, localIso, localTime, locationKey, mentioned, migrationBackupPath, moveCharacter, name, nextOccurrence, nextTimeOfDay, nodeSqliteDriver, occurrenceAt, openSession, openSessionStore, overlap, parseInstant, parseRecordsEdit, parseTimeOfDay, parseTriggerDefinition, planItemChange, planKnownMove, planMove, planVariableChange, poolCandidates, rankGoals, readRecords, readTriggerDefinition, recall, recallCandidates, recallPool, recordsEditSchema, retention, runTriggers, seededRoll, sessionDatabase, sessionFolder, shownVariables, subjectOf, systemClock, systemTimeZone, tokens, triggerReferenceProblems, valueIssue, zonedInstant };
11387
11564
 
11388
11565
  //# sourceMappingURL=index.js.map