engineering-behavior-observatory 0.2.1

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 (178) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +151 -0
  3. package/contracts/codex-app-server-0.150.1/manifest.json +25 -0
  4. package/contracts/codex-app-server-0.150.1/schema/InitializeParams.json +84 -0
  5. package/contracts/codex-app-server-0.150.1/schema/JSONRPCMessage.json +137 -0
  6. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadReadParams.json +17 -0
  7. package/contracts/codex-app-server-0.150.1/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  8. package/contracts/codex-app-server-0.150.1/schema/v2/TurnInterruptParams.json +17 -0
  9. package/contracts/codex-app-server-0.150.1/types/AskForApproval.ts +5 -0
  10. package/contracts/codex-app-server-0.150.1/types/ClientNotification.ts +5 -0
  11. package/contracts/codex-app-server-0.150.1/types/SandboxMode.ts +5 -0
  12. package/contracts/codex-app-server-0.150.1/types/ThreadReadParams.ts +9 -0
  13. package/contracts/codex-app-server-0.150.1/types/TokenUsageBreakdown.ts +5 -0
  14. package/contracts/codex-app-server-0.150.1/types/TurnInterruptParams.ts +5 -0
  15. package/contracts/codex-app-server-0.153.4/manifest.json +27 -0
  16. package/contracts/codex-app-server-0.153.4/schema/JSONRPCMessage.json +137 -0
  17. package/contracts/codex-app-server-0.153.4/schema/v1/InitializeParams.json +84 -0
  18. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadReadParams.json +17 -0
  19. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadStartParams.json +515 -0
  20. package/contracts/codex-app-server-0.153.4/schema/v2/ThreadTokenUsageUpdatedNotification.json +82 -0
  21. package/contracts/codex-app-server-0.153.4/schema/v2/TurnInterruptParams.json +17 -0
  22. package/contracts/codex-app-server-0.153.4/schema/v2/TurnStartParams.json +921 -0
  23. package/contracts/codex-app-server-0.153.4/types/AskForApproval.ts +5 -0
  24. package/contracts/codex-app-server-0.153.4/types/ClientNotification.ts +5 -0
  25. package/contracts/codex-app-server-0.153.4/types/SandboxMode.ts +5 -0
  26. package/contracts/codex-app-server-0.153.4/types/ThreadReadParams.ts +12 -0
  27. package/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.ts +5 -0
  28. package/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.ts +5 -0
  29. package/contracts/openhands-agent-server-v1.44.1.json +65 -0
  30. package/contracts/openhands-agent-server-v1.46.0.json +46 -0
  31. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.d.ts +9 -0
  32. package/dist/contracts/codex-app-server-0.153.4/types/AskForApproval.js +2 -0
  33. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.d.ts +3 -0
  34. package/dist/contracts/codex-app-server-0.153.4/types/ClientNotification.js +2 -0
  35. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.d.ts +1 -0
  36. package/dist/contracts/codex-app-server-0.153.4/types/SandboxMode.js +2 -0
  37. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.d.ts +10 -0
  38. package/dist/contracts/codex-app-server-0.153.4/types/ThreadReadParams.js +2 -0
  39. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.d.ts +8 -0
  40. package/dist/contracts/codex-app-server-0.153.4/types/TokenUsageBreakdown.js +2 -0
  41. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.d.ts +4 -0
  42. package/dist/contracts/codex-app-server-0.153.4/types/TurnInterruptParams.js +2 -0
  43. package/dist/src/agent-sdk-normalizer.d.ts +22 -0
  44. package/dist/src/agent-sdk-normalizer.js +995 -0
  45. package/dist/src/agent-sdk-run.d.ts +31 -0
  46. package/dist/src/agent-sdk-run.js +242 -0
  47. package/dist/src/agent-sdk-runner.d.ts +96 -0
  48. package/dist/src/agent-sdk-runner.js +407 -0
  49. package/dist/src/agent-sdk.d.ts +294 -0
  50. package/dist/src/agent-sdk.js +644 -0
  51. package/dist/src/aggregation.d.ts +183 -0
  52. package/dist/src/aggregation.js +513 -0
  53. package/dist/src/artifacts.d.ts +36 -0
  54. package/dist/src/artifacts.js +1372 -0
  55. package/dist/src/atlas-grafana.d.ts +377 -0
  56. package/dist/src/atlas-grafana.js +47 -0
  57. package/dist/src/atlas-html.d.ts +2 -0
  58. package/dist/src/atlas-html.js +34 -0
  59. package/dist/src/atlas.d.ts +130 -0
  60. package/dist/src/atlas.js +235 -0
  61. package/dist/src/behavior-assertions.d.ts +95 -0
  62. package/dist/src/behavior-assertions.js +154 -0
  63. package/dist/src/cli.d.ts +5 -0
  64. package/dist/src/cli.js +957 -0
  65. package/dist/src/codex-judge.d.ts +44 -0
  66. package/dist/src/codex-judge.js +214 -0
  67. package/dist/src/codex-run.d.ts +114 -0
  68. package/dist/src/codex-run.js +519 -0
  69. package/dist/src/codex.d.ts +187 -0
  70. package/dist/src/codex.js +1028 -0
  71. package/dist/src/contracts.d.ts +155 -0
  72. package/dist/src/contracts.js +866 -0
  73. package/dist/src/corpus.d.ts +67 -0
  74. package/dist/src/corpus.js +540 -0
  75. package/dist/src/cursor-sdk-runner.d.ts +81 -0
  76. package/dist/src/cursor-sdk-runner.js +362 -0
  77. package/dist/src/cursor-sdk.d.ts +81 -0
  78. package/dist/src/cursor-sdk.js +1078 -0
  79. package/dist/src/deepseek-adapter.d.ts +152 -0
  80. package/dist/src/deepseek-adapter.js +777 -0
  81. package/dist/src/exports.d.ts +69 -0
  82. package/dist/src/exports.js +966 -0
  83. package/dist/src/human-calibration.d.ts +167 -0
  84. package/dist/src/human-calibration.js +618 -0
  85. package/dist/src/index.d.ts +71 -0
  86. package/dist/src/index.js +38 -0
  87. package/dist/src/lifecycle.d.ts +196 -0
  88. package/dist/src/lifecycle.js +1889 -0
  89. package/dist/src/normalization-integrity.d.ts +128 -0
  90. package/dist/src/normalization-integrity.js +429 -0
  91. package/dist/src/openhands-run.d.ts +36 -0
  92. package/dist/src/openhands-run.js +415 -0
  93. package/dist/src/openhands.d.ts +130 -0
  94. package/dist/src/openhands.js +859 -0
  95. package/dist/src/pi.d.ts +203 -0
  96. package/dist/src/pi.js +1345 -0
  97. package/dist/src/process-protocol.d.ts +245 -0
  98. package/dist/src/process-protocol.js +1261 -0
  99. package/dist/src/retained-evidence.d.ts +12 -0
  100. package/dist/src/retained-evidence.js +201 -0
  101. package/dist/src/run-bundles.d.ts +193 -0
  102. package/dist/src/run-bundles.js +993 -0
  103. package/dist/src/scheduler.d.ts +109 -0
  104. package/dist/src/scheduler.js +1080 -0
  105. package/dist/src/semantic-judge.d.ts +194 -0
  106. package/dist/src/semantic-judge.js +875 -0
  107. package/dist/src/structural-observations.d.ts +124 -0
  108. package/dist/src/structural-observations.js +671 -0
  109. package/dist/src/task-packets.d.ts +115 -0
  110. package/dist/src/task-packets.js +683 -0
  111. package/dist/src/uniform-events.d.ts +120 -0
  112. package/dist/src/uniform-events.js +158 -0
  113. package/dist/src/verifiers.d.ts +116 -0
  114. package/dist/src/verifiers.js +819 -0
  115. package/dist/src/workspaces.d.ts +56 -0
  116. package/dist/src/workspaces.js +1314 -0
  117. package/docs/README.md +60 -0
  118. package/docs/development/README.md +71 -0
  119. package/docs/development/documentation-sync.md +58 -0
  120. package/docs/development/extension-contracts.md +307 -0
  121. package/docs/evaluation/README.md +48 -0
  122. package/docs/evaluation/aggregation.md +112 -0
  123. package/docs/evaluation/behavior-assertions.md +45 -0
  124. package/docs/evaluation/human-calibration.md +170 -0
  125. package/docs/evaluation/normalization-integrity.md +80 -0
  126. package/docs/evaluation/semantic-judge.md +188 -0
  127. package/docs/evaluation/structural-observations.md +84 -0
  128. package/docs/evaluation/uniform-events.md +131 -0
  129. package/docs/guides/atlas.md +144 -0
  130. package/docs/guides/evidence-and-sharing.md +100 -0
  131. package/docs/guides/operator-guide.md +424 -0
  132. package/docs/guides/quickstart.md +152 -0
  133. package/docs/guides/telemetry.md +66 -0
  134. package/docs/harnesses/README.md +31 -0
  135. package/docs/harnesses/claude-agent-sdk.md +61 -0
  136. package/docs/harnesses/codex-harness.md +174 -0
  137. package/docs/harnesses/cursor-sdk.md +149 -0
  138. package/docs/harnesses/deepseek-harness.md +134 -0
  139. package/docs/harnesses/openhands-agent-server.md +153 -0
  140. package/docs/harnesses/pi-sdk.md +66 -0
  141. package/docs/reference/README.md +19 -0
  142. package/docs/reference/agent-sdk-operational-runner.md +118 -0
  143. package/docs/reference/cli.md +114 -0
  144. package/docs/reference/contracts.md +222 -0
  145. package/docs/reference/run-bundle-contract.md +354 -0
  146. package/docs/reference/run-lifecycle.md +54 -0
  147. package/examples/cursor-sdk/README.md +11 -0
  148. package/examples/cursor-sdk/capture-profile.json +1 -0
  149. package/examples/cursor-sdk/harness.json +1 -0
  150. package/examples/cursor-sdk/model.json +1 -0
  151. package/examples/cursor-sdk/native-limits.json +1 -0
  152. package/examples/cursor-sdk/native-tool-policy.json +1 -0
  153. package/ontology/behavior-categories.v1.json +46 -0
  154. package/package.json +66 -0
  155. package/release/0.1.0/KNOWN_LIMITATIONS.md +35 -0
  156. package/release/0.1.0/README.md +46 -0
  157. package/release/0.1.0/reproducibility.json +61 -0
  158. package/release/0.2.0/KNOWN_LIMITATIONS.md +40 -0
  159. package/release/0.2.0/README.md +42 -0
  160. package/release/0.2.0/reproducibility.json +73 -0
  161. package/release/0.2.1/KNOWN_LIMITATIONS.md +18 -0
  162. package/release/0.2.1/README.md +63 -0
  163. package/release/0.2.1/reproducibility.json +73 -0
  164. package/release/README.md +13 -0
  165. package/schemas/aggregation.v1.json +293 -0
  166. package/schemas/behavior-assertions.v1.json +220 -0
  167. package/schemas/deepseek-runtime-composition.v1.schema.json +122 -0
  168. package/schemas/experiment.v1.schema.json +188 -0
  169. package/schemas/human-calibration.v1.json +336 -0
  170. package/schemas/normalization-integrity.v1.json +302 -0
  171. package/schemas/run-bundles/v1.json +1040 -0
  172. package/schemas/run-queue.v1.schema.json +206 -0
  173. package/schemas/semantic-judge.v1.json +380 -0
  174. package/schemas/structural-observations.v1.json +131 -0
  175. package/schemas/task-packet-freeze.v1.schema.json +106 -0
  176. package/schemas/task-packet.v1.schema.json +234 -0
  177. package/schemas/uniform-events/v1.json +289 -0
  178. package/scripts/atlas-grafana.sh +18 -0
