@lokascript/planner 2.4.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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Type definitions for the BFS planner.
3
+ *
4
+ * These are the subset of Siren/GRAIL types that the planner depends on.
5
+ * Standalone — no external dependencies.
6
+ */
7
+ interface SirenEntity {
8
+ class?: string[];
9
+ properties?: Record<string, unknown>;
10
+ actions?: SirenAction[];
11
+ links?: SirenLink[];
12
+ entities?: SirenSubEntity[];
13
+ }
14
+ interface SirenAction {
15
+ name: string;
16
+ title?: string;
17
+ method?: string;
18
+ href: string;
19
+ type?: string;
20
+ fields?: SirenField[];
21
+ /** Cooperative affordance: preconditions that must be met */
22
+ preconditions?: string[];
23
+ /** Cooperative affordance: effects of this action */
24
+ effects?: string[];
25
+ /** Cooperative affordance: relative effort weight for planning */
26
+ cost?: number;
27
+ }
28
+ interface SirenField {
29
+ name: string;
30
+ type?: string;
31
+ value?: unknown;
32
+ title?: string;
33
+ description?: string;
34
+ options?: unknown[];
35
+ required?: boolean;
36
+ min?: number;
37
+ max?: number;
38
+ step?: number;
39
+ minlength?: number;
40
+ maxlength?: number;
41
+ pattern?: string;
42
+ }
43
+ interface SirenLink {
44
+ rel: string[];
45
+ href: string;
46
+ title?: string;
47
+ }
48
+ interface SirenSubEntity {
49
+ class?: string[];
50
+ rel?: string[];
51
+ href?: string;
52
+ properties?: Record<string, unknown>;
53
+ }
54
+ /**
55
+ * The properties block in a GRAIL 409 Blocked response body.
56
+ */
57
+ interface BlockedProperties {
58
+ status: 409;
59
+ message: string;
60
+ blockedAction: string;
61
+ blockedCondition: string;
62
+ unmetConditions?: string[];
63
+ }
64
+ /**
65
+ * A 409 Conflict response body from a GRAIL server.
66
+ * Contains the blocked action info and offered affordances.
67
+ */
68
+ interface BlockedResponse {
69
+ class: string[];
70
+ properties: BlockedProperties;
71
+ actions?: SirenAction[];
72
+ /** Active conditions on this entity (also sent as x-conditions header) */
73
+ 'x-conditions'?: string[];
74
+ }
75
+ /** Entry in a condition map from the /conditions endpoint */
76
+ interface ConditionMapEntry {
77
+ description?: string;
78
+ producedBy: string[];
79
+ requiredBy: string[];
80
+ }
81
+
82
+ /**
83
+ * BFS planner over the affordance graph.
84
+ *
85
+ * Port of siren-grail/packages/siren-agent/planner.js to TypeScript,
86
+ * extended with cost-aware path selection, preferred tiebreaker (GRAIL spec §5.4),
87
+ * and a buildDefsFromActions() path for client-side planning without a condition map.
88
+ *
89
+ * Pure module — no DOM, no fetch, no side effects.
90
+ */
91
+
92
+ interface PlanStep {
93
+ readonly name: string;
94
+ readonly params: Readonly<Record<string, string>>;
95
+ key(): string;
96
+ label(): string;
97
+ }
98
+ interface AffordanceDef {
99
+ readonly name: string;
100
+ readonly preconditions: readonly string[];
101
+ readonly effects: readonly string[];
102
+ readonly templateEffect: string | null;
103
+ readonly paramName: string | null;
104
+ readonly validValues: readonly string[];
105
+ readonly cost: number;
106
+ readonly preferred: boolean;
107
+ }
108
+ interface PlanResult {
109
+ /** Ordered steps to reach the goal (empty if already satisfied) */
110
+ steps: PlanStep[];
111
+ /** Total cost of all steps */
112
+ totalCost: number;
113
+ /** Number of alternative equal-length paths found */
114
+ alternativeCount: number;
115
+ }
116
+ interface PlanOptions {
117
+ maxDepth?: number;
118
+ maxQueueSize?: number;
119
+ blockedSteps?: PlanStep[];
120
+ }
121
+ interface HeuristicMapping {
122
+ property: string;
123
+ op: string;
124
+ value: string | number;
125
+ }
126
+ declare function createPlanStep(name: string, params?: Record<string, string>): PlanStep;
127
+ declare function createAffordanceDef(opts: {
128
+ name: string;
129
+ preconditions: string[];
130
+ effects: string[];
131
+ templateEffect?: string | null;
132
+ paramName?: string | null;
133
+ validValues?: string[];
134
+ cost?: number;
135
+ preferred?: boolean;
136
+ }): AffordanceDef;
137
+ /**
138
+ * Forward simulation: apply affordance effects, return new state.
139
+ * Handles template effect resolution and mutex prefix clearing.
140
+ */
141
+ declare function applyEffects(state: Set<string>, affDef: AffordanceDef, params: Record<string, string>, mutexPrefixes: Set<string>): Set<string>;
142
+ /**
143
+ * Infer heuristic condition mappings from condition name structure.
144
+ *
145
+ * - "x.y.z" (3 segments): property=y, op=eq, value=z
146
+ * - "x.y.z" where z="nonempty": property=yCount (singular), op=gt, value=0
147
+ * - "x.y" (2 segments): derived from action-presence
148
+ */
149
+ declare function inferHeuristicMappings(conditionNames: Set<string>): Record<string, HeuristicMapping>;
150
+ /**
151
+ * Infer active conditions from a Siren entity.
152
+ * Prefers server-provided x-conditions when available.
153
+ */
154
+ declare function inferConditions(entity: {
155
+ properties?: Record<string, unknown>;
156
+ actions?: unknown[];
157
+ 'x-conditions'?: string[];
158
+ }, conditionNames: Set<string>, mappings?: Record<string, HeuristicMapping>): Set<string>;
159
+ /**
160
+ * Parse a condition map (from /conditions endpoint) into AffordanceDef objects.
161
+ * Input shape: { conditionName: { description, producedBy: string[], requiredBy: string[] } }
162
+ */
163
+ declare function parseConditionMap(conditionMap: Record<string, ConditionMapEntry>): {
164
+ defs: AffordanceDef[];
165
+ conditionNames: Set<string>;
166
+ mutexPrefixes: Set<string>;
167
+ };
168
+ /**
169
+ * Build AffordanceDefs directly from SirenAction[].
170
+ * This is the "simple path" — no condition map or server endpoint required.
171
+ * Reads preconditions, effects, and cost from each action.
172
+ */
173
+ declare function buildDefsFromActions(actions: SirenAction[]): {
174
+ defs: AffordanceDef[];
175
+ conditionNames: Set<string>;
176
+ mutexPrefixes: Set<string>;
177
+ };
178
+ /**
179
+ * BFS: find ALL shortest action sequences from start state to goal condition.
180
+ */
181
+ declare function plan(start: Set<string>, goalCondition: string, defs: AffordanceDef[], mutexPrefixes: Set<string>, opts?: PlanOptions): PlanStep[][];
182
+ /**
183
+ * Find the lowest-cost shortest path.
184
+ * Among equal-cost paths, prefer ones with more preferred steps (spec §5.4).
185
+ */
186
+ declare function planWithCost(start: Set<string>, goalCondition: string, defs: AffordanceDef[], mutexPrefixes: Set<string>, opts?: PlanOptions): PlanResult;
187
+ /**
188
+ * Plan prerequisite chain for a goal action using a condition map.
189
+ * Uses BFS for each unmet precondition with forward state simulation.
190
+ */
191
+ declare function planForAction(entity: {
192
+ properties?: Record<string, unknown>;
193
+ actions?: unknown[];
194
+ 'x-conditions'?: string[];
195
+ }, goalActionName: string, conditionMap: Record<string, ConditionMapEntry>, opts?: PlanOptions): PlanStep[] | null;
196
+ /**
197
+ * Plan from entity actions directly — no condition map required.
198
+ * Builds AffordanceDefs from the entity's actions, infers current state,
199
+ * and runs cost-aware BFS for the goal action's unmet preconditions.
200
+ */
201
+ declare function planFromEntity(entity: SirenEntity & {
202
+ 'x-conditions'?: string[];
203
+ }, goalActionName: string, opts?: PlanOptions): PlanResult | null;
204
+
205
+ export { type AffordanceDef, type BlockedProperties, type BlockedResponse, type ConditionMapEntry, type PlanOptions, type PlanResult, type PlanStep, type SirenAction, type SirenEntity, type SirenField, type SirenLink, type SirenSubEntity, applyEffects, buildDefsFromActions, createAffordanceDef, createPlanStep, inferConditions, inferHeuristicMappings, parseConditionMap, plan, planForAction, planFromEntity, planWithCost };
package/dist/index.js ADDED
@@ -0,0 +1,358 @@
1
+ // src/planner.ts
2
+ function createPlanStep(name, params = {}) {
3
+ const frozenParams = Object.freeze({ ...params });
4
+ return Object.freeze({
5
+ name,
6
+ params: frozenParams,
7
+ key() {
8
+ const entries = Object.entries(frozenParams).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
9
+ if (entries.length === 0) return name;
10
+ return `${name}(${entries.map(([k, v]) => `${k}=${v}`).join(",")})`;
11
+ },
12
+ label() {
13
+ const entries = Object.entries(frozenParams);
14
+ if (entries.length === 0) return name;
15
+ const p = entries.map(([k, v]) => `${k}=${v}`).join(", ");
16
+ return `${name}(${p})`;
17
+ }
18
+ });
19
+ }
20
+ function createAffordanceDef(opts) {
21
+ return Object.freeze({
22
+ name: opts.name,
23
+ preconditions: Object.freeze([...opts.preconditions]),
24
+ effects: Object.freeze([...opts.effects]),
25
+ templateEffect: opts.templateEffect ?? null,
26
+ paramName: opts.paramName ?? null,
27
+ validValues: Object.freeze([...opts.validValues ?? []]),
28
+ cost: opts.cost ?? 1,
29
+ preferred: opts.preferred ?? false
30
+ });
31
+ }
32
+ function stateKey(state) {
33
+ return [...state].sort().join(",");
34
+ }
35
+ function applyEffects(state, affDef, params, mutexPrefixes) {
36
+ const next = new Set(state);
37
+ if (affDef.templateEffect && params && Object.keys(params).length > 0) {
38
+ let resolved = affDef.templateEffect;
39
+ for (const [k, v] of Object.entries(params)) {
40
+ resolved = resolved.replace(`{${k}}`, v);
41
+ }
42
+ const prefix = affDef.templateEffect.slice(0, affDef.templateEffect.indexOf("{"));
43
+ for (const c of next) {
44
+ if (c.startsWith(prefix)) next.delete(c);
45
+ }
46
+ next.add(resolved);
47
+ } else {
48
+ for (const effect of affDef.effects) {
49
+ for (const prefix of mutexPrefixes) {
50
+ if (effect.startsWith(prefix)) {
51
+ for (const c of next) {
52
+ if (c.startsWith(prefix)) next.delete(c);
53
+ }
54
+ break;
55
+ }
56
+ }
57
+ next.add(effect);
58
+ }
59
+ }
60
+ return next;
61
+ }
62
+ function inferHeuristicMappings(conditionNames) {
63
+ const mappings = {};
64
+ for (const name of conditionNames) {
65
+ const parts = name.split(".");
66
+ if (parts.length === 3) {
67
+ const [, prop, value] = parts;
68
+ if (value === "nonempty") {
69
+ const singular = prop.endsWith("s") ? prop.slice(0, -1) : prop;
70
+ mappings[name] = { property: `${singular}Count`, op: "gt", value: 0 };
71
+ } else {
72
+ mappings[name] = { property: prop, op: "eq", value };
73
+ }
74
+ } else if (parts.length === 2) {
75
+ mappings[name] = { property: "_derived", op: "action-presence", value: "" };
76
+ }
77
+ }
78
+ return mappings;
79
+ }
80
+ function inferConditions(entity, conditionNames, mappings) {
81
+ const xc = entity["x-conditions"];
82
+ if (Array.isArray(xc)) {
83
+ return new Set(xc);
84
+ }
85
+ if (!mappings) {
86
+ mappings = inferHeuristicMappings(conditionNames);
87
+ }
88
+ const props = entity.properties || {};
89
+ const actions = entity.actions || [];
90
+ const conds = /* @__PURE__ */ new Set();
91
+ for (const name of conditionNames) {
92
+ const m = mappings[name];
93
+ if (!m || m.property === "_derived") continue;
94
+ const val = props[m.property];
95
+ if (val === void 0 || val === null) continue;
96
+ if (m.op === "eq" && String(val) === String(m.value)) conds.add(name);
97
+ else if (m.op === "gt") {
98
+ try {
99
+ if (Number(val) > Number(m.value)) conds.add(name);
100
+ } catch {
101
+ }
102
+ }
103
+ }
104
+ for (const name of conditionNames) {
105
+ const m = mappings[name];
106
+ if (!m || m.property !== "_derived") continue;
107
+ if (actions.length > 0) conds.add(name);
108
+ }
109
+ return conds;
110
+ }
111
+ function detectTemplate(effects) {
112
+ if (effects.length < 2) return [null, null, effects];
113
+ let prefix = effects[0];
114
+ for (let i = 1; i < effects.length; i++) {
115
+ while (!effects[i].startsWith(prefix)) {
116
+ const dot = prefix.lastIndexOf(".");
117
+ if (dot === -1) return [null, null, effects];
118
+ prefix = prefix.slice(0, dot + 1);
119
+ }
120
+ }
121
+ if (!prefix.endsWith(".")) return [null, null, effects];
122
+ const values = [];
123
+ for (const e of effects) {
124
+ if (!e.startsWith(prefix)) return [null, null, effects];
125
+ values.push(e.slice(prefix.length));
126
+ }
127
+ const parts = prefix.replace(/\.$/, "").split(".");
128
+ const paramName = parts.length > 0 ? parts[parts.length - 1] : "value";
129
+ const template = `${prefix}{${paramName}}`;
130
+ return [template, paramName, values];
131
+ }
132
+ function parseConditionMap(conditionMap) {
133
+ const conditionNames = new Set(Object.keys(conditionMap));
134
+ const affNames = /* @__PURE__ */ new Set();
135
+ for (const info of Object.values(conditionMap)) {
136
+ for (const a of info.producedBy || []) affNames.add(a);
137
+ for (const a of info.requiredBy || []) affNames.add(a);
138
+ }
139
+ const defs = [];
140
+ for (const affName of [...affNames].sort()) {
141
+ const preconditions = [];
142
+ const effects = [];
143
+ for (const [condName, info] of Object.entries(conditionMap)) {
144
+ if ((info.requiredBy || []).includes(affName)) preconditions.push(condName);
145
+ if ((info.producedBy || []).includes(affName)) effects.push(condName);
146
+ }
147
+ preconditions.sort();
148
+ effects.sort();
149
+ const [templateEffect, paramName, validValues] = detectTemplate(effects);
150
+ defs.push(
151
+ createAffordanceDef({
152
+ name: affName,
153
+ preconditions,
154
+ effects,
155
+ templateEffect,
156
+ paramName,
157
+ validValues
158
+ })
159
+ );
160
+ }
161
+ const mutexPrefixes = /* @__PURE__ */ new Set();
162
+ for (const d of defs) {
163
+ if (d.templateEffect) {
164
+ mutexPrefixes.add(d.templateEffect.slice(0, d.templateEffect.indexOf("{")));
165
+ }
166
+ }
167
+ return { defs, conditionNames, mutexPrefixes };
168
+ }
169
+ function buildDefsFromActions(actions) {
170
+ const conditionNames = /* @__PURE__ */ new Set();
171
+ const defs = [];
172
+ for (const action of actions) {
173
+ const preconditions = action.preconditions ?? [];
174
+ const effects = action.effects ?? [];
175
+ for (const c of preconditions) conditionNames.add(c);
176
+ for (const c of effects) conditionNames.add(c);
177
+ const [templateEffect, paramName, validValues] = detectTemplate([...effects]);
178
+ defs.push(
179
+ createAffordanceDef({
180
+ name: action.name,
181
+ preconditions: [...preconditions],
182
+ effects: [...effects],
183
+ templateEffect,
184
+ paramName,
185
+ validValues,
186
+ cost: action.cost ?? 1
187
+ })
188
+ );
189
+ }
190
+ const mutexPrefixes = /* @__PURE__ */ new Set();
191
+ for (const d of defs) {
192
+ if (d.templateEffect) {
193
+ mutexPrefixes.add(d.templateEffect.slice(0, d.templateEffect.indexOf("{")));
194
+ }
195
+ }
196
+ return { defs, conditionNames, mutexPrefixes };
197
+ }
198
+ function availableSteps(state, defs, blockedKeys) {
199
+ const steps = [];
200
+ for (const d of defs) {
201
+ if (!d.preconditions.every((p) => state.has(p))) continue;
202
+ if (d.paramName) {
203
+ for (const v of d.validValues) {
204
+ const step = createPlanStep(d.name, { [d.paramName]: v });
205
+ if (!blockedKeys.has(step.key())) steps.push(step);
206
+ }
207
+ } else {
208
+ const step = createPlanStep(d.name);
209
+ if (!blockedKeys.has(step.key())) steps.push(step);
210
+ }
211
+ }
212
+ return steps;
213
+ }
214
+ function plan(start, goalCondition, defs, mutexPrefixes, opts = {}) {
215
+ const { maxDepth = 5, maxQueueSize = 1e4, blockedSteps = [] } = opts;
216
+ if (start.has(goalCondition)) return [[]];
217
+ const blockedKeys = new Set(blockedSteps.map((s) => s.key()));
218
+ const queue = [{ state: start, path: [] }];
219
+ const visited = /* @__PURE__ */ new Set([stateKey(start)]);
220
+ const solutions = [];
221
+ let solutionDepth = null;
222
+ while (queue.length > 0) {
223
+ if (queue.length > maxQueueSize) return solutions;
224
+ const { state, path } = queue.shift();
225
+ if (solutionDepth !== null && path.length >= solutionDepth) continue;
226
+ for (const step of availableSteps(state, defs, blockedKeys)) {
227
+ const affDef = defs.find((d) => d.name === step.name);
228
+ const newState = applyEffects(state, affDef, step.params, mutexPrefixes);
229
+ const newPath = [...path, step];
230
+ if (newState.has(goalCondition)) {
231
+ solutions.push(newPath);
232
+ solutionDepth = newPath.length;
233
+ continue;
234
+ }
235
+ const key = stateKey(newState);
236
+ if (!visited.has(key) && newPath.length < maxDepth) {
237
+ visited.add(key);
238
+ queue.push({ state: newState, path: newPath });
239
+ }
240
+ }
241
+ }
242
+ return solutions;
243
+ }
244
+ function pathCost(path, defs) {
245
+ return path.reduce((sum, step) => {
246
+ const def = defs.find((d) => d.name === step.name);
247
+ return sum + (def?.cost ?? 1);
248
+ }, 0);
249
+ }
250
+ function pathPreferredCount(path, defs) {
251
+ return path.reduce((count, step) => {
252
+ const def = defs.find((d) => d.name === step.name);
253
+ return count + (def?.preferred ? 1 : 0);
254
+ }, 0);
255
+ }
256
+ function planWithCost(start, goalCondition, defs, mutexPrefixes, opts = {}) {
257
+ const paths = plan(start, goalCondition, defs, mutexPrefixes, opts);
258
+ if (paths.length === 0) {
259
+ return { steps: [], totalCost: 0, alternativeCount: 0 };
260
+ }
261
+ if (paths.length === 1 && paths[0].length === 0) {
262
+ return { steps: [], totalCost: 0, alternativeCount: 1 };
263
+ }
264
+ let bestPath = paths[0];
265
+ let bestCost = pathCost(bestPath, defs);
266
+ let bestPreferred = pathPreferredCount(bestPath, defs);
267
+ for (let i = 1; i < paths.length; i++) {
268
+ const cost = pathCost(paths[i], defs);
269
+ const preferred = pathPreferredCount(paths[i], defs);
270
+ if (cost < bestCost || cost === bestCost && preferred > bestPreferred) {
271
+ bestPath = paths[i];
272
+ bestCost = cost;
273
+ bestPreferred = preferred;
274
+ }
275
+ }
276
+ return {
277
+ steps: bestPath,
278
+ totalCost: bestCost,
279
+ alternativeCount: paths.length
280
+ };
281
+ }
282
+ function planForAction(entity, goalActionName, conditionMap, opts = {}) {
283
+ const { defs, conditionNames, mutexPrefixes } = parseConditionMap(conditionMap);
284
+ const goalDef = defs.find((d) => d.name === goalActionName);
285
+ if (!goalDef) return null;
286
+ let state = inferConditions(entity, conditionNames);
287
+ const unmet = goalDef.preconditions.filter((p) => !state.has(p));
288
+ if (unmet.length === 0) return [createPlanStep(goalActionName)];
289
+ const steps = [];
290
+ const usedKeys = /* @__PURE__ */ new Set();
291
+ for (const precond of unmet) {
292
+ if (state.has(precond)) continue;
293
+ const paths = plan(state, precond, defs, mutexPrefixes, opts);
294
+ if (paths.length === 0) return null;
295
+ for (const step of paths[0]) {
296
+ if (usedKeys.has(step.key())) continue;
297
+ usedKeys.add(step.key());
298
+ steps.push(step);
299
+ const affDef = defs.find((d) => d.name === step.name);
300
+ if (affDef) state = applyEffects(state, affDef, step.params, mutexPrefixes);
301
+ }
302
+ }
303
+ steps.push(createPlanStep(goalActionName));
304
+ return steps;
305
+ }
306
+ function planFromEntity(entity, goalActionName, opts = {}) {
307
+ const actions = entity.actions ?? [];
308
+ if (actions.length === 0) return null;
309
+ const goalAction = actions.find((a) => a.name === goalActionName);
310
+ if (!goalAction) return null;
311
+ const goalPreconditions = goalAction.preconditions ?? [];
312
+ const { defs, conditionNames, mutexPrefixes } = buildDefsFromActions(actions);
313
+ const state = inferConditions(entity, conditionNames);
314
+ const unmet = goalPreconditions.filter((p) => !state.has(p));
315
+ if (unmet.length === 0) {
316
+ return {
317
+ steps: [createPlanStep(goalActionName)],
318
+ totalCost: goalAction.cost ?? 1,
319
+ alternativeCount: 1
320
+ };
321
+ }
322
+ let currentState = state;
323
+ const steps = [];
324
+ const usedKeys = /* @__PURE__ */ new Set();
325
+ let totalCost = 0;
326
+ for (const precond of unmet) {
327
+ if (currentState.has(precond)) continue;
328
+ const result = planWithCost(currentState, precond, defs, mutexPrefixes, opts);
329
+ if (result.steps.length === 0 && !currentState.has(precond)) return null;
330
+ for (const step of result.steps) {
331
+ if (usedKeys.has(step.key())) continue;
332
+ usedKeys.add(step.key());
333
+ steps.push(step);
334
+ const affDef = defs.find((d) => d.name === step.name);
335
+ if (affDef) {
336
+ totalCost += affDef.cost;
337
+ currentState = applyEffects(currentState, affDef, step.params, mutexPrefixes);
338
+ }
339
+ }
340
+ }
341
+ steps.push(createPlanStep(goalActionName));
342
+ totalCost += goalAction.cost ?? 1;
343
+ return { steps, totalCost, alternativeCount: 1 };
344
+ }
345
+ export {
346
+ applyEffects,
347
+ buildDefsFromActions,
348
+ createAffordanceDef,
349
+ createPlanStep,
350
+ inferConditions,
351
+ inferHeuristicMappings,
352
+ parseConditionMap,
353
+ plan,
354
+ planForAction,
355
+ planFromEntity,
356
+ planWithCost
357
+ };
358
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/planner.ts"],"sourcesContent":["/**\n * BFS planner over the affordance graph.\n *\n * Port of siren-grail/packages/siren-agent/planner.js to TypeScript,\n * extended with cost-aware path selection, preferred tiebreaker (GRAIL spec §5.4),\n * and a buildDefsFromActions() path for client-side planning without a condition map.\n *\n * Pure module — no DOM, no fetch, no side effects.\n */\n\nimport type { SirenAction, SirenEntity, ConditionMapEntry } from './types.js';\n\n// ---------- Types ----------\n\nexport interface PlanStep {\n readonly name: string;\n readonly params: Readonly<Record<string, string>>;\n key(): string;\n label(): string;\n}\n\nexport interface AffordanceDef {\n readonly name: string;\n readonly preconditions: readonly string[];\n readonly effects: readonly string[];\n readonly templateEffect: string | null;\n readonly paramName: string | null;\n readonly validValues: readonly string[];\n readonly cost: number;\n readonly preferred: boolean;\n}\n\nexport interface PlanResult {\n /** Ordered steps to reach the goal (empty if already satisfied) */\n steps: PlanStep[];\n /** Total cost of all steps */\n totalCost: number;\n /** Number of alternative equal-length paths found */\n alternativeCount: number;\n}\n\nexport interface PlanOptions {\n maxDepth?: number;\n maxQueueSize?: number;\n blockedSteps?: PlanStep[];\n}\n\ninterface HeuristicMapping {\n property: string;\n op: string;\n value: string | number;\n}\n\n// ---------- Factories ----------\n\nexport function createPlanStep(name: string, params: Record<string, string> = {}): PlanStep {\n const frozenParams = Object.freeze({ ...params });\n return Object.freeze({\n name,\n params: frozenParams,\n key() {\n const entries = Object.entries(frozenParams).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));\n if (entries.length === 0) return name;\n return `${name}(${entries.map(([k, v]) => `${k}=${v}`).join(',')})`;\n },\n label() {\n const entries = Object.entries(frozenParams);\n if (entries.length === 0) return name;\n const p = entries.map(([k, v]) => `${k}=${v}`).join(', ');\n return `${name}(${p})`;\n },\n });\n}\n\nexport function createAffordanceDef(opts: {\n name: string;\n preconditions: string[];\n effects: string[];\n templateEffect?: string | null;\n paramName?: string | null;\n validValues?: string[];\n cost?: number;\n preferred?: boolean;\n}): AffordanceDef {\n return Object.freeze({\n name: opts.name,\n preconditions: Object.freeze([...opts.preconditions]),\n effects: Object.freeze([...opts.effects]),\n templateEffect: opts.templateEffect ?? null,\n paramName: opts.paramName ?? null,\n validValues: Object.freeze([...(opts.validValues ?? [])]),\n cost: opts.cost ?? 1,\n preferred: opts.preferred ?? false,\n });\n}\n\n// ---------- State helpers ----------\n\nfunction stateKey(state: Set<string>): string {\n return [...state].sort().join(',');\n}\n\n/**\n * Forward simulation: apply affordance effects, return new state.\n * Handles template effect resolution and mutex prefix clearing.\n */\nexport function applyEffects(\n state: Set<string>,\n affDef: AffordanceDef,\n params: Record<string, string>,\n mutexPrefixes: Set<string>\n): Set<string> {\n const next = new Set(state);\n\n if (affDef.templateEffect && params && Object.keys(params).length > 0) {\n let resolved = affDef.templateEffect;\n for (const [k, v] of Object.entries(params)) {\n resolved = resolved.replace(`{${k}}`, v);\n }\n const prefix = affDef.templateEffect.slice(0, affDef.templateEffect.indexOf('{'));\n for (const c of next) {\n if (c.startsWith(prefix)) next.delete(c);\n }\n next.add(resolved);\n } else {\n for (const effect of affDef.effects) {\n for (const prefix of mutexPrefixes) {\n if (effect.startsWith(prefix)) {\n for (const c of next) {\n if (c.startsWith(prefix)) next.delete(c);\n }\n break;\n }\n }\n next.add(effect);\n }\n }\n\n return next;\n}\n\n// ---------- Condition inference ----------\n\n/**\n * Infer heuristic condition mappings from condition name structure.\n *\n * - \"x.y.z\" (3 segments): property=y, op=eq, value=z\n * - \"x.y.z\" where z=\"nonempty\": property=yCount (singular), op=gt, value=0\n * - \"x.y\" (2 segments): derived from action-presence\n */\nexport function inferHeuristicMappings(\n conditionNames: Set<string>\n): Record<string, HeuristicMapping> {\n const mappings: Record<string, HeuristicMapping> = {};\n for (const name of conditionNames) {\n const parts = name.split('.');\n if (parts.length === 3) {\n const [, prop, value] = parts;\n if (value === 'nonempty') {\n const singular = prop.endsWith('s') ? prop.slice(0, -1) : prop;\n mappings[name] = { property: `${singular}Count`, op: 'gt', value: 0 };\n } else {\n mappings[name] = { property: prop, op: 'eq', value };\n }\n } else if (parts.length === 2) {\n mappings[name] = { property: '_derived', op: 'action-presence', value: '' };\n }\n }\n return mappings;\n}\n\n/**\n * Infer active conditions from a Siren entity.\n * Prefers server-provided x-conditions when available.\n */\nexport function inferConditions(\n entity: { properties?: Record<string, unknown>; actions?: unknown[]; 'x-conditions'?: string[] },\n conditionNames: Set<string>,\n mappings?: Record<string, HeuristicMapping>\n): Set<string> {\n const xc = entity['x-conditions'];\n if (Array.isArray(xc)) {\n return new Set(xc);\n }\n\n if (!mappings) {\n mappings = inferHeuristicMappings(conditionNames);\n }\n\n const props = entity.properties || {};\n const actions = entity.actions || [];\n const conds = new Set<string>();\n\n // First pass: non-derived\n for (const name of conditionNames) {\n const m = mappings[name];\n if (!m || m.property === '_derived') continue;\n const val = props[m.property];\n if (val === undefined || val === null) continue;\n if (m.op === 'eq' && String(val) === String(m.value)) conds.add(name);\n else if (m.op === 'gt') {\n try {\n if (Number(val) > Number(m.value)) conds.add(name);\n } catch {\n /* skip */\n }\n }\n }\n\n // Second pass: derived (action-presence)\n for (const name of conditionNames) {\n const m = mappings[name];\n if (!m || m.property !== '_derived') continue;\n if (actions.length > 0) conds.add(name);\n }\n\n return conds;\n}\n\n// ---------- Template detection ----------\n\n/**\n * Detect parameterized template in a list of effect condition names.\n * Returns [template, paramName, validValues] or [null, null, effects].\n */\nfunction detectTemplate(effects: string[]): [string | null, string | null, string[]] {\n if (effects.length < 2) return [null, null, effects];\n\n let prefix = effects[0];\n for (let i = 1; i < effects.length; i++) {\n while (!effects[i].startsWith(prefix)) {\n const dot = prefix.lastIndexOf('.');\n if (dot === -1) return [null, null, effects];\n prefix = prefix.slice(0, dot + 1);\n }\n }\n\n if (!prefix.endsWith('.')) return [null, null, effects];\n\n const values: string[] = [];\n for (const e of effects) {\n if (!e.startsWith(prefix)) return [null, null, effects];\n values.push(e.slice(prefix.length));\n }\n\n const parts = prefix.replace(/\\.$/, '').split('.');\n const paramName = parts.length > 0 ? parts[parts.length - 1] : 'value';\n const template = `${prefix}{${paramName}}`;\n\n return [template, paramName, values];\n}\n\n// ---------- Condition map parsing ----------\n\n/**\n * Parse a condition map (from /conditions endpoint) into AffordanceDef objects.\n * Input shape: { conditionName: { description, producedBy: string[], requiredBy: string[] } }\n */\nexport function parseConditionMap(conditionMap: Record<string, ConditionMapEntry>): {\n defs: AffordanceDef[];\n conditionNames: Set<string>;\n mutexPrefixes: Set<string>;\n} {\n const conditionNames = new Set(Object.keys(conditionMap));\n\n const affNames = new Set<string>();\n for (const info of Object.values(conditionMap)) {\n for (const a of info.producedBy || []) affNames.add(a);\n for (const a of info.requiredBy || []) affNames.add(a);\n }\n\n const defs: AffordanceDef[] = [];\n for (const affName of [...affNames].sort()) {\n const preconditions: string[] = [];\n const effects: string[] = [];\n\n for (const [condName, info] of Object.entries(conditionMap)) {\n if ((info.requiredBy || []).includes(affName)) preconditions.push(condName);\n if ((info.producedBy || []).includes(affName)) effects.push(condName);\n }\n\n preconditions.sort();\n effects.sort();\n\n const [templateEffect, paramName, validValues] = detectTemplate(effects);\n\n defs.push(\n createAffordanceDef({\n name: affName,\n preconditions,\n effects,\n templateEffect,\n paramName,\n validValues,\n })\n );\n }\n\n const mutexPrefixes = new Set<string>();\n for (const d of defs) {\n if (d.templateEffect) {\n mutexPrefixes.add(d.templateEffect.slice(0, d.templateEffect.indexOf('{')));\n }\n }\n\n return { defs, conditionNames, mutexPrefixes };\n}\n\n// ---------- Build defs from entity actions (simple path) ----------\n\n/**\n * Build AffordanceDefs directly from SirenAction[].\n * This is the \"simple path\" — no condition map or server endpoint required.\n * Reads preconditions, effects, and cost from each action.\n */\nexport function buildDefsFromActions(actions: SirenAction[]): {\n defs: AffordanceDef[];\n conditionNames: Set<string>;\n mutexPrefixes: Set<string>;\n} {\n const conditionNames = new Set<string>();\n const defs: AffordanceDef[] = [];\n\n for (const action of actions) {\n const preconditions = action.preconditions ?? [];\n const effects = action.effects ?? [];\n\n for (const c of preconditions) conditionNames.add(c);\n for (const c of effects) conditionNames.add(c);\n\n const [templateEffect, paramName, validValues] = detectTemplate([...effects]);\n\n defs.push(\n createAffordanceDef({\n name: action.name,\n preconditions: [...preconditions],\n effects: [...effects],\n templateEffect,\n paramName,\n validValues,\n cost: action.cost ?? 1,\n })\n );\n }\n\n const mutexPrefixes = new Set<string>();\n for (const d of defs) {\n if (d.templateEffect) {\n mutexPrefixes.add(d.templateEffect.slice(0, d.templateEffect.indexOf('{')));\n }\n }\n\n return { defs, conditionNames, mutexPrefixes };\n}\n\n// ---------- BFS planner ----------\n\nfunction availableSteps(\n state: Set<string>,\n defs: AffordanceDef[],\n blockedKeys: Set<string>\n): PlanStep[] {\n const steps: PlanStep[] = [];\n for (const d of defs) {\n if (!d.preconditions.every(p => state.has(p))) continue;\n if (d.paramName) {\n for (const v of d.validValues) {\n const step = createPlanStep(d.name, { [d.paramName]: v });\n if (!blockedKeys.has(step.key())) steps.push(step);\n }\n } else {\n const step = createPlanStep(d.name);\n if (!blockedKeys.has(step.key())) steps.push(step);\n }\n }\n return steps;\n}\n\n/**\n * BFS: find ALL shortest action sequences from start state to goal condition.\n */\nexport function plan(\n start: Set<string>,\n goalCondition: string,\n defs: AffordanceDef[],\n mutexPrefixes: Set<string>,\n opts: PlanOptions = {}\n): PlanStep[][] {\n const { maxDepth = 5, maxQueueSize = 10000, blockedSteps = [] } = opts;\n\n if (start.has(goalCondition)) return [[]];\n\n const blockedKeys = new Set(blockedSteps.map(s => s.key()));\n const queue: Array<{ state: Set<string>; path: PlanStep[] }> = [{ state: start, path: [] }];\n const visited = new Set([stateKey(start)]);\n const solutions: PlanStep[][] = [];\n let solutionDepth: number | null = null;\n\n while (queue.length > 0) {\n if (queue.length > maxQueueSize) return solutions;\n\n const { state, path } = queue.shift()!;\n if (solutionDepth !== null && path.length >= solutionDepth) continue;\n\n for (const step of availableSteps(state, defs, blockedKeys)) {\n const affDef = defs.find(d => d.name === step.name)!;\n const newState = applyEffects(state, affDef, step.params, mutexPrefixes);\n const newPath = [...path, step];\n\n if (newState.has(goalCondition)) {\n solutions.push(newPath);\n solutionDepth = newPath.length;\n continue;\n }\n\n const key = stateKey(newState);\n if (!visited.has(key) && newPath.length < maxDepth) {\n visited.add(key);\n queue.push({ state: newState, path: newPath });\n }\n }\n }\n\n return solutions;\n}\n\n// ---------- Cost-aware planning ----------\n\nfunction pathCost(path: PlanStep[], defs: AffordanceDef[]): number {\n return path.reduce((sum, step) => {\n const def = defs.find(d => d.name === step.name);\n return sum + (def?.cost ?? 1);\n }, 0);\n}\n\nfunction pathPreferredCount(path: PlanStep[], defs: AffordanceDef[]): number {\n return path.reduce((count, step) => {\n const def = defs.find(d => d.name === step.name);\n return count + (def?.preferred ? 1 : 0);\n }, 0);\n}\n\n/**\n * Find the lowest-cost shortest path.\n * Among equal-cost paths, prefer ones with more preferred steps (spec §5.4).\n */\nexport function planWithCost(\n start: Set<string>,\n goalCondition: string,\n defs: AffordanceDef[],\n mutexPrefixes: Set<string>,\n opts: PlanOptions = {}\n): PlanResult {\n const paths = plan(start, goalCondition, defs, mutexPrefixes, opts);\n\n if (paths.length === 0) {\n return { steps: [], totalCost: 0, alternativeCount: 0 };\n }\n\n // Already-satisfied case: plan() returns [[]]\n if (paths.length === 1 && paths[0].length === 0) {\n return { steps: [], totalCost: 0, alternativeCount: 1 };\n }\n\n let bestPath = paths[0];\n let bestCost = pathCost(bestPath, defs);\n let bestPreferred = pathPreferredCount(bestPath, defs);\n\n for (let i = 1; i < paths.length; i++) {\n const cost = pathCost(paths[i], defs);\n const preferred = pathPreferredCount(paths[i], defs);\n if (cost < bestCost || (cost === bestCost && preferred > bestPreferred)) {\n bestPath = paths[i];\n bestCost = cost;\n bestPreferred = preferred;\n }\n }\n\n return {\n steps: bestPath,\n totalCost: bestCost,\n alternativeCount: paths.length,\n };\n}\n\n// ---------- Action-level planning ----------\n\n/**\n * Plan prerequisite chain for a goal action using a condition map.\n * Uses BFS for each unmet precondition with forward state simulation.\n */\nexport function planForAction(\n entity: { properties?: Record<string, unknown>; actions?: unknown[]; 'x-conditions'?: string[] },\n goalActionName: string,\n conditionMap: Record<string, ConditionMapEntry>,\n opts: PlanOptions = {}\n): PlanStep[] | null {\n const { defs, conditionNames, mutexPrefixes } = parseConditionMap(conditionMap);\n const goalDef = defs.find(d => d.name === goalActionName);\n if (!goalDef) return null;\n\n let state = inferConditions(entity, conditionNames);\n\n const unmet = goalDef.preconditions.filter(p => !state.has(p));\n if (unmet.length === 0) return [createPlanStep(goalActionName)];\n\n const steps: PlanStep[] = [];\n const usedKeys = new Set<string>();\n\n for (const precond of unmet) {\n if (state.has(precond)) continue;\n const paths = plan(state, precond, defs, mutexPrefixes, opts);\n if (paths.length === 0) return null;\n for (const step of paths[0]) {\n if (usedKeys.has(step.key())) continue;\n usedKeys.add(step.key());\n steps.push(step);\n const affDef = defs.find(d => d.name === step.name);\n if (affDef) state = applyEffects(state, affDef, step.params, mutexPrefixes);\n }\n }\n\n steps.push(createPlanStep(goalActionName));\n return steps;\n}\n\n// ---------- Entity-level planning (simple path) ----------\n\n/**\n * Plan from entity actions directly — no condition map required.\n * Builds AffordanceDefs from the entity's actions, infers current state,\n * and runs cost-aware BFS for the goal action's unmet preconditions.\n */\nexport function planFromEntity(\n entity: SirenEntity & { 'x-conditions'?: string[] },\n goalActionName: string,\n opts: PlanOptions = {}\n): PlanResult | null {\n const actions = entity.actions ?? [];\n if (actions.length === 0) return null;\n\n const goalAction = actions.find(a => a.name === goalActionName);\n if (!goalAction) return null;\n\n const goalPreconditions = goalAction.preconditions ?? [];\n\n const { defs, conditionNames, mutexPrefixes } = buildDefsFromActions(actions);\n\n const state = inferConditions(entity, conditionNames);\n\n const unmet = goalPreconditions.filter(p => !state.has(p));\n if (unmet.length === 0) {\n return {\n steps: [createPlanStep(goalActionName)],\n totalCost: goalAction.cost ?? 1,\n alternativeCount: 1,\n };\n }\n\n // Plan each unmet precondition with cost-aware selection, forward-simulating state\n let currentState = state;\n const steps: PlanStep[] = [];\n const usedKeys = new Set<string>();\n let totalCost = 0;\n\n for (const precond of unmet) {\n if (currentState.has(precond)) continue;\n const result = planWithCost(currentState, precond, defs, mutexPrefixes, opts);\n if (result.steps.length === 0 && !currentState.has(precond)) return null;\n for (const step of result.steps) {\n if (usedKeys.has(step.key())) continue;\n usedKeys.add(step.key());\n steps.push(step);\n const affDef = defs.find(d => d.name === step.name);\n if (affDef) {\n totalCost += affDef.cost;\n currentState = applyEffects(currentState, affDef, step.params, mutexPrefixes);\n }\n }\n }\n\n steps.push(createPlanStep(goalActionName));\n totalCost += goalAction.cost ?? 1;\n\n return { steps, totalCost, alternativeCount: 1 };\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@lokascript/planner",
3
+ "version": "2.4.0",
4
+ "description": "BFS planner over affordance graphs — cost-aware planning with preconditions, effects, and template parameters",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:check": "vitest run --reporter=dot 2>&1 | tail -5",
22
+ "typecheck": "tsc --noEmit"
23
+ },
24
+ "author": "LokaScript Contributors",
25
+ "license": "MIT",
26
+ "devDependencies": {
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.0.0",
29
+ "vitest": "^4.1.5"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/codetalcott/hyperfixi.git",
34
+ "directory": "packages/planner"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/codetalcott/hyperfixi/issues"
38
+ },
39
+ "engines": {
40
+ "node": ">=18.0.0"
41
+ },
42
+ "sideEffects": false,
43
+ "files": [
44
+ "dist",
45
+ "src"
46
+ ],
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }