@graview/tools 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.
- package/LICENSE +96 -0
- package/README.md +47 -0
- package/dist/agent/adapters.d.ts +43 -0
- package/dist/agent/adapters.d.ts.map +1 -0
- package/dist/agent/adapters.js +52 -0
- package/dist/agent/adapters.js.map +1 -0
- package/dist/agent/tools.d.ts +105 -0
- package/dist/agent/tools.d.ts.map +1 -0
- package/dist/agent/tools.js +311 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.d.ts +37 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +306 -0
- package/dist/cli.js.map +1 -0
- package/dist/conversation.d.ts +108 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/conversation.js +698 -0
- package/dist/conversation.js.map +1 -0
- package/dist/decide.d.ts +43 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +125 -0
- package/dist/decide.js.map +1 -0
- package/dist/derive.d.ts +84 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +302 -0
- package/dist/derive.js.map +1 -0
- package/dist/edit.d.ts +70 -0
- package/dist/edit.d.ts.map +1 -0
- package/dist/edit.js +97 -0
- package/dist/edit.js.map +1 -0
- package/dist/figure.d.ts +53 -0
- package/dist/figure.d.ts.map +1 -0
- package/dist/figure.js +86 -0
- package/dist/figure.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/intelligence.d.ts +132 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +463 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/local.d.ts +192 -0
- package/dist/local.d.ts.map +1 -0
- package/dist/local.js +365 -0
- package/dist/local.js.map +1 -0
- package/dist/loop.d.ts +84 -0
- package/dist/loop.d.ts.map +1 -0
- package/dist/loop.js +173 -0
- package/dist/loop.js.map +1 -0
- package/dist/mcp-stdio.d.ts +38 -0
- package/dist/mcp-stdio.d.ts.map +1 -0
- package/dist/mcp-stdio.js +83 -0
- package/dist/mcp-stdio.js.map +1 -0
- package/dist/pins.d.ts +29 -0
- package/dist/pins.d.ts.map +1 -0
- package/dist/pins.js +60 -0
- package/dist/pins.js.map +1 -0
- package/dist/plan.d.ts +150 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +303 -0
- package/dist/plan.js.map +1 -0
- package/dist/providers/insight.d.ts +14 -0
- package/dist/providers/insight.d.ts.map +1 -0
- package/dist/providers/insight.js +81 -0
- package/dist/providers/insight.js.map +1 -0
- package/dist/providers/invariant.d.ts +14 -0
- package/dist/providers/invariant.d.ts.map +1 -0
- package/dist/providers/invariant.js +123 -0
- package/dist/providers/invariant.js.map +1 -0
- package/dist/providers/jev.d.ts +115 -0
- package/dist/providers/jev.d.ts.map +1 -0
- package/dist/providers/jev.js +148 -0
- package/dist/providers/jev.js.map +1 -0
- package/dist/providers/lens.d.ts +24 -0
- package/dist/providers/lens.d.ts.map +1 -0
- package/dist/providers/lens.js +36 -0
- package/dist/providers/lens.js.map +1 -0
- package/dist/providers/llm.d.ts +25 -0
- package/dist/providers/llm.d.ts.map +1 -0
- package/dist/providers/llm.js +44 -0
- package/dist/providers/llm.js.map +1 -0
- package/dist/providers/schema.d.ts +12 -0
- package/dist/providers/schema.d.ts.map +1 -0
- package/dist/providers/schema.js +508 -0
- package/dist/providers/schema.js.map +1 -0
- package/dist/providers/structure.d.ts +14 -0
- package/dist/providers/structure.d.ts.map +1 -0
- package/dist/providers/structure.js +211 -0
- package/dist/providers/structure.js.map +1 -0
- package/dist/questions.d.ts +162 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +316 -0
- package/dist/questions.js.map +1 -0
- package/dist/run.d.ts +202 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +453 -0
- package/dist/run.js.map +1 -0
- package/dist/space.d.ts +50 -0
- package/dist/space.d.ts.map +1 -0
- package/dist/space.js +55 -0
- package/dist/space.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/usage.d.ts +10 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +59 -0
- package/dist/usage.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Elastic License 2.0
|
|
2
|
+
|
|
3
|
+
## Acceptance
|
|
4
|
+
|
|
5
|
+
By using the software, you agree to all of the terms and conditions below.
|
|
6
|
+
|
|
7
|
+
## Copyright License
|
|
8
|
+
|
|
9
|
+
The licensor grants you a non-exclusive, royalty-free, worldwide,
|
|
10
|
+
non-sublicensable, non-transferable license to use, copy, distribute, make
|
|
11
|
+
available, and prepare derivative works of the software, in each case subject
|
|
12
|
+
to the limitations and conditions below.
|
|
13
|
+
|
|
14
|
+
## Limitations
|
|
15
|
+
|
|
16
|
+
You may not provide the software to third parties as a hosted or managed
|
|
17
|
+
service, where the service provides users with access to any substantial set
|
|
18
|
+
of the features or functionality of the software.
|
|
19
|
+
|
|
20
|
+
You may not move, change, disable, or circumvent the license key
|
|
21
|
+
functionality in the software, and you may not remove or obscure any
|
|
22
|
+
functionality in the software that is protected by the license key.
|
|
23
|
+
|
|
24
|
+
You may not alter, remove, or obscure any licensing, copyright, or other
|
|
25
|
+
notices of the licensor in the software. Any use of the licensor's trademarks
|
|
26
|
+
is subject to applicable law.
|
|
27
|
+
|
|
28
|
+
## Patents
|
|
29
|
+
|
|
30
|
+
The licensor grants you a license, under any patent claims the licensor can
|
|
31
|
+
license, or becomes able to license, to make, have made, use, sell, offer for
|
|
32
|
+
sale, import and have imported the software, in each case subject to the
|
|
33
|
+
limitations and conditions in this license. This license does not cover any
|
|
34
|
+
patent claims that you cause to be infringed by modifications or additions to
|
|
35
|
+
the software. If you or your company make any written claim that the software
|
|
36
|
+
infringes or contributes to infringement of any patent, your patent license
|
|
37
|
+
for the software granted under these terms ends immediately. If your company
|
|
38
|
+
makes such a claim, your patent license ends immediately for work on behalf
|
|
39
|
+
of your company.
|
|
40
|
+
|
|
41
|
+
## Notices
|
|
42
|
+
|
|
43
|
+
You must ensure that anyone who gets a copy of any part of the software from
|
|
44
|
+
you also gets a copy of these terms.
|
|
45
|
+
|
|
46
|
+
If you modify the software, you must include in any modified copies of the
|
|
47
|
+
software prominent notices stating that you have modified the software.
|
|
48
|
+
|
|
49
|
+
## No Other Rights
|
|
50
|
+
|
|
51
|
+
These terms do not imply any licenses other than those expressly granted in
|
|
52
|
+
these terms.
|
|
53
|
+
|
|
54
|
+
## Termination
|
|
55
|
+
|
|
56
|
+
If you use the software in violation of these terms, such use is not
|
|
57
|
+
licensed, and your licenses will automatically terminate. If the licensor
|
|
58
|
+
provides you with a notice of your violation, and you cease all violation of
|
|
59
|
+
this license no later than 30 days after you receive that notice, your
|
|
60
|
+
licenses will be reinstated retroactively. However, if you violate these
|
|
61
|
+
terms after such reinstatement, any additional violation of these terms will
|
|
62
|
+
cause your licenses to terminate automatically and permanently.
|
|
63
|
+
|
|
64
|
+
## No Liability
|
|
65
|
+
|
|
66
|
+
*As far as the law allows, the software comes as is, without any warranty or
|
|
67
|
+
condition, and the licensor will not be liable to you for any damages arising
|
|
68
|
+
out of these terms or the use or nature of the software, under any kind of
|
|
69
|
+
legal claim.*
|
|
70
|
+
|
|
71
|
+
## Definitions
|
|
72
|
+
|
|
73
|
+
The **licensor** is the entity offering these terms, and the **software** is
|
|
74
|
+
the software the licensor makes available under these terms, including any
|
|
75
|
+
portion of it.
|
|
76
|
+
|
|
77
|
+
**you** refers to the individual or entity agreeing to these terms.
|
|
78
|
+
|
|
79
|
+
**your company** is any legal entity, sole proprietorship, or other kind of
|
|
80
|
+
organization that you work for, plus all organizations that have control
|
|
81
|
+
over, are under the control of, or are under common control with that
|
|
82
|
+
organization. **control** means ownership of substantially all the assets of
|
|
83
|
+
an entity, or the power to direct its management and policies by vote,
|
|
84
|
+
contract, or otherwise. Control can be direct or indirect.
|
|
85
|
+
|
|
86
|
+
**your licenses** are all the licenses granted to you for the software under
|
|
87
|
+
these terms.
|
|
88
|
+
|
|
89
|
+
**use** means anything you do with the software requiring one of your
|
|
90
|
+
licenses.
|
|
91
|
+
|
|
92
|
+
**trademark** means trademarks, service marks, and similar rights.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
Copyright 2025-2026 En Dash Consulting
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @graview/tools
|
|
2
|
+
|
|
3
|
+
What can legally be done with a selection, and the agent surface that shares it.
|
|
4
|
+
|
|
5
|
+
Nobody authors an affordance. Providers notice things — a violation and the
|
|
6
|
+
repairs it names, a mutation whose subject accepts every selected kind, a
|
|
7
|
+
neighbour all but one of them share — and the results merge into one ranked
|
|
8
|
+
set. An LLM is one optional provider among these rather than the mechanism.
|
|
9
|
+
|
|
10
|
+
`createToolRuntime` generates a tool per mutation from the same declarations,
|
|
11
|
+
so an external agent over MCP and a seat inside the interface use literally the
|
|
12
|
+
same actions and produce literally the same diffs. That is why watching an
|
|
13
|
+
agent work needs no bespoke observability layer.
|
|
14
|
+
|
|
15
|
+
Read-only calls report the nodes they looked at, which is the half a diff
|
|
16
|
+
cannot show. A seat holding a principal gets tools for what that principal may
|
|
17
|
+
run, and is told plainly about the ones it may not.
|
|
18
|
+
|
|
19
|
+
## The host: `graview mcp` and `graview apply`
|
|
20
|
+
|
|
21
|
+
An external agent — an editor's assistant, a worker on a schedule — used to
|
|
22
|
+
edit the seed file, because the seed was the only thing it could reach. These
|
|
23
|
+
two commands put the runtime where the data is, so it evolves the live graph
|
|
24
|
+
the way a person does: through `store.apply`, under its own seat, judged by the
|
|
25
|
+
same policy, logged with its name.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
graview mcp ./dist/domain/app.js --data ./data --as cursor --roles keeper
|
|
29
|
+
graview mcp ./dist/domain/app.js --remote-url https://host.example/app --header "authorization: Bearer …"
|
|
30
|
+
graview mcp ./dist/domain/app.js --list --roles keeper # the seat's tools as tools/list JSON
|
|
31
|
+
|
|
32
|
+
graview apply ./dist/domain/app.js --data ./data --roles keeper \
|
|
33
|
+
--call add-task --args '{"listId":"today","label":"Book the van","id":"t-van"}'
|
|
34
|
+
graview apply ./dist/domain/app.js --data ./data --roles keeper --plan ./plan.json --preview
|
|
35
|
+
graview apply ./dist/domain/app.js --data ./data --roles keeper --undo batch:7
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`mcp` speaks MCP over stdio — JSON-RPC, one message per line, no SDK — around
|
|
39
|
+
`createToolRuntime` and `createMcpAdapter`; a reply waits for the write to
|
|
40
|
+
land, or for the server's verdict against a remote, so "done" is never said
|
|
41
|
+
before it is true. `apply` is one act, a plan of many as one batch (`[{
|
|
42
|
+
mutation, args, as? }]`, a later call naming an earlier one's node as
|
|
43
|
+
`{ "$plan": "<as>" }`), or a take-back; `--preview` writes nothing. Both take
|
|
44
|
+
the store backends `graview serve` takes — `--data`, `--sqlite`,
|
|
45
|
+
`--remote-url` — and the seat flags `--as` and `--roles`. Every act that
|
|
46
|
+
creates a kind accepts an optional `id` for the node it makes; every kind has
|
|
47
|
+
a derived `remove-<kind>`, permitted through the acts that create it.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AnySchema, GraphDiff, NodeOfSchema } from "@graview/core";
|
|
2
|
+
import type { ToolRuntime } from "./tools.js";
|
|
3
|
+
/** The shape an MCP server expects from `tools/list`. */
|
|
4
|
+
export interface McpTool {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly title?: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly inputSchema: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
export interface McpContent {
|
|
11
|
+
readonly type: "text";
|
|
12
|
+
readonly text: string;
|
|
13
|
+
}
|
|
14
|
+
export interface McpToolResult {
|
|
15
|
+
readonly content: readonly McpContent[];
|
|
16
|
+
readonly isError?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Exposes the runtime to an external agent (Claude Code, Cursor) over MCP.
|
|
20
|
+
*
|
|
21
|
+
* This is a TRANSPORT, not a second implementation: it renames fields and
|
|
22
|
+
* stringifies results, and that is all it does. The moment it starts making
|
|
23
|
+
* decisions the two agent surfaces have begun to diverge.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createMcpAdapter<S extends AnySchema>(runtime: ToolRuntime<S>): {
|
|
26
|
+
listTools(): McpTool[];
|
|
27
|
+
callTool(name: string, args?: Record<string, unknown>): Promise<McpToolResult>;
|
|
28
|
+
};
|
|
29
|
+
export interface InAppAgent<S extends AnySchema> {
|
|
30
|
+
readonly tools: ToolRuntime<S>["definitions"];
|
|
31
|
+
run(name: string, args?: Record<string, unknown>): Promise<unknown>;
|
|
32
|
+
/** Watch every change, whoever caused it. */
|
|
33
|
+
watch(listener: (diff: GraphDiff<NodeOfSchema<S>>) => void): () => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The same runtime, driven from a surface inside the interface.
|
|
37
|
+
*
|
|
38
|
+
* Both adapters emit the same diff stream, which is the whole point: a human
|
|
39
|
+
* edit and an agent edit are indistinguishable downstream, so the renderer
|
|
40
|
+
* highlights an agent's work with exactly the code that highlights yours.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createInAppAdapter<S extends AnySchema>(runtime: ToolRuntime<S>): InAppAgent<S>;
|
|
43
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../../src/agent/adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,yDAAyD;AACzD,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;iBAE5D,OAAO,EAAE;mBAUd,MAAM,SACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,aAAa,CAAC;EAY5B;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS,SAAS;IAC7C,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAC9C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,6CAA6C;IAC7C,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACzE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,SAAS,EACpD,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GACtB,UAAU,CAAC,CAAC,CAAC,CAYf"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exposes the runtime to an external agent (Claude Code, Cursor) over MCP.
|
|
3
|
+
*
|
|
4
|
+
* This is a TRANSPORT, not a second implementation: it renames fields and
|
|
5
|
+
* stringifies results, and that is all it does. The moment it starts making
|
|
6
|
+
* decisions the two agent surfaces have begun to diverge.
|
|
7
|
+
*/
|
|
8
|
+
export function createMcpAdapter(runtime) {
|
|
9
|
+
return {
|
|
10
|
+
listTools() {
|
|
11
|
+
return runtime.definitions.map((tool) => ({
|
|
12
|
+
name: tool.name,
|
|
13
|
+
...(tool.title === undefined ? {} : { title: tool.title }),
|
|
14
|
+
description: tool.description,
|
|
15
|
+
inputSchema: tool.inputSchema,
|
|
16
|
+
}));
|
|
17
|
+
},
|
|
18
|
+
async callTool(name, args = {}) {
|
|
19
|
+
const result = await runtime.call(name, args);
|
|
20
|
+
if (!result.ok) {
|
|
21
|
+
return { content: [{ type: "text", text: result.error }], isError: true };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{ type: "text", text: JSON.stringify({ data: result.data, diff: result.diff }, null, 2) },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The same runtime, driven from a surface inside the interface.
|
|
33
|
+
*
|
|
34
|
+
* Both adapters emit the same diff stream, which is the whole point: a human
|
|
35
|
+
* edit and an agent edit are indistinguishable downstream, so the renderer
|
|
36
|
+
* highlights an agent's work with exactly the code that highlights yours.
|
|
37
|
+
*/
|
|
38
|
+
export function createInAppAdapter(runtime) {
|
|
39
|
+
return {
|
|
40
|
+
tools: runtime.definitions,
|
|
41
|
+
async run(name, args = {}) {
|
|
42
|
+
const result = await runtime.call(name, args);
|
|
43
|
+
if (!result.ok)
|
|
44
|
+
throw new Error(result.error);
|
|
45
|
+
return result.data;
|
|
46
|
+
},
|
|
47
|
+
watch(listener) {
|
|
48
|
+
return runtime.onDiff(listener);
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=adapters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.js","sourceRoot":"","sources":["../../src/agent/adapters.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAsB,OAAuB;IAC3E,OAAO;QACL,SAAS;YACP,OAAO,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACxC,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC1D,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;aAC9B,CAAC,CAAC,CAAC;QACN,CAAC;QAED,KAAK,CAAC,QAAQ,CACZ,IAAY,EACZ,OAAgC,EAAE;YAElC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YAC5E,CAAC;YACD,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;iBAC1F;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AASD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAuB;IAEvB,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,WAAW;QAC1B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE;YACvB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC9C,OAAO,MAAM,CAAC,IAAI,CAAC;QACrB,CAAC;QACD,KAAK,CAAC,QAAQ;YACZ,OAAO,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type AnySchema, type GraphDiff, type JsonSchema, type NodeOfSchema, type Place, type Principal, type Store } from "@graview/core";
|
|
2
|
+
import { applyAffordance, type DeriveOptions } from "../derive.js";
|
|
3
|
+
export interface ToolDefinition {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly title?: string;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
readonly inputSchema: JsonSchema;
|
|
8
|
+
/** False for the read tools, true for anything that changes the graph. */
|
|
9
|
+
readonly mutating: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type ToolResult<S extends AnySchema> = {
|
|
12
|
+
readonly ok: true;
|
|
13
|
+
readonly data: unknown;
|
|
14
|
+
readonly diff?: GraphDiff<NodeOfSchema<S>>;
|
|
15
|
+
/**
|
|
16
|
+
* The nodes this call LOOKED AT.
|
|
17
|
+
*
|
|
18
|
+
* A diff can only ever show what changed, and an agent that reassigns
|
|
19
|
+
* one run after reading the whole week is doing something different
|
|
20
|
+
* from one that reassigns it after reading nothing. The runtime is the
|
|
21
|
+
* only place that knows, so it says so — and an interface can then draw
|
|
22
|
+
* attention as well as change.
|
|
23
|
+
*/
|
|
24
|
+
readonly reads?: readonly string[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly ok: false;
|
|
27
|
+
readonly error: string;
|
|
28
|
+
};
|
|
29
|
+
export interface ToolRuntimeOptions<S extends AnySchema> {
|
|
30
|
+
/**
|
|
31
|
+
* Who this seat acts as. A principal is an author with roles, so the seat's
|
|
32
|
+
* attribution and its authorisation are the same fact — there is no way to
|
|
33
|
+
* write as one participant and be permitted as another.
|
|
34
|
+
*/
|
|
35
|
+
readonly author?: Principal;
|
|
36
|
+
/**
|
|
37
|
+
* Options for the seat's own deriveAffordances calls. A FUNCTION is read
|
|
38
|
+
* fresh on every call — which is how a person's pins, toggled in the
|
|
39
|
+
* menu after this runtime was built, still reach the agent's tool list.
|
|
40
|
+
* The strip, the pointer menu and the seat must never disagree about
|
|
41
|
+
* the same acts.
|
|
42
|
+
*/
|
|
43
|
+
readonly derive?: DeriveOptions<S> | (() => DeriveOptions<S>);
|
|
44
|
+
/** Refuse every mutating tool. Useful for a read-only agent seat. */
|
|
45
|
+
readonly readOnly?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The places the app's pictures name, so `search_graph` can answer "where
|
|
48
|
+
* do I go for X" as well as "where is X". The store cannot see pictures;
|
|
49
|
+
* whoever built the runtime beside a view registry can.
|
|
50
|
+
*/
|
|
51
|
+
readonly places?: readonly Place[] | (() => readonly Place[]);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One tool call, as it happens.
|
|
55
|
+
*
|
|
56
|
+
* The claim was that watching an agent needs no bespoke observability
|
|
57
|
+
* because its edits produce the same diffs a human's do. True, and not
|
|
58
|
+
* enough: a diff says what changed, never what was CONSIDERED. An agent that
|
|
59
|
+
* reads six nodes and then reassigns one run appears, through diffs alone,
|
|
60
|
+
* as a single unexplained write — the interface can only offer a spinner and
|
|
61
|
+
* a toast. Emitting the calls themselves is what turns that into something a
|
|
62
|
+
* person can follow, and read-only calls are the interesting half.
|
|
63
|
+
*/
|
|
64
|
+
export interface ToolCall {
|
|
65
|
+
readonly name: string;
|
|
66
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
67
|
+
readonly mutating: boolean;
|
|
68
|
+
/** `running` on the way in; `ok` or `failed` on the way out. */
|
|
69
|
+
readonly phase: "running" | "ok" | "failed";
|
|
70
|
+
readonly error?: string;
|
|
71
|
+
readonly at: string;
|
|
72
|
+
/**
|
|
73
|
+
* The nodes a settled read-only call looked at. Absent while running, and
|
|
74
|
+
* absent for a mutating call — a change already reports its own reads
|
|
75
|
+
* through the op log, and reporting them twice would double-count.
|
|
76
|
+
*/
|
|
77
|
+
readonly reads?: readonly string[];
|
|
78
|
+
}
|
|
79
|
+
export interface ToolRuntime<S extends AnySchema> {
|
|
80
|
+
readonly definitions: readonly ToolDefinition[];
|
|
81
|
+
/**
|
|
82
|
+
* Who this seat writes as.
|
|
83
|
+
*
|
|
84
|
+
* Exposed because a read has to be attributed to the SAME participant the
|
|
85
|
+
* writes are, or one agent looking at the graph and then changing it reads
|
|
86
|
+
* as two people editing at once.
|
|
87
|
+
*/
|
|
88
|
+
readonly author?: ToolRuntimeOptions<S>["author"];
|
|
89
|
+
call(name: string, args: Record<string, unknown>): Promise<ToolResult<S>>;
|
|
90
|
+
/** Every applied change, whoever caused it. */
|
|
91
|
+
onDiff(listener: (diff: GraphDiff<NodeOfSchema<S>>) => void): () => void;
|
|
92
|
+
/** Every call, mutating or not, as it starts and as it settles. */
|
|
93
|
+
onCall(listener: (call: ToolCall) => void): () => void;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Tool definitions generate ONCE from the schema and are transport-agnostic.
|
|
97
|
+
*
|
|
98
|
+
* The MCP adapter and the in-app adapter are two thin wrappers over the same
|
|
99
|
+
* runtime, so an external agent and a surface inside the interface are using
|
|
100
|
+
* literally the same actions and emitting literally the same diffs. That is
|
|
101
|
+
* why watching an agent work needs no bespoke observability layer.
|
|
102
|
+
*/
|
|
103
|
+
export declare function createToolRuntime<S extends AnySchema>(store: Store<S>, options?: ToolRuntimeOptions<S>): ToolRuntime<S>;
|
|
104
|
+
export { applyAffordance };
|
|
105
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/agent/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { mutationToolSchema, search, } from "@graview/core";
|
|
2
|
+
import { deriveAffordances, applyAffordance, } from "../derive.js";
|
|
3
|
+
const READ_TOOLS = [
|
|
4
|
+
{
|
|
5
|
+
name: "search_graph",
|
|
6
|
+
description: "Find things by name: records whose name or readable fields carry the words, and the kinds, places and rules the words name — each with why it matched. Reach for this before get_graph when you know what something is called. Words match the start of words, case and accents aside; key:value tokens narrow as a list's filter does (done:false, is:any for past records, kind:<kind>). Pass subject (a node id) to get the acts you may run on it as hits too.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
query: { type: "string", description: "Words, with optional key:value conditions." },
|
|
11
|
+
limit: { type: "number", description: "Most hits to return; 20 when unsaid." },
|
|
12
|
+
subject: { type: "string", description: "A node id whose acts to include." },
|
|
13
|
+
},
|
|
14
|
+
required: ["query"],
|
|
15
|
+
additionalProperties: false,
|
|
16
|
+
},
|
|
17
|
+
mutating: false,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "get_graph",
|
|
21
|
+
description: "Read the whole graph: every node with its fields, and every edge. Start here when you need the shape of the domain rather than one thing in it.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {},
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
},
|
|
27
|
+
mutating: false,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "get_node",
|
|
31
|
+
description: "Read one node, its edges, and the violations that implicate it. Use this to check a thing before you change it.",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: { id: { type: "string", description: "Node id." } },
|
|
35
|
+
required: ["id"],
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
},
|
|
38
|
+
mutating: false,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "get_violations",
|
|
42
|
+
description: "List every invariant violation the graph currently has, with the repairs that would resolve each one. Read this before proposing work: a rule that is already broken is more urgent than anything you could add.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
context: {
|
|
47
|
+
type: "object",
|
|
48
|
+
description: "Evaluation context, e.g. { weekStart }.",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
},
|
|
53
|
+
mutating: false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "get_affordances",
|
|
57
|
+
description: "Ask what can legally be done with a selection, ranked. Prefer these over composing a mutation by hand: they are derived from the schema, the invariants and the shape of the graph, so they cannot name an action that does not exist.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
selection: {
|
|
62
|
+
type: "array",
|
|
63
|
+
items: { type: "string" },
|
|
64
|
+
description: "Node ids.",
|
|
65
|
+
},
|
|
66
|
+
context: { type: "object" },
|
|
67
|
+
},
|
|
68
|
+
required: ["selection"],
|
|
69
|
+
additionalProperties: false,
|
|
70
|
+
},
|
|
71
|
+
mutating: false,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "preview_mutation",
|
|
75
|
+
description: "Try a mutation without applying it: get back the diff it would produce and any invariant it would break. Do this when you are unsure, rather than applying and undoing.",
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
mutation: { type: "string" },
|
|
80
|
+
args: { type: "object" },
|
|
81
|
+
},
|
|
82
|
+
required: ["mutation", "args"],
|
|
83
|
+
additionalProperties: false,
|
|
84
|
+
},
|
|
85
|
+
mutating: false,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "undo_batch",
|
|
89
|
+
description: "Undo one batch of operations. Fails, naming the blocking operation, when a later operation read what it wrote.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
batch: { type: "string" },
|
|
94
|
+
include: { type: "array", items: { type: "string" } },
|
|
95
|
+
},
|
|
96
|
+
required: ["batch"],
|
|
97
|
+
additionalProperties: false,
|
|
98
|
+
},
|
|
99
|
+
mutating: true,
|
|
100
|
+
},
|
|
101
|
+
];
|
|
102
|
+
/**
|
|
103
|
+
* Tool definitions generate ONCE from the schema and are transport-agnostic.
|
|
104
|
+
*
|
|
105
|
+
* The MCP adapter and the in-app adapter are two thin wrappers over the same
|
|
106
|
+
* runtime, so an external agent and a surface inside the interface are using
|
|
107
|
+
* literally the same actions and emitting literally the same diffs. That is
|
|
108
|
+
* why watching an agent work needs no bespoke observability layer.
|
|
109
|
+
*/
|
|
110
|
+
export function createToolRuntime(store, options = {}) {
|
|
111
|
+
/*
|
|
112
|
+
* Only what this seat MAY do.
|
|
113
|
+
*
|
|
114
|
+
* The narrowing is the store's, not the runtime's: a seat holding a
|
|
115
|
+
* principal gets tools for what that principal can run, and there is no
|
|
116
|
+
* second list to keep in step with the policy. A seat that then calls a
|
|
117
|
+
* mutation it was not given still hits the store's refusal, because the
|
|
118
|
+
* schema is a convenience and the enforcement is elsewhere.
|
|
119
|
+
*/
|
|
120
|
+
const mutationTools = store
|
|
121
|
+
.permittedMutations(options.author ?? { kind: "agent" })
|
|
122
|
+
.map((mutation) => {
|
|
123
|
+
const tool = mutationToolSchema(mutation);
|
|
124
|
+
return {
|
|
125
|
+
name: mutation.name,
|
|
126
|
+
...(tool.title === undefined ? {} : { title: tool.title }),
|
|
127
|
+
description: tool.description,
|
|
128
|
+
inputSchema: tool.inputSchema,
|
|
129
|
+
mutating: true,
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
const definitions = options.readOnly
|
|
133
|
+
? [...READ_TOOLS.filter((tool) => !tool.mutating)]
|
|
134
|
+
: [...READ_TOOLS, ...mutationTools];
|
|
135
|
+
/**
|
|
136
|
+
* The call itself, separated from the announcing so that every exit —
|
|
137
|
+
* including an early return for an unknown tool — is reported exactly once.
|
|
138
|
+
*/
|
|
139
|
+
const run = async (name, args) => {
|
|
140
|
+
try {
|
|
141
|
+
const definition = definitions.find((tool) => tool.name === name);
|
|
142
|
+
if (!definition) {
|
|
143
|
+
/*
|
|
144
|
+
* A tool this seat may not use EXISTS, and saying "unknown" would be
|
|
145
|
+
* a lie an agent then reasons from — it would conclude the capability
|
|
146
|
+
* is missing and go looking for a workaround. The same honesty the
|
|
147
|
+
* interface owes a person: it is there, you may not use it, here is
|
|
148
|
+
* who can.
|
|
149
|
+
*/
|
|
150
|
+
const withheld = store.allMutations().find((mutation) => mutation.name === name);
|
|
151
|
+
if (withheld) {
|
|
152
|
+
const verdict = store.permits({ name, args }, options.author ?? { kind: "agent" });
|
|
153
|
+
if (!verdict.ok)
|
|
154
|
+
return { ok: false, error: verdict.refusal.message };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
ok: false,
|
|
158
|
+
error: `Unknown tool "${name}". Available: ${definitions.map((t) => t.name).join(", ")}`,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (definition.mutating && options.readOnly) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
error: `"${name}" changes the graph, and this seat is read-only.`,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
switch (name) {
|
|
168
|
+
case "search_graph": {
|
|
169
|
+
const places = typeof options.places === "function" ? options.places() : options.places;
|
|
170
|
+
const subject = typeof args["subject"] === "string" ? args["subject"] : undefined;
|
|
171
|
+
const found = search(store, String(args["query"] ?? ""), {
|
|
172
|
+
principal: options.author ?? { kind: "agent" },
|
|
173
|
+
limit: typeof args["limit"] === "number" ? args["limit"] : 20,
|
|
174
|
+
...(places ? { places } : {}),
|
|
175
|
+
...(subject ? { subject, from: [subject] } : {}),
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
ok: true,
|
|
179
|
+
data: found,
|
|
180
|
+
// What came back was looked at: the records named, and nothing else.
|
|
181
|
+
reads: found.hits.flatMap((hit) => (hit.about === "node" ? [hit.id] : [])),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case "get_graph": {
|
|
185
|
+
const snapshot = store.graph.snapshot();
|
|
186
|
+
return {
|
|
187
|
+
ok: true,
|
|
188
|
+
data: snapshot,
|
|
189
|
+
reads: snapshot.nodes.map((node) => node.id),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
case "get_node": {
|
|
193
|
+
const id = String(args["id"] ?? "");
|
|
194
|
+
const node = store.graph.getNode(id);
|
|
195
|
+
if (!node)
|
|
196
|
+
return { ok: false, error: `No node "${id}".` };
|
|
197
|
+
const out = store.graph.outEdges(id);
|
|
198
|
+
const inbound = store.graph.inEdges(id);
|
|
199
|
+
return {
|
|
200
|
+
ok: true,
|
|
201
|
+
data: {
|
|
202
|
+
node,
|
|
203
|
+
out,
|
|
204
|
+
in: inbound,
|
|
205
|
+
violations: store
|
|
206
|
+
.violations()
|
|
207
|
+
.filter((violation) => violation.nodeIds.includes(id)),
|
|
208
|
+
},
|
|
209
|
+
// Asking about a node is asking about its neighbourhood: the
|
|
210
|
+
// answer names them, so looking at it looked at them.
|
|
211
|
+
reads: [id, ...out.map((edge) => edge.to), ...inbound.map((edge) => edge.from)],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
case "get_violations":
|
|
215
|
+
{
|
|
216
|
+
const violations = store.violations(args["context"]);
|
|
217
|
+
return {
|
|
218
|
+
ok: true,
|
|
219
|
+
data: violations,
|
|
220
|
+
reads: [...new Set(violations.flatMap((violation) => violation.nodeIds))],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
case "get_affordances": {
|
|
224
|
+
const selection = args["selection"] ?? [];
|
|
225
|
+
const derived = deriveAffordances(store, selection, {
|
|
226
|
+
...(typeof options.derive === "function" ? options.derive() : options.derive),
|
|
227
|
+
// The seat asks as ITSELF, so what it is offered is what it may
|
|
228
|
+
// do — and what it may not is stated rather than hidden, which
|
|
229
|
+
// is how an agent learns a capability exists that it lacks.
|
|
230
|
+
...(options.author ? { principal: options.author } : {}),
|
|
231
|
+
...(args["context"]
|
|
232
|
+
? { context: args["context"] }
|
|
233
|
+
: {}),
|
|
234
|
+
});
|
|
235
|
+
return { ok: true, data: derived, reads: selection };
|
|
236
|
+
}
|
|
237
|
+
case "preview_mutation": {
|
|
238
|
+
const preview = store.preview({
|
|
239
|
+
name: String(args["mutation"]),
|
|
240
|
+
args: args["args"] ?? {},
|
|
241
|
+
});
|
|
242
|
+
return { ok: true, data: preview };
|
|
243
|
+
}
|
|
244
|
+
case "undo_batch": {
|
|
245
|
+
const batch = String(args["batch"]);
|
|
246
|
+
const include = args["include"] ?? [];
|
|
247
|
+
const check = store.canUndo([batch, ...include]);
|
|
248
|
+
if (!check.ok)
|
|
249
|
+
return { ok: false, error: check.message };
|
|
250
|
+
const result = store.undo([batch, ...include], {
|
|
251
|
+
...(options.author ? { author: options.author } : {}),
|
|
252
|
+
});
|
|
253
|
+
return { ok: true, data: result, diff: result.diff };
|
|
254
|
+
}
|
|
255
|
+
default: {
|
|
256
|
+
const result = store.apply({ name, args }, { ...(options.author ? { author: options.author } : {}) });
|
|
257
|
+
return {
|
|
258
|
+
ok: true,
|
|
259
|
+
data: {
|
|
260
|
+
batch: result.batch,
|
|
261
|
+
intent: result.intent,
|
|
262
|
+
introduces: result.introduces,
|
|
263
|
+
resolves: result.resolves,
|
|
264
|
+
},
|
|
265
|
+
diff: result.diff,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
error: error instanceof Error ? error.message : String(error),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const watchers = new Set();
|
|
278
|
+
const announce = (call) => {
|
|
279
|
+
for (const watcher of watchers)
|
|
280
|
+
watcher(call);
|
|
281
|
+
};
|
|
282
|
+
return {
|
|
283
|
+
definitions,
|
|
284
|
+
...(options.author ? { author: options.author } : {}),
|
|
285
|
+
onDiff(listener) {
|
|
286
|
+
return store.subscribe(listener);
|
|
287
|
+
},
|
|
288
|
+
onCall(listener) {
|
|
289
|
+
watchers.add(listener);
|
|
290
|
+
return () => watchers.delete(listener);
|
|
291
|
+
},
|
|
292
|
+
async call(name, args) {
|
|
293
|
+
const mutating = definitions.find((tool) => tool.name === name)?.mutating ?? false;
|
|
294
|
+
const started = { name, args, mutating, at: new Date().toISOString() };
|
|
295
|
+
announce({ ...started, phase: "running" });
|
|
296
|
+
const settle = (result) => {
|
|
297
|
+
announce(result.ok
|
|
298
|
+
? {
|
|
299
|
+
...started,
|
|
300
|
+
phase: "ok",
|
|
301
|
+
...(!mutating && result.reads ? { reads: result.reads } : {}),
|
|
302
|
+
}
|
|
303
|
+
: { ...started, phase: "failed", error: result.error });
|
|
304
|
+
return result;
|
|
305
|
+
};
|
|
306
|
+
return settle(await run(name, args));
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
export { applyAffordance };
|
|
311
|
+
//# sourceMappingURL=tools.js.map
|