@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.
Files changed (46) hide show
  1. package/CLI.md +270 -47
  2. package/ENVIRONMENTS.md +11 -1
  3. package/README.md +19 -3
  4. package/dist/client.d.ts +5 -5
  5. package/dist/client.js +13 -6
  6. package/dist/environment/tools.d.ts +6 -1
  7. package/dist/environment/tools.js +7 -1
  8. package/dist/environment/types.d.ts +6 -1
  9. package/dist/evals/simulation.d.ts +12 -4
  10. package/dist/evals/simulation.js +34 -24
  11. package/dist/evals.d.ts +1 -1
  12. package/dist/receipt.js +36 -8
  13. package/dist/setup/application.d.ts +74 -0
  14. package/dist/setup/application.js +766 -0
  15. package/dist/setup/backend.d.ts +229 -0
  16. package/dist/setup/backend.js +855 -0
  17. package/dist/setup/checkpoint.js +100 -30
  18. package/dist/setup/cli.js +20 -4
  19. package/dist/setup/configure.d.ts +13 -0
  20. package/dist/setup/configure.js +454 -0
  21. package/dist/setup/credential.d.ts +2 -0
  22. package/dist/setup/credential.js +9 -0
  23. package/dist/setup/detect.js +4 -1
  24. package/dist/setup/installation.d.ts +118 -0
  25. package/dist/setup/installation.js +605 -0
  26. package/dist/setup/lock.d.ts +2 -0
  27. package/dist/setup/lock.js +38 -0
  28. package/dist/setup/machine.d.ts +1 -10
  29. package/dist/setup/machine.js +8 -7
  30. package/dist/setup/render.d.ts +3 -1
  31. package/dist/setup/render.js +209 -6
  32. package/dist/setup/runner.d.ts +26 -76
  33. package/dist/setup/runner.js +320 -45
  34. package/dist/setup/socket.d.ts +7 -0
  35. package/dist/setup/socket.js +144 -0
  36. package/dist/setup/source.d.ts +9 -0
  37. package/dist/setup/source.js +269 -0
  38. package/dist/setup/types.d.ts +16 -9
  39. package/dist/setup/types.js +1 -1
  40. package/dist/setup.d.ts +6 -2
  41. package/dist/setup.js +3 -0
  42. package/dist/types.d.ts +24 -0
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.js +1 -1
  45. package/package.json +2 -1
  46. package/setup-events.schema.json +16 -9
