@cursor/july 0.1.41 → 0.1.43

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 (195) hide show
  1. package/AGENTS.md +10 -3
  2. package/dist/bin/agent-serve.js +3 -1
  3. package/dist/channels/github/api.d.ts +5 -0
  4. package/dist/channels/github/api.d.ts.map +1 -1
  5. package/dist/channels/github/api.js +18 -0
  6. package/dist/channels/github/defaults.d.ts +4 -5
  7. package/dist/channels/github/defaults.d.ts.map +1 -1
  8. package/dist/channels/github/defaults.js +27 -1
  9. package/dist/channels/github/github-channel.d.ts.map +1 -1
  10. package/dist/channels/github/github-channel.js +28 -5
  11. package/dist/channels/github/index.d.ts +1 -0
  12. package/dist/channels/github/index.d.ts.map +1 -1
  13. package/dist/channels/github/index.js +1 -0
  14. package/dist/channels/github/progress.d.ts +54 -0
  15. package/dist/channels/github/progress.d.ts.map +1 -0
  16. package/dist/channels/github/progress.js +168 -0
  17. package/dist/channels/github/state.d.ts +2 -0
  18. package/dist/channels/github/state.d.ts.map +1 -1
  19. package/dist/channels/github/state.js +1 -1
  20. package/dist/channels/github/types.d.ts +8 -4
  21. package/dist/channels/github/types.d.ts.map +1 -1
  22. package/dist/channels/slack/slack-channel.d.ts +4 -0
  23. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  24. package/dist/channels/slack/slack-channel.js +4 -0
  25. package/dist/docs/404.html +2 -2
  26. package/dist/docs/ab.html +4 -4
  27. package/dist/docs/assets/{app.mIw24pb1.js → app.DsTLo2j1.js} +1 -1
  28. package/dist/docs/assets/chunks/@localSearchIndexroot.BZx4pmZL.js +1 -0
  29. package/dist/docs/assets/chunks/{VPLocalSearchBox.hOzpPwrG.js → VPLocalSearchBox.DoVfutcV.js} +1 -1
  30. package/dist/docs/assets/chunks/{theme.BtFhUYmh.js → theme.DtuVtKkc.js} +2 -2
  31. package/dist/docs/assets/{deployment.md.BHVjVqRa.js → deployment.md.Cdp4rTE4.js} +1 -1
  32. package/dist/docs/assets/{guides_github.md.R2QlpR75.js → guides_github.md.DSg0j9qj.js} +29 -3
  33. package/dist/docs/assets/{guides_github.md.R2QlpR75.lean.js → guides_github.md.DSg0j9qj.lean.js} +1 -1
  34. package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.js +20 -0
  35. package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.lean.js +1 -0
  36. package/dist/docs/assets/{index.md.BKpbkCa6.js → index.md.CMc5FP7E.js} +2 -1
  37. package/dist/docs/assets/{reference_channels.md.BIabFUAI.js → reference_channels.md.MN8iDy8V.js} +1 -1
  38. package/dist/docs/assets/reference_hooks.md.4cbRRIIu.js +14 -0
  39. package/dist/docs/assets/{reference_hooks.md.BHeG-vjK.lean.js → reference_hooks.md.4cbRRIIu.lean.js} +1 -1
  40. package/dist/docs/assets/{reference_project-layout.md._XdeMahr.js → reference_project-layout.md.D3MdHM2z.js} +2 -1
  41. package/dist/docs/assets/templates_triage.md.CConZt1z.js +11 -0
  42. package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +1 -0
  43. package/dist/docs/building-with-agents.html +5 -5
  44. package/dist/docs/concepts.html +5 -5
  45. package/dist/docs/deployment.html +6 -6
  46. package/dist/docs/evals.html +4 -4
  47. package/dist/docs/example-agents/approval-buddy.html +4 -4
  48. package/dist/docs/example-agents/benny.html +4 -4
  49. package/dist/docs/example-agents/bugbot.html +4 -4
  50. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  51. package/dist/docs/example-agents/codeowners-review.html +4 -4
  52. package/dist/docs/example-agents/concierge.html +4 -4
  53. package/dist/docs/example-agents/fsd.html +4 -4
  54. package/dist/docs/example-agents/index.html +5 -5
  55. package/dist/docs/example-agents/knowledge-base.html +4 -4
  56. package/dist/docs/example-agents/oncall.html +4 -4
  57. package/dist/docs/example-agents/security-reviewer.html +4 -4
  58. package/dist/docs/example-agents/slack-agent.html +4 -4
  59. package/dist/docs/example-agents/weather-agent.html +4 -4
  60. package/dist/docs/guides/agent-to-agent.html +4 -4
  61. package/dist/docs/guides/cloud-runtime.html +5 -5
  62. package/dist/docs/guides/github.html +33 -7
  63. package/dist/docs/guides/human-in-the-loop.html +4 -4
  64. package/dist/docs/guides/mcp-oauth.html +4 -4
  65. package/dist/docs/guides/opentelemetry.html +44 -0
  66. package/dist/docs/guides/slack.html +4 -4
  67. package/dist/docs/guides/webhooks.html +4 -4
  68. package/dist/docs/hashmap.json +1 -1
  69. package/dist/docs/hillclimbing.html +4 -4
  70. package/dist/docs/index.html +7 -6
  71. package/dist/docs/quickstart.html +4 -4
  72. package/dist/docs/reference/agent-config.html +4 -4
  73. package/dist/docs/reference/artifacts.html +4 -4
  74. package/dist/docs/reference/channels.html +6 -6
  75. package/dist/docs/reference/cli.html +4 -4
  76. package/dist/docs/reference/connections.html +4 -4
  77. package/dist/docs/reference/hooks.html +9 -7
  78. package/dist/docs/reference/http-api.html +4 -4
  79. package/dist/docs/reference/instructions.html +4 -4
  80. package/dist/docs/reference/playground.html +4 -4
  81. package/dist/docs/reference/project-layout.html +7 -6
  82. package/dist/docs/reference/prompt.html +4 -4
  83. package/dist/docs/reference/schedules.html +4 -4
  84. package/dist/docs/reference/sessions.html +4 -4
  85. package/dist/docs/reference/skills.html +4 -4
  86. package/dist/docs/reference/subagents.html +4 -4
  87. package/dist/docs/reference/tools.html +4 -4
  88. package/dist/docs/scaffolding-agents.html +4 -4
  89. package/dist/docs/storage.html +4 -4
  90. package/dist/docs/templates/triage.html +35 -0
  91. package/dist/docs/troubleshooting.html +4 -4
  92. package/dist/hooks.d.ts +3 -1
  93. package/dist/hooks.d.ts.map +1 -1
  94. package/dist/hooks.js +3 -1
  95. package/dist/index.d.ts +2 -0
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +1 -0
  98. package/dist/internal/cli-ax.d.ts +1 -0
  99. package/dist/internal/cli-ax.d.ts.map +1 -1
  100. package/dist/internal/cli-ax.js +18 -3
  101. package/dist/internal/discovery.d.ts.map +1 -1
  102. package/dist/internal/discovery.js +36 -4
  103. package/dist/internal/distribution.d.ts.map +1 -1
  104. package/dist/internal/distribution.js +1 -0
  105. package/dist/internal/init-project.d.ts +24 -0
  106. package/dist/internal/init-project.d.ts.map +1 -1
  107. package/dist/internal/init-project.js +126 -7
  108. package/dist/internal/otel/attrs.d.ts +7 -0
  109. package/dist/internal/otel/attrs.d.ts.map +1 -0
  110. package/dist/internal/otel/attrs.js +13 -0
  111. package/dist/internal/otel/collector.d.ts +16 -0
  112. package/dist/internal/otel/collector.d.ts.map +1 -0
  113. package/dist/internal/otel/collector.js +564 -0
  114. package/dist/internal/otel/config.d.ts +42 -0
  115. package/dist/internal/otel/config.d.ts.map +1 -0
  116. package/dist/internal/otel/config.js +236 -0
  117. package/dist/internal/otel/http.d.ts +11 -0
  118. package/dist/internal/otel/http.d.ts.map +1 -0
  119. package/dist/internal/otel/http.js +97 -0
  120. package/dist/internal/otel/runtime.d.ts +21 -0
  121. package/dist/internal/otel/runtime.d.ts.map +1 -0
  122. package/dist/internal/otel/runtime.js +188 -0
  123. package/dist/internal/server.d.ts.map +1 -1
  124. package/dist/internal/server.js +25 -1
  125. package/dist/internal/session-engine.d.ts +10 -1
  126. package/dist/internal/session-engine.d.ts.map +1 -1
  127. package/dist/internal/session-engine.js +36 -2
  128. package/dist/memory.d.ts +4 -0
  129. package/dist/memory.d.ts.map +1 -1
  130. package/dist/memory.js +4 -0
  131. package/dist/otel.d.ts +88 -0
  132. package/dist/otel.d.ts.map +1 -0
  133. package/dist/otel.js +64 -0
  134. package/dist/playground/assets/{index-B3HBneZZ.js → index-D-DIYQzw.js} +31 -31
  135. package/dist/playground/assets/index-DQTH622f.css +1 -0
  136. package/dist/playground/index.html +2 -2
  137. package/dist/types.d.ts +31 -4
  138. package/dist/types.d.ts.map +1 -1
  139. package/docs/README.md +3 -0
  140. package/docs/deployment.md +2 -1
  141. package/docs/guides/github.md +79 -6
  142. package/docs/guides/opentelemetry.md +184 -0
  143. package/docs/reference/channels.md +3 -1
  144. package/docs/reference/hooks.md +25 -5
  145. package/docs/reference/project-layout.md +2 -0
  146. package/docs/templates/triage.md +147 -0
  147. package/package.json +23 -1
  148. package/skills/create-agent/SKILL.md +2 -2
  149. package/skills/debug/SKILL.md +4 -0
  150. package/skills/framework-map/SKILL.md +2 -0
  151. package/skills/otel/SKILL.md +80 -0
  152. package/src/bin/agent-serve.ts +3 -1
  153. package/src/channels/github/api.ts +22 -0
  154. package/src/channels/github/defaults.ts +27 -6
  155. package/src/channels/github/github-channel.ts +27 -1
  156. package/src/channels/github/index.ts +1 -0
  157. package/src/channels/github/progress.ts +243 -0
  158. package/src/channels/github/state.ts +3 -0
  159. package/src/channels/github/types.ts +8 -4
  160. package/src/channels/slack/slack-channel.ts +4 -0
  161. package/src/hooks.ts +3 -1
  162. package/src/index.ts +6 -0
  163. package/src/internal/cli-ax.ts +19 -1
  164. package/src/internal/discovery.ts +59 -4
  165. package/src/internal/distribution.ts +1 -0
  166. package/src/internal/init-project.ts +150 -7
  167. package/src/internal/otel/attrs.ts +18 -0
  168. package/src/internal/otel/collector.ts +796 -0
  169. package/src/internal/otel/config.ts +307 -0
  170. package/src/internal/otel/http.ts +126 -0
  171. package/src/internal/otel/runtime.ts +271 -0
  172. package/src/internal/server.ts +38 -1
  173. package/src/internal/session-engine.ts +52 -0
  174. package/src/memory.ts +4 -0
  175. package/src/otel.ts +155 -0
  176. package/src/types.ts +29 -4
  177. package/templates/triage/README.md +48 -0
  178. package/templates/triage/agent/agent.ts +14 -0
  179. package/templates/triage/agent/channels/intake.ts +20 -0
  180. package/templates/triage/agent/channels/slack.ts +3 -0
  181. package/templates/triage/agent/hooks/memory.ts +3 -0
  182. package/templates/triage/agent/instructions.md +27 -0
  183. package/templates/triage/agent/skills/triage-rubric.md +43 -0
  184. package/templates/triage/agent/tools/record_triage.ts +23 -0
  185. package/templates/triage/evals/evals.config.ts +5 -0
  186. package/templates/triage/evals/triage.eval.ts +43 -0
  187. package/templates/triage/package.json +18 -0
  188. package/templates/triage/tsconfig.json +12 -0
  189. package/dist/docs/assets/chunks/@localSearchIndexroot.BA7V8dEJ.js +0 -1
  190. package/dist/docs/assets/reference_hooks.md.BHeG-vjK.js +0 -12
  191. package/dist/playground/assets/index-Bqk9nplh.css +0 -1
  192. /package/dist/docs/assets/{deployment.md.BHVjVqRa.lean.js → deployment.md.Cdp4rTE4.lean.js} +0 -0
  193. /package/dist/docs/assets/{index.md.BKpbkCa6.lean.js → index.md.CMc5FP7E.lean.js} +0 -0
  194. /package/dist/docs/assets/{reference_channels.md.BIabFUAI.lean.js → reference_channels.md.MN8iDy8V.lean.js} +0 -0
  195. /package/dist/docs/assets/{reference_project-layout.md._XdeMahr.lean.js → reference_project-layout.md.D3MdHM2z.lean.js} +0 -0
