@cursor/july 0.1.43 → 0.1.45

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 (136) hide show
  1. package/AGENTS.md +1 -1
  2. package/dist/channels/github/github-channel.d.ts.map +1 -1
  3. package/dist/channels/github/github-channel.js +13 -5
  4. package/dist/channels/github/types.d.ts +8 -0
  5. package/dist/channels/github/types.d.ts.map +1 -1
  6. package/dist/channels.d.ts +17 -0
  7. package/dist/channels.d.ts.map +1 -1
  8. package/dist/channels.js +31 -0
  9. package/dist/docs/404.html +2 -2
  10. package/dist/docs/ab.html +3 -3
  11. package/dist/docs/assets/{app.DsTLo2j1.js → app.fHnNXEJ6.js} +1 -1
  12. package/dist/docs/assets/chunks/@localSearchIndexroot.BMT0xM4I.js +1 -0
  13. package/dist/docs/assets/chunks/{VPLocalSearchBox.DoVfutcV.js → VPLocalSearchBox.Cf6ghfFY.js} +1 -1
  14. package/dist/docs/assets/chunks/{theme.DtuVtKkc.js → theme.B_5-VLe8.js} +2 -2
  15. package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.js +27 -0
  16. package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.lean.js +1 -0
  17. package/dist/docs/assets/index.md.COeoSkcF.js +5 -0
  18. package/dist/docs/assets/index.md.COeoSkcF.lean.js +1 -0
  19. package/dist/docs/assets/{quickstart.md.rIbfahJM.js → quickstart.md.DZxBu44y.js} +18 -49
  20. package/dist/docs/assets/quickstart.md.DZxBu44y.lean.js +1 -0
  21. package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.js → reference_agent-config.md.XRpxctsA.js} +9 -4
  22. package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.XRpxctsA.lean.js} +1 -1
  23. package/dist/docs/assets/{reference_cli.md.DqEqQpGn.js → reference_cli.md.BDmjPPAm.js} +2 -2
  24. package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.js} +1 -1
  25. package/dist/docs/building-with-agents.html +3 -3
  26. package/dist/docs/concepts.html +3 -3
  27. package/dist/docs/deployment.html +3 -3
  28. package/dist/docs/evals.html +3 -3
  29. package/dist/docs/example-agents/approval-buddy.html +3 -3
  30. package/dist/docs/example-agents/benny.html +3 -3
  31. package/dist/docs/example-agents/bugbot.html +3 -3
  32. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  33. package/dist/docs/example-agents/codeowners-review.html +3 -3
  34. package/dist/docs/example-agents/concierge.html +3 -3
  35. package/dist/docs/example-agents/fsd.html +3 -3
  36. package/dist/docs/example-agents/index.html +3 -3
  37. package/dist/docs/example-agents/knowledge-base.html +3 -3
  38. package/dist/docs/example-agents/oncall.html +3 -3
  39. package/dist/docs/example-agents/security-reviewer.html +3 -3
  40. package/dist/docs/example-agents/slack-agent.html +3 -3
  41. package/dist/docs/example-agents/weather-agent.html +3 -3
  42. package/dist/docs/guides/agent-to-agent.html +3 -3
  43. package/dist/docs/guides/cloud-runtime.html +3 -3
  44. package/dist/docs/guides/github.html +3 -3
  45. package/dist/docs/guides/human-in-the-loop.html +3 -3
  46. package/dist/docs/guides/mcp-oauth.html +3 -3
  47. package/dist/docs/guides/opentelemetry.html +12 -5
  48. package/dist/docs/guides/slack.html +3 -3
  49. package/dist/docs/guides/webhooks.html +3 -3
  50. package/dist/docs/hashmap.json +1 -1
  51. package/dist/docs/hillclimbing.html +3 -3
  52. package/dist/docs/index.html +8 -28
  53. package/dist/docs/quickstart.html +24 -55
  54. package/dist/docs/reference/agent-config.html +11 -6
  55. package/dist/docs/reference/artifacts.html +3 -3
  56. package/dist/docs/reference/channels.html +3 -3
  57. package/dist/docs/reference/cli.html +6 -6
  58. package/dist/docs/reference/connections.html +3 -3
  59. package/dist/docs/reference/hooks.html +3 -3
  60. package/dist/docs/reference/http-api.html +3 -3
  61. package/dist/docs/reference/instructions.html +3 -3
  62. package/dist/docs/reference/playground.html +3 -3
  63. package/dist/docs/reference/project-layout.html +3 -3
  64. package/dist/docs/reference/prompt.html +3 -3
  65. package/dist/docs/reference/schedules.html +3 -3
  66. package/dist/docs/reference/sessions.html +3 -3
  67. package/dist/docs/reference/skills.html +3 -3
  68. package/dist/docs/reference/subagents.html +3 -3
  69. package/dist/docs/reference/tools.html +5 -5
  70. package/dist/docs/scaffolding-agents.html +3 -3
  71. package/dist/docs/storage.html +3 -3
  72. package/dist/docs/templates/triage.html +3 -3
  73. package/dist/docs/troubleshooting.html +3 -3
  74. package/dist/index.d.ts +1 -1
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.js +1 -1
  77. package/dist/internal/cli-ax.js +2 -2
  78. package/dist/internal/cli-deploy.d.ts.map +1 -1
  79. package/dist/internal/cli-deploy.js +38 -7
  80. package/dist/internal/deploy-client.d.ts +8 -0
  81. package/dist/internal/deploy-client.d.ts.map +1 -1
  82. package/dist/internal/deploy-client.js +5 -2
  83. package/dist/internal/discovery.d.ts.map +1 -1
  84. package/dist/internal/discovery.js +46 -9
  85. package/dist/internal/host-otel.d.ts +25 -0
  86. package/dist/internal/host-otel.d.ts.map +1 -0
  87. package/dist/internal/host-otel.js +162 -0
  88. package/dist/internal/init-project.js +10 -10
  89. package/dist/internal/otel/collector.d.ts +5 -0
  90. package/dist/internal/otel/collector.d.ts.map +1 -1
  91. package/dist/internal/otel/collector.js +68 -27
  92. package/dist/internal/sdk-runner.d.ts +1 -1
  93. package/dist/internal/sdk-runner.d.ts.map +1 -1
  94. package/dist/internal/sdk-runner.js +1 -1
  95. package/dist/internal/server.d.ts.map +1 -1
  96. package/dist/internal/server.js +1 -0
  97. package/dist/internal/session-engine.d.ts +5 -1
  98. package/dist/internal/session-engine.d.ts.map +1 -1
  99. package/dist/internal/session-engine.js +23 -5
  100. package/dist/playground/assets/index-CZH9EFvO.css +1 -0
  101. package/dist/playground/assets/{index-D-DIYQzw.js → index-Dces8BDp.js} +26 -26
  102. package/dist/playground/index.html +2 -2
  103. package/dist/types.d.ts +81 -2
  104. package/dist/types.d.ts.map +1 -1
  105. package/docs/README.md +33 -106
  106. package/docs/guides/opentelemetry.md +30 -0
  107. package/docs/quickstart.md +89 -227
  108. package/docs/reference/agent-config.md +52 -0
  109. package/docs/reference/cli.md +2 -2
  110. package/docs/reference/tools.md +5 -4
  111. package/package.json +2 -2
  112. package/skills/otel/SKILL.md +20 -2
  113. package/src/channels/github/github-channel.ts +9 -0
  114. package/src/channels/github/types.ts +4 -0
  115. package/src/channels.ts +38 -0
  116. package/src/index.ts +1 -0
  117. package/src/internal/cli-ax.ts +2 -2
  118. package/src/internal/cli-deploy.ts +58 -3
  119. package/src/internal/deploy-client.ts +11 -0
  120. package/src/internal/discovery.ts +49 -0
  121. package/src/internal/host-otel.ts +252 -0
  122. package/src/internal/init-project.ts +10 -10
  123. package/src/internal/otel/collector.ts +135 -50
  124. package/src/internal/sdk-runner.ts +6 -0
  125. package/src/internal/server.ts +1 -0
  126. package/src/internal/session-engine.ts +32 -2
  127. package/src/types.ts +89 -2
  128. package/dist/docs/assets/chunks/@localSearchIndexroot.BZx4pmZL.js +0 -1
  129. package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.js +0 -20
  130. package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.lean.js +0 -1
  131. package/dist/docs/assets/index.md.CMc5FP7E.js +0 -25
  132. package/dist/docs/assets/index.md.CMc5FP7E.lean.js +0 -1
  133. package/dist/docs/assets/quickstart.md.rIbfahJM.lean.js +0 -1
  134. package/dist/playground/assets/index-DQTH622f.css +0 -1
  135. /package/dist/docs/assets/{reference_cli.md.DqEqQpGn.lean.js → reference_cli.md.BDmjPPAm.lean.js} +0 -0
  136. /package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