@@ -0,0 +1,269 @@
1
+ import { parse } from "@babel/parser";
2
+ import { spawnSync } from "node:child_process";
3
+ function node(value) {
4
+ return value && typeof value === "object" && typeof value.type === "string"
5
+ ? value
6
+ : undefined;
7
+ }
8
+ function nodes(value) {
9
+ return Array.isArray(value) ? value.flatMap((value) => (node(value) ? [node(value)] : [])) : [];
10
+ }
11
+ function identifier(value, name) {
12
+ const item = node(value);
13
+ return item?.type === "Identifier" && item.name === name;
14
+ }
15
+ function member(value, owner, name) {
16
+ const item = node(value);
17
+ return (item?.type === "MemberExpression" &&
18
+ item.computed === false &&
19
+ identifier(item.object, owner) &&
20
+ identifier(item.property, name));
21
+ }
22
+ function walk(value, visit, parent) {
23
+ if (Array.isArray(value))
24
+ for (const item of value)
25
+ walk(item, visit, parent);
26
+ else if (node(value)) {
27
+ visit(value, parent);
28
+ for (const [key, item] of Object.entries(value))
29
+ if (key !== "loc" && key !== "comments" && key !== "tokens")
30
+ walk(item, visit, value);
31
+ }
32
+ }
33
+ /** Syntax inspection only: no application imports, evaluation, or package lifecycle. */
34
+ export function inspectExpressSource(source) {
35
+ const fail = () => new Error("Unsupported or ambiguous Express source/telemetry ownership");
36
+ const program = parse(source, { sourceType: "module", plugins: ["typescript"] }).program;
37
+ const body = program.body;
38
+ if (program.interpreter || program.directives.length)
39
+ throw fail();
40
+ const constructors = body.filter((item) => {
41
+ if (item.type !== "VariableDeclaration")
42
+ return false;
43
+ const declarations = nodes(item.declarations);
44
+ const call = node(declarations[0]?.init);
45
+ return (declarations.length === 1 &&
46
+ identifier(declarations[0]?.id, "app") &&
47
+ call?.type === "CallExpression" &&
48
+ identifier(call.callee, "express") &&
49
+ nodes(call.arguments).length === 0);
50
+ });
51
+ if (constructors.length !== 1 || constructors[0].end === undefined)
52
+ throw fail();
53
+ const imports = body.filter((item) => item.type === "ImportDeclaration");
54
+ if (!imports.some((item) => node(item.source)?.value === "express" &&
55
+ nodes(item.specifiers).some((specifier) => specifier.type === "ImportDefaultSpecifier" && identifier(specifier.local, "express"))))
56
+ throw fail();
57
+ for (const item of imports) {
58
+ const source = node(item.source)?.value;
59
+ if (source === "@opentelemetry/api" &&
60
+ nodes(item.specifiers).some((specifier) => specifier.type !== "ImportSpecifier" ||
61
+ !["trace", "SpanKind"].some((name) => identifier(specifier.imported, name))))
62
+ throw fail();
63
+ // Unknown bootstraps/import graphs could initialize a competing context manager.
64
+ if (typeof source !== "string" ||
65
+ !(source === "express" ||
66
+ source === "@opentelemetry/api" ||
67
+ ["node:fs", "node:fs/promises", "node:timers/promises", "node:stream"].includes(source)))
68
+ throw fail();
69
+ }
70
+ let routeCalls = 0;
71
+ let listenerCalls = 0;
72
+ walk(program, (item, parent) => {
73
+ // The one setup request traverses a private local proxy. Apps inspecting
74
+ // socket identity need manual integration so setup cannot alter their input.
75
+ if (["MemberExpression", "OptionalMemberExpression", "ObjectProperty"].includes(item.type) &&
76
+ ["socket", "connection"].some((name) => identifier(item.property ?? item.key, name) ||
77
+ node(item.property ?? item.key)?.value === name))
78
+ throw fail();
79
+ if (["process", "Number"].some((name) => identifier(item, name)) &&
80
+ ((parent?.type === "VariableDeclarator" && parent.id === item) ||
81
+ ((parent?.type === "FunctionDeclaration" || parent?.type === "ClassDeclaration") &&
82
+ parent.id === item) ||
83
+ (Array.isArray(parent?.params) && parent.params.includes(item)) ||
84
+ (parent?.type.endsWith("Specifier") && parent.local === item) ||
85
+ (parent?.type === "ObjectProperty" && parent.value === item) ||
86
+ parent?.type === "ArrayPattern" ||
87
+ parent?.type === "RestElement" ||
88
+ (parent?.type === "AssignmentPattern" && parent.left === item) ||
89
+ (parent?.type === "AssignmentExpression" && parent.left === item) ||
90
+ (parent?.type === "UpdateExpression" && parent.argument === item)))
91
+ throw fail();
92
+ if (identifier(item, "app") &&
93
+ !((parent?.type === "VariableDeclarator" &&
94
+ parent.id === item &&
95
+ node(parent.init)?.type === "CallExpression" &&
96
+ identifier(node(parent.init)?.callee, "express")) ||
97
+ (parent?.type === "MemberExpression" && parent.object === item)))
98
+ throw fail();
99
+ if (item.type === "MemberExpression" &&
100
+ identifier(item.object, "app") &&
101
+ !(parent?.type === "CallExpression" &&
102
+ parent.callee === item &&
103
+ ["get", "listen"].some((name) => member(item, "app", name))))
104
+ throw fail();
105
+ if (item.type === "ExportAllDeclaration" ||
106
+ (item.type === "ExportNamedDeclaration" && item.source) ||
107
+ item.type === "TSImportEqualsDeclaration")
108
+ throw fail();
109
+ if ((item.type === "AssignmentExpression" && identifier(item.left, "app")) ||
110
+ (item.type === "UpdateExpression" && identifier(item.argument, "app")))
111
+ throw fail();
112
+ if (item.type === "CallExpression" &&
113
+ node(item.callee)?.type === "MemberExpression" &&
114
+ identifier(node(item.callee)?.object, "app") &&
115
+ !["get", "listen"].some((name) => member(item.callee, "app", name)))
116
+ throw fail();
117
+ if (item.type === "CallExpression" && member(item.callee, "app", "get"))
118
+ routeCalls++;
119
+ if (item.type === "CallExpression" && member(item.callee, "app", "listen"))
120
+ listenerCalls++;
121
+ if (item.type === "ImportExpression" ||
122
+ (item.type === "CallExpression" &&
123
+ (node(item.callee)?.type === "Import" ||
124
+ identifier(item.callee, "require") ||
125
+ identifier(item.callee, "eval"))))
126
+ throw fail();
127
+ if (item.type === "MemberExpression" &&
128
+ ["setGlobalContextManager", "disable", "register"].some((name) => identifier(item.property, name)))
129
+ throw fail();
130
+ });
131
+ const routes = body
132
+ .filter((item) => item.type === "ExpressionStatement")
133
+ .map((item) => node(item.expression))
134
+ .filter((item) => item?.type === "CallExpression" && member(item.callee, "app", "get"));
135
+ if (routes.length !== 1 || routeCalls !== 1 || routes[0].start <= constructors[0].end)
136
+ throw fail();
137
+ const arguments_ = nodes(routes[0].arguments);
138
+ if (arguments_.length !== 2 ||
139
+ arguments_[0].type !== "StringLiteral" ||
140
+ typeof arguments_[0].value !== "string")
141
+ throw fail();
142
+ const raw = arguments_[0].extra?.raw;
143
+ if (raw !== JSON.stringify(arguments_[0].value) && raw !== `'${arguments_[0].value}'`)
144
+ throw fail();
145
+ const handler = arguments_[1];
146
+ if (!["FunctionExpression", "ArrowFunctionExpression"].includes(handler.type) &&
147
+ !(handler.type === "Identifier" &&
148
+ body.some((item) => item.type === "FunctionDeclaration" && identifier(item.id, String(handler.name)))))
149
+ throw fail();
150
+ const listeners = body
151
+ .filter((item) => item.type === "ExpressionStatement")
152
+ .map((item) => node(item.expression))
153
+ .filter((item) => item?.type === "CallExpression" && member(item.callee, "app", "listen"));
154
+ if (listeners.length !== 1 || listenerCalls !== 1 || listeners[0].start <= routes[0].end)
155
+ throw fail();
156
+ const listenerArguments = nodes(listeners[0].arguments);
157
+ const portArgument = listenerArguments[0];
158
+ const port = portArgument?.type === "CallExpression" && identifier(portArgument.callee, "Number")
159
+ ? nodes(portArgument.arguments).length === 1
160
+ ? nodes(portArgument.arguments)[0]
161
+ : undefined
162
+ : portArgument;
163
+ if (listenerArguments.length !== 2 ||
164
+ port?.type !== "MemberExpression" ||
165
+ port.computed !== false ||
166
+ !member(port.object, "process", "env") ||
167
+ !identifier(port.property, "PORT") ||
168
+ listenerArguments[1]?.type !== "StringLiteral" ||
169
+ listenerArguments[1].value !== "127.0.0.1")
170
+ throw fail();
171
+ return {
172
+ constructorEnd: constructors[0].end,
173
+ importOffset: 0,
174
+ requestPath: arguments_[0].value,
175
+ };
176
+ }
177
+ const PYTHON_INSPECT = String.raw `
178
+ import ast, json, sys
179
+ source = sys.stdin.read()
180
+ tree = ast.parse(source)
181
+ compile(source, '<setup-static-inspection>', 'exec')
182
+ parents = {child: parent for parent in ast.walk(tree) for child in ast.iter_child_nodes(parent)}
183
+ lines = source.splitlines(keepends=True)
184
+ def endline(item):
185
+ return sum(len(line.encode('utf-8')) for line in lines[:item.end_lineno])
186
+ def name(item, value):
187
+ return isinstance(item, ast.Name) and item.id == value
188
+ def member(item, owner, attr):
189
+ return isinstance(item, ast.Attribute) and name(item.value, owner) and item.attr == attr
190
+ constructors = [item for item in tree.body if isinstance(item, ast.Assign) and len(item.targets) == 1 and name(item.targets[0], 'app') and isinstance(item.value, ast.Call) and name(item.value.func, 'Flask') and len(item.value.args) == 1 and name(item.value.args[0], '__name__') and not item.value.keywords]
191
+ assert len(constructors) == 1
192
+ for item in ast.walk(tree):
193
+ parent = parents.get(item)
194
+ if isinstance(item, ast.Attribute) and item.attr in ('socket', 'connection'):
195
+ raise AssertionError('Socket-aware applications require manual integration')
196
+ if isinstance(item, ast.Constant) and item.value == 'werkzeug.socket':
197
+ raise AssertionError('Socket-aware applications require manual integration')
198
+ if isinstance(item, ast.Name) and item.id in ('int', 'os') and isinstance(item.ctx, ast.Store):
199
+ raise AssertionError('Custom port conversion requires manual integration')
200
+ if isinstance(item, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) and item.name in ('int', 'os'):
201
+ raise AssertionError('Custom port conversion requires manual integration')
202
+ if isinstance(item, ast.arg) and item.arg in ('int', 'os'):
203
+ raise AssertionError('Custom port conversion requires manual integration')
204
+ if isinstance(item, ast.alias):
205
+ bound = item.asname or item.name.split('.')[0]
206
+ if bound == 'int' or (bound == 'os' and not (isinstance(parent, ast.Import) and item.name == 'os')):
207
+ raise AssertionError('Custom port conversion requires manual integration')
208
+ if name(item, 'app'):
209
+ assert (parent is constructors[0] and item in parent.targets) or (isinstance(parent, ast.Attribute) and parent.value is item)
210
+ if isinstance(item, ast.Attribute) and name(item.value, 'app'):
211
+ assert isinstance(parent, ast.Call) and parent.func is item and item.attr in ('get', 'run')
212
+ # Insertion after a statement's line must not cross another semicolon statement.
213
+ assert all(left.end_lineno < right.lineno for left, right in zip(tree.body, tree.body[1:]))
214
+ assert any(isinstance(item, ast.ImportFrom) and item.module == 'flask' and any(alias.name == 'Flask' and alias.asname in (None, 'Flask') for alias in item.names) for item in tree.body)
215
+ routes = [(item, decorator) for item in tree.body if isinstance(item, ast.FunctionDef) for decorator in item.decorator_list if isinstance(decorator, ast.Call) and member(decorator.func, 'app', 'get')]
216
+ all_routes = [item for item in ast.walk(tree) if isinstance(item, ast.Call) and member(item.func, 'app', 'get')]
217
+ assert len(routes) == len(all_routes) == 1
218
+ handler, route = routes[0]
219
+ assert handler.decorator_list == [route]
220
+ assert handler.lineno > constructors[0].end_lineno and len(route.args) == 1 and not route.keywords and isinstance(route.args[0], ast.Constant) and isinstance(route.args[0].value, str)
221
+ assert ast.get_source_segment(source, route.args[0]) in (json.dumps(route.args[0].value), "'" + route.args[0].value + "'")
222
+ listeners = [item for item in ast.walk(tree) if isinstance(item, ast.Call) and member(item.func, 'app', 'run')]
223
+ assert len(listeners) == 1
224
+ listener = listeners[0]
225
+ statement = parents.get(listener)
226
+ assert isinstance(statement, ast.Expr) and statement.value is listener and statement.lineno > handler.end_lineno
227
+ owner = parents.get(statement)
228
+ if owner is not tree:
229
+ assert isinstance(owner, ast.If) and parents.get(owner) is tree and owner.body == [statement] and not owner.orelse
230
+ condition = owner.test
231
+ assert isinstance(condition, ast.Compare) and name(condition.left, '__name__') and len(condition.ops) == 1 and isinstance(condition.ops[0], ast.Eq) and len(condition.comparators) == 1 and isinstance(condition.comparators[0], ast.Constant) and condition.comparators[0].value == '__main__'
232
+ assert not listener.args and all(keyword.arg in ('host', 'port') for keyword in listener.keywords)
233
+ keywords = {keyword.arg: keyword.value for keyword in listener.keywords}
234
+ assert len(keywords) == len(listener.keywords) and 'port' in keywords
235
+ if 'host' in keywords:
236
+ assert isinstance(keywords['host'], ast.Constant) and keywords['host'].value == '127.0.0.1'
237
+ port = keywords['port']
238
+ assert isinstance(port, ast.Call) and name(port.func, 'int') and len(port.args) == 1 and not port.keywords
239
+ value = port.args[0]
240
+ assert isinstance(value, ast.Subscript) and member(value.value, 'os', 'environ') and isinstance(value.slice, ast.Constant) and value.slice.value == 'PORT'
241
+ assert any(isinstance(item, ast.Import) and any(alias.name == 'os' and alias.asname in (None, 'os') for alias in item.names) for item in tree.body)
242
+ index = 0
243
+ if tree.body and isinstance(tree.body[0], ast.Expr) and isinstance(tree.body[0].value, ast.Constant) and isinstance(tree.body[0].value.value, str): index = 1
244
+ while index < len(tree.body) and isinstance(tree.body[index], ast.ImportFrom) and tree.body[index].module == '__future__': index += 1
245
+ offset = endline(tree.body[index - 1]) if index else sum(len(line.encode('utf-8')) for line in lines[:tree.body[0].lineno - 1])
246
+ print(json.dumps(dict(constructorEnd=endline(constructors[0]), importOffset=offset, requestPath=route.args[0].value)))
247
+ `;
248
+ /** An isolated stdlib parser never imports/executes the customer's Python module. */
249
+ export function inspectFlaskSource(source) {
250
+ const result = spawnSync("python3", ["-I", "-B", "-S", "-c", PYTHON_INSPECT], {
251
+ input: source,
252
+ encoding: "utf8",
253
+ timeout: 5000,
254
+ maxBuffer: 8192,
255
+ shell: false,
256
+ });
257
+ if (result.status !== 0)
258
+ throw new Error("An isolated Python 3 parser and unambiguous Flask source are required");
259
+ const value = JSON.parse(result.stdout);
260
+ const bytes = Buffer.from(source);
261
+ for (const field of ["constructorEnd", "importOffset"]) {
262
+ if (!Number.isInteger(value[field]) || value[field] < 0 || value[field] > bytes.length)
263
+ throw new Error("Invalid Python source position");
264
+ value[field] = bytes.subarray(0, value[field]).toString("utf8").length;
265
+ }
266
+ if (typeof value.requestPath !== "string")
267
+ throw new Error("Invalid Python route");
268
+ return value;
269
+ }
@@ -1,7 +1,7 @@
1
1
  /** Version carried by every setup JSONL event. */