@@ -27,7 +27,9 @@ import { defineHook } from "@cursor/july/hooks";
27
27
  export default defineHook({
28
28
  events: {
29
29
  async "turn.completed"(event, ctx) {
30
- console.log("turn done", ctx.session.id, event.data.usage);
30
+ const prior = await ctx.host.kv.get("last-result");
31
+ const notes = await ctx.host.files.read("notes.md");
32
+ console.log("turn done", ctx.session.id, event.data.usage, prior, notes);
31
33
  },
32
34
  async "turn.failed"(event, ctx) {
33
35
  // page, count, or record
@@ -46,9 +48,14 @@ for everything. Handlers receive the event with its envelope (`index`,
46
48
  | `ctx.session` | Read-only session info: id, channel, mode, auth |
47
49
  | `ctx.agent` | `{ name }` of the agent the event belongs to |
48
50
  | `ctx.channel` | `{ id, continuationToken }` for the owning channel |
49
- | `ctx.stateRoot` | The agent's durable state root; hooks maintaining derived state write here |
51
+ | `ctx.stateRoot` | The agent's durable state root. Prefer `ctx.host.kv` / `ctx.host.files` for derived state; this tree resets on hosted replace |
52
+ | `ctx.host` | Shared host services; same as a tool's `ctx.host`. Pull JSON with `ctx.host.kv` and file-shaped state with `ctx.host.files` (session-bound by default; pass `{ scope: "deployment" }` for agent-wide files) |
50
53
  | `ctx.artifacts` | Session-bound [artifacts](./artifacts.md) facade: `tag` auto-fills the session |
51
54
 
55
+ Hook context includes `ctx.host`, the same shared services a tool
56
+ gets. Persist JSON with `ctx.host.kv` and file-shaped state with
57
+ `ctx.host.files`. Hooks observe; they do not own delivery surfaces.
58
+
52
59
  ## Hooks, channel events, evals, or A/B?
53
60
 
54
61
  All of them consume the same stream, for different jobs:
@@ -60,6 +67,15 @@ All of them consume the same stream, for different jobs:
60
67
  | Can affect the run | no | yes, it owns the surface | n/a | yes through arm instructions or `session.abs`; collection is observe-only |
61
68
  | Authored at | `agent/hooks/*.ts` | channel config | `evals/**/*.eval.ts` | [`agent/ab.ts` or `agent/ab/*.ts`](../ab.md) |
62
69
 
70
+ For GitHub commit status and sticky PR banners, use
71
+ `githubChannel({ progress: { commitStatus, banner } })` from
72
+ `@cursor/july/channels/github`. That is the supported Autofix-style
73
+ path. See [GitHub: Show PR progress](../guides/github.md#show-pr-progress).
74
+ Override channel `events` only when the lifecycle is custom (for
75
+ example [Approval Buddy](../example-agents/approval-buddy.md)'s
76
+ never-red status from tool output). Do not use `defineHook` for those
77
+ writes.
78
+
63
79
  ## Patterns
64
80
 
65
81
  Usage metering: subscribe to `turn.completed` and forward
@@ -70,7 +86,9 @@ Failure alerting: `turn.failed` carries the message, and
70
86
 
71
87
  Derived state: `agent.bound` fires when the Cursor SDK agent id is known
72
88
  (`bc-…` on cloud). A PR agent can record PR → agent id from it in a
73
- hook, so later webhook wakes resume the same cloud conversation.
89
+ hook with `ctx.host.kv`, so later webhook wakes resume the same cloud
90
+ conversation. Prefer `ctx.host.kv` or `ctx.host.files` for ids that
91
+ must survive hosted replace. `stateRoot` resets on replace.
74
92
 
75
93
  Transcript export: subscribe to `"*"` and append to your own store. The
76
94
  NDJSON envelope is already ordered and replayable.
@@ -80,8 +98,10 @@ NDJSON envelope is already ordered and replayable.
80
98
  Continue with these pages:
81
99
 
82
100
  - [Sessions and streaming](./sessions.md): every event a hook can see
83
- - [Deployment](../deployment.md#observability): hooks as the metrics
84
- export path
101
+ - [OpenTelemetry](../guides/opentelemetry.md): OTLP traces and metrics
102
+ from the same event stream
103
+ - [Deployment](../deployment.md#observability): runtime logs and export
104
+ paths
85
105
  - [Channels](./channels.md#events): the delivery-side counterpart
86
106
  - [Live A/B metrics](../ab.md): sticky variants over the same event
87
107
  stream
@@ -53,6 +53,7 @@ my-agent/
53
53
  │ │ └── github.ts # the GitHub webhook channel pack
54
54
  │ ├── hooks/
55
55
  │ │ └── audit.ts # observe the runtime event stream
56
+ │ ├── otel.ts # optional OpenTelemetry export
56
57
  │ ├── ab.ts # optional live A/B experiment
57
58
  │ ├── ab/
58
59
  │ │ └── concise.ts # optional: more experiments
@@ -83,6 +84,7 @@ Each path maps to a capability and a reference page.
83
84
  | `agent/subagents/<id>/` | Child agent directory; `description` required | [Subagents](./subagents.md) |
84
85
  | `agent/channels/*.ts` | HTTP surfaces beyond the built-in session API; `slack.ts` and `github.ts` use the platform packs | [Channels](./channels.md) |
85
86
  | `agent/hooks/*.ts` | Observe-only event subscribers, never fatal | [Hooks](./hooks.md) |
87
+ | `agent/otel.ts` | `defineOtel` OTLP export (traces, metrics, optional logs) | [OpenTelemetry](../guides/opentelemetry.md) |
86
88
  | `agent/ab.ts`, `agent/ab/*.ts` | `defineAB` experiments with sticky variants and live metrics | [Live A/B metrics](../ab.md) |
87
89
  | `agent/ab.config.ts` | `defineABConfig` shared A/B settings | [Live A/B metrics](../ab.md) |
88
90
  | `agent/storage.ts` | `defineStorage` backend for the durable `host.kv` / `host.files` APIs | [Storage](../storage.md) |
@@ -0,0 +1,147 @@
1
+ ---
2
+ title: "Ship a triage agent from the template"
3
+ description: "Scaffold the triage template, make its rubric yours, prove it with evals, and deploy it to Cursor-managed hosting."
4
+ ---
5
+
6
+ # Ship a triage agent from the template
7
+
8
+ The triage template is a working agent you own from the first command.
9
+ It reads a report, classifies it as a bug, a question, or feedback,
10
+ judges severity against a rubric, records a structured verdict, and
11
+ replies with the next step. This page walks the whole arc: scaffold,
12
+ first verdict, customize, prove, wire Slack, deploy.
13
+
14
+ Every file the template ships is yours to edit. The only dependency is
15
+ `@cursor/july` itself, pinned to the version the template was tested
16
+ against. Nothing phones home to a template registry.
17
+
18
+ ## Scaffold it
19
+
20
+ ```bash
21
+ npx @cursor/july init ./triage-agent --template triage
22
+ ```
23
+
24
+ One command with no prior install. It writes twelve files, runs
25
+ `npm install`, puts `agent-sdk` on your PATH, and offers a browser
26
+ sign-in when the machine has no Cursor credential. Model turns need
27
+ that credential, so sign in now rather than mid-flow:
28
+
29
+ ```bash
30
+ agent-sdk login
31
+ ```
32
+
33
+ The scaffold is a plain directory. Commit it to your own repo.
34
+
35
+ ## Get the first verdict
36
+
37
+ ```bash
38
+ cd triage-agent
39
+ agent-sdk dev
40
+ ```
41
+
42
+ Open the playground URL it prints and type a real report:
43
+
44
+ > Export to CSV has returned 500s since this morning. Request id 41ac.
45
+ > Retrying does not help.
46
+
47
+ Watch the turn. The agent reads the `triage-rubric` skill, calls
48
+ `record_triage` with a structured verdict, and replies in six lines or
49
+ fewer. The tool call renders inline, so you see the exact kind,
50
+ severity, and next step it recorded.
51
+
52
+ Integrations use the same door your playground does. The `intake`
53
+ channel accepts reports over HTTP:
54
+
55
+ ```bash
56
+ curl -X POST http://127.0.0.1:3000/triage-agent/v1/channels/intake/report \
57
+ -H 'content-type: application/json' \
58
+ -d '{"message":"How do I rotate staging API keys?","reporter":"dana"}'
59
+ ```
60
+
61
+ The composer also autocompletes `/report`, which drives this route.
62
+
63
+ ## Make the rubric yours
64
+
65
+ `agent/skills/triage-rubric.md` holds the judgment: what counts as a
66
+ bug, the tie-breakers, and the severity ladder. Edit it first. Your
67
+ team's definition of `urgent` belongs there, in plain markdown, and the
68
+ next turn uses it without a restart step.
69
+
70
+ `agent/instructions.md` owns the flow around the rubric: when to ask a
71
+ clarifying question, the one-call `record_triage` rule, and the reply
72
+ shape.
73
+
74
+ ## Prove it with evals
75
+
76
+ The template ships three smoke cases, one per kind. Run them after
77
+ every rubric edit:
78
+
79
+ ```bash
80
+ agent-sdk eval
81
+ ```
82
+
83
+ Each case sends a realistic report and asserts a verdict was recorded
84
+ with the expected kind. When you tighten the rubric, add a case for the
85
+ report style you tightened it for. The eval suite is the regression
86
+ net for prompt changes, so grow it with the rubric.
87
+
88
+ ## File verdicts into your tracker
89
+
90
+ `agent/tools/record_triage.ts` defines the verdict shape with a zod
91
+ schema and returns it. Point its `execute` at Linear, GitHub Issues, or
92
+ a webhook to file real tickets. Test the tool without a model turn:
93
+
94
+ ```bash
95
+ agent-sdk call record_triage --dir . --input '{
96
+ "kind": "bug",
97
+ "severity": "high",
98
+ "title": "CSV export 500s",
99
+ "summary": "Export returns 500 since this morning.",
100
+ "nextStep": "Check export service logs."
101
+ }'
102
+ ```
103
+
104
+ The call validates against the schema and runs your `execute` body
105
+ in-process, so tracker wiring gets a fast loop.
106
+
107
+ ## Wire it to Slack
108
+
109
+ The `slack` channel idles until its tokens exist, so local work never
110
+ blocks on Slack. When you want it live:
111
+
112
+ ```bash
113
+ agent-sdk slack setup
114
+ ```
115
+
116
+ The guided flow creates the Slack app and writes the token pair to
117
+ `.env.local`. Restart `agent-sdk dev`, invite the bot to a channel, and
118
+ @mention it with a report. The [Slack guide](../guides/slack.md) covers
119
+ channel watching and approvals when you outgrow mentions.
120
+
121
+ ## Deploy it
122
+
123
+ ```bash
124
+ agent-sdk deploy
125
+ ```
126
+
127
+ From your git checkout, `deploy` infers the repo, ref, and slug, builds
128
+ the ref on Cursor-managed hosting, and prints the hosted URL plus a
129
+ one-time alias token. Store the token. Production Slack tokens go in as
130
+ deployment secrets:
131
+
132
+ ```bash
133
+ agent-sdk secrets set SLACK_BOT_TOKEN SLACK_APP_TOKEN --slug triage-agent
134
+ agent-sdk deploy
135
+ ```
136
+
137
+ Check on it with `agent-sdk deployments`. The
138
+ [deployment guide](../deployment.md) covers self-hosting, storage, and
139
+ auth when you need them.
140
+
141
+ ## Where to go next
142
+
143
+ - Add a GitHub intake with the [GitHub guide](../guides/github.md), so
144
+ issue comments triage themselves.
145
+ - Persist verdicts across restarts with [storage](../storage.md).
146
+ - Measure rubric changes on live traffic with
147
+ [A/B metrics](../ab.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor/july",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
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": {
@@ -31,6 +31,7 @@
31
31
  "!src/internal/tenant-mcp/**",
32
32
  "skills/**/*",
33
33
  "!skills/operate/**",
34
+ "templates/**/*",
34
35
  "docs/**/*.md",
35
36
  "!docs/.cursor/**",
36
37
  "package.json",
@@ -177,6 +178,12 @@
177
178
  "import": "./dist/storage-backends/cursor-hosted.js",
178
179
  "default": "./dist/storage-backends/cursor-hosted.js"
179
180
  },
181
+ "./otel": {
182
+ "anysphere-source": "./src/otel.ts",
183
+ "types": "./dist/otel.d.ts",
184
+ "import": "./dist/otel.js",
185
+ "default": "./dist/otel.js"
186
+ },
180
187
  "./package.json": {
181
188
  "anysphere-source": "./package.json",
182
189
  "bun": "./package.json",
@@ -212,6 +219,21 @@
212
219
  "@modelcontextprotocol/sdk": "1.25.1",
213
220
  "@octokit/auth-app": "^7.2.0",
214
221
  "@octokit/rest": "^21.1.1",
222
+ "@opentelemetry/api": "^1.9.0",
223
+ "@opentelemetry/api-logs": "^0.208.0",
224
+ "@opentelemetry/core": "^2.6.1",
225
+ "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
226
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.208.0",
227
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.208.0",
228
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.208.0",
229
+ "@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
230
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.208.0",
231
+ "@opentelemetry/resources": "^2.6.1",
232
+ "@opentelemetry/sdk-logs": "^0.208.0",
233
+ "@opentelemetry/sdk-metrics": "^2.2.0",
234
+ "@opentelemetry/sdk-trace-base": "^2.6.1",
235
+ "@opentelemetry/sdk-trace-node": "^2.6.1",
236
+ "@opentelemetry/semantic-conventions": "^1.27.0",
215
237
  "@slack/socket-mode": "^2.0.7",
216
238
  "@slack/web-api": "^7.18.0",
217
239
  "croner": "^8.0.1",
@@ -16,8 +16,8 @@ channels working
16
16
  with the user, prove a turn runs end-to-end, and set up the improvement loop.
17
17
  Sibling skills: `framework-map` for orientation, `setup-slack` for Slack
18
18
  wiring, `mcp-auth` for host MCP OAuth, `github` for webhook-driven
19
- agents, `evals` for the eval API, `ab` for live A/B metrics, `hillclimb`
20
- for iteration once the agent runs.
19
+ agents, `evals` for the eval API, `ab` for live A/B metrics, `otel` for
20
+ OpenTelemetry export, `hillclimb` for iteration once the agent runs.
21
21
 
22
22
  Read `framework-map/SKILL.md` first if you haven't; treat the package
23
23
  `AGENTS.md` and `README.md` as ground truth for
@@ -10,6 +10,10 @@ description: >-
10
10
 
11
11
  # Debugging the Agent SDK locally
12
12
 
13
+ This skill is **local** `agent-sdk serve` / playground. A **hosted**
14
+ deployment that is stopped, slow to start, or failing `/v1/health` is
15
+ `.cursor/skills/debugging/debug-agent-serve-deployment/SKILL.md`.
16
+
13
17
  Read `framework-map/SKILL.md` (sibling skill) first if you don't know the
14
18
  project structure or session model. Everything below assumes Node/tsx (`pnpm exec tsx
15
19
  src/bin/agent-serve.ts ...` in the everysphere monorepo).
@@ -63,6 +63,7 @@ path live under README "Folder structure".
63
63
  | `agent/hooks/*.ts` | Observe-only event subscribers (never fatal) |
64
64
  | `agent/ab.ts` or `agent/ab/*.ts` | Live A/B metrics (`defineAB`); sticky `split` + `onSample` |
65
65
  | `agent/ab.config.ts` | Optional A/B `maxPlaygroundSessions` / `persistSamples` / `persistSnapshots` |
66
+ | `agent/otel.ts` | OpenTelemetry export (`defineOtel`); also `OTEL_EXPORTER_OTLP_*` env |
66
67
  | `agent/schedules/*` | Cron-driven runs (UTC, 5-field; never auto-fire under `--dev`) |
67
68
  | `agent/sandbox/workspace/` | Files seeded into each session workspace (local runtime only) |
68
69
  | `agent/lib/` | Import-only shared code, never discovered |
@@ -151,6 +152,7 @@ folder mounts every sibling agent too.
151
152
  - Scaffolding → `packages/agent-serve/skills/create-agent/SKILL.md`
152
153
  - Evals → `packages/agent-serve/skills/evals/SKILL.md`
153
154
  - Live A/B metrics → `packages/agent-serve/skills/ab/SKILL.md`
155
+ - OpenTelemetry → `packages/agent-serve/skills/otel/SKILL.md`
154
156
  - GitHub webhooks → `packages/agent-serve/skills/github/SKILL.md`
155
157
  - Slack → `packages/agent-serve/skills/setup-slack/SKILL.md`
156
158
  - Host MCP OAuth → `packages/agent-serve/skills/mcp-auth/SKILL.md`
@@ -0,0 +1,80 @@
1
+ ---
2
+ name: agentkit-otel
3
+ description: >-
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.
7
+ ---
8
+
9
+ # Agent SDK OpenTelemetry (`defineOtel`)
10
+
11
+ Push session, turn, tool, and subagent traces, plus token and cost
12
+ metrics, from the serve process to an OTLP collector. Logs are off
13
+ until you opt in.
14
+
15
+ Read [OpenTelemetry](../../docs/guides/opentelemetry.md) for the full
16
+ guide, including how this joins Cursor enterprise export.
17
+
18
+ ## Enable
19
+
20
+ Any one of:
21
+
22
+ 1. **Env.** Set `OTEL_EXPORTER_OTLP_ENDPOINT` (and optional
23
+ `OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_HEADERS`,
24
+ `OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf|http/json`).
25
+ Per-signal URLs work when the base URL is unset:
26
+ `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` / `_METRICS_ENDPOINT` /
27
+ `_LOGS_ENDPOINT`.
28
+ 2. **Authored.** `agent/otel.ts`:
29
+
30
+ ```ts
31
+ import { defineOtel } from "@cursor/july/otel";
32
+
33
+ export default defineOtel({
34
+ serviceName: "cursor",
35
+ exporters: [
36
+ {
37
+ url: "https://otel.example.com",
38
+ protocol: "http/protobuf",
39
+ headers: { Authorization: "Bearer …" },
40
+ },
41
+ ],
42
+ });
43
+ ```
44
+
45
+ 3. **Programmatic.** `serve(dir, { otel: { … } })`. Pass `otel: false`
46
+ to disable even when env or `agent/otel.ts` are set.
47
+
48
+ Multiple `exporters` fan out. Default protocol is `http/protobuf`.
49
+ Use `http/json` only when the collector requires it. `grpc` is ignored.
50
+
51
+ Precedence: `serve({ otel })` over `agent/otel.ts` over env.
52
+
53
+ ## Signals
54
+
55
+ | Signal | Default | What |
56
+ | --- | --- | --- |
57
+ | Traces | on | `agent_sdk.http` → `agent_sdk.session` → `agent_sdk.turn` → `agent_sdk.tool` / `agent_sdk.subagent` |
58
+ | Metrics | on | `cursor.token.usage`, `cursor.tool.calls`, `cursor.cost.usage`, plus `agent_sdk.*` counts and HTTP duration |
59
+ | Logs | off | Opt in with `logs: true`, `OTEL_LOGS_EXPORTER=otlp`, or `OTEL_LOG_USER_PROMPTS=1` / `OTEL_LOG_TOOL_CONTENT=1` |
60
+
61
+ Prompt text, tool payloads, and failure text stay off the wire unless
62
+ those flags are set.
63
+
64
+ ## Attributes worth joining on
65
+
66
+ - `cursor.conversation.id` (session id)
67
+ - `cursor.tool.name` / `cursor.tool.kind` / `cursor.tool.status`
68
+ - `cursor.token.type`, `cursor.model.name`
69
+ - `agent_sdk.agent`, `agent_sdk.turn_id`, `agent_sdk.call_id`, `agent_sdk.channel`
70
+
71
+ Resource defaults: `service.name=cursor`, `cursor.entrypoint=sdk_ts`,
72
+ `cursor.surface=unspecified`.
73
+
74
+ Inbound HTTP extracts W3C `traceparent`, so a channel request parents
75
+ the session span when the turn starts in that request.
76
+
77
+ If another TracerProvider already owns the process (CLI / IDE), Agent
78
+ SDK reuses it. Run `serve` as its own process when Agent SDK should
79
+ own the destination. Conflicting `agent/otel.ts` files across mounts
80
+ fail at serve start.
@@ -100,7 +100,7 @@ Usage:
100
100
  ${CLI} trajectory --events <file.ndjson> [--text]
101
101
  ${CLI} call <tool> [--input <json>] [--dir <path> | --url <baseUrl> | --prod]
102
102
  [--session <id>] [--slug <slug>] [--team <id>]
103
- ${CLI} init [dir] [--json]
103
+ ${CLI} init [dir] [--template <name>] [--json]
104
104
  ${CLI} convert-automation <url> [--dir <path>] [--json]
105
105
  ${CLI} install-skills [--print] [--json]
106
106
  ${CLI} mcp --prod [--slug <slug>] [--team <id>] [--dir <path>]
@@ -467,6 +467,7 @@ async function main(argv: string[]): Promise<number> {
467
467
  allowPositionals: true,
468
468
  options: {
469
469
  dir: { type: "string", default: "." },
470
+ template: { type: "string" },
470
471
  port: { type: "string" },
471
472
  host: { type: "string" },
472
473
  mode: { type: "string" },
@@ -624,6 +625,7 @@ async function main(argv: string[]): Promise<number> {
624
625
  return cmdInit({
625
626
  dir,
626
627
  initTarget: positionals[0],
628
+ template: values.template,
627
629
  json: values.json,
628
630
  });
629
631
  }
@@ -258,6 +258,28 @@ export async function createGitHubIssueComment(
258
258
  }
259
259
  }
260
260
 
261
+ /** Update an issue or PR timeline comment by id. */
262
+ export async function updateGitHubIssueComment(
263
+ input: GitHubResourceInput & { body: string; commentId: number }
264
+ ): Promise<GitHubPostedComment> {
265
+ const octokit = await createGitHubOctokit(input);
266
+ try {
267
+ const response = await octokit.rest.issues.updateComment({
268
+ owner: input.owner,
269
+ repo: input.repo,
270
+ comment_id: input.commentId,
271
+ body: input.body,
272
+ });
273
+ return toPostedComment(response.data);
274
+ } catch (error) {
275
+ throw wrapOctokitError(
276
+ error,
277
+ "PATCH",
278
+ `/repos/${input.owner}/${input.repo}/issues/comments/${input.commentId}`
279
+ );
280
+ }
281
+ }
282
+
261
283
  /** Reply to an inline pull-request review comment thread. */
262
284
  export async function createGitHubReviewCommentReply(
263
285
  input: GitHubResourceInput & {
@@ -5,14 +5,11 @@
5
5
  import type { ChannelEventHandlers } from "../../types.js";
6
6
  import type { GitHubBindingConfig } from "./binding.js";
7
7
  import { buildGitHubBinding, formatGitHubOutboundComment } from "./binding.js";
8
+ import { applyGitHubProgress, type GitHubProgressConfig } from "./progress.js";
8
9
  import type { GitHubChannelState } from "./state.js";
9
10
 
10
- export interface GitHubProgressConfig {
11
- /** When false, skip the eyes reaction on turn.started. Defaults to true. */
12
- reactions?: boolean;
13
- }
14
-
15
11
  export interface BuildDefaultGitHubEventsOptions extends GitHubBindingConfig {
12
+ botName?: string;
16
13
  progress?: GitHubProgressConfig;
17
14
  /**
18
15
  * When false, skip posting completed assistant messages back to GitHub.
@@ -22,7 +19,8 @@ export interface BuildDefaultGitHubEventsOptions extends GitHubBindingConfig {
22
19
  }
23
20
 
24
21
  /**
25
- * Built-in delivery: eyes reaction on turn start, post assistant text on
22
+ * Built-in delivery: eyes reaction on turn start, optional commit status /
23
+ * sticky banner via {@link GitHubProgressConfig}, post assistant text on
26
24
  * `message.completed`, and a short error comment on turn/session failure.
27
25
  */
28
26
  export function buildDefaultGitHubEvents(
@@ -32,6 +30,11 @@ export function buildDefaultGitHubEvents(
32
30
 
33
31
  return {
34
32
  async "turn.started"(_event, channel) {
33
+ await applyGitHubProgress({
34
+ phase: "pending",
35
+ channel,
36
+ options,
37
+ });
35
38
  if (options.progress?.reactions === false) {
36
39
  return;
37
40
  }
@@ -49,6 +52,14 @@ export function buildDefaultGitHubEvents(
49
52
  }
50
53
  },
51
54
 
55
+ async "turn.completed"(_event, channel) {
56
+ await applyGitHubProgress({
57
+ phase: "success",
58
+ channel,
59
+ options,
60
+ });
61
+ },
62
+
52
63
  async "message.completed"(event, channel) {
53
64
  if (!deliverReplies) {
54
65
  return;
@@ -71,10 +82,20 @@ export function buildDefaultGitHubEvents(
71
82
  },
72
83
 
73
84
  async "turn.failed"(event, channel) {
85
+ await applyGitHubProgress({
86
+ phase: "failure",
87
+ channel,
88
+ options,
89
+ });
74
90
  await postFailure(channel, options, event.data.message);
75
91
  },
76
92
 
77
93
  async "session.failed"(event, channel) {
94
+ await applyGitHubProgress({
95
+ phase: "failure",
96
+ channel,
97
+ options,
98
+ });
78
99
  await postFailure(channel, options, event.data.message);
79
100
  },
80
101
  };
@@ -958,6 +958,9 @@ async function handleReviewComment(input: {
958
958
  binding,
959
959
  deliveryId: ctx.deliveryId,
960
960
  eventName: ctx.eventName,
961
+ headSha: parsed.headSha,
962
+ headRef: parsed.headRef,
963
+ baseRef: parsed.baseRef,
961
964
  }),
962
965
  });
963
966
  }
@@ -1459,6 +1462,8 @@ async function startTurn(input: {
1459
1462
  title: input.dispatch.title ?? defaultGitHubTitle(input.ctx),
1460
1463
  ...(input.cloud === undefined ? {} : { cloud: input.cloud }),
1461
1464
  state: input.state as unknown as JsonValue,
1465
+ // Resume must refresh headSha / refs without wiping sticky banner id.
1466
+ refreshState: input.state as unknown as JsonValue,
1462
1467
  });
1463
1468
  return Response.json({
1464
1469
  ok: true,
@@ -1612,6 +1617,9 @@ function parseReviewCommentEvent(args: {
1612
1617
  action: string;
1613
1618
  ctx: GitHubParsedContext;
1614
1619
  comment: GitHubComment;
1620
+ headSha?: string;
1621
+ headRef?: string;
1622
+ baseRef?: string;
1615
1623
  } | null {
1616
1624
  const schema = z.object({
1617
1625
  action: z.string().min(1),
@@ -1619,6 +1627,17 @@ function parseReviewCommentEvent(args: {
1619
1627
  pull_request: z.object({
1620
1628
  number: z.number().int().positive(),
1621
1629
  html_url: z.string().url(),
1630
+ head: z
1631
+ .object({
1632
+ sha: z.string().min(1).optional(),
1633
+ ref: z.string().min(1).optional(),
1634
+ })
1635
+ .optional(),
1636
+ base: z
1637
+ .object({
1638
+ ref: z.string().min(1).optional(),
1639
+ })
1640
+ .optional(),
1622
1641
  }),
1623
1642
  comment: z.object({
1624
1643
  id: z.number(),
@@ -1679,7 +1698,14 @@ function parseReviewCommentEvent(args: {
1679
1698
  ? {}
1680
1699
  : { inReplyToId: payload.comment.in_reply_to_id }),
1681
1700
  };
1682
- return { action: payload.action, ctx, comment };
1701
+ return {
1702
+ action: payload.action,
1703
+ ctx,
1704
+ comment,
1705
+ headSha: payload.pull_request.head?.sha,
1706
+ headRef: payload.pull_request.head?.ref,
1707
+ baseRef: payload.pull_request.base?.ref,
1708
+ };
1683
1709
  }
1684
1710
 
1685
1711
  function parseIssueEvent(args: {
@@ -47,6 +47,7 @@ export * from "./defaults.js";
47
47
  export * from "./events.js";
48
48
  export * from "./forward.js";
49
49
  export * from "./github-channel.js";
50
+ export * from "./progress.js";
50
51
  export * from "./replay.js";
51
52
  export * from "./state.js";
52
53
  export * from "./types.js";