@cursor/july 0.1.112 → 0.1.113

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 (211) hide show
  1. package/README.md +4 -0
  2. package/dist/bin/agent-serve.js +2 -3
  3. package/dist/channels/checks.d.ts +10 -0
  4. package/dist/channels/checks.d.ts.map +1 -1
  5. package/dist/channels/origin/checks.d.ts +1 -1
  6. package/dist/channels/origin/checks.d.ts.map +1 -1
  7. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  8. package/dist/channels/slack/dispatch.js +6 -2
  9. package/dist/docs/404.html +2 -2
  10. package/dist/docs/assets/{app.DxTdhphC.js → app.CAeK13eM.js} +4 -4
  11. package/dist/docs/assets/chunks/@localSearchIndexroot.Ck9E52Ls.js +1 -0
  12. package/dist/docs/assets/chunks/{VPLocalSearchBox.CR3KTF0X.js → VPLocalSearchBox.C9LbPHod.js} +1 -1
  13. package/dist/docs/assets/chunks/{arc.CVVqBOdS.js → arc.CmMq2zmS.js} +1 -1
  14. package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.CJHGP4ki.js → architectureDiagram-Q4EWVU46.CCXB8Uj5.js} +1 -1
  15. package/dist/docs/assets/chunks/{baseUniq.r7UVVRBP.js → baseUniq.CyQo6eLe.js} +1 -1
  16. package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.DKmMaTre.js → blockDiagram-DXYQGD6D.JYq6w91N.js} +1 -1
  17. package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.DDJsntUO.js → c4Diagram-AHTNJAMY.BRV8GPJJ.js} +1 -1
  18. package/dist/docs/assets/chunks/channel.BHiYmnZ4.js +1 -0
  19. package/dist/docs/assets/chunks/{chunk-4BX2VUAB.BK2rKt6W.js → chunk-4BX2VUAB.Bv4ooYQR.js} +1 -1
  20. package/dist/docs/assets/chunks/{chunk-4TB4RGXK.DRLV8RnF.js → chunk-4TB4RGXK.t4JtKPcj.js} +1 -1
  21. package/dist/docs/assets/chunks/{chunk-55IACEB6.DaKjxtb7.js → chunk-55IACEB6.34lCHj9Y.js} +1 -1
  22. package/dist/docs/assets/chunks/{chunk-EDXVE4YY.C5sPCIT1.js → chunk-EDXVE4YY.BSwrPNrt.js} +1 -1
  23. package/dist/docs/assets/chunks/{chunk-FMBD7UC4.CSGWyNTB.js → chunk-FMBD7UC4.Beeun-R-.js} +1 -1
  24. package/dist/docs/assets/chunks/{chunk-OYMX7WX6.D5tK9XEr.js → chunk-OYMX7WX6.BUUFUcJc.js} +1 -1
  25. package/dist/docs/assets/chunks/{chunk-QZHKN3VN.BeZGd1UZ.js → chunk-QZHKN3VN.B2XjHzN_.js} +1 -1
  26. package/dist/docs/assets/chunks/{chunk-YZCP3GAM.U_tfWwQR.js → chunk-YZCP3GAM.CLYG8znk.js} +1 -1
  27. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.Degh8l90.js +1 -0
  28. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.Degh8l90.js +1 -0
  29. package/dist/docs/assets/chunks/clone.BIywbczV.js +1 -0
  30. package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.DVeRXIb6.js → cose-bilkent-S5V4N54A.DVEa6fZp.js} +1 -1
  31. package/dist/docs/assets/chunks/{dagre-KV5264BT.BpKJAeRZ.js → dagre-KV5264BT.C9PZQK-S.js} +1 -1
  32. package/dist/docs/assets/chunks/{diagram-5BDNPKRD.BQOtrd1Z.js → diagram-5BDNPKRD.DoN0uv3Y.js} +1 -1
  33. package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.CSDAhjPI.js → diagram-G4DWMVQ6.Czv3duqx.js} +1 -1
  34. package/dist/docs/assets/chunks/{diagram-MMDJMWI5.Dpztst2S.js → diagram-MMDJMWI5.BinJ5kWb.js} +1 -1
  35. package/dist/docs/assets/chunks/{diagram-TYMM5635.qJHRizHR.js → diagram-TYMM5635.DW326M4K.js} +1 -1
  36. package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.vbDotH3l.js → erDiagram-SMLLAGMA.U2pR_OA7.js} +1 -1
  37. package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.CqS_ZQr4.js → flowDiagram-DWJPFMVM.ByWJXeYK.js} +1 -1
  38. package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.DTLdR4pN.js → ganttDiagram-T4ZO3ILL.OquF0Rtg.js} +1 -1
  39. package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.D04lnbnr.js → gitGraphDiagram-UUTBAWPF.Bpn01P7X.js} +1 -1
  40. package/dist/docs/assets/chunks/{graph.BlfqLJsM.js → graph.CNRB6ETL.js} +1 -1
  41. package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.tAooImWA.js → infoDiagram-42DDH7IO.CqhknMWi.js} +1 -1
  42. package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.ClUsVqnJ.js → ishikawaDiagram-UXIWVN3A.C6xpR2af.js} +1 -1
  43. package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.C3tUgyCg.js → journeyDiagram-VCZTEJTY.Cg5f7oB3.js} +1 -1
  44. package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.CtD9-QCe.js → kanban-definition-6JOO6SKY.Cx9YTwlU.js} +1 -1
  45. package/dist/docs/assets/chunks/{layout.D38U-LnT.js → layout.ljS-wFtK.js} +1 -1
  46. package/dist/docs/assets/chunks/{linear.BJmssyhN.js → linear.jSxNrsFC.js} +1 -1
  47. package/dist/docs/assets/chunks/{min.DNgXoouU.js → min.Cum8AlQw.js} +1 -1
  48. package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.Dcp6cxeu.js → mindmap-definition-QFDTVHPH.BLiysLpe.js} +1 -1
  49. package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.CLDw6zIs.js → pieDiagram-DEJITSTG.BoIDyuKF.js} +1 -1
  50. package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.CYaeeY4c.js → quadrantDiagram-34T5L4WZ.DLkpDytR.js} +1 -1
  51. package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.gMYuRpq2.js → requirementDiagram-MS252O5E.DqTVqSu2.js} +1 -1
  52. package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.CZqyHFbc.js → sankeyDiagram-XADWPNL6.CG_6FF7j.js} +1 -1
  53. package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.BTsCjUDN.js → sequenceDiagram-FGHM5R23.BIp9602K.js} +1 -1
  54. package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.CftT9mLJ.js → stateDiagram-FHFEXIEX.COSXsD9I.js} +1 -1
  55. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.qrxrbFsX.js +1 -0
  56. package/dist/docs/assets/chunks/{theme.B_7J9ZsV.js → theme.CXJ7PNwy.js} +2 -2
  57. package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.DbU3WUNw.js → timeline-definition-GMOUNBTQ.CXdVqkLq.js} +1 -1
  58. package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.ixsq-q2u.js → vennDiagram-DHZGUBPP.CZxGuc4r.js} +1 -1
  59. package/dist/docs/assets/chunks/wardley-RL74JXVD.3oVgfqQk.js +162 -0
  60. package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.C0ewvgbp.js → wardleyDiagram-NUSXRM2D.6_irCgGJ.js} +1 -1
  61. package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.nAEhF4bO.js → xychartDiagram-5P7HB3ND.TRPe92m3.js} +1 -1
  62. package/dist/docs/assets/{evals.md.BYvfZ-PO.js → evals.md.D3Y3Aixt.js} +2 -2
  63. package/dist/docs/assets/{evals.md.BYvfZ-PO.lean.js → evals.md.D3Y3Aixt.lean.js} +1 -1
  64. package/dist/docs/assets/guides_agent-to-agent.md.CD4T5FIl.js +41 -0
  65. package/dist/docs/assets/guides_agent-to-agent.md.CD4T5FIl.lean.js +1 -0
  66. package/dist/docs/assets/guides_jev.md.F5fAkkfN.js +189 -0
  67. package/dist/docs/assets/guides_jev.md.F5fAkkfN.lean.js +1 -0
  68. package/dist/docs/assets/{reference_connections.md.CmyrlXfY.js → reference_connections.md.Je9dMsdd.js} +1 -1
  69. package/dist/docs/assets/{reference_extensions.md.Ceq-qT8d.js → reference_extensions.md.Cv5aLCz_.js} +1 -1
  70. package/dist/docs/assets/{reference_subagents.md.BHsSMMyO.js → reference_subagents.md.Dl16gcBj.js} +2 -2
  71. package/dist/docs/assets/{reference_subagents.md.BHsSMMyO.lean.js → reference_subagents.md.Dl16gcBj.lean.js} +1 -1
  72. package/dist/docs/assets/{reference_tools.md.BYzUTeVA.js → reference_tools.md.B1dH1lpa.js} +2 -2
  73. package/dist/docs/assets/{reference_tools.md.BYzUTeVA.lean.js → reference_tools.md.B1dH1lpa.lean.js} +1 -1
  74. package/dist/docs/building-with-agents.html +35 -35
  75. package/dist/docs/deployment.html +36 -36
  76. package/dist/docs/evals.html +36 -36
  77. package/dist/docs/evals.md +3 -0
  78. package/dist/docs/guides/agent-to-agent.html +65 -54
  79. package/dist/docs/guides/agent-to-agent.md +73 -68
  80. package/dist/docs/guides/bitbucket.html +35 -35
  81. package/dist/docs/guides/cloud-agents.html +35 -35
  82. package/dist/docs/guides/convert-automation.html +35 -35
  83. package/dist/docs/guides/github.html +35 -35
  84. package/dist/docs/guides/gitlab.html +35 -35
  85. package/dist/docs/guides/grokbot-agents.html +35 -35
  86. package/dist/docs/guides/improve.html +36 -36
  87. package/dist/docs/guides/jev.html +248 -0
  88. package/dist/docs/guides/jev.md +348 -0
  89. package/dist/docs/guides/mcp-oauth.html +36 -36
  90. package/dist/docs/guides/opentelemetry.html +35 -35
  91. package/dist/docs/guides/slack.html +35 -35
  92. package/dist/docs/guides/webhooks.html +35 -35
  93. package/dist/docs/hashmap.json +1 -1
  94. package/dist/docs/hillclimbing.html +35 -35
  95. package/dist/docs/index.html +35 -35
  96. package/dist/docs/llms-full.txt +434 -68
  97. package/dist/docs/llms.txt +2 -1
  98. package/dist/docs/quickstart.html +35 -35
  99. package/dist/docs/reference/agent-config.html +35 -35
  100. package/dist/docs/reference/artifacts.html +35 -35
  101. package/dist/docs/reference/channels.html +35 -35
  102. package/dist/docs/reference/cli.html +35 -35
  103. package/dist/docs/reference/connections.html +37 -37
  104. package/dist/docs/reference/connections.md +1 -1
  105. package/dist/docs/reference/evals.html +35 -35
  106. package/dist/docs/reference/extensions.html +37 -37
  107. package/dist/docs/reference/extensions.md +1 -0
  108. package/dist/docs/reference/hooks.html +35 -35
  109. package/dist/docs/reference/http-api.html +35 -35
  110. package/dist/docs/reference/instructions.html +35 -35
  111. package/dist/docs/reference/playground.html +35 -35
  112. package/dist/docs/reference/project-layout.html +35 -35
  113. package/dist/docs/reference/prompt.html +35 -35
  114. package/dist/docs/reference/schedules.html +35 -35
  115. package/dist/docs/reference/sessions.html +35 -35
  116. package/dist/docs/reference/skills.html +35 -35
  117. package/dist/docs/reference/subagents.html +37 -37
  118. package/dist/docs/reference/subagents.md +1 -1
  119. package/dist/docs/reference/tools.html +37 -37
  120. package/dist/docs/reference/tools.md +4 -0
  121. package/dist/docs/templates/agentic-owners.html +35 -35
  122. package/dist/docs/templates/pr-autofixer.html +35 -35
  123. package/dist/docs/templates/security-reviewer.html +35 -35
  124. package/dist/docs/templates/thermo-quality-review.html +35 -35
  125. package/dist/docs/templates/thermo-review.html +35 -35
  126. package/dist/docs/templates/triage.html +35 -35
  127. package/dist/docs/troubleshooting.html +35 -35
  128. package/dist/extensions/jev/extension.d.ts +43 -0
  129. package/dist/extensions/jev/extension.d.ts.map +1 -0
  130. package/dist/extensions/jev/extension.js +47 -0
  131. package/dist/extensions/jev/lib/evaluate.d.ts +101 -0
  132. package/dist/extensions/jev/lib/evaluate.d.ts.map +1 -0
  133. package/dist/extensions/jev/lib/evaluate.js +167 -0
  134. package/dist/extensions/jev/skills/gated-write.md +25 -0
  135. package/dist/extensions/jev/skills/questions.md +33 -0
  136. package/dist/extensions/jev/tools/evaluate.d.ts +4 -0
  137. package/dist/extensions/jev/tools/evaluate.d.ts.map +1 -0
  138. package/dist/extensions/jev/tools/evaluate.js +88 -0
  139. package/dist/extensions.d.ts +1 -1
  140. package/dist/extensions.d.ts.map +1 -1
  141. package/dist/extensions.js +2 -0
  142. package/dist/internal/advertise-tools.d.ts.map +1 -1
  143. package/dist/internal/advertise-tools.js +6 -0
  144. package/dist/internal/discovery/connections.d.ts.map +1 -1
  145. package/dist/internal/discovery/connections.js +18 -0
  146. package/dist/internal/discovery/extensions.d.ts.map +1 -1
  147. package/dist/internal/discovery/extensions.js +8 -4
  148. package/dist/internal/discovery/info.d.ts.map +1 -1
  149. package/dist/internal/discovery/info.js +1 -0
  150. package/dist/internal/hosted-delivery-protocol.d.ts +3 -0
  151. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  152. package/dist/internal/hosted-delivery-protocol.js +1 -0
  153. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  154. package/dist/internal/hosted-delivery.js +15 -25
  155. package/dist/internal/hosted-execution-diag.d.ts +12 -4
  156. package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
  157. package/dist/internal/hosted-execution-diag.js +26 -4
  158. package/dist/internal/hosted-execution-flush.d.ts +1 -0
  159. package/dist/internal/hosted-execution-flush.d.ts.map +1 -1
  160. package/dist/internal/hosted-execution-flush.js +4 -2
  161. package/dist/internal/server.d.ts.map +1 -1
  162. package/dist/internal/server.js +17 -9
  163. package/dist/internal/session-engine.d.ts +4 -1
  164. package/dist/internal/session-engine.d.ts.map +1 -1
  165. package/dist/internal/session-engine.js +28 -4
  166. package/dist/playground/assets/index-DSMAewbx.css +1 -0
  167. package/dist/playground/assets/{index-B1c1LeIf.js → index-De_lpFxE.js} +43 -43
  168. package/dist/playground/index.html +2 -2
  169. package/dist/types.d.ts +23 -3
  170. package/dist/types.d.ts.map +1 -1
  171. package/docs/evals.md +3 -0
  172. package/docs/guides/agent-to-agent.md +74 -69
  173. package/docs/guides/jev.md +353 -0
  174. package/docs/reference/connections.md +1 -1
  175. package/docs/reference/extensions.md +1 -0
  176. package/docs/reference/subagents.md +1 -1
  177. package/docs/reference/tools.md +4 -0
  178. package/package.json +8 -1
  179. package/src/bin/agent-serve.ts +2 -3
  180. package/src/channels/checks.ts +8 -0
  181. package/src/channels/origin/checks.ts +3 -1
  182. package/src/channels/slack/dispatch.ts +7 -2
  183. package/src/extensions/jev/extension.ts +95 -0
  184. package/src/extensions/jev/lib/evaluate.ts +289 -0
  185. package/src/extensions/jev/skills/gated-write.md +25 -0
  186. package/src/extensions/jev/skills/questions.md +33 -0
  187. package/src/extensions/jev/tools/evaluate.ts +90 -0
  188. package/src/extensions.ts +2 -0
  189. package/src/internal/advertise-tools.ts +6 -0
  190. package/src/internal/discovery/connections.ts +21 -0
  191. package/src/internal/discovery/extensions.ts +12 -4
  192. package/src/internal/discovery/info.ts +1 -0
  193. package/src/internal/hosted-delivery-protocol.ts +4 -0
  194. package/src/internal/hosted-delivery.ts +15 -0
  195. package/src/internal/hosted-execution-diag.ts +33 -4
  196. package/src/internal/hosted-execution-flush.ts +4 -0
  197. package/src/internal/server.ts +26 -12
  198. package/src/internal/session-engine.ts +30 -4
  199. package/src/types.ts +24 -3
  200. package/dist/docs/assets/chunks/@localSearchIndexroot.QmjDU6Jh.js +0 -1
  201. package/dist/docs/assets/chunks/channel.BjpoSbz_.js +0 -1
  202. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.BgxOlMHw.js +0 -1
  203. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.BgxOlMHw.js +0 -1
  204. package/dist/docs/assets/chunks/clone.DRuGBKZC.js +0 -1
  205. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.-43J68xB.js +0 -1
  206. package/dist/docs/assets/chunks/wardley-RL74JXVD.WRXz-Dux.js +0 -162
  207. package/dist/docs/assets/guides_agent-to-agent.md.8oDTfu-E.js +0 -30
  208. package/dist/docs/assets/guides_agent-to-agent.md.8oDTfu-E.lean.js +0 -1
  209. package/dist/playground/assets/index-CK2LX3iD.css +0 -1
  210. /package/dist/docs/assets/{reference_connections.md.CmyrlXfY.lean.js → reference_connections.md.Je9dMsdd.lean.js} +0 -0
  211. /package/dist/docs/assets/{reference_extensions.md.Ceq-qT8d.lean.js → reference_extensions.md.Cv5aLCz_.lean.js} +0 -0
