@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.
Files changed (58) hide show
  1. package/README.md +9 -2
  2. package/agent/instructions.md +1 -2
  3. package/bin/plasm-agent.mjs +5 -7
  4. package/package.json +29 -19
  5. package/scripts/build-stubs.ts +27 -0
  6. package/scripts/plasm-cli.ts +162 -0
  7. package/scripts/register-plasm-loader.mjs +6 -0
  8. package/scripts/run-plasm-cli.mjs +41 -0
  9. package/src/cli/init-scaffold.ts +198 -0
  10. package/src/cli/init.ts +20 -208
  11. package/src/cli/nitro-dev.ts +69 -0
  12. package/src/cli/node-dev-imports.ts +14 -0
  13. package/src/engine/create-host-transport.ts +7 -0
  14. package/src/index.ts +1 -1
  15. package/src/package-version.ts +14 -0
  16. package/src/project-info.ts +2 -1
  17. package/src/stubs/catalog-client.ts +2 -2
  18. package/src/tools/harness-tools.ts +42 -41
  19. package/src/tools/plasm-tools.ts +64 -55
  20. package/src/tools/tool-input.ts +9 -0
  21. package/templates/mcp-radar/.vercelignore +3 -0
  22. package/templates/mcp-radar/README.md +139 -0
  23. package/templates/mcp-radar/agent/agent.ts +38 -0
  24. package/templates/mcp-radar/agent/catalogs/hackernews/README.md +23 -0
  25. package/templates/mcp-radar/agent/catalogs/hackernews/domain.yaml +329 -0
  26. package/templates/mcp-radar/agent/catalogs/hackernews/eval/cases.yaml +112 -0
  27. package/templates/mcp-radar/agent/catalogs/hackernews/mappings.yaml +129 -0
  28. package/templates/mcp-radar/agent/catalogs/tavily/README.md +218 -0
  29. package/templates/mcp-radar/agent/catalogs/tavily/domain.yaml +373 -0
  30. package/templates/mcp-radar/agent/catalogs/tavily/eval/cases.yaml +56 -0
  31. package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.human.txt +67 -0
  32. package/templates/mcp-radar/agent/catalogs/tavily/eval/google-gemma-3-4b-it.latest.json +363 -0
  33. package/templates/mcp-radar/agent/catalogs/tavily/mappings.yaml +171 -0
  34. package/templates/mcp-radar/agent/channels/mcp-radar.ts +85 -0
  35. package/templates/mcp-radar/agent/hooks/proof-audit.ts +10 -0
  36. package/templates/mcp-radar/agent/instructions.md +24 -0
  37. package/templates/mcp-radar/agent/research/mcp-innovations-proof.md +4 -0
  38. package/templates/mcp-radar/agent/schedules/mcp-radar-scan.ts +14 -0
  39. package/templates/mcp-radar/agent/skills/mcp-proof-format.md +19 -0
  40. package/templates/mcp-radar/api/[[...path]].ts +23 -0
  41. package/templates/mcp-radar/evals/mcp-radar-discover.eval.ts +14 -0
  42. package/templates/mcp-radar/lib/proof-store.ts +265 -0
  43. package/templates/mcp-radar/lib/run-radar.ts +214 -0
  44. package/templates/mcp-radar/nitro.config.ts +17 -0
  45. package/templates/mcp-radar/package.json +14 -0
  46. package/templates/mcp-radar/public/index.html +10 -0
  47. package/templates/mcp-radar/routes/[...path].ts +29 -0
  48. package/templates/mcp-radar/scripts/run-evals.ts +46 -0
  49. package/templates/mcp-radar/scripts/smoke-channel.ts +66 -0
  50. package/templates/mcp-radar/vercel.json +15 -0
  51. package/templates/scaffold/.vercelignore +3 -0
  52. package/templates/scaffold/api/[[...path]].ts +23 -0
  53. package/templates/scaffold/nitro.config.ts +17 -0
  54. package/templates/scaffold/public/index.html +10 -0
  55. package/templates/scaffold/public/index.mcp-radar.html +10 -0
  56. package/templates/scaffold/routes/[...path].ts +29 -0
  57. package/templates/scaffold/vercel.default.json +4 -0
  58. 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.