@@ -0,0 +1,81 @@
1
+ import { type ModelSelection, type SDKModel } from "@cursor/sdk";
2
+ import { type ArtifactReference } from "./contracts.js";
3
+ import { CURSOR_SDK_HARNESS, CURSOR_SDK_VERSION, type CursorSdkAgentFactory, type CursorSdkToolPolicy } from "./cursor-sdk.js";
4
+ import type { AttemptClassificationKind, TerminalRecord } from "./lifecycle.js";
5
+ import type { CaptureQualificationStatus } from "./run-bundles.js";
6
+ import { type TaskPacket } from "./task-packets.js";
7
+ export declare const CURSOR_SDK_CONFIG_SCHEMA_VERSION = "ebo.cursor-sdk-config/v1";
8
+ export type CursorSdkModelConfiguration = {
9
+ schemaVersion: typeof CURSOR_SDK_CONFIG_SCHEMA_VERSION;
10
+ kind: "model";
11
+ provider: "cursor";
12
+ model: ModelSelection;
13
+ };
14
+ export type CursorSdkHarnessConfiguration = {
15
+ schemaVersion: typeof CURSOR_SDK_CONFIG_SCHEMA_VERSION;
16
+ kind: "harness";
17
+ adapter: typeof CURSOR_SDK_HARNESS;
18
+ sdkVersion: typeof CURSOR_SDK_VERSION;
19
+ };
20
+ export type CursorSdkNativeLimitsConfiguration = {
21
+ schemaVersion: typeof CURSOR_SDK_CONFIG_SCHEMA_VERSION;
22
+ kind: "native-limits";
23
+ shutdownGraceMs: number;
24
+ maxNativeRecordBytes: number;
25
+ };
26
+ export type CursorSdkNativeToolPolicyConfiguration = CursorSdkToolPolicy & {
27
+ schemaVersion: typeof CURSOR_SDK_CONFIG_SCHEMA_VERSION;
28
+ kind: "native-tool-policy";
29
+ };
30
+ export type CursorSdkCaptureProfileConfiguration = {
31
+ schemaVersion: typeof CURSOR_SDK_CONFIG_SCHEMA_VERSION;
32
+ kind: "capture-profile";
33
+ /** SDK-local per-run configuration/receipt only; not Cursor's Enterprise team export. */
34
+ nativeOtlp: "unsupported";
35
+ workspaceOutcome?: {
36
+ excludeDirectoryNames: string[];
37
+ respectGitignore?: boolean;
38
+ omitEmptyDirectories?: boolean;
39
+ };
40
+ };
41
+ export type CursorSdkConfigurationRecord = CursorSdkModelConfiguration | CursorSdkHarnessConfiguration | CursorSdkNativeLimitsConfiguration | CursorSdkNativeToolPolicyConfiguration | CursorSdkCaptureProfileConfiguration;
42
+ export type CursorSdkConfigurationKind = CursorSdkConfigurationRecord["kind"];
43
+ export type CursorSdkModelLister = (options: {
44
+ apiKey: string;
45
+ signal?: AbortSignal;
46
+ }) => Promise<SDKModel[]>;
47
+ export type RunCursorSdkQueueEntryOptions = {
48
+ bundleRoot: string;
49
+ queuePath: string;
50
+ runId: string;
51
+ outputRoot: string;
52
+ workspaceRoot?: string;
53
+ attemptId?: string;
54
+ apiKey?: string;
55
+ agentFactory?: CursorSdkAgentFactory;
56
+ modelLister?: CursorSdkModelLister;
57
+ signal?: AbortSignal;
58
+ };
59
+ export type CursorSdkRunSummary = {
60
+ runId: string;
61
+ attemptId: string;
62
+ bundlePath: string;
63
+ terminal: TerminalRecord;
64
+ classification: AttemptClassificationKind;
65
+ captureQualification: CaptureQualificationStatus;
66
+ assessmentMode: TaskPacket["assessmentMode"];
67
+ sessionId?: string;
68
+ nativeRunId?: string;
69
+ retainedWorkspacePath?: string;
70
+ };
71
+ /** Execute exactly one digest-pinned queue entry through the local Cursor SDK. */
72
+ export declare function runCursorSdkQueueEntry(options: RunCursorSdkQueueEntryOptions): Promise<CursorSdkRunSummary>;
73
+ type ConfigurationByKind = {
74
+ model: CursorSdkModelConfiguration;
75
+ harness: CursorSdkHarnessConfiguration;
76
+ "native-limits": CursorSdkNativeLimitsConfiguration;
77
+ "native-tool-policy": CursorSdkNativeToolPolicyConfiguration;
78
+ "capture-profile": CursorSdkCaptureProfileConfiguration;
79
+ };
80
+ export declare function resolveCursorSdkConfigurationRecord<Kind extends CursorSdkConfigurationKind>(bundleRoot: string, reference: ArtifactReference, kind: Kind): ConfigurationByKind[Kind];
81
+ export {};
@@ -0,0 +1,362 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { lstatSync } from "node:fs";
3
+ import { cp, mkdtemp, rm } from "node:fs/promises";
4
+ import { tmpdir } from "node:os";
5
+ import { isAbsolute, join, relative, resolve, sep } from "node:path";
6
+ import { Cursor } from "@cursor/sdk";
7
+ import { assertNoDuplicateJsonKeys, digestMetadata, validateArtifact, validateRunManifestEvidence } from "./artifacts.js";
8
+ import { isSafeArtifactRelativePath, resolveBundleConfiguration } from "./contracts.js";
9
+ import { assertCursorWorkspaceIsolation, captureCursorSdkRun, CURSOR_SDK_HARNESS, CURSOR_SDK_VERSION, } from "./cursor-sdk.js";
10
+ import { readBoundedFile, readRunQueue } from "./scheduler.js";
11
+ import { assertTaskPacketAdmitted, formatErrors } from "./task-packets.js";
12
+ import { executeVerifier } from "./verifiers.js";
13
+ import { cleanupWorkspace, materializeWorkspace } from "./workspaces.js";
14
+ export const CURSOR_SDK_CONFIG_SCHEMA_VERSION = "ebo.cursor-sdk-config/v1";
15
+ const ATTEMPT_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
16
+ /** Execute exactly one digest-pinned queue entry through the local Cursor SDK. */
17
+ export async function runCursorSdkQueueEntry(options) {
18
+ const bundleRoot = resolve(options.bundleRoot);
19
+ const queue = readRunQueue(options.queuePath, undefined, { bundleRoot });
20
+ const matches = queue.entries.filter((candidate) => candidate.runId === options.runId);
21
+ if (matches.length === 0)
22
+ throw new Error(`Run "${options.runId}" is not in the queue.`);
23
+ if (matches.length > 1)
24
+ throw new Error(`Run "${options.runId}" matches more than one queue entry.`);
25
+ const entry = matches[0];
26
+ if (entry.harness.id !== CURSOR_SDK_HARNESS)
27
+ throw new Error(`Queue entry harness must be ${CURSOR_SDK_HARNESS}.`);
28
+ const model = resolveCursorSdkConfigurationRecord(bundleRoot, entry.configuration.model, "model");
29
+ resolveCursorSdkConfigurationRecord(bundleRoot, entry.configuration.harness, "harness");
30
+ const limits = resolveCursorSdkConfigurationRecord(bundleRoot, entry.configuration.nativeLimits, "native-limits");
31
+ const toolPolicy = resolveCursorSdkConfigurationRecord(bundleRoot, entry.configuration.nativeToolPolicy, "native-tool-policy");
32
+ const captureProfile = resolveCursorSdkConfigurationRecord(bundleRoot, queue.captureProfile, "capture-profile");
33
+ const auth = options.apiKey ?? process.env.CURSOR_API_KEY;
34
+ if (auth === undefined || auth.trim() === "") {
35
+ throw new Error("An approved Cursor credential is required through the runtime secret path.");
36
+ }
37
+ if (options.signal?.aborted)
38
+ throw new Error("Cursor model catalog lookup was interrupted.");
39
+ const catalogPromise = (options.modelLister ?? ((input) => Cursor.models.list({ apiKey: input.apiKey })))({ apiKey: auth, ...(options.signal === undefined ? {} : { signal: options.signal }) });
40
+ const availableModels = await abortablePreflight(catalogPromise, options.signal, "Cursor model catalog lookup");
41
+ const catalogModel = availableModels.find(({ id }) => id === model.model.id);
42
+ if (catalogModel === undefined)
43
+ throw new Error(`Cursor model "${model.model.id}" is not an exact available catalog model.`);
44
+ validateModelParameters(model.model, catalogModel);
45
+ const inspection = assertTaskPacketAdmitted(bundleRoot, entry.task.packetRef.locator);
46
+ const packet = inspection.packet;
47
+ if (inspection.packetDigest === null || inspection.packetDigest.value !== entry.task.packetRef.digest.value
48
+ || inspection.packetDigest.algorithm !== entry.task.packetRef.digest.algorithm) {
49
+ throw new Error(`Task packet "${entry.task.packetRef.locator}" changed from its frozen queue reference.`);
50
+ }
51
+ const verifierReference = packet.assessmentMode === "verified" ? packet.restricted.verifier : undefined;
52
+ const verifierFormat = verifierReference?.locator.toLowerCase().endsWith(".mjs") ? "module" : "commonjs";
53
+ const attemptId = options.attemptId ?? randomUUID();
54
+ if (!ATTEMPT_ID_PATTERN.test(attemptId))
55
+ throw new Error("Attempt ID must be one safe path component.");
56
+ const outputRoot = resolve(options.outputRoot);
57
+ const attemptBundleRoot = join(outputRoot, entry.runId, attemptId);
58
+ const fromOutputRoot = relative(outputRoot, attemptBundleRoot);
59
+ if (fromOutputRoot === "" || isAbsolute(fromOutputRoot) || fromOutputRoot === ".." || fromOutputRoot.startsWith(`..${sep}`)) {
60
+ throw new Error("Attempt destination escapes the selected output root.");
61
+ }
62
+ if (pathExists(attemptBundleRoot))
63
+ throw new Error(`Attempt destination "${attemptBundleRoot}" already exists and is never replaced.`);
64
+ const definition = buildRunBundleDefinition(queue.captureProfile, entry, packet, model.model.id, attemptBundleRoot, attemptId, verifierFormat);
65
+ const configuration = {
66
+ model: structuredClone(model.model),
67
+ toolPolicy: {
68
+ tools: [...toolPolicy.tools],
69
+ ...(toolPolicy.disallowedTools === undefined ? {} : { disallowedTools: [...toolPolicy.disallowedTools] }),
70
+ sandbox: { enabled: toolPolicy.sandbox.enabled },
71
+ settingSources: [],
72
+ autoReview: false,
73
+ enableAgentRetries: false,
74
+ },
75
+ maxNativeRecordBytes: limits.maxNativeRecordBytes,
76
+ };
77
+ const workspace = await materializeWorkspace({
78
+ bundleRoot,
79
+ packetLocator: entry.task.packetRef.locator,
80
+ freezeLocator: entry.task.freezeLocator,
81
+ ...(options.workspaceRoot === undefined ? {} : { workspaceParent: options.workspaceRoot }),
82
+ });
83
+ if (workspace.status !== "ready") {
84
+ throw new Error(`Workspace materialization failed before the attempt started: ${workspace.error ?? "unknown failure"}`);
85
+ }
86
+ let baselineRoot;
87
+ let captureStarted = false;
88
+ try {
89
+ await assertCursorWorkspaceIsolation(workspace.path);
90
+ baselineRoot = await mkdtemp(join(tmpdir(), "ebo-cursor-sdk-baseline-"));
91
+ const startingWorkspacePath = join(baselineRoot, "workspace");
92
+ await cp(workspace.path, startingWorkspacePath, { recursive: true, preserveTimestamps: true, force: false });
93
+ const result = await captureCursorSdkRun({
94
+ definition,
95
+ startingWorkspacePath,
96
+ workspace: {
97
+ setup: () => {
98
+ captureStarted = true;
99
+ return { status: "ready", path: workspace.path, artifactId: "workspace", retained: true };
100
+ },
101
+ cleanup: async () => cleanupWorkspace(workspace),
102
+ },
103
+ configuration,
104
+ apiKey: auth,
105
+ prompt: packet.agentInput.prompt,
106
+ ...(captureProfile.workspaceOutcome === undefined ? {} : {
107
+ workspaceOutcomeExcludedDirectoryNames: captureProfile.workspaceOutcome.excludeDirectoryNames,
108
+ ...(captureProfile.workspaceOutcome.respectGitignore === undefined ? {} : { workspaceOutcomeRespectsGitignore: captureProfile.workspaceOutcome.respectGitignore }),
109
+ ...(captureProfile.workspaceOutcome.omitEmptyDirectories === undefined ? {} : { workspaceOutcomeOmitsEmptyDirectories: captureProfile.workspaceOutcome.omitEmptyDirectories }),
110
+ }),
111
+ ...(verifierReference === undefined ? {} : {
112
+ verifier: (context, outcome, projectedWorkspacePath) => executeVerifier({
113
+ bundleId: definition.bundleId,
114
+ verifierRoot: bundleRoot,
115
+ verifier: verifierReference,
116
+ workspacePath: projectedWorkspacePath,
117
+ workspaceFingerprint: outcome.fingerprint,
118
+ workspace: { artifactId: outcome.descriptor.id, digest: outcome.descriptor.digest, fingerprint: outcome.fingerprint },
119
+ artifactRoot: attemptBundleRoot,
120
+ moduleFormat: verifierFormat,
121
+ signal: context.signal,
122
+ }),
123
+ }),
124
+ maxWallClockMs: queue.coordinatorBudget.maxWallClockMs,
125
+ shutdownGraceMs: limits.shutdownGraceMs,
126
+ ...(options.agentFactory === undefined ? {} : { agentFactory: options.agentFactory }),
127
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
128
+ });
129
+ const manifest = reopenFinalManifest(attemptBundleRoot);
130
+ return {
131
+ runId: entry.runId,
132
+ attemptId,
133
+ bundlePath: attemptBundleRoot,
134
+ terminal: structuredClone(manifest.terminal),
135
+ classification: result.attempt.classification.kind,
136
+ captureQualification: result.qualification.status,
137
+ assessmentMode: packet.assessmentMode,
138
+ ...(manifest.run.native?.sessionId === undefined ? {} : { sessionId: manifest.run.native.sessionId }),
139
+ ...(result.capture.runId === undefined ? {} : { nativeRunId: result.capture.runId }),
140
+ ...(workspace.state === "ready" ? { retainedWorkspacePath: workspace.path } : {}),
141
+ };
142
+ }
143
+ finally {
144
+ if (baselineRoot !== undefined)
145
+ await rm(baselineRoot, { recursive: true, force: true });
146
+ if (!captureStarted && workspace.state === "ready")
147
+ await cleanupWorkspace(workspace).catch(() => undefined);
148
+ }
149
+ }
150
+ export function resolveCursorSdkConfigurationRecord(bundleRoot, reference, kind) {
151
+ const bytes = resolveBundleConfiguration(bundleRoot, reference);
152
+ let source;
153
+ try {
154
+ source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
155
+ }
156
+ catch {
157
+ throw configurationError(reference, "is not valid UTF-8");
158
+ }
159
+ assertNoDuplicateJsonKeys(source);
160
+ let value;
161
+ try {
162
+ value = JSON.parse(source);
163
+ }
164
+ catch (error) {
165
+ throw configurationError(reference, `is not valid JSON: ${errorMessage(error)}`);
166
+ }
167
+ if (!isRecord(value))
168
+ throw configurationError(reference, "must be a JSON object");
169
+ if (value.schemaVersion !== CURSOR_SDK_CONFIG_SCHEMA_VERSION) {
170
+ throw configurationError(reference, `must declare schemaVersion ${CURSOR_SDK_CONFIG_SCHEMA_VERSION}`);
171
+ }
172
+ if (value.kind !== kind)
173
+ throw configurationError(reference, `has kind "${String(value.kind)}" instead of "${kind}"`);
174
+ validateConfigurationRecord(value, kind, reference);
175
+ return value;
176
+ }
177
+ function validateConfigurationRecord(record, kind, reference) {
178
+ switch (kind) {
179
+ case "model": {
180
+ assertRecordKeys(record, ["provider", "model"], ["provider", "model"], reference);
181
+ if (record.provider !== "cursor" || !isRecord(record.model))
182
+ throw configurationError(reference, "must declare a Cursor model object");
183
+ assertKeys(record.model, ["id", "params"], ["id"], reference);
184
+ assertText(record.model.id, "model.id", reference);
185
+ if (record.model.params !== undefined) {
186
+ if (!Array.isArray(record.model.params) || record.model.params.some((entry) => !isRecord(entry)
187
+ || Object.keys(entry).some((key) => !["id", "value"].includes(key))
188
+ || typeof entry.id !== "string" || entry.id.trim() === "" || typeof entry.value !== "string" || entry.value.trim() === "")) {
189
+ throw configurationError(reference, "model.params must contain only nonempty id/value pairs");
190
+ }
191
+ }
192
+ return;
193
+ }
194
+ case "harness":
195
+ assertRecordKeys(record, ["adapter", "sdkVersion"], ["adapter", "sdkVersion"], reference);
196
+ if (record.adapter !== CURSOR_SDK_HARNESS || record.sdkVersion !== CURSOR_SDK_VERSION) {
197
+ throw configurationError(reference, `must pin ${CURSOR_SDK_HARNESS} ${CURSOR_SDK_VERSION}`);
198
+ }
199
+ return;
200
+ case "native-limits":
201
+ assertRecordKeys(record, ["shutdownGraceMs", "maxNativeRecordBytes"], ["shutdownGraceMs", "maxNativeRecordBytes"], reference);
202
+ for (const field of ["shutdownGraceMs", "maxNativeRecordBytes"]) {
203
+ if (!Number.isSafeInteger(record[field]) || record[field] < 1)
204
+ throw configurationError(reference, `${field} must be a positive safe integer`);
205
+ }
206
+ return;
207
+ case "native-tool-policy":
208
+ assertRecordKeys(record, ["tools", "disallowedTools", "sandbox", "settingSources", "autoReview", "enableAgentRetries"], ["tools", "sandbox", "settingSources", "autoReview", "enableAgentRetries"], reference);
209
+ assertStringList(record.tools, "tools", reference);
210
+ if (record.disallowedTools !== undefined)
211
+ assertStringList(record.disallowedTools, "disallowedTools", reference);
212
+ if (!isRecord(record.sandbox) || typeof record.sandbox.enabled !== "boolean" || Object.keys(record.sandbox).some((key) => key !== "enabled")) {
213
+ throw configurationError(reference, "sandbox.enabled must be an explicit boolean");
214
+ }
215
+ if (!Array.isArray(record.settingSources) || record.settingSources.length !== 0) {
216
+ throw configurationError(reference, "settingSources must be [] so unrelated user/candidate settings are not loaded");
217
+ }
218
+ if (record.autoReview !== false || record.enableAgentRetries !== false) {
219
+ throw configurationError(reference, "autoReview and enableAgentRetries must both be false for an observational attempt");
220
+ }
221
+ return;
222
+ case "capture-profile":
223
+ assertRecordKeys(record, ["nativeOtlp", "workspaceOutcome"], ["nativeOtlp"], reference);
224
+ if (record.nativeOtlp !== "unsupported")
225
+ throw configurationError(reference, 'nativeOtlp must be "unsupported" for SDK 1.0.31');
226
+ if (record.workspaceOutcome !== undefined) {
227
+ if (!isRecord(record.workspaceOutcome))
228
+ throw configurationError(reference, "workspaceOutcome must be an object");
229
+ assertKeys(record.workspaceOutcome, ["excludeDirectoryNames", "respectGitignore", "omitEmptyDirectories"], ["excludeDirectoryNames"], reference);
230
+ assertStringList(record.workspaceOutcome.excludeDirectoryNames, "workspaceOutcome.excludeDirectoryNames", reference);
231
+ if (record.workspaceOutcome.excludeDirectoryNames.some((name) => name.includes("/") || !isSafeArtifactRelativePath(name))) {
232
+ throw configurationError(reference, "workspace exclusions must be safe path segments");
233
+ }
234
+ for (const flag of ["respectGitignore", "omitEmptyDirectories"]) {
235
+ if (record.workspaceOutcome[flag] !== undefined && typeof record.workspaceOutcome[flag] !== "boolean") {
236
+ throw configurationError(reference, `workspaceOutcome.${flag} must be a boolean`);
237
+ }
238
+ }
239
+ }
240
+ return;
241
+ }
242
+ }
243
+ function buildRunBundleDefinition(captureProfile, entry, packet, effectiveModelId, bundleRoot, attemptId, verifierFormat) {
244
+ return {
245
+ bundleRoot,
246
+ bundleId: `bundle-${attemptId}`,
247
+ run: {
248
+ id: entry.runId,
249
+ trial: { index: entry.trial.index },
250
+ assessmentMode: packet.assessmentMode,
251
+ task: { id: entry.task.id, digest: `sha256:${entry.task.packetRef.digest.value}` },
252
+ fixture: { id: packet.agentInput.fixture.source.locator, digest: `sha256:${packet.agentInput.fixture.source.digest.value}` },
253
+ model: { provider: "cursor", id: effectiveModelId, configurationDigest: `sha256:${entry.configuration.model.digest.value}` },
254
+ harness: { id: CURSOR_SDK_HARNESS, version: CURSOR_SDK_VERSION, configurationDigest: `sha256:${entry.configuration.harness.digest.value}` },
255
+ runtime: [
256
+ { source: "cursor", name: "cursor-sdk", version: CURSOR_SDK_VERSION },
257
+ { source: "cursor", name: "local-agent-runtime", version: "not-exposed" },
258
+ ],
259
+ ...(packet.assessmentMode === "verified" ? {
260
+ verifier: { locator: packet.restricted.verifier.locator, digest: `sha256:${packet.restricted.verifier.digest.value}`, format: verifierFormat },
261
+ } : {}),
262
+ },
263
+ attempt: { id: attemptId, number: 1 },
264
+ configuration: {
265
+ digest: `sha256:${digestMetadata({ model: entry.configuration.model, harness: entry.configuration.harness, captureProfile }).value}`,
266
+ captureProfileDigest: `sha256:${captureProfile.digest.value}`,
267
+ budgetDigest: `sha256:${entry.configuration.nativeLimits.digest.value}`,
268
+ toolPolicyDigest: `sha256:${entry.configuration.nativeToolPolicy.digest.value}`,
269
+ },
270
+ };
271
+ }
272
+ function validateModelParameters(selection, catalog) {
273
+ const selected = selection.params ?? [];
274
+ if (new Set(selected.map(({ id }) => id)).size !== selected.length)
275
+ throw new Error("Cursor model parameters must not repeat IDs.");
276
+ for (const parameter of selected) {
277
+ const definition = catalog.parameters?.find(({ id }) => id === parameter.id);
278
+ if (definition === undefined || !definition.values.some(({ value }) => value === parameter.value)) {
279
+ throw new Error(`Cursor model parameter ${parameter.id}=${parameter.value} is not available in the current catalog.`);
280
+ }
281
+ }
282
+ }
283
+ async function abortablePreflight(promise, signal, label) {
284
+ if (signal === undefined)
285
+ return await promise;
286
+ if (signal.aborted)
287
+ throw new Error(`${label} was interrupted.`);
288
+ return await new Promise((resolvePromise, rejectPromise) => {
289
+ let settled = false;
290
+ const onAbort = () => {
291
+ if (settled)
292
+ return;
293
+ settled = true;
294
+ signal.removeEventListener("abort", onAbort);
295
+ rejectPromise(new Error(`${label} was interrupted.`));
296
+ };
297
+ signal.addEventListener("abort", onAbort, { once: true });
298
+ void promise.then((value) => {
299
+ if (settled)
300
+ return;
301
+ settled = true;
302
+ signal.removeEventListener("abort", onAbort);
303
+ resolvePromise(value);
304
+ }, (error) => {
305
+ if (settled)
306
+ return;
307
+ settled = true;
308
+ signal.removeEventListener("abort", onAbort);
309
+ rejectPromise(error);
310
+ });
311
+ });
312
+ }
313
+ function reopenFinalManifest(bundleRoot) {
314
+ const source = new TextDecoder("utf-8", { fatal: true }).decode(readBoundedFile(join(bundleRoot, "manifest.json"), "Run manifest"));
315
+ assertNoDuplicateJsonKeys(source);
316
+ const manifest = JSON.parse(source);
317
+ const errors = [...validateArtifact("manifest.json", manifest), ...validateRunManifestEvidence("manifest.json", manifest, bundleRoot)];
318
+ if (errors.length > 0)
319
+ throw new Error(`Retained run manifest failed validation:\n${formatErrors(errors)}`);
320
+ return manifest;
321
+ }
322
+ function assertRecordKeys(record, allowed, required, reference) {
323
+ assertKeys(record, ["schemaVersion", "kind", ...allowed], required, reference);
324
+ }
325
+ function assertKeys(record, allowed, required, reference) {
326
+ const permitted = new Set(allowed);
327
+ for (const key of Object.keys(record))
328
+ if (!permitted.has(key))
329
+ throw configurationError(reference, `contains unknown field "${key}"`);
330
+ for (const key of required)
331
+ if (record[key] === undefined)
332
+ throw configurationError(reference, `is missing required field "${key}"`);
333
+ }
334
+ function assertText(value, field, reference) {
335
+ if (typeof value !== "string" || value.trim() === "")
336
+ throw configurationError(reference, `${field} must be a nonempty string`);
337
+ }
338
+ function assertStringList(value, field, reference) {
339
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.trim() === "") || new Set(value).size !== value.length) {
340
+ throw configurationError(reference, `${field} must be a unique array of nonempty strings`);
341
+ }
342
+ }
343
+ function pathExists(path) {
344
+ try {
345
+ lstatSync(path);
346
+ return true;
347
+ }
348
+ catch (error) {
349
+ if (error.code === "ENOENT")
350
+ return false;
351
+ throw error;
352
+ }
353
+ }
354
+ function configurationError(reference, detail) {
355
+ return new Error(`Cursor SDK configuration "${reference.locator}" ${detail}.`);
356
+ }
357
+ function isRecord(value) {
358
+ return typeof value === "object" && value !== null && !Array.isArray(value);
359
+ }
360
+ function errorMessage(error) {
361
+ return error instanceof Error ? error.message : String(error);
362
+ }
@@ -0,0 +1,81 @@
1
+ import { type AgentOptions, type ModelSelection, type RunResult, type SDKAgent } from "@cursor/sdk";
2
+ import { type RunAttemptResult, type VerifierExecutionContext, type WorkspaceCoordinator } from "./lifecycle.js";
3
+ import { type AdapterCoverageReport, type NormalizedDataset } from "./normalization-integrity.js";
4
+ import { type AgentSdkNativeRecord } from "./agent-sdk-normalizer.js";
5
+ import { type CaptureQualificationReport, type CapturedWorkspaceOutcome, type RunBundleDefinition, type RunManifest } from "./run-bundles.js";
6
+ import { type AdapterCapabilityProfile, type NativeEvidenceResolver, type NormalizationInput, type NormalizationResult } from "./uniform-events.js";
7
+ import type { VerifierResult } from "./verifiers.js";
8
+ export declare const CURSOR_SDK_VERSION = "1.0.31";
9
+ export declare const CURSOR_SDK_HARNESS = "cursor-sdk";
10
+ export declare const CURSOR_SDK_ADAPTER_ID = "ebo-cursor-sdk-v1";
11
+ export declare const CURSOR_SDK_ADAPTER_VERSION = "0.1.0";
12
+ export declare const CURSOR_SDK_DEFAULT_SHUTDOWN_GRACE_MS = 2000;
13
+ export type CursorSdkToolPolicy = {
14
+ tools: readonly string[];
15
+ disallowedTools?: readonly string[];
16
+ sandbox: {
17
+ enabled: boolean;
18
+ };
19
+ settingSources: readonly [];
20
+ autoReview: false;
21
+ enableAgentRetries: false;
22
+ };
23
+ export type CursorSdkCaptureConfiguration = {
24
+ model: ModelSelection;
25
+ toolPolicy: CursorSdkToolPolicy;
26
+ maxNativeRecordBytes?: number;
27
+ };
28
+ export type CursorSdkAgentFactory = (options: AgentOptions) => Promise<SDKAgent>;
29
+ export type CaptureCursorSdkVerifier = (context: VerifierExecutionContext, workspace: CapturedWorkspaceOutcome, workspacePath: string) => VerifierResult | Promise<VerifierResult>;
30
+ export type CaptureCursorSdkRunOptions = {
31
+ definition: RunBundleDefinition;
32
+ startingWorkspacePath: string;
33
+ workspace: WorkspaceCoordinator;
34
+ configuration: CursorSdkCaptureConfiguration;
35
+ apiKey: string;
36
+ prompt: string;
37
+ verifier?: CaptureCursorSdkVerifier;
38
+ workspaceOutcomeExcludedDirectoryNames?: readonly string[];
39
+ workspaceOutcomeRespectsGitignore?: boolean;
40
+ workspaceOutcomeOmitsEmptyDirectories?: boolean;
41
+ signal?: AbortSignal;
42
+ maxWallClockMs?: number;
43
+ shutdownGraceMs?: number;
44
+ agentFactory?: CursorSdkAgentFactory;
45
+ now?: () => string;
46
+ };
47
+ export type CaptureCursorSdkRunResult = {
48
+ attempt: RunAttemptResult;
49
+ manifest: RunManifest;
50
+ qualification: CaptureQualificationReport;
51
+ capture: CursorSdkCaptureReport;
52
+ };
53
+ export type CursorSdkCaptureReport = {
54
+ sdkVersion: typeof CURSOR_SDK_VERSION;
55
+ agentId?: string;
56
+ runId?: string;
57
+ terminal?: RunResult;
58
+ streamRecords: number;
59
+ deltaRecords: number;
60
+ stepRecords: number;
61
+ historyStatus: "captured" | "failed" | "not-started";
62
+ billingStatus: "captured" | "unavailable" | "not-started";
63
+ storeFiles: readonly string[];
64
+ errors: readonly string[];
65
+ };
66
+ export type CursorNativeRecord = Record<string, unknown>;
67
+ export type CursorSdkBehaviorEvidence = {
68
+ capture: NormalizationInput<CursorNativeRecord>;
69
+ outcomeCapture: NormalizationInput<AgentSdkNativeRecord>;
70
+ dataset: NormalizedDataset;
71
+ resolver: NativeEvidenceResolver;
72
+ coverage: AdapterCoverageReport;
73
+ };
74
+ export declare const CURSOR_SDK_CAPABILITIES: AdapterCapabilityProfile;
75
+ /** Execute one SDK-owned local attempt and retain all source channels before projection. */
76
+ export declare function captureCursorSdkRun(options: CaptureCursorSdkRunOptions): Promise<CaptureCursorSdkRunResult>;
77
+ /** Prevent Cursor from promoting an ancestor checkout above the materialized workspace. */
78
+ export declare function assertCursorWorkspaceIsolation(workspacePath: string): Promise<void>;
79
+ /** Read and normalize one retained, capture-qualified Cursor SDK bundle. */
80
+ export declare function createCursorSdkBehaviorEvidence(bundleRoot: string): Promise<CursorSdkBehaviorEvidence>;
81
+ export declare function normalizeCursorSdkCapture(input: NormalizationInput<CursorNativeRecord>): NormalizationResult;