@stratta/mcp 0.9.8 → 0.12.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 +47 -11
- package/dist/confirm.d.ts +15 -0
- package/dist/confirm.js +54 -0
- package/dist/errors.js +27 -0
- package/dist/index.js +27 -2
- package/dist/prompts.d.ts +4 -0
- package/dist/prompts.js +40 -0
- package/dist/resources.d.ts +3 -0
- package/dist/resources.js +35 -0
- package/dist/tools/catalog.gen.d.ts +34 -0
- package/dist/tools/catalog.gen.js +724 -0
- package/dist/tools/dossier.d.ts +96 -4
- package/dist/tools/dossier.js +291 -90
- package/dist/tools/ingest.d.ts +8 -0
- package/dist/tools/ingest.js +116 -98
- package/dist/tools/read.d.ts +42 -17
- package/dist/tools/read.js +160 -84
- package/dist/usage.d.ts +3 -0
- package/dist/usage.js +50 -0
- package/package.json +1 -1
- package/scripts/ingest-prepass.py +499 -54
- package/scripts/tests/test_prepass.py +211 -0
- package/skills/consult-stratta/SKILL.md +73 -0
- package/skills/ingest-norm/SKILL.md +99 -44
- package/skills/verification-note/SKILL.md +54 -0
package/dist/tools/dossier.d.ts
CHANGED
|
@@ -4,14 +4,37 @@ export declare const openDossier: import("./define.js").ToolDef<{
|
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
reference: z.ZodOptional<z.ZodString>;
|
|
6
6
|
}>;
|
|
7
|
+
export declare const openQuestion: import("./define.js").ToolDef<{
|
|
8
|
+
dossierId: z.ZodString;
|
|
9
|
+
title: z.ZodString;
|
|
10
|
+
body: z.ZodOptional<z.ZodString>;
|
|
11
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
12
|
+
}>;
|
|
7
13
|
export declare const saveFinding: import("./define.js").ToolDef<{
|
|
14
|
+
attachmentId: z.ZodOptional<z.ZodString>;
|
|
15
|
+
attachmentPage: z.ZodOptional<z.ZodNumber>;
|
|
16
|
+
title: z.ZodString;
|
|
17
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
18
|
+
value: z.ZodOptional<z.ZodString>;
|
|
19
|
+
confidence: z.ZodOptional<z.ZodEnum<{
|
|
20
|
+
established: "established";
|
|
21
|
+
judgement: "judgement";
|
|
22
|
+
to_confirm: "to_confirm";
|
|
23
|
+
}>>;
|
|
24
|
+
normCode: z.ZodOptional<z.ZodString>;
|
|
25
|
+
sectionPath: z.ZodOptional<z.ZodString>;
|
|
26
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
normEdition: z.ZodOptional<z.ZodString>;
|
|
8
28
|
dossierId: z.ZodString;
|
|
29
|
+
questionId: z.ZodOptional<z.ZodString>;
|
|
9
30
|
kind: z.ZodEnum<{
|
|
10
31
|
reference: "reference";
|
|
11
32
|
hypothesis: "hypothesis";
|
|
12
33
|
observation: "observation";
|
|
13
|
-
question: "question";
|
|
14
34
|
}>;
|
|
35
|
+
}>;
|
|
36
|
+
export declare const recordDecision: import("./define.js").ToolDef<{
|
|
37
|
+
confirmed: z.ZodOptional<z.ZodBoolean>;
|
|
15
38
|
title: z.ZodString;
|
|
16
39
|
detail: z.ZodOptional<z.ZodString>;
|
|
17
40
|
value: z.ZodOptional<z.ZodString>;
|
|
@@ -23,26 +46,74 @@ export declare const saveFinding: import("./define.js").ToolDef<{
|
|
|
23
46
|
normCode: z.ZodOptional<z.ZodString>;
|
|
24
47
|
sectionPath: z.ZodOptional<z.ZodString>;
|
|
25
48
|
page: z.ZodOptional<z.ZodNumber>;
|
|
49
|
+
normEdition: z.ZodOptional<z.ZodString>;
|
|
50
|
+
questionId: z.ZodString;
|
|
51
|
+
retainedOption: z.ZodOptional<z.ZodString>;
|
|
26
52
|
}>;
|
|
27
53
|
export declare const loadDossier: import("./define.js").ToolDef<{
|
|
28
54
|
dossierId: z.ZodOptional<z.ZodString>;
|
|
29
55
|
name: z.ZodOptional<z.ZodString>;
|
|
30
56
|
}>;
|
|
31
57
|
export declare const resolveQuestion: import("./define.js").ToolDef<{
|
|
32
|
-
|
|
58
|
+
questionId: z.ZodString;
|
|
33
59
|
resolved: z.ZodOptional<z.ZodBoolean>;
|
|
60
|
+
confirmed: z.ZodOptional<z.ZodBoolean>;
|
|
61
|
+
}>;
|
|
62
|
+
export declare const listAttachments: import("./define.js").ToolDef<{
|
|
63
|
+
dossierId: z.ZodString;
|
|
64
|
+
}>;
|
|
65
|
+
export declare const readAttachment: import("./define.js").ToolDef<{
|
|
66
|
+
attachmentId: z.ZodString;
|
|
67
|
+
from: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
to: z.ZodOptional<z.ZodNumber>;
|
|
69
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
70
|
+
}>;
|
|
71
|
+
export declare const searchInDossier: import("./define.js").ToolDef<{
|
|
72
|
+
dossierId: z.ZodString;
|
|
73
|
+
keyword: z.ZodString;
|
|
74
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
75
|
+
}>;
|
|
76
|
+
export declare const addAttachment: import("./define.js").ToolDef<{
|
|
77
|
+
dossierId: z.ZodString;
|
|
78
|
+
filePath: z.ZodString;
|
|
79
|
+
}>;
|
|
80
|
+
export declare const listTemplates: import("./define.js").ToolDef<{}>;
|
|
81
|
+
export declare const applyTemplate: import("./define.js").ToolDef<{
|
|
82
|
+
dossierId: z.ZodString;
|
|
83
|
+
templateId: z.ZodString;
|
|
34
84
|
}>;
|
|
35
85
|
export declare const dossierTools: (import("./define.js").ToolDef<{}> | import("./define.js").ToolDef<{
|
|
36
86
|
name: z.ZodString;
|
|
37
87
|
reference: z.ZodOptional<z.ZodString>;
|
|
38
88
|
}> | import("./define.js").ToolDef<{
|
|
39
89
|
dossierId: z.ZodString;
|
|
90
|
+
title: z.ZodString;
|
|
91
|
+
body: z.ZodOptional<z.ZodString>;
|
|
92
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
93
|
+
}> | import("./define.js").ToolDef<{
|
|
94
|
+
attachmentId: z.ZodOptional<z.ZodString>;
|
|
95
|
+
attachmentPage: z.ZodOptional<z.ZodNumber>;
|
|
96
|
+
title: z.ZodString;
|
|
97
|
+
detail: z.ZodOptional<z.ZodString>;
|
|
98
|
+
value: z.ZodOptional<z.ZodString>;
|
|
99
|
+
confidence: z.ZodOptional<z.ZodEnum<{
|
|
100
|
+
established: "established";
|
|
101
|
+
judgement: "judgement";
|
|
102
|
+
to_confirm: "to_confirm";
|
|
103
|
+
}>>;
|
|
104
|
+
normCode: z.ZodOptional<z.ZodString>;
|
|
105
|
+
sectionPath: z.ZodOptional<z.ZodString>;
|
|
106
|
+
page: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
normEdition: z.ZodOptional<z.ZodString>;
|
|
108
|
+
dossierId: z.ZodString;
|
|
109
|
+
questionId: z.ZodOptional<z.ZodString>;
|
|
40
110
|
kind: z.ZodEnum<{
|
|
41
111
|
reference: "reference";
|
|
42
112
|
hypothesis: "hypothesis";
|
|
43
113
|
observation: "observation";
|
|
44
|
-
question: "question";
|
|
45
114
|
}>;
|
|
115
|
+
}> | import("./define.js").ToolDef<{
|
|
116
|
+
confirmed: z.ZodOptional<z.ZodBoolean>;
|
|
46
117
|
title: z.ZodString;
|
|
47
118
|
detail: z.ZodOptional<z.ZodString>;
|
|
48
119
|
value: z.ZodOptional<z.ZodString>;
|
|
@@ -54,10 +125,31 @@ export declare const dossierTools: (import("./define.js").ToolDef<{}> | import("
|
|
|
54
125
|
normCode: z.ZodOptional<z.ZodString>;
|
|
55
126
|
sectionPath: z.ZodOptional<z.ZodString>;
|
|
56
127
|
page: z.ZodOptional<z.ZodNumber>;
|
|
128
|
+
normEdition: z.ZodOptional<z.ZodString>;
|
|
129
|
+
questionId: z.ZodString;
|
|
130
|
+
retainedOption: z.ZodOptional<z.ZodString>;
|
|
57
131
|
}> | import("./define.js").ToolDef<{
|
|
58
132
|
dossierId: z.ZodOptional<z.ZodString>;
|
|
59
133
|
name: z.ZodOptional<z.ZodString>;
|
|
60
134
|
}> | import("./define.js").ToolDef<{
|
|
61
|
-
|
|
135
|
+
questionId: z.ZodString;
|
|
62
136
|
resolved: z.ZodOptional<z.ZodBoolean>;
|
|
137
|
+
confirmed: z.ZodOptional<z.ZodBoolean>;
|
|
138
|
+
}> | import("./define.js").ToolDef<{
|
|
139
|
+
dossierId: z.ZodString;
|
|
140
|
+
}> | import("./define.js").ToolDef<{
|
|
141
|
+
attachmentId: z.ZodString;
|
|
142
|
+
from: z.ZodOptional<z.ZodNumber>;
|
|
143
|
+
to: z.ZodOptional<z.ZodNumber>;
|
|
144
|
+
maxChars: z.ZodOptional<z.ZodNumber>;
|
|
145
|
+
}> | import("./define.js").ToolDef<{
|
|
146
|
+
dossierId: z.ZodString;
|
|
147
|
+
keyword: z.ZodString;
|
|
148
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
149
|
+
}> | import("./define.js").ToolDef<{
|
|
150
|
+
dossierId: z.ZodString;
|
|
151
|
+
filePath: z.ZodString;
|
|
152
|
+
}> | import("./define.js").ToolDef<{
|
|
153
|
+
dossierId: z.ZodString;
|
|
154
|
+
templateId: z.ZodString;
|
|
63
155
|
}>)[];
|
package/dist/tools/dossier.js
CHANGED
|
@@ -1,32 +1,58 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { basename, extname } from 'node:path';
|
|
1
3
|
import { z } from 'zod';
|
|
2
4
|
import { api } from '../client.js';
|
|
3
5
|
import { requireApiKey } from '../auth.js';
|
|
6
|
+
import { catalogEntry, param } from './catalog.gen.js';
|
|
4
7
|
import { defineTool } from './define.js';
|
|
5
8
|
/**
|
|
6
|
-
* The
|
|
9
|
+
* The seven dossier tools: how the work survives the conversation.
|
|
7
10
|
*
|
|
8
|
-
*
|
|
9
|
-
* it,
|
|
10
|
-
*
|
|
11
|
+
* A dossier is a set of questions. Each question holds the evidence gathered
|
|
12
|
+
* for it, the options on the table and, once the engineer has confirmed it, a
|
|
13
|
+
* decision. The question is the unit of work: it is what gets assigned,
|
|
14
|
+
* reviewed and closed.
|
|
11
15
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* that mattered. `get_methodology` tells it when to open a dossier; these tell
|
|
16
|
-
* it what is worth putting in one.
|
|
16
|
+
* Names, descriptions and annotations come from the generated catalogue,
|
|
17
|
+
* shared with the remote transport. `get_methodology` tells the agent when to
|
|
18
|
+
* open a dossier; the descriptions tell it what is worth putting in one.
|
|
17
19
|
*/
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const headOf = (name) => {
|
|
21
|
+
const entry = catalogEntry(name);
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
title: entry.title,
|
|
25
|
+
description: entry.description,
|
|
26
|
+
annotations: entry.annotations,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
const dossierId = (tool) => z.string().min(1).describe(param(tool, 'dossierId'));
|
|
30
|
+
const questionId = (tool) => z.string().min(1).describe(param(tool, 'questionId'));
|
|
31
|
+
/** The fields a piece of evidence or a decision carries. */
|
|
32
|
+
const entryFields = (tool) => ({
|
|
33
|
+
title: z.string().min(1).max(200).describe(param(tool, 'title')),
|
|
34
|
+
detail: z.string().max(4000).optional().describe(param(tool, 'detail')),
|
|
35
|
+
value: z.string().max(120).optional().describe(param(tool, 'value')),
|
|
36
|
+
confidence: z
|
|
37
|
+
.enum(['established', 'judgement', 'to_confirm'])
|
|
38
|
+
.optional()
|
|
39
|
+
.describe(param(tool, 'confidence')),
|
|
40
|
+
normCode: z.string().max(40).optional().describe(param(tool, 'normCode')),
|
|
41
|
+
sectionPath: z
|
|
42
|
+
.string()
|
|
43
|
+
.max(60)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe(param(tool, 'sectionPath')),
|
|
46
|
+
page: z.number().int().positive().optional().describe(param(tool, 'page')),
|
|
47
|
+
normEdition: z
|
|
48
|
+
.string()
|
|
49
|
+
.max(40)
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(param(tool, 'normEdition')),
|
|
52
|
+
});
|
|
24
53
|
export const listDossiers = defineTool({
|
|
25
|
-
|
|
26
|
-
title: 'List dossiers',
|
|
27
|
-
description: 'List the dossiers of YOUR organisation, most recently touched first. A dossier is a project record: what was decided, what it rests on, and what is still open. Call this when the user mentions a project by name, or asks what they were working on. Each row carries the count of entries and of UNRESOLVED questions — a dossier with open questions is the one to resume.',
|
|
54
|
+
...headOf('list_dossiers'),
|
|
28
55
|
inputSchema: {},
|
|
29
|
-
annotations: readOnly,
|
|
30
56
|
run: async (client) => ({
|
|
31
57
|
dossiers: await client.action(api.dossiersApi.listDossiers, {
|
|
32
58
|
apiKey: requireApiKey(),
|
|
@@ -34,107 +60,106 @@ export const listDossiers = defineTool({
|
|
|
34
60
|
}),
|
|
35
61
|
});
|
|
36
62
|
export const openDossier = defineTool({
|
|
37
|
-
|
|
38
|
-
title: 'Open a dossier',
|
|
39
|
-
description: "Open the dossier for a project, creating it if it does not exist yet. IDEMPOTENT on the name: calling it twice with the same name returns the same dossier rather than splitting a project's history in two. Call this when the user starts working on a named project and there is something worth keeping — a retained value, an assumption, a site observation, an unanswered question. Do not open one for a passing lookup. Returns { dossierId, created }.",
|
|
63
|
+
...headOf('open_dossier'),
|
|
40
64
|
inputSchema: {
|
|
41
|
-
name: z
|
|
42
|
-
.string()
|
|
43
|
-
.min(1)
|
|
44
|
-
.max(120)
|
|
45
|
-
.describe('How the user refers to the project, e.g. "Villa Morges — toiture". Reuse their words: this is the name they will search for.'),
|
|
65
|
+
name: z.string().min(1).max(120).describe(param('open_dossier', 'name')),
|
|
46
66
|
reference: z
|
|
47
67
|
.string()
|
|
48
68
|
.max(60)
|
|
49
69
|
.optional()
|
|
50
|
-
.describe(
|
|
70
|
+
.describe(param('open_dossier', 'reference')),
|
|
51
71
|
},
|
|
52
|
-
annotations: { ...write, idempotentHint: true },
|
|
53
72
|
run: (client, args) => client.action(api.dossiersApi.openDossier, {
|
|
54
73
|
apiKey: requireApiKey(),
|
|
55
74
|
name: args.name,
|
|
56
75
|
reference: args.reference,
|
|
57
76
|
}),
|
|
58
77
|
});
|
|
59
|
-
export const
|
|
60
|
-
|
|
61
|
-
title: 'Save a finding to a dossier',
|
|
62
|
-
description: `Record ONE decision in a dossier. Call it as you work, not in a batch at the end — a finding saved when it is made carries the reasoning that produced it.
|
|
63
|
-
|
|
64
|
-
WHAT TO RECORD, by kind:
|
|
65
|
-
- reference: what a norm says, that the project relies on. ALWAYS fill normCode + sectionPath (+ page). If you cannot cite it, it is not a reference.
|
|
66
|
-
- hypothesis: a value the engineer RETAINS, and why. This is the one that matters most in geotechnics, where a retained value is a judgement between disagreeing measurements rather than the output of a formula. Put the number in \`value\` and the reasoning in \`detail\`.
|
|
67
|
-
- observation: what the site, a borehole, or a survey showed. Include the date in \`detail\` when known.
|
|
68
|
-
- question: something not settled. These surface FIRST when the dossier is reloaded, so record them even when you cannot answer — especially then.
|
|
69
|
-
|
|
70
|
-
WHAT NOT TO RECORD: your own prose, intermediate steps, anything the user did not treat as a decision. A dossier of forty entries where three mattered is worse than a dossier of three.
|
|
71
|
-
|
|
72
|
-
Set \`confidence\` whenever the entry is a value: established (computed or read directly), judgement (the engineer chose it), to_confirm (provisional, needs a test or a check). Confusing those three is the professional fault this field exists to prevent.`,
|
|
78
|
+
export const openQuestion = defineTool({
|
|
79
|
+
...headOf('open_question'),
|
|
73
80
|
inputSchema: {
|
|
74
|
-
dossierId,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.describe('See the tool description: pick by what the entry IS.'),
|
|
78
|
-
title: z
|
|
79
|
-
.string()
|
|
80
|
-
.min(1)
|
|
81
|
-
.max(200)
|
|
82
|
-
.describe('The decision in one line, as an engineer would write it in a report.'),
|
|
83
|
-
detail: z
|
|
81
|
+
dossierId: dossierId('open_question'),
|
|
82
|
+
title: z.string().min(1).max(200).describe(param('open_question', 'title')),
|
|
83
|
+
body: z
|
|
84
84
|
.string()
|
|
85
85
|
.max(4000)
|
|
86
86
|
.optional()
|
|
87
|
-
.describe('
|
|
88
|
-
|
|
89
|
-
.string()
|
|
90
|
-
.max(
|
|
91
|
-
.optional()
|
|
92
|
-
.describe('The retained value WITH its unit, e.g. "30°", "1,25 kN/m²".'),
|
|
93
|
-
confidence: z
|
|
94
|
-
.enum(['established', 'judgement', 'to_confirm'])
|
|
87
|
+
.describe(param('open_question', 'body')),
|
|
88
|
+
options: z
|
|
89
|
+
.array(z.string().min(1).max(120))
|
|
90
|
+
.max(10)
|
|
95
91
|
.optional()
|
|
96
|
-
.describe('
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
.describe(param('open_question', 'options')),
|
|
93
|
+
},
|
|
94
|
+
run: (client, args) => client.action(api.dossiersApi.openQuestion, {
|
|
95
|
+
apiKey: requireApiKey(),
|
|
96
|
+
dossierId: args.dossierId,
|
|
97
|
+
title: args.title,
|
|
98
|
+
body: args.body,
|
|
99
|
+
options: args.options,
|
|
100
|
+
}),
|
|
101
|
+
});
|
|
102
|
+
export const saveFinding = defineTool({
|
|
103
|
+
...headOf('save_finding'),
|
|
104
|
+
inputSchema: {
|
|
105
|
+
dossierId: dossierId('save_finding'),
|
|
106
|
+
questionId: questionId('save_finding').optional(),
|
|
107
|
+
kind: z
|
|
108
|
+
.enum(['reference', 'hypothesis', 'observation'])
|
|
109
|
+
.describe(param('save_finding', 'kind')),
|
|
110
|
+
...entryFields('save_finding'),
|
|
111
|
+
attachmentId: z
|
|
103
112
|
.string()
|
|
104
|
-
.max(60)
|
|
105
113
|
.optional()
|
|
106
|
-
.describe('
|
|
107
|
-
|
|
114
|
+
.describe(param('save_finding', 'attachmentId')),
|
|
115
|
+
attachmentPage: z
|
|
108
116
|
.number()
|
|
109
117
|
.int()
|
|
110
|
-
.
|
|
118
|
+
.min(1)
|
|
111
119
|
.optional()
|
|
112
|
-
.describe('
|
|
120
|
+
.describe(param('save_finding', 'attachmentPage')),
|
|
113
121
|
},
|
|
114
|
-
annotations: write,
|
|
115
122
|
run: (client, args) => client.action(api.dossiersApi.saveFinding, {
|
|
116
123
|
apiKey: requireApiKey(),
|
|
117
124
|
...args,
|
|
118
125
|
}),
|
|
119
126
|
});
|
|
127
|
+
export const recordDecision = defineTool({
|
|
128
|
+
...headOf('record_decision'),
|
|
129
|
+
inputSchema: {
|
|
130
|
+
questionId: questionId('record_decision'),
|
|
131
|
+
retainedOption: z
|
|
132
|
+
.string()
|
|
133
|
+
.max(120)
|
|
134
|
+
.optional()
|
|
135
|
+
.describe(param('record_decision', 'retainedOption')),
|
|
136
|
+
...entryFields('record_decision'),
|
|
137
|
+
confirmed: z
|
|
138
|
+
.boolean()
|
|
139
|
+
.optional()
|
|
140
|
+
.describe(param('record_decision', 'confirmed')),
|
|
141
|
+
},
|
|
142
|
+
// `confirmed` is read by the confirmation gate in `index.ts`, never sent.
|
|
143
|
+
run: (client, { confirmed: _confirmed, ...args }) => client.action(api.dossiersApi.recordDecision, {
|
|
144
|
+
apiKey: requireApiKey(),
|
|
145
|
+
...args,
|
|
146
|
+
}),
|
|
147
|
+
});
|
|
120
148
|
export const loadDossier = defineTool({
|
|
121
|
-
|
|
122
|
-
title: 'Load a dossier',
|
|
123
|
-
description: 'Reload everything a dossier holds: retained values, what they rest on, site observations, open questions and the comments a colleague left. UNRESOLVED QUESTIONS COME FIRST — read them before anything else and tell the user what is still open. Call this whenever the user returns to a project ("reprends le dossier X", "on en était où sur Y") BEFORE answering anything about it, so you build on what was decided instead of deciding it again. Accepts either the id or the name the user uses.',
|
|
149
|
+
...headOf('load_dossier'),
|
|
124
150
|
inputSchema: {
|
|
125
151
|
dossierId: z
|
|
126
152
|
.string()
|
|
127
153
|
.min(1)
|
|
128
154
|
.optional()
|
|
129
|
-
.describe('
|
|
155
|
+
.describe(param('load_dossier', 'dossierId')),
|
|
130
156
|
name: z
|
|
131
157
|
.string()
|
|
132
158
|
.min(1)
|
|
133
159
|
.max(120)
|
|
134
160
|
.optional()
|
|
135
|
-
.describe('
|
|
161
|
+
.describe(param('load_dossier', 'name')),
|
|
136
162
|
},
|
|
137
|
-
annotations: readOnly,
|
|
138
163
|
run: (client, args) => client.action(api.dossiersApi.loadDossier, {
|
|
139
164
|
apiKey: requireApiKey(),
|
|
140
165
|
dossierId: args.dossierId,
|
|
@@ -142,33 +167,209 @@ export const loadDossier = defineTool({
|
|
|
142
167
|
}),
|
|
143
168
|
});
|
|
144
169
|
export const resolveQuestion = defineTool({
|
|
145
|
-
|
|
146
|
-
title: 'Close an open question',
|
|
147
|
-
description: 'Mark a question entry as settled, once it actually is. The entry stays in the dossier — the trail of what was once uncertain is part of the record — but it stops surfacing at the top on reload. Only call this when the user has confirmed the answer, never on your own reasoning.',
|
|
170
|
+
...headOf('resolve_question'),
|
|
148
171
|
inputSchema: {
|
|
149
|
-
|
|
150
|
-
.string()
|
|
151
|
-
.min(1)
|
|
152
|
-
.describe('Entry id of the question, from load_dossier.'),
|
|
172
|
+
questionId: questionId('resolve_question'),
|
|
153
173
|
resolved: z
|
|
154
174
|
.boolean()
|
|
155
175
|
.optional()
|
|
156
|
-
.describe('
|
|
176
|
+
.describe(param('resolve_question', 'resolved')),
|
|
177
|
+
confirmed: z
|
|
178
|
+
.boolean()
|
|
179
|
+
.optional()
|
|
180
|
+
.describe(param('resolve_question', 'confirmed')),
|
|
157
181
|
},
|
|
158
|
-
annotations: { ...write, idempotentHint: true },
|
|
159
182
|
run: async (client, args) => {
|
|
160
183
|
await client.action(api.dossiersApi.resolveQuestion, {
|
|
161
184
|
apiKey: requireApiKey(),
|
|
162
|
-
|
|
185
|
+
questionId: args.questionId,
|
|
163
186
|
resolved: args.resolved,
|
|
164
187
|
});
|
|
165
188
|
return { ok: true };
|
|
166
189
|
},
|
|
167
190
|
});
|
|
191
|
+
const MIME_BY_EXT = {
|
|
192
|
+
'.pdf': 'application/pdf',
|
|
193
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
194
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
195
|
+
'.png': 'image/png',
|
|
196
|
+
'.jpg': 'image/jpeg',
|
|
197
|
+
'.jpeg': 'image/jpeg',
|
|
198
|
+
'.txt': 'text/plain',
|
|
199
|
+
'.md': 'text/markdown',
|
|
200
|
+
'.csv': 'text/csv',
|
|
201
|
+
};
|
|
202
|
+
export const listAttachments = defineTool({
|
|
203
|
+
...headOf('list_attachments'),
|
|
204
|
+
inputSchema: {
|
|
205
|
+
dossierId: z.string().describe(param('list_attachments', 'dossierId')),
|
|
206
|
+
},
|
|
207
|
+
run: async (client, args) => {
|
|
208
|
+
const attachments = await client.action(api.dossiersApi.listAttachments, {
|
|
209
|
+
apiKey: requireApiKey(),
|
|
210
|
+
dossierId: args.dossierId,
|
|
211
|
+
});
|
|
212
|
+
if (attachments === null)
|
|
213
|
+
return { error: 'Dossier not found in your workspace.' };
|
|
214
|
+
return { dossierId: args.dossierId, attachments };
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
export const readAttachment = defineTool({
|
|
218
|
+
...headOf('read_attachment'),
|
|
219
|
+
inputSchema: {
|
|
220
|
+
attachmentId: z.string().describe(param('read_attachment', 'attachmentId')),
|
|
221
|
+
from: z
|
|
222
|
+
.number()
|
|
223
|
+
.int()
|
|
224
|
+
.min(1)
|
|
225
|
+
.optional()
|
|
226
|
+
.describe(param('read_attachment', 'from')),
|
|
227
|
+
to: z
|
|
228
|
+
.number()
|
|
229
|
+
.int()
|
|
230
|
+
.min(1)
|
|
231
|
+
.optional()
|
|
232
|
+
.describe(param('read_attachment', 'to')),
|
|
233
|
+
maxChars: z
|
|
234
|
+
.number()
|
|
235
|
+
.int()
|
|
236
|
+
.min(500)
|
|
237
|
+
.max(40_000)
|
|
238
|
+
.optional()
|
|
239
|
+
.describe(param('read_attachment', 'maxChars')),
|
|
240
|
+
},
|
|
241
|
+
run: async (client, args) => {
|
|
242
|
+
const read = (await client.action(api.dossiersApi.readAttachment, {
|
|
243
|
+
apiKey: requireApiKey(),
|
|
244
|
+
attachmentId: args.attachmentId,
|
|
245
|
+
from: args.from,
|
|
246
|
+
to: args.to,
|
|
247
|
+
maxChars: args.maxChars,
|
|
248
|
+
}));
|
|
249
|
+
if (read === null) {
|
|
250
|
+
return {
|
|
251
|
+
error: 'Attachment not found in your workspace. Call list_attachments on the dossier.',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return read;
|
|
255
|
+
},
|
|
256
|
+
toContent: (result) => {
|
|
257
|
+
const r = result;
|
|
258
|
+
if (r.error || !r.markdown) {
|
|
259
|
+
return {
|
|
260
|
+
content: [{ type: 'text', text: r.error ?? 'Attachment unavailable.' }],
|
|
261
|
+
isError: true,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
const { markdown, ...structured } = r;
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: 'text', text: markdown }],
|
|
267
|
+
structuredContent: { ...structured, markdown },
|
|
268
|
+
};
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
export const searchInDossier = defineTool({
|
|
272
|
+
...headOf('search_in_dossier'),
|
|
273
|
+
inputSchema: {
|
|
274
|
+
dossierId: z.string().describe(param('search_in_dossier', 'dossierId')),
|
|
275
|
+
keyword: z
|
|
276
|
+
.string()
|
|
277
|
+
.min(1)
|
|
278
|
+
.max(200)
|
|
279
|
+
.describe(param('search_in_dossier', 'keyword')),
|
|
280
|
+
limit: z
|
|
281
|
+
.number()
|
|
282
|
+
.int()
|
|
283
|
+
.min(1)
|
|
284
|
+
.max(30)
|
|
285
|
+
.optional()
|
|
286
|
+
.describe(param('search_in_dossier', 'limit')),
|
|
287
|
+
},
|
|
288
|
+
run: async (client, args) => {
|
|
289
|
+
const hits = await client.action(api.dossiersApi.searchInDossier, {
|
|
290
|
+
apiKey: requireApiKey(),
|
|
291
|
+
dossierId: args.dossierId,
|
|
292
|
+
keyword: args.keyword,
|
|
293
|
+
limit: args.limit,
|
|
294
|
+
});
|
|
295
|
+
if (hits === null)
|
|
296
|
+
return { error: 'Dossier not found in your workspace.' };
|
|
297
|
+
return { keyword: args.keyword, hits };
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
export const addAttachment = defineTool({
|
|
301
|
+
...headOf('add_attachment'),
|
|
302
|
+
inputSchema: {
|
|
303
|
+
dossierId: z.string().describe(param('add_attachment', 'dossierId')),
|
|
304
|
+
filePath: z.string().min(1).describe(param('add_attachment', 'filePath')),
|
|
305
|
+
},
|
|
306
|
+
run: async (client, args) => {
|
|
307
|
+
const mimeType = MIME_BY_EXT[extname(args.filePath).toLowerCase()];
|
|
308
|
+
if (!mimeType) {
|
|
309
|
+
return {
|
|
310
|
+
error: `Unsupported file type "${extname(args.filePath)}". Allowed: ${Object.keys(MIME_BY_EXT).join(', ')}.`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
let bytes;
|
|
314
|
+
try {
|
|
315
|
+
bytes = readFileSync(args.filePath);
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return {
|
|
319
|
+
error: `Cannot read ${args.filePath}. Check the path with the user.`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (bytes.byteLength > 25 * 1024 * 1024) {
|
|
323
|
+
return { error: 'A file is limited to 25 MB.' };
|
|
324
|
+
}
|
|
325
|
+
const { attachmentId } = await client.action(api.dossiersApi.uploadAttachment, {
|
|
326
|
+
apiKey: requireApiKey(),
|
|
327
|
+
dossierId: args.dossierId,
|
|
328
|
+
name: basename(args.filePath),
|
|
329
|
+
mimeType,
|
|
330
|
+
base64: bytes.toString('base64'),
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
attachmentId,
|
|
334
|
+
name: basename(args.filePath),
|
|
335
|
+
bytes: bytes.byteLength,
|
|
336
|
+
note: 'Text extraction runs in the background; list_attachments shows when it is ready.',
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
export const listTemplates = defineTool({
|
|
341
|
+
...headOf('list_templates'),
|
|
342
|
+
inputSchema: {},
|
|
343
|
+
run: async (client) => ({
|
|
344
|
+
templates: await client.action(api.dossiersApi.listTemplates, {
|
|
345
|
+
apiKey: requireApiKey(),
|
|
346
|
+
}),
|
|
347
|
+
}),
|
|
348
|
+
});
|
|
349
|
+
export const applyTemplate = defineTool({
|
|
350
|
+
...headOf('apply_template'),
|
|
351
|
+
inputSchema: {
|
|
352
|
+
dossierId: z.string().describe(param('apply_template', 'dossierId')),
|
|
353
|
+
templateId: z.string().describe(param('apply_template', 'templateId')),
|
|
354
|
+
},
|
|
355
|
+
run: (client, args) => client.action(api.dossiersApi.applyTemplate, {
|
|
356
|
+
apiKey: requireApiKey(),
|
|
357
|
+
dossierId: args.dossierId,
|
|
358
|
+
templateId: args.templateId,
|
|
359
|
+
}),
|
|
360
|
+
});
|
|
168
361
|
export const dossierTools = [
|
|
169
362
|
listDossiers,
|
|
170
363
|
openDossier,
|
|
364
|
+
openQuestion,
|
|
171
365
|
saveFinding,
|
|
366
|
+
recordDecision,
|
|
172
367
|
loadDossier,
|
|
173
368
|
resolveQuestion,
|
|
369
|
+
listAttachments,
|
|
370
|
+
readAttachment,
|
|
371
|
+
searchInDossier,
|
|
372
|
+
addAttachment,
|
|
373
|
+
listTemplates,
|
|
374
|
+
applyTemplate,
|
|
174
375
|
];
|
package/dist/tools/ingest.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export declare const ingestCreateDocument: import("./define.js").ToolDef<{
|
|
|
13
13
|
en: "en";
|
|
14
14
|
}>;
|
|
15
15
|
totalPages: z.ZodNumber;
|
|
16
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
17
|
+
edition: z.ZodOptional<z.ZodString>;
|
|
16
18
|
}>;
|
|
17
19
|
export declare const ingestCreateSections: import("./define.js").ToolDef<{
|
|
18
20
|
documentId: z.ZodString;
|
|
@@ -71,6 +73,7 @@ export declare const ingestNormalizeCrossRefs: import("./define.js").ToolDef<{
|
|
|
71
73
|
}>;
|
|
72
74
|
export declare const ingestPublish: import("./define.js").ToolDef<{
|
|
73
75
|
documentId: z.ZodString;
|
|
76
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
74
77
|
}>;
|
|
75
78
|
export declare const ingestDelete: import("./define.js").ToolDef<{
|
|
76
79
|
documentId: z.ZodString;
|
|
@@ -88,6 +91,8 @@ export declare const ingestTools: (import("./define.js").ToolDef<{
|
|
|
88
91
|
en: "en";
|
|
89
92
|
}>;
|
|
90
93
|
totalPages: z.ZodNumber;
|
|
94
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
95
|
+
edition: z.ZodOptional<z.ZodString>;
|
|
91
96
|
}> | import("./define.js").ToolDef<{
|
|
92
97
|
documentId: z.ZodString;
|
|
93
98
|
sections: z.ZodArray<z.ZodObject<{
|
|
@@ -137,4 +142,7 @@ export declare const ingestTools: (import("./define.js").ToolDef<{
|
|
|
137
142
|
figureNumber: z.ZodString;
|
|
138
143
|
}> | import("./define.js").ToolDef<{
|
|
139
144
|
documentId: z.ZodString;
|
|
145
|
+
}> | import("./define.js").ToolDef<{
|
|
146
|
+
documentId: z.ZodString;
|
|
147
|
+
force: z.ZodOptional<z.ZodBoolean>;
|
|
140
148
|
}>)[];
|