@hue-run/sdk 0.3.2 → 0.4.2
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/CLI.md +270 -47
- package/ENVIRONMENTS.md +11 -1
- package/README.md +19 -3
- package/dist/client.d.ts +5 -5
- package/dist/client.js +13 -6
- package/dist/environment/tools.d.ts +6 -1
- package/dist/environment/tools.js +7 -1
- package/dist/environment/types.d.ts +6 -1
- package/dist/evals/simulation.d.ts +12 -4
- package/dist/evals/simulation.js +34 -24
- package/dist/evals.d.ts +1 -1
- package/dist/receipt.js +36 -8
- package/dist/setup/application.d.ts +74 -0
- package/dist/setup/application.js +766 -0
- package/dist/setup/backend.d.ts +229 -0
- package/dist/setup/backend.js +855 -0
- package/dist/setup/checkpoint.js +100 -30
- package/dist/setup/cli.js +20 -4
- package/dist/setup/configure.d.ts +13 -0
- package/dist/setup/configure.js +454 -0
- package/dist/setup/credential.d.ts +2 -0
- package/dist/setup/credential.js +9 -0
- package/dist/setup/detect.js +4 -1
- package/dist/setup/installation.d.ts +118 -0
- package/dist/setup/installation.js +605 -0
- package/dist/setup/lock.d.ts +2 -0
- package/dist/setup/lock.js +38 -0
- package/dist/setup/machine.d.ts +1 -10
- package/dist/setup/machine.js +8 -7
- package/dist/setup/render.d.ts +3 -1
- package/dist/setup/render.js +209 -6
- package/dist/setup/runner.d.ts +26 -76
- package/dist/setup/runner.js +320 -45
- package/dist/setup/socket.d.ts +7 -0
- package/dist/setup/socket.js +144 -0
- package/dist/setup/source.d.ts +9 -0
- package/dist/setup/source.js +269 -0
- package/dist/setup/types.d.ts +16 -9
- package/dist/setup/types.js +1 -1
- package/dist/setup.d.ts +6 -2
- package/dist/setup.js +3 -0
- package/dist/types.d.ts +24 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/setup-events.schema.json +16 -9
package/dist/evals/simulation.js
CHANGED
|
@@ -46,24 +46,24 @@ function normalizedEnvironmentDefinition(definition) {
|
|
|
46
46
|
metadata: definition.metadata ?? {},
|
|
47
47
|
}, aggregateBounds(240_000));
|
|
48
48
|
}
|
|
49
|
-
function
|
|
50
|
-
if (
|
|
51
|
-
return
|
|
49
|
+
function definitionIdentity(definition) {
|
|
50
|
+
if (definition.kind === "experiment")
|
|
51
|
+
return definition;
|
|
52
52
|
return json({
|
|
53
|
-
kind:
|
|
54
|
-
name:
|
|
55
|
-
slug:
|
|
56
|
-
description:
|
|
53
|
+
kind: definition.kind,
|
|
54
|
+
name: definition.name,
|
|
55
|
+
slug: definition.slug,
|
|
56
|
+
description: definition.description ?? "",
|
|
57
57
|
environment: {
|
|
58
|
-
...
|
|
59
|
-
definition: normalizedEnvironmentDefinition(
|
|
58
|
+
...definition.environment,
|
|
59
|
+
definition: normalizedEnvironmentDefinition(definition.environment.definition),
|
|
60
60
|
},
|
|
61
|
-
cases:
|
|
62
|
-
scorers:
|
|
61
|
+
cases: definition.cases,
|
|
62
|
+
scorers: definition.scorers.map(({ scorer, ...identity }) => ({
|
|
63
63
|
...identity,
|
|
64
64
|
definition: normalizeScorerDefinitionForPublication("definition" in scorer ? scorer.definition : scorer),
|
|
65
65
|
})),
|
|
66
|
-
config:
|
|
66
|
+
config: definition.config ?? {},
|
|
67
67
|
}, aggregateBounds(8 * 1024 * 1024));
|
|
68
68
|
}
|
|
69
69
|
async function findBySlug(page, slug) {
|
|
@@ -283,9 +283,9 @@ async function allCases(client, versionId) {
|
|
|
283
283
|
after = page.nextCursor;
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
-
async function resolveExperiment(options, idempotencyKey) {
|
|
287
|
-
if (
|
|
288
|
-
const source = await options.client.getExperiment(
|
|
286
|
+
async function resolveExperiment(options, definition, idempotencyKey) {
|
|
287
|
+
if (definition.kind === "experiment") {
|
|
288
|
+
const source = await options.client.getExperiment(definition.experimentId);
|
|
289
289
|
const created = await options.client.createExperiment({
|
|
290
290
|
idempotencyKey,
|
|
291
291
|
name: options.runName ?? source.name,
|
|
@@ -295,25 +295,35 @@ async function resolveExperiment(options, idempotencyKey) {
|
|
|
295
295
|
});
|
|
296
296
|
return { experimentId: created.id, bindings: options.localScorers ?? [] };
|
|
297
297
|
}
|
|
298
|
-
const environmentVersionId = await resolveEnvironment(options.environmentClient,
|
|
299
|
-
const datasetVersionId = await resolveDataset(options.client,
|
|
300
|
-
const scorers = await resolveScorers(options.client,
|
|
298
|
+
const environmentVersionId = await resolveEnvironment(options.environmentClient, definition.environment);
|
|
299
|
+
const datasetVersionId = await resolveDataset(options.client, definition, environmentVersionId);
|
|
300
|
+
const scorers = await resolveScorers(options.client, definition.scorers);
|
|
301
301
|
const created = await options.client.createExperiment({
|
|
302
302
|
idempotencyKey,
|
|
303
|
-
name: options.runName ??
|
|
303
|
+
name: options.runName ?? definition.name,
|
|
304
304
|
datasetVersionId,
|
|
305
305
|
scorerVersionIds: scorers.versionIds,
|
|
306
|
-
config:
|
|
306
|
+
config: definition.config ?? {},
|
|
307
307
|
});
|
|
308
308
|
return {
|
|
309
309
|
experimentId: created.id,
|
|
310
310
|
bindings: [...scorers.bindings, ...(options.localScorers ?? [])],
|
|
311
311
|
};
|
|
312
312
|
}
|
|
313
|
+
let scenarioDeprecationWarned = false;
|
|
313
314
|
/** Run an existing agent callback against one fresh hosted world per case. The helper
|
|
314
315
|
* owns immutable resolution, execution linkage, finalization, scoring and resumable uploads.
|
|
315
316
|
*/
|
|
316
317
|
export async function runSimulation(options) {
|
|
318
|
+
const definition = options.definition ?? options.scenario;
|
|
319
|
+
if (!definition)
|
|
320
|
+
throw new TypeError("runSimulation requires a definition");
|
|
321
|
+
if (options.definition && options.scenario && options.definition !== options.scenario)
|
|
322
|
+
throw new TypeError("Pass either definition or scenario, not both");
|
|
323
|
+
if (!options.definition && !scenarioDeprecationWarned) {
|
|
324
|
+
scenarioDeprecationWarned = true;
|
|
325
|
+
process.emitWarning("runSimulation option scenario is deprecated; use definition", "DeprecationWarning");
|
|
326
|
+
}
|
|
317
327
|
const requestedConfiguration = requestedAttemptV2(options);
|
|
318
328
|
if (options.maxSteps !== undefined &&
|
|
319
329
|
(!Number.isInteger(options.maxSteps) ||
|
|
@@ -332,7 +342,7 @@ export async function runSimulation(options) {
|
|
|
332
342
|
baseUrl: options.client.baseUrl,
|
|
333
343
|
});
|
|
334
344
|
try {
|
|
335
|
-
const scenarioDigest = digest(
|
|
345
|
+
const scenarioDigest = digest(definitionIdentity(definition));
|
|
336
346
|
let attempt = await store.read("active-attempt");
|
|
337
347
|
if (attempt && attempt.stage !== "completed" && attempt.scenarioDigest !== scenarioDigest)
|
|
338
348
|
throw new Error("Recover the unfinished simulation before running a changed scenario");
|
|
@@ -346,15 +356,15 @@ export async function runSimulation(options) {
|
|
|
346
356
|
}
|
|
347
357
|
let bindings = options.localScorers ?? [];
|
|
348
358
|
if (!attempt.experimentId) {
|
|
349
|
-
const resolved = await resolveExperiment(options, attempt.idempotencyKey);
|
|
359
|
+
const resolved = await resolveExperiment(options, definition, attempt.idempotencyKey);
|
|
350
360
|
attempt.experimentId = resolved.experimentId;
|
|
351
361
|
bindings = resolved.bindings;
|
|
352
362
|
attempt.stage = "running";
|
|
353
363
|
await store.write("active-attempt", attempt);
|
|
354
364
|
}
|
|
355
|
-
else if (
|
|
365
|
+
else if (definition.kind === "repository") {
|
|
356
366
|
bindings = [
|
|
357
|
-
...
|
|
367
|
+
...definition.scorers
|
|
358
368
|
.filter((item) => "definition" in item.scorer)
|
|
359
369
|
.map((item) => item.scorer),
|
|
360
370
|
...(options.localScorers ?? []),
|
package/dist/evals.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { EvaluationClientOptions } from "./evals/client.js";
|
|
|
3
3
|
export { runExperiment, rescore, UncertainExecutionError, OutcomeSerializationError, TargetCancelledError, TargetOutcomeUncertainError, } from "./evals/runner.js";
|
|
4
4
|
export type { RunExperimentOptions, RescoreOptions, RunnerReport } from "./evals/runner.js";
|
|
5
5
|
export { runSimulation } from "./evals/simulation.js";
|
|
6
|
-
export type { RepositorySimulationCase, RepositorySimulationScorer, RunSimulationOptions, SimulationProgress, SimulationReport, SimulationScenario, SimulationTargetContext, } from "./evals/simulation.js";
|
|
6
|
+
export type { RepositorySimulationCase, RepositorySimulationScorer, RunSimulationOptions, SimulationDefinition, SimulationProgress, SimulationReport, SimulationScenario, SimulationTargetContext, } from "./evals/simulation.js";
|
|
7
7
|
export { actualAgentManifestV2, agentManifestDigestV2, attemptBaselineV2, attemptBindingRead, attemptConnectionBundleV2, attemptIdentityV2, dependencyManifestV2, dependencyProviderV2, expectedAgentManifestV2, executionManifestDigestV2, parityEvidenceV2, preflightFindingV2, preflightReportV2, prepareAttemptInputV2, projectMcpConnectionV2, secretFreeBindingV2, surfaceBindingV2, } from "./evals/attempt.js";
|
|
8
8
|
export type { ActualAgentManifestInputV2, ActualAgentManifestV2, AttemptBaselineV2, AttemptBindingRead, AttemptConnectionBundleV2, AttemptIdentityV2, DependencyManifestV2, DependencyProviderV2, ExpectedAgentManifestV2, ParityEvidenceV2, PreflightFindingV2, PreflightReportV2, PrepareAttemptIncompleteV2, PrepareAttemptInputV2, PrepareAttemptReadyV2, PrepareAttemptRequestV2, PrepareAttemptResultV2, RefreshAttemptResultV2, RequestedAttemptProviderV2, RevokeAttemptResult, SurfaceBindingV2, } from "./evals/attempt.js";
|
|
9
9
|
export { builtins, defineLocalScorer, scoreLocally } from "./evals/scorers.js";
|
package/dist/receipt.js
CHANGED
|
@@ -31,7 +31,7 @@ function validId(value, length) {
|
|
|
31
31
|
function record(value) {
|
|
32
32
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
33
33
|
}
|
|
34
|
-
function parseReceipt(value, traceId, expected, origin) {
|
|
34
|
+
function parseReceipt(value, traceId, expected, origin, setup = false) {
|
|
35
35
|
if (!record(value) ||
|
|
36
36
|
value.traceId !== traceId ||
|
|
37
37
|
!Number.isSafeInteger(value.spanCount) ||
|
|
@@ -52,6 +52,24 @@ function parseReceipt(value, traceId, expected, origin) {
|
|
|
52
52
|
}
|
|
53
53
|
if (traceUrl.origin !== origin || traceUrl.username || traceUrl.password)
|
|
54
54
|
invalidResponse();
|
|
55
|
+
if (setup) {
|
|
56
|
+
const uuid = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/u;
|
|
57
|
+
if (traceUrl.href !== value.traceUrl ||
|
|
58
|
+
traceUrl.hash ||
|
|
59
|
+
!traceUrl.pathname.startsWith("/traces/") ||
|
|
60
|
+
!uuid.test(traceUrl.pathname.slice(8)))
|
|
61
|
+
invalidResponse();
|
|
62
|
+
const queryNames = [];
|
|
63
|
+
traceUrl.searchParams.forEach((value, name) => {
|
|
64
|
+
if (!["projectId", "organizationId"].includes(name) ||
|
|
65
|
+
queryNames.includes(name) ||
|
|
66
|
+
!uuid.test(value))
|
|
67
|
+
invalidResponse();
|
|
68
|
+
queryNames.push(name);
|
|
69
|
+
});
|
|
70
|
+
if (queryNames.length !== 2)
|
|
71
|
+
invalidResponse();
|
|
72
|
+
}
|
|
55
73
|
const matched = value.matchedSpanIds, missing = value.missingSpanIds;
|
|
56
74
|
if (!Array.isArray(matched) ||
|
|
57
75
|
!Array.isArray(missing) ||
|
|
@@ -128,6 +146,13 @@ async function pause(milliseconds, signal) {
|
|
|
128
146
|
}
|
|
129
147
|
/** Observe persisted evidence after the application and its exporter have finished. */
|
|
130
148
|
export async function verifyTrace(connection, traceId, options = {}) {
|
|
149
|
+
return verifyTraceAtPath(connection, traceId, options, false, fetch);
|
|
150
|
+
}
|
|
151
|
+
/** @internal Dedicated receipt path for setup credentials; ordinary clients stay unchanged. */
|
|
152
|
+
export async function verifySetupTrace(connection, traceId, options, fetcher, signal) {
|
|
153
|
+
return verifyTraceAtPath(connection, traceId, options, true, fetcher, signal);
|
|
154
|
+
}
|
|
155
|
+
async function verifyTraceAtPath(connection, traceId, options, setup, fetcher, signal) {
|
|
131
156
|
if (!validId(traceId, 32))
|
|
132
157
|
throw new TypeError("traceId must be a nonzero lowercase 32-character OpenTelemetry trace ID");
|
|
133
158
|
if (options === null || typeof options !== "object" || Array.isArray(options))
|
|
@@ -148,27 +173,28 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
148
173
|
throw new TypeError("timeoutMillis must be greater than zero and at most 60000");
|
|
149
174
|
// Snapshot caller arrays so concurrent mutation cannot alter the verification criteria.
|
|
150
175
|
const expectedIds = [...expected], requiredFields = [...required];
|
|
151
|
-
const url = new URL(`/api/v1/traces/${traceId}/receipt`, connection.baseUrl);
|
|
176
|
+
const url = new URL(`/api/v1/${setup ? "setup/" : ""}traces/${traceId}/receipt`, connection.baseUrl);
|
|
152
177
|
for (const id of expectedIds)
|
|
153
178
|
url.searchParams.append("expectedSpanId", id);
|
|
154
179
|
const controller = new AbortController();
|
|
155
180
|
const deadline = performance.now() + timeout;
|
|
156
181
|
const timer = setTimeout(() => controller.abort(), timeout);
|
|
182
|
+
const requestSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
157
183
|
let receipt = null;
|
|
158
184
|
let delay = 250;
|
|
159
185
|
try {
|
|
160
|
-
while (!
|
|
161
|
-
const response = await
|
|
186
|
+
while (!requestSignal.aborted && performance.now() < deadline) {
|
|
187
|
+
const response = await fetcher(url, {
|
|
162
188
|
headers: { Authorization: `Bearer ${connection.apiKey}`, Accept: "application/json" },
|
|
163
189
|
redirect: "manual",
|
|
164
190
|
credentials: "omit",
|
|
165
191
|
cache: "no-store",
|
|
166
|
-
signal:
|
|
192
|
+
signal: requestSignal,
|
|
167
193
|
});
|
|
168
194
|
let retryAfter = 0;
|
|
169
195
|
if (response.status === 200) {
|
|
170
|
-
receipt = parseReceipt(await readJson(response), traceId, expectedIds, url.origin);
|
|
171
|
-
if (!
|
|
196
|
+
receipt = parseReceipt(await readJson(response), traceId, expectedIds, url.origin, setup);
|
|
197
|
+
if (!requestSignal.aborted &&
|
|
172
198
|
performance.now() < deadline &&
|
|
173
199
|
receipt.missingSpanIds.length === 0 &&
|
|
174
200
|
requiredFields.every((field) => receipt.fields[field]))
|
|
@@ -195,7 +221,7 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
195
221
|
if (remaining <= 0)
|
|
196
222
|
break;
|
|
197
223
|
const wait = Math.max(delay, retryAfter);
|
|
198
|
-
await pause(Math.min(wait, remaining),
|
|
224
|
+
await pause(Math.min(wait, remaining), requestSignal);
|
|
199
225
|
// A truncated backoff exhausts this call even if a timer wakes just early.
|
|
200
226
|
if (wait >= remaining)
|
|
201
227
|
break;
|
|
@@ -203,6 +229,8 @@ export async function verifyTrace(connection, traceId, options = {}) {
|
|
|
203
229
|
}
|
|
204
230
|
}
|
|
205
231
|
catch (error) {
|
|
232
|
+
if (signal?.aborted)
|
|
233
|
+
throw new Error("Setup interrupted");
|
|
206
234
|
if (!controller.signal.aborted && performance.now() < deadline) {
|
|
207
235
|
if (error instanceof HueTraceVerificationError)
|
|
208
236
|
throw error;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { SetupFileChange } from "./configure.js";
|
|
2
|
+
import { type FileSetupInstallationStore, type SetupInstallationRecord, type SetupStoredApplicationEvidence } from "./installation.js";
|
|
3
|
+
import type { SetupProjectDetection } from "./types.js";
|
|
4
|
+
/** Closed automatic application matrix; other projects require an explicit agent-owned integration. */
|
|
5
|
+
export type SetupApplicationPlan = {
|
|
6
|
+
/** JavaScript or TypeScript application using the shared TypeScript SDK. */
|
|
7
|
+
language: "typescript";
|
|
8
|
+
/** Package manager selected from the project's manifest and lockfile. */
|
|
9
|
+
manager: "bun" | "npm";
|
|
10
|
+
/** Recognized HTTP framework. */
|
|
11
|
+
framework: "express";
|
|
12
|
+
/** Runtime named by the recognized start script; defaults to Node when omitted. */
|
|
13
|
+
runtime?: "node" | "bun";
|
|
14
|
+
/** Whether the start script explicitly enables Node source maps. */
|
|
15
|
+
sourceMaps?: boolean;
|
|
16
|
+
/** Validated project-relative application entrypoint. */
|
|
17
|
+
entrypoint: string;
|
|
18
|
+
/** One literal loopback HTTP pathname selected for the application request. */
|
|
19
|
+
requestPath: string;
|
|
20
|
+
/** SHA-256 of the source inspected during planning. */
|
|
21
|
+
entryDigest: string;
|
|
22
|
+
} | {
|
|
23
|
+
/** Python application using the Python SDK. */
|
|
24
|
+
language: "python";
|
|
25
|
+
/** Environment manager for the supported Python application. */
|
|
26
|
+
manager: "uv";
|
|
27
|
+
/** Recognized HTTP framework. */
|
|
28
|
+
framework: "flask";
|
|
29
|
+
/** Supported project-relative Flask entrypoint. */
|
|
30
|
+
entrypoint: "app.py";
|
|
31
|
+
/** One literal loopback HTTP pathname selected for the application request. */
|
|
32
|
+
requestPath: string;
|
|
33
|
+
/** SHA-256 of the source inspected during planning. */
|
|
34
|
+
entryDigest: string;
|
|
35
|
+
};
|
|
36
|
+
/** Stable reason an automatic application integration is not safe. */
|
|
37
|
+
export declare class SetupApplicationActionRequired extends Error {
|
|
38
|
+
/** Stable reason the caller must resolve before automatic integration continues. */
|
|
39
|
+
readonly code: "ambiguous-project" | "unsupported-manager" | "unsupported-framework" | "ambiguous-entrypoint" | "custom-instrumentation";
|
|
40
|
+
constructor(
|
|
41
|
+
/** Stable reason the caller must resolve before automatic integration continues. */
|
|
42
|
+
code: "ambiguous-project" | "unsupported-manager" | "unsupported-framework" | "ambiguous-entrypoint" | "custom-instrumentation", message: string);
|
|
43
|
+
}
|
|
44
|
+
/** Fixed argv execution boundary used for package managers and application entrypoints. */
|
|
45
|
+
export interface SetupCommand {
|
|
46
|
+
/** Fixed executable name or runtime path; never evaluated by a shell. */
|
|
47
|
+
command: string;
|
|
48
|
+
/** Explicit argument vector supplied to the executable. */
|
|
49
|
+
args: string[];
|
|
50
|
+
/** Selected application's working directory. */
|
|
51
|
+
cwd: string;
|
|
52
|
+
/** Optional child environment; values are never included in command output. */
|
|
53
|
+
env?: NodeJS.ProcessEnv;
|
|
54
|
+
/** Maximum elapsed execution time before termination. */
|
|
55
|
+
timeoutMillis: number;
|
|
56
|
+
}
|
|
57
|
+
/** Executes a fixed argv without a shell and returns only its status. */
|
|
58
|
+
export type SetupCommandRunner = (command: SetupCommand) => Promise<void>;
|
|
59
|
+
/** Statically recognizes the deliberately narrow automatic matrix without executing project code. */
|
|
60
|
+
export declare function planSetupApplication(project: SetupProjectDetection): Promise<SetupApplicationPlan>;
|
|
61
|
+
/** Default command runner: fixed argv, no shell, bounded output and deadline. */
|
|
62
|
+
export declare function runSetupCommand(input: SetupCommand): Promise<void>;
|
|
63
|
+
/** Installs the exact runtime with the detected owner of the project's manifest and lockfile. */
|
|
64
|
+
export declare function installSetupRuntime(project: SetupProjectDetection, plan: SetupApplicationPlan, runner?: SetupCommandRunner): Promise<boolean>;
|
|
65
|
+
/** Adds only two owned wiring blocks around an existing app object; business logic is untouched. */
|
|
66
|
+
export declare function wireSetupApplication(store: FileSetupInstallationStore, record: SetupInstallationRecord, plan: SetupApplicationPlan): Promise<SetupFileChange | undefined>;
|
|
67
|
+
/** Starts the existing entrypoint without a shell and exercises one existing HTTP GET route. */
|
|
68
|
+
export declare function exerciseSetupApplication(store: FileSetupInstallationStore, record: SetupInstallationRecord, plan: SetupApplicationPlan, signal?: AbortSignal, deadlines?: {
|
|
69
|
+
readinessMillis: number;
|
|
70
|
+
requestMillis: number;
|
|
71
|
+
evidenceMillis: number;
|
|
72
|
+
/** Internal loopback test seam; the public CLI always chooses a random port. */
|
|
73
|
+
port?: number;
|
|
74
|
+
}): Promise<SetupStoredApplicationEvidence>;
|