@@ -1,29 +1,27 @@
1
1
  ---
2
- title: "Build your first PR approver"
3
- description: "Create an agent that reviews pull requests by complexity, approves the safe ones, and wakes from GitHub webhooks."
2
+ title: "Build your first PR reviewer"
3
+ description: "Create an agent that reviews pull requests by complexity, approves safe changes, and handles GitHub webhook events."
4
4
  ---
5
5
 
6
- # Build your first PR approver
6
+ # Build your first PR reviewer
7
7
 
8
- Build an agent that reviews GitHub pull requests. It fetches the diff,
9
- rates the change's complexity in plain TypeScript, approves the safe
10
- ones, and flags the rest for a human. Then wire it to GitHub webhooks
11
- and watch a pull request wake it.
8
+ Build a GitHub PR reviewer that classifies changes as `trivial`,
9
+ `moderate`, or `large`, then approves safe changes or requests human
10
+ review. Add GitHub event handling so pull requests can trigger reviews.
12
11
 
13
- The split is the point of the exercise: deterministic policy lives in
14
- typed tools, judgment lives in the model, and every decision is
15
- inspectable in the playground.
12
+ ## Getting started
16
13
 
17
- ## Prerequisites
14
+ - **Get started with an agent in Cursor:** follow
15
+ [Scaffold an agent with Cursor](./scaffolding-agents.md) and ask Cursor
16
+ to read [`skills/create-agent/SKILL.md`](../skills/create-agent/SKILL.md).
17
+ - **Get started in the CLI:** continue below.
18
18
 
