@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
@@ -1,4 +1,4 @@
1
- import { tool } from "ai";
1
+ import { tool, type ToolSet } from "ai";
2
2
  import { z } from "zod";
3
3
 
4
4
  import type { AgentRuntime } from "../runtime/agent-runtime.js";
@@ -8,89 +8,98 @@ import {
8
8
  PLASM_RUN_TOOL_DESCRIPTION,
9
9
  PLASM_TOOL_DESCRIPTION,
10
10
  } from "./descriptions.js";
11
+ import { toolInput } from "./tool-input.js";
11
12
 
12
13
  const seedSchema = z.object({
13
14
  api: z.string().describe("Registry entry_id / catalog api"),
14
15
  entity: z.string().describe("CGS entity name from discovery or prior knowledge"),
15
16
  });
16
17
 
17
- export function createPlasmTools(runtime: AgentRuntime) {
18
+ const discoverInputSchema = z.object({
19
+ intent: z
20
+ .string()
21
+ .min(1)
22
+ .describe(
23
+ "One plain-language task description for the whole user goal. Returns catalog api/entity picks — not program symbols.",
24
+ ),
25
+ });
26
+
27
+ const plasmContextInputSchema = z.object({
28
+ intent: z
29
+ .string()
30
+ .min(1)
31
+ .describe(
32
+ "Stable string for one user goal (same value every turn for that goal — do not rotate per message).",
33
+ ),
34
+ seeds: z
35
+ .array(seedSchema)
36
+ .min(1)
37
+ .describe("Non-empty array of {api, entity} capability picks"),
38
+ ranked_capabilities: z
39
+ .array(z.string())
40
+ .nullable()
41
+ .optional()
42
+ .describe(
43
+ "Optional capability wire names from discover_capabilities. Omit on expand; null or [] clears.",
44
+ ),
45
+ });
46
+
47
+ const plasmInputSchema = z.object({
48
+ logical_session_ref: z
49
+ .string()
50
+ .describe("Same logical_session_ref returned by plasm_context"),
51
+ program: z
52
+ .string()
53
+ .min(1)
54
+ .describe("Plasm source program using e#/m#/p#/r# from the session teaching TSV"),
55
+ reasoning: z
56
+ .string()
57
+ .optional()
58
+ .describe("Optional short note explaining the intent of this call"),
59
+ });
60
+
61
+ const plasmRunInputSchema = z.object({
62
+ logical_session_ref: z
63
+ .string()
64
+ .describe("Same logical_session_ref returned by plasm_context"),
65
+ plan_commit_ref: z
66
+ .string()
67
+ .describe("Executable plan token (pcN) from a prior plasm dry-run"),
68
+ reasoning: z
69
+ .string()
70
+ .optional()
71
+ .describe("Optional short note explaining the intent of this call"),
72
+ });
73
+
74
+ export function createPlasmTools(runtime: AgentRuntime): ToolSet {
18
75
  return {
19
76
  discover_capabilities: tool({
20
77
  description: DISCOVER_TOOL_DESCRIPTION,
21
- inputSchema: z.object({
22
- intent: z
23
- .string()
24
- .min(1)
25
- .describe(
26
- "One plain-language task description for the whole user goal. Returns catalog api/entity picks — not program symbols.",
27
- ),
28
- }),
78
+ inputSchema: toolInput(discoverInputSchema),
29
79
  execute: async ({ intent }) => runtime.discoverCapabilities({ intent }),
30
80
  }),
31
81
 
32
82
  plasm_context: tool({
33
83
  description: PLASM_CONTEXT_TOOL_DESCRIPTION,
34
- inputSchema: z.object({
35
- intent: z
36
- .string()
37
- .min(1)
38
- .describe(
39
- "Stable string for one user goal (same value every turn for that goal — do not rotate per message).",
40
- ),
41
- seeds: z
42
- .array(seedSchema)
43
- .min(1)
44
- .describe("Non-empty array of {api, entity} capability picks"),
45
- ranked_capabilities: z
46
- .array(z.string())
47
- .nullable()
48
- .optional()
49
- .describe(
50
- "Optional capability wire names from discover_capabilities. Omit on expand; null or [] clears.",
51
- ),
52
- }),
84
+ inputSchema: toolInput(plasmContextInputSchema),
53
85
  execute: async ({ intent, seeds, ranked_capabilities }) =>
54
86
  runtime.plasmContext({
55
87
  intent,
56
- seeds,
88
+ seeds: seeds as Array<{ api: string; entity: string }>,
57
89
  rankedCapabilities: ranked_capabilities,
58
90
  }),
59
91
  }),
60
92
 
61
93
  plasm: tool({
62
94
  description: PLASM_TOOL_DESCRIPTION,
63
- inputSchema: z.object({
64
- logical_session_ref: z
65
- .string()
66
- .describe("Same logical_session_ref returned by plasm_context"),
67
- program: z
68
- .string()
69
- .min(1)
70
- .describe("Plasm source program using e#/m#/p#/r# from the session teaching TSV"),
71
- reasoning: z
72
- .string()
73
- .optional()
74
- .describe("Optional short note explaining the intent of this call"),
75
- }),
95
+ inputSchema: toolInput(plasmInputSchema),
76
96
  execute: async ({ logical_session_ref, program, reasoning }) =>
77
97
  runtime.plasm({ logicalSessionRef: logical_session_ref, program, reasoning }),
78
98
  }),
79
99
 
80
100
  plasm_run: tool({
81
101
  description: PLASM_RUN_TOOL_DESCRIPTION,
82
- inputSchema: z.object({
83
- logical_session_ref: z
84
- .string()
85
- .describe("Same logical_session_ref returned by plasm_context"),
86
- plan_commit_ref: z
87
- .string()
88
- .describe("Executable plan token (pcN) from a prior plasm dry-run"),
89
- reasoning: z
90
- .string()
91
- .optional()
92
- .describe("Optional short note explaining the intent of this call"),
93
- }),
102
+ inputSchema: toolInput(plasmRunInputSchema),
94
103
  execute: async ({ logical_session_ref, plan_commit_ref, reasoning }) =>
95
104
  runtime.plasmRun({
96
105
  logicalSessionRef: logical_session_ref,
@@ -0,0 +1,9 @@
1
+ import { zodSchema, type FlexibleSchema } from "ai";
2
+ import type { z } from "zod";
3
+
4
+ /** Wrap Zod for AI SDK `tool()` — avoids TS2589 deep instantiation during Vercel typecheck. */
5
+ export function toolInput<T extends z.ZodTypeAny>(
6
+ schema: T,
7
+ ): FlexibleSchema<z.infer<T>> {
8
+ return zodSchema(schema);
9
+ }
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ .env*
3
+ agent/.plasm/research
@@ -0,0 +1,139 @@
1
+ # MCP Radar Agent
2
+
3
+ Event-driven research example for [`@plasm_lang/vercel-agent`](../../plasm-oss/packages/plasm-agent/): watch Hacker News for **MCP** innovation signals, corroborate with **Tavily**, and append structured entries to a maintained proof log.
4
+
5
+ ## Purpose
6
+
7
+ Demonstrates Plasm’s **dual surface**:
8
+
9
+ - **Code surface** — channel/schedule handlers preflight HN via typed stubs and own proof persistence
10
+ - **Model surface** — federated `plasm_context` (hackernews + tavily) → `plasm` → `plasm_run` for synthesis
11
+
12
+ ## Bootstrap from CLI
13
+
14
+ Scaffold a fresh copy (verified by `npm run smoke:bootstrap` in the framework package):
15
+
16
+ ```bash
17
+ mkdir /tmp/my-radar && cd /tmp/my-radar
18
+ plasm-agent init --template mcp-radar .
19
+ npm install
20
+ plasm-agent build
21
+ npm run smoke:channel
22
+ plasm-agent dev
23
+ ```
24
+
25
+ The canonical template source is this directory (`examples/mcp-radar-agent/`).
26
+
27
+ ## Deploy to Vercel
28
+
29
+ Low-friction production path (eve-aligned): `vercel.json` + catch-all `api/[[...path]].ts` mounts the same routes as local dev (channels, cron, `/plasm/v1/info`).
30
+
31
+ ```bash
32
+ cd examples/mcp-radar-agent # or your bootstrapped project
33
+ plasm-agent link
34
+ plasm-agent build
35
+ vercel env pull .env.local # AI_GATEWAY_API_KEY, CRON_SECRET, KV, Blob
36
+ vercel deploy
37
+ curl -s "$DEPLOY_URL/channel/mcp-radar/status" | jq .
38
+ ```
39
+
40
+ | Variable | Deploy |
41
+ |----------|--------|
42
+ | `AI_GATEWAY_API_KEY` | Required for agent synthesis |
43
+ | `CRON_SECRET` | Required — Vercel Cron hits `/internal/cron/mcp-radar-scan` |
44
+ | `KV_REST_API_URL` + `KV_REST_API_TOKEN` | Durable seen-items + last-run state |
45
+ | `BLOB_READ_WRITE_TOKEN` | Durable proof markdown log |
46
+ | `TAVILY_API_TOKEN` | Optional corroboration |
47
+
48
+ On Vercel, `POST /channel/mcp-radar/run` returns `202` and continues in the background via `waitUntil`. Locally it runs synchronously for smoke tests.
49
+
50
+ Monorepo deploy: set Vercel **Root Directory** to `examples/mcp-radar-agent`.
51
+
52
+ ## Setup (monorepo checkout)
53
+
54
+ ```bash
55
+ cd examples/mcp-radar-agent
56
+ npm install
57
+ cp .env.example .env.local
58
+ ```
59
+
60
+ | Variable | Required | Role |
61
+ |----------|----------|------|
62
+ | `AI_GATEWAY_API_KEY` | Yes (live runs) | Agent model turns |
63
+ | `TAVILY_API_TOKEN` | Optional | Tavily corroboration; HN-only when unset |
64
+
65
+ Build CGS stubs (symlinked catalogs):
66
+
67
+ ```bash
68
+ npm run build
69
+ ```
70
+
71
+ ## Dev
72
+
73
+ ```bash
74
+ npm run build
75
+ npm run dev # Nitro dev server — Vercel routing parity (channels, cron, /plasm/v1/*)
76
+ npm run dev:interactive # optional: in-process server + TUI + sessions + hot reload
77
+ ```
78
+
79
+ `plasm-agent dev` starts Nitro by default so channel routes like `/channel/mcp-radar/status` match production.
80
+
81
+ Slash commands in the interactive TUI: `/info`, `/catalogs`, `/new`, `/quit`.
82
+
83
+ ## Channel API
84
+
85
+ Start headless dev server (`npm run dev:headless`), then:
86
+
87
+ ```bash
88
+ BASE=http://127.0.0.1:3000
89
+
90
+ # Status
91
+ curl -s "$BASE/channel/mcp-radar/status" | jq .
92
+
93
+ # Read proof log (markdown)
94
+ curl -s "$BASE/channel/mcp-radar/proof"
95
+
96
+ # Trigger one scan (dedupes seen HN ids)
97
+ curl -s -X POST "$BASE/channel/mcp-radar/run" -H 'content-type: application/json' -d '{}'
98
+
99
+ # Force scan even when no new ids
100
+ curl -s -X POST "$BASE/channel/mcp-radar/run" -H 'content-type: application/json' -d '{"force":true}'
101
+ ```
102
+
103
+ Proof artifact (local fs): [`agent/research/mcp-innovations-proof.md`](./agent/research/mcp-innovations-proof.md)
104
+
105
+ Dedupe state (local fs): `agent/.plasm/research/seen-hn-items.json` (gitignored). On Vercel deploy, proof + dedupe use Blob + KV when env vars are set.
106
+
107
+ ## Schedule
108
+
109
+ `agent/schedules/mcp-radar-scan.ts` runs every **6 hours** (`0 */6 * * *`). Vercel Cron calls `/internal/cron/mcp-radar-scan` with `Authorization: Bearer $CRON_SECRET`.
110
+
111
+ ## Evals
112
+
113
+ ```bash
114
+ npm run eval # requires AI_GATEWAY_API_KEY
115
+ ```
116
+
117
+ Live eval: `evals/mcp-radar-discover.eval.ts`
118
+
119
+ ## Smoke
120
+
121
+ ```bash
122
+ npm run build
123
+ npm run smoke:channel
124
+ ```
125
+
126
+ ## Catalogs
127
+
128
+ Symlinked from monorepo `apis/`:
129
+
130
+ - `agent/catalogs/hackernews` → `plasm-oss/apis/hackernews`
131
+ - `agent/catalogs/tavily` → `plasm-oss/apis/tavily`
132
+
133
+ ## Stable session intent
134
+
135
+ ```
136
+ track MCP innovations from Hacker News and corroborate with Tavily web search
137
+ ```
138
+
139
+ Federated seeds: `hackernews:Item`, `tavily:SearchResult`
@@ -0,0 +1,38 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ import {
5
+ createAgentFromProject,
6
+ createProductionHostTransport,
7
+ defineAgent,
8
+ loadAgentEnv,
9
+ } from "@plasm_lang/vercel-agent";
10
+
11
+ loadAgentEnv();
12
+
13
+ const agentRoot = path.dirname(fileURLToPath(import.meta.url));
14
+
15
+ const agentDefinition = defineAgent({
16
+ model: process.env.PLASM_AGENT_MODEL ?? "anthropic/claude-sonnet-4.6",
17
+ compaction: { thresholdPercent: 0.75 },
18
+ modelOptions: { temperature: 0.2 },
19
+ experimental: {
20
+ workflow: { world: { type: "vercel" } },
21
+ skills: true,
22
+ },
23
+ build: {
24
+ externalDependencies: ["@plasm_lang/engine"],
25
+ },
26
+ });
27
+
28
+ export default agentDefinition;
29
+
30
+ export async function createPlasmAgent() {
31
+ return createAgentFromProject(agentDefinition, {
32
+ agentRoot,
33
+ tenantScope: process.env.PLASM_TENANT_SCOPE ?? "mcp-radar",
34
+ maxSteps: 24,
35
+ telemetry: process.env.PLASM_AGENT_TELEMETRY !== "0",
36
+ hostTransport: createProductionHostTransport(),
37
+ });
38
+ }
@@ -0,0 +1,23 @@
1
+ # Hacker News (Firebase API)
2
+
3
+ Read-only mirror of the public [Hacker News API](https://github.com/HackerNews/API) plus the [Algolia HN search API](https://hn.algolia.com/api) (full-URL CML; same `http_backend` is Firebase for `item_get`). No authentication.
4
+
5
+ ## Scope
6
+
7
+ - **item_search** / **item_search_by_date** — Algolia `search` and `search_by_date` (default `tags=story`). Rows use `objectID` as `id`; use **item_get** to load the Firebase item.
8
+ - **item_feed_query** — ordered item ids for `top`, `new`, `best`, `ask`, `show`, or `job` (ids only; use **item_get** to hydrate).
9
+ - **max_item_id_query** — current largest item id (`maxitem.json` returns a bare integer; mapped via `wrap_root_scalar`).
10
+ - **recent_updated_item_query** / **recent_updated_user_query** — live slices from `updates.json` (`items` and `profiles` arrays; profiles are usernames only).
11
+ - **item_get** / **user_get** — full JSON for an item or user. Poll stories expose **`parts`** (option ids) and the **`poll_options`** relation for chaining.
12
+
13
+ ## Try it
14
+
15
+ ```bash
16
+ cargo run --bin plasm-repl -- --schema apis/hackernews
17
+ ```
18
+
19
+ Eval coverage (no LLM):
20
+
21
+ ```bash
22
+ cargo run -p plasm-eval -- coverage --schema apis/hackernews --cases apis/hackernews/eval/cases.yaml
23
+ ```
@@ -0,0 +1,329 @@
1
+ version: 2
2
+ auth:
3
+ scheme: none
4
+ http_backend: https://hacker-news.firebaseio.com
5
+ entities:
6
+ Item:
7
+ id_field: id
8
+ description: A Hacker News item (story, comment, job, poll, or poll option). Use item_search (Algolia) to discover ids.
9
+ from text, then item_get for full rows from Firebase. Feed endpoints return only numeric ids; use item_get to load title,
10
+ text, score, and comment trees. Direct replies are listed under kids as further item ids. Poll posts expose poll option
11
+ ids under parts; each option is another Item row.
12
+ fields:
13
+ id:
14
+ required: true
15
+ value_ref: nv_item_id
16
+ type:
17
+ required: false
18
+ value_ref: nv_item_type
19
+ by:
20
+ required: false
21
+ value_ref: nv_item_by
22
+ time:
23
+ required: false
24
+ value_ref: nv_item_time
25
+ title:
26
+ required: false
27
+ value_ref: nv_item_title
28
+ text:
29
+ required: false
30
+ value_ref: nv_item_text
31
+ url:
32
+ required: false
33
+ value_ref: nv_item_url
34
+ score:
35
+ required: false
36
+ value_ref: nv_item_score
37
+ descendants:
38
+ required: false
39
+ value_ref: nv_item_descendants
40
+ parent:
41
+ required: false
42
+ value_ref: nv_item_parent
43
+ poll:
44
+ required: false
45
+ value_ref: nv_item_poll
46
+ parts:
47
+ required: false
48
+ value_ref: nv_item_parts
49
+ deleted:
50
+ required: false
51
+ value_ref: nv_item_deleted
52
+ dead:
53
+ required: false
54
+ value_ref: nv_item_dead
55
+ relations:
56
+ kids:
57
+ description: Direct child item ids (typically comments under a story).
58
+ target: Item
59
+ cardinality: many
60
+ materialize:
61
+ kind: from_parent_get
62
+ path:
63
+ - key: kids
64
+ - wildcard: true
65
+ poll_options:
66
+ description: Poll option rows referenced by parts (each id is an Item).
67
+ target: Item
68
+ cardinality: many
69
+ materialize:
70
+ kind: from_parent_get
71
+ path:
72
+ - key: parts
73
+ - wildcard: true
74
+ User:
75
+ id_field: id
76
+ description: A Hacker News user profile keyed by username. The submitted list holds item ids only; fetch each via item_get.
77
+ when you need titles or comment bodies.
78
+ fields:
79
+ id:
80
+ required: true
81
+ value_ref: nv_user_id
82
+ created:
83
+ required: false
84
+ value_ref: nv_user_created
85
+ karma:
86
+ required: false
87
+ value_ref: nv_user_karma
88
+ about:
89
+ required: false
90
+ value_ref: nv_user_about
91
+ delay:
92
+ required: false
93
+ value_ref: nv_user_delay
94
+ relations:
95
+ submitted:
96
+ description: Item ids this user has submitted.
97
+ target: Item
98
+ cardinality: many
99
+ materialize:
100
+ kind: from_parent_get
101
+ path:
102
+ - key: submitted
103
+ - wildcard: true
104
+ MaxItemId:
105
+ id_field: id
106
+ description: Current largest item id reported by maxitem.json (a bare JSON integer on the wire). Hydrate the referenced.
107
+ row with item_get when you need the full record.
108
+ fields:
109
+ id:
110
+ required: true
111
+ value_ref: nv_max_item_id_id
112
+ RecentUpdatedItem:
113
+ id_field: id
114
+ description: Item ids listed under updates.json items (live slice of ids with recent activity). Each entry is an id only.
115
+ use item_get for payloads.
116
+ fields:
117
+ id:
118
+ required: true
119
+ value_ref: nv_recent_updated_item_id
120
+ RecentUpdatedUser:
121
+ id_field: id
122
+ description: Usernames listed under updates.json profiles (accounts whose profile data changed). Each entry is the username.
123
+ string only; use user_get for karma and about text.
124
+ fields:
125
+ id:
126
+ required: true
127
+ value_ref: nv_recent_updated_user_id
128
+ capabilities:
129
+ item_search:
130
+ description: Full-text search over HN via the public Algolia index (https://hn.algolia.com/api). Returns story Item rows.
131
+ with id from objectID and title/url from the hit; use item_get to hydrate the full Firebase record, kids, and score.
132
+ kind: search
133
+ entity: Item
134
+ provides:
135
+ - id
136
+ - title
137
+ - url
138
+ parameters:
139
+ - name: query
140
+ value_ref: nv_wire_str_short
141
+ required: true
142
+ role: search
143
+ - name: tags
144
+ value_ref: nv_wire_str_short
145
+ required: false
146
+ role: filter
147
+ - name: page
148
+ value_ref: nv_wire_int
149
+ required: false
150
+ role: response_control
151
+ - name: per_page
152
+ value_ref: nv_wire_int
153
+ required: false
154
+ role: response_control
155
+ item_search_by_date:
156
+ description: Same as item_search but sorted by time (search_by_date). Use for recency; combine with item_get for full.
157
+ items.
158
+ kind: search
159
+ entity: Item
160
+ provides:
161
+ - id
162
+ - title
163
+ - url
164
+ parameters:
165
+ - name: query
166
+ value_ref: nv_wire_str_short
167
+ required: true
168
+ role: search
169
+ - name: tags
170
+ value_ref: nv_wire_str_short
171
+ required: false
172
+ role: filter
173
+ - name: page
174
+ value_ref: nv_wire_int
175
+ required: false
176
+ role: response_control
177
+ - name: per_page
178
+ value_ref: nv_wire_int
179
+ required: false
180
+ role: response_control
181
+ item_feed_query:
182
+ description: Ordered list of item ids for a public feed (top, new, best, ask, show, or job). Each entry is an Item id only.
183
+ hydrate with item_get for full records and kids.
184
+ kind: query
185
+ entity: Item
186
+ provides:
187
+ - id
188
+ parameters:
189
+ - name: feed
190
+ value_ref: nv_item_feed_query_feed
191
+ required: true
192
+ role: filter
193
+ max_item_id_query:
194
+ description: Current largest item id (maxitem.json).
195
+ kind: query
196
+ entity: MaxItemId
197
+ provides:
198
+ - id
199
+ recent_updated_item_query:
200
+ description: Item ids from the live updates snapshot (updates.json items array).
201
+ kind: query
202
+ entity: RecentUpdatedItem
203
+ provides:
204
+ - id
205
+ recent_updated_user_query:
206
+ description: Usernames from the live updates snapshot (updates.json profiles array).
207
+ kind: query
208
+ entity: RecentUpdatedUser
209
+ provides:
210
+ - id
211
+ item_get:
212
+ kind: get
213
+ entity: Item
214
+ provides:
215
+ - id
216
+ - type
217
+ - by
218
+ - time
219
+ - title
220
+ - text
221
+ - url
222
+ - score
223
+ - descendants
224
+ - parent
225
+ - poll
226
+ - parts
227
+ - deleted
228
+ - dead
229
+ user_get:
230
+ description: Load a user profile by username.
231
+ kind: get
232
+ entity: User
233
+ provides:
234
+ - id
235
+ - created
236
+ - karma
237
+ - about
238
+ - delay
239
+ values:
240
+ nv_item_by:
241
+ type: string
242
+ string_semantics: short
243
+ description: Username of the author when present.
244
+ nv_item_dead:
245
+ type: boolean
246
+ description: Whether the item is dead.
247
+ nv_item_deleted:
248
+ type: boolean
249
+ description: Whether the item was deleted.
250
+ nv_item_descendants:
251
+ type: integer
252
+ description: Comment count for stories when known.
253
+ nv_item_feed_query_feed:
254
+ type: select
255
+ allowed_values:
256
+ - top
257
+ - new
258
+ - best
259
+ - ask
260
+ - show
261
+ - job
262
+ nv_item_id:
263
+ type: integer
264
+ description: Numeric item id used in Firebase paths.
265
+ nv_item_parent:
266
+ type: integer
267
+ description: Parent item id for comments and poll options.
268
+ nv_item_poll:
269
+ type: integer
270
+ description: Poll id when this item is a story that hosts a poll.
271
+ nv_item_score:
272
+ type: integer
273
+ description: Net votes for stories and jobs when present.
274
+ nv_item_text:
275
+ type: string
276
+ string_semantics: markdown
277
+ description: Comment or story text payload when present.
278
+ nv_item_time:
279
+ type: integer
280
+ description: Unix time the item was created.
281
+ nv_item_title:
282
+ type: string
283
+ string_semantics: short
284
+ description: Story or job title when applicable.
285
+ nv_item_type:
286
+ type: string
287
+ string_semantics: short
288
+ description: Wire kind such as story, comment, job, poll, or pollopt.
289
+ nv_item_url:
290
+ type: string
291
+ string_semantics: short
292
+ description: Outbound link for link posts when present.
293
+ nv_max_item_id_id:
294
+ type: integer
295
+ description: Largest item id currently allocated on HN.
296
+ nv_recent_updated_item_id:
297
+ type: integer
298
+ description: Item id from the live updates snapshot.
299
+ nv_recent_updated_user_id:
300
+ type: string
301
+ string_semantics: short
302
+ description: Username from the live updates snapshot.
303
+ nv_user_about:
304
+ type: string
305
+ string_semantics: markdown
306
+ description: Self-description text when set.
307
+ nv_user_created:
308
+ type: integer
309
+ description: Unix time the account was created.
310
+ nv_user_delay:
311
+ type: integer
312
+ description: Account delay setting when present.
313
+ nv_user_id:
314
+ type: string
315
+ string_semantics: short
316
+ description: Unique username.
317
+ nv_user_karma:
318
+ type: integer
319
+ description: User karma score.
320
+ nv_wire_int:
321
+ type: integer
322
+ nv_wire_str_short:
323
+ type: string
324
+ string_semantics: short
325
+ nv_item_parts:
326
+ type: array
327
+ items:
328
+ value_ref: nv_wire_int
329
+ description: Poll option item ids when type is poll (empty or absent otherwise).