@putervision/behavior-mcp 0.1.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/CONTRIBUTING.md +7 -0
- package/LICENSE +21 -0
- package/PROJECT_INSTRUCTIONS_TEMPLATE.md +9 -0
- package/README.md +94 -0
- package/SECURITY.md +11 -0
- package/dist/chunk-FXYS43D6.js +291 -0
- package/dist/chunk-FXYS43D6.js.map +1 -0
- package/dist/chunk-L2LJRSDU.js +608 -0
- package/dist/chunk-L2LJRSDU.js.map +1 -0
- package/dist/chunk-MXYFWAJG.js +1176 -0
- package/dist/chunk-MXYFWAJG.js.map +1 -0
- package/dist/chunk-NCJFRFRK.js +399 -0
- package/dist/chunk-NCJFRFRK.js.map +1 -0
- package/dist/chunk-QU5PNQP4.js +266 -0
- package/dist/chunk-QU5PNQP4.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +169 -0
- package/dist/cli.js.map +1 -0
- package/dist/db-ZOU6XEDM.js +35 -0
- package/dist/db-ZOU6XEDM.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/init-QMBHF4FZ.js +15 -0
- package/dist/init-QMBHF4FZ.js.map +1 -0
- package/dist/lib.d.ts +672 -0
- package/dist/lib.js +443 -0
- package/dist/lib.js.map +1 -0
- package/glama.json +44 -0
- package/manifest.json +26 -0
- package/package.json +133 -0
- package/server.json +42 -0
package/CONTRIBUTING.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PuterVision
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Agent Instructions for behavior-mcp
|
|
2
|
+
|
|
3
|
+
This project uses `@putervision/behavior-mcp` for high-frequency in-browser behavior execution.
|
|
4
|
+
|
|
5
|
+
## Mandatory Workflow
|
|
6
|
+
1. **Load**: Activate behavior trees via `load_behavior(load)`.
|
|
7
|
+
2. **Monitor**: Check execution status with `get_status(current)`.
|
|
8
|
+
3. **Triggers**: Register reactive emergency interrupts via `register_trigger(register)`.
|
|
9
|
+
4. **Safety**: Use `abort_behavior(abort)` to halt execution on anomalies.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @putervision/behavior-mcp
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@putervision/behavior-mcp)
|
|
4
|
+
[](https://github.com/putervision/behavior-mcp/actions/workflows/ci.yml)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
> **High-Frequency (~60Hz) In-Browser Behavior Tree Tactical Runtime Engine for AI Agents**
|
|
9
|
+
|
|
10
|
+
`@putervision/behavior-mcp` is a Model Context Protocol (MCP) server that executes deterministic behavior trees directly in browser runtimes at ~60Hz with reactive trigger preemption, frame recordings, SHA-256 Merkle audit chains, and a 5-layer safety guardrail stack.
|
|
11
|
+
|
|
12
|
+
🌐 **Official Documentation**: [putervision.com](https://putervision.com) • [Interactive Web Docs](docs/index.html)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## ⚡ 15-Second Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 1. Initialize runtime & seed default behavior trees in your workspace
|
|
20
|
+
npx @putervision/behavior-mcp init
|
|
21
|
+
|
|
22
|
+
# 2. Run system diagnostic and health checks
|
|
23
|
+
npx @putervision/behavior-mcp doctor
|
|
24
|
+
|
|
25
|
+
# 3. Inspect active executions, blackboard state, and reactive triggers
|
|
26
|
+
npx @putervision/behavior-mcp inspect
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🛠️ 10 Core MCP Tools
|
|
32
|
+
|
|
33
|
+
| Tool | Actions | Purpose |
|
|
34
|
+
|------|---------|---------|
|
|
35
|
+
| `load_behavior` | `load`, `unload`, `swap` | Inject, initialize, or hot-swap a behavior tree in browser runtime |
|
|
36
|
+
| `set_parameters` | `set`, `get`, `reset` | Dynamically update or query runtime execution parameters |
|
|
37
|
+
| `get_status` | `current`, `history`, `tree_state` | Query active status, node traversal path, tick count, duration |
|
|
38
|
+
| `abort_behavior` | `abort`, `pause`, `resume` | Halt, pause, or resume behavior execution safely |
|
|
39
|
+
| `register_trigger` | `register`, `list` | Configure reactive interrupt triggers with priority preemption |
|
|
40
|
+
| `replay_recording` | `capture`, `list` | Capture or inspect deterministic browser action sequences |
|
|
41
|
+
| `get_metrics` | `current`, `history` | Retrieve runtime telemetry, tick durations, and stuck events |
|
|
42
|
+
| `manage_behaviors` | `register`, `get`, `list` | CRUD for immutable behavior tree JSON with SHA-256 tree hashes |
|
|
43
|
+
| `manage_blackboard` | `get`, `set` | Read, write, or query shared behavior tree blackboard variables |
|
|
44
|
+
| `manage_runtime_db` | `stats`, `audit`, `snapshot`, `restore` | SQLite diagnostics, SHA-256 Merkle audit, and checkpoint rollback |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🛡️ 5-Layer Safety Guardrail Stack
|
|
49
|
+
|
|
50
|
+
1. **Fail-Closed Evaluator**: Unrecognized node definitions throw fatal exceptions immediately.
|
|
51
|
+
2. **Watchdog Heartbeat Timer**: Halts execution if tick evaluation stalls beyond 5,000ms.
|
|
52
|
+
3. **Action Rate Limiter**: Strict 60 actions/sec maximum throughput ceiling.
|
|
53
|
+
4. **Leaf Policy Gate**: Blocks irreversible, high-risk mutations (`delete_item`, `spend_currency`).
|
|
54
|
+
5. **Emergency Kill Switch**: Atomic safety latch (`EmergencySafety.engageKillSwitch()`) halts all runtimes.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 📚 Deep Documentation Guides
|
|
59
|
+
|
|
60
|
+
- 📖 **[Formal API Reference](docs/api-reference.md)**: Full parameter tables, type definitions, and tool schemas.
|
|
61
|
+
- 💡 **[Core Architecture & Concepts](docs/concepts.md)**: Behavior tree execution model, ~60Hz loop, and trigger lifecycle.
|
|
62
|
+
- 🖥️ **[CLI Usage Guide](docs/cli-usage.md)**: Complete CLI command reference (`init`, `doctor`, `inspect`, `run`).
|
|
63
|
+
- 💾 **[Database Schema](docs/database-schema.md)**: SQLite table structures, indexes, and Merkle audit ledger.
|
|
64
|
+
- ⚙️ **[Configuration Reference](docs/configuration.md)**: `.behavior-mcp.json` parameters and environment variables.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🔗 Client Configuration
|
|
69
|
+
|
|
70
|
+
Add to `.cursor/mcp.json` or `.vscode/mcp.json`:
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"behavior-mcp": {
|
|
75
|
+
"command": "behavior-mcp",
|
|
76
|
+
"args": ["run"]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 🧪 Testing
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Run full unit and integration test suite across 15 test files (87 tests)
|
|
88
|
+
npm test
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 📄 License
|
|
94
|
+
MIT © PuterVision
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 0.1.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability, please report it via private security advisory on GitHub or email security@putervision.org. Please do not open public issues for security vulnerabilities.
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BehaviorRegistry,
|
|
3
|
+
generateId,
|
|
4
|
+
getCurrentIsoString,
|
|
5
|
+
logRuntimeEvent,
|
|
6
|
+
safeJsonParse,
|
|
7
|
+
safeJsonStringify
|
|
8
|
+
} from "./chunk-QU5PNQP4.js";
|
|
9
|
+
import {
|
|
10
|
+
NotFoundError,
|
|
11
|
+
ValidationError
|
|
12
|
+
} from "./chunk-L2LJRSDU.js";
|
|
13
|
+
|
|
14
|
+
// src/engine/executor.ts
|
|
15
|
+
var ExecutionEngine = class {
|
|
16
|
+
static startExecution(db, params) {
|
|
17
|
+
const behavior = BehaviorRegistry.getBehavior(db, {
|
|
18
|
+
project: params.project,
|
|
19
|
+
name: params.behavior_name,
|
|
20
|
+
version: params.behavior_version
|
|
21
|
+
});
|
|
22
|
+
const id = generateId();
|
|
23
|
+
const now = getCurrentIsoString();
|
|
24
|
+
db.prepare(
|
|
25
|
+
`
|
|
26
|
+
INSERT INTO execution_state (
|
|
27
|
+
id, project, behavior_name, behavior_version, session_id, intention_id,
|
|
28
|
+
status, active_node_path, blackboard_json, current_tick, tick_rate_hz,
|
|
29
|
+
duration_ms, stuck_score, created_at, updated_at
|
|
30
|
+
) VALUES (?, ?, ?, ?, ?, ?, 'running', 'root', ?, 0, 60, 0, 0.0, ?, ?)
|
|
31
|
+
`
|
|
32
|
+
).run(
|
|
33
|
+
id,
|
|
34
|
+
params.project,
|
|
35
|
+
behavior.name,
|
|
36
|
+
behavior.version,
|
|
37
|
+
params.session_id ?? null,
|
|
38
|
+
params.intention_id ?? null,
|
|
39
|
+
safeJsonStringify(params.parameters || {}),
|
|
40
|
+
now,
|
|
41
|
+
now
|
|
42
|
+
);
|
|
43
|
+
logRuntimeEvent(db, {
|
|
44
|
+
project: params.project,
|
|
45
|
+
entity_id: id,
|
|
46
|
+
entity_type: "execution",
|
|
47
|
+
action: "start",
|
|
48
|
+
details: {
|
|
49
|
+
behavior_name: behavior.name,
|
|
50
|
+
intention_id: params.intention_id,
|
|
51
|
+
trace_id: params.trace_id
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
project: params.project,
|
|
57
|
+
behavior_name: behavior.name,
|
|
58
|
+
behavior_version: behavior.version,
|
|
59
|
+
session_id: params.session_id,
|
|
60
|
+
intention_id: params.intention_id,
|
|
61
|
+
trace_id: params.trace_id,
|
|
62
|
+
status: "running",
|
|
63
|
+
active_node_path: "root",
|
|
64
|
+
blackboard_json: safeJsonStringify(params.parameters || {}),
|
|
65
|
+
current_tick: 0,
|
|
66
|
+
tick_rate_hz: 60,
|
|
67
|
+
duration_ms: 0,
|
|
68
|
+
stuck_score: 0,
|
|
69
|
+
created_at: now,
|
|
70
|
+
updated_at: now
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
static tickExecution(db, params) {
|
|
74
|
+
const current = this.getExecution(db, { project: params.project, id: params.execution_id });
|
|
75
|
+
const now = getCurrentIsoString();
|
|
76
|
+
const newTick = current.current_tick + 1;
|
|
77
|
+
const durationMs = Math.round(newTick * (1e3 / current.tick_rate_hz));
|
|
78
|
+
const status = params.status || current.status;
|
|
79
|
+
const nodePath = params.active_node_path || current.active_node_path;
|
|
80
|
+
const stuckScore = params.stuck_score !== void 0 ? params.stuck_score : current.stuck_score;
|
|
81
|
+
const bbJson = params.blackboard ? safeJsonStringify(params.blackboard) : current.blackboard_json;
|
|
82
|
+
db.prepare(
|
|
83
|
+
`
|
|
84
|
+
UPDATE execution_state SET
|
|
85
|
+
current_tick = ?, duration_ms = ?, status = ?, active_node_path = ?,
|
|
86
|
+
stuck_score = ?, blackboard_json = ?, updated_at = ?
|
|
87
|
+
WHERE id = ?
|
|
88
|
+
`
|
|
89
|
+
).run(newTick, durationMs, status, nodePath, stuckScore, bbJson, now, current.id);
|
|
90
|
+
return {
|
|
91
|
+
...current,
|
|
92
|
+
current_tick: newTick,
|
|
93
|
+
duration_ms: durationMs,
|
|
94
|
+
status,
|
|
95
|
+
active_node_path: nodePath,
|
|
96
|
+
stuck_score: stuckScore,
|
|
97
|
+
blackboard_json: bbJson,
|
|
98
|
+
updated_at: now
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
static stopExecution(db, params) {
|
|
102
|
+
const current = this.getExecution(db, { project: params.project, id: params.execution_id });
|
|
103
|
+
const now = getCurrentIsoString();
|
|
104
|
+
db.prepare(
|
|
105
|
+
`
|
|
106
|
+
UPDATE execution_state SET
|
|
107
|
+
status = ?, error = ?, updated_at = ?
|
|
108
|
+
WHERE id = ?
|
|
109
|
+
`
|
|
110
|
+
).run(params.status, params.error_message ?? null, now, current.id);
|
|
111
|
+
logRuntimeEvent(db, {
|
|
112
|
+
project: params.project,
|
|
113
|
+
entity_id: current.id,
|
|
114
|
+
entity_type: "execution",
|
|
115
|
+
action: "stop",
|
|
116
|
+
details: { status: params.status, error: params.error_message }
|
|
117
|
+
});
|
|
118
|
+
return {
|
|
119
|
+
intention_id: current.intention_id || "manual",
|
|
120
|
+
execution_id: current.id,
|
|
121
|
+
session_id: current.session_id || "default",
|
|
122
|
+
status: params.status,
|
|
123
|
+
active_node: current.active_node_path,
|
|
124
|
+
tick_count: current.current_tick,
|
|
125
|
+
duration_ms: current.duration_ms,
|
|
126
|
+
error_message: params.error_message,
|
|
127
|
+
completed_at: now
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
static getExecution(db, params) {
|
|
131
|
+
const row = db.prepare("SELECT * FROM execution_state WHERE project = ? AND id = ?").get(params.project, params.id);
|
|
132
|
+
if (!row) throw new NotFoundError(`Execution instance "${params.id}" not found.`);
|
|
133
|
+
return this.mapRowToExecution(row);
|
|
134
|
+
}
|
|
135
|
+
static listExecutions(db, params) {
|
|
136
|
+
let sql = "SELECT * FROM execution_state WHERE project = ?";
|
|
137
|
+
const sqlParams = [params.project];
|
|
138
|
+
if (params.status) {
|
|
139
|
+
sql += " AND status = ?";
|
|
140
|
+
sqlParams.push(params.status);
|
|
141
|
+
}
|
|
142
|
+
sql += " ORDER BY created_at DESC LIMIT ?";
|
|
143
|
+
sqlParams.push(params.limit || 50);
|
|
144
|
+
const rows = db.prepare(sql).all(...sqlParams);
|
|
145
|
+
return rows.map((r) => this.mapRowToExecution(r));
|
|
146
|
+
}
|
|
147
|
+
static mapRowToExecution(row) {
|
|
148
|
+
return {
|
|
149
|
+
id: row.id,
|
|
150
|
+
project: row.project,
|
|
151
|
+
behavior_name: row.behavior_name,
|
|
152
|
+
behavior_version: row.behavior_version,
|
|
153
|
+
session_id: row.session_id,
|
|
154
|
+
intention_id: row.intention_id,
|
|
155
|
+
status: row.status,
|
|
156
|
+
active_node_path: row.active_node_path,
|
|
157
|
+
blackboard_json: row.blackboard_json || "{}",
|
|
158
|
+
current_tick: row.current_tick,
|
|
159
|
+
tick_rate_hz: row.tick_rate_hz,
|
|
160
|
+
duration_ms: row.duration_ms,
|
|
161
|
+
stuck_score: row.stuck_score,
|
|
162
|
+
error: row.error,
|
|
163
|
+
metadata: safeJsonParse(row.metadata_json, void 0),
|
|
164
|
+
created_at: row.created_at,
|
|
165
|
+
updated_at: row.updated_at
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/engine/triggers.ts
|
|
171
|
+
var TriggerRegistry = class {
|
|
172
|
+
static registerTrigger(db, params) {
|
|
173
|
+
if (!params.name || !params.behavior_name || !params.condition_type) {
|
|
174
|
+
throw new ValidationError("Trigger name, behavior_name, and condition_type are required.");
|
|
175
|
+
}
|
|
176
|
+
const id = generateId();
|
|
177
|
+
const now = getCurrentIsoString();
|
|
178
|
+
const priority = params.priority !== void 0 ? params.priority : 0.5;
|
|
179
|
+
const cooldown_ms = params.cooldown_ms !== void 0 ? params.cooldown_ms : 1e3;
|
|
180
|
+
db.prepare(
|
|
181
|
+
`
|
|
182
|
+
INSERT INTO triggers (
|
|
183
|
+
id, project, name, behavior_name, condition_type, condition_params_json,
|
|
184
|
+
priority, cooldown_ms, is_enabled, created_at, updated_at
|
|
185
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)
|
|
186
|
+
ON CONFLICT(project, name) DO UPDATE SET
|
|
187
|
+
behavior_name = excluded.behavior_name,
|
|
188
|
+
condition_type = excluded.condition_type,
|
|
189
|
+
condition_params_json = excluded.condition_params_json,
|
|
190
|
+
priority = excluded.priority,
|
|
191
|
+
cooldown_ms = excluded.cooldown_ms,
|
|
192
|
+
updated_at = excluded.updated_at
|
|
193
|
+
`
|
|
194
|
+
).run(
|
|
195
|
+
id,
|
|
196
|
+
params.project,
|
|
197
|
+
params.name,
|
|
198
|
+
params.behavior_name,
|
|
199
|
+
params.condition_type,
|
|
200
|
+
safeJsonStringify(params.condition_params || {}),
|
|
201
|
+
priority,
|
|
202
|
+
cooldown_ms,
|
|
203
|
+
now,
|
|
204
|
+
now
|
|
205
|
+
);
|
|
206
|
+
logRuntimeEvent(db, {
|
|
207
|
+
project: params.project,
|
|
208
|
+
entity_id: id,
|
|
209
|
+
entity_type: "trigger",
|
|
210
|
+
action: "register",
|
|
211
|
+
details: { name: params.name, behavior_name: params.behavior_name, priority }
|
|
212
|
+
});
|
|
213
|
+
return {
|
|
214
|
+
id,
|
|
215
|
+
project: params.project,
|
|
216
|
+
name: params.name,
|
|
217
|
+
behavior_name: params.behavior_name,
|
|
218
|
+
condition_type: params.condition_type,
|
|
219
|
+
condition_params: params.condition_params || {},
|
|
220
|
+
priority,
|
|
221
|
+
cooldown_ms,
|
|
222
|
+
is_enabled: true,
|
|
223
|
+
created_at: now,
|
|
224
|
+
updated_at: now
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
static listTriggers(db, project) {
|
|
228
|
+
const rows = db.prepare("SELECT * FROM triggers WHERE project = ? ORDER BY priority DESC, name ASC").all(project);
|
|
229
|
+
return rows.map((r) => this.mapRowToTrigger(r));
|
|
230
|
+
}
|
|
231
|
+
static evaluateTriggers(db, params) {
|
|
232
|
+
const triggers = this.listTriggers(db, params.project).filter((t) => t.is_enabled);
|
|
233
|
+
const now = Date.now();
|
|
234
|
+
for (const trig of triggers) {
|
|
235
|
+
if (trig.last_fired_at && now - new Date(trig.last_fired_at).getTime() < trig.cooldown_ms) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
let matches = false;
|
|
239
|
+
if (trig.condition_type === "hp_threshold") {
|
|
240
|
+
const hp = params.telemetry.hp ?? 100;
|
|
241
|
+
const threshold = trig.condition_params.threshold ?? 30;
|
|
242
|
+
matches = hp <= threshold;
|
|
243
|
+
} else if (trig.condition_type === "enemy_proximity") {
|
|
244
|
+
const enemyDist = params.telemetry.enemy_distance ?? 999;
|
|
245
|
+
const radius = trig.condition_params.radius ?? 10;
|
|
246
|
+
matches = enemyDist <= radius;
|
|
247
|
+
}
|
|
248
|
+
if (matches) {
|
|
249
|
+
db.prepare("UPDATE triggers SET last_fired_at = ?, updated_at = ? WHERE id = ?").run(
|
|
250
|
+
new Date(now).toISOString(),
|
|
251
|
+
new Date(now).toISOString(),
|
|
252
|
+
trig.id
|
|
253
|
+
);
|
|
254
|
+
return trig;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
static mapRowToTrigger(row) {
|
|
260
|
+
return {
|
|
261
|
+
id: row.id,
|
|
262
|
+
project: row.project,
|
|
263
|
+
name: row.name,
|
|
264
|
+
behavior_name: row.behavior_name,
|
|
265
|
+
condition_type: row.condition_type,
|
|
266
|
+
condition_params: safeJsonParse(row.condition_params_json, {}),
|
|
267
|
+
priority: row.priority,
|
|
268
|
+
cooldown_ms: row.cooldown_ms,
|
|
269
|
+
last_fired_at: row.last_fired_at,
|
|
270
|
+
is_enabled: row.is_enabled === 1,
|
|
271
|
+
metadata: safeJsonParse(row.metadata_json, void 0),
|
|
272
|
+
created_at: row.created_at,
|
|
273
|
+
updated_at: row.updated_at
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/utils/version.ts
|
|
279
|
+
function getVersion() {
|
|
280
|
+
if (true) {
|
|
281
|
+
return "0.1.1";
|
|
282
|
+
}
|
|
283
|
+
return "0.1.1";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export {
|
|
287
|
+
ExecutionEngine,
|
|
288
|
+
TriggerRegistry,
|
|
289
|
+
getVersion
|
|
290
|
+
};
|
|
291
|
+
//# sourceMappingURL=chunk-FXYS43D6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/executor.ts","../src/engine/triggers.ts","../src/utils/version.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { ExecutionState, ExecutionId, ExecutionStatus, Outcome } from '../schema/types.js';\nimport { generateId } from '../utils/id.js';\nimport { getCurrentIsoString } from '../utils/time.js';\nimport { safeJsonParse, safeJsonStringify } from '../utils/json-validator.js';\nimport { NotFoundError } from '../utils/errors.js';\nimport { BehaviorRegistry } from './behaviors.js';\nimport { logRuntimeEvent } from './events.js';\n\nexport class ExecutionEngine {\n static startExecution(\n db: Database.Database,\n params: {\n project: string;\n behavior_name: string;\n behavior_version?: number;\n session_id?: string;\n intention_id?: string;\n trace_id?: string;\n parameters?: Record<string, unknown>;\n client_request_id?: string;\n }\n ): ExecutionState {\n const behavior = BehaviorRegistry.getBehavior(db, {\n project: params.project,\n name: params.behavior_name,\n version: params.behavior_version,\n });\n\n const id = generateId() as ExecutionId;\n const now = getCurrentIsoString();\n\n db.prepare(\n `\n INSERT INTO execution_state (\n id, project, behavior_name, behavior_version, session_id, intention_id,\n status, active_node_path, blackboard_json, current_tick, tick_rate_hz,\n duration_ms, stuck_score, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, 'running', 'root', ?, 0, 60, 0, 0.0, ?, ?)\n `\n ).run(\n id,\n params.project,\n behavior.name,\n behavior.version,\n params.session_id ?? null,\n params.intention_id ?? null,\n safeJsonStringify(params.parameters || {}),\n now,\n now\n );\n\n logRuntimeEvent(db, {\n project: params.project,\n entity_id: id,\n entity_type: 'execution',\n action: 'start',\n details: {\n behavior_name: behavior.name,\n intention_id: params.intention_id,\n trace_id: params.trace_id,\n },\n });\n\n return {\n id,\n project: params.project,\n behavior_name: behavior.name,\n behavior_version: behavior.version,\n session_id: params.session_id,\n intention_id: params.intention_id,\n trace_id: params.trace_id,\n status: 'running',\n active_node_path: 'root',\n blackboard_json: safeJsonStringify(params.parameters || {}),\n current_tick: 0,\n tick_rate_hz: 60,\n duration_ms: 0,\n stuck_score: 0.0,\n created_at: now,\n updated_at: now,\n };\n }\n\n static tickExecution(\n db: Database.Database,\n params: {\n project: string;\n execution_id: string;\n active_node_path?: string;\n status?: ExecutionStatus;\n stuck_score?: number;\n blackboard?: Record<string, unknown>;\n }\n ): ExecutionState {\n const current = this.getExecution(db, { project: params.project, id: params.execution_id });\n const now = getCurrentIsoString();\n const newTick = current.current_tick + 1;\n const durationMs = Math.round(newTick * (1000 / current.tick_rate_hz));\n const status = params.status || current.status;\n const nodePath = params.active_node_path || current.active_node_path;\n const stuckScore = params.stuck_score !== undefined ? params.stuck_score : current.stuck_score;\n const bbJson = params.blackboard\n ? safeJsonStringify(params.blackboard)\n : current.blackboard_json;\n\n db.prepare(\n `\n UPDATE execution_state SET\n current_tick = ?, duration_ms = ?, status = ?, active_node_path = ?,\n stuck_score = ?, blackboard_json = ?, updated_at = ?\n WHERE id = ?\n `\n ).run(newTick, durationMs, status, nodePath, stuckScore, bbJson, now, current.id);\n\n return {\n ...current,\n current_tick: newTick,\n duration_ms: durationMs,\n status,\n active_node_path: nodePath,\n stuck_score: stuckScore,\n blackboard_json: bbJson,\n updated_at: now,\n };\n }\n\n static stopExecution(\n db: Database.Database,\n params: {\n project: string;\n execution_id: string;\n status: ExecutionStatus;\n error_message?: string;\n }\n ): Outcome {\n const current = this.getExecution(db, { project: params.project, id: params.execution_id });\n const now = getCurrentIsoString();\n\n db.prepare(\n `\n UPDATE execution_state SET\n status = ?, error = ?, updated_at = ?\n WHERE id = ?\n `\n ).run(params.status, params.error_message ?? null, now, current.id);\n\n logRuntimeEvent(db, {\n project: params.project,\n entity_id: current.id,\n entity_type: 'execution',\n action: 'stop',\n details: { status: params.status, error: params.error_message },\n });\n\n return {\n intention_id: current.intention_id || 'manual',\n execution_id: current.id,\n session_id: current.session_id || 'default',\n status: params.status,\n active_node: current.active_node_path,\n tick_count: current.current_tick,\n duration_ms: current.duration_ms,\n error_message: params.error_message,\n completed_at: now,\n };\n }\n\n static getExecution(\n db: Database.Database,\n params: { project: string; id: string }\n ): ExecutionState {\n const row = db\n .prepare('SELECT * FROM execution_state WHERE project = ? AND id = ?')\n .get(params.project, params.id) as any;\n if (!row) throw new NotFoundError(`Execution instance \"${params.id}\" not found.`);\n return this.mapRowToExecution(row);\n }\n\n static listExecutions(\n db: Database.Database,\n params: { project: string; status?: ExecutionStatus; limit?: number }\n ): ExecutionState[] {\n let sql = 'SELECT * FROM execution_state WHERE project = ?';\n const sqlParams: any[] = [params.project];\n\n if (params.status) {\n sql += ' AND status = ?';\n sqlParams.push(params.status);\n }\n\n sql += ' ORDER BY created_at DESC LIMIT ?';\n sqlParams.push(params.limit || 50);\n\n const rows = db.prepare(sql).all(...sqlParams) as any[];\n return rows.map((r) => this.mapRowToExecution(r));\n }\n\n private static mapRowToExecution(row: any): ExecutionState {\n return {\n id: row.id as ExecutionId,\n project: row.project,\n behavior_name: row.behavior_name,\n behavior_version: row.behavior_version,\n session_id: row.session_id,\n intention_id: row.intention_id,\n status: row.status as ExecutionStatus,\n active_node_path: row.active_node_path,\n blackboard_json: row.blackboard_json || '{}',\n current_tick: row.current_tick,\n tick_rate_hz: row.tick_rate_hz,\n duration_ms: row.duration_ms,\n stuck_score: row.stuck_score,\n error: row.error,\n metadata: safeJsonParse(row.metadata_json, undefined),\n created_at: row.created_at,\n updated_at: row.updated_at,\n };\n }\n}\n","import Database from 'better-sqlite3';\nimport { ReactiveTrigger, TriggerId } from '../schema/types.js';\nimport { generateId } from '../utils/id.js';\nimport { getCurrentIsoString } from '../utils/time.js';\nimport { safeJsonParse, safeJsonStringify } from '../utils/json-validator.js';\nimport { ValidationError, NotFoundError } from '../utils/errors.js';\nimport { logRuntimeEvent } from './events.js';\n\nexport class TriggerRegistry {\n static registerTrigger(\n db: Database.Database,\n params: {\n project: string;\n name: string;\n behavior_name: string;\n condition_type: string;\n condition_params: Record<string, unknown>;\n priority?: number;\n cooldown_ms?: number;\n }\n ): ReactiveTrigger {\n if (!params.name || !params.behavior_name || !params.condition_type) {\n throw new ValidationError('Trigger name, behavior_name, and condition_type are required.');\n }\n\n const id = generateId() as TriggerId;\n const now = getCurrentIsoString();\n const priority = params.priority !== undefined ? params.priority : 0.5;\n const cooldown_ms = params.cooldown_ms !== undefined ? params.cooldown_ms : 1000;\n\n db.prepare(\n `\n INSERT INTO triggers (\n id, project, name, behavior_name, condition_type, condition_params_json,\n priority, cooldown_ms, is_enabled, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)\n ON CONFLICT(project, name) DO UPDATE SET\n behavior_name = excluded.behavior_name,\n condition_type = excluded.condition_type,\n condition_params_json = excluded.condition_params_json,\n priority = excluded.priority,\n cooldown_ms = excluded.cooldown_ms,\n updated_at = excluded.updated_at\n `\n ).run(\n id,\n params.project,\n params.name,\n params.behavior_name,\n params.condition_type,\n safeJsonStringify(params.condition_params || {}),\n priority,\n cooldown_ms,\n now,\n now\n );\n\n logRuntimeEvent(db, {\n project: params.project,\n entity_id: id,\n entity_type: 'trigger',\n action: 'register',\n details: { name: params.name, behavior_name: params.behavior_name, priority },\n });\n\n return {\n id,\n project: params.project,\n name: params.name,\n behavior_name: params.behavior_name,\n condition_type: params.condition_type,\n condition_params: params.condition_params || {},\n priority,\n cooldown_ms,\n is_enabled: true,\n created_at: now,\n updated_at: now,\n };\n }\n\n static listTriggers(db: Database.Database, project: string): ReactiveTrigger[] {\n const rows = db\n .prepare('SELECT * FROM triggers WHERE project = ? ORDER BY priority DESC, name ASC')\n .all(project) as any[];\n return rows.map((r) => this.mapRowToTrigger(r));\n }\n\n static evaluateTriggers(\n db: Database.Database,\n params: { project: string; telemetry: Record<string, any> }\n ): ReactiveTrigger | null {\n const triggers = this.listTriggers(db, params.project).filter((t) => t.is_enabled);\n const now = Date.now();\n\n for (const trig of triggers) {\n if (trig.last_fired_at && now - new Date(trig.last_fired_at).getTime() < trig.cooldown_ms) {\n continue; // In cooldown\n }\n\n let matches = false;\n if (trig.condition_type === 'hp_threshold') {\n const hp = params.telemetry.hp ?? 100;\n const threshold = (trig.condition_params.threshold as number) ?? 30;\n matches = hp <= threshold;\n } else if (trig.condition_type === 'enemy_proximity') {\n const enemyDist = params.telemetry.enemy_distance ?? 999;\n const radius = (trig.condition_params.radius as number) ?? 10;\n matches = enemyDist <= radius;\n }\n\n if (matches) {\n db.prepare('UPDATE triggers SET last_fired_at = ?, updated_at = ? WHERE id = ?').run(\n new Date(now).toISOString(),\n new Date(now).toISOString(),\n trig.id\n );\n return trig;\n }\n }\n return null;\n }\n\n private static mapRowToTrigger(row: any): ReactiveTrigger {\n return {\n id: row.id as TriggerId,\n project: row.project,\n name: row.name,\n behavior_name: row.behavior_name,\n condition_type: row.condition_type,\n condition_params: safeJsonParse(row.condition_params_json, {}),\n priority: row.priority,\n cooldown_ms: row.cooldown_ms,\n last_fired_at: row.last_fired_at,\n is_enabled: row.is_enabled === 1,\n metadata: safeJsonParse(row.metadata_json, undefined),\n created_at: row.created_at,\n updated_at: row.updated_at,\n };\n }\n}\n","declare const __APP_VERSION__: string | undefined;\n\nexport function getVersion(): string {\n if (typeof __APP_VERSION__ !== 'undefined') {\n return __APP_VERSION__;\n }\n return '0.1.1';\n}\n"],"mappings":"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","names":[]}
|