@plasm_lang/vercel-agent 0.3.77 → 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/README.md +9 -2
- package/agent/instructions.md +1 -2
- package/bin/plasm-agent.mjs +5 -7
- package/package.json +29 -19
- package/scripts/build-stubs.ts +27 -0
- package/scripts/plasm-cli.ts +162 -0
- package/scripts/register-plasm-loader.mjs +6 -0
- package/scripts/run-plasm-cli.mjs +41 -0
- package/src/cli/init-scaffold.ts +198 -0
- package/src/cli/init.ts +20 -208
- package/src/cli/nitro-dev.ts +69 -0
- package/src/cli/node-dev-imports.ts +14 -0
- package/src/engine/create-host-transport.ts +7 -0
- package/src/index.ts +1 -1
- package/src/package-version.ts +14 -0
- package/src/project-info.ts +2 -1
- package/src/stubs/catalog-client.ts +2 -2
- package/src/tools/harness-tools.ts +42 -41
- package/src/tools/plasm-tools.ts +64 -55
- package/src/tools/tool-input.ts +9 -0
- 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,112 @@
|
|
|
1
|
+
# Hacker News — NL→Plasm eval cases (schema: hackernews)
|
|
2
|
+
- id: hn-01
|
|
3
|
+
schema: hackernews
|
|
4
|
+
goal: Top story id list from the front page feed
|
|
5
|
+
tags: [query, enum]
|
|
6
|
+
covers: [query_filtered]
|
|
7
|
+
expect:
|
|
8
|
+
entities_any: [Item]
|
|
9
|
+
pred_fields_any: [feed]
|
|
10
|
+
pred_values_any: [top]
|
|
11
|
+
|
|
12
|
+
- id: hn-02
|
|
13
|
+
schema: hackernews
|
|
14
|
+
goal: Newest story ids
|
|
15
|
+
tags: [query, enum]
|
|
16
|
+
covers: [query_filtered]
|
|
17
|
+
expect:
|
|
18
|
+
entities_any: [Item]
|
|
19
|
+
pred_fields_any: [feed]
|
|
20
|
+
pred_values_any: [new]
|
|
21
|
+
|
|
22
|
+
- id: hn-03
|
|
23
|
+
schema: hackernews
|
|
24
|
+
goal: Show HN category story ids
|
|
25
|
+
tags: [query, enum]
|
|
26
|
+
covers: [query_filtered]
|
|
27
|
+
expect:
|
|
28
|
+
entities_any: [Item]
|
|
29
|
+
pred_fields_any: [feed]
|
|
30
|
+
pred_values_any: [show]
|
|
31
|
+
|
|
32
|
+
- id: hn-04
|
|
33
|
+
schema: hackernews
|
|
34
|
+
goal: Fetch item 8863
|
|
35
|
+
tags: [get]
|
|
36
|
+
covers: [get]
|
|
37
|
+
expect:
|
|
38
|
+
entities_any: [Item]
|
|
39
|
+
|
|
40
|
+
- id: hn-05
|
|
41
|
+
schema: hackernews
|
|
42
|
+
goal: Load user jl
|
|
43
|
+
tags: [get]
|
|
44
|
+
covers: [get]
|
|
45
|
+
expect:
|
|
46
|
+
entities_any: [User]
|
|
47
|
+
|
|
48
|
+
- id: hn-06
|
|
49
|
+
schema: hackernews
|
|
50
|
+
goal: Item 1 and only its title field
|
|
51
|
+
tags: [get, projection]
|
|
52
|
+
covers: [get, projection]
|
|
53
|
+
expect:
|
|
54
|
+
entities_any: [Item]
|
|
55
|
+
projection_contains: [title]
|
|
56
|
+
|
|
57
|
+
- id: hn-07
|
|
58
|
+
schema: hackernews
|
|
59
|
+
goal: Story 8863 and expand its direct replies
|
|
60
|
+
tags: [chain]
|
|
61
|
+
covers: [chain]
|
|
62
|
+
expect:
|
|
63
|
+
entities_any: [Item]
|
|
64
|
+
chain_selectors_any: [kids]
|
|
65
|
+
|
|
66
|
+
- id: hn-08
|
|
67
|
+
schema: hackernews
|
|
68
|
+
goal: >-
|
|
69
|
+
Compare item 1 and user jl in one pass so both profiles are available for discussion.
|
|
70
|
+
tags: [multi_step]
|
|
71
|
+
covers: [multi_step]
|
|
72
|
+
expect:
|
|
73
|
+
entities_any: [Item, User]
|
|
74
|
+
|
|
75
|
+
- id: hn-09
|
|
76
|
+
schema: hackernews
|
|
77
|
+
goal: >-
|
|
78
|
+
Snapshot the live max item id, the recent item id slice from updates, and the recent
|
|
79
|
+
username slice from updates in one analysis pass.
|
|
80
|
+
tags: [query_all]
|
|
81
|
+
covers: [query_all]
|
|
82
|
+
expect:
|
|
83
|
+
entities_any: [MaxItemId, RecentUpdatedItem, RecentUpdatedUser]
|
|
84
|
+
|
|
85
|
+
- id: hn-10
|
|
86
|
+
schema: hackernews
|
|
87
|
+
goal: Poll item 126809 and list its poll option rows
|
|
88
|
+
tags: [chain]
|
|
89
|
+
covers: [chain]
|
|
90
|
+
expect:
|
|
91
|
+
entities_any: [Item]
|
|
92
|
+
chain_selectors_any: [poll_options]
|
|
93
|
+
|
|
94
|
+
- id: hn-11
|
|
95
|
+
schema: hackernews
|
|
96
|
+
goal: Search HN stories for “rust memory safety” and list matching item ids
|
|
97
|
+
tags: [search]
|
|
98
|
+
covers: [search_text]
|
|
99
|
+
expect:
|
|
100
|
+
entities_any: [Item]
|
|
101
|
+
pred_fields_any: [query]
|
|
102
|
+
|
|
103
|
+
- id: hn-12
|
|
104
|
+
schema: hackernews
|
|
105
|
+
goal: >-
|
|
106
|
+
Find HN stories mentioning “Show HN”, then load item 1 by id for the classic “Y Combinator”
|
|
107
|
+
post in one pass.
|
|
108
|
+
tags: [search, get, multi_step]
|
|
109
|
+
covers: [search_text, get, multi_step]
|
|
110
|
+
expect:
|
|
111
|
+
entities_any: [Item]
|
|
112
|
+
pred_fields_any: [query]
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Hacker News: Firebase v0 (https://github.com/HackerNews/API) plus the public
|
|
2
|
+
# Algolia HN search API (https://hn.algolia.com/api) — two origins, one CGS. Firebase paths are
|
|
3
|
+
# relative to http_backend; search uses a full https:// URL so plasm-runtime joins the absolute
|
|
4
|
+
# request URL without double-prefixing the catalog origin (see `join_base_url_path`).
|
|
5
|
+
|
|
6
|
+
item_search:
|
|
7
|
+
method: GET
|
|
8
|
+
path:
|
|
9
|
+
- type: literal
|
|
10
|
+
value: "https://hn.algolia.com/api/v1/search"
|
|
11
|
+
response:
|
|
12
|
+
items: hits
|
|
13
|
+
query:
|
|
14
|
+
type: object
|
|
15
|
+
fields:
|
|
16
|
+
- - query
|
|
17
|
+
- type: var
|
|
18
|
+
name: query
|
|
19
|
+
- - tags
|
|
20
|
+
- type: if
|
|
21
|
+
condition: { type: exists, var: tags }
|
|
22
|
+
then_expr: { type: var, name: tags }
|
|
23
|
+
else_expr: { type: const, value: "story" }
|
|
24
|
+
- - page
|
|
25
|
+
- type: if
|
|
26
|
+
condition: { type: exists, var: page }
|
|
27
|
+
then_expr: { type: var, name: page }
|
|
28
|
+
else_expr: { type: const, value: null }
|
|
29
|
+
- - hitsPerPage
|
|
30
|
+
- type: if
|
|
31
|
+
condition: { type: exists, var: per_page }
|
|
32
|
+
then_expr: { type: var, name: per_page }
|
|
33
|
+
else_expr: { type: const, value: null }
|
|
34
|
+
|
|
35
|
+
item_search_by_date:
|
|
36
|
+
method: GET
|
|
37
|
+
path:
|
|
38
|
+
- type: literal
|
|
39
|
+
value: "https://hn.algolia.com/api/v1/search_by_date"
|
|
40
|
+
response:
|
|
41
|
+
items: hits
|
|
42
|
+
query:
|
|
43
|
+
type: object
|
|
44
|
+
fields:
|
|
45
|
+
- - query
|
|
46
|
+
- type: var
|
|
47
|
+
name: query
|
|
48
|
+
- - tags
|
|
49
|
+
- type: if
|
|
50
|
+
condition: { type: exists, var: tags }
|
|
51
|
+
then_expr: { type: var, name: tags }
|
|
52
|
+
else_expr: { type: const, value: "story" }
|
|
53
|
+
- - page
|
|
54
|
+
- type: if
|
|
55
|
+
condition: { type: exists, var: page }
|
|
56
|
+
then_expr: { type: var, name: page }
|
|
57
|
+
else_expr: { type: const, value: null }
|
|
58
|
+
- - hitsPerPage
|
|
59
|
+
- type: if
|
|
60
|
+
condition: { type: exists, var: per_page }
|
|
61
|
+
then_expr: { type: var, name: per_page }
|
|
62
|
+
else_expr: { type: const, value: null }
|
|
63
|
+
|
|
64
|
+
item_feed_query:
|
|
65
|
+
method: GET
|
|
66
|
+
path:
|
|
67
|
+
- type: literal
|
|
68
|
+
value: v0
|
|
69
|
+
- type: var
|
|
70
|
+
name: feed
|
|
71
|
+
suffix: stories.json
|
|
72
|
+
|
|
73
|
+
max_item_id_query:
|
|
74
|
+
method: GET
|
|
75
|
+
path:
|
|
76
|
+
- type: literal
|
|
77
|
+
value: v0
|
|
78
|
+
- type: literal
|
|
79
|
+
value: maxitem.json
|
|
80
|
+
response:
|
|
81
|
+
wrap_root_scalar: true
|
|
82
|
+
|
|
83
|
+
recent_updated_item_query:
|
|
84
|
+
method: GET
|
|
85
|
+
path:
|
|
86
|
+
- type: literal
|
|
87
|
+
value: v0
|
|
88
|
+
- type: literal
|
|
89
|
+
value: updates.json
|
|
90
|
+
response:
|
|
91
|
+
items_path:
|
|
92
|
+
- items
|
|
93
|
+
|
|
94
|
+
recent_updated_user_query:
|
|
95
|
+
method: GET
|
|
96
|
+
path:
|
|
97
|
+
- type: literal
|
|
98
|
+
value: v0
|
|
99
|
+
- type: literal
|
|
100
|
+
value: updates.json
|
|
101
|
+
response:
|
|
102
|
+
items_path:
|
|
103
|
+
- profiles
|
|
104
|
+
|
|
105
|
+
item_get:
|
|
106
|
+
method: GET
|
|
107
|
+
response:
|
|
108
|
+
single: true
|
|
109
|
+
path:
|
|
110
|
+
- type: literal
|
|
111
|
+
value: v0
|
|
112
|
+
- type: literal
|
|
113
|
+
value: item
|
|
114
|
+
- type: var
|
|
115
|
+
name: id
|
|
116
|
+
suffix: .json
|
|
117
|
+
|
|
118
|
+
user_get:
|
|
119
|
+
method: GET
|
|
120
|
+
response:
|
|
121
|
+
single: true
|
|
122
|
+
path:
|
|
123
|
+
- type: literal
|
|
124
|
+
value: v0
|
|
125
|
+
- type: literal
|
|
126
|
+
value: user
|
|
127
|
+
- type: var
|
|
128
|
+
name: id
|
|
129
|
+
suffix: .json
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Tavily REST API — Plasm CGS Schema
|
|
2
|
+
|
|
3
|
+
A [Plasm](../../README.md) domain model for the [Tavily API](https://docs.tavily.com/), covering the full current surface: web search, URL extraction, site crawling, and async deep research.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
export TAVILY_API_TOKEN=tvly-... # in /Users/ryan/code/plasm/.env
|
|
7
|
+
source /Users/ryan/code/plasm/.env
|
|
8
|
+
|
|
9
|
+
cargo run --bin plasm -- \
|
|
10
|
+
--schema apis/tavily \
|
|
11
|
+
--backend https://api.tavily.com \
|
|
12
|
+
--repl
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## What is implemented
|
|
18
|
+
|
|
19
|
+
### Entities
|
|
20
|
+
|
|
21
|
+
| Entity | Key | Fields |
|
|
22
|
+
|--------|-----|--------|
|
|
23
|
+
| `SearchResult` | `url` | url, title, content, score, raw_content, favicon |
|
|
24
|
+
| `ExtractResult` | `url` | url, raw_content, favicon |
|
|
25
|
+
| `ResearchTask` | `request_id` | request_id, status, input, model, created_at, content, sources, response_time |
|
|
26
|
+
|
|
27
|
+
`ExtractResult` is shared between `/extract` and `/crawl` — both endpoints return the same `{url, raw_content, favicon}` shape.
|
|
28
|
+
|
|
29
|
+
### Capabilities
|
|
30
|
+
|
|
31
|
+
| Capability | Kind | CLI | Endpoint | Tested |
|
|
32
|
+
|------------|------|-----|----------|--------|
|
|
33
|
+
| `web_search` | search | `searchresult search --query "..."` | `POST /search` | ✓ Live |
|
|
34
|
+
| `url_extract` | query | `extractresult query --urls https://...` | `POST /extract` | ✓ Live |
|
|
35
|
+
| `site_crawl` | query | `extractresult site-crawl --url https://...` | `POST /crawl` | ✓ Live |
|
|
36
|
+
| `research_create` | create | `researchtask research-create --input "..."` | `POST /research` | ✓ Live |
|
|
37
|
+
| `research_get` | get | `researchtask <request_id>` | `GET /research/{id}` | ✓ Live |
|
|
38
|
+
|
|
39
|
+
`site_map` (`POST /map`) is deferred — see Known Limitations.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## CLI examples
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
BASE="--schema apis/tavily --backend https://api.tavily.com"
|
|
47
|
+
|
|
48
|
+
# Web search — basic
|
|
49
|
+
plasm $BASE searchresult search \
|
|
50
|
+
--query "Rust async runtime comparison 2025" \
|
|
51
|
+
--max_results 5
|
|
52
|
+
|
|
53
|
+
# News search with LLM answer
|
|
54
|
+
plasm $BASE searchresult search \
|
|
55
|
+
--query "AI agent frameworks latest releases" \
|
|
56
|
+
--topic news \
|
|
57
|
+
--include_answer \
|
|
58
|
+
--max_results 5
|
|
59
|
+
|
|
60
|
+
# Date-bounded search
|
|
61
|
+
plasm $BASE searchresult search \
|
|
62
|
+
--query "Rust 1.94 release notes" \
|
|
63
|
+
--start_date 2026-01-01 \
|
|
64
|
+
--end_date 2026-03-31 \
|
|
65
|
+
--max_results 3
|
|
66
|
+
|
|
67
|
+
# Domain-filtered search
|
|
68
|
+
plasm $BASE searchresult search \
|
|
69
|
+
--query "Rust async tutorial" \
|
|
70
|
+
--include_domains docs.rs \
|
|
71
|
+
--include_domains blog.rust-lang.org \
|
|
72
|
+
--max_results 5
|
|
73
|
+
|
|
74
|
+
# Fast search with auto parameter selection
|
|
75
|
+
plasm $BASE searchresult search \
|
|
76
|
+
--query "latest OpenClaw features" \
|
|
77
|
+
--search_depth fast \
|
|
78
|
+
--auto_parameters \
|
|
79
|
+
--max_results 3
|
|
80
|
+
|
|
81
|
+
# Extract a single URL
|
|
82
|
+
plasm $BASE extractresult query \
|
|
83
|
+
--urls https://docs.tavily.com/documentation/about
|
|
84
|
+
|
|
85
|
+
# Extract multiple URLs
|
|
86
|
+
plasm $BASE extractresult query \
|
|
87
|
+
--urls https://blog.rust-lang.org/ \
|
|
88
|
+
--urls https://doc.rust-lang.org/book/ \
|
|
89
|
+
--extract_depth advanced \
|
|
90
|
+
--format markdown
|
|
91
|
+
|
|
92
|
+
# Crawl a website
|
|
93
|
+
plasm $BASE extractresult site-crawl \
|
|
94
|
+
--url https://docs.tavily.com \
|
|
95
|
+
--max_depth 2 \
|
|
96
|
+
--limit 10
|
|
97
|
+
|
|
98
|
+
# Crawl with instructions
|
|
99
|
+
plasm $BASE extractresult site-crawl \
|
|
100
|
+
--url https://docs.tavily.com \
|
|
101
|
+
--instructions "Find all pages about the Python SDK" \
|
|
102
|
+
--limit 5
|
|
103
|
+
|
|
104
|
+
# Start async research task
|
|
105
|
+
plasm $BASE researchtask research-create \
|
|
106
|
+
--input "What is Plasm, the Rust typed agent CLI for REST APIs?" \
|
|
107
|
+
--model mini \
|
|
108
|
+
--citation_format numbered
|
|
109
|
+
|
|
110
|
+
# Poll research status (returns content when completed)
|
|
111
|
+
plasm $BASE researchtask ec406c91-e2e9-45cd-8f07-0d5725472c1a
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Authentication
|
|
117
|
+
|
|
118
|
+
Set `TAVILY_API_TOKEN` to your Tavily API key. The schema reads from this variable:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
export TAVILY_API_TOKEN=tvly-your_key_here
|
|
122
|
+
# or source the project .env:
|
|
123
|
+
source /Users/ryan/code/plasm/.env
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## search_depth values (updated Dec 2025)
|
|
129
|
+
|
|
130
|
+
The `search_depth` parameter now has four options:
|
|
131
|
+
|
|
132
|
+
| Value | Cost | Latency | Content |
|
|
133
|
+
|-------|------|---------|---------|
|
|
134
|
+
| `basic` | 1 credit | balanced | 1 NLP summary per URL |
|
|
135
|
+
| `fast` (BETA) | 1 credit | low | multiple relevant snippets per URL |
|
|
136
|
+
| `ultra-fast` (BETA) | 1 credit | lowest | 1 NLP summary per URL |
|
|
137
|
+
| `advanced` | 2 credits | high | multiple high-precision snippets |
|
|
138
|
+
|
|
139
|
+
Earlier snapshots of this schema only exposed `basic` and `advanced` for `search_depth`. The current `apis/tavily` schema includes all four values above.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Integration testing (live, March 2026)
|
|
144
|
+
|
|
145
|
+
All capabilities tested against the real Tavily API with the dev token.
|
|
146
|
+
|
|
147
|
+
### web_search
|
|
148
|
+
|
|
149
|
+
1. **Basic search**: `--query "Plasm Rust REST API typed agent" --max_results 3` → 3 ranked results, correct score and content fields decoded. ✓
|
|
150
|
+
2. **News + answer**: `--topic news --include_answer --max_results 3` → news results from past week including OpenClaw surge coverage. ✓
|
|
151
|
+
3. **Date range** (`start_date`/`end_date` are new params): `--start_date 2026-01-01 --end_date 2026-03-31` → results limited to Q1 2026, including Rust 1.94.1 release blog. ✓
|
|
152
|
+
4. **fast depth + auto_parameters** (both new params): returns results with `auto_parameters` flag set. ✓
|
|
153
|
+
|
|
154
|
+
### url_extract
|
|
155
|
+
|
|
156
|
+
- Single URL extraction: `https://docs.tavily.com/documentation/about` → full `raw_content` field decoded (1,900+ char markdown). ✓
|
|
157
|
+
|
|
158
|
+
### site_crawl
|
|
159
|
+
|
|
160
|
+
- `--url https://docs.tavily.com --max_depth 1 --limit 3` → 3 crawled pages (`/`, `/changelog`, `/welcome`) decoded as `ExtractResult` entities. ✓
|
|
161
|
+
|
|
162
|
+
**Note:** A runtime bug was found and fixed during testing. Scope parameters whose names match entity field names (e.g. `url` in `site_crawl` matching `ExtractResult.url`) were incorrectly applied as client-side result filters, returning 0 results. The fix adds scope/search parameter awareness to `entity_field_predicate_scoped` in `execution.rs` — only true field predicates (not scope params) are applied client-side. This fix also benefits GitHub `issue_query` (owner/repo scope params) and Jira `comment_query` (issueIdOrKey scope param).
|
|
163
|
+
|
|
164
|
+
### research_create + research_get
|
|
165
|
+
|
|
166
|
+
- `POST /research` with `--input "What is Plasm...?" --model mini` → request_id `ec406c91...`, status `pending`. ✓
|
|
167
|
+
- `GET /research/{request_id}` after ~35 seconds → status `completed`, `content` field with 530-char report. ✓
|
|
168
|
+
|
|
169
|
+
**Note during testing:** The research GET response uses `content` (not `report`) for the generated text. The initial schema used `report` — corrected to `content`.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Design notes
|
|
174
|
+
|
|
175
|
+
### All capabilities use POST body
|
|
176
|
+
|
|
177
|
+
Unlike REST APIs that use query params for GET requests, Tavily puts all parameters in the JSON request body (even for non-mutating operations). The CML `body:` block is used for `web_search`, `url_extract`, and `site_crawl`. Only `research_get` uses a GET with no body.
|
|
178
|
+
|
|
179
|
+
### url_extract and site_crawl as `kind: query`
|
|
180
|
+
|
|
181
|
+
Both capabilities POST a body and return a list of entities — the same execution path as any paginated query. Using `kind: query` means:
|
|
182
|
+
- CLI generates repeatable/typed flags from `parameters:`
|
|
183
|
+
- Parameters flow through predicate compilation into the CML env
|
|
184
|
+
- Response decoded as a collection via `response: {items: results}`
|
|
185
|
+
|
|
186
|
+
### research_create as `kind: create`
|
|
187
|
+
|
|
188
|
+
The async research endpoint creates a task (HTTP 201) and returns `{request_id, status: pending, ...}`. The `kind: create` capability collects flags via `args_to_input` into a Value::Object, then `body: {type: var, name: input}` sends that whole object as the request body. The create response is decoded as a single `ResearchTask` entity.
|
|
189
|
+
|
|
190
|
+
### Scope params excluded from client-side filtering
|
|
191
|
+
|
|
192
|
+
Capabilities like `site_crawl` (with `url: role: scope`) and `url_extract` (with `urls: role: scope`) need their scope param predicates excluded from client-side entity filtering. The runtime now calls `capability_non_filter_params()` to get the set of scope/search params and exclude them in `entity_field_predicate_scoped()`.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Known limitations
|
|
197
|
+
|
|
198
|
+
### site_map deferred
|
|
199
|
+
|
|
200
|
+
`POST /map` returns `{results: ["url1", "url2", ...]}` — a bare string array. The Plasm decoder expects JSON objects with named fields; it cannot decode bare string items as entities. This is a decoder gap. Once the decoder supports "use the bare string value as the id field", `site_map` can be modelled.
|
|
201
|
+
|
|
202
|
+
Workaround: use `site_crawl` when you need page content, or call the map endpoint directly with `curl`.
|
|
203
|
+
|
|
204
|
+
### research polling is manual
|
|
205
|
+
|
|
206
|
+
The research workflow requires two steps: `research_create` (start task) → wait → `research_get` (poll). There is no automatic polling loop. Agents must poll manually by re-running `researchtask <request_id>`. A future `kind: async_job` capability kind with built-in polling would improve this.
|
|
207
|
+
|
|
208
|
+
### Large `content` field
|
|
209
|
+
|
|
210
|
+
Research `content` can be thousands of characters. In table output format, it's truncated. Use `--output json` or `--output compact` to get the full text.
|
|
211
|
+
|
|
212
|
+
### `include_domains` / `exclude_domains` in body
|
|
213
|
+
|
|
214
|
+
For web search, `include_domains` and `exclude_domains` are sent as JSON arrays in the body (not repeated query params). Tavily's body parser handles them correctly. They appear in the CLI as repeatable flags: `--include_domains reuters.com --include_domains bbc.com`.
|
|
215
|
+
|
|
216
|
+
### `answer` field not on SearchResult entity
|
|
217
|
+
|
|
218
|
+
The search response includes a top-level `answer` string (LLM-generated summary) separate from the individual results. This field is NOT decoded into `SearchResult` entities because it's a response-level field that doesn't belong to any individual result. Agents requesting the answer should use `--include_answer` and read the raw JSON output.
|