run-dmcp 0.8.0 → 0.10.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/dist/bin/run-dmcp.js +16 -1
- package/dist/db/schema.js +62 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +12 -1
- package/dist/mcp-server.d.ts +6 -1
- package/dist/mcp-server.js +11 -3
- package/dist/reader/preflight.d.ts +62 -0
- package/dist/reader/preflight.js +40 -0
- package/dist/reader/turnReader.d.ts +169 -13
- package/dist/reader/turnReader.js +232 -78
- package/dist/register/conditions.d.ts +16 -0
- package/dist/register/conditions.js +45 -0
- package/dist/register/reader.d.ts +2 -0
- package/dist/register/reader.js +132 -0
- package/dist/register/render.js +7 -2
- package/dist/register/resolve.js +8 -2
- package/dist/register/timeline.js +29 -2
- package/dist/rpg/server.d.ts +2 -0
- package/dist/schemas/index.d.ts +10 -10
- package/dist/timeline/constrained.d.ts +4 -5
- package/dist/timeline/constrained.js +31 -25
- package/dist/timeline/declared.d.ts +147 -0
- package/dist/timeline/declared.js +388 -0
- package/dist/timeline/registry.d.ts +42 -4
- package/dist/timeline/registry.js +62 -4
- package/dist/timeline/render.d.ts +3 -0
- package/dist/timeline/render.js +1 -1
- package/dist/timeline/replay.d.ts +15 -0
- package/dist/timeline/replay.js +33 -2
- package/dist/timeline/resolve.d.ts +60 -5
- package/dist/timeline/resolve.js +135 -5
- package/dist/timeline/schema.js +28 -0
- package/dist/timeline/turns.d.ts +60 -0
- package/dist/timeline/turns.js +93 -0
- package/dist/tools/constraint.js +10 -20
- package/dist/types/index.d.ts +3 -0
- package/dist/utils/output-schemas.d.ts +6 -6
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { ANNOTATIONS } from "../utils/tool-annotations.js";
|
|
3
3
|
import { createLogger } from "../utils/logger.js";
|
|
4
4
|
import { replay } from "../timeline/replay.js";
|
|
5
|
+
import { declareTurnOrder, dueAt, advanceTurn } from "../timeline/turns.js";
|
|
5
6
|
import { declareTimeAxis, setStoryTime, currentStoryTime } from "../timeline/clock.js";
|
|
6
7
|
import { declareIrreversible, listIrreversibleFacts } from "../timeline/irreversible.js";
|
|
7
8
|
import { exportTimelineToFile, importTimelineFromFile } from "../timeline/export.js";
|
|
@@ -46,11 +47,16 @@ export function registerTimelineTools(server) {
|
|
|
46
47
|
inputSchema: {
|
|
47
48
|
gameId: z.string().max(100).describe("The game ID"),
|
|
48
49
|
t: tSchema,
|
|
50
|
+
entityIds: z
|
|
51
|
+
.array(z.string().max(100))
|
|
52
|
+
.max(10000)
|
|
53
|
+
.optional()
|
|
54
|
+
.describe("Scope the read to these entities -- a selection you built, such as what one character perceives. Omitted: every entity. An empty list: none. An id not alive at t is simply absent."),
|
|
49
55
|
},
|
|
50
56
|
annotations: ANNOTATIONS.READ_ONLY,
|
|
51
|
-
}, async ({ gameId, t }) => {
|
|
57
|
+
}, async ({ gameId, t, entityIds }) => {
|
|
52
58
|
try {
|
|
53
|
-
const snapshot = replay({ gameId, t });
|
|
59
|
+
const snapshot = replay({ gameId, t, entityIds });
|
|
54
60
|
return { content: [{ type: "text", text: JSON.stringify(snapshot, null, 2) }] };
|
|
55
61
|
}
|
|
56
62
|
catch (error) {
|
|
@@ -308,4 +314,25 @@ export function registerTimelineTools(server) {
|
|
|
308
314
|
};
|
|
309
315
|
}
|
|
310
316
|
});
|
|
317
|
+
// Issue #40: a principal is due to act. The engine records whose turn it
|
|
318
|
+
// is and refuses an out-of-turn `resolve`; it never runs a turn.
|
|
319
|
+
const turnTool = (name, description, inputSchema, annotations, run) => server.registerTool(name, { description, inputSchema, annotations }, async (args) => {
|
|
320
|
+
try {
|
|
321
|
+
return { content: [{ type: "text", text: JSON.stringify(run(args), null, 2) }] };
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
log.error(`${name} failed`, { error: error.message });
|
|
325
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: error.message }) }], isError: true };
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
turnTool("declare_turn_order", "Declare which entities act, in what cycle, one per t on this game's counter axis from fromT onward. Append-only: " +
|
|
329
|
+
"to change the order, declare again from a later t -- earlier turns keep answering as they did. Once declared, a resolve " +
|
|
330
|
+
"naming an actor who is not due at the current t is refused. The engine records whose turn it is; it never runs one.", {
|
|
331
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
332
|
+
principals: z.array(z.string().max(100)).max(1000).describe("Entity ids, in the order they act."),
|
|
333
|
+
fromT: tSchema.describe("The first t this order covers. An integer t on the counter axis, not before the current t."),
|
|
334
|
+
}, ANNOTATIONS.UPDATE, (a) => declareTurnOrder({ gameId: a.gameId, principals: a.principals, fromT: a.fromT }));
|
|
335
|
+
turnTool("due_at", "Who is due to act at t under this game's declared turn order -- any t, past included -- with the round and " +
|
|
336
|
+
"position in the cycle. {due: null} when no declaration covers t.", { gameId: z.string().max(100).describe("The game ID"), t: tSchema }, ANNOTATIONS.READ_ONLY, (a) => dueAt({ gameId: a.gameId, t: a.t }) ?? { due: null });
|
|
337
|
+
turnTool("advance_turn", "Move this game's clock to the next turn and say who is due there. Moves time only; runs nothing.", { gameId: z.string().max(100).describe("The game ID") }, ANNOTATIONS.UPDATE, (a) => advanceTurn({ gameId: a.gameId }));
|
|
311
338
|
}
|
package/dist/rpg/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import type { Mechanic } from "../timeline/resolve.js";
|
|
3
3
|
import type { RenderVocabulary } from "../timeline/render.js";
|
|
4
|
+
import type { DeclaredRules } from "../timeline/declared.js";
|
|
4
5
|
/**
|
|
5
6
|
* Build the FULL assembly: every core tool plus every RPG tool, resource and
|
|
6
7
|
* prompt this engine has always served. Same name and same `{ mechanics?,
|
|
@@ -12,5 +13,6 @@ import type { RenderVocabulary } from "../timeline/render.js";
|
|
|
12
13
|
export declare function createMcpServer(options?: {
|
|
13
14
|
mechanics?: readonly Mechanic[];
|
|
14
15
|
vocabulary?: RenderVocabulary;
|
|
16
|
+
rules?: DeclaredRules;
|
|
15
17
|
}): McpServer;
|
|
16
18
|
export { createHttpServer, startHttpServer } from "../http/server.js";
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -83,14 +83,14 @@ export declare const subjectDescriptionSchema: z.ZodObject<{
|
|
|
83
83
|
condition: z.ZodOptional<z.ZodString>;
|
|
84
84
|
glowOrEffects: z.ZodOptional<z.ZodString>;
|
|
85
85
|
}, "strip", z.ZodTypeAny, {
|
|
86
|
+
condition?: string | undefined;
|
|
86
87
|
size?: string | undefined;
|
|
87
88
|
material?: string | undefined;
|
|
88
|
-
condition?: string | undefined;
|
|
89
89
|
glowOrEffects?: string | undefined;
|
|
90
90
|
}, {
|
|
91
|
+
condition?: string | undefined;
|
|
91
92
|
size?: string | undefined;
|
|
92
93
|
material?: string | undefined;
|
|
93
|
-
condition?: string | undefined;
|
|
94
94
|
glowOrEffects?: string | undefined;
|
|
95
95
|
}>>;
|
|
96
96
|
pose: z.ZodOptional<z.ZodString>;
|
|
@@ -127,9 +127,9 @@ export declare const subjectDescriptionSchema: z.ZodObject<{
|
|
|
127
127
|
vegetation?: string | undefined;
|
|
128
128
|
} | undefined;
|
|
129
129
|
objectDetails?: {
|
|
130
|
+
condition?: string | undefined;
|
|
130
131
|
size?: string | undefined;
|
|
131
132
|
material?: string | undefined;
|
|
132
|
-
condition?: string | undefined;
|
|
133
133
|
glowOrEffects?: string | undefined;
|
|
134
134
|
} | undefined;
|
|
135
135
|
pose?: string | undefined;
|
|
@@ -166,9 +166,9 @@ export declare const subjectDescriptionSchema: z.ZodObject<{
|
|
|
166
166
|
vegetation?: string | undefined;
|
|
167
167
|
} | undefined;
|
|
168
168
|
objectDetails?: {
|
|
169
|
+
condition?: string | undefined;
|
|
169
170
|
size?: string | undefined;
|
|
170
171
|
material?: string | undefined;
|
|
171
|
-
condition?: string | undefined;
|
|
172
172
|
glowOrEffects?: string | undefined;
|
|
173
173
|
} | undefined;
|
|
174
174
|
pose?: string | undefined;
|
|
@@ -394,14 +394,14 @@ export declare const imageGenSchema: z.ZodObject<{
|
|
|
394
394
|
condition: z.ZodOptional<z.ZodString>;
|
|
395
395
|
glowOrEffects: z.ZodOptional<z.ZodString>;
|
|
396
396
|
}, "strip", z.ZodTypeAny, {
|
|
397
|
+
condition?: string | undefined;
|
|
397
398
|
size?: string | undefined;
|
|
398
399
|
material?: string | undefined;
|
|
399
|
-
condition?: string | undefined;
|
|
400
400
|
glowOrEffects?: string | undefined;
|
|
401
401
|
}, {
|
|
402
|
+
condition?: string | undefined;
|
|
402
403
|
size?: string | undefined;
|
|
403
404
|
material?: string | undefined;
|
|
404
|
-
condition?: string | undefined;
|
|
405
405
|
glowOrEffects?: string | undefined;
|
|
406
406
|
}>>;
|
|
407
407
|
pose: z.ZodOptional<z.ZodString>;
|
|
@@ -438,9 +438,9 @@ export declare const imageGenSchema: z.ZodObject<{
|
|
|
438
438
|
vegetation?: string | undefined;
|
|
439
439
|
} | undefined;
|
|
440
440
|
objectDetails?: {
|
|
441
|
+
condition?: string | undefined;
|
|
441
442
|
size?: string | undefined;
|
|
442
443
|
material?: string | undefined;
|
|
443
|
-
condition?: string | undefined;
|
|
444
444
|
glowOrEffects?: string | undefined;
|
|
445
445
|
} | undefined;
|
|
446
446
|
pose?: string | undefined;
|
|
@@ -477,9 +477,9 @@ export declare const imageGenSchema: z.ZodObject<{
|
|
|
477
477
|
vegetation?: string | undefined;
|
|
478
478
|
} | undefined;
|
|
479
479
|
objectDetails?: {
|
|
480
|
+
condition?: string | undefined;
|
|
480
481
|
size?: string | undefined;
|
|
481
482
|
material?: string | undefined;
|
|
482
|
-
condition?: string | undefined;
|
|
483
483
|
glowOrEffects?: string | undefined;
|
|
484
484
|
} | undefined;
|
|
485
485
|
pose?: string | undefined;
|
|
@@ -726,9 +726,9 @@ export declare const imageGenSchema: z.ZodObject<{
|
|
|
726
726
|
vegetation?: string | undefined;
|
|
727
727
|
} | undefined;
|
|
728
728
|
objectDetails?: {
|
|
729
|
+
condition?: string | undefined;
|
|
729
730
|
size?: string | undefined;
|
|
730
731
|
material?: string | undefined;
|
|
731
|
-
condition?: string | undefined;
|
|
732
732
|
glowOrEffects?: string | undefined;
|
|
733
733
|
} | undefined;
|
|
734
734
|
pose?: string | undefined;
|
|
@@ -822,9 +822,9 @@ export declare const imageGenSchema: z.ZodObject<{
|
|
|
822
822
|
vegetation?: string | undefined;
|
|
823
823
|
} | undefined;
|
|
824
824
|
objectDetails?: {
|
|
825
|
+
condition?: string | undefined;
|
|
825
826
|
size?: string | undefined;
|
|
826
827
|
material?: string | undefined;
|
|
827
|
-
condition?: string | undefined;
|
|
828
828
|
glowOrEffects?: string | undefined;
|
|
829
829
|
} | undefined;
|
|
830
830
|
pose?: string | undefined;
|
|
@@ -190,11 +190,10 @@ export declare function transferConstrainedValue(params: {
|
|
|
190
190
|
* Fact transitions are joined to their annotation (if any) by
|
|
191
191
|
* `json_extract(causes, '$.fact_id') = facts.id` -- an EXACT, unique link,
|
|
192
192
|
* because `applyLiveWrite` recorded the fact id at write time. This is
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
* id costs nothing extra and removes the approximation entirely.
|
|
193
|
+
* the same move issue #30 later made for a fact's opening event
|
|
194
|
+
* (`facts.opened_by_event_id`, stamped by the projection trigger in the
|
|
195
|
+
* firing that opens the fact): record the edge when it is true rather than
|
|
196
|
+
* approximate it afterward from `(at_t, row_id)`.
|
|
198
197
|
*
|
|
199
198
|
* `json_valid(causes)` guards every extraction, matching irreversible.ts's
|
|
200
199
|
* `CASE WHEN json_valid(causes) THEN causes END` idiom for the same reason
|
|
@@ -122,12 +122,23 @@ export function assertConstraintsAllow(params) {
|
|
|
122
122
|
throw new ConstraintViolationError("monotonic", entityId, `Resource '${entityId}' is constrained to never increase; rejected change from ${previousValue} to ${intendedValue}.`);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
if (constraint.kind === "bounded"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
if (constraint.kind === "bounded") {
|
|
126
|
+
// Two sources of bounds, both checked, so the tighter side wins. The
|
|
127
|
+
// write's own `bounds` are how every 'bounded' constraint has always
|
|
128
|
+
// worked (a resource's min/max columns, supplied by its caller). A
|
|
129
|
+
// constraint DECLARED with bounds -- a `create` leg's `constraints`
|
|
130
|
+
// (issue #42) -- carries its own, and holds them against a write that
|
|
131
|
+
// supplies none: "held bounded from the moment it exists" means the
|
|
132
|
+
// declaration binds, not that every later writer must repeat it.
|
|
133
|
+
// Declared nulls leave that side to the write, as before.
|
|
134
|
+
const declared = { minValue: constraint.minValue ?? null, maxValue: constraint.maxValue ?? null };
|
|
135
|
+
for (const pair of bounds ? [declared, bounds] : [declared]) {
|
|
136
|
+
if (pair.minValue !== null && intendedValue < pair.minValue) {
|
|
137
|
+
throw new ConstraintViolationError("bounded", entityId, `Resource '${entityId}' is bounded-constrained (min ${pair.minValue}); rejected value ${intendedValue} instead of clamping.`);
|
|
138
|
+
}
|
|
139
|
+
if (pair.maxValue !== null && intendedValue > pair.maxValue) {
|
|
140
|
+
throw new ConstraintViolationError("bounded", entityId, `Resource '${entityId}' is bounded-constrained (max ${pair.maxValue}); rejected value ${intendedValue} instead of clamping.`);
|
|
141
|
+
}
|
|
131
142
|
}
|
|
132
143
|
}
|
|
133
144
|
// 'resolve_only' (design §5.3, §5.2a; issue #13): every direct write to
|
|
@@ -270,20 +281,16 @@ function applyLiveWrite(params) {
|
|
|
270
281
|
const at = new Date().toISOString();
|
|
271
282
|
const delta = newValue - previousValue;
|
|
272
283
|
const eventId = uuidv4();
|
|
273
|
-
// `causes`
|
|
274
|
-
//
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
// `
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
// land at the same `t`) would make `findOpenedByEventId`'s pick
|
|
284
|
-
// non-deterministic. `entity_id` is the accurate key for what this event
|
|
285
|
-
// is about anyway, so using it instead of `row_id` is both the honest
|
|
286
|
-
// name and the one that can never collide with that lookup.
|
|
284
|
+
// `causes` carries `entity_id`, not `row_id`. `row_id` is the PROJECTION
|
|
285
|
+
// triggers' own token for "the live row this projection event was
|
|
286
|
+
// generated from" (projection.ts), and an annotation event this choke
|
|
287
|
+
// point writes is not a projection event, so `entity_id` is the honest
|
|
288
|
+
// name for what it is about. (This once also kept a derived one-hop
|
|
289
|
+
// lookup deterministic when a projection event and this event shared a
|
|
290
|
+
// `t`; issue #30 replaced that lookup with the recorded
|
|
291
|
+
// `facts.opened_by_event_id`, so that reason is gone. `projectionEventId`
|
|
292
|
+
// below still matches `$.row_id`, but filters by event kind, so it could
|
|
293
|
+
// not collide either way.)
|
|
287
294
|
const causes = JSON.stringify({
|
|
288
295
|
source: "constrained_write",
|
|
289
296
|
entity_id: entityId,
|
|
@@ -555,11 +562,10 @@ export function transferConstrainedValue(params) {
|
|
|
555
562
|
* Fact transitions are joined to their annotation (if any) by
|
|
556
563
|
* `json_extract(causes, '$.fact_id') = facts.id` -- an EXACT, unique link,
|
|
557
564
|
* because `applyLiveWrite` recorded the fact id at write time. This is
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
* id costs nothing extra and removes the approximation entirely.
|
|
565
|
+
* the same move issue #30 later made for a fact's opening event
|
|
566
|
+
* (`facts.opened_by_event_id`, stamped by the projection trigger in the
|
|
567
|
+
* firing that opens the fact): record the edge when it is true rather than
|
|
568
|
+
* approximate it afterward from `(at_t, row_id)`.
|
|
563
569
|
*
|
|
564
570
|
* `json_valid(causes)` guards every extraction, matching irreversible.ts's
|
|
565
571
|
* `CASE WHEN json_valid(causes) THEN causes END` idiom for the same reason
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type T } from "./t.js";
|
|
2
|
+
import type { Mechanic } from "./resolve.js";
|
|
3
|
+
/**
|
|
4
|
+
* Mechanics, gates and end conditions as DECLARED DATA (GitHub issue #41),
|
|
5
|
+
* so a stock server can load a game whose rules would otherwise be
|
|
6
|
+
* TypeScript handed to `createResolver` by whoever assembled it.
|
|
7
|
+
*
|
|
8
|
+
* DELIBERATELY SMALL. The issue's admission paragraph scopes the first cut to
|
|
9
|
+
* what its real caller's rules are made of -- bounded changes to numeric
|
|
10
|
+
* facts, and gates that open once a fact crosses a threshold -- "a small,
|
|
11
|
+
* generic subset to start from, not a general rules language". So:
|
|
12
|
+
*
|
|
13
|
+
* - a CONDITION is a named set of clauses (`all` or `any`), each comparing
|
|
14
|
+
* one stored fact to a declared value, or naming another condition;
|
|
15
|
+
* - a DECLARED MECHANIC applies its `legs` when every condition in `when`
|
|
16
|
+
* holds, and its `otherwise` legs (or nothing) when one does not;
|
|
17
|
+
* - a leg is `adjust` (current + sign * amount, clamped) or `set`.
|
|
18
|
+
*
|
|
19
|
+
* Anything else stays a TypeScript `Mechanic` -- that is the escape hatch, and
|
|
20
|
+
* both kinds register side by side on one resolver. A rule that needs an
|
|
21
|
+
* event ("after a search") rather than a fact, or arithmetic across
|
|
22
|
+
* entities, is the escape hatch's, not a reason to grow this.
|
|
23
|
+
*
|
|
24
|
+
* THE ENGINE READS NONE OF IT FOR MEANING (hard rule 4). `id`, `name`,
|
|
25
|
+
* `for`, `then` and `text` are opaque strings, compared for equality or
|
|
26
|
+
* carried through. A clause compares a stored value with a declared one:
|
|
27
|
+
* numerically when both are finite numbers, by equality otherwise -- a
|
|
28
|
+
* structural comparison of the same kind `contradictions()` makes, never an
|
|
29
|
+
* interpretation. `holds` is that comparison's result (hard rule 2): a row
|
|
30
|
+
* saying a stored value meets a declared threshold, never "the game is won".
|
|
31
|
+
*
|
|
32
|
+
* WHY `for`/`then` LIVE HERE. A principal's condition list -- "if these hold,
|
|
33
|
+
* this becomes available to you" -- is what measurably made model-driven
|
|
34
|
+
* principals act on their unlocks. Declared once, the list a caller renders
|
|
35
|
+
* and the gate a mechanic enforces are the SAME data, instead of prose kept
|
|
36
|
+
* in step with code by a test.
|
|
37
|
+
*
|
|
38
|
+
* All of it is plain JSON, so it round-trips through a file unchanged; the
|
|
39
|
+
* application loads one from `DMCP_RULES_FILE` (src/bin/run-dmcp.ts).
|
|
40
|
+
*/
|
|
41
|
+
export type ComparisonOp = "<" | "<=" | "==" | "!=" | ">=" | ">";
|
|
42
|
+
/** A value from the proposal's own parameters, by name. */
|
|
43
|
+
export interface ParamRef {
|
|
44
|
+
param: string;
|
|
45
|
+
}
|
|
46
|
+
/** An entity: its id, a proposal parameter holding its id, or its name --
|
|
47
|
+
* resolved against the entities that have facts at `t`, and reported as
|
|
48
|
+
* unresolved when no entity or more than one carries that name. */
|
|
49
|
+
export type EntityOperand = string | ParamRef | {
|
|
50
|
+
named: string;
|
|
51
|
+
};
|
|
52
|
+
export type FactClause = {
|
|
53
|
+
entity: EntityOperand;
|
|
54
|
+
key: string;
|
|
55
|
+
op: ComparisonOp;
|
|
56
|
+
value: number | string | ParamRef;
|
|
57
|
+
/** Opaque, carried through for a caller's condition list. */
|
|
58
|
+
text?: string;
|
|
59
|
+
};
|
|
60
|
+
export type ConditionClause = FactClause | {
|
|
61
|
+
condition: string;
|
|
62
|
+
};
|
|
63
|
+
export interface DeclaredCondition {
|
|
64
|
+
id: string;
|
|
65
|
+
/** Opaque: whose condition this is, for rendering one principal's list. */
|
|
66
|
+
for?: string;
|
|
67
|
+
/** Opaque: what holding it makes available. */
|
|
68
|
+
then?: string;
|
|
69
|
+
all?: readonly ConditionClause[];
|
|
70
|
+
any?: readonly ConditionClause[];
|
|
71
|
+
}
|
|
72
|
+
export type NumberOperand = number | ParamRef;
|
|
73
|
+
export type DeclaredLeg = {
|
|
74
|
+
kind: "adjust";
|
|
75
|
+
entity: EntityOperand;
|
|
76
|
+
key: string;
|
|
77
|
+
amount: NumberOperand;
|
|
78
|
+
/** 1 adds, -1 subtracts. Default 1. */
|
|
79
|
+
sign?: 1 | -1;
|
|
80
|
+
min?: NumberOperand | null;
|
|
81
|
+
max?: NumberOperand | null;
|
|
82
|
+
} | {
|
|
83
|
+
kind: "set";
|
|
84
|
+
entity: EntityOperand;
|
|
85
|
+
key: string;
|
|
86
|
+
value: number | string | null | ParamRef;
|
|
87
|
+
min?: NumberOperand | null;
|
|
88
|
+
max?: NumberOperand | null;
|
|
89
|
+
};
|
|
90
|
+
export interface DeclaredMechanic {
|
|
91
|
+
name: string;
|
|
92
|
+
/** Condition ids that must ALL hold for `legs` to apply. */
|
|
93
|
+
when?: readonly string[];
|
|
94
|
+
legs: readonly DeclaredLeg[];
|
|
95
|
+
/** Applied instead when a `when` condition does not hold. */
|
|
96
|
+
otherwise?: readonly DeclaredLeg[];
|
|
97
|
+
}
|
|
98
|
+
export interface DeclaredRules {
|
|
99
|
+
conditions: readonly DeclaredCondition[];
|
|
100
|
+
mechanics: readonly DeclaredMechanic[];
|
|
101
|
+
}
|
|
102
|
+
export interface ClauseRow {
|
|
103
|
+
/** Present for a fact clause. */
|
|
104
|
+
entityId?: string | null;
|
|
105
|
+
resolution?: "resolved" | "none" | "ambiguous";
|
|
106
|
+
key?: string;
|
|
107
|
+
op?: ComparisonOp;
|
|
108
|
+
value?: number | string;
|
|
109
|
+
observed?: number | string | null;
|
|
110
|
+
/** Present for a clause naming another condition. */
|
|
111
|
+
condition?: string;
|
|
112
|
+
/** The proposal parameter this clause needed and was not given; the clause
|
|
113
|
+
* then does not hold. A gate that fails because of it is refused naming it. */
|
|
114
|
+
missingParameter?: string;
|
|
115
|
+
text?: string;
|
|
116
|
+
holds: boolean;
|
|
117
|
+
}
|
|
118
|
+
export interface ConditionRow {
|
|
119
|
+
id: string;
|
|
120
|
+
for?: string;
|
|
121
|
+
then?: string;
|
|
122
|
+
holds: boolean;
|
|
123
|
+
clauses: ClauseRow[];
|
|
124
|
+
}
|
|
125
|
+
export declare function validateDeclaredRules(rules: DeclaredRules): void;
|
|
126
|
+
type Params = Readonly<Record<string, unknown>>;
|
|
127
|
+
/**
|
|
128
|
+
* Every declared condition at `t`, in declared order, with whether it holds
|
|
129
|
+
* and each clause's observed value -- or only the conditions whose `for`
|
|
130
|
+
* equals the one given, which is a principal's condition list. Read-only.
|
|
131
|
+
*/
|
|
132
|
+
export declare function evaluateConditions(params: {
|
|
133
|
+
gameId: string;
|
|
134
|
+
t: T;
|
|
135
|
+
rules: DeclaredRules;
|
|
136
|
+
for?: string;
|
|
137
|
+
parameters?: Params;
|
|
138
|
+
}): ConditionRow[];
|
|
139
|
+
/**
|
|
140
|
+
* One `Mechanic` per declared mechanic, for `createResolver` beside any
|
|
141
|
+
* hand-written ones. Each reads only the constraint `resolve()` hands it, like
|
|
142
|
+
* every mechanic, and returns ordinary changes that go through the one choke
|
|
143
|
+
* point. `result` records what it did: which gate conditions held, which leg
|
|
144
|
+
* set applied (`legs`, `otherwise`, or `none`), and each leg's before/after.
|
|
145
|
+
*/
|
|
146
|
+
export declare function declaredMechanics(rules: DeclaredRules): Mechanic[];
|
|
147
|
+
export {};
|