arborkit 1.0.1 → 1.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.
- package/CHANGELOG.md +19 -0
- package/README.md +8 -0
- package/dist/arbor.js +2 -2
- package/dist/{chunk-IUV6LML6.js → chunk-WEO5KBRI.js} +2 -2
- package/dist/{chunk-5RUBU72S.js → chunk-ZDANDXJ6.js} +33 -2
- package/dist/chunk-ZDANDXJ6.js.map +1 -0
- package/dist/index.js +2 -2
- package/dist/toolset.d.ts +10 -0
- package/dist/toolset.js +1 -1
- package/package.json +170 -170
- package/dist/chunk-5RUBU72S.js.map +0 -1
- /package/dist/{chunk-IUV6LML6.js.map → chunk-WEO5KBRI.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.0 — 2026-07-05
|
|
4
|
+
|
|
5
|
+
- **New patch op: `edit`** — exact-substring surgery on string-valued nodes:
|
|
6
|
+
`patch(ref, { op: "edit", old, new, replaceAll?, ifVersion? })`. `old` must occur
|
|
7
|
+
exactly once in the node's string value (or set `replaceAll`); on a miss or an
|
|
8
|
+
ambiguous match the op fails with a structured `INVALID_OP` that reports the
|
|
9
|
+
occurrence count, so an agent can re-quote a larger fragment and retry.
|
|
10
|
+
Replacement is literal (no `$&`-style pattern expansion). Scope and `ifVersion`
|
|
11
|
+
are enforced *before* any content inspection — out-of-scope probes get an
|
|
12
|
+
identical `SCOPE_VIOLATION` whether or not `old` matches, leaking nothing.
|
|
13
|
+
- Under the hood `edit` is pure sugar over `set`: the event log records an ordinary
|
|
14
|
+
`set` with full before/after, so replay, revert, delta persistence, and the AG-UI
|
|
15
|
+
adapter need no changes.
|
|
16
|
+
- Motivation: output tokens are ~5× input price on current Claude models. An agent
|
|
17
|
+
quoting `old`/`new` fragments (~100 output tokens) instead of regenerating a whole
|
|
18
|
+
block (~1500) is the dominant cost lever for agent-driven editing. Pattern: `get`
|
|
19
|
+
the node first, quote `old` from the live value — the Claude Code `Edit`-tool
|
|
20
|
+
semantics.
|
|
21
|
+
|
|
3
22
|
## 1.0.1 — 2026-07-03
|
|
4
23
|
|
|
5
24
|
- **Fix: cross-entry `instanceof` breakage.** 1.0.0 was built without code splitting, so every
|
package/README.md
CHANGED
|
@@ -44,6 +44,14 @@ const home = await tools.get({ path: "/pages/home" }); // home.value.content ===
|
|
|
44
44
|
const refused = await tools.patch({ path: "/plan" }, { op: "set", value: "hacked" });
|
|
45
45
|
// refused.ok === false — out of scope; violations are returned, never thrown
|
|
46
46
|
|
|
47
|
+
// Agent edits — surgical substring replacement instead of regenerating a block:
|
|
48
|
+
// get the node first, quote `old` from the live value (Claude Code Edit semantics).
|
|
49
|
+
await tools.patch({ path: "/pages/home" }, { op: "set", value: { title: "Home", html: "<p>Bonus: 100% do 2000 PLN</p>" } });
|
|
50
|
+
const edited = await tools.patch(
|
|
51
|
+
{ path: "/pages/home/html" },
|
|
52
|
+
{ op: "edit", old: "100% do 2000 PLN", new: "150% do 3000 PLN" },
|
|
53
|
+
); // unique-or-fail: an ambiguous `old` returns INVALID_OP with the occurrence count
|
|
54
|
+
|
|
47
55
|
// Semantic search — mutations only mark nodes stale; reindex() embeds:
|
|
48
56
|
await arbor.index!.reindex();
|
|
49
57
|
const found = await arbor.index!.search("home page"); // { results, staleCount }
|
package/dist/arbor.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createArbor,
|
|
3
3
|
restoreArbor
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-WEO5KBRI.js";
|
|
5
|
+
import "./chunk-ZDANDXJ6.js";
|
|
6
6
|
import "./chunk-6DOI4FNX.js";
|
|
7
7
|
import "./chunk-OZCCBVUV.js";
|
|
8
8
|
import "./chunk-QRGDUGY6.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
makeToolset
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZDANDXJ6.js";
|
|
4
4
|
import {
|
|
5
5
|
typeAwareDecision
|
|
6
6
|
} from "./chunk-6DOI4FNX.js";
|
|
@@ -145,4 +145,4 @@ export {
|
|
|
145
145
|
createArbor,
|
|
146
146
|
restoreArbor
|
|
147
147
|
};
|
|
148
|
-
//# sourceMappingURL=chunk-
|
|
148
|
+
//# sourceMappingURL=chunk-WEO5KBRI.js.map
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
ArborError,
|
|
9
9
|
InvalidOpError,
|
|
10
10
|
NodeNotFoundError,
|
|
11
|
-
ScopeViolationError
|
|
11
|
+
ScopeViolationError,
|
|
12
|
+
StaleVersionError
|
|
12
13
|
} from "./chunk-V3HDDWER.js";
|
|
13
14
|
|
|
14
15
|
// src/toolset.ts
|
|
@@ -91,6 +92,36 @@ function makeToolset(deps, binding = {}) {
|
|
|
91
92
|
deps.mutator.move(ref, op.to, op.key, common);
|
|
92
93
|
return { id: node.id, path: addressing.pathOf(node.id), version: node.meta.version };
|
|
93
94
|
}
|
|
95
|
+
case "edit": {
|
|
96
|
+
const node = resolve(ref);
|
|
97
|
+
const path = addressing.pathOf(node.id);
|
|
98
|
+
if (binding.writeScope !== void 0 && !isWithin(path, binding.writeScope)) {
|
|
99
|
+
throw new ScopeViolationError(path, binding.writeScope);
|
|
100
|
+
}
|
|
101
|
+
if (op.ifVersion !== void 0 && node.meta.version !== op.ifVersion) {
|
|
102
|
+
throw new StaleVersionError(node.id, op.ifVersion, node.meta.version);
|
|
103
|
+
}
|
|
104
|
+
const value = tree.toJson(node.id);
|
|
105
|
+
if (typeof value !== "string") {
|
|
106
|
+
const kind = value === null ? "null" : Array.isArray(value) ? "an array" : typeof value === "object" ? "an object" : `a ${typeof value}`;
|
|
107
|
+
throw new InvalidOpError(
|
|
108
|
+
`edit targets string values; ${path} is ${kind} \u2014 target a string field inside it`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (op.old === "") throw new InvalidOpError("edit: old must be non-empty");
|
|
112
|
+
if (op.old === op.new) throw new InvalidOpError("edit: old and new are identical");
|
|
113
|
+
const count = value.split(op.old).length - 1;
|
|
114
|
+
if (count === 0) throw new InvalidOpError(`edit: old string not found in ${path}`);
|
|
115
|
+
if (count > 1 && !op.replaceAll) {
|
|
116
|
+
throw new InvalidOpError(
|
|
117
|
+
`edit: old string occurs ${count} times in ${path} \u2014 quote a larger unique fragment or set replaceAll`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
const next = value.split(op.old).join(op.new);
|
|
121
|
+
deps.mutator.set(ref, next, common);
|
|
122
|
+
const after = resolve(ref);
|
|
123
|
+
return { id: after.id, path: addressing.pathOf(after.id), version: after.meta.version };
|
|
124
|
+
}
|
|
94
125
|
}
|
|
95
126
|
}),
|
|
96
127
|
history: (ref, opts = {}) => run(() => {
|
|
@@ -114,4 +145,4 @@ function makeToolset(deps, binding = {}) {
|
|
|
114
145
|
export {
|
|
115
146
|
makeToolset
|
|
116
147
|
};
|
|
117
|
-
//# sourceMappingURL=chunk-
|
|
148
|
+
//# sourceMappingURL=chunk-ZDANDXJ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/toolset.ts"],"sourcesContent":["import type { ArtifactTree } from \"./artifact-tree\";\r\nimport type { Addressing } from \"./addressing\";\r\nimport type { EventLog, MutationEvent } from \"./event-log\";\r\nimport type { Mutator } from \"./mutator\";\r\nimport type { SemanticIndex } from \"./semantic-index\";\r\nimport {\r\n Navigator,\r\n type DescribeOpts,\r\n type DescribeResult,\r\n type GetOpts,\r\n type GetResult,\r\n type FindSelector,\r\n type FindOpts,\r\n type FindResult,\r\n} from \"./navigator\";\r\nimport type { SearchOpts, SearchResult } from \"./semantic-index\";\r\nimport {\r\n type Ref,\r\n ArborError,\r\n ScopeViolationError,\r\n StaleVersionError,\r\n InvalidOpError,\r\n NodeNotFoundError,\r\n} from \"./errors\";\r\nimport { isWithin } from \"./jsonpointer\";\r\nimport type { Json, NodeId } from \"./types\";\r\n\r\n/** Every toolset call returns this — errors are structured, never thrown across the agent boundary. */\r\nexport type ToolResult<T> = { ok: true; value: T } | { ok: false; error: { code: string; message: string } };\r\n\r\n/** `edit` is exact-substring surgery on a string leaf — pure sugar over `set`: the event\r\n * log records a plain `set` with the full before/after value, so history/revert/replay/\r\n * AG-UI need nothing new. `old` must match the live value exactly (and uniquely, unless\r\n * `replaceAll`) — `get` the node first and quote `old` from what it returns. */\r\nexport type PatchOp =\r\n | { op: \"set\"; value: Json; ifVersion?: number }\r\n | { op: \"insert\"; key: string | number; value: Json; ifVersion?: number }\r\n | { op: \"remove\"; ifVersion?: number }\r\n | { op: \"move\"; to: Ref; key: string | number; ifVersion?: number }\r\n | { op: \"edit\"; old: string; new: string; replaceAll?: boolean; ifVersion?: number };\r\n\r\nexport interface ToolsetDeps {\r\n tree: ArtifactTree;\r\n addressing: Addressing;\r\n log: EventLog;\r\n mutator: Mutator;\r\n index?: SemanticIndex;\r\n}\r\n\r\nexport interface ToolsetBinding {\r\n owner?: string;\r\n /** JSON Pointer prefix: writes must be at or under it (enforced by the Mutator). */\r\n writeScope?: string;\r\n /** JSON Pointer prefix: reads are confined to it. */\r\n readScope?: string;\r\n}\r\n\r\nexport interface PatchResult {\r\n id: NodeId;\r\n path: string;\r\n /** The affected node's version AFTER the op (remove: the parent's). Feed into the next ifVersion. */\r\n version: number;\r\n}\r\n\r\nexport interface Toolset {\r\n describe(ref?: Ref, opts?: DescribeOpts): Promise<ToolResult<DescribeResult>>;\r\n get(ref: Ref, opts?: GetOpts): Promise<ToolResult<GetResult>>;\r\n find(selector: FindSelector, opts?: FindOpts): Promise<ToolResult<FindResult>>;\r\n search(query: string, opts?: SearchOpts): Promise<ToolResult<SearchResult>>;\r\n patch(ref: Ref, op: PatchOp): Promise<ToolResult<PatchResult>>;\r\n history(ref?: Ref, opts?: { limit?: number }): Promise<ToolResult<MutationEvent[]>>;\r\n}\r\n\r\n/** An event is within scope if any of its recorded paths is within scope. */\r\nfunction eventWithinScope(e: MutationEvent, scope: string): boolean {\r\n if (e.path !== undefined && isWithin(e.path, scope)) return true;\r\n if (e.toPath !== undefined && isWithin(e.toPath, scope)) return true;\r\n if (e.fromPath !== undefined && isWithin(e.fromPath, scope)) return true;\r\n return false;\r\n}\r\n\r\n/** Run a tool body, converting thrown errors into a structured ToolResult. */\r\nasync function run<T>(fn: () => T | Promise<T>): Promise<ToolResult<T>> {\r\n try {\r\n return { ok: true, value: await fn() };\r\n } catch (e) {\r\n if (e instanceof ArborError) return { ok: false, error: { code: e.code, message: e.message } };\r\n const message = e instanceof Error ? e.message : String(e);\r\n return { ok: false, error: { code: \"ERROR\", message } };\r\n }\r\n}\r\n\r\n/** A scoped, agent-facing bundle of tools returning structured results. */\r\nexport function makeToolset(deps: ToolsetDeps, binding: ToolsetBinding = {}): Toolset {\r\n const { tree, addressing } = deps;\r\n const navigator = new Navigator(tree, addressing);\r\n\r\n return {\r\n describe: (ref, opts) =>\r\n run(() => {\r\n const target: Ref = ref ?? (binding.readScope !== undefined ? { path: binding.readScope } : { path: \"\" });\r\n const r = navigator.describe(target, opts);\r\n if (!isWithin(r.node.path, binding.readScope)) {\r\n throw new ScopeViolationError(r.node.path, binding.readScope!);\r\n }\r\n return r;\r\n }),\r\n\r\n get: (ref, opts) =>\r\n run(() => {\r\n const r = navigator.get(ref, opts);\r\n if (!isWithin(r.path, binding.readScope)) {\r\n throw new ScopeViolationError(r.path, binding.readScope!);\r\n }\r\n // Deep-clone the WHOLE result: `content` (and `meta`) come from the live\r\n // tree by reference; handing them across the agent boundary would let a\r\n // caller mutate the artifact without an event, bypassing write-scope.\r\n return structuredClone(r);\r\n }),\r\n\r\n find: (selector, opts) =>\r\n run(() => {\r\n if (binding.readScope !== undefined && opts?.within !== undefined && !isWithin(opts.within, binding.readScope)) {\r\n throw new ScopeViolationError(opts.within, binding.readScope);\r\n }\r\n return navigator.find(selector, { ...opts, within: opts?.within ?? binding.readScope });\r\n }),\r\n\r\n search: (query, opts = {}) =>\r\n run(async () => {\r\n if (!deps.index) throw new InvalidOpError(\"no semantic index configured for this toolset\");\r\n if (binding.readScope !== undefined && opts.under !== undefined && !isWithin(opts.under, binding.readScope)) {\r\n throw new ScopeViolationError(opts.under, binding.readScope);\r\n }\r\n const under = opts.under ?? binding.readScope;\r\n return deps.index.search(query, { ...opts, under });\r\n }),\r\n\r\n patch: (ref, op) =>\r\n run<PatchResult>(() => {\r\n const common = { owner: binding.owner, writeScope: binding.writeScope, ifVersion: op.ifVersion };\r\n const resolve = (r: Ref) => {\r\n const node = \"id\" in r ? addressing.byId(r.id) : addressing.byPath(r.path);\r\n if (!node) throw new NodeNotFoundError(r);\r\n return node;\r\n };\r\n switch (op.op) {\r\n case \"set\": {\r\n deps.mutator.set(ref, op.value, common);\r\n const node = resolve(ref);\r\n return { id: node.id, path: addressing.pathOf(node.id), version: node.meta.version };\r\n }\r\n case \"insert\": {\r\n const id = deps.mutator.insert(ref, op.key, op.value, common);\r\n const node = tree.get(id)!;\r\n return { id, path: addressing.pathOf(id), version: node.meta.version };\r\n }\r\n case \"remove\": {\r\n const node = resolve(ref);\r\n const removed = { id: node.id, path: addressing.pathOf(node.id) };\r\n const parentId = node.parentId;\r\n deps.mutator.remove(ref, common);\r\n const parent = parentId !== null ? tree.get(parentId) : undefined;\r\n return { id: removed.id, path: removed.path, version: parent?.meta.version ?? 0 };\r\n }\r\n case \"move\": {\r\n const node = resolve(ref);\r\n deps.mutator.move(ref, op.to, op.key, common);\r\n return { id: node.id, path: addressing.pathOf(node.id), version: node.meta.version };\r\n }\r\n case \"edit\": {\r\n const node = resolve(ref);\r\n const path = addressing.pathOf(node.id);\r\n // Scope, then version, then content — the Mutator's own ordering. The\r\n // content checks below read the live value, so they must not run first:\r\n // \"not found\" vs SCOPE_VIOLATION would be a binary-search oracle on\r\n // out-of-scope content, and INVALID_OP on a stale read sends the agent\r\n // chasing a \"wrong quote\" instead of re-getting.\r\n if (binding.writeScope !== undefined && !isWithin(path, binding.writeScope)) {\r\n throw new ScopeViolationError(path, binding.writeScope);\r\n }\r\n if (op.ifVersion !== undefined && node.meta.version !== op.ifVersion) {\r\n throw new StaleVersionError(node.id, op.ifVersion, node.meta.version);\r\n }\r\n const value = tree.toJson(node.id);\r\n if (typeof value !== \"string\") {\r\n const kind =\r\n value === null\r\n ? \"null\"\r\n : Array.isArray(value)\r\n ? \"an array\"\r\n : typeof value === \"object\"\r\n ? \"an object\"\r\n : `a ${typeof value}`;\r\n throw new InvalidOpError(\r\n `edit targets string values; ${path} is ${kind} — target a string field inside it`,\r\n );\r\n }\r\n if (op.old === \"\") throw new InvalidOpError(\"edit: old must be non-empty\");\r\n if (op.old === op.new) throw new InvalidOpError(\"edit: old and new are identical\");\r\n const count = value.split(op.old).length - 1;\r\n if (count === 0) throw new InvalidOpError(`edit: old string not found in ${path}`);\r\n if (count > 1 && !op.replaceAll) {\r\n throw new InvalidOpError(\r\n `edit: old string occurs ${count} times in ${path} — quote a larger unique fragment or set replaceAll`,\r\n );\r\n }\r\n // split/join instead of String.replace: replace() interprets `$&` etc. in the\r\n // replacement string, which would corrupt an exact-substring edit. When count\r\n // is 1 the two are equivalent anyway.\r\n const next = value.split(op.old).join(op.new);\r\n deps.mutator.set(ref, next, common);\r\n const after = resolve(ref);\r\n return { id: after.id, path: addressing.pathOf(after.id), version: after.meta.version };\r\n }\r\n }\r\n }),\r\n\r\n history: (ref, opts = {}) =>\r\n run(() => {\r\n let events = [...deps.log.entries()];\r\n if (ref !== undefined) {\r\n const node = \"id\" in ref ? addressing.byId(ref.id) : addressing.byPath(ref.path);\r\n if (!node) throw new NodeNotFoundError(ref);\r\n const path = addressing.pathOf(node.id);\r\n if (!isWithin(path, binding.readScope)) throw new ScopeViolationError(path, binding.readScope!);\r\n const id = node.id;\r\n events = events.filter((e) => e.targetId === id);\r\n } else if (binding.readScope !== undefined) {\r\n const scope = binding.readScope;\r\n events = events.filter((e) => eventWithinScope(e, scope));\r\n }\r\n return structuredClone(opts.limit !== undefined ? events.slice(-opts.limit) : events);\r\n }),\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
createArbor,
|
|
18
18
|
restoreArbor
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WEO5KBRI.js";
|
|
20
20
|
import {
|
|
21
21
|
makeToolset
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-ZDANDXJ6.js";
|
|
23
23
|
import {
|
|
24
24
|
typeAwareDecision
|
|
25
25
|
} from "./chunk-6DOI4FNX.js";
|
package/dist/toolset.d.ts
CHANGED
|
@@ -24,6 +24,10 @@ type ToolResult<T> = {
|
|
|
24
24
|
message: string;
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
+
/** `edit` is exact-substring surgery on a string leaf — pure sugar over `set`: the event
|
|
28
|
+
* log records a plain `set` with the full before/after value, so history/revert/replay/
|
|
29
|
+
* AG-UI need nothing new. `old` must match the live value exactly (and uniquely, unless
|
|
30
|
+
* `replaceAll`) — `get` the node first and quote `old` from what it returns. */
|
|
27
31
|
type PatchOp = {
|
|
28
32
|
op: "set";
|
|
29
33
|
value: Json;
|
|
@@ -41,6 +45,12 @@ type PatchOp = {
|
|
|
41
45
|
to: Ref;
|
|
42
46
|
key: string | number;
|
|
43
47
|
ifVersion?: number;
|
|
48
|
+
} | {
|
|
49
|
+
op: "edit";
|
|
50
|
+
old: string;
|
|
51
|
+
new: string;
|
|
52
|
+
replaceAll?: boolean;
|
|
53
|
+
ifVersion?: number;
|
|
44
54
|
};
|
|
45
55
|
interface ToolsetDeps {
|
|
46
56
|
tree: ArtifactTree;
|
package/dist/toolset.js
CHANGED
package/package.json
CHANGED
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "arborkit",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Arbor — a shared, typed, versioned JSON artifact tree for multi-agent systems: lazy navigation, scoped agent tools, exact + semantic indexing, reversible event log with time-travel, snapshot + delta persistence.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"agents",
|
|
7
|
-
"multi-agent",
|
|
8
|
-
"shared-state",
|
|
9
|
-
"artifact",
|
|
10
|
-
"json-tree",
|
|
11
|
-
"event-sourcing",
|
|
12
|
-
"time-travel",
|
|
13
|
-
"semantic-search",
|
|
14
|
-
"blackboard",
|
|
15
|
-
"llm"
|
|
16
|
-
],
|
|
17
|
-
"author": "D. A. Pominov <d.a.pominov@gmail.com>",
|
|
18
|
-
"license": "MIT",
|
|
19
|
-
"type": "module",
|
|
20
|
-
"engines": {
|
|
21
|
-
"node": ">=20.6"
|
|
22
|
-
},
|
|
23
|
-
"sideEffects": false,
|
|
24
|
-
"main": "./dist/index.js",
|
|
25
|
-
"module": "./dist/index.js",
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
27
|
-
"exports": {
|
|
28
|
-
".": {
|
|
29
|
-
"types": "./dist/index.d.ts",
|
|
30
|
-
"import": "./dist/index.js"
|
|
31
|
-
},
|
|
32
|
-
"./addressing": {
|
|
33
|
-
"types": "./dist/addressing.d.ts",
|
|
34
|
-
"import": "./dist/addressing.js"
|
|
35
|
-
},
|
|
36
|
-
"./ag-ui": {
|
|
37
|
-
"types": "./dist/ag-ui.d.ts",
|
|
38
|
-
"import": "./dist/ag-ui.js"
|
|
39
|
-
},
|
|
40
|
-
"./arbor": {
|
|
41
|
-
"types": "./dist/arbor.d.ts",
|
|
42
|
-
"import": "./dist/arbor.js"
|
|
43
|
-
},
|
|
44
|
-
"./artifact-tree": {
|
|
45
|
-
"types": "./dist/artifact-tree.d.ts",
|
|
46
|
-
"import": "./dist/artifact-tree.js"
|
|
47
|
-
},
|
|
48
|
-
"./clock": {
|
|
49
|
-
"types": "./dist/clock.d.ts",
|
|
50
|
-
"import": "./dist/clock.js"
|
|
51
|
-
},
|
|
52
|
-
"./decompose": {
|
|
53
|
-
"types": "./dist/decompose.d.ts",
|
|
54
|
-
"import": "./dist/decompose.js"
|
|
55
|
-
},
|
|
56
|
-
"./delta-storage": {
|
|
57
|
-
"types": "./dist/delta-storage.d.ts",
|
|
58
|
-
"import": "./dist/delta-storage.js"
|
|
59
|
-
},
|
|
60
|
-
"./delta": {
|
|
61
|
-
"types": "./dist/delta.d.ts",
|
|
62
|
-
"import": "./dist/delta.js"
|
|
63
|
-
},
|
|
64
|
-
"./embedding-port": {
|
|
65
|
-
"types": "./dist/embedding-port.d.ts",
|
|
66
|
-
"import": "./dist/embedding-port.js"
|
|
67
|
-
},
|
|
68
|
-
"./embedding-text": {
|
|
69
|
-
"types": "./dist/embedding-text.d.ts",
|
|
70
|
-
"import": "./dist/embedding-text.js"
|
|
71
|
-
},
|
|
72
|
-
"./errors": {
|
|
73
|
-
"types": "./dist/errors.d.ts",
|
|
74
|
-
"import": "./dist/errors.js"
|
|
75
|
-
},
|
|
76
|
-
"./event-log": {
|
|
77
|
-
"types": "./dist/event-log.d.ts",
|
|
78
|
-
"import": "./dist/event-log.js"
|
|
79
|
-
},
|
|
80
|
-
"./file-storage": {
|
|
81
|
-
"types": "./dist/file-storage.d.ts",
|
|
82
|
-
"import": "./dist/file-storage.js"
|
|
83
|
-
},
|
|
84
|
-
"./ids": {
|
|
85
|
-
"types": "./dist/ids.d.ts",
|
|
86
|
-
"import": "./dist/ids.js"
|
|
87
|
-
},
|
|
88
|
-
"./json-edit": {
|
|
89
|
-
"types": "./dist/json-edit.d.ts",
|
|
90
|
-
"import": "./dist/json-edit.js"
|
|
91
|
-
},
|
|
92
|
-
"./jsonpointer": {
|
|
93
|
-
"types": "./dist/jsonpointer.d.ts",
|
|
94
|
-
"import": "./dist/jsonpointer.js"
|
|
95
|
-
},
|
|
96
|
-
"./mutator": {
|
|
97
|
-
"types": "./dist/mutator.d.ts",
|
|
98
|
-
"import": "./dist/mutator.js"
|
|
99
|
-
},
|
|
100
|
-
"./navigator": {
|
|
101
|
-
"types": "./dist/navigator.d.ts",
|
|
102
|
-
"import": "./dist/navigator.js"
|
|
103
|
-
},
|
|
104
|
-
"./path-glob": {
|
|
105
|
-
"types": "./dist/path-glob.d.ts",
|
|
106
|
-
"import": "./dist/path-glob.js"
|
|
107
|
-
},
|
|
108
|
-
"./registry-validator": {
|
|
109
|
-
"types": "./dist/registry-validator.d.ts",
|
|
110
|
-
"import": "./dist/registry-validator.js"
|
|
111
|
-
},
|
|
112
|
-
"./replay": {
|
|
113
|
-
"types": "./dist/replay.d.ts",
|
|
114
|
-
"import": "./dist/replay.js"
|
|
115
|
-
},
|
|
116
|
-
"./semantic-index": {
|
|
117
|
-
"types": "./dist/semantic-index.d.ts",
|
|
118
|
-
"import": "./dist/semantic-index.js"
|
|
119
|
-
},
|
|
120
|
-
"./storage": {
|
|
121
|
-
"types": "./dist/storage.d.ts",
|
|
122
|
-
"import": "./dist/storage.js"
|
|
123
|
-
},
|
|
124
|
-
"./toolset": {
|
|
125
|
-
"types": "./dist/toolset.d.ts",
|
|
126
|
-
"import": "./dist/toolset.js"
|
|
127
|
-
},
|
|
128
|
-
"./type-aware-decision": {
|
|
129
|
-
"types": "./dist/type-aware-decision.d.ts",
|
|
130
|
-
"import": "./dist/type-aware-decision.js"
|
|
131
|
-
},
|
|
132
|
-
"./type-registry": {
|
|
133
|
-
"types": "./dist/type-registry.d.ts",
|
|
134
|
-
"import": "./dist/type-registry.js"
|
|
135
|
-
},
|
|
136
|
-
"./types": {
|
|
137
|
-
"types": "./dist/types.d.ts",
|
|
138
|
-
"import": "./dist/types.js"
|
|
139
|
-
},
|
|
140
|
-
"./vector-index-port": {
|
|
141
|
-
"types": "./dist/vector-index-port.d.ts",
|
|
142
|
-
"import": "./dist/vector-index-port.js"
|
|
143
|
-
},
|
|
144
|
-
"./zod-adapter": {
|
|
145
|
-
"types": "./dist/zod-adapter.d.ts",
|
|
146
|
-
"import": "./dist/zod-adapter.js"
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
"files": [
|
|
150
|
-
"dist",
|
|
151
|
-
"CHANGELOG.md"
|
|
152
|
-
],
|
|
153
|
-
"scripts": {
|
|
154
|
-
"build": "tsup",
|
|
155
|
-
"prepack": "npm run build",
|
|
156
|
-
"test": "vitest run",
|
|
157
|
-
"test:watch": "vitest",
|
|
158
|
-
"typecheck": "tsc --noEmit",
|
|
159
|
-
"bench": "tsx bench/bench.ts",
|
|
160
|
-
"example": "tsx examples/content-site.ts"
|
|
161
|
-
},
|
|
162
|
-
"devDependencies": {
|
|
163
|
-
"@types/node": "^20.14.0",
|
|
164
|
-
"tsup": "^8.0.0",
|
|
165
|
-
"tsx": "^4.19.0",
|
|
166
|
-
"typescript": "^5.6.3",
|
|
167
|
-
"vitest": "^2.1.8",
|
|
168
|
-
"zod": "^3.24.0"
|
|
169
|
-
}
|
|
170
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "arborkit",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Arbor — a shared, typed, versioned JSON artifact tree for multi-agent systems: lazy navigation, scoped agent tools, exact + semantic indexing, reversible event log with time-travel, snapshot + delta persistence.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agents",
|
|
7
|
+
"multi-agent",
|
|
8
|
+
"shared-state",
|
|
9
|
+
"artifact",
|
|
10
|
+
"json-tree",
|
|
11
|
+
"event-sourcing",
|
|
12
|
+
"time-travel",
|
|
13
|
+
"semantic-search",
|
|
14
|
+
"blackboard",
|
|
15
|
+
"llm"
|
|
16
|
+
],
|
|
17
|
+
"author": "D. A. Pominov <d.a.pominov@gmail.com>",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20.6"
|
|
22
|
+
},
|
|
23
|
+
"sideEffects": false,
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./addressing": {
|
|
33
|
+
"types": "./dist/addressing.d.ts",
|
|
34
|
+
"import": "./dist/addressing.js"
|
|
35
|
+
},
|
|
36
|
+
"./ag-ui": {
|
|
37
|
+
"types": "./dist/ag-ui.d.ts",
|
|
38
|
+
"import": "./dist/ag-ui.js"
|
|
39
|
+
},
|
|
40
|
+
"./arbor": {
|
|
41
|
+
"types": "./dist/arbor.d.ts",
|
|
42
|
+
"import": "./dist/arbor.js"
|
|
43
|
+
},
|
|
44
|
+
"./artifact-tree": {
|
|
45
|
+
"types": "./dist/artifact-tree.d.ts",
|
|
46
|
+
"import": "./dist/artifact-tree.js"
|
|
47
|
+
},
|
|
48
|
+
"./clock": {
|
|
49
|
+
"types": "./dist/clock.d.ts",
|
|
50
|
+
"import": "./dist/clock.js"
|
|
51
|
+
},
|
|
52
|
+
"./decompose": {
|
|
53
|
+
"types": "./dist/decompose.d.ts",
|
|
54
|
+
"import": "./dist/decompose.js"
|
|
55
|
+
},
|
|
56
|
+
"./delta-storage": {
|
|
57
|
+
"types": "./dist/delta-storage.d.ts",
|
|
58
|
+
"import": "./dist/delta-storage.js"
|
|
59
|
+
},
|
|
60
|
+
"./delta": {
|
|
61
|
+
"types": "./dist/delta.d.ts",
|
|
62
|
+
"import": "./dist/delta.js"
|
|
63
|
+
},
|
|
64
|
+
"./embedding-port": {
|
|
65
|
+
"types": "./dist/embedding-port.d.ts",
|
|
66
|
+
"import": "./dist/embedding-port.js"
|
|
67
|
+
},
|
|
68
|
+
"./embedding-text": {
|
|
69
|
+
"types": "./dist/embedding-text.d.ts",
|
|
70
|
+
"import": "./dist/embedding-text.js"
|
|
71
|
+
},
|
|
72
|
+
"./errors": {
|
|
73
|
+
"types": "./dist/errors.d.ts",
|
|
74
|
+
"import": "./dist/errors.js"
|
|
75
|
+
},
|
|
76
|
+
"./event-log": {
|
|
77
|
+
"types": "./dist/event-log.d.ts",
|
|
78
|
+
"import": "./dist/event-log.js"
|
|
79
|
+
},
|
|
80
|
+
"./file-storage": {
|
|
81
|
+
"types": "./dist/file-storage.d.ts",
|
|
82
|
+
"import": "./dist/file-storage.js"
|
|
83
|
+
},
|
|
84
|
+
"./ids": {
|
|
85
|
+
"types": "./dist/ids.d.ts",
|
|
86
|
+
"import": "./dist/ids.js"
|
|
87
|
+
},
|
|
88
|
+
"./json-edit": {
|
|
89
|
+
"types": "./dist/json-edit.d.ts",
|
|
90
|
+
"import": "./dist/json-edit.js"
|
|
91
|
+
},
|
|
92
|
+
"./jsonpointer": {
|
|
93
|
+
"types": "./dist/jsonpointer.d.ts",
|
|
94
|
+
"import": "./dist/jsonpointer.js"
|
|
95
|
+
},
|
|
96
|
+
"./mutator": {
|
|
97
|
+
"types": "./dist/mutator.d.ts",
|
|
98
|
+
"import": "./dist/mutator.js"
|
|
99
|
+
},
|
|
100
|
+
"./navigator": {
|
|
101
|
+
"types": "./dist/navigator.d.ts",
|
|
102
|
+
"import": "./dist/navigator.js"
|
|
103
|
+
},
|
|
104
|
+
"./path-glob": {
|
|
105
|
+
"types": "./dist/path-glob.d.ts",
|
|
106
|
+
"import": "./dist/path-glob.js"
|
|
107
|
+
},
|
|
108
|
+
"./registry-validator": {
|
|
109
|
+
"types": "./dist/registry-validator.d.ts",
|
|
110
|
+
"import": "./dist/registry-validator.js"
|
|
111
|
+
},
|
|
112
|
+
"./replay": {
|
|
113
|
+
"types": "./dist/replay.d.ts",
|
|
114
|
+
"import": "./dist/replay.js"
|
|
115
|
+
},
|
|
116
|
+
"./semantic-index": {
|
|
117
|
+
"types": "./dist/semantic-index.d.ts",
|
|
118
|
+
"import": "./dist/semantic-index.js"
|
|
119
|
+
},
|
|
120
|
+
"./storage": {
|
|
121
|
+
"types": "./dist/storage.d.ts",
|
|
122
|
+
"import": "./dist/storage.js"
|
|
123
|
+
},
|
|
124
|
+
"./toolset": {
|
|
125
|
+
"types": "./dist/toolset.d.ts",
|
|
126
|
+
"import": "./dist/toolset.js"
|
|
127
|
+
},
|
|
128
|
+
"./type-aware-decision": {
|
|
129
|
+
"types": "./dist/type-aware-decision.d.ts",
|
|
130
|
+
"import": "./dist/type-aware-decision.js"
|
|
131
|
+
},
|
|
132
|
+
"./type-registry": {
|
|
133
|
+
"types": "./dist/type-registry.d.ts",
|
|
134
|
+
"import": "./dist/type-registry.js"
|
|
135
|
+
},
|
|
136
|
+
"./types": {
|
|
137
|
+
"types": "./dist/types.d.ts",
|
|
138
|
+
"import": "./dist/types.js"
|
|
139
|
+
},
|
|
140
|
+
"./vector-index-port": {
|
|
141
|
+
"types": "./dist/vector-index-port.d.ts",
|
|
142
|
+
"import": "./dist/vector-index-port.js"
|
|
143
|
+
},
|
|
144
|
+
"./zod-adapter": {
|
|
145
|
+
"types": "./dist/zod-adapter.d.ts",
|
|
146
|
+
"import": "./dist/zod-adapter.js"
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"files": [
|
|
150
|
+
"dist",
|
|
151
|
+
"CHANGELOG.md"
|
|
152
|
+
],
|
|
153
|
+
"scripts": {
|
|
154
|
+
"build": "tsup",
|
|
155
|
+
"prepack": "npm run build",
|
|
156
|
+
"test": "vitest run",
|
|
157
|
+
"test:watch": "vitest",
|
|
158
|
+
"typecheck": "tsc --noEmit",
|
|
159
|
+
"bench": "tsx bench/bench.ts",
|
|
160
|
+
"example": "tsx examples/content-site.ts"
|
|
161
|
+
},
|
|
162
|
+
"devDependencies": {
|
|
163
|
+
"@types/node": "^20.14.0",
|
|
164
|
+
"tsup": "^8.0.0",
|
|
165
|
+
"tsx": "^4.19.0",
|
|
166
|
+
"typescript": "^5.6.3",
|
|
167
|
+
"vitest": "^2.1.8",
|
|
168
|
+
"zod": "^3.24.0"
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/toolset.ts"],"sourcesContent":["import type { ArtifactTree } from \"./artifact-tree\";\r\nimport type { Addressing } from \"./addressing\";\r\nimport type { EventLog, MutationEvent } from \"./event-log\";\r\nimport type { Mutator } from \"./mutator\";\r\nimport type { SemanticIndex } from \"./semantic-index\";\r\nimport {\r\n Navigator,\r\n type DescribeOpts,\r\n type DescribeResult,\r\n type GetOpts,\r\n type GetResult,\r\n type FindSelector,\r\n type FindOpts,\r\n type FindResult,\r\n} from \"./navigator\";\r\nimport type { SearchOpts, SearchResult } from \"./semantic-index\";\r\nimport { type Ref, ArborError, ScopeViolationError, InvalidOpError, NodeNotFoundError } from \"./errors\";\r\nimport { isWithin } from \"./jsonpointer\";\r\nimport type { Json, NodeId } from \"./types\";\r\n\r\n/** Every toolset call returns this — errors are structured, never thrown across the agent boundary. */\r\nexport type ToolResult<T> = { ok: true; value: T } | { ok: false; error: { code: string; message: string } };\r\n\r\nexport type PatchOp =\r\n | { op: \"set\"; value: Json; ifVersion?: number }\r\n | { op: \"insert\"; key: string | number; value: Json; ifVersion?: number }\r\n | { op: \"remove\"; ifVersion?: number }\r\n | { op: \"move\"; to: Ref; key: string | number; ifVersion?: number };\r\n\r\nexport interface ToolsetDeps {\r\n tree: ArtifactTree;\r\n addressing: Addressing;\r\n log: EventLog;\r\n mutator: Mutator;\r\n index?: SemanticIndex;\r\n}\r\n\r\nexport interface ToolsetBinding {\r\n owner?: string;\r\n /** JSON Pointer prefix: writes must be at or under it (enforced by the Mutator). */\r\n writeScope?: string;\r\n /** JSON Pointer prefix: reads are confined to it. */\r\n readScope?: string;\r\n}\r\n\r\nexport interface PatchResult {\r\n id: NodeId;\r\n path: string;\r\n /** The affected node's version AFTER the op (remove: the parent's). Feed into the next ifVersion. */\r\n version: number;\r\n}\r\n\r\nexport interface Toolset {\r\n describe(ref?: Ref, opts?: DescribeOpts): Promise<ToolResult<DescribeResult>>;\r\n get(ref: Ref, opts?: GetOpts): Promise<ToolResult<GetResult>>;\r\n find(selector: FindSelector, opts?: FindOpts): Promise<ToolResult<FindResult>>;\r\n search(query: string, opts?: SearchOpts): Promise<ToolResult<SearchResult>>;\r\n patch(ref: Ref, op: PatchOp): Promise<ToolResult<PatchResult>>;\r\n history(ref?: Ref, opts?: { limit?: number }): Promise<ToolResult<MutationEvent[]>>;\r\n}\r\n\r\n/** An event is within scope if any of its recorded paths is within scope. */\r\nfunction eventWithinScope(e: MutationEvent, scope: string): boolean {\r\n if (e.path !== undefined && isWithin(e.path, scope)) return true;\r\n if (e.toPath !== undefined && isWithin(e.toPath, scope)) return true;\r\n if (e.fromPath !== undefined && isWithin(e.fromPath, scope)) return true;\r\n return false;\r\n}\r\n\r\n/** Run a tool body, converting thrown errors into a structured ToolResult. */\r\nasync function run<T>(fn: () => T | Promise<T>): Promise<ToolResult<T>> {\r\n try {\r\n return { ok: true, value: await fn() };\r\n } catch (e) {\r\n if (e instanceof ArborError) return { ok: false, error: { code: e.code, message: e.message } };\r\n const message = e instanceof Error ? e.message : String(e);\r\n return { ok: false, error: { code: \"ERROR\", message } };\r\n }\r\n}\r\n\r\n/** A scoped, agent-facing bundle of tools returning structured results. */\r\nexport function makeToolset(deps: ToolsetDeps, binding: ToolsetBinding = {}): Toolset {\r\n const { tree, addressing } = deps;\r\n const navigator = new Navigator(tree, addressing);\r\n\r\n return {\r\n describe: (ref, opts) =>\r\n run(() => {\r\n const target: Ref = ref ?? (binding.readScope !== undefined ? { path: binding.readScope } : { path: \"\" });\r\n const r = navigator.describe(target, opts);\r\n if (!isWithin(r.node.path, binding.readScope)) {\r\n throw new ScopeViolationError(r.node.path, binding.readScope!);\r\n }\r\n return r;\r\n }),\r\n\r\n get: (ref, opts) =>\r\n run(() => {\r\n const r = navigator.get(ref, opts);\r\n if (!isWithin(r.path, binding.readScope)) {\r\n throw new ScopeViolationError(r.path, binding.readScope!);\r\n }\r\n // Deep-clone the WHOLE result: `content` (and `meta`) come from the live\r\n // tree by reference; handing them across the agent boundary would let a\r\n // caller mutate the artifact without an event, bypassing write-scope.\r\n return structuredClone(r);\r\n }),\r\n\r\n find: (selector, opts) =>\r\n run(() => {\r\n if (binding.readScope !== undefined && opts?.within !== undefined && !isWithin(opts.within, binding.readScope)) {\r\n throw new ScopeViolationError(opts.within, binding.readScope);\r\n }\r\n return navigator.find(selector, { ...opts, within: opts?.within ?? binding.readScope });\r\n }),\r\n\r\n search: (query, opts = {}) =>\r\n run(async () => {\r\n if (!deps.index) throw new InvalidOpError(\"no semantic index configured for this toolset\");\r\n if (binding.readScope !== undefined && opts.under !== undefined && !isWithin(opts.under, binding.readScope)) {\r\n throw new ScopeViolationError(opts.under, binding.readScope);\r\n }\r\n const under = opts.under ?? binding.readScope;\r\n return deps.index.search(query, { ...opts, under });\r\n }),\r\n\r\n patch: (ref, op) =>\r\n run<PatchResult>(() => {\r\n const common = { owner: binding.owner, writeScope: binding.writeScope, ifVersion: op.ifVersion };\r\n const resolve = (r: Ref) => {\r\n const node = \"id\" in r ? addressing.byId(r.id) : addressing.byPath(r.path);\r\n if (!node) throw new NodeNotFoundError(r);\r\n return node;\r\n };\r\n switch (op.op) {\r\n case \"set\": {\r\n deps.mutator.set(ref, op.value, common);\r\n const node = resolve(ref);\r\n return { id: node.id, path: addressing.pathOf(node.id), version: node.meta.version };\r\n }\r\n case \"insert\": {\r\n const id = deps.mutator.insert(ref, op.key, op.value, common);\r\n const node = tree.get(id)!;\r\n return { id, path: addressing.pathOf(id), version: node.meta.version };\r\n }\r\n case \"remove\": {\r\n const node = resolve(ref);\r\n const removed = { id: node.id, path: addressing.pathOf(node.id) };\r\n const parentId = node.parentId;\r\n deps.mutator.remove(ref, common);\r\n const parent = parentId !== null ? tree.get(parentId) : undefined;\r\n return { id: removed.id, path: removed.path, version: parent?.meta.version ?? 0 };\r\n }\r\n case \"move\": {\r\n const node = resolve(ref);\r\n deps.mutator.move(ref, op.to, op.key, common);\r\n return { id: node.id, path: addressing.pathOf(node.id), version: node.meta.version };\r\n }\r\n }\r\n }),\r\n\r\n history: (ref, opts = {}) =>\r\n run(() => {\r\n let events = [...deps.log.entries()];\r\n if (ref !== undefined) {\r\n const node = \"id\" in ref ? addressing.byId(ref.id) : addressing.byPath(ref.path);\r\n if (!node) throw new NodeNotFoundError(ref);\r\n const path = addressing.pathOf(node.id);\r\n if (!isWithin(path, binding.readScope)) throw new ScopeViolationError(path, binding.readScope!);\r\n const id = node.id;\r\n events = events.filter((e) => e.targetId === id);\r\n } else if (binding.readScope !== undefined) {\r\n const scope = binding.readScope;\r\n events = events.filter((e) => eventWithinScope(e, scope));\r\n }\r\n return structuredClone(opts.limit !== undefined ? events.slice(-opts.limit) : events);\r\n }),\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
|
|
File without changes
|