19
- You need:
19
+ ## Prerequisites
20
20
 
21
21
  - Node 22.13 or newer. Bun isn't supported.
22
- - The `agent-sdk` CLI. `npx @cursor/july init ./pr-approver` bootstraps
23
- it with no prior install: `init` scaffolds the project, runs
24
- `npm install`, links `agent-sdk` onto your PATH, and offers a Cursor
25
- sign-in. See [Run the CLI](/#run-the-cli) for other setups, such as a
26
- monorepo source checkout.
22
+ - Run commands as `agent-sdk <command>`, or use
23
+ `npx @cursor/july <command>` when the CLI isn't on `PATH`. See
24
+ [Run the CLI](/#run-the-cli) for monorepo checkouts and other setups.
27
25
  - A Cursor credential for model turns. Sign in once:
28
26
 
29
27
  ```bash
@@ -32,122 +30,33 @@ agent-sdk login
32
30
 
33
31
  You can also set `CURSOR_API_KEY` instead of signing in.
34
32
 
35
- - A GitHub credential. `gh auth login` is enough, or set
36
- `GITHUB_TOKEN`. The tools you write resolve either one
37
- automatically. Reading pull requests works on any public repo;
38
- posting reviews needs write access to the repo you review.
33
+ - Authenticate with `gh auth login` or `GITHUB_TOKEN`. You can read
34
+ public pull requests. Posting reviews requires repository write access.
39
35
 
40
- ## First run in 10 minutes
36
+ ## Create and run the project
41
37
 
42
- Want a working agent before the full tutorial? Four commands get you
43
- there:
38
+ Initialize the project and start the development server:
44
39
 
45
40
  ```bash
46
- npx @cursor/july init ./pr-approver # scaffold + npm install + PATH link + sign-in offer
47
- cd pr-approver
48
- agent-sdk dev # serve, and print the playground URL
49
- ```
50
-
51
- Open the playground URL and chat with the scaffold. Then, in a second
52
- terminal (`dev` keeps running), run one turn from the command line:
53
-
54
- ```bash
55
- agent-sdk run --dir . --message "Introduce yourself in one sentence."
56
- ```
57
-
58
- That's the whole loop: files become an agent, `dev` serves it, and
59
- `run` exercises it. The rest of this page turns that scaffold into a
60
- real PR approver.
61
-
62
- ## Scaffolding Agents
63
-
64
- Have Cursor read [`skills/create-agent/SKILL.md`](../skills/create-agent/SKILL.md)
65
- and describe what you want:
66
-
67
- > Build me a PR approver for the playground. Start with one tool that
68
- > inspects a pull request and guide me through the remaining decisions.
69
-
70
- Cursor asks for missing choices, shows you the plan, then builds and
71
- verifies the agent. Continue below to do the same by hand.
72
-
73
- See [Scaffold an agent with Cursor](./scaffolding-agents.md) for the
74
- full guided workflow.
75
-
76
- ## Create your project
77
-
78
- Start with the built-in scaffold:
79
-
80
- ```bash
81
- npx @cursor/july init ./pr-approver
82
- cd pr-approver
41
+ npx @cursor/july init ./sdk-pr-reviewer
42
+ cd sdk-pr-reviewer
83
43
  agent-sdk dev
84
44
  ```
85
45
 
86
- The scaffold creates the files the Agent SDK discovers, plus empty capability
87
- folders (each with a `.gitkeep`) so you can drop tools, channels, and
88
- evals in place:
89
-
90
- ```text
91
- pr-approver/
92
- ├── agent/
93
- │ ├── agent.ts
94
- │ ├── instructions.md
95
- │ ├── tools/
96
- │ │ └── echo.ts
97
- │ ├── skills/
98
- │ ├── mcp-connections/
99
- │ ├── subagents/
100
- │ ├── channels/
101
- │ ├── hooks/
102
- │ │ └── memory.ts
103
- │ ├── ab/
104
- │ ├── schedules/
105
- │ ├── sandbox/workspace/
106
- │ └── lib/
107
- ├── evals/
108
- ├── package.json
109
- └── tsconfig.json
110
- ```
111
-
112
- `agent.ts` holds the model and runtime settings. `instructions.md` is
113
- the always-on system prompt, and the scaffold's version includes a
114
- memory section that tells the agent how to consult its journal. Each
115
- file under `agent/tools/` becomes a tool, and `agent/hooks/memory.ts`
116
- journals every turn so future sessions can recall past work (delete it
117
- to opt out). `tsconfig.json` type-checks the project (`npm run check`);
118
- the framework runs your TypeScript directly, so nothing compiles.
119
-
120
- `agent-sdk dev` blocks until you stop it. Keep it running and open a
121
- second terminal for every other command on this page, starting with
122
- these checks:
46
+ Keep `agent-sdk dev` running. In a second terminal, run:
123
47
 
124
48
  ```bash
125
- agent-sdk validate --dir .
126
- agent-sdk info --dir .
127
- ```
128
-
129
- These commands inspect the project without starting a model turn.
130
-
131
- ## Run your agent
132
-
133
- The scaffold already works. Run one turn from the terminal:
134
-
135
- ```bash
136
- agent-sdk run --dir . \
137
- --message "Introduce yourself in one sentence."
49
+ agent-sdk run --dir . --message "Introduce yourself in one sentence."
138
50
  ```
139
51
 
140
- `run` starts the agent, sends the message, and waits for the final
141
- reply. It prints a JSON trajectory with the response, tool calls, and
142
- token usage. It also writes an NDJSON trace under
143
- `.agent-serve/traces/`.
52
+ Confirm the agent replies.
144
53
 
145
- ## Teach it to review
54
+ ## Add review instructions
146
55
 
147
56
  Replace `agent/instructions.md`:
148
57
 
149
58
  ```md
150
- # PR approver
59
+ # PR reviewer
151
60
 
152
61
  You review GitHub pull requests. Be specific and brief.
153
62
 
@@ -178,9 +87,6 @@ rm agent/tools/echo.ts
178
87
 
179
88
  ## Add a shared helper
180
89
 
181
- Both tools need to split a PR URL into its parts. Shared code lives in
182
- `agent/lib/`, which the framework never loads as tools.
183
-
184
90
  Create `agent/lib/github.ts`:
185
91
 
186
92
  ```ts
@@ -213,7 +119,7 @@ import { parsePullUrl } from "../lib/github.js";
213
119
 
214
120
  export type Complexity = "trivial" | "moderate" | "large";
215
121
 
216
- /** Deterministic policy: the tool rates the change, not the model. */
122
+ /** Rate complexity from fixed line and file-count thresholds. */
217
123
  function rateComplexity(linesChanged: number, changedFiles: number): Complexity {
218
124
  if (linesChanged <= 25 && changedFiles <= 2) {
219
125
  return "trivial";
@@ -280,58 +186,30 @@ export default defineTool({
280
186
  });
281
187
  ```
282
188
 
283
- The file adds one tool named `inspect_pr`:
284
-
285
- - `description` tells the model when to call it.
286
- - `inputSchema` defines and validates the arguments.
287
- - `execute` runs on the server and returns data to the model.
189
+ ## Verify complexity classification
288
190
 
289
- Two details carry the design. `rateComplexity` is the review policy,
290
- and it lives in code: the model never decides what counts as a big
291
- change. And `ctx.host.github` is the shared host GitHub client, so the
292
- tool inherits whatever credential the host has (a token, `gh auth`, or
293
- a GitHub App) without parsing any of it.
294
-
295
- ## Try the inspect tool
296
-
297
- Call the tool directly first, on a real merged pull request:
191
+ Run `inspect_pr` against a small pull request:
298
192
 
299
193
  ```bash
300
194
  agent-sdk call inspect_pr --dir . \
301
195
  --input '{"prUrl":"https://github.com/facebook/react/pull/35623"}'
302
196
  ```
303
197
 
304
- `call` validates the input and runs `execute` without a model turn.
305
- This PR is a one-character typo fix, so the result comes back rated
306
- `trivial` with the whole patch inline:
307
-
308
- ```json
309
- {
310
- "title": "Fix typo: accomodate -> accommodate",
311
- "additions": 1,
312
- "deletions": 1,
313
- "changedFiles": 1,
314
- "complexity": "trivial",
315
- "files": [{ "path": "compiler/packages/...", "patch": "@@ -1315,7 ..." }]
316
- }
317
- ```
198
+ Confirm the result contains `"complexity": "trivial"`.
318
199
 
319
- Now call it on the PR that added `experimental_useEvent` to React:
320
- 1,027 additions across 26 files.
200
+ Run it against a large pull request:
321
201
 
322
202
  ```bash
323
203
  agent-sdk call inspect_pr --dir . \
324
204
  --input '{"prUrl":"https://github.com/facebook/react/pull/25229"}'
325
205
  ```
326
206
 
327
- The rating flips to `large` and the patches disappear from the result.
328
- The policy in the tool decides how much the model gets to see, before
329
- any model turn spends a token on it.
207
+ Confirm the result contains `"complexity": "large"` and no `patch`
208
+ fields.
330
209
 
331
210
  ## Add the review tool
332
211
 
333
- The approver needs a way to act on its verdict. Create
334
- `agent/tools/submit_review.ts`:
212
+ Create `agent/tools/submit_review.ts` to post each verdict:
335
213
 
336
214
  ```ts
337
215
  import { defineTool } from "@cursor/july/tools";
@@ -354,10 +232,10 @@ export default defineTool({
354
232
  const { owner, repo, number } = parsePullUrl(prUrl);
355
233
  const review =
356
234
  verdict === "approve"
357
- ? { event: "APPROVE" as const, body: `PR approver: ${summary}` }
235
+ ? { event: "APPROVE" as const, body: `PR reviewer: ${summary}` }
358
236
  : {
359
237
  event: "COMMENT" as const,
360
- body: `PR approver: this change needs a human review. ${summary}`,
238
+ body: `PR reviewer: this change needs a human review. ${summary}`,
361
239
  };
362
240
 
363
241
  const octokit = await ctx.host.github.getOctokit();
@@ -373,47 +251,24 @@ export default defineTool({
373
251
  });
374
252
  ```
375
253
 
376
- The tool posts a real review: an APPROVE when the agent approves, a
377
- comment asking for a human otherwise. Two GitHub rules shape how you
378
- test it. Your credential needs write access to the repo it reviews,
379
- and GitHub rejects approving your own pull request, so hand the agent
380
- a teammate's PR rather than one you authored. When a post fails, the
381
- tool call reports the GitHub error to the model and the turn keeps
382
- going.
383
-
384
- Want a person to sign off before the review lands? Set
385
- `needsApproval: true` on the tool and the call parks until someone
386
- approves it from the playground or Slack.
387
- [Human-in-the-loop approvals](./guides/human-in-the-loop.md) shows the
388
- flow.
254
+ Use a GitHub credential with write access and a pull request you didn't
255
+ author.
389
256
 
390
257
  ## Review a pull request
391
258
 
392
- Run the whole loop on a pull request your credential can review. A
393
- teammate's open PR is the right pick: write access to the repo, and
394
- not authored by you.
259
+ Review an open pull request:
395
260
 
396
261
  ```bash
397
262
  agent-sdk run --dir . \
398
263
  --message "Review https://github.com/acme/checkout/pull/42"
399
264
  ```
400
265
 
401
- The trajectory shows two tool calls. The agent inspects the PR, reads
402
- the patches, and submits its verdict. A small, clean change gets an
403
- APPROVE review on the spot, with a one-line summary of what it checked.
404
- A large one gets a comment asking for a human review, pointing at the
405
- files a reviewer should start with. Same instructions, different
406
- behavior, because the policy in the tool decided how much the model got
407
- to see.
408
-
409
- Open the PR on GitHub: the review is on the timeline, posted by
410
- whatever identity your credential belongs to.
266
+ Confirm `inspect_pr` runs before `submit_review`, then verify the review
267
+ on GitHub.
411
268
 
412
- ## Wake it from GitHub
269
+ ## Trigger reviews from GitHub
413
270
 
414
- A reviewer you have to prompt is only half useful. Give the agent a
415
- GitHub channel so pull requests wake it. Create
416
- `agent/channels/github.ts`:
271
+ Create `agent/channels/github.ts`:
417
272
 
418
273
  ```ts
419
274
  import {
@@ -424,10 +279,8 @@ import {
424
279
  const REVIEW_ACTIONS = new Set(["opened", "reopened", "ready_for_review"]);
425
280
 
426
281
  export default githubChannel({
427
- botName: "pr-approver",
282
+ botName: "sdk-pr-reviewer",
428
283
  webhookEvents: ["pull_request"],
429
- // submit_review owns every GitHub write. Without these flags the channel
430
- // also posts chat replies and reactions to the PR when the token allows it.
431
284
  deliverReplies: false,
432
285
  progress: { reactions: false },
433
286
  onPullRequest: (ctx, pr) => {
@@ -446,57 +299,66 @@ export default githubChannel({
446
299
  });
447
300
  ```
448
301
 
449
- The channel mounts `POST /v1/channels/github` and dispatches on the
450
- `pull_request` events you declared. Opened, reopened, and undrafted PRs
451
- start a model turn; everything else returns `null` and is skipped.
452
-
453
- Serve the agent, then replay a real PR at it from a second terminal.
454
- `replay` reads the PR through `gh api`, synthesizes a GitHub-shaped
455
- webhook delivery, and POSTs it to the channel. No repo admin, no
456
- tunnel:
302
+ Keep `agent-sdk dev` running. In a second terminal, replay an open pull
303
+ request. Pull access is enough; repo admin isn't required:
457
304
 
458
305
  ```bash
459
- agent-sdk dev
460
- # second terminal:
461
306
  agent-sdk github replay https://github.com/acme/checkout/pull/42 \
462
307
  --dir . --action opened
463
308
  ```
464
309
 
465
- The replay prints the delivery, and the serve terminal shows the wake:
310
+ Open the playground and confirm `Review acme/checkout#42` contains the
311
+ trigger, both tool calls, and the verdict.
466
312
 
467
- ```text
468
- [agent-sdk] replaying acme/checkout#42 (pull_request) → 1 channel
469
- [agent-sdk] pull_request.opened pr-approver/github 200
470
- ```
313
+ ## Run options
314
+
315
+ 1. **CLI message**
316
+
317
+ ```bash
318
+ agent-sdk run --dir . --message "Review https://github.com/acme/checkout/pull/42"
319
+ ```
320
+
321
+ 2. **Local replay**
322
+
323
+ ```bash
324
+ agent-sdk github replay https://github.com/acme/checkout/pull/42 \
325
+ --dir . --action opened
326
+ ```
327
+
328
+ 3. **Forward or `--cursor-events`**
329
+
330
+ Pull Cursor events:
331
+
332
+ ```bash
333
+ agent-sdk serve --dir . --cursor-events --repo owner/repo
334
+ ```
335
+
336
+ Forward GitHub webhooks:
337
+
338
+ ```bash
339
+ agent-sdk github forward --dir .
340
+ ```
471
341
 
472
- The agent runs the same inspect-then-submit loop, unprompted this time.
473
- Replay the same PR again and the channel resumes that PR's session
474
- instead of starting a new one: each pull request keeps one running
475
- conversation.
342
+ Keep the local `dev` or `serve` process running.
476
343
 
477
- ## Open the playground
344
+ 4. **Hosted deployment**
478
345
 
479
- Keep `agent-sdk dev` running and open the playground URL it printed. The
480
- webhook session is in the session list, titled
481
- `Review acme/checkout#42`, with the trigger message, both tool calls,
482
- and the verdict laid out. Start a new chat there and ask for another
483
- review to watch a turn stream live.
346
+ Deploy once, set the GitHub App secrets, then redeploy:
484
347
 
485
- ## Go live
348
+ ```bash
349
+ agent-sdk deploy
350
+ agent-sdk secrets set sdk-pr-reviewer GITHUB_APP_ID GITHUB_APP_PRIVATE_KEY
351
+ agent-sdk deploy
352
+ ```
486
353
 
487
- Replay is for development. For real deliveries, serve with
488
- `--cursor-events --repo owner/repo` to pull events for repositories
489
- connected to Cursor with no public URL, or run
490
- `agent-sdk github forward` to relay webhooks to your dev server. The
491
- [GitHub guide](./guides/github.md) compares the options. In production,
492
- give the host GitHub App credentials so reviews post as your app's bot
493
- identity instead of a personal account.
354
+ See [GitHub](./guides/github.md) for local event delivery and
355
+ [Deployment](./deployment.md) for hosting.
494
356
 
495
357
  ## Where to go next
496
358
 
497
- - [`examples/approval-buddy`](../examples/approval-buddy/): the
498
- production-shaped sibling, with commit statuses, review subagents,
499
- and a deterministic stamp policy
359
+ - [`examples/approval-buddy`](../examples/approval-buddy/): an example
360
+ with commit statuses, review subagents, and a deterministic stamp
361
+ policy
500
362
  - [Evals](./evals.md): freeze these two PRs as regression checks so
501
363
  prompt changes can't flip a verdict
502
364
  - [Tools](./reference/tools.md): more on typed tools, approvals, and
@@ -44,6 +44,7 @@ export default defineAgent({
44
44
  | `hosting` | `{ egressDomains?, secretNames? }` | Managed-hosting declarations read by `agent-sdk deploy`: the pod's egress allowlist and the secret names the agent expects. Ignored by local serving. |
45
45
  | `concurrency` | `{ maxRunningTurns? }` | Engine-wide turn admission limit. See [Concurrency](#concurrency). |
46
46
  | `builtinTools` | `{ reminders? }` | Framework-provided model-facing tools, opted in per capability. See [Built-in tools](#built-in-tools). |
47
+ | `tools` | `ToolName[]` | Allowlist of built-in harness tools offered to the model. Unset = the model's full standard toolset. See [Allowlist built-in harness tools](#allowlist-built-in-harness-tools). |
47
48
 
48
49
  ## Choose a model
49
50
 
@@ -120,6 +121,57 @@ outside it (or runs with `--state-root` under `/tmp`). An agent that
120
121
  *needs* a specific checkout's skills and rules points `cwd` inside that
121
122
  checkout.
122
123
 
124
+ ## Allowlist built-in harness tools
125
+
126
+ `tools` names which of the Cursor harness's built-in tools the model
127
+ is offered, riding the SDK's `AgentOptions.tools`. It is opt-in by
128
+ design: unset keeps the model's full standard toolset, and once set
129
+ the agent gets exactly what it names — nothing more. `[]` removes
130
+ every native built-in tool. There is deliberately no exclusion form:
131
+ an agent that restricts tools states what it keeps, so tools the
132
+ platform adds later stay off until someone opts in.
133
+
134
+ ```ts
135
+ export default defineAgent({
136
+ model: "composer-2.5",
137
+ // Read-only triage agent: search and read only.
138
+ // No shell, no edits, no subagents.
139
+ tools: ["read", "grep", "glob", "ls"],
140
+ });
141
+ ```
142
+
143
+ One name is injected for you: **`"mcp"` is always granted alongside a
144
+ configured allowlist**. It is transport, not capability — this kit's
145
+ authored server tools reach the model over the SDK custom-tool MCP
146
+ path, so omitting it would silently strand `agent/tools/`. What MCP
147
+ actually exposes stays governed by the second layer: the tools you
148
+ author, the connections you declare, and the harness cwd's ambient
149
+ `.cursor` config. An agent that must not see a checkout's MCP servers
150
+ points `local.cwd` outside it (see [Local cwd](#local-cwd)), and
151
+ `local.sandbox` makes MCP tool calls fail closed.
152
+
153
+ The other names use the SDK's public tool vocabulary (`"shell"`,
154
+ `"read"`, `"edit"`, `"grep"`, `"glob"`, `"ls"`, `"task"`, ...);
155
+ unknown names fail the turn with a `ConfigurationError`. Two deserve
156
+ care: `"shell"` is a capability group that also grants shell stdin
157
+ writes — and is what `execution: "agent"` tool scripts run through,
158
+ so an allowlist without it strands them (discovery warns about that
159
+ combination). `"task"` gates subagents (which otherwise keep their
160
+ own curated toolsets).
161
+
162
+ The allowlist states runtime-agnostic intent, but the SDK cannot
163
+ enforce tool restrictions on cloud agents yet — and a restriction
164
+ must never silently not apply. A `runtime: "cloud"` agent that sets
165
+ `tools` fails at serve start with an error diagnostic, and a hybrid
166
+ agent's per-send cloud sessions are refused (the turn errors) rather
167
+ than run unrestricted. When the SDK lifts the limitation, the same
168
+ field will simply start applying to cloud turns.
169
+
170
+ Not a security boundary against the serve host: like `local.sandbox`,
171
+ the allowlist only shapes what the model can call; pair with
172
+ `sandbox: true` when the concern is untrusted input steering the
173
+ model.
174
+
123
175
  ## The `cloud` block
124
176
 
125
177
  Cloud agent defaults forwarded to the Cursor SDK: `repos` (each
@@ -141,8 +141,8 @@ agent folder as a positional path, or run it from inside the project:
141
141
 
142
142
  ```bash
143
143
  agent-sdk dev
144
- agent-sdk dev ./pr-approver
145
- agent-sdk dev ./pr-approver --port 3000
144
+ agent-sdk dev ./sdk-pr-reviewer
145
+ agent-sdk dev ./sdk-pr-reviewer --port 3000
146
146
  ```
147
147
 
148
148
  `dev` accepts the same flags as [`serve`](#serve). You can use `--dir`
@@ -82,10 +82,11 @@ export default defineTool({
82
82
  `ctx.host` carries the shared host services: `host.mcp` (authored MCP
83
83
  connections: `names()`, `listTools(name)`, `callTool(name, tool, args)`),
84
84
  `host.github` and `host.slack` (shared platform clients), `host.kv` and
85
- `host.files` (durable [storage](../storage.md)), `host.reminders`
86
- (per-session wakes, when attached), `host.evals` (playground eval
87
- batches, when attached), and `host.slackNudges` (Slack ask-dedupe
88
- helpers).
85
+ `host.files` (durable [storage](../storage.md)), `host.otel` (custom
86
+ metrics and session tags; see [OpenTelemetry](../guides/opentelemetry.md)),
87
+ `host.reminders` (per-session wakes, when attached), `host.evals`
88
+ (playground eval batches, when attached), and `host.slackNudges`
89
+ (Slack ask-dedupe helpers).
89
90
 
90
91
  ### Return values
91
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor/july",
3
- "version": "0.1.43",
3
+ "version": "0.1.45",
4
4
  "description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -215,7 +215,7 @@
215
215
  "node": ">=22.13"
216
216
  },
217
217
  "dependencies": {
218
- "@cursor/sdk": "1.0.26",
218
+ "@cursor/sdk": "1.0.27",
219
219
  "@modelcontextprotocol/sdk": "1.25.1",
220
220
  "@octokit/auth-app": "^7.2.0",
221
221
  "@octokit/rest": "^21.1.1",
@@ -2,8 +2,8 @@
2
2
  name: agentkit-otel
3
3
  description: >-
4
4
  Wire OpenTelemetry export for an Agent SDK agent (defineOtel, OTLP env,
5
- traces/metrics/logs). Use when adding observability, Datadog/Grafana/OTLP
6
- export, or session/turn/tool tracing.
5
+ traces/metrics/logs, ctx.host.otel). Use when adding observability,
6
+ Datadog/Grafana/OTLP export, session/turn/tool tracing, or custom metrics.
7
7
  ---
8
8
 
9
9
  # Agent SDK OpenTelemetry (`defineOtel`)
@@ -61,6 +61,24 @@ Precedence: `serve({ otel })` over `agent/otel.ts` over env.
61
61
  Prompt text, tool payloads, and failure text stay off the wire unless
62
62
  those flags are set.
63
63
 
64
+ ## Custom metrics (`ctx.host.otel`)
65
+
66
+ Always present. `increment` / `record` no-op without a meter.
67
+ `setAttributes` still tags the open session when a collector is
68
+ attached. Prefix names with team or agent. No custom spans. Reserved
69
+ metric names: `cursor.token.usage`, `cursor.tool.calls`,
70
+ `cursor.cost.usage`, `agent_sdk.session.count`,
71
+ `agent_sdk.turn.count`, `agent_sdk.subagent.count`,
72
+ `agent_sdk.http.duration`. Reserved attributes:
73
+ `cursor.conversation.id`, `agent_sdk.agent`, `agent_sdk.turn_id`,
74
+ `agent_sdk.framework`.
75
+
76
+ ```ts
77
+ ctx.host.otel.setAttributes({ "abc.ticket_id": "INC-123" });
78
+ ctx.host.otel.increment("abc.ticket.resolved");
79
+ ctx.host.otel.record("abc.approval.duration_ms", 1420);
80
+ ```
81
+
64
82
  ## Attributes worth joining on
65
83
 
66
84
  - `cursor.conversation.id` (session id)
@@ -137,6 +137,12 @@ const githubPullRequestPayloadSchema: z.ZodType<GitHubPullRequestPayload> =
137
137
  }),
138
138
  sender: senderSchema,
139
139
  installation: installationSchema,
140
+ label: z
141
+ .object({
142
+ name: z.string().min(1),
143
+ })
144
+ .nullable()
145
+ .optional(),
140
146
  });
141
147
 
142
148
  /**
@@ -336,6 +342,9 @@ export function parseGitHubPullRequestEvent(args: {
336
342
  ...(payload.pull_request.user?.login === undefined
337
343
  ? {}
338
344
  : { authorLogin: payload.pull_request.user.login }),
345
+ ...(payload.label?.name === undefined
346
+ ? {}
347
+ : { label: { name: payload.label.name } }),
339
348
  };
340
349
  return { ctx, pullRequest };
341
350
  }
@@ -171,6 +171,8 @@ export type GitHubPullRequestPayload = {
171
171
  type?: string;
172
172
  } | null;
173
173
  installation?: { id?: number } | null;
174
+ /** Present on `labeled` / `unlabeled` pull_request deliveries. */
175
+ label?: { name: string } | null;
174
176
  };
175
177
 
176
178
  /** Normalized `pull_request` event handed to {@link GitHubPullRequestHook}. */
@@ -188,6 +190,8 @@ export interface GitHubPullRequestEvent {
188
190
  /** True when the PR was merged (from the webhook payload). */
189
191
  merged: boolean;
190
192
  authorLogin?: string;
193
+ /** Label added/removed on `labeled` / `unlabeled` actions. */
194
+ label?: { name: string };
191
195
  }
192
196
 
193
197
  /** Normalized `issues` event. */