@arcane-engine/runtime 0.1.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.
Files changed (57) hide show
  1. package/README.md +38 -0
  2. package/index.ts +19 -0
  3. package/package.json +53 -0
  4. package/src/agent/agent.test.ts +384 -0
  5. package/src/agent/describe.ts +72 -0
  6. package/src/agent/index.ts +20 -0
  7. package/src/agent/protocol.ts +125 -0
  8. package/src/agent/types.ts +73 -0
  9. package/src/pathfinding/astar.test.ts +208 -0
  10. package/src/pathfinding/astar.ts +193 -0
  11. package/src/pathfinding/index.ts +2 -0
  12. package/src/pathfinding/types.ts +21 -0
  13. package/src/physics/aabb.ts +54 -0
  14. package/src/physics/index.ts +2 -0
  15. package/src/rendering/animation.test.ts +119 -0
  16. package/src/rendering/animation.ts +132 -0
  17. package/src/rendering/audio.test.ts +33 -0
  18. package/src/rendering/audio.ts +70 -0
  19. package/src/rendering/camera.ts +35 -0
  20. package/src/rendering/index.ts +56 -0
  21. package/src/rendering/input.test.ts +70 -0
  22. package/src/rendering/input.ts +82 -0
  23. package/src/rendering/lighting.ts +38 -0
  24. package/src/rendering/loop.ts +21 -0
  25. package/src/rendering/sprites.ts +60 -0
  26. package/src/rendering/text.test.ts +91 -0
  27. package/src/rendering/text.ts +184 -0
  28. package/src/rendering/texture.ts +31 -0
  29. package/src/rendering/tilemap.ts +46 -0
  30. package/src/rendering/types.ts +54 -0
  31. package/src/rendering/validate.ts +132 -0
  32. package/src/state/error.test.ts +45 -0
  33. package/src/state/error.ts +20 -0
  34. package/src/state/index.ts +70 -0
  35. package/src/state/observe.test.ts +173 -0
  36. package/src/state/observe.ts +110 -0
  37. package/src/state/prng.test.ts +221 -0
  38. package/src/state/prng.ts +162 -0
  39. package/src/state/query.test.ts +208 -0
  40. package/src/state/query.ts +144 -0
  41. package/src/state/store.test.ts +211 -0
  42. package/src/state/store.ts +109 -0
  43. package/src/state/transaction.test.ts +235 -0
  44. package/src/state/transaction.ts +280 -0
  45. package/src/state/types.test.ts +33 -0
  46. package/src/state/types.ts +30 -0
  47. package/src/systems/index.ts +2 -0
  48. package/src/systems/system.test.ts +217 -0
  49. package/src/systems/system.ts +150 -0
  50. package/src/systems/types.ts +35 -0
  51. package/src/testing/harness.ts +271 -0
  52. package/src/testing/mock-renderer.test.ts +93 -0
  53. package/src/testing/mock-renderer.ts +178 -0
  54. package/src/ui/index.ts +3 -0
  55. package/src/ui/primitives.test.ts +105 -0
  56. package/src/ui/primitives.ts +260 -0
  57. package/src/ui/types.ts +57 -0