2
- export declare const SETUP_EVENT_CONTRACT_VERSION: 1;
2
+ export declare const SETUP_EVENT_CONTRACT_VERSION: 2;
3
3
  /** Names in the version 1 setup event contract. */
4
- export type SetupEventName = "run.started" | "project.detected" | "plan.ready" | "step.started" | "step.completed" | "file.changed" | "diagnostic" | "action.required" | "trial.created" | "receipt.verified" | "claim.required" | "claim.completed" | "run.completed" | "run.failed";
4
+ export type SetupEventName = "run.started" | "project.detected" | "plan.ready" | "step.started" | "step.completed" | "file.changed" | "diagnostic" | "privacy.notice" | "action.required" | "trial.created" | "receipt.verified" | "claim.required" | "claim.completed" | "run.completed" | "run.failed";
5
5
  interface EventBase<Name extends SetupEventName> {
6
6
  /** JSONL contract version, independent of the package version. */
7
7
  contractVersion: typeof SETUP_EVENT_CONTRACT_VERSION;
@@ -48,7 +48,7 @@ export interface ProjectDetectedEvent extends EventBase<"project.detected"> {
48
48
  /** A bounded local setup plan. */
49
49
  export interface SetupPlan {
50
50
  /** Ordered setup step names. */
51
- steps: Array<"detect-project" | "configure-telemetry" | "verify-receipt" | "claim-project">;
51
+ steps: Array<"detect-project" | "install-runtime" | "configure-telemetry" | "verify-application-receipt" | "claim-project">;
52
52
  /** Whether this plan is permitted to change project files. */
53
53
  mutatesProject: boolean;
54
54
  /** Whether completion ultimately requires a backend adapter. */
@@ -87,16 +87,23 @@ export interface DiagnosticEvent extends EventBase<"diagnostic"> {
87
87
  /** Secret-free human-readable explanation. */
88
88
  message: string;
89
89
  }
90
+ /** Published, non-blocking privacy and security disclosure shown before telemetry. */
91
+ export interface PrivacyNoticeEvent extends EventBase<"privacy.notice"> {
92
+ /** Canonical privacy notice URL supplied by setup preflight. */
93
+ privacyUrl: "https://hue.run/privacy";
94
+ /** Published effective date supplied by setup preflight. */
95
+ effectiveDate: "2026-08-24";
96
+ /** Canonical security information URL supplied by setup preflight. */
97
+ securityUrl: "https://trust.hue.run/";
98
+ }
90
99
  /** Progress needs an explicit local or human action. */
91
100
  export interface ActionRequiredEvent extends EventBase<"action.required"> {
92
101
  /** Kind of action needed to continue. */
93
- action: "claim-project" | "configure" | "run-instrumented-request" | "open-claim-url" | "capture-approved-content" | "review-content-approved-trace";
102
+ action: "claim-project" | "configure" | "select-project" | "integrate-application" | "run-instrumented-request" | "open-claim-handoff" | "restart-claim-handoff";
94
103
  /** Secret-free explanation of the action. */
95
104
  message: string;
96
105
  /** Optional command the caller may run. */
97
106
  command?: string;
98
- /** Optional HTTPS destination for a user action. */
99
- url?: string;
100
107
  }
101
108
  /** A backend adapter created an anonymous trial. */
102
109
  export interface TrialCreatedEvent extends EventBase<"trial.created"> {
@@ -111,13 +118,13 @@ export interface ReceiptVerifiedEvent extends EventBase<"receipt.verified"> {
111
118
  receiptId: string;
112
119
  /** Verified lowercase OpenTelemetry trace identifier; this does not prove content approval. */
113
120
  traceId: string;
121
+ /** Repository-owned runtime boundary that produced the verified trace. */
122
+ source: "repository-http-boundary";
114
123
  }
115
124
  /** The anonymous project can be claimed by a person. */
116
125
  export interface ClaimRequiredEvent extends EventBase<"claim.required"> {
117
126
  /** Non-secret claim identifier. */
118
127
  claimId: string;
119
- /** User-facing claim destination; never persisted in setup checkpoints. */
120
- url: string;
121
128
  }
122
129
  /** A backend adapter confirmed that the project was claimed. */
123
130
  export interface ClaimCompletedEvent extends EventBase<"claim.completed"> {
@@ -141,5 +148,5 @@ export interface RunFailedEvent extends EventBase<"run.failed"> {
141
148
  resumable: boolean;
142
149
  }
143
150
  /** Version 1 setup JSONL event union. */
144
- export type SetupEvent = RunStartedEvent | ProjectDetectedEvent | PlanReadyEvent | StepStartedEvent | StepCompletedEvent | FileChangedEvent | DiagnosticEvent | ActionRequiredEvent | TrialCreatedEvent | ReceiptVerifiedEvent | ClaimRequiredEvent | ClaimCompletedEvent | RunCompletedEvent | RunFailedEvent;
151
+ export type SetupEvent = RunStartedEvent | ProjectDetectedEvent | PlanReadyEvent | StepStartedEvent | StepCompletedEvent | FileChangedEvent | DiagnosticEvent | PrivacyNoticeEvent | ActionRequiredEvent | TrialCreatedEvent | ReceiptVerifiedEvent | ClaimRequiredEvent | ClaimCompletedEvent | RunCompletedEvent | RunFailedEvent;
145
152
  export {};
@@ -1,2 +1,2 @@
1
1
  /** Version carried by every setup JSONL event. */
2
- export const SETUP_EVENT_CONTRACT_VERSION = 1;
2
+ export const SETUP_EVENT_CONTRACT_VERSION = 2;
package/dist/setup.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  /** Local, resumable installer setup-session contracts used by the `hue` command. */
2
- export { SETUP_EVENT_CONTRACT_VERSION, type ActionRequiredEvent, type ClaimCompletedEvent, type ClaimRequiredEvent, type DiagnosticEvent, type FileChangedEvent, type PlanReadyEvent, type ProjectDetectedEvent, type ReceiptVerifiedEvent, type RunCompletedEvent, type RunFailedEvent, type RunStartedEvent, type SetupEvent, type SetupEventName, type SetupPlan, type SetupProjectDetection, type StepCompletedEvent, type StepStartedEvent, type TrialCreatedEvent, } from "./setup/types.js";
2
+ export { SETUP_EVENT_CONTRACT_VERSION, type ActionRequiredEvent, type ClaimCompletedEvent, type ClaimRequiredEvent, type DiagnosticEvent, type FileChangedEvent, type PlanReadyEvent, type PrivacyNoticeEvent, type ProjectDetectedEvent, type ReceiptVerifiedEvent, type RunCompletedEvent, type RunFailedEvent, type RunStartedEvent, type SetupEvent, type SetupEventName, type SetupPlan, type SetupProjectDetection, type StepCompletedEvent, type StepStartedEvent, type TrialCreatedEvent, } from "./setup/types.js";
3
3
  export { createInitialSetupState, transitionSetup, type SetupEffect, type SetupMachineInput, type SetupMachineState, type SetupTransition, } from "./setup/machine.js";
4
- export { runSetup, type SetupBackendAdapter, type SetupBackendClaim, type SetupBackendReceipt, type SetupBackendTrial, type SetupCheckpointAdapter, type SetupProjectAdapter, type SetupRunOptions, type SetupRunResult, } from "./setup/runner.js";
4
+ export { runSetup, type SetupBackendOperations, type SetupCheckpointAdapter, type SetupProjectAdapter, type SetupRunOptions, type SetupRunResult, } from "./setup/runner.js";
5
+ export { SetupBackendAdapter, SetupBackendError, type SetupBackendAdapterOptions, type SetupBackendClaim, type SetupApplicationEvidence, type SetupBackendReceipt, type SetupBackendTrial, type SetupCredentialResult, type SetupInstallationStatus, type SetupPreflightStatus, type SetupClaimHandoff, type SetupProbeEvidence, } from "./setup/backend.js";
6
+ export { SetupApplicationActionRequired, installSetupRuntime, planSetupApplication, runSetupCommand, wireSetupApplication, type SetupApplicationPlan, type SetupCommand, type SetupCommandRunner, } from "./setup/application.js";
7
+ export { type SetupFileChange } from "./setup/configure.js";
8
+ export { FileSetupInstallationStore, type SetupInstallationRecord, type SetupStoredCredential, type SetupStoredApplicationEvidence, type SetupStoredApplicationAttempt, type SetupStoredClaimHandoff, type SetupStoredProbe, } from "./setup/installation.js";
5
9
  export { detectSetupProject } from "./setup/detect.js";
6
10
  export { renderHumanEvent, renderJsonlEvent, renderPlainEvent, selectSetupOutputMode, type SetupOutputMode, } from "./setup/render.js";
package/dist/setup.js CHANGED
@@ -2,5 +2,8 @@
2
2
  export { SETUP_EVENT_CONTRACT_VERSION, } from "./setup/types.js";
3
3
  export { createInitialSetupState, transitionSetup, } from "./setup/machine.js";
4
4
  export { runSetup, } from "./setup/runner.js";
5
+ export { SetupBackendAdapter, SetupBackendError, } from "./setup/backend.js";
6
+ export { SetupApplicationActionRequired, installSetupRuntime, planSetupApplication, runSetupCommand, wireSetupApplication, } from "./setup/application.js";
7
+ export { FileSetupInstallationStore, } from "./setup/installation.js";
5
8
  export { detectSetupProject } from "./setup/detect.js";
6
9
  export { renderHumanEvent, renderJsonlEvent, renderPlainEvent, selectSetupOutputMode, } from "./setup/render.js";
package/dist/types.d.ts CHANGED
@@ -138,6 +138,30 @@ export interface SpanOptions {
138
138
  /** Explicit parent context, for example from {@link HueClient.extract}. */
139
139
  parentContext?: Context;
140
140
  }
141
+ /**
142
+ * MCP `initialize` `serverInfo` for {@link HueClient.tool}. Pass
143
+ * `client.getServerVersion()` after connect; any MCP server works.
144
+ */
145
+ export interface McpServerInfo {
146
+ /** `serverInfo.name` from MCP initialize, recorded as `mcp.server.name`. */
147
+ name?: string;
148
+ /** `serverInfo.version` from MCP initialize, recorded as `mcp.server.version`. */
149
+ version?: string;
150
+ }
151
+ /**
152
+ * Options for {@link HueClient.tool}. `callId` is the provider-issued tool-call
153
+ * id; `mcp` is the MCP server that handled the call.
154
+ */
155
+ export interface ToolOptions extends Pick<SpanOptions, "parentContext"> {
156
+ /** Provider-issued identifier of this tool call, recorded as `gen_ai.tool.call.id`. */
157
+ callId?: string;
158
+ /**
159
+ * MCP server that handled this call. Pass `client.getServerVersion()` or the
160
+ * `initialize` `serverInfo` so a generic verb such as `get_thread` is attributed
161
+ * to that server rather than inferred from the tool name.
162
+ */
163
+ mcp?: McpServerInfo;
164
+ }
141
165
  /** Provider-reported token counts for {@link HueSpan.setUsage}. */
142
166
  export interface TokenUsage {
143
167
  /** Provider-reported prompt tokens (`gen_ai.usage.input_tokens`). */
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Package version shared by the instrumentation scope and the export User-Agent. */
2
- export declare const sdkVersion = "0.3.2";
2
+ export declare const sdkVersion = "0.4.2";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated by scripts/write-version.mjs from package.json; do not edit by hand.
2
2
  /** Package version shared by the instrumentation scope and the export User-Agent. */
3
- export const sdkVersion = "0.3.2";
3
+ export const sdkVersion = "0.4.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hue-run/sdk",
3
- "version": "0.3.2",
3
+ "version": "0.4.2",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -84,6 +84,7 @@
84
84
  "test": "bun test tests"
85
85
  },
86
86
  "dependencies": {
87
+ "@babel/parser": "7.29.9",
87
88
  "@opentelemetry/api-logs": "0.222.0",
88
89
  "@opentelemetry/core": "2.11.0",
89
90
  "@opentelemetry/otlp-exporter-base": "0.222.0",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://hue.run/schemas/setup-events-v1.json",
4
- "title": "Hue setup-session installer event version 1",
3
+ "$id": "https://hue.run/schemas/setup-events-v2.json",
4
+ "title": "Hue setup-session installer event version 2",
5
5
  "description": "Installer lifecycle events only. run.* events are setup-session invocations, not Hue Runs.",
6
6
  "oneOf": [
7
7
  { "$ref": "#/$defs/run.started" },
@@ -11,6 +11,7 @@
11
11
  { "$ref": "#/$defs/step.completed" },
12
12
  { "$ref": "#/$defs/file.changed" },
13
13
  { "$ref": "#/$defs/diagnostic" },
14
+ { "$ref": "#/$defs/privacy.notice" },
14
15
  { "$ref": "#/$defs/action.required" },
15
16
  { "$ref": "#/$defs/trial.created" },
16
17
  { "$ref": "#/$defs/receipt.verified" },
@@ -24,14 +25,14 @@
24
25
  "type": "object",
25
26
  "required": ["contractVersion", "event", "runId", "sequence", "timestamp"],
26
27
  "properties": {
27
- "contractVersion": { "const": 1 },
28
+ "contractVersion": { "const": 2 },
28
29
  "event": { "type": "string", "maxLength": 40 },
29
30
  "runId": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,80}$" },
30
31
  "sequence": { "type": "integer", "minimum": 1, "maximum": 10000 },
31
32
  "timestamp": { "type": "string", "format": "date-time", "maxLength": 40 }
32
33
  }
33
34
  },
34
- "step": { "enum": ["detect-project", "configure-telemetry", "verify-receipt", "claim-project"] },
35
+ "step": { "enum": ["detect-project", "install-runtime", "configure-telemetry", "verify-application-receipt", "claim-project"] },
35
36
  "text": { "type": "string", "minLength": 1, "maxLength": 1000 },
36
37
  "identifier": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,128}$" },
37
38
  "plan": {
@@ -93,26 +94,32 @@
93
94
  "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["level", "code", "message"], "properties": { "event": { "const": "diagnostic" }, "level": { "enum": ["info", "warning", "error"] }, "code": { "type": "string", "pattern": "^[a-z][a-z0-9_.-]{0,79}$" }, "message": { "$ref": "#/$defs/text" } } }],
94
95
  "unevaluatedProperties": false
95
96
  },
97
+ "privacy.notice": {
98
+ "type": "object",
99
+ "description": "Published privacy and security disclosure presented before telemetry; no acknowledgement or consent is inferred.",
100
+ "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["privacyUrl", "effectiveDate", "securityUrl"], "properties": { "event": { "const": "privacy.notice" }, "privacyUrl": { "const": "https://hue.run/privacy" }, "effectiveDate": { "const": "2026-08-24" }, "securityUrl": { "const": "https://trust.hue.run/" } } }],
101
+ "unevaluatedProperties": false
102
+ },
96
103
  "action.required": {
97
104
  "type": "object",
98
- "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["action", "message"], "properties": { "event": { "const": "action.required" }, "action": { "enum": ["claim-project", "configure", "run-instrumented-request", "open-claim-url", "capture-approved-content", "review-content-approved-trace"] }, "message": { "$ref": "#/$defs/text" }, "command": { "type": "string", "minLength": 1, "maxLength": 200 }, "url": { "type": "string", "format": "uri", "maxLength": 2048 } } }],
105
+ "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["action", "message"], "properties": { "event": { "const": "action.required" }, "action": { "enum": ["claim-project", "configure", "select-project", "integrate-application", "run-instrumented-request", "open-claim-handoff", "restart-claim-handoff"] }, "message": { "$ref": "#/$defs/text" }, "command": { "type": "string", "minLength": 1, "maxLength": 200 } } }],
99
106
  "unevaluatedProperties": false
100
107
  },
101
108
  "trial.created": {
102
109
  "type": "object",
103
- "description": "An anonymous setup trial hard-pinned to trial_metadata_v1.",
110
+ "description": "An anonymous setup trial restricted to metadata-only-v1.",
104
111
  "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["trialId", "expiresAt"], "properties": { "event": { "const": "trial.created" }, "trialId": { "$ref": "#/$defs/identifier" }, "expiresAt": { "type": "string", "format": "date-time", "maxLength": 40 } } }],
105
112
  "unevaluatedProperties": false
106
113
  },
107
114
  "receipt.verified": {
108
115
  "type": "object",
109
- "description": "Instrumentation-only proof for an anonymous trace; not evidence of content approval or Scenario suitability.",
110
- "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["receiptId", "traceId"], "properties": { "event": { "const": "receipt.verified" }, "receiptId": { "$ref": "#/$defs/identifier" }, "traceId": { "type": "string", "pattern": "^[a-f0-9]{32}$" } } }],
116
+ "description": "Exact stored trace/span evidence from the exercised repository HTTP boundary; does not establish that every application operation is instrumented.",
117
+ "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["receiptId", "traceId", "source"], "properties": { "event": { "const": "receipt.verified" }, "receiptId": { "$ref": "#/$defs/identifier" }, "traceId": { "type": "string", "pattern": "^[a-f0-9]{32}$" }, "source": { "const": "repository-http-boundary" } } }],
111
118
  "unevaluatedProperties": false
112
119
  },
113
120
  "claim.required": {
114
121
  "type": "object",
115
- "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["claimId", "url"], "properties": { "event": { "const": "claim.required" }, "claimId": { "$ref": "#/$defs/identifier" }, "url": { "type": "string", "format": "uri", "maxLength": 2048 } } }],
122
+ "allOf": [{ "$ref": "#/$defs/base" }, { "type": "object", "required": ["claimId"], "properties": { "event": { "const": "claim.required" }, "claimId": { "$ref": "#/$defs/identifier" } } }],
116
123
  "unevaluatedProperties": false
117
124
  },
118
125
  "claim.completed": {