@cursor/july 0.1.43 → 0.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -3
- package/README.md +3 -1
- package/dist/channels/github/api.d.ts +25 -0
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +48 -0
- package/dist/channels/github/cursor-account.d.ts +7 -0
- package/dist/channels/github/cursor-account.d.ts.map +1 -1
- package/dist/channels/github/cursor-account.js +18 -0
- package/dist/channels/github/defaults.d.ts +1 -1
- package/dist/channels/github/defaults.js +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +15 -7
- package/dist/channels/github/progress.d.ts +6 -5
- package/dist/channels/github/progress.d.ts.map +1 -1
- package/dist/channels/github/progress.js +58 -16
- package/dist/channels/github/state.d.ts +11 -0
- package/dist/channels/github/state.d.ts.map +1 -1
- package/dist/channels/github/state.js +1 -1
- package/dist/channels/github/types.d.ts +11 -3
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels.d.ts +17 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +31 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DsTLo2j1.js → app.C2NFTb-h.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.B-FxvuoI.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DoVfutcV.js → VPLocalSearchBox.o1_FJGip.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DtuVtKkc.js → theme.BcGduE7g.js} +2 -2
- package/dist/docs/assets/{guides_github.md.DSg0j9qj.js → guides_github.md.B8QKtIpw.js} +7 -6
- package/dist/docs/assets/{guides_github.md.DSg0j9qj.lean.js → guides_github.md.B8QKtIpw.lean.js} +1 -1
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.js +27 -0
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.lean.js +1 -0
- package/dist/docs/assets/index.md.COeoSkcF.js +5 -0
- package/dist/docs/assets/index.md.COeoSkcF.lean.js +1 -0
- package/dist/docs/assets/{quickstart.md.rIbfahJM.js → quickstart.md.DZxBu44y.js} +18 -49
- package/dist/docs/assets/quickstart.md.DZxBu44y.lean.js +1 -0
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.js → reference_agent-config.md.DHkkvcEZ.js} +9 -4
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.DHkkvcEZ.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DqEqQpGn.js → reference_cli.md.BDmjPPAm.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.4cbRRIIu.js → reference_hooks.md.DJE5DXcT.js} +1 -1
- package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.js} +1 -1
- package/dist/docs/assets/{storage.md.DcGSnIxX.js → storage.md.CJYd5YmN.js} +2 -2
- package/dist/docs/assets/{storage.md.DcGSnIxX.lean.js → storage.md.CJYd5YmN.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +10 -9
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +12 -5
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +8 -28
- package/dist/docs/quickstart.html +24 -55
- package/dist/docs/reference/agent-config.html +11 -6
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +6 -6
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +5 -5
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +5 -5
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +5 -5
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/auth.d.ts +4 -0
- package/dist/internal/auth.d.ts.map +1 -1
- package/dist/internal/auth.js +7 -0
- package/dist/internal/cli-ax.js +2 -2
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -7
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +5 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +46 -9
- package/dist/internal/durable-session-store.d.ts +35 -23
- package/dist/internal/durable-session-store.d.ts.map +1 -1
- package/dist/internal/durable-session-store.js +71 -26
- package/dist/internal/host-otel.d.ts +25 -0
- package/dist/internal/host-otel.d.ts.map +1 -0
- package/dist/internal/host-otel.js +162 -0
- package/dist/internal/init-project.js +10 -10
- package/dist/internal/ledger-serving.d.ts +135 -67
- package/dist/internal/ledger-serving.d.ts.map +1 -1
- package/dist/internal/ledger-serving.js +326 -107
- package/dist/internal/otel/collector.d.ts +5 -0
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +68 -27
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +3 -2
- package/dist/internal/session-engine.d.ts +23 -18
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +83 -32
- package/dist/kv/memory-kv.d.ts +8 -0
- package/dist/kv/memory-kv.d.ts.map +1 -1
- package/dist/kv/memory-kv.js +8 -0
- package/dist/ledger/janitor.d.ts +60 -0
- package/dist/ledger/janitor.d.ts.map +1 -0
- package/dist/ledger/janitor.js +137 -0
- package/dist/ledger/session-ledger.d.ts +132 -43
- package/dist/ledger/session-ledger.d.ts.map +1 -1
- package/dist/ledger/session-ledger.js +282 -50
- package/dist/ledger.d.ts +9 -8
- package/dist/ledger.d.ts.map +1 -1
- package/dist/ledger.js +7 -7
- package/dist/playground/assets/{index-D-DIYQzw.js → index-6wW6SAc4.js} +19 -19
- package/dist/playground/assets/index-CSRYDAFw.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +5 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +6 -0
- package/dist/types.d.ts +100 -10
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +33 -106
- package/docs/guides/github.md +23 -18
- package/docs/guides/opentelemetry.md +30 -0
- package/docs/quickstart.md +89 -227
- package/docs/reference/agent-config.md +47 -0
- package/docs/reference/cli.md +2 -2
- package/docs/reference/hooks.md +1 -1
- package/docs/reference/tools.md +5 -4
- package/docs/storage.md +26 -9
- package/package.json +2 -2
- package/skills/otel/SKILL.md +20 -2
- package/src/channels/github/api.ts +122 -0
- package/src/channels/github/cursor-account.ts +23 -0
- package/src/channels/github/defaults.ts +1 -1
- package/src/channels/github/github-channel.ts +14 -1
- package/src/channels/github/progress.ts +84 -19
- package/src/channels/github/state.ts +14 -0
- package/src/channels/github/types.ts +7 -3
- package/src/channels.ts +38 -0
- package/src/index.ts +2 -0
- package/src/internal/auth.ts +10 -0
- package/src/internal/cli-ax.ts +2 -2
- package/src/internal/cli-deploy.ts +58 -3
- package/src/internal/deploy-client.ts +11 -0
- package/src/internal/discovery.ts +49 -0
- package/src/internal/durable-session-store.ts +66 -27
- package/src/internal/host-otel.ts +252 -0
- package/src/internal/init-project.ts +10 -10
- package/src/internal/ledger-serving.ts +439 -148
- package/src/internal/otel/collector.ts +135 -50
- package/src/internal/sdk-runner.ts +6 -0
- package/src/internal/server.ts +7 -2
- package/src/internal/session-engine.ts +127 -39
- package/src/kv/memory-kv.ts +8 -0
- package/src/ledger/janitor.ts +193 -0
- package/src/ledger/session-ledger.ts +372 -68
- package/src/ledger.ts +21 -6
- package/src/storage-backends/cursor-hosted.ts +17 -2
- package/src/types.ts +108 -10
- package/dist/docs/assets/chunks/@localSearchIndexroot.BZx4pmZL.js +0 -1
- package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.js +0 -20
- package/dist/docs/assets/guides_opentelemetry.md.DmyUwC5R.lean.js +0 -1
- package/dist/docs/assets/index.md.CMc5FP7E.js +0 -25
- package/dist/docs/assets/index.md.CMc5FP7E.lean.js +0 -1
- package/dist/docs/assets/quickstart.md.rIbfahJM.lean.js +0 -1
- package/dist/playground/assets/index-DQTH622f.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.DqEqQpGn.lean.js → reference_cli.md.BDmjPPAm.lean.js} +0 -0
- /package/dist/docs/assets/{reference_hooks.md.4cbRRIIu.lean.js → reference_hooks.md.DJE5DXcT.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
package/docs/storage.md
CHANGED
|
@@ -213,12 +213,29 @@ server).
|
|
|
213
213
|
|
|
214
214
|
Conditional writes are the substrate for the session ledger, which
|
|
215
215
|
ships behind `defineAgent({ serving: "ledger" })`: session identity,
|
|
216
|
-
one-writer-per-session leases with generation fencing,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
216
|
+
one-writer-per-session leases with generation fencing, a fenced commit
|
|
217
|
+
sequence, and a **durable inbox**. A ledger-mode delivery parks its
|
|
218
|
+
event in the session mailbox first — the source ack point moves after
|
|
219
|
+
that write — then tries the lease: if another holder is serving the
|
|
220
|
+
session, the delivery walks away and the holder's commit drains the
|
|
221
|
+
parked event in a follow-up turn under the same lease. Sessions whose
|
|
222
|
+
holder died with events pending are recovered by a janitor loop that
|
|
223
|
+
scans ledgers by prefix, which is why ledger mode requires the full
|
|
224
|
+
`cas` group **including `listKeys`**: the janitor scan and the orphan
|
|
225
|
+
sweep are prefix scans, and mailbox payload blobs go through the
|
|
226
|
+
backend's plain `get`/`put`/`delete`. Coordination runs through the
|
|
227
|
+
agent's storage backend by default or an explicit `durableSessionStore`
|
|
228
|
+
override. Either way you supply the **raw** store: the engine
|
|
229
|
+
namespaces every ledger key under
|
|
230
|
+
`agentkit/v1/{agent}/durable-session/` itself, at the same boundary
|
|
231
|
+
where `host.kv` gets its agent scoping — a self-hoster never writes
|
|
232
|
+
prefixes. A backend that implements `cas` needs no storage migration to
|
|
233
|
+
adopt this.
|
|
234
|
+
|
|
235
|
+
One sharp edge: the `cas` group and the plain methods must be backed by
|
|
236
|
+
the **same durable store**. Pairing a real plain backend with
|
|
237
|
+
`memoryCasTable()` (or any process-local `cas` stand-in) and
|
|
238
|
+
`serving: "ledger"` silently voids ack-after-durable — the mailbox
|
|
239
|
+
blobs persist but the leases, heads, and pending indexes evaporate on
|
|
240
|
+
restart, so acked events are never redelivered. `memoryCasTable()` is
|
|
241
|
+
for test fixtures and inert sinks only.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.46",
|
|
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.
|
|
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",
|
package/skills/otel/SKILL.md
CHANGED
|
@@ -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,
|
|
6
|
-
export,
|
|
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)
|
|
@@ -384,6 +384,128 @@ export async function createGitHubCommitStatus(
|
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
export type GitHubCheckRunStatus = "queued" | "in_progress" | "completed";
|
|
388
|
+
export type GitHubCheckRunConclusion =
|
|
389
|
+
| "action_required"
|
|
390
|
+
| "cancelled"
|
|
391
|
+
| "failure"
|
|
392
|
+
| "neutral"
|
|
393
|
+
| "skipped"
|
|
394
|
+
| "success"
|
|
395
|
+
| "timed_out";
|
|
396
|
+
|
|
397
|
+
export interface GitHubCheckRun {
|
|
398
|
+
htmlUrl: string | undefined;
|
|
399
|
+
id: number;
|
|
400
|
+
raw: unknown;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export interface GitHubCheckRunWriteInput {
|
|
404
|
+
conclusion?: GitHubCheckRunConclusion;
|
|
405
|
+
detailsUrl?: string;
|
|
406
|
+
name: string;
|
|
407
|
+
output?: { summary: string; title: string };
|
|
408
|
+
status: GitHubCheckRunStatus;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/** Create a check run (Checks API). */
|
|
412
|
+
export async function createGitHubCheckRun(
|
|
413
|
+
input: GitHubResourceInput &
|
|
414
|
+
GitHubCheckRunWriteInput & {
|
|
415
|
+
sha: string;
|
|
416
|
+
}
|
|
417
|
+
): Promise<GitHubCheckRun> {
|
|
418
|
+
const octokit = await createGitHubOctokit(input);
|
|
419
|
+
const now = new Date().toISOString();
|
|
420
|
+
try {
|
|
421
|
+
const response = await octokit.rest.checks.create({
|
|
422
|
+
owner: input.owner,
|
|
423
|
+
repo: input.repo,
|
|
424
|
+
name: input.name,
|
|
425
|
+
head_sha: input.sha,
|
|
426
|
+
status: input.status,
|
|
427
|
+
started_at: now,
|
|
428
|
+
...checkRunCompletionFields(input, now),
|
|
429
|
+
...checkRunDetailsUrl(input),
|
|
430
|
+
...checkRunOutput(input),
|
|
431
|
+
});
|
|
432
|
+
return toCheckRun(response.data);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
throw wrapOctokitError(
|
|
435
|
+
error,
|
|
436
|
+
"POST",
|
|
437
|
+
`/repos/${input.owner}/${input.repo}/check-runs`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/** Update a check run (Checks API). */
|
|
443
|
+
export async function updateGitHubCheckRun(
|
|
444
|
+
input: GitHubResourceInput &
|
|
445
|
+
GitHubCheckRunWriteInput & {
|
|
446
|
+
checkRunId: number;
|
|
447
|
+
}
|
|
448
|
+
): Promise<GitHubCheckRun> {
|
|
449
|
+
const octokit = await createGitHubOctokit(input);
|
|
450
|
+
try {
|
|
451
|
+
const response = await octokit.rest.checks.update({
|
|
452
|
+
owner: input.owner,
|
|
453
|
+
repo: input.repo,
|
|
454
|
+
check_run_id: input.checkRunId,
|
|
455
|
+
name: input.name,
|
|
456
|
+
status: input.status,
|
|
457
|
+
...checkRunCompletionFields(input, new Date().toISOString()),
|
|
458
|
+
...checkRunDetailsUrl(input),
|
|
459
|
+
...checkRunOutput(input),
|
|
460
|
+
});
|
|
461
|
+
return toCheckRun(response.data);
|
|
462
|
+
} catch (error) {
|
|
463
|
+
throw wrapOctokitError(
|
|
464
|
+
error,
|
|
465
|
+
"PATCH",
|
|
466
|
+
`/repos/${input.owner}/${input.repo}/check-runs/${input.checkRunId}`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function checkRunCompletionFields(
|
|
472
|
+
input: GitHubCheckRunWriteInput,
|
|
473
|
+
completedAt: string
|
|
474
|
+
): {
|
|
475
|
+
completed_at?: string;
|
|
476
|
+
conclusion?: GitHubCheckRunConclusion;
|
|
477
|
+
} {
|
|
478
|
+
if (input.status !== "completed" || input.conclusion === undefined) {
|
|
479
|
+
return {};
|
|
480
|
+
}
|
|
481
|
+
return { completed_at: completedAt, conclusion: input.conclusion };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function checkRunDetailsUrl(input: GitHubCheckRunWriteInput): {
|
|
485
|
+
details_url?: string;
|
|
486
|
+
} {
|
|
487
|
+
return input.detailsUrl === undefined
|
|
488
|
+
? {}
|
|
489
|
+
: { details_url: input.detailsUrl };
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function checkRunOutput(input: GitHubCheckRunWriteInput): {
|
|
493
|
+
output?: { summary: string; title: string };
|
|
494
|
+
} {
|
|
495
|
+
return input.output === undefined ? {} : { output: input.output };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function toCheckRun(raw: {
|
|
499
|
+
html_url?: string | null;
|
|
500
|
+
id?: number;
|
|
501
|
+
}): GitHubCheckRun {
|
|
502
|
+
return {
|
|
503
|
+
htmlUrl: typeof raw.html_url === "string" ? raw.html_url : undefined,
|
|
504
|
+
id: typeof raw.id === "number" ? raw.id : 0,
|
|
505
|
+
raw,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
387
509
|
function toPostedComment(raw: {
|
|
388
510
|
html_url?: string;
|
|
389
511
|
id?: number;
|
|
@@ -117,6 +117,29 @@ export function parseCursorGithubRepoScope(
|
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Hosted `cursorAccount` mints `checks:write` on the `contents-write` tier.
|
|
122
|
+
* Enabling `progress.commitStatus` opts a `pr-write` (or omitted) channel up
|
|
123
|
+
* to that tier so github-proxy / `/github-token` can post Checks API runs.
|
|
124
|
+
* `"read"` cannot post merge-box checks.
|
|
125
|
+
*/
|
|
126
|
+
export function cursorGithubPermissionsForChannel(
|
|
127
|
+
options: Pick<GitHubChannelOptions, "progress">,
|
|
128
|
+
authored: CursorGithubPermissions | undefined = "pr-write"
|
|
129
|
+
): CursorGithubPermissions {
|
|
130
|
+
const base = authored ?? "pr-write";
|
|
131
|
+
const commitStatus = options.progress?.commitStatus;
|
|
132
|
+
if (commitStatus === undefined || commitStatus === false) {
|
|
133
|
+
return base;
|
|
134
|
+
}
|
|
135
|
+
if (base === "read") {
|
|
136
|
+
throw new Error(
|
|
137
|
+
'progress.commitStatus needs checks:write; cursorAccount permissions: "read" cannot post merge-box checks. Use "pr-write" or "contents-write".'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return "contents-write";
|
|
141
|
+
}
|
|
142
|
+
|
|
120
143
|
/** One host credential: the most privileged requested tier wins. */
|
|
121
144
|
export function resolveCursorGithubPermissions(
|
|
122
145
|
values: readonly CursorGithubPermissions[]
|
|
@@ -19,7 +19,7 @@ export interface BuildDefaultGitHubEventsOptions extends GitHubBindingConfig {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Built-in delivery: eyes reaction on turn start, optional
|
|
22
|
+
* Built-in delivery: eyes reaction on turn start, optional Checks API run /
|
|
23
23
|
* sticky banner via {@link GitHubProgressConfig}, post assistant text on
|
|
24
24
|
* `message.completed`, and a short error comment on turn/session failure.
|
|
25
25
|
*/
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
createCursorAccountGithubTokenResolver,
|
|
29
29
|
createPendingCursorGithubCredentials,
|
|
30
|
+
cursorGithubPermissionsForChannel,
|
|
30
31
|
type PendingCursorGithubCredentials,
|
|
31
32
|
parseCursorGithubRepoScope,
|
|
32
33
|
registerGithubCursorAccountRuntime,
|
|
@@ -137,6 +138,12 @@ const githubPullRequestPayloadSchema: z.ZodType<GitHubPullRequestPayload> =
|
|
|
137
138
|
}),
|
|
138
139
|
sender: senderSchema,
|
|
139
140
|
installation: installationSchema,
|
|
141
|
+
label: z
|
|
142
|
+
.object({
|
|
143
|
+
name: z.string().min(1),
|
|
144
|
+
})
|
|
145
|
+
.nullable()
|
|
146
|
+
.optional(),
|
|
140
147
|
});
|
|
141
148
|
|
|
142
149
|
/**
|
|
@@ -336,6 +343,9 @@ export function parseGitHubPullRequestEvent(args: {
|
|
|
336
343
|
...(payload.pull_request.user?.login === undefined
|
|
337
344
|
? {}
|
|
338
345
|
: { authorLogin: payload.pull_request.user.login }),
|
|
346
|
+
...(payload.label?.name === undefined
|
|
347
|
+
? {}
|
|
348
|
+
: { label: { name: payload.label.name } }),
|
|
339
349
|
};
|
|
340
350
|
return { ctx, pullRequest };
|
|
341
351
|
}
|
|
@@ -599,7 +609,10 @@ export function githubChannel(
|
|
|
599
609
|
typeof options.cursorAccount === "object"
|
|
600
610
|
? parseCursorGithubRepoScope(
|
|
601
611
|
options.cursorAccount.repos,
|
|
602
|
-
|
|
612
|
+
cursorGithubPermissionsForChannel(
|
|
613
|
+
options,
|
|
614
|
+
options.cursorAccount.permissions
|
|
615
|
+
)
|
|
603
616
|
)
|
|
604
617
|
: undefined;
|
|
605
618
|
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Opt-in PR progress surfaces:
|
|
2
|
+
* Opt-in PR progress surfaces: Checks API run + sticky banner comment.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
createGitHubCheckRun,
|
|
7
7
|
createGitHubIssueComment,
|
|
8
8
|
GitHubApiError,
|
|
9
|
+
type GitHubCheckRunWriteInput,
|
|
10
|
+
updateGitHubCheckRun,
|
|
9
11
|
updateGitHubIssueComment,
|
|
10
12
|
} from "./api.js";
|
|
11
13
|
import type { GitHubBindingConfig } from "./binding.js";
|
|
12
14
|
import { formatGitHubOutboundComment } from "./binding.js";
|
|
13
15
|
import type { GitHubChannelState } from "./state.js";
|
|
14
16
|
|
|
15
|
-
/** GitHub
|
|
17
|
+
/** Merge-box check titles stay short; GitHub allows 1024, we clip to 140. */
|
|
16
18
|
const STATUS_DESCRIPTION_MAX = 140;
|
|
17
19
|
|
|
18
20
|
export type GitHubProgressPhase = "pending" | "success" | "failure";
|
|
@@ -24,7 +26,7 @@ export interface GitHubProgressPhaseCopy {
|
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
export interface GitHubCommitStatusProgress extends GitHubProgressPhaseCopy {
|
|
27
|
-
/**
|
|
29
|
+
/** Check run name (unique per check in the merge box). */
|
|
28
30
|
context: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
@@ -34,9 +36,10 @@ export interface GitHubProgressConfig {
|
|
|
34
36
|
/** When false, skip the eyes reaction on turn.started. Defaults to true. */
|
|
35
37
|
reactions?: boolean;
|
|
36
38
|
/**
|
|
37
|
-
* Opt-in
|
|
38
|
-
* behavior (no
|
|
39
|
-
*
|
|
39
|
+
* Opt-in Checks API run on the PR head. Omit or `false` keeps today's
|
|
40
|
+
* behavior (no check from default events). With `cursorAccount`, enabling
|
|
41
|
+
* this opts the minted token up to `permissions: "contents-write"` so the
|
|
42
|
+
* hosted github-proxy lease includes `checks:write`.
|
|
40
43
|
*/
|
|
41
44
|
commitStatus?: false | GitHubCommitStatusProgress;
|
|
42
45
|
/**
|
|
@@ -127,7 +130,7 @@ export async function applyGitHubProgress(input: {
|
|
|
127
130
|
const tasks: Array<Promise<void>> = [];
|
|
128
131
|
if (commitStatus !== null) {
|
|
129
132
|
tasks.push(
|
|
130
|
-
|
|
133
|
+
postProgressCheckRun({
|
|
131
134
|
phase: input.phase,
|
|
132
135
|
channel: input.channel,
|
|
133
136
|
options: input.options,
|
|
@@ -149,7 +152,7 @@ export async function applyGitHubProgress(input: {
|
|
|
149
152
|
await Promise.all(tasks);
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
async function
|
|
155
|
+
async function postProgressCheckRun(input: {
|
|
153
156
|
phase: GitHubProgressPhase;
|
|
154
157
|
channel: { state: GitHubChannelState };
|
|
155
158
|
options: GitHubProgressOptions;
|
|
@@ -164,6 +167,7 @@ async function postCommitStatus(input: {
|
|
|
164
167
|
) {
|
|
165
168
|
return;
|
|
166
169
|
}
|
|
170
|
+
const sha = state.headSha;
|
|
167
171
|
const description = clipStatusDescription(
|
|
168
172
|
progressPhaseCopy({
|
|
169
173
|
copy: input.commitStatus,
|
|
@@ -171,23 +175,84 @@ async function postCommitStatus(input: {
|
|
|
171
175
|
label: progressLabel(input.options, input.commitStatus.context),
|
|
172
176
|
})
|
|
173
177
|
);
|
|
178
|
+
const write: GitHubCheckRunWriteInput = {
|
|
179
|
+
name: input.commitStatus.context,
|
|
180
|
+
...checkRunPhaseFields(input.phase),
|
|
181
|
+
output: { title: description, summary: description },
|
|
182
|
+
};
|
|
183
|
+
const resource = {
|
|
184
|
+
api: input.options.api,
|
|
185
|
+
credentials: input.options.credentials,
|
|
186
|
+
installationId: state.installationId ?? undefined,
|
|
187
|
+
owner: state.owner,
|
|
188
|
+
repo: state.repo,
|
|
189
|
+
};
|
|
190
|
+
const reuseId =
|
|
191
|
+
state.progressCheckRunId !== null && state.progressCheckRunSha === sha
|
|
192
|
+
? state.progressCheckRunId
|
|
193
|
+
: null;
|
|
194
|
+
// Completed runs cannot go back to in_progress. GitHub often 200s and
|
|
195
|
+
// leaves the run completed (sometimes 422). Create a new run so a later
|
|
196
|
+
// turn on the same SHA shows pending again.
|
|
197
|
+
const completedBlocksReuse =
|
|
198
|
+
write.status === "in_progress" && state.progressCheckRunCompleted === true;
|
|
174
199
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
if (reuseId !== null && !completedBlocksReuse) {
|
|
201
|
+
try {
|
|
202
|
+
await updateGitHubCheckRun({
|
|
203
|
+
...resource,
|
|
204
|
+
...write,
|
|
205
|
+
checkRunId: reuseId,
|
|
206
|
+
});
|
|
207
|
+
state.progressCheckRunCompleted = write.status === "completed";
|
|
208
|
+
return;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (!shouldRecreateCheckRun(error, write.status)) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
state.progressCheckRunId = null;
|
|
214
|
+
state.progressCheckRunSha = null;
|
|
215
|
+
state.progressCheckRunCompleted = false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const created = await createGitHubCheckRun({
|
|
219
|
+
...resource,
|
|
220
|
+
...write,
|
|
221
|
+
sha,
|
|
185
222
|
});
|
|
223
|
+
state.progressCheckRunId = created.id;
|
|
224
|
+
state.progressCheckRunSha = sha;
|
|
225
|
+
state.progressCheckRunCompleted = write.status === "completed";
|
|
186
226
|
} catch {
|
|
187
227
|
// Soft-fail: missing token / permission is fine.
|
|
188
228
|
}
|
|
189
229
|
}
|
|
190
230
|
|
|
231
|
+
function shouldRecreateCheckRun(
|
|
232
|
+
error: unknown,
|
|
233
|
+
status: GitHubCheckRunWriteInput["status"]
|
|
234
|
+
): boolean {
|
|
235
|
+
if (!(error instanceof GitHubApiError)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (error.status === 404) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
return error.status === 422 && status === "in_progress";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function checkRunPhaseFields(
|
|
245
|
+
phase: GitHubProgressPhase
|
|
246
|
+
): Pick<GitHubCheckRunWriteInput, "conclusion" | "status"> {
|
|
247
|
+
if (phase === "pending") {
|
|
248
|
+
return { status: "in_progress" };
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
status: "completed",
|
|
252
|
+
conclusion: phase === "success" ? "success" : "failure",
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
191
256
|
async function upsertBanner(input: {
|
|
192
257
|
phase: GitHubProgressPhase;
|
|
193
258
|
channel: { state: GitHubChannelState };
|
|
@@ -13,6 +13,17 @@ export interface GitHubChannelState extends GitHubBindingState {
|
|
|
13
13
|
headRef: string | null;
|
|
14
14
|
/** Sticky progress banner comment id (issue/PR timeline). */
|
|
15
15
|
progressBannerCommentId: number | null;
|
|
16
|
+
/** Latest Checks API run id for `progress.commitStatus`. */
|
|
17
|
+
progressCheckRunId: number | null;
|
|
18
|
+
/** Head SHA the latest check run was created against. */
|
|
19
|
+
progressCheckRunSha: string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Whether that run is already `completed`. `null` means unknown (wake
|
|
22
|
+
* patches omit this so merge keeps the persisted value). GitHub rejects
|
|
23
|
+
* PATCH back to `in_progress`, so a later pending turn on the same SHA
|
|
24
|
+
* must create a new run.
|
|
25
|
+
*/
|
|
26
|
+
progressCheckRunCompleted: boolean | null;
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
export function emptyGitHubChannelState(
|
|
@@ -35,6 +46,9 @@ export function emptyGitHubChannelState(
|
|
|
35
46
|
baseRef: null,
|
|
36
47
|
headRef: null,
|
|
37
48
|
progressBannerCommentId: null,
|
|
49
|
+
progressCheckRunId: null,
|
|
50
|
+
progressCheckRunSha: null,
|
|
51
|
+
progressCheckRunCompleted: null,
|
|
38
52
|
...overrides,
|
|
39
53
|
};
|
|
40
54
|
}
|
|
@@ -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. */
|
|
@@ -448,7 +452,7 @@ export interface GitHubChannelOptions {
|
|
|
448
452
|
* Token capability tier:
|
|
449
453
|
* - `"read"` — PR/issue/status read
|
|
450
454
|
* - `"pr-write"` (default) — comments / PR+issue write, no `contents:write`
|
|
451
|
-
* - `"contents-write"` — push code and
|
|
455
|
+
* - `"contents-write"` — push code and post merge-box check runs (host-held)
|
|
452
456
|
*/
|
|
453
457
|
permissions?: "read" | "pr-write" | "contents-write";
|
|
454
458
|
};
|
|
@@ -500,12 +504,12 @@ export interface GitHubChannelOptions {
|
|
|
500
504
|
onEvent?: GitHubEventHook;
|
|
501
505
|
/**
|
|
502
506
|
* Override / extend default stream-event handlers (eyes reaction, reply
|
|
503
|
-
* posting, opt-in
|
|
507
|
+
* posting, opt-in check run / banner). Handlers you provide replace
|
|
504
508
|
* the matching defaults.
|
|
505
509
|
*/
|
|
506
510
|
events?: ChannelEventHandlers<GitHubChannelState>;
|
|
507
511
|
/**
|
|
508
|
-
* Acknowledgement progress surfaces. Reactions default on;
|
|
512
|
+
* Acknowledgement progress surfaces. Reactions default on; merge-box check
|
|
509
513
|
* and sticky banner are opt-in via `commitStatus` / `banner`.
|
|
510
514
|
*/
|
|
511
515
|
progress?: GitHubProgressConfig;
|
package/src/channels.ts
CHANGED
|
@@ -383,3 +383,41 @@ export function allowAll(): AuthPolicy {
|
|
|
383
383
|
principalType: "user",
|
|
384
384
|
});
|
|
385
385
|
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* `Symbol.for` so the marker survives duplicated copies of the framework in
|
|
389
|
+
* one process (e.g. a monorepo agent resolving its own `@cursor/july`).
|
|
390
|
+
*/
|
|
391
|
+
const PUBLIC_ENDPOINT_MARKER = Symbol.for("agent-serve.public-endpoint");
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Like {@link allowAll}, but ALSO declares the channel publicly reachable on
|
|
395
|
+
* Cursor's managed hosting: `deploy` collects every channel whose auth chain
|
|
396
|
+
* contains this policy and the platform serves that channel's routes
|
|
397
|
+
* (`/v1/channels/<id>/…`) through the stable alias WITHOUT
|
|
398
|
+
* `X-Agent-Alias-Token`.
|
|
399
|
+
*
|
|
400
|
+
* Use it for webhook senders that cannot attach custom headers (Linear,
|
|
401
|
+
* Stripe, …) — **the handler must authenticate deliveries itself**, e.g. by
|
|
402
|
+
* verifying the provider's HMAC signature over the raw body. Everything
|
|
403
|
+
* else (sessions, MCP, playground) stays behind platform auth.
|
|
404
|
+
*
|
|
405
|
+
* Locally this admits every caller, exactly like `allowAll()`.
|
|
406
|
+
*/
|
|
407
|
+
export function publicEndpoint(): AuthPolicy {
|
|
408
|
+
const policy: AuthPolicy = () => ({
|
|
409
|
+
authenticator: "public-endpoint",
|
|
410
|
+
principalId: "anonymous",
|
|
411
|
+
principalType: "user",
|
|
412
|
+
});
|
|
413
|
+
return Object.assign(policy, { [PUBLIC_ENDPOINT_MARKER]: true as const });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** True when a policy was created by {@link publicEndpoint}. */
|
|
417
|
+
export function isPublicEndpointPolicy(policy: AuthPolicy): boolean {
|
|
418
|
+
return (
|
|
419
|
+
(policy as { [PUBLIC_ENDPOINT_MARKER]?: unknown })[
|
|
420
|
+
PUBLIC_ENDPOINT_MARKER
|
|
421
|
+
] === true
|
|
422
|
+
);
|
|
423
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -48,6 +48,7 @@ export {
|
|
|
48
48
|
PATCH,
|
|
49
49
|
POST,
|
|
50
50
|
PUT,
|
|
51
|
+
publicEndpoint,
|
|
51
52
|
} from "./channels.js";
|
|
52
53
|
export { defineConnection } from "./connections.js";
|
|
53
54
|
export { defineHook } from "./hooks.js";
|
|
@@ -93,6 +94,7 @@ export {
|
|
|
93
94
|
} from "./internal/session-engine.js";
|
|
94
95
|
export { createMemoryKv, memoryCasTable } from "./kv/memory-kv.js";
|
|
95
96
|
export {
|
|
97
|
+
createLedgerJanitor,
|
|
96
98
|
LedgerCasExhaustedError,
|
|
97
99
|
LedgerFencedError,
|
|
98
100
|
SessionLedger,
|
package/src/internal/auth.ts
CHANGED
|
@@ -20,6 +20,16 @@ export function isSafeSessionId(sessionId: string): boolean {
|
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** Thrown when a send addresses a session owned by a different principal. */
|
|
24
|
+
export class SessionAuthError extends Error {
|
|
25
|
+
constructor(sessionId: string) {
|
|
26
|
+
super(
|
|
27
|
+
`Session ${sessionId} belongs to a different principal; pass the original caller auth on follow-up`
|
|
28
|
+
);
|
|
29
|
+
this.name = "SessionAuthError";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
23
33
|
/** True when both sides identify the same principal (or both are null). */
|
|
24
34
|
export function samePrincipal(
|
|
25
35
|
a: AuthContext | null,
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -23,7 +23,7 @@ import { lookupSessionContinuation, runChat } from "./chat-client.js";
|
|
|
23
23
|
import { cmdLogin } from "./cli-cursor.js";
|
|
24
24
|
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
25
25
|
import { resolveApiKey } from "./cursor/credentials.js";
|
|
26
|
-
import { CLI_COMMAND_NAME } from "./distribution.js";
|
|
26
|
+
import { CLI_COMMAND_NAME, PACKAGE_NAME } from "./distribution.js";
|
|
27
27
|
import {
|
|
28
28
|
discoverEvals,
|
|
29
29
|
type EvalRunResult,
|
|
@@ -374,7 +374,7 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
374
374
|
const cliOnPath = linked.ok;
|
|
375
375
|
if (!cliOnPath) {
|
|
376
376
|
process.stderr.write(
|
|
377
|
-
`\nCould not put ${CLI_COMMAND_NAME} on PATH (${linked.stderr.trim() || `exit ${linked.exitCode}`}). Next steps
|
|
377
|
+
`\nCould not put ${CLI_COMMAND_NAME} on PATH (${linked.stderr.trim() || `exit ${linked.exitCode}`}). Next steps use npx ${PACKAGE_NAME} instead.\n\n`
|
|
378
378
|
);
|
|
379
379
|
} else {
|
|
380
380
|
process.stdout.write("\n");
|