@ifsoul/core 0.4.0 → 0.6.0
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 +4 -1
- package/dist/packages/memory/src/port.d.ts +31 -0
- package/dist/packages/memory/src/sql.d.ts +3 -1
- package/dist/packages/memory/src/sql.js +105 -1
- package/dist/packages/memory/src/sqlite-search.d.ts +3 -0
- package/dist/packages/memory/src/sqlite-search.js +44 -0
- package/dist/packages/memory/src/sqlite.js +4 -1
- package/dist/packages/protocol/src/index.d.ts +1 -1
- package/dist/packages/protocol/src/index.js +1 -1
- package/dist/packages/runtime/src/behavior-types.d.ts +150 -0
- package/dist/packages/runtime/src/behavior-types.js +1 -0
- package/dist/packages/runtime/src/behavior.d.ts +40 -0
- package/dist/packages/runtime/src/behavior.js +621 -0
- package/dist/packages/runtime/src/group-scheduler.d.ts +165 -0
- package/dist/packages/runtime/src/group-scheduler.js +647 -0
- package/dist/packages/runtime/src/index.d.ts +4 -0
- package/dist/packages/runtime/src/index.js +3 -0
- package/dist/packages/runtime/src/participants.d.ts +107 -0
- package/dist/packages/runtime/src/participants.js +309 -0
- package/dist/packages/runtime/src/types.d.ts +1 -0
- package/package.json +1 -1
- package/release.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ compose these public artifacts. See [repository ownership](https://github.com/op
|
|
|
15
15
|
Use Node.js 22.18–24 and an ESM project:
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
npm install @ifsoul/core@0.
|
|
18
|
+
npm install @ifsoul/core@0.6.0
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
The package includes compiled JavaScript and TypeScript declarations. Import the
|
|
@@ -62,3 +62,6 @@ pnpm build
|
|
|
62
62
|
See [development](https://github.com/openifsoul/core/blob/main/docs/development.md),
|
|
63
63
|
[architecture](https://github.com/openifsoul/core/blob/main/docs/architecture.md)
|
|
64
64
|
and [contribution](https://github.com/openifsoul/core/blob/main/CONTRIBUTING.md).
|
|
65
|
+
|
|
66
|
+
See [behavior configuration and dynamic parameters](docs/behavior.md) for shared goals,
|
|
67
|
+
reaction policy and evidence-linked state.
|
|
@@ -28,6 +28,35 @@ export interface RecallQuery {
|
|
|
28
28
|
maxBytes: number;
|
|
29
29
|
publicOnly?: boolean;
|
|
30
30
|
}
|
|
31
|
+
export interface MemoryScanQuery {
|
|
32
|
+
characterId: string;
|
|
33
|
+
/** Trusted owner-visible identities. Every participant on a non-public row must be listed. */
|
|
34
|
+
visibleParticipantIds: string[];
|
|
35
|
+
/** Exact full-text phrase over retained canonical event documents. */
|
|
36
|
+
text?: string;
|
|
37
|
+
/** Skip the candidate count when iterating an anchored projection. */
|
|
38
|
+
includeTotal?: boolean;
|
|
39
|
+
after?: number;
|
|
40
|
+
through?: number;
|
|
41
|
+
anchor?: string;
|
|
42
|
+
/** A reset excludes earlier entries in this environment, including after cursor creation. */
|
|
43
|
+
reset?: {
|
|
44
|
+
environmentId: string;
|
|
45
|
+
eventType: string;
|
|
46
|
+
};
|
|
47
|
+
limit: number;
|
|
48
|
+
}
|
|
49
|
+
export interface MemoryScanPage {
|
|
50
|
+
entries: {
|
|
51
|
+
sequence: number;
|
|
52
|
+
event: MemoryEvent;
|
|
53
|
+
}[];
|
|
54
|
+
through: number;
|
|
55
|
+
anchor: string;
|
|
56
|
+
next?: number;
|
|
57
|
+
/** Authorized event count within the current search anchor. */
|
|
58
|
+
total?: number;
|
|
59
|
+
}
|
|
31
60
|
/** Trusted local storage port; do not expose unrestricted methods to controllers or hosts. */
|
|
32
61
|
export interface MemoryPort {
|
|
33
62
|
putCharacter(character: CharacterInstance): Result<CharacterInstance>;
|
|
@@ -42,6 +71,8 @@ export interface MemoryPort {
|
|
|
42
71
|
events: MemoryEvent[];
|
|
43
72
|
}): Result<void>;
|
|
44
73
|
recall(query: RecallQuery): Result<MemoryEvent[]>;
|
|
74
|
+
/** Trusted owner inspection only. Bounds retained events; exchange journals are excluded. */
|
|
75
|
+
scan?(query: MemoryScanQuery): Result<MemoryScanPage>;
|
|
45
76
|
/** Trusted owner-only atomic retention. Exact source IDs must still exist in the
|
|
46
77
|
* nominated character/environment/scope. Replacements validate before pruning.
|
|
47
78
|
* Optional for older adapters; callers must reject unsupported retention. */
|
|
@@ -10,4 +10,6 @@ export interface SQLDriver {
|
|
|
10
10
|
}
|
|
11
11
|
export declare const MEMORY_SCHEMA = "CREATE TABLE IF NOT EXISTS characters (id TEXT PRIMARY KEY, document TEXT NOT NULL);\nCREATE TABLE IF NOT EXISTS exchanges (id TEXT PRIMARY KEY, character_id TEXT NOT NULL REFERENCES characters(id), document TEXT NOT NULL);\nCREATE TABLE IF NOT EXISTS events (sequence INTEGER PRIMARY KEY, id TEXT UNIQUE NOT NULL, character_id TEXT NOT NULL REFERENCES characters(id), environment_id TEXT NOT NULL, document TEXT NOT NULL);\nCREATE INDEX IF NOT EXISTS event_scope ON events(character_id, environment_id, sequence);";
|
|
12
12
|
export declare function validScope(scope: Scope): boolean;
|
|
13
|
-
export declare function createSQLMemory(db: SQLDriver, closeOwner?: () => void
|
|
13
|
+
export declare function createSQLMemory(db: SQLDriver, closeOwner?: () => void, options?: {
|
|
14
|
+
indexedSearch?: boolean;
|
|
15
|
+
}): MemoryPort;
|
|
@@ -19,7 +19,7 @@ export function validScope(scope) {
|
|
|
19
19
|
? scope.participantIds.length === 1
|
|
20
20
|
: scope.participantIds.length > 0));
|
|
21
21
|
}
|
|
22
|
-
export function createSQLMemory(db, closeOwner = () => { }) {
|
|
22
|
+
export function createSQLMemory(db, closeOwner = () => { }, options = {}) {
|
|
23
23
|
let closed = false;
|
|
24
24
|
const guarded = (action) => {
|
|
25
25
|
if (closed)
|
|
@@ -268,6 +268,110 @@ export function createSQLMemory(db, closeOwner = () => { }) {
|
|
|
268
268
|
}
|
|
269
269
|
return success(selected.reverse());
|
|
270
270
|
}),
|
|
271
|
+
scan: (query) => guarded(() => {
|
|
272
|
+
const after = query.after ?? 0;
|
|
273
|
+
if (!query.visibleParticipantIds.length ||
|
|
274
|
+
query.visibleParticipantIds.length > 32 ||
|
|
275
|
+
!Number.isSafeInteger(query.limit) ||
|
|
276
|
+
query.limit < 1 ||
|
|
277
|
+
query.limit > 128 ||
|
|
278
|
+
!Number.isSafeInteger(after) ||
|
|
279
|
+
after < 0 ||
|
|
280
|
+
(query.through !== undefined &&
|
|
281
|
+
(!Number.isSafeInteger(query.through) || query.through < after)))
|
|
282
|
+
return failure("budget-exceeded", "Memory scan bounds are invalid.");
|
|
283
|
+
const queryText = query.text?.trim() ?? "";
|
|
284
|
+
if (queryText.length > 120 || queryText.includes("\0"))
|
|
285
|
+
return failure("invalid-document", "Invalid memory search text.");
|
|
286
|
+
if (queryText && !options.indexedSearch)
|
|
287
|
+
return failure("unsupported", "This store does not support full-text memory search.");
|
|
288
|
+
const visibility = `(json_extract(document,'$.scope.audience')='public' OR (
|
|
289
|
+
json_extract(document,'$.scope.audience') IN ('private', 'participants')
|
|
290
|
+
AND json_array_length(document,'$.scope.participantIds') > 0
|
|
291
|
+
AND NOT EXISTS (
|
|
292
|
+
SELECT 1 FROM json_each(events.document,'$.scope.participantIds') AS recipient
|
|
293
|
+
WHERE recipient.value NOT IN (SELECT value FROM json_each(?))
|
|
294
|
+
)
|
|
295
|
+
))`;
|
|
296
|
+
const audience = JSON.stringify(query.visibleParticipantIds);
|
|
297
|
+
const anchor = query.through === undefined
|
|
298
|
+
? db
|
|
299
|
+
.prepare(`SELECT sequence, id FROM events WHERE character_id=? AND ${visibility} ORDER BY sequence DESC LIMIT 1`)
|
|
300
|
+
.get(query.characterId, audience)
|
|
301
|
+
: db
|
|
302
|
+
.prepare(`SELECT sequence, id FROM events WHERE character_id=? AND sequence=? AND ${visibility}`)
|
|
303
|
+
.get(query.characterId, query.through, audience);
|
|
304
|
+
const through = Number(anchor?.sequence ?? 0);
|
|
305
|
+
const anchorId = String(anchor?.id ?? "");
|
|
306
|
+
if (query.through !== undefined &&
|
|
307
|
+
(through !== query.through || anchorId !== query.anchor))
|
|
308
|
+
return failure("stale-context", "Retained memory changed. Start the search again.");
|
|
309
|
+
const reset = query.reset;
|
|
310
|
+
const resetAt = reset
|
|
311
|
+
? Number(db
|
|
312
|
+
.prepare(`SELECT sequence FROM events
|
|
313
|
+
WHERE character_id=? AND environment_id=?
|
|
314
|
+
AND json_extract(document,'$.eventType')=? AND ${visibility}
|
|
315
|
+
ORDER BY sequence DESC LIMIT 1`)
|
|
316
|
+
.get(query.characterId, reset.environmentId, reset.eventType, audience)?.sequence ?? 0)
|
|
317
|
+
: 0;
|
|
318
|
+
const textFilter = queryText
|
|
319
|
+
? "AND sequence IN (SELECT rowid FROM event_search_v1 WHERE event_search_v1 MATCH ?)"
|
|
320
|
+
: "";
|
|
321
|
+
const textArgs = queryText
|
|
322
|
+
? [`"${queryText.replaceAll('"', '""')}"`]
|
|
323
|
+
: [];
|
|
324
|
+
const visibleRange = `character_id=? AND sequence<=?
|
|
325
|
+
AND (?=0 OR environment_id<>? OR sequence>?)
|
|
326
|
+
AND ${visibility} ${textFilter}`;
|
|
327
|
+
const rangeArgs = [
|
|
328
|
+
query.characterId,
|
|
329
|
+
through,
|
|
330
|
+
resetAt,
|
|
331
|
+
reset?.environmentId ?? "",
|
|
332
|
+
resetAt,
|
|
333
|
+
audience,
|
|
334
|
+
...textArgs,
|
|
335
|
+
];
|
|
336
|
+
const total = query.includeTotal === false
|
|
337
|
+
? undefined
|
|
338
|
+
: Number(db
|
|
339
|
+
.prepare(`SELECT count(*) AS total FROM events WHERE ${visibleRange}`)
|
|
340
|
+
.get(...rangeArgs)?.total ?? 0);
|
|
341
|
+
const rows = db
|
|
342
|
+
.prepare(`WITH page AS (
|
|
343
|
+
SELECT sequence, length(CAST(document AS BLOB)) AS bytes
|
|
344
|
+
FROM events WHERE sequence>? AND ${visibleRange}
|
|
345
|
+
ORDER BY sequence LIMIT ?
|
|
346
|
+
), sized AS (
|
|
347
|
+
SELECT sequence, sum(bytes) OVER (ORDER BY sequence) AS total,
|
|
348
|
+
row_number() OVER (ORDER BY sequence) AS position FROM page
|
|
349
|
+
)
|
|
350
|
+
SELECT sized.sequence, CASE WHEN position=1 OR total<=512000 AND position<=?
|
|
351
|
+
THEN events.document END AS document
|
|
352
|
+
FROM sized JOIN events ON events.sequence=sized.sequence ORDER BY sized.sequence`)
|
|
353
|
+
.all(after, ...rangeArgs, query.limit + 1, query.limit);
|
|
354
|
+
const entries = [];
|
|
355
|
+
let next;
|
|
356
|
+
for (const row of rows) {
|
|
357
|
+
const sequence = Number(row.sequence);
|
|
358
|
+
if (typeof row.document !== "string") {
|
|
359
|
+
next = entries.at(-1)?.sequence;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
entries.push({
|
|
363
|
+
sequence,
|
|
364
|
+
event: stored(row.document, "memory-event"),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
return success({
|
|
368
|
+
entries,
|
|
369
|
+
through,
|
|
370
|
+
anchor: anchorId,
|
|
371
|
+
total,
|
|
372
|
+
...(next === undefined ? {} : { next }),
|
|
373
|
+
});
|
|
374
|
+
}),
|
|
271
375
|
close: () => {
|
|
272
376
|
try {
|
|
273
377
|
closeOwner();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** External-content index, with canonical event documents as the sole source. */
|
|
2
|
+
export function initializeEventSearch(database) {
|
|
3
|
+
database.exec("BEGIN IMMEDIATE");
|
|
4
|
+
try {
|
|
5
|
+
const existing = database
|
|
6
|
+
.prepare("SELECT 1 FROM sqlite_schema WHERE type='table' AND name='event_search_v1'")
|
|
7
|
+
.get();
|
|
8
|
+
database.exec(`
|
|
9
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS event_search_v1 USING fts5(
|
|
10
|
+
document, content='events', content_rowid='sequence', tokenize='unicode61'
|
|
11
|
+
);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS event_search_versions (version INTEGER PRIMARY KEY);
|
|
13
|
+
CREATE TRIGGER IF NOT EXISTS event_search_v1_insert AFTER INSERT ON events BEGIN
|
|
14
|
+
INSERT INTO event_search_v1(rowid, document) VALUES (new.sequence, new.document);
|
|
15
|
+
END;
|
|
16
|
+
CREATE TRIGGER IF NOT EXISTS event_search_v1_delete AFTER DELETE ON events BEGIN
|
|
17
|
+
INSERT INTO event_search_v1(event_search_v1, rowid, document)
|
|
18
|
+
VALUES ('delete', old.sequence, old.document);
|
|
19
|
+
END;
|
|
20
|
+
CREATE TRIGGER IF NOT EXISTS event_search_v1_update AFTER UPDATE ON events BEGIN
|
|
21
|
+
INSERT INTO event_search_v1(event_search_v1, rowid, document)
|
|
22
|
+
VALUES ('delete', old.sequence, old.document);
|
|
23
|
+
INSERT INTO event_search_v1(rowid, document) VALUES (new.sequence, new.document);
|
|
24
|
+
END;
|
|
25
|
+
`);
|
|
26
|
+
if (!existing ||
|
|
27
|
+
!database
|
|
28
|
+
.prepare("SELECT 1 FROM event_search_versions WHERE version=1")
|
|
29
|
+
.get()) {
|
|
30
|
+
database.exec(`
|
|
31
|
+
INSERT INTO event_search_v1(event_search_v1) VALUES ('rebuild');
|
|
32
|
+
INSERT OR REPLACE INTO event_search_versions(version) VALUES (1);
|
|
33
|
+
`);
|
|
34
|
+
}
|
|
35
|
+
database.exec("COMMIT");
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
database.exec("ROLLBACK");
|
|
40
|
+
if (error instanceof Error && error.message === "no such module: fts5")
|
|
41
|
+
return false;
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -3,6 +3,7 @@ import { closeSync, existsSync, mkdirSync, openSync, realpathSync, statSync, unl
|
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { failure, success } from "./port.js";
|
|
5
5
|
import { createSQLMemory, MEMORY_SCHEMA } from "./sql.js";
|
|
6
|
+
import { initializeEventSearch } from "./sqlite-search.js";
|
|
6
7
|
export { validScope } from "./sql.js";
|
|
7
8
|
/** Opens one locally owned SQLite writer. Never removes an existing ownership lock. */
|
|
8
9
|
export function openSQLiteMemory(filename) {
|
|
@@ -49,6 +50,8 @@ export function openSQLiteMemory(filename) {
|
|
|
49
50
|
chmodSync(path, 0o600);
|
|
50
51
|
database.exec("PRAGMA foreign_keys=ON; PRAGMA journal_mode=DELETE;");
|
|
51
52
|
database.exec(MEMORY_SCHEMA);
|
|
53
|
+
database.exec("CREATE INDEX IF NOT EXISTS event_owner_sequence ON events(character_id, sequence)");
|
|
54
|
+
const indexedSearch = initializeEventSearch(database);
|
|
52
55
|
const db = database;
|
|
53
56
|
let closed = false;
|
|
54
57
|
return success(createSQLMemory({
|
|
@@ -71,7 +74,7 @@ export function openSQLiteMemory(filename) {
|
|
|
71
74
|
closed = true;
|
|
72
75
|
}
|
|
73
76
|
release();
|
|
74
|
-
}));
|
|
77
|
+
}, { indexedSearch }));
|
|
75
78
|
}
|
|
76
79
|
catch {
|
|
77
80
|
let connectionClosed = true;
|
|
@@ -6,6 +6,6 @@ export declare function canonical(value: unknown): string;
|
|
|
6
6
|
export { resolveResourceLimits, BUFFERED_JSON_MAX_BYTES, MAX_TIMER_MS, } from "./resource-policy.js";
|
|
7
7
|
export type { BudgetReport } from "./resource-policy.js";
|
|
8
8
|
/** Current train identifier; wire families require exact kind/version validation. */
|
|
9
|
-
export declare const IFSOUL_VERSION: "0.
|
|
9
|
+
export declare const IFSOUL_VERSION: "0.6.0";
|
|
10
10
|
/** Structural current ModelProvider spec validation; not a provider wire transport. */
|
|
11
11
|
export declare function validateModelProviderDocument(value: unknown): boolean;
|
|
@@ -47,7 +47,7 @@ export function canonical(value) {
|
|
|
47
47
|
}
|
|
48
48
|
export { resolveResourceLimits, BUFFERED_JSON_MAX_BYTES, MAX_TIMER_MS, } from "./resource-policy.js";
|
|
49
49
|
/** Current train identifier; wire families require exact kind/version validation. */
|
|
50
|
-
export const IFSOUL_VERSION = "0.
|
|
50
|
+
export const IFSOUL_VERSION = "0.6.0";
|
|
51
51
|
/** Structural current ModelProvider spec validation; not a provider wire transport. */
|
|
52
52
|
export function validateModelProviderDocument(value) {
|
|
53
53
|
return validators.current_provider(value) === true;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type BehaviorPermission = "observe" | "remember" | "updateRelationship" | "speak" | "requestAction";
|
|
2
|
+
export type BehaviorPermissions = Record<BehaviorPermission, boolean>;
|
|
3
|
+
export type BehaviorEventKind = "provocation" | "helpful" | "observation";
|
|
4
|
+
export interface BehaviorPolicy {
|
|
5
|
+
general: BehaviorPermissions;
|
|
6
|
+
provocation: BehaviorPermissions;
|
|
7
|
+
}
|
|
8
|
+
export interface BehaviorGoal {
|
|
9
|
+
goalId: string;
|
|
10
|
+
objective: string;
|
|
11
|
+
priority: number;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ParameterDefinition {
|
|
15
|
+
parameterId: string;
|
|
16
|
+
label: string;
|
|
17
|
+
kind: "trait" | "state" | "relationship";
|
|
18
|
+
scope: "portable" | "world";
|
|
19
|
+
initial: number;
|
|
20
|
+
min: number;
|
|
21
|
+
max: number;
|
|
22
|
+
maxDelta: number;
|
|
23
|
+
decay?: {
|
|
24
|
+
perHour: number;
|
|
25
|
+
toward: number;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface BehaviorSettings {
|
|
29
|
+
policy: BehaviorPolicy;
|
|
30
|
+
goals: BehaviorGoal[];
|
|
31
|
+
}
|
|
32
|
+
export interface BehaviorOverride {
|
|
33
|
+
scope: {
|
|
34
|
+
kind: "npc" | "group" | "world";
|
|
35
|
+
id: string;
|
|
36
|
+
};
|
|
37
|
+
policy?: {
|
|
38
|
+
general?: Partial<BehaviorPermissions>;
|
|
39
|
+
provocation?: Partial<BehaviorPermissions>;
|
|
40
|
+
};
|
|
41
|
+
goals?: BehaviorGoal[];
|
|
42
|
+
initialValues?: Record<string, number>;
|
|
43
|
+
}
|
|
44
|
+
export interface BehaviorConfiguration {
|
|
45
|
+
version: "ifsoul-behavior/0.4.0";
|
|
46
|
+
revision: number;
|
|
47
|
+
parameters: ParameterDefinition[];
|
|
48
|
+
defaults: BehaviorSettings;
|
|
49
|
+
overrides: BehaviorOverride[];
|
|
50
|
+
}
|
|
51
|
+
/** Injected by trusted server composition; never accepted in configuration bodies. */
|
|
52
|
+
export interface BehaviorOperatorPolicy {
|
|
53
|
+
canEditConfig: boolean;
|
|
54
|
+
lockedParameterIds: string[];
|
|
55
|
+
deniedPermissions: BehaviorPermission[];
|
|
56
|
+
lockedPolicy: boolean;
|
|
57
|
+
lockedGoals: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface BehaviorParameterValue {
|
|
60
|
+
npcId: string;
|
|
61
|
+
parameterId: string;
|
|
62
|
+
targetParticipantId?: string;
|
|
63
|
+
worldId?: string;
|
|
64
|
+
value: number;
|
|
65
|
+
confidence: number;
|
|
66
|
+
revision: number;
|
|
67
|
+
updatedAt: number;
|
|
68
|
+
}
|
|
69
|
+
export interface BehaviorChange {
|
|
70
|
+
changeId: string;
|
|
71
|
+
npcId: string;
|
|
72
|
+
parameterId: string;
|
|
73
|
+
targetParticipantId?: string;
|
|
74
|
+
worldId?: string;
|
|
75
|
+
before: number;
|
|
76
|
+
after: number;
|
|
77
|
+
confidence: number;
|
|
78
|
+
revision: number;
|
|
79
|
+
configRevision: number;
|
|
80
|
+
evidenceEventIds: string[];
|
|
81
|
+
reason: string;
|
|
82
|
+
kind: "evidence" | "decay";
|
|
83
|
+
recordedAt: number;
|
|
84
|
+
}
|
|
85
|
+
export interface BehaviorProposal {
|
|
86
|
+
proposalId: string;
|
|
87
|
+
npcId: string;
|
|
88
|
+
sessionId: string;
|
|
89
|
+
configRevision: number;
|
|
90
|
+
expectedStateRevision: number;
|
|
91
|
+
changes: {
|
|
92
|
+
parameterId: string;
|
|
93
|
+
targetParticipantId?: string;
|
|
94
|
+
delta: number;
|
|
95
|
+
confidence: number;
|
|
96
|
+
evidenceEventIds: string[];
|
|
97
|
+
reason: string;
|
|
98
|
+
}[];
|
|
99
|
+
}
|
|
100
|
+
export interface BehaviorObservation {
|
|
101
|
+
eventId: string;
|
|
102
|
+
npcId: string;
|
|
103
|
+
kind: BehaviorEventKind;
|
|
104
|
+
configRevision: number;
|
|
105
|
+
permissions: BehaviorPermissions;
|
|
106
|
+
}
|
|
107
|
+
export interface BehaviorState {
|
|
108
|
+
format: "ifsoul-behavior/0.4.0";
|
|
109
|
+
config: BehaviorConfiguration;
|
|
110
|
+
observations: BehaviorObservation[];
|
|
111
|
+
values: BehaviorParameterValue[];
|
|
112
|
+
history: BehaviorChange[];
|
|
113
|
+
proposals: {
|
|
114
|
+
proposal: BehaviorProposal;
|
|
115
|
+
revision: number;
|
|
116
|
+
}[];
|
|
117
|
+
stateRevisions: Record<string, number>;
|
|
118
|
+
}
|
|
119
|
+
export interface BehaviorInspection {
|
|
120
|
+
npcId: string;
|
|
121
|
+
sessionId: string;
|
|
122
|
+
configRevision: number;
|
|
123
|
+
stateRevision: number;
|
|
124
|
+
effective: BehaviorSettings & {
|
|
125
|
+
initialValues: Record<string, number>;
|
|
126
|
+
};
|
|
127
|
+
parameters: ParameterDefinition[];
|
|
128
|
+
values: BehaviorParameterValue[];
|
|
129
|
+
history: BehaviorChange[];
|
|
130
|
+
}
|
|
131
|
+
export interface BehaviorEditorSnapshot {
|
|
132
|
+
config: BehaviorConfiguration;
|
|
133
|
+
operator: BehaviorOperatorPolicy;
|
|
134
|
+
catalog: {
|
|
135
|
+
npcs: {
|
|
136
|
+
npcId: string;
|
|
137
|
+
label: string;
|
|
138
|
+
}[];
|
|
139
|
+
sessions: {
|
|
140
|
+
sessionId: string;
|
|
141
|
+
worldId: string;
|
|
142
|
+
npcIds: string[];
|
|
143
|
+
}[];
|
|
144
|
+
groups: {
|
|
145
|
+
groupId: string;
|
|
146
|
+
npcIds: string[];
|
|
147
|
+
}[];
|
|
148
|
+
worlds: string[];
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ParticipantState, ParticipantStore, SessionAccess } from "./participants.js";
|
|
2
|
+
import type { BehaviorConfiguration, BehaviorOperatorPolicy, BehaviorPermissions, BehaviorSettings, BehaviorInspection, BehaviorProposal, BehaviorEventKind, BehaviorEditorSnapshot } from "./behavior-types.js";
|
|
3
|
+
export type * from "./behavior-types.js";
|
|
4
|
+
export declare const DEFAULT_BEHAVIOR_OPERATOR: Readonly<BehaviorOperatorPolicy>;
|
|
5
|
+
export interface BehaviorLimits {
|
|
6
|
+
parameters: number;
|
|
7
|
+
overrides: number;
|
|
8
|
+
values: number;
|
|
9
|
+
history: number;
|
|
10
|
+
observations: number;
|
|
11
|
+
proposals: number;
|
|
12
|
+
changesPerProposal: number;
|
|
13
|
+
evidencePerChange: number;
|
|
14
|
+
}
|
|
15
|
+
export declare const DEFAULT_BEHAVIOR_LIMITS: Readonly<BehaviorLimits>;
|
|
16
|
+
export declare function defaultBehaviorConfiguration(): BehaviorConfiguration;
|
|
17
|
+
/** World -> matching groups in lexicographic ID order -> NPC -> immutable operator denies. */
|
|
18
|
+
export declare function effectiveBehavior(s: ParticipantState, access: SessionAccess, npcId: string, sessionId: string, operator?: BehaviorOperatorPolicy): BehaviorSettings & {
|
|
19
|
+
initialValues: Record<string, number>;
|
|
20
|
+
};
|
|
21
|
+
export declare function behaviorPermissions(s: ParticipantState, access: SessionAccess, npcId: string, sessionId: string, eventId?: string, operator?: BehaviorOperatorPolicy): BehaviorPermissions;
|
|
22
|
+
export declare const behaviorRevision: (s: ParticipantState) => number;
|
|
23
|
+
/** Previously unobserved/unremembered evidence never becomes implicit model context. */
|
|
24
|
+
export declare function behaviorEvidenceVisible(s: ParticipantState, access: SessionAccess, npcId: string, sessionId: string, eventId: string, operator?: BehaviorOperatorPolicy): boolean;
|
|
25
|
+
export declare function createBehaviorRuntime(store: ParticipantStore, options?: {
|
|
26
|
+
operator?: BehaviorOperatorPolicy;
|
|
27
|
+
limits?: Partial<BehaviorLimits>;
|
|
28
|
+
ceilings?: Partial<BehaviorLimits>;
|
|
29
|
+
}): {
|
|
30
|
+
read(a: SessionAccess): BehaviorEditorSnapshot;
|
|
31
|
+
update(a: SessionAccess, expectedRevision: number, config: BehaviorConfiguration): BehaviorConfiguration;
|
|
32
|
+
adminInspect(a: SessionAccess, sessionId: string, npcId: string): BehaviorInspection;
|
|
33
|
+
inspect(a: SessionAccess, sessionId: string, npcId: string): BehaviorInspection;
|
|
34
|
+
observe(a: SessionAccess, sessionId: string, npcId: string, eventId: string, kind: BehaviorEventKind): {
|
|
35
|
+
observation: import("./behavior-types.js").BehaviorObservation;
|
|
36
|
+
fresh: boolean;
|
|
37
|
+
};
|
|
38
|
+
apply(a: SessionAccess, proposal: BehaviorProposal, nowMs: number): BehaviorInspection;
|
|
39
|
+
decay(a: SessionAccess, sessionId: string, npcId: string, nowMs: number): BehaviorInspection;
|
|
40
|
+
};
|