@roam-research/roam-tools-core 0.5.0 → 0.6.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/README.md +23 -8
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/operations/blocks.d.ts +25 -20
- package/dist/operations/blocks.d.ts.map +1 -1
- package/dist/operations/blocks.js +66 -15
- package/dist/operations/datalog.d.ts +15 -0
- package/dist/operations/datalog.d.ts.map +1 -0
- package/dist/operations/datalog.js +16 -0
- package/dist/operations/files.d.ts +6 -7
- package/dist/operations/files.d.ts.map +1 -1
- package/dist/operations/files.js +18 -5
- package/dist/operations/navigation.d.ts +9 -10
- package/dist/operations/navigation.d.ts.map +1 -1
- package/dist/operations/navigation.js +4 -1
- package/dist/operations/pages.d.ts +15 -13
- package/dist/operations/pages.d.ts.map +1 -1
- package/dist/operations/pages.js +34 -10
- package/dist/operations/query.d.ts +10 -11
- package/dist/operations/query.d.ts.map +1 -1
- package/dist/operations/query.js +24 -6
- package/dist/operations/search.d.ts +7 -8
- package/dist/operations/search.d.ts.map +1 -1
- package/dist/operations/search.js +22 -6
- package/dist/tools.d.ts +37 -4
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +91 -80
- package/dist/types.d.ts +11 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +14 -4
- package/package.json +7 -10
- package/dist/client.d.ts +0 -34
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -275
- package/dist/connect.d.ts +0 -10
- package/dist/connect.d.ts.map +0 -1
- package/dist/connect.js +0 -477
- package/dist/graph-resolver.d.ts +0 -54
- package/dist/graph-resolver.d.ts.map +0 -1
- package/dist/graph-resolver.js +0 -338
- package/dist/operations/graphs.d.ts +0 -26
- package/dist/operations/graphs.d.ts.map +0 -1
- package/dist/operations/graphs.js +0 -214
- package/dist/roam-api.d.ts +0 -32
- package/dist/roam-api.d.ts.map +0 -1
- package/dist/roam-api.js +0 -50
package/dist/operations/pages.js
CHANGED
|
@@ -4,12 +4,22 @@ import { textResult } from "../types.js";
|
|
|
4
4
|
export const CreatePageSchema = z.object({
|
|
5
5
|
title: z.string().describe("Page title"),
|
|
6
6
|
markdown: z.string().optional().describe("Markdown content for the page"),
|
|
7
|
-
uid: z
|
|
7
|
+
uid: z
|
|
8
|
+
.string()
|
|
9
|
+
.optional()
|
|
10
|
+
.describe("Custom UID to assign to the new page. Omit to let Roam auto-generate one (recommended)"),
|
|
11
|
+
childrenViewType: z
|
|
12
|
+
.enum(["document", "bullet", "numbered"])
|
|
13
|
+
.optional()
|
|
14
|
+
.describe("How children are displayed (document, bullet, or numbered)"),
|
|
8
15
|
});
|
|
9
16
|
export const GetPageSchema = z.object({
|
|
10
17
|
title: z.string().optional().describe("Page title (alternative to uid)"),
|
|
11
18
|
uid: z.string().optional().describe("Page UID"),
|
|
12
|
-
maxDepth: z.coerce
|
|
19
|
+
maxDepth: z.coerce
|
|
20
|
+
.number()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Max depth of children to include in markdown (omit for full tree)"),
|
|
13
23
|
});
|
|
14
24
|
export const DeletePageSchema = z.object({
|
|
15
25
|
uid: z.string().describe("Page UID to delete"),
|
|
@@ -17,21 +27,31 @@ export const DeletePageSchema = z.object({
|
|
|
17
27
|
export const UpdatePageSchema = z.object({
|
|
18
28
|
uid: z.string().describe("Page UID"),
|
|
19
29
|
title: z.string().optional().describe("New page title"),
|
|
20
|
-
childrenViewType: z
|
|
21
|
-
|
|
30
|
+
childrenViewType: z
|
|
31
|
+
.enum(["document", "bullet", "numbered"])
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("How children are displayed (document, bullet, or numbered)"),
|
|
34
|
+
mergePages: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.optional()
|
|
37
|
+
.describe("If true, merge with existing page when renaming to a title that already exists (default: false)"),
|
|
22
38
|
});
|
|
23
39
|
export const GetGuidelinesSchema = z.object({});
|
|
24
40
|
export async function createPage(client, params) {
|
|
41
|
+
const page = { title: params.title };
|
|
42
|
+
if (params.uid !== undefined)
|
|
43
|
+
page.uid = params.uid;
|
|
44
|
+
if (params.childrenViewType !== undefined)
|
|
45
|
+
page["children-view-type"] = params.childrenViewType;
|
|
25
46
|
const response = await client.call("data.page.fromMarkdown", [
|
|
26
|
-
{
|
|
27
|
-
page: { title: params.title, uid: params.uid },
|
|
28
|
-
"markdown-string": params.markdown,
|
|
29
|
-
},
|
|
47
|
+
{ page, "markdown-string": params.markdown },
|
|
30
48
|
]);
|
|
31
49
|
return textResult(response.result ?? { uid: "" });
|
|
32
50
|
}
|
|
33
51
|
export async function getPage(client, params) {
|
|
34
|
-
const apiParams = params.uid
|
|
52
|
+
const apiParams = params.uid
|
|
53
|
+
? { uid: params.uid }
|
|
54
|
+
: { title: params.title };
|
|
35
55
|
if (params.maxDepth !== undefined)
|
|
36
56
|
apiParams.maxDepth = params.maxDepth;
|
|
37
57
|
const response = await client.call("data.ai.getPage", [apiParams]);
|
|
@@ -55,7 +75,11 @@ export async function updatePage(client, params) {
|
|
|
55
75
|
}
|
|
56
76
|
export async function getGuidelines(client) {
|
|
57
77
|
const response = await client.call("data.ai.getGraphGuidelines", []);
|
|
58
|
-
const result = response.result ?? {
|
|
78
|
+
const result = response.result ?? {
|
|
79
|
+
guidelines: null,
|
|
80
|
+
starredPages: [],
|
|
81
|
+
todaysDailyNotePage: null,
|
|
82
|
+
};
|
|
59
83
|
const dnpTitle = result.todaysDailyNotePage;
|
|
60
84
|
const nextSteps = dnpTitle
|
|
61
85
|
? `Start by reading today's daily note page ("${dnpTitle}") with get_page — this is the user's primary workspace for the day. If you need more context, call search with an empty query for recently edited and viewed content. Skip these orientation steps only when the user has already given you a specific task to execute (e.g. "create a page called X").`
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
3
|
-
import type { CallToolResult } from "../types.js";
|
|
2
|
+
import type { CallToolResult, RoamActionClient } from "../types.js";
|
|
4
3
|
export declare const QuerySchema: z.ZodObject<{
|
|
5
4
|
uid: z.ZodOptional<z.ZodString>;
|
|
6
5
|
query: z.ZodOptional<z.ZodString>;
|
|
@@ -13,22 +12,22 @@ export declare const QuerySchema: z.ZodObject<{
|
|
|
13
12
|
}, "strip", z.ZodTypeAny, {
|
|
14
13
|
sort?: "created-date" | "edited-date" | "daily-note-date" | undefined;
|
|
15
14
|
uid?: string | undefined;
|
|
16
|
-
|
|
17
|
-
sortOrder?: "asc" | "desc" | undefined;
|
|
18
|
-
includePath?: boolean | undefined;
|
|
15
|
+
maxDepth?: number | undefined;
|
|
19
16
|
offset?: number | undefined;
|
|
20
17
|
limit?: number | undefined;
|
|
21
|
-
|
|
18
|
+
sortOrder?: "asc" | "desc" | undefined;
|
|
19
|
+
includePath?: boolean | undefined;
|
|
20
|
+
query?: string | undefined;
|
|
22
21
|
}, {
|
|
23
22
|
sort?: "created-date" | "edited-date" | "daily-note-date" | undefined;
|
|
24
23
|
uid?: string | undefined;
|
|
25
|
-
|
|
26
|
-
sortOrder?: "asc" | "desc" | undefined;
|
|
27
|
-
includePath?: boolean | undefined;
|
|
24
|
+
maxDepth?: number | undefined;
|
|
28
25
|
offset?: number | undefined;
|
|
29
26
|
limit?: number | undefined;
|
|
30
|
-
|
|
27
|
+
sortOrder?: "asc" | "desc" | undefined;
|
|
28
|
+
includePath?: boolean | undefined;
|
|
29
|
+
query?: string | undefined;
|
|
31
30
|
}>;
|
|
32
31
|
export type QueryParams = z.infer<typeof QuerySchema>;
|
|
33
|
-
export declare function query(client:
|
|
32
|
+
export declare function query(client: RoamActionClient, params: QueryParams): Promise<CallToolResult>;
|
|
34
33
|
//# sourceMappingURL=query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/operations/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/operations/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAiB,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAKnF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BtB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,wBAAsB,KAAK,CACzB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,cAAc,CAAC,CA2BzB"}
|
package/dist/operations/query.js
CHANGED
|
@@ -3,14 +3,32 @@ import { textResult } from "../types.js";
|
|
|
3
3
|
// Schema for executing Roam queries ({{query: }} or {{[[query]]: }} blocks, NOT Datalog)
|
|
4
4
|
// Supports two modes: UID mode (execute existing query block) or Query mode (raw query string)
|
|
5
5
|
export const QuerySchema = z.object({
|
|
6
|
-
uid: z
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
uid: z
|
|
7
|
+
.string()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("UID of a block containing {{query: ...}} or {{[[query]]: ...}} - uses the block's saved display settings and filters"),
|
|
10
|
+
query: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Raw Roam query string (e.g., "{and: [[TODO]] {not: [[DONE]]}}") - NOT Datalog - results are flat list, no user filters applied'),
|
|
14
|
+
sort: z
|
|
15
|
+
.enum(["created-date", "edited-date", "daily-note-date"])
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Sort order (only for query mode, default: created-date)"),
|
|
18
|
+
sortOrder: z
|
|
19
|
+
.enum(["asc", "desc"])
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Sort direction (only for query mode, default: desc)"),
|
|
22
|
+
includePath: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Include breadcrumb path in results (only for query mode, default: true)"),
|
|
11
26
|
offset: z.coerce.number().optional().describe("Skip first N results (default: 0)"),
|
|
12
27
|
limit: z.coerce.number().optional().describe("Max results to return (default: 20)"),
|
|
13
|
-
maxDepth: z.coerce
|
|
28
|
+
maxDepth: z.coerce
|
|
29
|
+
.number()
|
|
30
|
+
.optional()
|
|
31
|
+
.describe("Max depth of children to include in markdown (default: 1)"),
|
|
14
32
|
});
|
|
15
33
|
export async function query(client, params) {
|
|
16
34
|
// Validate: exactly one of uid or query must be provided
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
3
|
-
import type { CallToolResult } from "../types.js";
|
|
2
|
+
import type { CallToolResult, RoamActionClient } from "../types.js";
|
|
4
3
|
export declare const SearchSchema: z.ZodObject<{
|
|
5
4
|
query: z.ZodString;
|
|
6
5
|
scope: z.ZodOptional<z.ZodEnum<["pages", "blocks", "all"]>>;
|
|
@@ -10,17 +9,17 @@ export declare const SearchSchema: z.ZodObject<{
|
|
|
10
9
|
maxDepth: z.ZodOptional<z.ZodNumber>;
|
|
11
10
|
}, "strip", z.ZodTypeAny, {
|
|
12
11
|
query: string;
|
|
13
|
-
|
|
12
|
+
maxDepth?: number | undefined;
|
|
14
13
|
offset?: number | undefined;
|
|
15
14
|
limit?: number | undefined;
|
|
16
|
-
|
|
15
|
+
includePath?: boolean | undefined;
|
|
17
16
|
scope?: "pages" | "blocks" | "all" | undefined;
|
|
18
17
|
}, {
|
|
19
18
|
query: string;
|
|
20
|
-
|
|
19
|
+
maxDepth?: number | undefined;
|
|
21
20
|
offset?: number | undefined;
|
|
22
21
|
limit?: number | undefined;
|
|
23
|
-
|
|
22
|
+
includePath?: boolean | undefined;
|
|
24
23
|
scope?: "pages" | "blocks" | "all" | undefined;
|
|
25
24
|
}>;
|
|
26
25
|
export declare const SearchTemplatesSchema: z.ZodObject<{
|
|
@@ -32,6 +31,6 @@ export declare const SearchTemplatesSchema: z.ZodObject<{
|
|
|
32
31
|
}>;
|
|
33
32
|
export type SearchParams = z.infer<typeof SearchSchema>;
|
|
34
33
|
export type SearchTemplatesParams = z.infer<typeof SearchTemplatesSchema>;
|
|
35
|
-
export declare function search(client:
|
|
36
|
-
export declare function searchTemplates(client:
|
|
34
|
+
export declare function search(client: RoamActionClient, params: SearchParams): Promise<CallToolResult>;
|
|
35
|
+
export declare function searchTemplates(client: RoamActionClient, params: SearchTemplatesParams): Promise<CallToolResult>;
|
|
37
36
|
//# sourceMappingURL=search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/operations/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/operations/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAIV,cAAc,EACd,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAIrB,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;EAoBvB,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;EAOhC,CAAC;AAGH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AACxD,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAE1E,wBAAsB,MAAM,CAC1B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,cAAc,CAAC,CAczB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAKzB"}
|
|
@@ -2,15 +2,29 @@ import { z } from "zod";
|
|
|
2
2
|
import { textResult } from "../types.js";
|
|
3
3
|
// Schemas
|
|
4
4
|
export const SearchSchema = z.object({
|
|
5
|
-
query: z
|
|
6
|
-
|
|
5
|
+
query: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe("Search query — use empty string to get recently edited and viewed content"),
|
|
8
|
+
scope: z
|
|
9
|
+
.enum(["pages", "blocks", "all"])
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Search scope: 'pages' for page titles only, 'blocks' for block content only, 'all' for both (default: 'all')"),
|
|
7
12
|
offset: z.coerce.number().optional().describe("Skip first N results (default: 0)"),
|
|
8
13
|
limit: z.coerce.number().optional().describe("Max results (default: 20)"),
|
|
9
|
-
includePath: z
|
|
10
|
-
|
|
14
|
+
includePath: z
|
|
15
|
+
.boolean()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Include breadcrumb path to each result (default: true)"),
|
|
18
|
+
maxDepth: z.coerce
|
|
19
|
+
.number()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Max depth of children to include in markdown (default: 0)"),
|
|
11
22
|
});
|
|
12
23
|
export const SearchTemplatesSchema = z.object({
|
|
13
|
-
query: z
|
|
24
|
+
query: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Keywords to filter templates by name (case-insensitive). Try relevant keywords first before listing all."),
|
|
14
28
|
});
|
|
15
29
|
export async function search(client, params) {
|
|
16
30
|
const apiParams = {
|
|
@@ -22,7 +36,9 @@ export async function search(client, params) {
|
|
|
22
36
|
};
|
|
23
37
|
if (params.maxDepth !== undefined)
|
|
24
38
|
apiParams.maxDepth = params.maxDepth;
|
|
25
|
-
const response = await client.call("data.ai.search", [
|
|
39
|
+
const response = await client.call("data.ai.search", [
|
|
40
|
+
apiParams,
|
|
41
|
+
]);
|
|
26
42
|
return textResult(response.result ?? { total: 0, results: [] });
|
|
27
43
|
}
|
|
28
44
|
export async function searchTemplates(client, params) {
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import type { CallToolResult } from "./types.js";
|
|
3
|
-
import { RoamClient } from "./client.js";
|
|
2
|
+
import type { CallToolResult, AccessLevel, RoamActionClient, ToolGraph } from "./types.js";
|
|
4
3
|
export interface ClientToolDefinition {
|
|
5
4
|
name: string;
|
|
6
5
|
description: string;
|
|
7
6
|
schema: z.ZodObject<z.ZodRawShape>;
|
|
8
|
-
action: (client:
|
|
7
|
+
action: (client: RoamActionClient, args: unknown) => Promise<CallToolResult>;
|
|
9
8
|
type: "client";
|
|
10
9
|
}
|
|
11
10
|
export interface StandaloneToolDefinition {
|
|
@@ -16,7 +15,41 @@ export interface StandaloneToolDefinition {
|
|
|
16
15
|
type: "standalone";
|
|
17
16
|
}
|
|
18
17
|
export type ToolDefinition = ClientToolDefinition | StandaloneToolDefinition;
|
|
18
|
+
export declare function defineTool<T extends z.ZodRawShape>(name: string, description: string, schema: z.ZodObject<T>, action: (client: RoamActionClient, args: z.infer<z.ZodObject<T>>) => Promise<CallToolResult>): ClientToolDefinition;
|
|
19
|
+
export declare function defineStandaloneTool<T extends z.ZodRawShape>(name: string, description: string, schema: z.ZodObject<T>, action: (args: z.infer<z.ZodObject<T>>) => Promise<CallToolResult>): StandaloneToolDefinition;
|
|
20
|
+
export declare const dataTools: ClientToolDefinition[];
|
|
21
|
+
export declare const desktopUiTools: ClientToolDefinition[];
|
|
22
|
+
export declare const contentTools: ClientToolDefinition[];
|
|
19
23
|
export declare const tools: ToolDefinition[];
|
|
20
24
|
export declare function findTool(name: string): ToolDefinition | undefined;
|
|
21
|
-
export
|
|
25
|
+
export interface RouteToolCallOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a graph identifier (nickname/name) to a ToolGraph. Required.
|
|
28
|
+
* Local consumers use the resolver from @roam-research/roam-tools-local;
|
|
29
|
+
* hosted consumers wire their own (e.g., reading picker grants from RTDB).
|
|
30
|
+
*/
|
|
31
|
+
resolveGraph: (providedGraph?: string) => Promise<ToolGraph>;
|
|
32
|
+
/**
|
|
33
|
+
* Construct a client for the resolved graph. Required.
|
|
34
|
+
* Local consumers return a RoamClient; hosted consumers return a transport
|
|
35
|
+
* that talks to proxy.api.roamresearch.com.
|
|
36
|
+
*/
|
|
37
|
+
createClient: (graph: ToolGraph) => Promise<RoamActionClient> | RoamActionClient;
|
|
38
|
+
/**
|
|
39
|
+
* "local-sync" runs the desktop token-info side-flow on get_graph_guidelines:
|
|
40
|
+
* parallel getTokenInfo, access-level validation, status writes, and result
|
|
41
|
+
* enrichment. "skip" (default) disables that side-flow entirely. Graph-name
|
|
42
|
+
* prefix (prependGraphInfo) is unaffected by this mode and runs in both.
|
|
43
|
+
*/
|
|
44
|
+
tokenInfoMode?: "local-sync" | "skip";
|
|
45
|
+
/**
|
|
46
|
+
* Only consulted in local-sync mode. Hosted callers may use this to write to
|
|
47
|
+
* their own grant store. If omitted, status changes are not persisted.
|
|
48
|
+
*/
|
|
49
|
+
onTokenStatusUpdate?: (nickname: string, patch: {
|
|
50
|
+
accessLevel?: AccessLevel;
|
|
51
|
+
lastKnownTokenStatus?: "active" | "revoked";
|
|
52
|
+
}) => Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
export declare function routeToolCall(toolName: string, args: Record<string, unknown>, options: RouteToolCallOptions): Promise<CallToolResult>;
|
|
22
55
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { RoamError } from "./types.js";
|
|
3
|
-
import { RoamClient } from "./client.js";
|
|
4
|
-
import { resolveGraph, getPort, updateGraphTokenStatus } from "./graph-resolver.js";
|
|
5
3
|
import { CreatePageSchema, GetPageSchema, DeletePageSchema, UpdatePageSchema, GetGuidelinesSchema, createPage, getPage, deletePage, updatePage, getGuidelines, } from "./operations/pages.js";
|
|
6
4
|
import { CreateBlockSchema, GetBlockSchema, UpdateBlockSchema, DeleteBlockSchema, MoveBlockSchema, GetBacklinksSchema, AddCommentSchema, GetCommentsSchema, createBlock, getBlock, updateBlock, deleteBlock, moveBlock, getBacklinks, addComment, getComments, } from "./operations/blocks.js";
|
|
7
|
-
import { SearchSchema, SearchTemplatesSchema, search, searchTemplates } from "./operations/search.js";
|
|
5
|
+
import { SearchSchema, SearchTemplatesSchema, search, searchTemplates, } from "./operations/search.js";
|
|
8
6
|
import { QuerySchema, query } from "./operations/query.js";
|
|
7
|
+
import { DatalogQuerySchema, datalogQuery } from "./operations/datalog.js";
|
|
9
8
|
import { GetOpenWindowsSchema, GetSelectionSchema, OpenMainWindowSchema, OpenSidebarSchema, getOpenWindows, getSelection, openMainWindow, openSidebar, } from "./operations/navigation.js";
|
|
10
|
-
import { FileGetSchema, FileUploadSchema, FileDeleteSchema, getFile, uploadFile, deleteFile } from "./operations/files.js";
|
|
11
|
-
import { ListGraphsSchema, SetupNewGraphSchema, listGraphs, setupNewGraph } from "./operations/graphs.js";
|
|
9
|
+
import { FileGetSchema, FileUploadSchema, FileDeleteSchema, getFile, uploadFile, deleteFile, } from "./operations/files.js";
|
|
12
10
|
// Common schema for graph parameter (used by most tools)
|
|
13
11
|
const GraphSchema = z.object({
|
|
14
|
-
graph: z
|
|
12
|
+
graph: z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("Graph nickname or name (optional - auto-selects if only one graph is configured)"),
|
|
15
16
|
});
|
|
16
17
|
// Helper to extend any schema with graph parameter
|
|
17
18
|
function withGraph(schema) {
|
|
18
19
|
return schema.extend(GraphSchema.shape);
|
|
19
20
|
}
|
|
20
21
|
// Helper to create tool with graph parameter
|
|
21
|
-
function defineTool(name, description, schema, action) {
|
|
22
|
+
export function defineTool(name, description, schema, action) {
|
|
22
23
|
return {
|
|
23
24
|
name,
|
|
24
25
|
description,
|
|
@@ -28,7 +29,7 @@ function defineTool(name, description, schema, action) {
|
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
// Helper to create standalone tool (no graph parameter, handles its own resolution)
|
|
31
|
-
function defineStandaloneTool(name, description, schema, action) {
|
|
32
|
+
export function defineStandaloneTool(name, description, schema, action) {
|
|
32
33
|
return {
|
|
33
34
|
name,
|
|
34
35
|
description,
|
|
@@ -37,43 +38,57 @@ function defineStandaloneTool(name, description, schema, action) {
|
|
|
37
38
|
type: "standalone",
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
|
-
// Graph Management Tools (standalone - handle their own resolution)
|
|
41
|
-
const graphManagementTools = [
|
|
42
|
-
defineStandaloneTool("list_graphs", "List all configured graphs with their nicknames. Also provides setup instructions for connecting additional graphs.", ListGraphsSchema, listGraphs),
|
|
43
|
-
defineStandaloneTool("setup_new_graph", "Set up a new Roam graph for access, or list available graphs. Call without arguments to see which graphs are available in Roam Desktop. Call with graph and nickname to connect a specific graph — ask the user what they'd like to call the graph before choosing a nickname. The user will see an approval dialog in Roam desktop app and must approve the token request. If the graph is already configured, returns the existing configuration without making changes.", SetupNewGraphSchema, setupNewGraph),
|
|
44
|
-
];
|
|
45
41
|
// Note appended to all client tool descriptions
|
|
46
42
|
const GUIDELINES_NOTE = "\n\nNote: Call get_graph_guidelines first when starting to work with a graph.";
|
|
47
|
-
//
|
|
48
|
-
const
|
|
43
|
+
// Data Tools (require graph/client; reusable across local + hosted MCP transports)
|
|
44
|
+
export const dataTools = [
|
|
49
45
|
defineTool("get_graph_guidelines", "IMPORTANT: Call this tool first when starting to work with a graph, before performing any other operations. Returns user-defined instructions and preferences for AI agents. The user may have specified naming conventions, preferred structures, or constraints that should guide your behavior. After receiving the response, follow the nextSteps field — it contains orientation actions you should take before proceeding.", GetGuidelinesSchema, getGuidelines),
|
|
50
46
|
defineTool("create_page", "Create a new page in Roam, optionally with markdown content." + GUIDELINES_NOTE, CreatePageSchema, createPage),
|
|
51
|
-
defineTool("create_block", "Create blocks from markdown content. Target by parentUid, pageTitle, or dailyNotePage (page created if needed). Use nestUnder to insert under a specific child block. Supports nested bulleted lists via markdown indentation." +
|
|
47
|
+
defineTool("create_block", "Create blocks from markdown content. Target by parentUid, pageTitle, or dailyNotePage (page created if needed). Use nestUnder to insert under a specific child block. Supports nested bulleted lists via markdown indentation." +
|
|
48
|
+
GUIDELINES_NOTE, CreateBlockSchema, createBlock),
|
|
52
49
|
defineTool("update_block", "Update an existing block's content or properties." + GUIDELINES_NOTE, UpdateBlockSchema, updateBlock),
|
|
53
50
|
defineTool("delete_block", "Delete a block and all its children." + GUIDELINES_NOTE, DeleteBlockSchema, deleteBlock),
|
|
54
51
|
defineTool("move_block", "Move a block to a new location." + GUIDELINES_NOTE, MoveBlockSchema, moveBlock),
|
|
55
|
-
defineTool("add_comment", "Add a comment to a block (comment thread, NOT a child block). Prefer `comment` for simple text; use `commentMarkdown` for structured content. Same-day calls on the same block append to your existing comment." +
|
|
56
|
-
|
|
52
|
+
defineTool("add_comment", "Add a comment to a block (comment thread, NOT a child block). Prefer `comment` for simple text; use `commentMarkdown` for structured content. Same-day calls on the same block append to your existing comment." +
|
|
53
|
+
GUIDELINES_NOTE, AddCommentSchema, addComment),
|
|
54
|
+
defineTool("get_comments", "Get comments on a block with author, timestamps, and edit info. If singleEditableUid is set, the comment can be edited with update_block. Only works for blocks, not pages." +
|
|
55
|
+
GUIDELINES_NOTE, GetCommentsSchema, getComments),
|
|
57
56
|
defineTool("delete_page", "Delete a page and all its contents." + GUIDELINES_NOTE, DeletePageSchema, deletePage),
|
|
58
|
-
defineTool("update_page", "Update a page's title or children view type. Set mergePages to true if renaming to a title that already exists." +
|
|
59
|
-
|
|
60
|
-
defineTool("
|
|
61
|
-
|
|
62
|
-
defineTool("
|
|
63
|
-
|
|
64
|
-
defineTool("
|
|
57
|
+
defineTool("update_page", "Update a page's title or children view type. Set mergePages to true if renaming to a title that already exists." +
|
|
58
|
+
GUIDELINES_NOTE, UpdatePageSchema, updatePage),
|
|
59
|
+
defineTool("search", "Search for pages and blocks by text. Returns paginated results with markdown content and optional breadcrumb paths. Call with an empty query to get recently edited and viewed content — useful for understanding what the user is currently working on." +
|
|
60
|
+
GUIDELINES_NOTE, SearchSchema, search),
|
|
61
|
+
defineTool("search_templates", "Search Roam templates by name. When the user mentions 'my X template' or 'the X template', use this tool to find it. Templates are user-created reusable content blocks tagged with [[roam/templates]]. Returns template name, uid, and content as markdown." +
|
|
62
|
+
GUIDELINES_NOTE, SearchTemplatesSchema, searchTemplates),
|
|
63
|
+
defineTool("roam_query", 'Execute a Roam query ({{query: }} or {{[[query]]: }} blocks, NOT Datalog). Two modes: (1) UID mode - pass a block UID containing a query component to run it with saved settings/filters; (2) Query mode - pass a raw query string like "{and: [[TODO]] {not: [[DONE]]}}". Returns paginated results with markdown content.' +
|
|
64
|
+
GUIDELINES_NOTE, QuerySchema, query),
|
|
65
|
+
defineTool("datalog_query", "Execute a datomic-style datalog query against the graph's datascript database. Supported clauses: :find, :where, :in, and :timeout (ms). Inputs are positional parameters bound to :in variables after $. Write specific :where clauses to keep results bounded." +
|
|
66
|
+
GUIDELINES_NOTE, DatalogQuerySchema, datalogQuery),
|
|
67
|
+
defineTool("get_page", "Get a page's content as markdown. Returns content with <roam> metadata tags containing UIDs - use these for follow-up operations but strip them when showing content to the user. Show remaining content verbatim, never paraphrase. Use maxDepth for large pages." +
|
|
68
|
+
GUIDELINES_NOTE, GetPageSchema, getPage),
|
|
69
|
+
defineTool("get_block", "Get a block's content as markdown. Returns content with <roam> metadata tags containing UIDs - use these for follow-up operations but strip them when showing content to the user. Show remaining content verbatim, never paraphrase. Use maxDepth for large blocks." +
|
|
70
|
+
GUIDELINES_NOTE, GetBlockSchema, getBlock),
|
|
71
|
+
defineTool("get_backlinks", "Get paginated backlinks (linked references) for a page or block, formatted as markdown. Returns total count and results with optional breadcrumb paths." +
|
|
72
|
+
GUIDELINES_NOTE, GetBacklinksSchema, getBacklinks),
|
|
73
|
+
];
|
|
74
|
+
// Desktop UI Tools (require local Roam Desktop — file ops + window/selection introspection;
|
|
75
|
+
// hosted MCP omits these because the parameters/effects assume a local environment).
|
|
76
|
+
export const desktopUiTools = [
|
|
65
77
|
defineTool("get_open_windows", "Get the current view in the main window and all open sidebar windows." + GUIDELINES_NOTE, GetOpenWindowsSchema, getOpenWindows),
|
|
66
78
|
defineTool("get_selection", "Get the currently focused block and any multi-selected blocks." + GUIDELINES_NOTE, GetSelectionSchema, getSelection),
|
|
67
79
|
defineTool("open_main_window", "Navigate to a page or block in the main window." + GUIDELINES_NOTE, OpenMainWindowSchema, openMainWindow),
|
|
68
80
|
defineTool("open_sidebar", "Open a page or block in the right sidebar." + GUIDELINES_NOTE, OpenSidebarSchema, openSidebar),
|
|
69
81
|
defineTool("file_get", "Fetch a file hosted on Roam (handles decryption for encrypted graphs)." + GUIDELINES_NOTE, FileGetSchema, getFile),
|
|
70
|
-
defineTool("file_upload", "Upload a file to Roam. Returns the Firebase storage URL. Usually you'll want to create a new block with the file as markdown: ``. Provide ONE of: filePath (preferred - local file, server reads directly), url (remote URL, server fetches), or base64 (raw data, fallback for sandboxed clients)." +
|
|
82
|
+
defineTool("file_upload", "Upload a file to Roam. Returns the Firebase storage URL. Usually you'll want to create a new block with the file as markdown: ``. Provide ONE of: filePath (preferred - local file, server reads directly), url (remote URL, server fetches), or base64 (raw data, fallback for sandboxed clients)." +
|
|
83
|
+
GUIDELINES_NOTE, FileUploadSchema, uploadFile),
|
|
71
84
|
defineTool("file_delete", "Delete a file hosted on Roam." + GUIDELINES_NOTE, FileDeleteSchema, deleteFile),
|
|
72
85
|
];
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
// Backwards-compatible aggregate of all client tools.
|
|
87
|
+
export const contentTools = [...dataTools, ...desktopUiTools];
|
|
88
|
+
// All client tools available in core. Local standalone tools (list_graphs,
|
|
89
|
+
// setup_new_graph) live in @roam-research/roam-tools-local since they touch
|
|
90
|
+
// ~/.roam-tools.json and the Roam Desktop API.
|
|
91
|
+
export const tools = [...dataTools, ...desktopUiTools];
|
|
77
92
|
export function findTool(name) {
|
|
78
93
|
return tools.find((t) => t.name === name);
|
|
79
94
|
}
|
|
@@ -89,19 +104,13 @@ function prependGraphInfo(result, nickname) {
|
|
|
89
104
|
if (first.type === "text") {
|
|
90
105
|
return {
|
|
91
106
|
...result,
|
|
92
|
-
content: [
|
|
93
|
-
{ ...first, text: `${prefix}\n\n${first.text}` },
|
|
94
|
-
...content.slice(1),
|
|
95
|
-
],
|
|
107
|
+
content: [{ ...first, text: `${prefix}\n\n${first.text}` }, ...content.slice(1)],
|
|
96
108
|
};
|
|
97
109
|
}
|
|
98
110
|
// For image or other content types, prepend a text block
|
|
99
111
|
return {
|
|
100
112
|
...result,
|
|
101
|
-
content: [
|
|
102
|
-
{ type: "text", text: prefix },
|
|
103
|
-
...content,
|
|
104
|
-
],
|
|
113
|
+
content: [{ type: "text", text: prefix }, ...content],
|
|
105
114
|
};
|
|
106
115
|
}
|
|
107
116
|
/**
|
|
@@ -159,46 +168,40 @@ function roamErrorResult(error) {
|
|
|
159
168
|
isError: true,
|
|
160
169
|
};
|
|
161
170
|
}
|
|
162
|
-
export async function routeToolCall(toolName, args) {
|
|
171
|
+
export async function routeToolCall(toolName, args, options) {
|
|
163
172
|
const tool = findTool(toolName);
|
|
164
173
|
if (!tool) {
|
|
165
174
|
throw new Error(`Unknown tool: ${toolName}`);
|
|
166
175
|
}
|
|
176
|
+
// Core only registers client tools. Standalone tools live in
|
|
177
|
+
// @roam-research/roam-tools-local; route them through that wrapper.
|
|
178
|
+
if (tool.type !== "client") {
|
|
179
|
+
throw new Error(`Tool ${toolName}: core's routeToolCall only handles client tools. ` +
|
|
180
|
+
`Standalone tools live in @roam-research/roam-tools-local.`);
|
|
181
|
+
}
|
|
167
182
|
// Validate and parse args with Zod
|
|
168
183
|
const parsed = tool.schema.safeParse(args);
|
|
169
184
|
if (!parsed.success) {
|
|
170
185
|
throw new Error(`Invalid arguments: ${parsed.error.message}`);
|
|
171
186
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
return await tool.action(parsed.data);
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
if (error instanceof RoamError) {
|
|
179
|
-
return roamErrorResult(error);
|
|
180
|
-
}
|
|
181
|
-
throw error;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Handle client tools (require graph resolution)
|
|
187
|
+
const tokenInfoMode = options.tokenInfoMode ?? "skip";
|
|
188
|
+
const updateTokenStatus = options.onTokenStatusUpdate;
|
|
185
189
|
try {
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (tool.name === "get_graph_guidelines") {
|
|
190
|
+
const { graph: graphArg, ...restArgs } = parsed.data;
|
|
191
|
+
const graph = await options.resolveGraph(graphArg);
|
|
192
|
+
const client = await options.createClient(graph);
|
|
193
|
+
// Special handling for get_graph_guidelines: sync token info in parallel.
|
|
194
|
+
// Only fires in local-sync mode AND when the client implements getTokenInfo.
|
|
195
|
+
// Bind early so TS narrows the optional method through the truthy check.
|
|
196
|
+
const getTokenInfoFn = client.getTokenInfo?.bind(client);
|
|
197
|
+
if (tool.name === "get_graph_guidelines" && tokenInfoMode === "local-sync" && getTokenInfoFn) {
|
|
198
|
+
// In local-sync mode, the resolver is expected to return ResolvedGraph
|
|
199
|
+
// (with lastKnownTokenStatus). If a custom resolver omits the field,
|
|
200
|
+
// the read returns undefined and behavior is identical.
|
|
201
|
+
const resolvedGraph = graph;
|
|
199
202
|
const [actionSettled, tokenInfoSettled] = await Promise.allSettled([
|
|
200
203
|
tool.action(client, restArgs),
|
|
201
|
-
|
|
204
|
+
getTokenInfoFn(),
|
|
202
205
|
]);
|
|
203
206
|
// getTokenInfo() never throws, so always fulfilled
|
|
204
207
|
const tokenInfoResult = tokenInfoSettled.status === "fulfilled"
|
|
@@ -206,11 +209,15 @@ export async function routeToolCall(toolName, args) {
|
|
|
206
209
|
: { status: "unknown" };
|
|
207
210
|
// Handle revoked token FIRST (before examining action result)
|
|
208
211
|
if (tokenInfoResult.status === "revoked") {
|
|
209
|
-
if (resolvedGraph.lastKnownTokenStatus !== "revoked") {
|
|
212
|
+
if (updateTokenStatus && resolvedGraph.lastKnownTokenStatus !== "revoked") {
|
|
210
213
|
try {
|
|
211
|
-
await
|
|
214
|
+
await updateTokenStatus(resolvedGraph.nickname, {
|
|
215
|
+
lastKnownTokenStatus: "revoked",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// best-effort status update
|
|
212
220
|
}
|
|
213
|
-
catch { }
|
|
214
221
|
}
|
|
215
222
|
const baseResult = actionSettled.status === "fulfilled"
|
|
216
223
|
? actionSettled.value
|
|
@@ -226,22 +233,24 @@ export async function routeToolCall(toolName, args) {
|
|
|
226
233
|
const result = actionSettled.value;
|
|
227
234
|
if (tokenInfoResult.status === "active") {
|
|
228
235
|
const info = tokenInfoResult.info;
|
|
229
|
-
// Validate access level before writing to prevent
|
|
236
|
+
// Validate access level before writing to prevent status corruption
|
|
230
237
|
const validLevels = ["read-only", "read-append", "full"];
|
|
231
238
|
const level = validLevels.includes(info.grantedAccessLevel)
|
|
232
239
|
? info.grantedAccessLevel
|
|
233
240
|
: undefined;
|
|
234
|
-
// Only write
|
|
241
|
+
// Only write if something actually changed
|
|
235
242
|
const accessLevelChanged = level && resolvedGraph.accessLevel !== level;
|
|
236
243
|
const tokenStatusChanged = resolvedGraph.lastKnownTokenStatus !== "active";
|
|
237
|
-
if (accessLevelChanged || tokenStatusChanged) {
|
|
244
|
+
if (updateTokenStatus && (accessLevelChanged || tokenStatusChanged)) {
|
|
238
245
|
try {
|
|
239
|
-
await
|
|
246
|
+
await updateTokenStatus(resolvedGraph.nickname, {
|
|
240
247
|
...(accessLevelChanged ? { accessLevel: level } : {}),
|
|
241
248
|
lastKnownTokenStatus: "active",
|
|
242
249
|
});
|
|
243
250
|
}
|
|
244
|
-
catch {
|
|
251
|
+
catch {
|
|
252
|
+
// best-effort status update
|
|
253
|
+
}
|
|
245
254
|
}
|
|
246
255
|
if (!result.isError) {
|
|
247
256
|
const enriched = enrichResultWithTokenInfo(result, info);
|
|
@@ -250,22 +259,24 @@ export async function routeToolCall(toolName, args) {
|
|
|
250
259
|
return result;
|
|
251
260
|
}
|
|
252
261
|
// status === "unknown" — action succeeded, so token isn't revoked; clear stale status
|
|
253
|
-
if (resolvedGraph.lastKnownTokenStatus !== "active") {
|
|
262
|
+
if (updateTokenStatus && resolvedGraph.lastKnownTokenStatus !== "active") {
|
|
254
263
|
try {
|
|
255
|
-
await
|
|
264
|
+
await updateTokenStatus(resolvedGraph.nickname, { lastKnownTokenStatus: "active" });
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// best-effort status update
|
|
256
268
|
}
|
|
257
|
-
catch { }
|
|
258
269
|
}
|
|
259
270
|
if (!result.isError) {
|
|
260
271
|
return prependGraphInfo(result, resolvedGraph.nickname);
|
|
261
272
|
}
|
|
262
273
|
return result;
|
|
263
274
|
}
|
|
264
|
-
// Normal flow for all other tools
|
|
275
|
+
// Normal flow for all other tools (and get_graph_guidelines when token-info
|
|
276
|
+
// sync is skipped or unavailable). Graph-name prefix runs in both modes.
|
|
265
277
|
const result = await tool.action(client, restArgs);
|
|
266
|
-
// Prepend graph info to successful responses
|
|
267
278
|
if (!result.isError) {
|
|
268
|
-
return prependGraphInfo(result,
|
|
279
|
+
return prependGraphInfo(result, graph.nickname);
|
|
269
280
|
}
|
|
270
281
|
return result;
|
|
271
282
|
}
|