package/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # @arcane/runtime
2
+
3
+ TypeScript runtime for Arcane — a code-first, test-native, agent-native 2D game engine.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @arcane/runtime
9
+ ```
10
+
11
+ **Note:** You also need the Arcane CLI:
12
+ ```bash
13
+ cargo install arcane-cli
14
+ ```
15
+
16
+ ## Quick Example
17
+
18
+ ```typescript
19
+ import { createStore } from "@arcane/runtime/state";
20
+ import { onFrame, drawSprite } from "@arcane/runtime/rendering";
21
+
22
+ const store = createStore({ player: { x: 0, y: 0 } });
23
+
24
+ onFrame(() => {
25
+ const state = store.getState();
26
+ drawSprite("player", state.player.x, state.player.y, { width: 32, height: 32 });
27
+ });
28
+ ```
29
+
30
+ ## Documentation
31
+
32
+ - [Getting Started](https://github.com/anthropics/arcane/blob/main/docs/getting-started.md)
33
+ - [API Reference](https://github.com/anthropics/arcane/blob/main/docs/api-reference.md)
34
+ - [Examples](https://github.com/anthropics/arcane/tree/main/examples)
35
+
36
+ ## License
37
+
38
+ Apache 2.0
package/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @arcane/runtime — Agent-native 2D game engine runtime
3
+ *
4
+ * This package contains the TypeScript runtime APIs for Arcane.
5
+ * Import specific modules from subpaths:
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createStore } from "@arcane/runtime/state";
10
+ * import { onFrame, drawSprite } from "@arcane/runtime/rendering";
11
+ * import { findPath } from "@arcane/runtime/pathfinding";
12
+ * ```
13
+ *
14
+ * @see https://github.com/anthropics/arcane
15
+ */
16
+
17
+ // Re-export commonly used types
18
+ export type { Vec2, EntityId } from "./src/state/types.ts";
19
+ export type { GameStore } from "./src/state/store.ts";
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@arcane-engine/runtime",
3
+ "version": "0.1.0",
4
+ "description": "Agent-native 2D game engine runtime - TypeScript APIs for state management, rendering, and game logic",
5
+ "type": "module",
6
+ "main": "./index.ts",
7
+ "types": "./index.ts",
8
+ "exports": {
9
+ ".": "./index.ts",
10
+ "./state": "./src/state/index.ts",
11
+ "./rendering": "./src/rendering/index.ts",
12
+ "./ui": "./src/ui/index.ts",
13
+ "./physics": "./src/physics/index.ts",
14
+ "./pathfinding": "./src/pathfinding/index.ts",
15
+ "./systems": "./src/systems/index.ts",
16
+ "./agent": "./src/agent/index.ts",
17
+ "./testing": "./src/testing/harness.ts"
18
+ },
19
+ "files": [
20
+ "src",
21
+ "index.ts",
22
+ "README.md"
23
+ ],
24
+ "keywords": [
25
+ "game-engine",
26
+ "2d",
27
+ "gamedev",
28
+ "typescript",
29
+ "ai-agent",
30
+ "agent-native",
31
+ "state-management",
32
+ "ecs",
33
+ "recipes"
34
+ ],
35
+ "author": "Arcane Contributors",
36
+ "license": "Apache-2.0",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/anthropics/arcane.git",
40
+ "directory": "packages/runtime"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/anthropics/arcane/issues"
44
+ },
45
+ "homepage": "https://github.com/anthropics/arcane#readme",
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^20.0.0",
51
+ "typescript": "^5.3.0"
52
+ }
53
+ }
@@ -0,0 +1,384 @@
1
+ import { describe, it, assert } from "../testing/harness.ts";
2
+ import { registerAgent } from "./protocol.ts";
3
+ import { defaultDescribe } from "./describe.ts";
4
+ import { createStore } from "../state/store.ts";
5
+ import type { AgentConfig, DescribeOptions } from "./types.ts";
6
+
7
+ // --- Test state types ---
8
+
9
+ type RPGState = {
10
+ player: { hp: number; maxHp: number; pos: { x: number; y: number } };
11
+ enemies: { id: string; hp: number }[];
12
+ turn: number;
13
+ phase: "playing" | "won" | "dead";
14
+ };
15
+
16
+ const makeState = (): RPGState => ({
17
+ player: { hp: 20, maxHp: 20, pos: { x: 1, y: 1 } },
18
+ enemies: [
19
+ { id: "goblin", hp: 6 },
20
+ { id: "rat", hp: 3 },
21
+ ],
22
+ turn: 1,
23
+ phase: "playing",
24
+ });
25
+
26
+ // Helper to create a get/setState-backed agent
27
+ function makeAgent(initial?: RPGState) {
28
+ let state = initial ?? makeState();
29
+ return registerAgent<RPGState>({
30
+ name: "test-agent",
31
+ getState: () => state,
32
+ setState: (s) => { state = s; },
33
+ actions: {
34
+ move: {
35
+ handler: (s, args) => ({
36
+ ...s,
37
+ player: {
38
+ ...s.player,
39
+ pos: {
40
+ x: s.player.pos.x + (args.dx as number ?? 0),
41
+ y: s.player.pos.y + (args.dy as number ?? 0),
42
+ },
43
+ },
44
+ turn: s.turn + 1,
45
+ }),
46
+ description: "Move the player by (dx, dy)",
47
+ args: [
48
+ { name: "dx", type: "number" },
49
+ { name: "dy", type: "number" },
50
+ ],
51
+ },
52
+ wait: {
53
+ handler: (s) => ({ ...s, turn: s.turn + 1 }),
54
+ description: "Wait one turn",
55
+ },
56
+ failAction: {
57
+ handler: () => { throw new Error("intentional failure"); },
58
+ description: "Always fails",
59
+ },
60
+ },
61
+ });
62
+ }
63
+
64
+ // --- registerAgent ---
65
+
66
+ describe("registerAgent", () => {
67
+ it("returns a protocol with the agent name", () => {
68
+ const agent = makeAgent();
69
+ assert.equal(agent.name, "test-agent");
70
+ });
71
+
72
+ it("installs on globalThis.__arcaneAgent", () => {
73
+ const agent = makeAgent();
74
+ assert.equal((globalThis as any).__arcaneAgent, agent);
75
+ });
76
+
77
+ it("works with a GameStore", () => {
78
+ const store = createStore(makeState());
79
+ const agent = registerAgent<RPGState>({
80
+ name: "store-agent",
81
+ store,
82
+ actions: {
83
+ wait: {
84
+ handler: (s) => ({ ...s, turn: s.turn + 1 }),
85
+ description: "Wait one turn",
86
+ },
87
+ },
88
+ });
89
+ assert.equal(agent.getState().turn, 1);
90
+ agent.executeAction("wait");
91
+ assert.equal(agent.getState().turn, 2);
92
+ // store should reflect the change
93
+ assert.equal(store.getState().turn, 2);
94
+ });
95
+ });
96
+
97
+ // --- getState ---
98
+
99
+ describe("getState", () => {
100
+ it("returns the current state", () => {
101
+ const agent = makeAgent();
102
+ const state = agent.getState();
103
+ assert.equal(state.player.hp, 20);
104
+ assert.equal(state.turn, 1);
105
+ });
106
+ });
107
+
108
+ // --- inspect ---
109
+
110
+ describe("inspect", () => {
111
+ it("returns a value at a simple path", () => {
112
+ const agent = makeAgent();
113
+ assert.equal(agent.inspect("turn"), 1);
114
+ });
115
+
116
+ it("returns a value at a nested path", () => {
117
+ const agent = makeAgent();
118
+ assert.equal(agent.inspect("player.hp"), 20);
119
+ assert.equal(agent.inspect("player.pos.x"), 1);
120
+ });
121
+
122
+ it("returns undefined for nonexistent path", () => {
123
+ const agent = makeAgent();
124
+ assert.equal(agent.inspect("nonexistent"), undefined);
125
+ });
126
+
127
+ it("returns array elements by index path", () => {
128
+ const agent = makeAgent();
129
+ assert.equal(agent.inspect("enemies.0.id"), "goblin");
130
+ assert.equal(agent.inspect("enemies.1.hp"), 3);
131
+ });
132
+ });
133
+
134
+ // --- describe ---
135
+
136
+ describe("describe", () => {
137
+ it("uses default describe when none provided", () => {
138
+ const agent = makeAgent();
139
+ const desc = agent.describe();
140
+ assert.ok(desc.includes("player"));
141
+ assert.ok(desc.includes("turn"));
142
+ });
143
+
144
+ it("accepts a custom describe function", () => {
145
+ let state = makeState();
146
+ const agent = registerAgent<RPGState>({
147
+ name: "custom-desc",
148
+ getState: () => state,
149
+ setState: (s) => { state = s; },
150
+ describe: (s) => `Turn ${s.turn}, HP: ${s.player.hp}/${s.player.maxHp}`,
151
+ });
152
+ assert.equal(agent.describe(), "Turn 1, HP: 20/20");
153
+ });
154
+
155
+ it("passes options to describe function", () => {
156
+ let state = makeState();
157
+ const agent = registerAgent<RPGState>({
158
+ name: "opts-desc",
159
+ getState: () => state,
160
+ setState: (s) => { state = s; },
161
+ describe: (s, opts) => `v=${opts.verbosity ?? "normal"}, p=${opts.path ?? "none"}`,
162
+ });
163
+ assert.equal(agent.describe({ verbosity: "minimal" }), "v=minimal, p=none");
164
+ assert.equal(agent.describe({ path: "player" }), "v=normal, p=player");
165
+ });
166
+ });
167
+
168
+ // --- listActions ---
169
+
170
+ describe("listActions", () => {
171
+ it("returns all registered actions", () => {
172
+ const agent = makeAgent();
173
+ const actions = agent.listActions();
174
+ assert.equal(actions.length, 3);
175
+ const names = actions.map((a) => a.name);
176
+ assert.ok(names.includes("move"));
177
+ assert.ok(names.includes("wait"));
178
+ assert.ok(names.includes("failAction"));
179
+ });
180
+
181
+ it("includes description and args metadata", () => {
182
+ const agent = makeAgent();
183
+ const moveAction = agent.listActions().find((a) => a.name === "move");
184
+ assert.ok(moveAction !== undefined);
185
+ assert.equal(moveAction!.description, "Move the player by (dx, dy)");
186
+ assert.equal(moveAction!.args!.length, 2);
187
+ assert.equal(moveAction!.args![0].name, "dx");
188
+ });
189
+
190
+ it("returns empty array when no actions registered", () => {
191
+ let state = makeState();
192
+ const agent = registerAgent<RPGState>({
193
+ name: "no-actions",
194
+ getState: () => state,
195
+ setState: (s) => { state = s; },
196
+ });
197
+ assert.deepEqual(agent.listActions(), []);
198
+ });
199
+ });
200
+
201
+ // --- executeAction ---
202
+
203
+ describe("executeAction", () => {
204
+ it("executes an action and updates state", () => {
205
+ const agent = makeAgent();
206
+ const result = agent.executeAction("move", JSON.stringify({ dx: 1, dy: 0 }));
207
+ assert.equal(result.ok, true);
208
+ assert.equal(result.state.player.pos.x, 2);
209
+ assert.equal(result.state.turn, 2);
210
+ // State is committed
211
+ assert.equal(agent.getState().player.pos.x, 2);
212
+ });
213
+
214
+ it("executes an action with no args", () => {
215
+ const agent = makeAgent();
216
+ const result = agent.executeAction("wait");
217
+ assert.equal(result.ok, true);
218
+ assert.equal(result.state.turn, 2);
219
+ });
220
+
221
+ it("returns error for unknown action", () => {
222
+ const agent = makeAgent();
223
+ const result = agent.executeAction("nonexistent");
224
+ assert.equal(result.ok, false);
225
+ assert.ok(result.error!.includes("Unknown action"));
226
+ });
227
+
228
+ it("returns error for invalid JSON args", () => {
229
+ const agent = makeAgent();
230
+ const result = agent.executeAction("move", "{bad json");
231
+ assert.equal(result.ok, false);
232
+ assert.ok(result.error!.includes("Invalid JSON"));
233
+ });
234
+
235
+ it("returns error when action handler throws", () => {
236
+ const agent = makeAgent();
237
+ const result = agent.executeAction("failAction");
238
+ assert.equal(result.ok, false);
239
+ assert.ok(result.error!.includes("intentional failure"));
240
+ });
241
+ });
242
+
243
+ // --- simulate ---
244
+
245
+ describe("simulate", () => {
246
+ it("simulates an action without committing state", () => {
247
+ const agent = makeAgent();
248
+ const result = agent.simulate("move", JSON.stringify({ dx: 1, dy: 0 }));
249
+ assert.equal(result.ok, true);
250
+ assert.equal(result.state.player.pos.x, 2);
251
+ // Original state unchanged
252
+ assert.equal(agent.getState().player.pos.x, 1);
253
+ });
254
+
255
+ it("returns error for unknown action", () => {
256
+ const agent = makeAgent();
257
+ const result = agent.simulate("nonexistent");
258
+ assert.equal(result.ok, false);
259
+ assert.ok(result.error!.includes("Unknown action"));
260
+ });
261
+
262
+ it("returns error for invalid JSON args", () => {
263
+ const agent = makeAgent();
264
+ const result = agent.simulate("move", "{bad}");
265
+ assert.equal(result.ok, false);
266
+ assert.ok(result.error!.includes("Invalid JSON"));
267
+ });
268
+
269
+ it("returns error when simulation throws", () => {
270
+ const agent = makeAgent();
271
+ const result = agent.simulate("failAction");
272
+ assert.equal(result.ok, false);
273
+ assert.ok(result.error!.includes("intentional failure"));
274
+ });
275
+ });
276
+
277
+ // --- rewind ---
278
+
279
+ describe("rewind", () => {
280
+ it("resets to initial state", () => {
281
+ const agent = makeAgent();
282
+ agent.executeAction("move", JSON.stringify({ dx: 5, dy: 5 }));
283
+ agent.executeAction("wait");
284
+ assert.equal(agent.getState().turn, 3);
285
+ assert.equal(agent.getState().player.pos.x, 6);
286
+
287
+ const restored = agent.rewind();
288
+ assert.equal(restored.turn, 1);
289
+ assert.equal(restored.player.pos.x, 1);
290
+ assert.equal(agent.getState().turn, 1);
291
+ });
292
+
293
+ it("returns a deep clone (modifying it does not affect agent)", () => {
294
+ const agent = makeAgent();
295
+ const restored = agent.rewind();
296
+ (restored as any).turn = 999;
297
+ assert.equal(agent.getState().turn, 1);
298
+ });
299
+ });
300
+
301
+ // --- captureSnapshot ---
302
+
303
+ describe("captureSnapshot", () => {
304
+ it("captures current state with timestamp", () => {
305
+ const agent = makeAgent();
306
+ agent.executeAction("wait");
307
+ const snap = agent.captureSnapshot();
308
+ assert.equal(snap.state.turn, 2);
309
+ assert.ok(typeof snap.timestamp === "number");
310
+ assert.ok(snap.timestamp > 0);
311
+ });
312
+
313
+ it("returns a deep clone", () => {
314
+ const agent = makeAgent();
315
+ const snap = agent.captureSnapshot();
316
+ (snap.state as any).turn = 999;
317
+ assert.equal(agent.getState().turn, 1);
318
+ });
319
+ });
320
+
321
+ // --- defaultDescribe ---
322
+
323
+ describe("defaultDescribe", () => {
324
+ it("minimal: shows key names for objects", () => {
325
+ const desc = defaultDescribe({ a: 1, b: 2 }, { verbosity: "minimal" });
326
+ assert.equal(desc, "{a, b}");
327
+ });
328
+
329
+ it("minimal: shows count for arrays", () => {
330
+ const desc = defaultDescribe([1, 2, 3], { verbosity: "minimal" });
331
+ assert.equal(desc, "Array(3)");
332
+ });
333
+
334
+ it("normal: shows first 3 keys with values", () => {
335
+ const desc = defaultDescribe(
336
+ { a: 1, b: "hello", c: [1, 2], d: true },
337
+ { verbosity: "normal" },
338
+ );
339
+ assert.ok(desc.includes("a: 1"));
340
+ assert.ok(desc.includes('b: "hello"'));
341
+ assert.ok(desc.includes("c: Array(2)"));
342
+ assert.ok(desc.includes("4 keys total"));
343
+ });
344
+
345
+ it("normal: shows first 3 array items", () => {
346
+ const desc = defaultDescribe([10, 20, 30, 40], { verbosity: "normal" });
347
+ assert.ok(desc.includes("10"));
348
+ assert.ok(desc.includes("20"));
349
+ assert.ok(desc.includes("30"));
350
+ assert.ok(desc.includes("4 total"));
351
+ });
352
+
353
+ it("detailed: returns full JSON", () => {
354
+ const obj = { x: 1, y: 2 };
355
+ const desc = defaultDescribe(obj, { verbosity: "detailed" });
356
+ assert.deepEqual(JSON.parse(desc), obj);
357
+ });
358
+
359
+ it("handles path option", () => {
360
+ const state = { player: { hp: 10 }, enemies: [1, 2] };
361
+ const desc = defaultDescribe(state, { path: "player", verbosity: "detailed" });
362
+ assert.deepEqual(JSON.parse(desc), { hp: 10 });
363
+ });
364
+
365
+ it("returns not-found message for invalid path", () => {
366
+ const desc = defaultDescribe({ a: 1 }, { path: "x.y.z" });
367
+ assert.ok(desc.includes("not found"));
368
+ });
369
+
370
+ it("handles primitives", () => {
371
+ assert.equal(defaultDescribe(42, {}), "42");
372
+ assert.equal(defaultDescribe("hello", {}), "hello");
373
+ assert.equal(defaultDescribe(true, {}), "true");
374
+ });
375
+
376
+ it("handles null state", () => {
377
+ assert.equal(defaultDescribe(null, {}), "null");
378
+ });
379
+
380
+ it("defaults to normal verbosity", () => {
381
+ const desc = defaultDescribe({ a: 1 }, {});
382
+ assert.ok(desc.includes("a: 1"));
383
+ });
384
+ });
@@ -0,0 +1,72 @@
1
+ import type { DescribeOptions, Verbosity } from "./types.ts";
2
+
3
+ /** Default describe function that summarizes game state at the given verbosity */
4
+ export function defaultDescribe(state: unknown, options: DescribeOptions): string {
5
+ const verbosity: Verbosity = options.verbosity ?? "normal";
6
+ const target = options.path ? getByPath(state, options.path) : state;
7
+
8
+ if (target === undefined) {
9
+ return options.path ? `Path "${options.path}" not found` : "No state";
10
+ }
11
+
12
+ if (verbosity === "detailed") {
13
+ return JSON.stringify(target, null, 2);
14
+ }
15
+
16
+ if (typeof target !== "object" || target === null) {
17
+ return String(target);
18
+ }
19
+
20
+ if (Array.isArray(target)) {
21
+ return describeArray(target, verbosity);
22
+ }
23
+
24
+ return describeObject(target as Record<string, unknown>, verbosity);
25
+ }
26
+
27
+ function describeArray(arr: unknown[], verbosity: Verbosity): string {
28
+ if (verbosity === "minimal") {
29
+ return `Array(${arr.length})`;
30
+ }
31
+ // normal: show first 3 items
32
+ const preview = arr.slice(0, 3).map((item) => summarizeValue(item));
33
+ const suffix = arr.length > 3 ? `, ... (${arr.length} total)` : "";
34
+ return `[${preview.join(", ")}${suffix}]`;
35
+ }
36
+
37
+ function describeObject(obj: Record<string, unknown>, verbosity: Verbosity): string {
38
+ const keys = Object.keys(obj);
39
+ if (verbosity === "minimal") {
40
+ return `{${keys.join(", ")}}`;
41
+ }
42
+ // normal: show key: summarized value for first 3 keys
43
+ const lines = keys.slice(0, 3).map((k) => ` ${k}: ${summarizeValue(obj[k])}`);
44
+ if (keys.length > 3) {
45
+ lines.push(` ... (${keys.length} keys total)`);
46
+ }
47
+ return `{\n${lines.join("\n")}\n}`;
48
+ }
49
+
50
+ function summarizeValue(value: unknown): string {
51
+ if (value === null) return "null";
52
+ if (value === undefined) return "undefined";
53
+ if (typeof value === "string") return JSON.stringify(value);
54
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
55
+ if (Array.isArray(value)) return `Array(${value.length})`;
56
+ if (typeof value === "object") {
57
+ const keys = Object.keys(value as Record<string, unknown>);
58
+ return `{${keys.length} keys}`;
59
+ }
60
+ return String(value);
61
+ }
62
+
63
+ function getByPath(obj: unknown, path: string): unknown {
64
+ const segments = path.split(".");
65
+ let current: unknown = obj;
66
+ for (const seg of segments) {
67
+ if (current === null || current === undefined) return undefined;
68
+ if (typeof current !== "object") return undefined;
69
+ current = (current as Record<string, unknown>)[seg];
70
+ }
71
+ return current;
72
+ }
@@ -0,0 +1,20 @@
1
+ // Types
2
+ export type {
3
+ Verbosity,
4
+ DescribeOptions,
5
+ ActionInfo,
6
+ ArgInfo,
7
+ ActionResult,
8
+ SimulateResult,
9
+ SnapshotData,
10
+ ActionHandler,
11
+ DescribeFn,
12
+ AgentConfig,
13
+ AgentProtocol,
14
+ } from "./types.ts";
15
+
16
+ // Protocol
17
+ export { registerAgent } from "./protocol.ts";
18
+
19
+ // Describe
20
+ export { defaultDescribe } from "./describe.ts";