@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.
- package/AGENTS.md +1 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +13 -5
- package/dist/channels/github/types.d.ts +8 -0
- 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.fHnNXEJ6.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BMT0xM4I.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DoVfutcV.js → VPLocalSearchBox.Cf6ghfFY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DtuVtKkc.js → theme.B_5-VLe8.js} +2 -2
- 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.XRpxctsA.js} +9 -4
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.XRpxctsA.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_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.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 +3 -3
- 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 +3 -3
- 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 +3 -3
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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/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/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 +1 -0
- package/dist/internal/session-engine.d.ts +5 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +23 -5
- package/dist/playground/assets/index-CZH9EFvO.css +1 -0
- package/dist/playground/assets/{index-D-DIYQzw.js → index-Dces8BDp.js} +26 -26
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +81 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +33 -106
- package/docs/guides/opentelemetry.md +30 -0
- package/docs/quickstart.md +89 -227
- package/docs/reference/agent-config.md +52 -0
- package/docs/reference/cli.md +2 -2
- package/docs/reference/tools.md +5 -4
- package/package.json +2 -2
- package/skills/otel/SKILL.md +20 -2
- package/src/channels/github/github-channel.ts +9 -0
- package/src/channels/github/types.ts +4 -0
- package/src/channels.ts +38 -0
- package/src/index.ts +1 -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/host-otel.ts +252 -0
- package/src/internal/init-project.ts +10 -10
- package/src/internal/otel/collector.ts +135 -50
- package/src/internal/sdk-runner.ts +6 -0
- package/src/internal/server.ts +1 -0
- package/src/internal/session-engine.ts +32 -2
- package/src/types.ts +89 -2
- 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_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
|
@@ -27,6 +27,15 @@ const CONTENT_MAX = 2_048;
|
|
|
27
27
|
export interface OtelCollector {
|
|
28
28
|
onEvent(event: SessionEvent, session: SessionInfo): void;
|
|
29
29
|
close(): void;
|
|
30
|
+
/** Merge author tags onto the session; paints open spans immediately. */
|
|
31
|
+
setSessionAttributes(
|
|
32
|
+
sessionId: string,
|
|
33
|
+
attributes: Record<string, AttrValue>
|
|
34
|
+
): void;
|
|
35
|
+
/** Session tags previously set by {@link setSessionAttributes}. */
|
|
36
|
+
getSessionAttributes(
|
|
37
|
+
sessionId: string
|
|
38
|
+
): Record<string, AttrValue> | undefined;
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
interface TurnState {
|
|
@@ -49,6 +58,7 @@ export function createOtelCollector(
|
|
|
49
58
|
log: (line: string) => void
|
|
50
59
|
): OtelCollector {
|
|
51
60
|
const sessions = new Map<string, SessionState>();
|
|
61
|
+
const extraBySession = new Map<string, Record<string, AttrValue>>();
|
|
52
62
|
const metrics = runtime.meter ? buildMetrics(runtime.meter) : undefined;
|
|
53
63
|
const logOpts =
|
|
54
64
|
runtime.config.logs === false ? undefined : runtime.config.logs;
|
|
@@ -71,6 +81,17 @@ export function createOtelCollector(
|
|
|
71
81
|
state.span.end();
|
|
72
82
|
sessions.delete(sessionId);
|
|
73
83
|
}
|
|
84
|
+
extraBySession.clear();
|
|
85
|
+
},
|
|
86
|
+
setSessionAttributes(sessionId, attributes) {
|
|
87
|
+
const bag = extraBySession.get(sessionId) ?? {};
|
|
88
|
+
Object.assign(bag, attributes);
|
|
89
|
+
extraBySession.set(sessionId, bag);
|
|
90
|
+
paintOpenSpans(sessionId, attributes);
|
|
91
|
+
},
|
|
92
|
+
getSessionAttributes(sessionId) {
|
|
93
|
+
const bag = extraBySession.get(sessionId);
|
|
94
|
+
return bag === undefined ? undefined : { ...bag };
|
|
74
95
|
},
|
|
75
96
|
};
|
|
76
97
|
|
|
@@ -215,7 +236,7 @@ export function createOtelCollector(
|
|
|
215
236
|
{
|
|
216
237
|
kind: SpanKind.INTERNAL,
|
|
217
238
|
startTime,
|
|
218
|
-
attributes:
|
|
239
|
+
attributes: join(event.sessionId, {
|
|
219
240
|
"cursor.conversation.id": event.sessionId,
|
|
220
241
|
"cursor.tool.name": call.toolName,
|
|
221
242
|
"cursor.tool.kind": "builtin",
|
|
@@ -287,12 +308,15 @@ export function createOtelCollector(
|
|
|
287
308
|
span.end(startTime);
|
|
288
309
|
turn?.tools.delete(event.data.callId);
|
|
289
310
|
}
|
|
290
|
-
metrics?.toolCalls.add(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
311
|
+
metrics?.toolCalls.add(
|
|
312
|
+
1,
|
|
313
|
+
join(event.sessionId, {
|
|
314
|
+
"cursor.tool.name": event.data.toolName,
|
|
315
|
+
"cursor.tool.kind": "builtin",
|
|
316
|
+
"cursor.tool.status": status,
|
|
317
|
+
"agent_sdk.agent": agentName,
|
|
318
|
+
})
|
|
319
|
+
);
|
|
296
320
|
emitLog(
|
|
297
321
|
"action.result",
|
|
298
322
|
event.data.isError ? SeverityNumber.ERROR : SeverityNumber.INFO,
|
|
@@ -327,7 +351,7 @@ export function createOtelCollector(
|
|
|
327
351
|
{
|
|
328
352
|
kind: SpanKind.INTERNAL,
|
|
329
353
|
startTime,
|
|
330
|
-
attributes:
|
|
354
|
+
attributes: join(event.sessionId, {
|
|
331
355
|
"cursor.conversation.id": event.sessionId,
|
|
332
356
|
"agent_sdk.agent": agentName,
|
|
333
357
|
"agent_sdk.turn_id": event.turnId,
|
|
@@ -343,10 +367,13 @@ export function createOtelCollector(
|
|
|
343
367
|
trace.setSpan(context.active(), turn.span)
|
|
344
368
|
);
|
|
345
369
|
turn.subagents.set(event.data.callId, span);
|
|
346
|
-
metrics?.subagents.add(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
370
|
+
metrics?.subagents.add(
|
|
371
|
+
1,
|
|
372
|
+
join(event.sessionId, {
|
|
373
|
+
"agent_sdk.agent": agentName,
|
|
374
|
+
"agent_sdk.subagent_name": event.data.name ?? "unknown",
|
|
375
|
+
})
|
|
376
|
+
);
|
|
350
377
|
return;
|
|
351
378
|
}
|
|
352
379
|
case "subagent.completed": {
|
|
@@ -389,7 +416,7 @@ export function createOtelCollector(
|
|
|
389
416
|
"agent_sdk.usage.reasoning_tokens": usage.reasoningTokens,
|
|
390
417
|
})
|
|
391
418
|
);
|
|
392
|
-
recordTokens(usage);
|
|
419
|
+
recordTokens(event.sessionId, usage);
|
|
393
420
|
}
|
|
394
421
|
if (cost !== undefined) {
|
|
395
422
|
turn.span.setAttributes(
|
|
@@ -399,18 +426,24 @@ export function createOtelCollector(
|
|
|
399
426
|
"agent_sdk.cost_source": cost.source,
|
|
400
427
|
})
|
|
401
428
|
);
|
|
402
|
-
metrics?.cost.add(
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
429
|
+
metrics?.cost.add(
|
|
430
|
+
cost.totalUsd,
|
|
431
|
+
join(event.sessionId, {
|
|
432
|
+
"cursor.model.name": cost.model,
|
|
433
|
+
"agent_sdk.agent": agentName,
|
|
434
|
+
})
|
|
435
|
+
);
|
|
406
436
|
}
|
|
407
437
|
turn.span.setStatus({ code: SpanStatusCode.OK });
|
|
408
438
|
turn.span.end(startTime);
|
|
409
439
|
sessions.get(event.sessionId)?.turns.delete(event.turnId);
|
|
410
|
-
metrics?.turns.add(
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
440
|
+
metrics?.turns.add(
|
|
441
|
+
1,
|
|
442
|
+
join(event.sessionId, {
|
|
443
|
+
"agent_sdk.agent": agentName,
|
|
444
|
+
"agent_sdk.turn_status": "completed",
|
|
445
|
+
})
|
|
446
|
+
);
|
|
414
447
|
emitLog(
|
|
415
448
|
"turn.completed",
|
|
416
449
|
SeverityNumber.INFO,
|
|
@@ -441,10 +474,13 @@ export function createOtelCollector(
|
|
|
441
474
|
}
|
|
442
475
|
turn.span.end(startTime);
|
|
443
476
|
sessions.get(event.sessionId)?.turns.delete(event.turnId);
|
|
444
|
-
metrics?.turns.add(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
477
|
+
metrics?.turns.add(
|
|
478
|
+
1,
|
|
479
|
+
join(event.sessionId, {
|
|
480
|
+
"agent_sdk.agent": agentName,
|
|
481
|
+
"agent_sdk.turn_status": "failed",
|
|
482
|
+
})
|
|
483
|
+
);
|
|
448
484
|
emitLog(
|
|
449
485
|
"turn.failed",
|
|
450
486
|
SeverityNumber.ERROR,
|
|
@@ -531,7 +567,7 @@ export function createOtelCollector(
|
|
|
531
567
|
{
|
|
532
568
|
kind: SpanKind.INTERNAL,
|
|
533
569
|
startTime: args.startTime,
|
|
534
|
-
attributes:
|
|
570
|
+
attributes: join(args.sessionId, {
|
|
535
571
|
"cursor.conversation.id": args.sessionId,
|
|
536
572
|
"agent_sdk.agent": agentName,
|
|
537
573
|
"agent_sdk.channel": args.channelId,
|
|
@@ -549,10 +585,13 @@ export function createOtelCollector(
|
|
|
549
585
|
};
|
|
550
586
|
sessions.set(args.sessionId, state);
|
|
551
587
|
if (!args.restored) {
|
|
552
|
-
metrics?.sessions.add(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
588
|
+
metrics?.sessions.add(
|
|
589
|
+
1,
|
|
590
|
+
join(args.sessionId, {
|
|
591
|
+
"agent_sdk.agent": agentName,
|
|
592
|
+
"agent_sdk.channel": args.channelId,
|
|
593
|
+
})
|
|
594
|
+
);
|
|
556
595
|
}
|
|
557
596
|
return state;
|
|
558
597
|
}
|
|
@@ -600,7 +639,7 @@ export function createOtelCollector(
|
|
|
600
639
|
{
|
|
601
640
|
kind: SpanKind.INTERNAL,
|
|
602
641
|
startTime: args.startTime,
|
|
603
|
-
attributes:
|
|
642
|
+
attributes: join(args.sessionId, {
|
|
604
643
|
"cursor.conversation.id": args.sessionId,
|
|
605
644
|
"agent_sdk.agent": agentName,
|
|
606
645
|
"agent_sdk.turn_id": args.turnId,
|
|
@@ -666,34 +705,77 @@ export function createOtelCollector(
|
|
|
666
705
|
);
|
|
667
706
|
state.span.end(args.endTime);
|
|
668
707
|
sessions.delete(args.sessionId);
|
|
708
|
+
extraBySession.delete(args.sessionId);
|
|
669
709
|
}
|
|
670
710
|
|
|
671
|
-
function
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
711
|
+
function extra(sessionId: string): Record<string, AttrValue> {
|
|
712
|
+
return extraBySession.get(sessionId) ?? {};
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function join(
|
|
716
|
+
sessionId: string,
|
|
717
|
+
rest: Record<string, AttrValue | undefined>
|
|
718
|
+
): Record<string, AttrValue> {
|
|
719
|
+
return attrs({ ...extra(sessionId), ...rest });
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function paintOpenSpans(
|
|
723
|
+
sessionId: string,
|
|
724
|
+
incoming: Record<string, AttrValue>
|
|
725
|
+
): void {
|
|
726
|
+
const state = sessions.get(sessionId);
|
|
727
|
+
if (state === undefined) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const painted = attrs(incoming);
|
|
731
|
+
if (Object.keys(painted).length === 0) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
state.span.setAttributes(painted);
|
|
735
|
+
for (const turn of state.turns.values()) {
|
|
736
|
+
turn.span.setAttributes(painted);
|
|
737
|
+
for (const span of turn.tools.values()) {
|
|
738
|
+
span.setAttributes(painted);
|
|
739
|
+
}
|
|
740
|
+
for (const span of turn.subagents.values()) {
|
|
741
|
+
span.setAttributes(painted);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function recordTokens(
|
|
747
|
+
sessionId: string,
|
|
748
|
+
usage: {
|
|
749
|
+
inputTokens: number;
|
|
750
|
+
outputTokens: number;
|
|
751
|
+
cacheReadTokens: number;
|
|
752
|
+
cacheWriteTokens: number;
|
|
753
|
+
reasoningTokens?: number;
|
|
754
|
+
}
|
|
755
|
+
): void {
|
|
678
756
|
if (metrics === undefined) {
|
|
679
757
|
return;
|
|
680
758
|
}
|
|
681
759
|
const agent = agentName;
|
|
682
760
|
// Wire token types match enterprise export (`cache_creation`, not write).
|
|
683
|
-
addTokens(usage.inputTokens, "input", agent);
|
|
684
|
-
addTokens(usage.outputTokens, "output", agent);
|
|
685
|
-
addTokens(usage.cacheReadTokens, "cache_read", agent);
|
|
686
|
-
addTokens(usage.cacheWriteTokens, "cache_creation", agent);
|
|
761
|
+
addTokens(sessionId, usage.inputTokens, "input", agent);
|
|
762
|
+
addTokens(sessionId, usage.outputTokens, "output", agent);
|
|
763
|
+
addTokens(sessionId, usage.cacheReadTokens, "cache_read", agent);
|
|
764
|
+
addTokens(sessionId, usage.cacheWriteTokens, "cache_creation", agent);
|
|
687
765
|
if (usage.reasoningTokens !== undefined) {
|
|
688
766
|
// Reasoning is Agent-SDK-only; enterprise wire has no reasoning enum.
|
|
689
|
-
metrics.tokens.add(
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
767
|
+
metrics.tokens.add(
|
|
768
|
+
usage.reasoningTokens,
|
|
769
|
+
join(sessionId, {
|
|
770
|
+
"cursor.token.type": "reasoning",
|
|
771
|
+
"agent_sdk.agent": agent,
|
|
772
|
+
})
|
|
773
|
+
);
|
|
693
774
|
}
|
|
694
775
|
}
|
|
695
776
|
|
|
696
777
|
function addTokens(
|
|
778
|
+
sessionId: string,
|
|
697
779
|
value: number,
|
|
698
780
|
type: "input" | "output" | "cache_read" | "cache_creation",
|
|
699
781
|
agent: string
|
|
@@ -701,10 +783,13 @@ export function createOtelCollector(
|
|
|
701
783
|
if (metrics === undefined || value === 0) {
|
|
702
784
|
return;
|
|
703
785
|
}
|
|
704
|
-
metrics.tokens.add(
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
786
|
+
metrics.tokens.add(
|
|
787
|
+
value,
|
|
788
|
+
join(sessionId, {
|
|
789
|
+
"cursor.token.type": type,
|
|
790
|
+
"agent_sdk.agent": agent,
|
|
791
|
+
})
|
|
792
|
+
);
|
|
708
793
|
}
|
|
709
794
|
|
|
710
795
|
/** Content opt-in for failure text (same gate as tool payloads). */
|
|
@@ -52,6 +52,7 @@ export type RunnerPrewarmRequest = Pick<
|
|
|
52
52
|
| "workspaceDir"
|
|
53
53
|
| "runnerStateDir"
|
|
54
54
|
| "sandbox"
|
|
55
|
+
| "tools"
|
|
55
56
|
| "mcpServers"
|
|
56
57
|
| "subagents"
|
|
57
58
|
| "model"
|
|
@@ -373,6 +374,11 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
373
374
|
this.options.store ?? (await this.getStore(request.runnerStateDir));
|
|
374
375
|
return {
|
|
375
376
|
...this.buildSharedOptions(request),
|
|
377
|
+
// The built-in tool allowlist rides AgentOptions, not `local`, and is
|
|
378
|
+
// deliberately absent from openCloudAgent's options: the SDK rejects
|
|
379
|
+
// tool restrictions on cloud agents. Passed on create *and* resume
|
|
380
|
+
// (both flow through here) — the SDK does not persist it across runs.
|
|
381
|
+
...(request.tools === undefined ? {} : { tools: request.tools }),
|
|
376
382
|
local: {
|
|
377
383
|
cwd: request.workspaceDir,
|
|
378
384
|
settingSources: ["project"],
|
package/src/internal/server.ts
CHANGED
|
@@ -435,6 +435,7 @@ export async function startServer(
|
|
|
435
435
|
logger,
|
|
436
436
|
platforms: hostPlatforms,
|
|
437
437
|
otel: otelCollector,
|
|
438
|
+
otelMeter: otelRuntime?.meter,
|
|
438
439
|
});
|
|
439
440
|
// Rehydrate sessions from the authored storage sink before any
|
|
440
441
|
// channel or route can address them (continuation tokens must resolve).
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import { dirname, join } from "node:path";
|
|
12
12
|
import type { SDKCustomTool, SDKCustomToolResult } from "@cursor/sdk";
|
|
13
|
+
import type { Meter } from "@opentelemetry/api";
|
|
13
14
|
import {
|
|
14
15
|
type ABDefinition,
|
|
15
16
|
mergeABInstructions,
|
|
@@ -95,6 +96,7 @@ import { TurnEventMapper } from "./event-mapper.js";
|
|
|
95
96
|
import { selectFrameworkStorageDefinition } from "./framework-storage-selection.js";
|
|
96
97
|
import { createHostFilesApi } from "./host-files.js";
|
|
97
98
|
import { createHostKvApi } from "./host-kv.js";
|
|
99
|
+
import { createHostOtelApi, type HostOtelSessionApi } from "./host-otel.js";
|
|
98
100
|
import {
|
|
99
101
|
createHostPlatforms,
|
|
100
102
|
createHostSlackClient,
|
|
@@ -246,6 +248,8 @@ export interface SessionEngineOptions {
|
|
|
246
248
|
durableSessionStore?: HostKvApi;
|
|
247
249
|
/** Process-level OTel collector; observe-only, never fatal. */
|
|
248
250
|
otel?: OtelCollector;
|
|
251
|
+
/** Process-level meter for {@link HostContext.otel}. Absent → no-op counters. */
|
|
252
|
+
otelMeter?: Meter;
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
/**
|
|
@@ -362,6 +366,7 @@ export class SessionEngine {
|
|
|
362
366
|
private readonly approvals = new ApprovalGate();
|
|
363
367
|
private readonly abCollector: ABCollector;
|
|
364
368
|
private readonly otel: OtelCollector | undefined;
|
|
369
|
+
private readonly otelHost: HostOtelSessionApi;
|
|
365
370
|
private reminderApi: HostContext["reminders"];
|
|
366
371
|
private evalsApi: HostContext["evals"];
|
|
367
372
|
private readonly kvApi: HostContext["kv"];
|
|
@@ -395,6 +400,11 @@ export class SessionEngine {
|
|
|
395
400
|
this.getCloudUsage = options.getCloudUsage ?? defaultGetCloudUsage;
|
|
396
401
|
this.estimateTurnCost = options.estimateTurnCost ?? estimateTurnCostFromApi;
|
|
397
402
|
this.otel = options.otel;
|
|
403
|
+
this.otelHost = createHostOtelApi({
|
|
404
|
+
agentName: options.project.name,
|
|
405
|
+
meter: options.otelMeter,
|
|
406
|
+
collector: options.otel,
|
|
407
|
+
});
|
|
398
408
|
this.turnGovernor = new TurnGovernor(options.project.agent.concurrency);
|
|
399
409
|
this.sessions = new SessionStore(join(options.stateRoot, "sessions"));
|
|
400
410
|
this.nudgeStore = new SlackNudgeStore(
|
|
@@ -574,7 +584,7 @@ export class SessionEngine {
|
|
|
574
584
|
this.cloudServerToolsConnection = connection;
|
|
575
585
|
}
|
|
576
586
|
|
|
577
|
-
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / Slack nudges). */
|
|
587
|
+
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
|
|
578
588
|
get host(): HostContext {
|
|
579
589
|
return {
|
|
580
590
|
mcp: this.mcpHost,
|
|
@@ -584,6 +594,7 @@ export class SessionEngine {
|
|
|
584
594
|
evals: this.evalsApi,
|
|
585
595
|
kv: this.kvApi,
|
|
586
596
|
files: this.filesApi,
|
|
597
|
+
otel: this.otelHost,
|
|
587
598
|
slackNudges: this.slackNudgeHostApi(),
|
|
588
599
|
};
|
|
589
600
|
}
|
|
@@ -598,7 +609,11 @@ export class SessionEngine {
|
|
|
598
609
|
const host = this.host;
|
|
599
610
|
return sessionId === undefined
|
|
600
611
|
? host
|
|
601
|
-
: {
|
|
612
|
+
: {
|
|
613
|
+
...host,
|
|
614
|
+
files: this.filesApi.forSession(sessionId),
|
|
615
|
+
otel: this.otelHost.forSession(sessionId),
|
|
616
|
+
};
|
|
602
617
|
}
|
|
603
618
|
|
|
604
619
|
/** Unbound `ctx.artifacts` facade (channel handlers, scratch tool calls). */
|
|
@@ -1359,6 +1374,9 @@ export class SessionEngine {
|
|
|
1359
1374
|
workspaceDir,
|
|
1360
1375
|
runnerStateDir: join(this.stateRoot, "runner"),
|
|
1361
1376
|
sandbox: agent.local?.sandbox === true,
|
|
1377
|
+
...(agent.allowedTools === undefined
|
|
1378
|
+
? {}
|
|
1379
|
+
: { tools: agent.allowedTools }),
|
|
1362
1380
|
mcpServers: this.buildMcpServers("local"),
|
|
1363
1381
|
subagents: buildRunnerSubagents(agent),
|
|
1364
1382
|
...(agent.model === undefined ? {} : { model: agent.model }),
|
|
@@ -1614,6 +1632,15 @@ export class SessionEngine {
|
|
|
1614
1632
|
agentRuntime: agent.runtime,
|
|
1615
1633
|
cloudOverride: record.cloudOverride,
|
|
1616
1634
|
});
|
|
1635
|
+
// Fail closed: the SDK cannot enforce the built-in tool allowlist on
|
|
1636
|
+
// cloud agents yet, and a restriction that silently does not apply is
|
|
1637
|
+
// worse than a refused turn. Cloud-runtime agents already fail
|
|
1638
|
+
// discovery; this guards hybrid agents' per-send cloud sessions.
|
|
1639
|
+
if (runtime === "cloud" && agent.allowedTools !== undefined) {
|
|
1640
|
+
throw new Error(
|
|
1641
|
+
`Agent "${agent.name}" declares a built-in tool allowlist (tools), which cannot be enforced on cloud turns yet. Refusing the cloud session rather than running it unrestricted.`
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1617
1644
|
const mergedCloud =
|
|
1618
1645
|
runtime === "cloud"
|
|
1619
1646
|
? mergeCloudOptions({
|
|
@@ -1668,6 +1695,9 @@ export class SessionEngine {
|
|
|
1668
1695
|
: { sdkAgentId: record.sdkAgentId }),
|
|
1669
1696
|
workspaceDir: this.harnessCwd(record),
|
|
1670
1697
|
sandbox: this.project.agent.local?.sandbox === true,
|
|
1698
|
+
...(runtime === "local" && agent.allowedTools !== undefined
|
|
1699
|
+
? { tools: agent.allowedTools }
|
|
1700
|
+
: {}),
|
|
1671
1701
|
runnerStateDir: join(this.stateRoot, "runner"),
|
|
1672
1702
|
prompt: turnPrompt,
|
|
1673
1703
|
isFirstTurn,
|
package/src/types.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* SDK as the execution harness.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type { InteractionUpdate, SDKCustomTool } from "@cursor/sdk";
|
|
11
|
+
import type { InteractionUpdate, SDKCustomTool, ToolName } from "@cursor/sdk";
|
|
12
12
|
import type { z } from "zod";
|
|
13
13
|
import type { ABConfigFile } from "./ab.js";
|
|
14
14
|
import type { OtelConfig, OtelDefinition } from "./otel.js";
|
|
@@ -206,6 +206,40 @@ export interface AgentConfig {
|
|
|
206
206
|
* {@link BuiltinToolsConfig}.
|
|
207
207
|
*/
|
|
208
208
|
builtinTools?: BuiltinToolsConfig;
|
|
209
|
+
/**
|
|
210
|
+
* Allowlist of built-in harness tools offered to the model
|
|
211
|
+
* (`AgentOptions.tools` on the SDK). Opt-in by design: unset keeps the
|
|
212
|
+
* model's full standard toolset, and once set the agent gets exactly
|
|
213
|
+
* what it names — nothing more, `[]` meaning no native built-in tools.
|
|
214
|
+
* There is deliberately no exclusion form: an agent that restricts
|
|
215
|
+
* tools states what it keeps, so tools the platform adds later stay
|
|
216
|
+
* off until someone opts in.
|
|
217
|
+
*
|
|
218
|
+
* `"mcp"` is always granted alongside a configured allowlist. It is
|
|
219
|
+
* transport, not capability: this kit's own server tools reach the
|
|
220
|
+
* model over the SDK custom-tool MCP path, so omitting it would
|
|
221
|
+
* silently strand `agent/tools/`. What MCP actually exposes stays
|
|
222
|
+
* governed by the second layer — the tools you author, the connections
|
|
223
|
+
* you declare, and the harness cwd's ambient `.cursor` config (point
|
|
224
|
+
* `local.cwd` outside checkouts whose MCP servers the agent must not
|
|
225
|
+
* see, and `local.sandbox` makes MCP calls fail closed).
|
|
226
|
+
*
|
|
227
|
+
* Names use the SDK's public tool vocabulary (`"shell"`, `"read"`,
|
|
228
|
+
* `"edit"`, `"grep"`, `"glob"`, `"ls"`, `"task"`, ...). `"shell"` is a
|
|
229
|
+
* capability group that also grants shell stdin writes — and is what
|
|
230
|
+
* `execution: "agent"` tool scripts run through, so an allowlist
|
|
231
|
+
* without it strands them (discovery warns). `"task"` gates subagents.
|
|
232
|
+
* Unknown names fail the turn (`ConfigurationError` at `Agent.create`).
|
|
233
|
+
*
|
|
234
|
+
* The allowlist states runtime-agnostic intent, but the SDK cannot
|
|
235
|
+
* enforce tool restrictions on cloud agents yet, and it must never
|
|
236
|
+
* silently not apply: a `runtime: "cloud"` agent that sets it fails at
|
|
237
|
+
* serve start (error diagnostic), and a hybrid agent's per-send cloud
|
|
238
|
+
* sessions are refused (the turn errors) rather than run unrestricted.
|
|
239
|
+
* Not a security boundary against the serve host: like `local.sandbox`,
|
|
240
|
+
* it only shapes what the model can call.
|
|
241
|
+
*/
|
|
242
|
+
tools?: ToolName[];
|
|
209
243
|
/**
|
|
210
244
|
* How the serve host coordinates event delivery and turn execution. See
|
|
211
245
|
* {@link AgentServingMode}. Defaults to `"resident"`; opt in to
|
|
@@ -506,6 +540,12 @@ export interface HostContext {
|
|
|
506
540
|
* process.
|
|
507
541
|
*/
|
|
508
542
|
files: HostFilesApi;
|
|
543
|
+
/**
|
|
544
|
+
* Author-facing OpenTelemetry metrics and session tags. Always present.
|
|
545
|
+
* Counters and histograms no-op when no meter is running. `setAttributes`
|
|
546
|
+
* still tags the open session when a collector is attached.
|
|
547
|
+
*/
|
|
548
|
+
otel: HostOtelApi;
|
|
509
549
|
}
|
|
510
550
|
|
|
511
551
|
/** Host-facing JSON key-value API attached to {@link HostContext.kv}. */
|
|
@@ -706,6 +746,39 @@ export interface HostFilesApi {
|
|
|
706
746
|
): string | undefined;
|
|
707
747
|
}
|
|
708
748
|
|
|
749
|
+
/** Attribute bag accepted by {@link HostOtelApi}. `undefined` values are dropped. */
|
|
750
|
+
export type HostOtelAttributes = Record<
|
|
751
|
+
string,
|
|
752
|
+
string | number | boolean | undefined
|
|
753
|
+
>;
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Author-facing OpenTelemetry surface on {@link HostContext.otel}.
|
|
757
|
+
*
|
|
758
|
+
* Metrics and tags only — no custom spans, and no raw OTel `Meter` /
|
|
759
|
+
* `Tracer`. First-party names (`cursor.token.usage`, …) and join keys
|
|
760
|
+
* (`cursor.conversation.id`, `agent_sdk.agent`, …) are reserved.
|
|
761
|
+
*/
|
|
762
|
+
export interface HostOtelApi {
|
|
763
|
+
/** True when a meter is running and `increment` / `record` export. */
|
|
764
|
+
readonly enabled: boolean;
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Merge tags onto this host binding. On a session-bound host they join
|
|
768
|
+
* the open session span and later first-party metrics. Unbound tags
|
|
769
|
+
* stay on this host instance only.
|
|
770
|
+
*/
|
|
771
|
+
setAttributes(attributes: HostOtelAttributes): void;
|
|
772
|
+
|
|
773
|
+
/** Add 1 to a counter. */
|
|
774
|
+
increment(name: string, attributes?: HostOtelAttributes): void;
|
|
775
|
+
/** Add `value` to a counter. */
|
|
776
|
+
increment(name: string, value: number, attributes?: HostOtelAttributes): void;
|
|
777
|
+
|
|
778
|
+
/** Record one histogram observation. */
|
|
779
|
+
record(name: string, value: number, attributes?: HostOtelAttributes): void;
|
|
780
|
+
}
|
|
781
|
+
|
|
709
782
|
/** Runtime context handed to a tool's `execute`. */
|
|
710
783
|
export interface ToolContext {
|
|
711
784
|
/** Correlates with `actions.requested` / `action.result` stream events. */
|
|
@@ -1838,7 +1911,12 @@ export interface ChannelMeta {
|
|
|
1838
1911
|
}
|
|
1839
1912
|
|
|
1840
1913
|
export interface ChannelConfig<TState = JsonValue> {
|
|
1841
|
-
/**
|
|
1914
|
+
/**
|
|
1915
|
+
* Route auth chain. Defaults to `[localDevStrict()]`. Include
|
|
1916
|
+
* `publicEndpoint()` to serve this channel WITHOUT the platform alias
|
|
1917
|
+
* token on Cursor hosting (for webhook senders that cannot attach custom
|
|
1918
|
+
* headers — the handler must verify deliveries itself).
|
|
1919
|
+
*/
|
|
1842
1920
|
auth?: AuthPolicy[];
|
|
1843
1921
|
routes?: ChannelRoute[];
|
|
1844
1922
|
/**
|
|
@@ -2347,6 +2425,13 @@ export interface ResolvedAgent {
|
|
|
2347
2425
|
* (`cwd` is absolute — relative paths resolved against the project root).
|
|
2348
2426
|
*/
|
|
2349
2427
|
local?: AgentLocalOptions;
|
|
2428
|
+
/**
|
|
2429
|
+
* Built-in harness tool allowlist from {@link AgentConfig.tools}, with
|
|
2430
|
+
* the "mcp" transport grant injected. Named apart from {@link tools}
|
|
2431
|
+
* (the authored `agent/tools/` list). Applied to local turns; cloud
|
|
2432
|
+
* turns fail closed (cloud-runtime agents already fail discovery).
|
|
2433
|
+
*/
|
|
2434
|
+
allowedTools?: ToolName[];
|
|
2350
2435
|
/** Managed-hosting declarations from {@link AgentConfig.hosting}. */
|
|
2351
2436
|
hosting?: AgentHostingOptions;
|
|
2352
2437
|
/** Validated turn admission limits from {@link AgentConfig.concurrency}. */
|
|
@@ -2503,6 +2588,8 @@ export interface RunnerTurnRequest {
|
|
|
2503
2588
|
workspaceDir: string;
|
|
2504
2589
|
/** Run the local harness sandboxed ({@link AgentLocalOptions.sandbox}). */
|
|
2505
2590
|
sandbox?: boolean;
|
|
2591
|
+
/** Built-in tool allowlist ({@link AgentConfig.tools}, resolved). */
|
|
2592
|
+
tools?: ToolName[];
|
|
2506
2593
|
/** Absolute path for runner-owned durable state (conversation store). */
|
|
2507
2594
|
runnerStateDir: string;
|
|
2508
2595
|
prompt: string;
|