@@ -0,0 +1,289 @@
1
+ /**
2
+ * System One decisions for Agent SDK host code.
3
+ *
4
+ * Chat models write. {@link evaluate} asks Jev for typed choice / score /
5
+ * boolean answers over shared state. Call it from a project tool, a
6
+ * router, or other host code after mounting `@cursor/july/extensions/jev`.
7
+ * Do not set Jev as the `defineAgent` turn model.
8
+ *
9
+ * ```ts
10
+ * import { evaluate, decide, above, needsHuman } from "@cursor/july/extensions/jev";
11
+ *
12
+ * const { answers } = await evaluate({
13
+ * state: draft,
14
+ * questions: {
15
+ * route: {
16
+ * type: "choice",
17
+ * instructions: "Which queue should handle this?",
18
+ * criteria: {
19
+ * billing: "charges, invoices, refunds",
20
+ * shipping: "delivery",
21
+ * technical: "bugs and outages",
22
+ * },
23
+ * },
24
+ * post: {
25
+ * type: "boolean",
26
+ * instructions: "Is this safe to post publicly?",
27
+ * },
28
+ * },
29
+ * });
30
+ *
31
+ * if (needsHuman(answers.route) || !above(answers.post, 0.85)) {
32
+ * return { action: "review" };
33
+ * }
34
+ * ```
35
+ *
36
+ * Auth is `TYPESAFE_API_KEY`. `model` is a TypeSafe id (`jev-latest` by
37
+ * default).
38
+ */
39
+
40
+ /** TypeSafe model used when {@link EvaluateArgs.model} is omitted. */
41
+ export const DEFAULT_EVALUATION_MODEL = "jev-latest";
42
+
43
+ /** Default bar for {@link needsHuman} when the caller does not pass one. */
44
+ export const DEFAULT_HUMAN_THRESHOLD = 0.7;
45
+
46
+ const DEFAULT_BASE_URL = "https://api.typesafe.ai/v1";
47
+
48
+ export type EvaluationState =
49
+ | string
50
+ | Record<string, unknown>
51
+ | readonly unknown[];
52
+
53
+ export type BooleanQuestion = {
54
+ type: "boolean";
55
+ instructions: string;
56
+ };
57
+
58
+ export type ChoiceCriterion =
59
+ | string
60
+ | readonly string[]
61
+ | { includes: readonly string[] }
62
+ | null;
63
+
64
+ export type ChoiceQuestion<
65
+ C extends Record<string, ChoiceCriterion> = Record<string, ChoiceCriterion>,
66
+ > = {
67
+ type: "choice";
68
+ instructions: string;
69
+ criteria: C;
70
+ };
71
+
72
+ export type ScoreQuestion = {
73
+ type: "score";
74
+ instructions: string;
75
+ criteria: readonly string[];
76
+ };
77
+
78
+ export type EvaluationQuestion =
79
+ | BooleanQuestion
80
+ | ChoiceQuestion
81
+ | ScoreQuestion;
82
+
83
+ export type QuestionMap = Record<string, EvaluationQuestion>;
84
+
85
+ export type BooleanAnswer = {
86
+ type: "boolean";
87
+ probability: number;
88
+ };
89
+
90
+ export type ChoiceAnswer<
91
+ C extends Record<string, unknown> = Record<string, unknown>,
92
+ > = {
93
+ type: "choice";
94
+ choice: keyof C & string;
95
+ probabilities: { [K in keyof C]: number };
96
+ };
97
+
98
+ export type ScoreAnswer = {
99
+ type: "score";
100
+ score: number;
101
+ probabilities: Record<string, number>;
102
+ };
103
+
104
+ export type EvaluationAnswer = BooleanAnswer | ChoiceAnswer | ScoreAnswer;
105
+
106
+ export type AnswerFor<Q> = Q extends BooleanQuestion
107
+ ? BooleanAnswer
108
+ : Q extends ChoiceQuestion<infer C>
109
+ ? ChoiceAnswer<C>
110
+ : Q extends ScoreQuestion
111
+ ? ScoreAnswer
112
+ : never;
113
+
114
+ export type AnswersFor<Q extends QuestionMap> = {
115
+ [K in keyof Q]: AnswerFor<Q[K]>;
116
+ };
117
+
118
+ export type EvaluateResult<Q extends QuestionMap = QuestionMap> = {
119
+ answers: AnswersFor<Q>;
120
+ };
121
+
122
+ export type EvaluateArgs<Q extends QuestionMap = QuestionMap> = {
123
+ model?: string;
124
+ state: EvaluationState;
125
+ questions: Q;
126
+ apiKey?: string;
127
+ baseUrl?: string;
128
+ fetch?: typeof globalThis.fetch;
129
+ };
130
+
131
+ type WireQuestion =
132
+ | { type: "noul"; instructions: string }
133
+ | {
134
+ type: "choice";
135
+ instructions: string;
136
+ criteria: ChoiceQuestion["criteria"];
137
+ }
138
+ | { type: "score"; instructions: string; criteria: readonly string[] };
139
+
140
+ function resolveApiKey(explicit: string | undefined): string {
141
+ const key = (explicit ?? process.env.TYPESAFE_API_KEY)?.trim();
142
+ if (key === undefined || key === "") {
143
+ throw new Error("evaluate requires TYPESAFE_API_KEY");
144
+ }
145
+ return key;
146
+ }
147
+
148
+ function toWireQuestion(question: EvaluationQuestion): WireQuestion {
149
+ if (question.type === "boolean") {
150
+ return { type: "noul", instructions: question.instructions };
151
+ }
152
+ return question;
153
+ }
154
+
155
+ function asRecord(value: unknown, label: string): Record<string, unknown> {
156
+ if (value === null || typeof value !== "object") {
157
+ throw new Error(`evaluate ${label}`);
158
+ }
159
+ return value as Record<string, unknown>;
160
+ }
161
+
162
+ function decodeAnswer(
163
+ question: EvaluationQuestion,
164
+ raw: unknown
165
+ ): EvaluationAnswer {
166
+ const value = asRecord(raw, "answer is missing");
167
+ if (question.type === "boolean") {
168
+ if (typeof value.noul !== "number") {
169
+ throw new Error("evaluate noul is not a number");
170
+ }
171
+ return { type: "boolean", probability: value.noul };
172
+ }
173
+ if (question.type === "choice") {
174
+ if (typeof value.choice !== "string") {
175
+ throw new Error("evaluate choice is not a string");
176
+ }
177
+ return {
178
+ type: "choice",
179
+ choice: value.choice,
180
+ probabilities: asRecord(
181
+ value.probabilities,
182
+ "choice probabilities are missing"
183
+ ) as ChoiceAnswer["probabilities"],
184
+ };
185
+ }
186
+ if (typeof value.score !== "number") {
187
+ throw new Error("evaluate score is not a number");
188
+ }
189
+ return {
190
+ type: "score",
191
+ score: value.score,
192
+ probabilities: asRecord(
193
+ value.probabilities,
194
+ "score probabilities are missing"
195
+ ) as ScoreAnswer["probabilities"],
196
+ };
197
+ }
198
+
199
+ async function postSystemOne(args: {
200
+ url: string;
201
+ apiKey: string;
202
+ body: {
203
+ model: string;
204
+ state: EvaluationState;
205
+ questions: Record<string, WireQuestion>;
206
+ };
207
+ fetch: typeof globalThis.fetch;
208
+ }): Promise<Record<string, unknown>> {
209
+ const response = await args.fetch(args.url, {
210
+ method: "POST",
211
+ headers: {
212
+ Authorization: `Bearer ${args.apiKey}`,
213
+ "Content-Type": "application/json",
214
+ },
215
+ body: JSON.stringify(args.body),
216
+ });
217
+ const text = await response.text();
218
+ let parsed: unknown;
219
+ try {
220
+ parsed = text.length === 0 ? {} : JSON.parse(text);
221
+ } catch {
222
+ throw new Error(`evaluate returned non-JSON (${response.status})`);
223
+ }
224
+ if (!response.ok) {
225
+ throw new Error(
226
+ `evaluate failed (${response.status}): ${text.slice(0, 240)}`
227
+ );
228
+ }
229
+ return asRecord(
230
+ asRecord(parsed, "response is not an object").answers,
231
+ "response is missing answers"
232
+ );
233
+ }
234
+
235
+ /** Ask Jev for typed answers over one state. */
236
+ export async function evaluate<const Q extends QuestionMap>(
237
+ args: EvaluateArgs<Q>
238
+ ): Promise<EvaluateResult<Q>> {
239
+ const questions: Record<string, WireQuestion> = {};
240
+ for (const [id, question] of Object.entries(args.questions)) {
241
+ questions[id] = toWireQuestion(question);
242
+ }
243
+ const raw = await postSystemOne({
244
+ url: `${(args.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/u, "")}/systemone`,
245
+ apiKey: resolveApiKey(args.apiKey),
246
+ fetch: args.fetch ?? globalThis.fetch,
247
+ body: {
248
+ model: args.model ?? DEFAULT_EVALUATION_MODEL,
249
+ state: args.state,
250
+ questions,
251
+ },
252
+ });
253
+ const answers = {} as AnswersFor<Q>;
254
+ for (const id of Object.keys(args.questions) as (keyof Q & string)[]) {
255
+ answers[id] = decodeAnswer(
256
+ args.questions[id],
257
+ raw[id]
258
+ ) as AnswersFor<Q>[typeof id];
259
+ }
260
+ return { answers };
261
+ }
262
+
263
+ /** Same as {@link evaluate}, returning only `answers`. */
264
+ export async function decide<const Q extends QuestionMap>(
265
+ args: EvaluateArgs<Q>
266
+ ): Promise<AnswersFor<Q>> {
267
+ const result = await evaluate(args);
268
+ return result.answers;
269
+ }
270
+
271
+ export function above(
272
+ answer: BooleanAnswer | ScoreAnswer,
273
+ threshold: number
274
+ ): boolean {
275
+ if (answer.type === "boolean") {
276
+ return answer.probability >= threshold;
277
+ }
278
+ return answer.score >= threshold;
279
+ }
280
+
281
+ export function needsHuman(
282
+ answer: BooleanAnswer | ChoiceAnswer,
283
+ threshold: number = DEFAULT_HUMAN_THRESHOLD
284
+ ): boolean {
285
+ if (answer.type === "boolean") {
286
+ return answer.probability < threshold;
287
+ }
288
+ return answer.probabilities[answer.choice] < threshold;
289
+ }
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: "Gate a channel wake, approval, or review comment on Jev by calling decide, then continuing or holding."
3
+ ---
4
+
5
+ # Gating a GitHub write on Jev
6
+
7
+ Channel hooks can call `decide` before they start a model turn. Return
8
+ `null` when the answer misses the bar; return `{ auth }` or `{ task }`
9
+ when the agent should run.
10
+
11
+ This extension's `evaluate` tool does not write to GitHub. When an
12
+ approval or a review comment must follow the judgment, add a server tool
13
+ in this project that calls `decide`, then writes or holds in the same
14
+ `execute`.
15
+
16
+ ```ts
17
+ import { above, decide, needsHuman } from "@cursor/july/extensions/jev";
18
+ ```
19
+
20
+ Hold when `needsHuman` is true or `above` misses the bar. Very low and
21
+ low risk can approve; medium and above hold. A finding comment posts
22
+ only when the draft is a real bug. Calibrate those numbers on labeled
23
+ pull requests from this workflow. Pass the pull request title, summary,
24
+ and draft, not the checkout. Return without writing when
25
+ `ctx.session.purpose === "eval"`.
@@ -0,0 +1,33 @@
1
+ ---
2
+ description: "Ask Jev for a risk tier, a finding judgment, or a score when you are not about to approve or comment."
3
+ ---
4
+
5
+ # Asking Jev
6
+
7
+ Call this extension's `evaluate` tool when you need a typed judgment over
8
+ a pull request and you are not about to approve or comment. It returns
9
+ answers only. The tool appears as `<namespace>__evaluate`.
10
+
11
+ ## Questions
12
+
13
+ Pass `state` (the pull request title, a short summary of the diff, and
14
+ any draft finding) and one or more questions. Each question has an `id`,
15
+ a `type`, and `instructions`.
16
+
17
+ - `boolean`: probability of true between 0 and 1
18
+ - `choice`: `criteria` maps each option name to a short description; the
19
+ answer names one option
20
+ - `score`: `criteria` from lowest to highest; the answer is a number
21
+ along that rubric
22
+
23
+ `instructions` can set the decision, who the review is for, and which
24
+ evidence to weigh. Pass the pull request summary, not the checkout.
25
+
26
+ ## Reading answers
27
+
28
+ A boolean answer has `probability`. A choice has `choice` and
29
+ `probabilities`. A score has `score` and `probabilities`.
30
+
31
+ When an approval or a review comment must follow the judgment, use a
32
+ project tool that calls `decide` itself. Do not treat these answers as
33
+ a GitHub write.
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import { defineTool } from "../../../tools.js";
3
+ import type { ToolDefinition } from "../../../types.js";
4
+ import extension from "../extension.js";
5
+ import { evaluate, type QuestionMap } from "../lib/evaluate.js";
6
+
7
+ const booleanQuestion = z.object({
8
+ id: z.string().min(1),
9
+ type: z.literal("boolean"),
10
+ instructions: z.string().min(1),
11
+ });
12
+
13
+ const choiceQuestion = z.object({
14
+ id: z.string().min(1),
15
+ type: z.literal("choice"),
16
+ instructions: z.string().min(1),
17
+ criteria: z.record(z.string()),
18
+ });
19
+
20
+ const scoreQuestion = z.object({
21
+ id: z.string().min(1),
22
+ type: z.literal("score"),
23
+ instructions: z.string().min(1),
24
+ criteria: z.array(z.string()).min(2),
25
+ });
26
+
27
+ const question = z.discriminatedUnion("type", [
28
+ booleanQuestion,
29
+ choiceQuestion,
30
+ scoreQuestion,
31
+ ]);
32
+
33
+ function toQuestionMap(questions: z.infer<typeof question>[]): QuestionMap {
34
+ const map: QuestionMap = {};
35
+ for (const item of questions) {
36
+ if (item.type === "boolean") {
37
+ map[item.id] = {
38
+ type: "boolean",
39
+ instructions: item.instructions,
40
+ };
41
+ } else if (item.type === "choice") {
42
+ map[item.id] = {
43
+ type: "choice",
44
+ instructions: item.instructions,
45
+ criteria: item.criteria,
46
+ };
47
+ } else {
48
+ map[item.id] = {
49
+ type: "score",
50
+ instructions: item.instructions,
51
+ criteria: item.criteria,
52
+ };
53
+ }
54
+ }
55
+ return map;
56
+ }
57
+
58
+ const evaluateTool: ToolDefinition = defineTool({
59
+ description:
60
+ "Ask Jev for typed boolean, choice, or score answers over one state. Returns answers only; it does not post, assign, or merge.",
61
+ effect: "read",
62
+ inputSchema: z
63
+ .object({
64
+ state: z
65
+ .union([z.string(), z.array(z.unknown()), z.record(z.unknown())])
66
+ .describe(
67
+ "Ticket, pull request, or other state Jev should judge. Not repo source."
68
+ ),
69
+ questions: z
70
+ .array(question)
71
+ .min(1)
72
+ .describe("One or more typed questions over the same state."),
73
+ })
74
+ .refine(
75
+ input =>
76
+ new Set(input.questions.map(item => item.id)).size ===
77
+ input.questions.length,
78
+ "questions must not repeat an id"
79
+ ),
80
+ async execute(input) {
81
+ return evaluate({
82
+ state: input.state,
83
+ questions: toQuestionMap(input.questions),
84
+ apiKey: extension.config.apiKey,
85
+ model: extension.config.model,
86
+ });
87
+ },
88
+ });
89
+
90
+ export default evaluateTool;
package/src/extensions.ts CHANGED
@@ -87,6 +87,7 @@ export type {
87
87
  DisabledToolDefinition,
88
88
  DiscoveredExtension,
89
89
  Extension,
90
+ ExtensionContributionSelection,
90
91
  ExtensionDeclaration,
91
92
  ExtensionMount,
92
93
  ExtensionOptions,
@@ -105,6 +106,7 @@ export function defineExtension<TConfig = undefined, TInput = TConfig>(
105
106
  return brandDefinition("extension", {
106
107
  extension: handle,
107
108
  config: parsed,
109
+ contributions: options.contributions?.(parsed),
108
110
  });
109
111
  };
110
112
  const handle = Object.defineProperties(mount, {
@@ -163,6 +163,9 @@ export async function resolveAdvertisedTools(args: {
163
163
  args.onConnectionError(connection.name, listing.error);
164
164
  continue;
165
165
  }
166
+ if (connection.optional === true) {
167
+ continue;
168
+ }
166
169
  throw new Error(
167
170
  `Failed to list tools on advertised connection "${connection.name}": ${describeError(listing.error)}`,
168
171
  { cause: listing.error }
@@ -181,6 +184,9 @@ export async function resolveAdvertisedTools(args: {
181
184
  args.onConnectionError(connection.name, error);
182
185
  continue;
183
186
  }
187
+ if (connection.optional === true) {
188
+ continue;
189
+ }
184
190
  throw error;
185
191
  }
186
192
  const intended = listed.filter(
@@ -311,6 +311,26 @@ export async function compileConnection(
311
311
  });
312
312
  return undefined;
313
313
  }
314
+ if (
315
+ connection.optional !== undefined &&
316
+ typeof connection.optional !== "boolean"
317
+ ) {
318
+ ctx.diagnostics.push({
319
+ severity: "error",
320
+ path: displayPath(ctx, entryPath),
321
+ message: "MCP connection `optional` must be a boolean.",
322
+ });
323
+ return undefined;
324
+ }
325
+ if (connection.optional === true && connection.advertiseTools !== true) {
326
+ ctx.diagnostics.push({
327
+ severity: "error",
328
+ path: displayPath(ctx, entryPath),
329
+ message:
330
+ "MCP connection `optional` skips advertised listing failures; set `advertiseTools: true` or drop `optional`.",
331
+ });
332
+ return undefined;
333
+ }
314
334
  const parsedEffects = parseConnectionEffects(connection.effects);
315
335
  if (!parsedEffects.ok) {
316
336
  ctx.diagnostics.push({
@@ -471,6 +491,7 @@ export async function compileConnection(
471
491
  description: connection.description,
472
492
  auth: connection.auth,
473
493
  advertiseTools: connection.advertiseTools,
494
+ optional: connection.optional,
474
495
  effects: parsedEffects.value,
475
496
  tools: parsedTools.value,
476
497
  disallowedTools: parsedDisallowed.value,
@@ -15,6 +15,7 @@ import type {
15
15
  DiscoveredSchedule,
16
16
  DiscoveredSkill,
17
17
  DiscoveredTool,
18
+ ExtensionContributionSelection,
18
19
  ExtensionMount,
19
20
  ResolvedAgent,
20
21
  SeedFile,
@@ -450,18 +451,25 @@ async function readExtensionTree(
450
451
  seedFiles: [],
451
452
  };
452
453
  }
453
- return readNativeTree(ctx, root);
454
+ return readNativeTree(ctx, root, mount.contributions);
454
455
  }
455
456
 
456
457
  async function readNativeTree(
457
458
  ctx: DiscoveryContext,
458
- root: string
459
+ root: string,
460
+ contributions?: ExtensionContributionSelection
459
461
  ): Promise<ExtensionTree> {
460
462
  await warnAgentRootOnlySlots(ctx, root);
461
463
  const connections = await loadConnections(ctx, join(root, "mcp-connections"));
462
464
  return {
463
- tools: await loadTools(ctx, join(root, "tools")),
464
- skills: await loadSkills(ctx, join(root, "skills")),
465
+ tools:
466
+ contributions?.tools === false
467
+ ? []
468
+ : await loadTools(ctx, join(root, "tools")),
469
+ skills:
470
+ contributions?.skills === false
471
+ ? []
472
+ : await loadSkills(ctx, join(root, "skills")),
465
473
  connections,
466
474
  hostConnections: await loadHostConnections(ctx, root, connections),
467
475
  overlaidServers: [],
@@ -80,6 +80,7 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
80
80
  name: connection.name,
81
81
  transport: connectionTransportLabel(connection.transport),
82
82
  advertiseTools: connection.advertiseTools,
83
+ optional: connection.optional,
83
84
  effects: connection.effects,
84
85
  tools: connection.tools,
85
86
  disallowedTools: connection.disallowedTools,
@@ -149,6 +149,8 @@ export type HostedGuestError = {
149
149
  classification: HostedFailureClassification;
150
150
  message?: string;
151
151
  turnStatus?: string;
152
+ /** Runner/SDK code (`unknown`, `canceled`, …). Not the human message. */
153
+ errorCode?: string;
152
154
  };
153
155
 
154
156
  /**
@@ -162,6 +164,7 @@ export function hostedGuestErrorResponse(input: {
162
164
  retry: HostedGuestRetryDisposition;
163
165
  message?: string;
164
166
  turnStatus?: string;
167
+ errorCode?: string;
165
168
  }): Response {
166
169
  const body: HostedGuestError = {
167
170
  ok: false,
@@ -170,6 +173,7 @@ export function hostedGuestErrorResponse(input: {
170
173
  classification: hostedFailureClassification(input.error),
171
174
  message: input.message,
172
175
  turnStatus: input.turnStatus,
176
+ errorCode: input.errorCode,
173
177
  };
174
178
  return Response.json(body, { status: input.status });
175
179
  }
@@ -14,6 +14,7 @@ import {
14
14
  createDeferredChannelSession,
15
15
  DeferredChannelSessionError,
16
16
  } from "./deferred-channel-session.js";
17
+ import { describeErrorCode } from "./describe-error.js";
17
18
  import {
18
19
  formatGuestNetworkServerTiming,
19
20
  GUEST_CONNECT_STALLED,
@@ -889,6 +890,7 @@ export function registerHostedDeliveryRoute(input: {
889
890
  | ReturnType<typeof abortReasonFromTurnStatus>
890
891
  | "execute_throw";
891
892
  failureCode?: string;
893
+ errorCode?: string;
892
894
  error?: unknown;
893
895
  }): Promise<void> => {
894
896
  const classification =
@@ -911,6 +913,9 @@ export function registerHostedDeliveryRoute(input: {
911
913
  ? {}
912
914
  : { abortReason: fields.abortReason }),
913
915
  failureCode: fields.failureCode,
916
+ ...(fields.errorCode === undefined
917
+ ? {}
918
+ : { errorCode: fields.errorCode }),
914
919
  classification,
915
920
  ...(fields.error === undefined ? {} : { error: fields.error }),
916
921
  });
@@ -1004,6 +1009,9 @@ export function registerHostedDeliveryRoute(input: {
1004
1009
  outcome.cancellationReason
1005
1010
  ),
1006
1011
  failureCode: hostedFailure?.error,
1012
+ ...(outcome.errorCode === undefined
1013
+ ? {}
1014
+ : { errorCode: outcome.errorCode }),
1007
1015
  });
1008
1016
  const reply =
1009
1017
  hostedFailure !== undefined
@@ -1018,6 +1026,9 @@ export function registerHostedDeliveryRoute(input: {
1018
1026
  ),
1019
1027
  turnStatus: outcome.status,
1020
1028
  message: outcome.errorMessage,
1029
+ ...(outcome.errorCode === undefined
1030
+ ? {}
1031
+ : { errorCode: outcome.errorCode }),
1021
1032
  },
1022
1033
  }
1023
1034
  : {
@@ -1047,6 +1058,7 @@ export function registerHostedDeliveryRoute(input: {
1047
1058
  sessionId,
1048
1059
  execution.sessionPublicId
1049
1060
  );
1061
+ const thrownErrorCode = describeErrorCode(error);
1050
1062
  await writeDiag({
1051
1063
  sessionId: "",
1052
1064
  turnStatus: "error",
@@ -1055,6 +1067,9 @@ export function registerHostedDeliveryRoute(input: {
1055
1067
  abortReason: "execute_throw",
1056
1068
  failureCode: "internal_error",
1057
1069
  error,
1070
+ ...(thrownErrorCode === undefined
1071
+ ? {}
1072
+ : { errorCode: thrownErrorCode }),
1058
1073
  });
1059
1074
  const endedAt = hostedExecutionNow();
1060
1075
  // Preserve completion/drain marks when the failure happened