@plasm_lang/vercel-agent 0.3.78 → 0.3.80
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/agent/instructions.md +1 -2
- package/bin/plasm-agent.mjs +5 -7
- package/package.json +26 -19
- package/scripts/plasm-cli.ts +4 -3
- package/scripts/run-plasm-cli.mjs +41 -0
- package/src/cli/init-scaffold.ts +198 -0
- package/src/cli/init.ts +16 -278
- package/src/cli/nitro-dev.ts +2 -6
- package/src/cli/node-dev-imports.ts +14 -0
- package/src/package-version.ts +14 -0
- package/src/project-info.ts +2 -1
- package/templates/mcp-radar/.vercelignore +3 -0
- package/templates/mcp-radar/README.md +139 -0
- package/templates/mcp-radar/agent/agent.ts +38 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/README.md +23 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/domain.yaml +329 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/eval/cases.yaml +112 -0
- package/templates/mcp-radar/agent/catalogs/hackernews/mappings.yaml +129 -0
- package/templates/mcp-radar/agent/catalogs/tavily/README.md +218 -0
- package/templates/mcp-radar/agent/catalogs/tavily/domain.yaml +373 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/cases.yaml +56 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.human.txt +67 -0
- package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.json +363 -0
- package/templates/mcp-radar/agent/catalogs/tavily/mappings.yaml +171 -0
- package/templates/mcp-radar/agent/channels/mcp-radar.ts +85 -0
- package/templates/mcp-radar/agent/hooks/proof-audit.ts +10 -0
- package/templates/mcp-radar/agent/instructions.md +24 -0
- package/templates/mcp-radar/agent/research/mcp-innovations-proof.md +4 -0
- package/templates/mcp-radar/agent/schedules/mcp-radar-scan.ts +14 -0
- package/templates/mcp-radar/agent/skills/mcp-proof-format.md +19 -0
- package/templates/mcp-radar/api/[[...path]].ts +23 -0
- package/templates/mcp-radar/evals/mcp-radar-discover.eval.ts +14 -0
- package/templates/mcp-radar/lib/proof-store.ts +265 -0
- package/templates/mcp-radar/lib/run-radar.ts +214 -0
- package/templates/mcp-radar/nitro.config.ts +17 -0
- package/templates/mcp-radar/package.json +14 -0
- package/templates/mcp-radar/public/index.html +10 -0
- package/templates/mcp-radar/routes/[...path].ts +29 -0
- package/templates/mcp-radar/scripts/run-evals.ts +46 -0
- package/templates/mcp-radar/scripts/smoke-channel.ts +66 -0
- package/templates/mcp-radar/vercel.json +15 -0
- package/templates/scaffold/.vercelignore +3 -0
- package/templates/scaffold/api/[[...path]].ts +23 -0
- package/templates/scaffold/nitro.config.ts +17 -0
- package/templates/scaffold/public/index.html +10 -0
- package/templates/scaffold/public/index.mcp-radar.html +10 -0
- package/templates/scaffold/routes/[...path].ts +29 -0
- package/templates/scaffold/vercel.default.json +4 -0
- package/templates/scaffold/vercel.mcp-radar.json +15 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
model: google/gemma-3-4b-it
|
|
2
|
+
eval summary: 7 cases | mean score 0.7982 | parse_ok 6/7 | typecheck_ok 6/7
|
|
3
|
+
|
|
4
|
+
════════════════════════════════════════════════════════════════════════
|
|
5
|
+
Pipeline failures (parse/typecheck never succeeded): 1
|
|
6
|
+
|
|
7
|
+
▸ tv-07 final_score=0.0000 semantic=0.000
|
|
8
|
+
goal: Web search for 'rust async' but return only titles and URLs in the result list
|
|
9
|
+
rounds=2 recovered=false pipeline_score=0.00
|
|
10
|
+
─── attempt 1 ───
|
|
11
|
+
step 0: ExtractResult{query="rust async", ~str}
|
|
12
|
+
reason: Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities.
|
|
13
|
+
validation: FAILED
|
|
14
|
+
• [parse] step 0 `ExtractResult{query="rust async", ~str}`
|
|
15
|
+
Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.
|
|
16
|
+
span_offset: 34
|
|
17
|
+
─── attempt 2 ───
|
|
18
|
+
step 0: ExtractResult{query="rust async", ~str}
|
|
19
|
+
reason: Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities.
|
|
20
|
+
correction_context_in (feedback from prior round, sent to LLM):
|
|
21
|
+
{
|
|
22
|
+
"round": 0,
|
|
23
|
+
"goal": "Web search for 'rust async' but return only titles and URLs in the result list",
|
|
24
|
+
"previous_steps": [
|
|
25
|
+
{
|
|
26
|
+
"text": "ExtractResult{query=\"rust async\", ~str}",
|
|
27
|
+
"reasoning": "Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities."
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"diagnostics": [
|
|
31
|
+
{
|
|
32
|
+
"step_index": 0,
|
|
33
|
+
"expression": "ExtractResult{query=\"rust async\", ~str}",
|
|
34
|
+
"error": {
|
|
35
|
+
"correction": "Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.",
|
|
36
|
+
"category": "parse",
|
|
37
|
+
"span_offset": 34
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
validation: FAILED
|
|
43
|
+
• [parse] step 0 `ExtractResult{query="rust async", ~str}`
|
|
44
|
+
Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.
|
|
45
|
+
span_offset: 34
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Expectation gaps (validated, soft score below 1.0): 2
|
|
49
|
+
|
|
50
|
+
▸ tv-01 blended_score=0.7750 semantic=0.750
|
|
51
|
+
goal: Research briefing: what are the main risks of LLM prompt injection in 2025?
|
|
52
|
+
expectation notes:
|
|
53
|
+
- entities got {"ResearchTask"} want any of ["SearchResult"]
|
|
54
|
+
final plan (for context):
|
|
55
|
+
step 0: ResearchTask{query="main risks of LLM prompt injection in 2025"}
|
|
56
|
+
reason: Retrieve ResearchTasks related to the specified query to initiate the research.
|
|
57
|
+
|
|
58
|
+
▸ tv-03 blended_score=0.8125 semantic=0.750
|
|
59
|
+
goal: Crawl the product marketing site up to depth 2 for a competitive summary
|
|
60
|
+
expectation notes:
|
|
61
|
+
- entities got {"ResearchTask"} want any of ["ExtractResult"]
|
|
62
|
+
final plan (for context):
|
|
63
|
+
step 0: ResearchTask{scope=Source(id), filter=marketing}
|
|
64
|
+
reason: Start by finding ResearchTasks specifically related to marketing sources.
|
|
65
|
+
|
|
66
|
+
════════════════════════════════════════════════════════════════════════
|
|
67
|
+
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cases": [
|
|
3
|
+
{
|
|
4
|
+
"attempt_trace": [
|
|
5
|
+
{
|
|
6
|
+
"attempt": 1,
|
|
7
|
+
"diagnostics": [
|
|
8
|
+
{
|
|
9
|
+
"error": {
|
|
10
|
+
"category": "parse",
|
|
11
|
+
"correction": "Expression: ResearchTask(42).method\n\n`method` is not a valid name after `ResearchTask.` (field, relation, or zero-arg action). Closest names in DOMAIN: model, content, input, status, request_id.\n\nAllowed names (8): content, created_at, input, model, request_id, response_time, sources, status\n\nFor reading scalar fields: `ResearchTask(id)[status,content]`.\n\nTo navigate: `ResearchTask(id).content` (relation or EntityRef).",
|
|
12
|
+
"span_offset": 17
|
|
13
|
+
},
|
|
14
|
+
"expression": "ResearchTask(42).method",
|
|
15
|
+
"step_index": 0
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"steps": [
|
|
19
|
+
{
|
|
20
|
+
"reasoning": "Retrieve the ResearchTask with ID 42 to access its method.",
|
|
21
|
+
"text": "ResearchTask(42).method"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"validation_ok": false
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"attempt": 2,
|
|
28
|
+
"correction_context_in": "{\n \"round\": 0,\n \"goal\": \"Research briefing: what are the main risks of LLM prompt injection in 2025?\",\n \"previous_steps\": [\n {\n \"text\": \"ResearchTask(42).method\",\n \"reasoning\": \"Retrieve the ResearchTask with ID 42 to access its method.\"\n }\n ],\n \"diagnostics\": [\n {\n \"step_index\": 0,\n \"expression\": \"ResearchTask(42).method\",\n \"error\": {\n \"correction\": \"Expression: ResearchTask(42).method\\n\\n`method` is not a valid name after `ResearchTask.` (field, relation, or zero-arg action). Closest names in DOMAIN: model, content, input, status, request_id.\\n\\nAllowed names (8): content, created_at, input, model, request_id, response_time, sources, status\\n\\nFor reading scalar fields: `ResearchTask(id)[status,content]`.\\n\\nTo navigate: `ResearchTask(id).content` (relation or EntityRef).\",\n \"category\": \"parse\",\n \"span_offset\": 17\n }\n }\n ]\n}",
|
|
29
|
+
"lexicon_notes": [
|
|
30
|
+
{
|
|
31
|
+
"emitted": "ResearchTask{query=\"main risks of LLM prompt injection in 2025\"}",
|
|
32
|
+
"resolved_to": "ResearchTask",
|
|
33
|
+
"step_index": 0
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"steps": [
|
|
37
|
+
{
|
|
38
|
+
"reasoning": "Retrieve ResearchTasks related to the specified query to initiate the research.",
|
|
39
|
+
"text": "ResearchTask{query=\"main risks of LLM prompt injection in 2025\"}"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"validation_ok": true
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"chain_match": true,
|
|
46
|
+
"correction": {
|
|
47
|
+
"final_full_ok": true,
|
|
48
|
+
"first_attempt_full_ok": false,
|
|
49
|
+
"first_attempt_had_errors": true,
|
|
50
|
+
"pipeline_score": 0.8500000238418579,
|
|
51
|
+
"recovered": true,
|
|
52
|
+
"rounds_used": 2
|
|
53
|
+
},
|
|
54
|
+
"correction_pipeline_score": 0.8500000238418579,
|
|
55
|
+
"entity_match": false,
|
|
56
|
+
"goal": "Research briefing: what are the main risks of LLM prompt injection in 2025?",
|
|
57
|
+
"id": "tv-01",
|
|
58
|
+
"notes": [
|
|
59
|
+
"entities got {\"ResearchTask\"} want any of [\"SearchResult\"]"
|
|
60
|
+
],
|
|
61
|
+
"parse_ok": true,
|
|
62
|
+
"pred_fields_match": true,
|
|
63
|
+
"pred_values_match": true,
|
|
64
|
+
"projection_match": true,
|
|
65
|
+
"score": 0.7749999761581421,
|
|
66
|
+
"semantic_score": 0.75,
|
|
67
|
+
"steps_ok": true,
|
|
68
|
+
"tags": [
|
|
69
|
+
"search"
|
|
70
|
+
],
|
|
71
|
+
"typecheck_ok": true
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"attempt_trace": [
|
|
75
|
+
{
|
|
76
|
+
"attempt": 1,
|
|
77
|
+
"steps": [
|
|
78
|
+
{
|
|
79
|
+
"reasoning": "To find URLs from the documentation.",
|
|
80
|
+
"text": "ExtractResult{query=\"urls~str\"}"
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"validation_ok": true
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"chain_match": true,
|
|
87
|
+
"correction": {
|
|
88
|
+
"final_full_ok": true,
|
|
89
|
+
"first_attempt_full_ok": true,
|
|
90
|
+
"first_attempt_had_errors": false,
|
|
91
|
+
"pipeline_score": 1.0,
|
|
92
|
+
"recovered": false,
|
|
93
|
+
"rounds_used": 1
|
|
94
|
+
},
|
|
95
|
+
"correction_pipeline_score": 1.0,
|
|
96
|
+
"entity_match": true,
|
|
97
|
+
"goal": "Extract readable text from these documentation URLs for offline notes",
|
|
98
|
+
"id": "tv-02",
|
|
99
|
+
"notes": [],
|
|
100
|
+
"parse_ok": true,
|
|
101
|
+
"pred_fields_match": true,
|
|
102
|
+
"pred_values_match": true,
|
|
103
|
+
"projection_match": true,
|
|
104
|
+
"score": 1.0,
|
|
105
|
+
"semantic_score": 1.0,
|
|
106
|
+
"steps_ok": true,
|
|
107
|
+
"tags": [
|
|
108
|
+
"extract"
|
|
109
|
+
],
|
|
110
|
+
"typecheck_ok": true
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"attempt_trace": [
|
|
114
|
+
{
|
|
115
|
+
"attempt": 1,
|
|
116
|
+
"lexicon_notes": [
|
|
117
|
+
{
|
|
118
|
+
"emitted": "ResearchTask{scope=Source(id), filter=marketing}",
|
|
119
|
+
"resolved_to": "ResearchTask",
|
|
120
|
+
"step_index": 0
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
"steps": [
|
|
124
|
+
{
|
|
125
|
+
"reasoning": "Start by finding ResearchTasks specifically related to marketing sources.",
|
|
126
|
+
"text": "ResearchTask{scope=Source(id), filter=marketing}"
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"validation_ok": true
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
"chain_match": true,
|
|
133
|
+
"correction": {
|
|
134
|
+
"final_full_ok": true,
|
|
135
|
+
"first_attempt_full_ok": true,
|
|
136
|
+
"first_attempt_had_errors": false,
|
|
137
|
+
"pipeline_score": 1.0,
|
|
138
|
+
"recovered": false,
|
|
139
|
+
"rounds_used": 1
|
|
140
|
+
},
|
|
141
|
+
"correction_pipeline_score": 1.0,
|
|
142
|
+
"entity_match": false,
|
|
143
|
+
"goal": "Crawl the product marketing site up to depth 2 for a competitive summary",
|
|
144
|
+
"id": "tv-03",
|
|
145
|
+
"notes": [
|
|
146
|
+
"entities got {\"ResearchTask\"} want any of [\"ExtractResult\"]"
|
|
147
|
+
],
|
|
148
|
+
"parse_ok": true,
|
|
149
|
+
"pred_fields_match": true,
|
|
150
|
+
"pred_values_match": true,
|
|
151
|
+
"projection_match": true,
|
|
152
|
+
"score": 0.8125,
|
|
153
|
+
"semantic_score": 0.75,
|
|
154
|
+
"steps_ok": true,
|
|
155
|
+
"tags": [
|
|
156
|
+
"crawl"
|
|
157
|
+
],
|
|
158
|
+
"typecheck_ok": true
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"attempt_trace": [
|
|
162
|
+
{
|
|
163
|
+
"attempt": 1,
|
|
164
|
+
"steps": [
|
|
165
|
+
{
|
|
166
|
+
"reasoning": "To initiate a research task and obtain its ID.",
|
|
167
|
+
"text": "ResearchTask(id)"
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
"validation_ok": true
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"chain_match": true,
|
|
174
|
+
"correction": {
|
|
175
|
+
"final_full_ok": true,
|
|
176
|
+
"first_attempt_full_ok": true,
|
|
177
|
+
"first_attempt_had_errors": false,
|
|
178
|
+
"pipeline_score": 1.0,
|
|
179
|
+
"recovered": false,
|
|
180
|
+
"rounds_used": 1
|
|
181
|
+
},
|
|
182
|
+
"correction_pipeline_score": 1.0,
|
|
183
|
+
"entity_match": true,
|
|
184
|
+
"goal": "Start a deep research job on quantum error correction and save the request id",
|
|
185
|
+
"id": "tv-04",
|
|
186
|
+
"notes": [],
|
|
187
|
+
"parse_ok": true,
|
|
188
|
+
"pred_fields_match": true,
|
|
189
|
+
"pred_values_match": true,
|
|
190
|
+
"projection_match": true,
|
|
191
|
+
"score": 1.0,
|
|
192
|
+
"semantic_score": 1.0,
|
|
193
|
+
"steps_ok": true,
|
|
194
|
+
"tags": [
|
|
195
|
+
"create"
|
|
196
|
+
],
|
|
197
|
+
"typecheck_ok": true
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"attempt_trace": [
|
|
201
|
+
{
|
|
202
|
+
"attempt": 1,
|
|
203
|
+
"steps": [
|
|
204
|
+
{
|
|
205
|
+
"reasoning": "To find the research task associated with the request identifier.",
|
|
206
|
+
"text": "ResearchTask(req_abc123)"
|
|
207
|
+
}
|
|
208
|
+
],
|
|
209
|
+
"validation_ok": true
|
|
210
|
+
}
|
|
211
|
+
],
|
|
212
|
+
"chain_match": true,
|
|
213
|
+
"correction": {
|
|
214
|
+
"final_full_ok": true,
|
|
215
|
+
"first_attempt_full_ok": true,
|
|
216
|
+
"first_attempt_had_errors": false,
|
|
217
|
+
"pipeline_score": 1.0,
|
|
218
|
+
"recovered": false,
|
|
219
|
+
"rounds_used": 1
|
|
220
|
+
},
|
|
221
|
+
"correction_pipeline_score": 1.0,
|
|
222
|
+
"entity_match": true,
|
|
223
|
+
"goal": "Poll research task req_abc123 until the report body is ready",
|
|
224
|
+
"id": "tv-05",
|
|
225
|
+
"notes": [],
|
|
226
|
+
"parse_ok": true,
|
|
227
|
+
"pred_fields_match": true,
|
|
228
|
+
"pred_values_match": true,
|
|
229
|
+
"projection_match": true,
|
|
230
|
+
"score": 1.0,
|
|
231
|
+
"semantic_score": 1.0,
|
|
232
|
+
"steps_ok": true,
|
|
233
|
+
"tags": [
|
|
234
|
+
"get"
|
|
235
|
+
],
|
|
236
|
+
"typecheck_ok": true
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"attempt_trace": [
|
|
240
|
+
{
|
|
241
|
+
"attempt": 1,
|
|
242
|
+
"steps": [
|
|
243
|
+
{
|
|
244
|
+
"reasoning": "The goal is to kick off async research on a topic, and the goal specifies the ResearchTask ID as 42.",
|
|
245
|
+
"text": "ResearchTask(42)"
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
"validation_ok": true
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
"chain_match": true,
|
|
252
|
+
"correction": {
|
|
253
|
+
"final_full_ok": true,
|
|
254
|
+
"first_attempt_full_ok": true,
|
|
255
|
+
"first_attempt_had_errors": false,
|
|
256
|
+
"pipeline_score": 1.0,
|
|
257
|
+
"recovered": false,
|
|
258
|
+
"rounds_used": 1
|
|
259
|
+
},
|
|
260
|
+
"correction_pipeline_score": 1.0,
|
|
261
|
+
"entity_match": true,
|
|
262
|
+
"goal": "Kick off async research on a topic, then fetch status when the user returns",
|
|
263
|
+
"id": "tv-06",
|
|
264
|
+
"notes": [],
|
|
265
|
+
"parse_ok": true,
|
|
266
|
+
"pred_fields_match": true,
|
|
267
|
+
"pred_values_match": true,
|
|
268
|
+
"projection_match": true,
|
|
269
|
+
"score": 1.0,
|
|
270
|
+
"semantic_score": 1.0,
|
|
271
|
+
"steps_ok": true,
|
|
272
|
+
"tags": [
|
|
273
|
+
"multi_step"
|
|
274
|
+
],
|
|
275
|
+
"typecheck_ok": true
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"attempt_trace": [
|
|
279
|
+
{
|
|
280
|
+
"attempt": 1,
|
|
281
|
+
"diagnostics": [
|
|
282
|
+
{
|
|
283
|
+
"error": {
|
|
284
|
+
"category": "parse",
|
|
285
|
+
"correction": "Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.",
|
|
286
|
+
"span_offset": 34
|
|
287
|
+
},
|
|
288
|
+
"expression": "ExtractResult{query=\"rust async\", ~str}",
|
|
289
|
+
"step_index": 0
|
|
290
|
+
}
|
|
291
|
+
],
|
|
292
|
+
"steps": [
|
|
293
|
+
{
|
|
294
|
+
"reasoning": "Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities.",
|
|
295
|
+
"text": "ExtractResult{query=\"rust async\", ~str}"
|
|
296
|
+
}
|
|
297
|
+
],
|
|
298
|
+
"validation_ok": false
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"attempt": 2,
|
|
302
|
+
"correction_context_in": "{\n \"round\": 0,\n \"goal\": \"Web search for 'rust async' but return only titles and URLs in the result list\",\n \"previous_steps\": [\n {\n \"text\": \"ExtractResult{query=\\\"rust async\\\", ~str}\",\n \"reasoning\": \"Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities.\"\n }\n ],\n \"diagnostics\": [\n {\n \"step_index\": 0,\n \"expression\": \"ExtractResult{query=\\\"rust async\\\", ~str}\",\n \"error\": {\n \"correction\": \"Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.\",\n \"category\": \"parse\",\n \"span_offset\": 34\n }\n }\n ]\n}",
|
|
303
|
+
"diagnostics": [
|
|
304
|
+
{
|
|
305
|
+
"error": {
|
|
306
|
+
"category": "parse",
|
|
307
|
+
"correction": "Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.",
|
|
308
|
+
"span_offset": 34
|
|
309
|
+
},
|
|
310
|
+
"expression": "ExtractResult{query=\"rust async\", ~str}",
|
|
311
|
+
"step_index": 0
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"steps": [
|
|
315
|
+
{
|
|
316
|
+
"reasoning": "Search for 'rust async' using full-text search and retrieve relevant ExtractResult entities.",
|
|
317
|
+
"text": "ExtractResult{query=\"rust async\", ~str}"
|
|
318
|
+
}
|
|
319
|
+
],
|
|
320
|
+
"validation_ok": false
|
|
321
|
+
}
|
|
322
|
+
],
|
|
323
|
+
"chain_match": false,
|
|
324
|
+
"correction": {
|
|
325
|
+
"final_full_ok": false,
|
|
326
|
+
"first_attempt_full_ok": false,
|
|
327
|
+
"first_attempt_had_errors": true,
|
|
328
|
+
"last_failure": [
|
|
329
|
+
{
|
|
330
|
+
"error": {
|
|
331
|
+
"category": "parse",
|
|
332
|
+
"correction": "Fix spelling so identifiers, `=`, `{{}}`, `.`, and parentheses match the SYNTAX block in the prompt.",
|
|
333
|
+
"span_offset": 34
|
|
334
|
+
},
|
|
335
|
+
"expression": "ExtractResult{query=\"rust async\", ~str}",
|
|
336
|
+
"step_index": 0
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
"pipeline_score": 0.0,
|
|
340
|
+
"recovered": false,
|
|
341
|
+
"rounds_used": 2
|
|
342
|
+
},
|
|
343
|
+
"correction_pipeline_score": 0.0,
|
|
344
|
+
"entity_match": false,
|
|
345
|
+
"goal": "Web search for 'rust async' but return only titles and URLs in the result list",
|
|
346
|
+
"id": "tv-07",
|
|
347
|
+
"notes": [],
|
|
348
|
+
"parse_ok": false,
|
|
349
|
+
"pred_fields_match": false,
|
|
350
|
+
"pred_values_match": false,
|
|
351
|
+
"projection_match": false,
|
|
352
|
+
"score": 0.0,
|
|
353
|
+
"semantic_score": 0.0,
|
|
354
|
+
"steps_ok": false,
|
|
355
|
+
"tags": [
|
|
356
|
+
"search",
|
|
357
|
+
"projection"
|
|
358
|
+
],
|
|
359
|
+
"typecheck_ok": false
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
"model": "google/gemma-3-4b-it"
|
|
363
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Tavily REST API — CML mappings. Domain model in domain.yaml.
|
|
2
|
+
#
|
|
3
|
+
# Base URL: Tavily HTTP API (set provider origin with --backend).
|
|
4
|
+
# All endpoints use POST except research_get (GET).
|
|
5
|
+
#
|
|
6
|
+
# All search/extract/crawl/research parameters go in the JSON request body,
|
|
7
|
+
# not the URL query string. The CML `body:` block is used throughout.
|
|
8
|
+
#
|
|
9
|
+
# Null-filtering: CML if/exists expressions emit null for absent optional
|
|
10
|
+
# params; the runtime strips null fields from the body before sending.
|
|
11
|
+
#
|
|
12
|
+
# include_domains / exclude_domains are sent as JSON arrays in the body:
|
|
13
|
+
# "include_domains": ["reuters.com", "bbc.com"]
|
|
14
|
+
# The `type: var` expression evaluates Value::Array which is serialised as
|
|
15
|
+
# a JSON array in the body (unlike query params where Array → repeated key).
|
|
16
|
+
#
|
|
17
|
+
# research_create returns a single object (not a list), decoded as `single`.
|
|
18
|
+
# research_get polls status — GET /research/{request_id}.
|
|
19
|
+
|
|
20
|
+
# ── Search ────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
web_search:
|
|
23
|
+
method: POST
|
|
24
|
+
path:
|
|
25
|
+
- { type: literal, value: search }
|
|
26
|
+
response:
|
|
27
|
+
items: results
|
|
28
|
+
body:
|
|
29
|
+
type: object
|
|
30
|
+
fields:
|
|
31
|
+
- [query, { type: var, name: query }]
|
|
32
|
+
- [search_depth, { type: if, condition: { type: exists, var: search_depth },
|
|
33
|
+
then_expr: { type: var, name: search_depth },
|
|
34
|
+
else_expr: { type: const, value: null } }]
|
|
35
|
+
- [chunks_per_source, { type: if, condition: { type: exists, var: chunks_per_source },
|
|
36
|
+
then_expr: { type: var, name: chunks_per_source },
|
|
37
|
+
else_expr: { type: const, value: null } }]
|
|
38
|
+
- [max_results, { type: if, condition: { type: exists, var: max_results },
|
|
39
|
+
then_expr: { type: var, name: max_results },
|
|
40
|
+
else_expr: { type: const, value: null } }]
|
|
41
|
+
- [topic, { type: if, condition: { type: exists, var: topic },
|
|
42
|
+
then_expr: { type: var, name: topic },
|
|
43
|
+
else_expr: { type: const, value: null } }]
|
|
44
|
+
- [time_range, { type: if, condition: { type: exists, var: time_range },
|
|
45
|
+
then_expr: { type: var, name: time_range },
|
|
46
|
+
else_expr: { type: const, value: null } }]
|
|
47
|
+
- [start_date, { type: if, condition: { type: exists, var: start_date },
|
|
48
|
+
then_expr: { type: var, name: start_date },
|
|
49
|
+
else_expr: { type: const, value: null } }]
|
|
50
|
+
- [end_date, { type: if, condition: { type: exists, var: end_date },
|
|
51
|
+
then_expr: { type: var, name: end_date },
|
|
52
|
+
else_expr: { type: const, value: null } }]
|
|
53
|
+
- [include_answer, { type: if, condition: { type: exists, var: include_answer },
|
|
54
|
+
then_expr: { type: var, name: include_answer },
|
|
55
|
+
else_expr: { type: const, value: null } }]
|
|
56
|
+
- [include_raw_content, { type: if, condition: { type: exists, var: include_raw_content },
|
|
57
|
+
then_expr: { type: var, name: include_raw_content },
|
|
58
|
+
else_expr: { type: const, value: null } }]
|
|
59
|
+
- [include_images, { type: if, condition: { type: exists, var: include_images },
|
|
60
|
+
then_expr: { type: var, name: include_images },
|
|
61
|
+
else_expr: { type: const, value: null } }]
|
|
62
|
+
- [include_favicon, { type: if, condition: { type: exists, var: include_favicon },
|
|
63
|
+
then_expr: { type: var, name: include_favicon },
|
|
64
|
+
else_expr: { type: const, value: null } }]
|
|
65
|
+
- [include_domains, { type: if, condition: { type: exists, var: include_domains },
|
|
66
|
+
then_expr: { type: var, name: include_domains },
|
|
67
|
+
else_expr: { type: const, value: null } }]
|
|
68
|
+
- [exclude_domains, { type: if, condition: { type: exists, var: exclude_domains },
|
|
69
|
+
then_expr: { type: var, name: exclude_domains },
|
|
70
|
+
else_expr: { type: const, value: null } }]
|
|
71
|
+
- [country, { type: if, condition: { type: exists, var: country },
|
|
72
|
+
then_expr: { type: var, name: country },
|
|
73
|
+
else_expr: { type: const, value: null } }]
|
|
74
|
+
- [auto_parameters, { type: if, condition: { type: exists, var: auto_parameters },
|
|
75
|
+
then_expr: { type: var, name: auto_parameters },
|
|
76
|
+
else_expr: { type: const, value: null } }]
|
|
77
|
+
- [exact_match, { type: if, condition: { type: exists, var: exact_match },
|
|
78
|
+
then_expr: { type: var, name: exact_match },
|
|
79
|
+
else_expr: { type: const, value: null } }]
|
|
80
|
+
|
|
81
|
+
# ── Extract ───────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
url_extract:
|
|
84
|
+
method: POST
|
|
85
|
+
path:
|
|
86
|
+
- { type: literal, value: extract }
|
|
87
|
+
response:
|
|
88
|
+
items: results
|
|
89
|
+
body:
|
|
90
|
+
type: object
|
|
91
|
+
fields:
|
|
92
|
+
- [urls, { type: var, name: urls }]
|
|
93
|
+
- [query, { type: if, condition: { type: exists, var: query },
|
|
94
|
+
then_expr: { type: var, name: query },
|
|
95
|
+
else_expr: { type: const, value: null } }]
|
|
96
|
+
- [extract_depth, { type: if, condition: { type: exists, var: extract_depth },
|
|
97
|
+
then_expr: { type: var, name: extract_depth },
|
|
98
|
+
else_expr: { type: const, value: null } }]
|
|
99
|
+
- [format, { type: if, condition: { type: exists, var: format },
|
|
100
|
+
then_expr: { type: var, name: format },
|
|
101
|
+
else_expr: { type: const, value: null } }]
|
|
102
|
+
- [chunks_per_source, { type: if, condition: { type: exists, var: chunks_per_source },
|
|
103
|
+
then_expr: { type: var, name: chunks_per_source },
|
|
104
|
+
else_expr: { type: const, value: null } }]
|
|
105
|
+
- [include_images, { type: if, condition: { type: exists, var: include_images },
|
|
106
|
+
then_expr: { type: var, name: include_images },
|
|
107
|
+
else_expr: { type: const, value: null } }]
|
|
108
|
+
- [include_favicon, { type: if, condition: { type: exists, var: include_favicon },
|
|
109
|
+
then_expr: { type: var, name: include_favicon },
|
|
110
|
+
else_expr: { type: const, value: null } }]
|
|
111
|
+
|
|
112
|
+
# ── Crawl ─────────────────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
site_crawl:
|
|
115
|
+
method: POST
|
|
116
|
+
path:
|
|
117
|
+
- { type: literal, value: crawl }
|
|
118
|
+
response:
|
|
119
|
+
items: results
|
|
120
|
+
body:
|
|
121
|
+
type: object
|
|
122
|
+
fields:
|
|
123
|
+
- [url, { type: var, name: url }]
|
|
124
|
+
- [instructions, { type: if, condition: { type: exists, var: instructions },
|
|
125
|
+
then_expr: { type: var, name: instructions },
|
|
126
|
+
else_expr: { type: const, value: null } }]
|
|
127
|
+
- [max_depth, { type: if, condition: { type: exists, var: max_depth },
|
|
128
|
+
then_expr: { type: var, name: max_depth },
|
|
129
|
+
else_expr: { type: const, value: null } }]
|
|
130
|
+
- [max_breadth, { type: if, condition: { type: exists, var: max_breadth },
|
|
131
|
+
then_expr: { type: var, name: max_breadth },
|
|
132
|
+
else_expr: { type: const, value: null } }]
|
|
133
|
+
- [limit, { type: if, condition: { type: exists, var: limit },
|
|
134
|
+
then_expr: { type: var, name: limit },
|
|
135
|
+
else_expr: { type: const, value: null } }]
|
|
136
|
+
- [chunks_per_source, { type: if, condition: { type: exists, var: chunks_per_source },
|
|
137
|
+
then_expr: { type: var, name: chunks_per_source },
|
|
138
|
+
else_expr: { type: const, value: null } }]
|
|
139
|
+
- [include_images, { type: if, condition: { type: exists, var: include_images },
|
|
140
|
+
then_expr: { type: var, name: include_images },
|
|
141
|
+
else_expr: { type: const, value: null } }]
|
|
142
|
+
|
|
143
|
+
# ── Research ──────────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
research_create:
|
|
146
|
+
# POST /research — async, returns {request_id, created_at, status, input, model}
|
|
147
|
+
# Explicit object body (not body: {type: var, name: input}): execute_create splats each
|
|
148
|
+
# param key into env, overwriting env["input"] with the scalar question string.
|
|
149
|
+
method: POST
|
|
150
|
+
path:
|
|
151
|
+
- { type: literal, value: research }
|
|
152
|
+
response: single
|
|
153
|
+
body:
|
|
154
|
+
type: object
|
|
155
|
+
fields:
|
|
156
|
+
- [input, { type: var, name: input }]
|
|
157
|
+
- [model, { type: if, condition: { type: exists, var: model },
|
|
158
|
+
then_expr: { type: var, name: model },
|
|
159
|
+
else_expr: { type: const, value: null } }]
|
|
160
|
+
- [citation_format, { type: if, condition: { type: exists, var: citation_format },
|
|
161
|
+
then_expr: { type: var, name: citation_format },
|
|
162
|
+
else_expr: { type: const, value: null } }]
|
|
163
|
+
|
|
164
|
+
research_get:
|
|
165
|
+
# GET /research/{request_id} — poll for task status and results
|
|
166
|
+
# id = request_id value from the ResearchTask entity
|
|
167
|
+
method: GET
|
|
168
|
+
path:
|
|
169
|
+
- { type: literal, value: research }
|
|
170
|
+
- { type: var, name: id }
|
|
171
|
+
response: single
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
import { waitUntil } from "@vercel/functions";
|
|
4
|
+
import { defineChannel } from "@plasm_lang/vercel-agent";
|
|
5
|
+
|
|
6
|
+
import { readProofMarkdown } from "../../lib/proof-store.js";
|
|
7
|
+
import { radarStatus, runRadar } from "../../lib/run-radar.js";
|
|
8
|
+
|
|
9
|
+
function readJsonBody(req: IncomingMessage): Promise<unknown> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
let body = "";
|
|
12
|
+
req.on("data", (chunk) => {
|
|
13
|
+
body += chunk;
|
|
14
|
+
});
|
|
15
|
+
req.on("end", () => {
|
|
16
|
+
if (!body.trim()) {
|
|
17
|
+
resolve({});
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
resolve(JSON.parse(body));
|
|
22
|
+
} catch (err) {
|
|
23
|
+
reject(err);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
req.on("error", reject);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sendJson(res: ServerResponse, status: number, payload: unknown): void {
|
|
31
|
+
res.statusCode = status;
|
|
32
|
+
res.setHeader("content-type", "application/json; charset=utf-8");
|
|
33
|
+
res.end(JSON.stringify(payload, null, 2));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default defineChannel({
|
|
37
|
+
name: "mcp-radar",
|
|
38
|
+
routes: [
|
|
39
|
+
{
|
|
40
|
+
method: "POST",
|
|
41
|
+
path: "/channel/mcp-radar/run",
|
|
42
|
+
handler: async (req, res, ctx) => {
|
|
43
|
+
const body = (await readJsonBody(req)) as { force?: boolean };
|
|
44
|
+
const options = { force: body.force === true };
|
|
45
|
+
|
|
46
|
+
if (process.env.VERCEL === "1") {
|
|
47
|
+
waitUntil(
|
|
48
|
+
runRadar(ctx, options).catch((err: unknown) => {
|
|
49
|
+
console.error("[mcp-radar] background run failed:", err);
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
sendJson(res, 202, { accepted: true, async: true });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const result = await runRadar(ctx, options);
|
|
57
|
+
const status = result.ok ? 200 : 500;
|
|
58
|
+
sendJson(res, status, result);
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
method: "GET",
|
|
63
|
+
path: "/channel/mcp-radar/proof",
|
|
64
|
+
handler: async (req, res, ctx) => {
|
|
65
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
66
|
+
const format = url.searchParams.get("format");
|
|
67
|
+
const markdown = await readProofMarkdown(ctx.agentRoot);
|
|
68
|
+
if (format === "json") {
|
|
69
|
+
sendJson(res, 200, { markdown, path: "agent/research/mcp-innovations-proof.md" });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
res.statusCode = 200;
|
|
73
|
+
res.setHeader("content-type", "text/markdown; charset=utf-8");
|
|
74
|
+
res.end(markdown);
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
method: "GET",
|
|
79
|
+
path: "/channel/mcp-radar/status",
|
|
80
|
+
handler: async (_req, res, ctx) => {
|
|
81
|
+
sendJson(res, 200, await radarStatus(ctx.agentRoot));
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
});
|