@dsh-agentic-roleplay/state 0.1.0 → 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 +10 -4
- package/lib/index.d.ts +87 -9
- package/lib/index.js +263 -55
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
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)`]
|
|
@@ -2033,6 +2039,7 @@ const MAX_DEPTH = 4;
|
|
|
2033
2039
|
const MAX_NODES = 24;
|
|
2034
2040
|
const MAX_EFFECTS = 6;
|
|
2035
2041
|
const MAX_TEXT$1 = 1e3;
|
|
2042
|
+
const MAX_JUDGED = 300;
|
|
2036
2043
|
const text = (entry, key) => typeof entry[key] === "string" && entry[key].trim() !== "" ? entry[key].trim() : void 0;
|
|
2037
2044
|
function parseCondition(value, path, problems, count, depth = 0) {
|
|
2038
2045
|
count.nodes += 1;
|
|
@@ -2132,6 +2139,17 @@ function parseCondition(value, path, problems, count, depth = 0) {
|
|
|
2132
2139
|
trigger
|
|
2133
2140
|
};
|
|
2134
2141
|
}
|
|
2142
|
+
case "judged": {
|
|
2143
|
+
const statement = need("text");
|
|
2144
|
+
if (statement !== void 0 && statement.length > MAX_JUDGED) {
|
|
2145
|
+
problems.push(`${path}.text: at most ${MAX_JUDGED} characters`);
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
return statement === void 0 ? void 0 : {
|
|
2149
|
+
type: "judged",
|
|
2150
|
+
text: statement
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2135
2153
|
case "all":
|
|
2136
2154
|
case "any": {
|
|
2137
2155
|
if (!Array.isArray(value["conditions"]) || value["conditions"].length === 0) {
|
|
@@ -2152,7 +2170,7 @@ function parseCondition(value, path, problems, count, depth = 0) {
|
|
|
2152
2170
|
};
|
|
2153
2171
|
}
|
|
2154
2172
|
default:
|
|
2155
|
-
problems.push(`${path}.type: one of time, place, variable, item, event, fired, all, any, not`);
|
|
2173
|
+
problems.push(`${path}.type: one of time, place, variable, item, event, fired, judged, all, any, not`);
|
|
2156
2174
|
return;
|
|
2157
2175
|
}
|
|
2158
2176
|
}
|
|
@@ -2302,6 +2320,7 @@ function conditionProblems(target, condition, path, known) {
|
|
|
2302
2320
|
];
|
|
2303
2321
|
case "event": return [];
|
|
2304
2322
|
case "fired": return known.has(condition.trigger) ? [] : [`${path}: there is no trigger "${condition.trigger}"`];
|
|
2323
|
+
case "judged": return [];
|
|
2305
2324
|
case "all":
|
|
2306
2325
|
case "any": return condition.conditions.flatMap((part, index) => conditionProblems(target, part, `${path}.conditions[${index}]`, known));
|
|
2307
2326
|
case "not": return conditionProblems(target, condition.condition, `${path}.condition`, known);
|
|
@@ -2338,6 +2357,16 @@ function compare(op, have, want) {
|
|
|
2338
2357
|
if (typeof have === "number" && typeof want === "number") return COMPARE[op](have, want);
|
|
2339
2358
|
return op === "ne" ? have !== want : op === "eq" ? have === want : false;
|
|
2340
2359
|
}
|
|
2360
|
+
/** The statements of the `judged` parts of a condition, in order, each once. */
|
|
2361
|
+
function judgedStatements(condition) {
|
|
2362
|
+
switch (condition.type) {
|
|
2363
|
+
case "judged": return [condition.text];
|
|
2364
|
+
case "all":
|
|
2365
|
+
case "any": return [...new Set(condition.conditions.flatMap(judgedStatements))];
|
|
2366
|
+
case "not": return judgedStatements(condition.condition);
|
|
2367
|
+
default: return [];
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2341
2370
|
function holds(target, condition, clock) {
|
|
2342
2371
|
switch (condition.type) {
|
|
2343
2372
|
case "time": {
|
|
@@ -2375,6 +2404,7 @@ function holds(target, condition, clock) {
|
|
|
2375
2404
|
}
|
|
2376
2405
|
case "event": return target.events.list().some((event) => event.tags.includes(condition.tag));
|
|
2377
2406
|
case "fired": return (target.triggers.getByKey(condition.trigger)?.fireCount ?? 0) > 0;
|
|
2407
|
+
case "judged": return clock.judged?.(condition.text) ?? false;
|
|
2378
2408
|
case "all": return condition.conditions.every((part) => holds(target, part, clock));
|
|
2379
2409
|
case "any": return condition.conditions.some((part) => holds(target, part, clock));
|
|
2380
2410
|
case "not": return !holds(target, condition.condition, clock);
|
|
@@ -5047,11 +5077,12 @@ const isObject = (value) => typeof value === "object" && value !== null && !Arra
|
|
|
5047
5077
|
const DEFAULT_MAX_FIRES = 3;
|
|
5048
5078
|
/** An edit effect, as it is applied at this turn and round: events default to now and to what those in their place saw. */
|
|
5049
5079
|
function editOf(target, raw, options) {
|
|
5080
|
+
const round = Math.max(1, options.round);
|
|
5050
5081
|
const events = Array.isArray(raw["events"]) ? raw["events"].filter(isObject).map((event) => {
|
|
5051
5082
|
const where = typeof event["location"] === "string" ? target.locations.list().find((entry) => locationKey(entry) === event["location"])?.id : void 0;
|
|
5052
5083
|
if (event["perceived_by"] === "everyone") return {
|
|
5053
5084
|
turn: options.turn,
|
|
5054
|
-
round
|
|
5085
|
+
round,
|
|
5055
5086
|
importance: 3,
|
|
5056
5087
|
tags: [],
|
|
5057
5088
|
involves: [],
|
|
@@ -5067,7 +5098,7 @@ function editOf(target, raw, options) {
|
|
|
5067
5098
|
}));
|
|
5068
5099
|
return {
|
|
5069
5100
|
turn: options.turn,
|
|
5070
|
-
round
|
|
5101
|
+
round,
|
|
5071
5102
|
importance: 3,
|
|
5072
5103
|
tags: [],
|
|
5073
5104
|
involves: [],
|
|
@@ -5185,7 +5216,9 @@ const DAY$1 = 864e5;
|
|
|
5185
5216
|
const DEFAULT_MEMORY_POLICY = {
|
|
5186
5217
|
flaky: true,
|
|
5187
5218
|
recall: 12,
|
|
5188
|
-
overRetrieve: 4
|
|
5219
|
+
overRetrieve: 4,
|
|
5220
|
+
associate: 2,
|
|
5221
|
+
diversity: .3
|
|
5189
5222
|
};
|
|
5190
5223
|
/** Story days for the memory of an unimportant event to halve; each step of importance doubles it. */
|
|
5191
5224
|
const HALF_LIFE_DAYS = 6;
|
|
@@ -5316,7 +5349,7 @@ const DENSE_RANGE = .5;
|
|
|
5316
5349
|
const denseScore = (similarity) => Math.min(1, Math.max(0, (similarity - DENSE_FLOOR) / DENSE_RANGE));
|
|
5317
5350
|
/** Being where something happened calls it to mind, about as much as sharing two words with it. */
|
|
5318
5351
|
const PLACE_CUE = 2;
|
|
5319
|
-
function gather(source, { characterId, characters, situation, locationId }) {
|
|
5352
|
+
function gather(source, { characterId, characters, situation, locationId, about }) {
|
|
5320
5353
|
const names = new Map(characters.map((character) => [character.id, character.name]));
|
|
5321
5354
|
const peers = mentioned(situation, characters);
|
|
5322
5355
|
peers.delete(characterId);
|
|
@@ -5334,24 +5367,46 @@ function gather(source, { characterId, characters, situation, locationId }) {
|
|
|
5334
5367
|
}
|
|
5335
5368
|
const atTurn = (eventTurn) => turns.get(eventTurn) ?? start;
|
|
5336
5369
|
const ageDays = (at) => now === void 0 || at === void 0 ? 0 : Math.max(0, now - at) / DAY$1;
|
|
5337
|
-
|
|
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) => {
|
|
5338
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
|
+
};
|
|
5339
5378
|
return {
|
|
5340
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
|
+
},
|
|
5341
5385
|
recalled: describeEvent(seen, names, at),
|
|
5342
|
-
|
|
5386
|
+
cues: cue,
|
|
5387
|
+
relevance: cue.people + cue.words + cue.place,
|
|
5343
5388
|
importance: seen.event.importance,
|
|
5344
5389
|
ageDays: ageDays(at),
|
|
5345
5390
|
recalls: seen.perception.recalls,
|
|
5346
5391
|
seed: seen.perception.id,
|
|
5347
5392
|
durability: 1
|
|
5348
5393
|
};
|
|
5349
|
-
}), ...source.memories.list(characterId).map((belief) => {
|
|
5394
|
+
}), ...source.memories.list(characterId).filter((belief) => concerns(belief.about)).map((belief) => {
|
|
5350
5395
|
const at = atTurn(belief.turn);
|
|
5396
|
+
const cue = {
|
|
5397
|
+
people: peopleCue(belief.about),
|
|
5398
|
+
words: overlap(cues, belief.text, []),
|
|
5399
|
+
place: 0
|
|
5400
|
+
};
|
|
5351
5401
|
return {
|
|
5352
5402
|
key: `m:${belief.id}`,
|
|
5403
|
+
links: {
|
|
5404
|
+
people: belief.about.filter((id) => id !== characterId),
|
|
5405
|
+
tags: []
|
|
5406
|
+
},
|
|
5353
5407
|
recalled: describeBelief(belief, at),
|
|
5354
|
-
|
|
5408
|
+
cues: cue,
|
|
5409
|
+
relevance: cue.people + cue.words,
|
|
5355
5410
|
importance: belief.importance,
|
|
5356
5411
|
ageDays: ageDays(at),
|
|
5357
5412
|
recalls: belief.recalls,
|
|
@@ -5380,20 +5435,25 @@ function fuseScores(searches) {
|
|
|
5380
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. */
|
|
5381
5436
|
function judged(candidate, relevance, policy) {
|
|
5382
5437
|
let clarity = "clear";
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
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
|
+
}
|
|
5391
5450
|
if (clarity === void 0) return void 0;
|
|
5392
5451
|
const score = RELEVANCE_SCALE * relevance + .6 * candidate.importance + 2 / (1 + Math.max(0, candidate.ageDays)) + (clarity === "vague" ? -1 : 0);
|
|
5393
5452
|
return {
|
|
5394
5453
|
clarity,
|
|
5395
5454
|
score,
|
|
5396
|
-
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 }
|
|
5397
5457
|
};
|
|
5398
5458
|
}
|
|
5399
5459
|
/**
|
|
@@ -5404,7 +5464,7 @@ function judged(candidate, relevance, policy) {
|
|
|
5404
5464
|
* is, and not what has faded from memory altogether). A reranker may then judge that pool.
|
|
5405
5465
|
*/
|
|
5406
5466
|
function recallPool(source, query, evidence = {}) {
|
|
5407
|
-
const { policy } = query;
|
|
5467
|
+
const { policy, explain } = query;
|
|
5408
5468
|
const candidates = gather(source, query);
|
|
5409
5469
|
const fused = fuseScores([
|
|
5410
5470
|
{
|
|
@@ -5421,18 +5481,28 @@ function recallPool(source, query, evidence = {}) {
|
|
|
5421
5481
|
}
|
|
5422
5482
|
]);
|
|
5423
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));
|
|
5424
5499
|
return {
|
|
5425
|
-
entries
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
entry,
|
|
5432
|
-
score: standing.score
|
|
5433
|
-
}];
|
|
5434
|
-
}).sort((a, b) => b.score - a.score || b.entry.candidate.recalled.turn - a.entry.candidate.recalled.turn).slice(0, width).map(({ entry }) => entry),
|
|
5435
|
-
query: { policy }
|
|
5500
|
+
entries,
|
|
5501
|
+
others: candidates.filter((candidate) => !kept.has(candidate.key)),
|
|
5502
|
+
query: {
|
|
5503
|
+
policy,
|
|
5504
|
+
...explain === true ? { explain } : {}
|
|
5505
|
+
}
|
|
5436
5506
|
};
|
|
5437
5507
|
}
|
|
5438
5508
|
/** What a reranker is asked to judge: every candidate of the pool, with the text it would be recalled by. */
|
|
@@ -5444,26 +5514,129 @@ function poolCandidates(pool) {
|
|
|
5444
5514
|
importance: candidate.importance
|
|
5445
5515
|
}));
|
|
5446
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
|
+
}
|
|
5447
5540
|
/**
|
|
5448
|
-
*
|
|
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
|
+
}
|
|
5567
|
+
/**
|
|
5568
|
+
* The last stage: choose the `policy.recall` best of the pool, spread by `policy.diversity`. With a reranker's judgement each
|
|
5449
5569
|
* candidate's relevance is mostly that (a candidate it left out counts as 0), and the rest is what
|
|
5450
5570
|
* retrieval found; without, it is what retrieval found.
|
|
5451
5571
|
*/
|
|
5452
5572
|
function finishRecall(pool, rerank) {
|
|
5453
5573
|
const { policy } = pool.query;
|
|
5454
|
-
|
|
5455
|
-
const
|
|
5456
|
-
|
|
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 [{
|
|
5457
5589
|
candidate,
|
|
5458
|
-
|
|
5590
|
+
relevance,
|
|
5591
|
+
...judgement,
|
|
5592
|
+
why
|
|
5459
5593
|
}];
|
|
5460
|
-
}).sort((a, b) => b.score - a.score || b.candidate.recalled.turn - a.candidate.recalled.turn)
|
|
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 }) => ({
|
|
5461
5597
|
clarity,
|
|
5462
5598
|
text,
|
|
5463
5599
|
turn: candidate.recalled.turn,
|
|
5464
5600
|
...candidate.recalled.at === void 0 ? {} : { at: candidate.recalled.at },
|
|
5465
|
-
source: candidate.recalled.source
|
|
5601
|
+
source: candidate.recalled.source,
|
|
5602
|
+
...why === void 0 ? {} : { why }
|
|
5466
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);
|
|
5467
5640
|
}
|
|
5468
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. */
|
|
5469
5642
|
function recall(source, query, evidence = {}) {
|
|
@@ -6716,7 +6889,7 @@ var SqliteItemRepository = class {
|
|
|
6716
6889
|
patch.notable ?? current.notable ? 1 : 0,
|
|
6717
6890
|
patch.hidden ?? current.hidden ? 1 : 0,
|
|
6718
6891
|
patch.container?.state ?? current.container?.state ?? null,
|
|
6719
|
-
patch.ownerId
|
|
6892
|
+
patch.ownerId === void 0 ? current.ownerId ?? null : patch.ownerId,
|
|
6720
6893
|
id
|
|
6721
6894
|
]);
|
|
6722
6895
|
return this.require(id);
|
|
@@ -7090,10 +7263,16 @@ var SqliteLocationRepository = class {
|
|
|
7090
7263
|
const features = patch.features === void 0 ? current.features : checkedFeatures(patch.features);
|
|
7091
7264
|
const removed = current.features.filter((feature) => !features.some((kept) => kept.key === feature.key));
|
|
7092
7265
|
for (const feature of removed) if (this.db.get("SELECT 1 FROM placements WHERE location_id = ? AND feature_key = ?", [id, feature.key]) !== void 0) throw new ConflictError(`feature "${feature.key}" cannot be removed while someone is at it`);
|
|
7093
|
-
|
|
7266
|
+
const parentId = patch.parentId === void 0 ? current.parentId ?? null : patch.parentId;
|
|
7267
|
+
if (parentId !== null && parentId !== (current.parentId ?? null)) {
|
|
7268
|
+
if (this.get(parentId) === void 0) throw new NotFoundError(`the parent place ${parentId} does not exist`);
|
|
7269
|
+
for (let ancestor = parentId; ancestor !== null; ancestor = this.get(ancestor)?.parentId ?? null) if (ancestor === id) throw new InvalidValueError(`${current.name} cannot be inside itself or a place within it`);
|
|
7270
|
+
}
|
|
7271
|
+
this.db.run("UPDATE locations SET name = ?, description = ?, features = ?, parent_id = ? WHERE id = ?", [
|
|
7094
7272
|
name,
|
|
7095
7273
|
(patch.description ?? current.description).trim(),
|
|
7096
7274
|
toJson(features),
|
|
7275
|
+
parentId,
|
|
7097
7276
|
id
|
|
7098
7277
|
]);
|
|
7099
7278
|
if (name !== current.name) this.renamed(id);
|
|
@@ -7621,10 +7800,10 @@ var SqliteMetadataRepository = class {
|
|
|
7621
7800
|
//#endregion
|
|
7622
7801
|
//#region src/sqlite/schema.ts
|
|
7623
7802
|
/**
|
|
7624
|
-
* Migrations are applied in order and never edited once released
|
|
7625
|
-
*
|
|
7626
|
-
*
|
|
7627
|
-
*
|
|
7803
|
+
* Migrations are applied in order and never edited once released (0.1.0 released
|
|
7804
|
+
* versions 1 to 6; tests/sqlite/migrate.spec.ts pins them): a change to the schema
|
|
7805
|
+
* is a new entry. Enumerations are spelled out so that a later change to the model
|
|
7806
|
+
* cannot alter what an old migration did.
|
|
7628
7807
|
*/
|
|
7629
7808
|
const MIGRATIONS = [
|
|
7630
7809
|
{
|
|
@@ -9138,6 +9317,7 @@ function openSessionStore(location, options = {}) {
|
|
|
9138
9317
|
try {
|
|
9139
9318
|
const state = inspect(db);
|
|
9140
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);
|
|
9141
9321
|
migrate(db);
|
|
9142
9322
|
return new SqliteSessionStore(db, options.clock ?? systemClock, vectors);
|
|
9143
9323
|
} catch (error) {
|
|
@@ -9145,6 +9325,24 @@ function openSessionStore(location, options = {}) {
|
|
|
9145
9325
|
throw error;
|
|
9146
9326
|
}
|
|
9147
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
|
+
}
|
|
9148
9346
|
//#endregion
|
|
9149
9347
|
//#region src/sessions.ts
|
|
9150
9348
|
const SESSION_DATABASE_FILE = "session.sqlite";
|
|
@@ -11161,11 +11359,17 @@ function createCharacterTools(link, characterId) {
|
|
|
11161
11359
|
defineTool({
|
|
11162
11360
|
name: "recall_my_memories",
|
|
11163
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.",
|
|
11164
|
-
parameters: {
|
|
11165
|
-
|
|
11166
|
-
|
|
11167
|
-
|
|
11168
|
-
|
|
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
|
+
},
|
|
11169
11373
|
output: {
|
|
11170
11374
|
schema: {
|
|
11171
11375
|
type: "array",
|
|
@@ -11190,20 +11394,24 @@ function createCharacterTools(link, characterId) {
|
|
|
11190
11394
|
},
|
|
11191
11395
|
render: (_args, value) => [{
|
|
11192
11396
|
type: "text",
|
|
11193
|
-
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")
|
|
11194
11398
|
}]
|
|
11195
11399
|
},
|
|
11196
11400
|
isConcurrencySafe: () => false,
|
|
11197
11401
|
async execute(args, exec) {
|
|
11198
11402
|
const agent = link();
|
|
11199
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}`;
|
|
11200
11407
|
return (await session.recallRanked({
|
|
11201
11408
|
characterId,
|
|
11202
|
-
situation
|
|
11409
|
+
situation,
|
|
11203
11410
|
turn,
|
|
11204
11411
|
policy,
|
|
11412
|
+
...who.length === 0 ? {} : { about: who },
|
|
11205
11413
|
...placeOf(session, characterId)
|
|
11206
|
-
}, exec.signal, await vectorOf(agent,
|
|
11414
|
+
}, exec.signal, await vectorOf(agent, situation, exec.signal))).map((entry) => ({
|
|
11207
11415
|
clarity: entry.clarity,
|
|
11208
11416
|
turn: entry.turn,
|
|
11209
11417
|
text: entry.text
|
|
@@ -11241,7 +11449,7 @@ function createCharacterTools(link, characterId) {
|
|
|
11241
11449
|
},
|
|
11242
11450
|
render: (_args, value) => [{
|
|
11243
11451
|
type: "text",
|
|
11244
|
-
text: value.length === 0 ? "You know of nothing matching that." : value.map((entry) => `- ${entry.kind}: ${entry.name}
|
|
11452
|
+
text: value.length === 0 ? "You know of nothing matching that." : value.map((entry) => `- ${entry.kind}: ${entry.name}`).join("\n")
|
|
11245
11453
|
}]
|
|
11246
11454
|
},
|
|
11247
11455
|
isConcurrencySafe: () => true,
|
|
@@ -11352,6 +11560,6 @@ function createCharacterTools(link, characterId) {
|
|
|
11352
11560
|
];
|
|
11353
11561
|
}
|
|
11354
11562
|
//#endregion
|
|
11355
|
-
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, 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 };
|
|
11356
11564
|
|
|
11357
11565
|
//# sourceMappingURL=index.js.map
|