pi-harness-runtime 1.1.385 → 1.1.387
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/index.ts +42 -24
- package/package.json +3 -2
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/dist/auto-continue.d.ts.map +1 -1
- package/packages/jev-judge/dist/auto-continue.js +26 -0
- package/packages/jev-judge/dist/auto-continue.js.map +1 -1
- package/packages/jev-judge/dist/environment-judge.d.ts.map +1 -1
- package/packages/jev-judge/dist/environment-judge.js +7 -4
- package/packages/jev-judge/dist/environment-judge.js.map +1 -1
- package/packages/jev-judge/dist/index.d.ts +87 -12
- package/packages/jev-judge/dist/index.d.ts.map +1 -1
- package/packages/jev-judge/dist/index.js +291 -75
- package/packages/jev-judge/dist/index.js.map +1 -1
- package/packages/jev-judge/dist/provider-error.d.ts +61 -0
- package/packages/jev-judge/dist/provider-error.d.ts.map +1 -0
- package/packages/jev-judge/dist/provider-error.js +165 -0
- package/packages/jev-judge/dist/provider-error.js.map +1 -0
- package/packages/jev-judge/dist/task-intake.d.ts.map +1 -1
- package/packages/jev-judge/dist/task-intake.js +3 -2
- package/packages/jev-judge/dist/task-intake.js.map +1 -1
- package/packages/jev-judge/dist/types.d.ts +56 -2
- package/packages/jev-judge/dist/types.d.ts.map +1 -1
- package/packages/jev-judge/dist/typesafe-provider.d.ts +26 -1
- package/packages/jev-judge/dist/typesafe-provider.d.ts.map +1 -1
- package/packages/jev-judge/dist/typesafe-provider.js +153 -62
- package/packages/jev-judge/dist/typesafe-provider.js.map +1 -1
- package/packages/jev-judge/dist/typesafe-request.d.ts +65 -0
- package/packages/jev-judge/dist/typesafe-request.d.ts.map +1 -0
- package/packages/jev-judge/dist/typesafe-request.js +138 -0
- package/packages/jev-judge/dist/typesafe-request.js.map +1 -0
- package/packages/jev-judge/package.json +1 -1
- package/packages/jev-judge/src/auto-continue.ts +26 -0
- package/packages/jev-judge/src/environment-judge.ts +7 -4
- package/packages/jev-judge/src/index.ts +617 -333
- package/packages/jev-judge/src/provider-error.ts +190 -0
- package/packages/jev-judge/src/task-intake.ts +3 -2
- package/packages/jev-judge/src/types.ts +62 -5
- package/packages/jev-judge/src/typesafe-provider.ts +452 -318
- package/packages/jev-judge/src/typesafe-request.ts +181 -0
- package/packages/jev-judge/tests/jev-provider-chain.test.ts +536 -0
- package/packages/jev-judge/tests/typesafe-provider.test.ts +504 -44
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/run-root-tests.mjs +207 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider errors for the Jev judge.
|
|
3
|
+
*
|
|
4
|
+
* The judge can talk to more than one provider (TypeSafe direct API first,
|
|
5
|
+
* OpenRouter as a fallback). A failure therefore carries the information the
|
|
6
|
+
* fallback logic needs: which provider failed, whether the failure was an
|
|
7
|
+
* HTTP error, and whether ANOTHER provider could still serve the request.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { JevProviderName } from "./types.js";
|
|
11
|
+
|
|
12
|
+
export interface JevProviderErrorOptions {
|
|
13
|
+
provider: JevProviderName;
|
|
14
|
+
/** HTTP status, when the failure came from an HTTP response. */
|
|
15
|
+
status?: number;
|
|
16
|
+
/**
|
|
17
|
+
* True when another provider could still serve the request.
|
|
18
|
+
*
|
|
19
|
+
* Retryable: network failures, timeouts, rate limits, auth failures and
|
|
20
|
+
* 5xx. NOT retryable: 400/422 - those are contract errors in the request
|
|
21
|
+
* we build, and falling back would hide the bug.
|
|
22
|
+
*/
|
|
23
|
+
retryable: boolean;
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Error raised by a single Jev provider call.
|
|
29
|
+
*/
|
|
30
|
+
export class JevProviderError extends Error {
|
|
31
|
+
readonly provider: JevProviderName;
|
|
32
|
+
readonly status?: number;
|
|
33
|
+
readonly retryable: boolean;
|
|
34
|
+
|
|
35
|
+
constructor(message: string, options: JevProviderErrorOptions) {
|
|
36
|
+
super(message, options.cause !== undefined ? { cause: options.cause } : undefined);
|
|
37
|
+
this.name = "JevProviderError";
|
|
38
|
+
this.provider = options.provider;
|
|
39
|
+
this.status = options.status;
|
|
40
|
+
this.retryable = options.retryable;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* HTTP statuses where another provider (or a later attempt) can still help.
|
|
46
|
+
*/
|
|
47
|
+
export function isRetryableStatus(status: number): boolean {
|
|
48
|
+
if (status === 401 || status === 402 || status === 403) return true; // auth / quota
|
|
49
|
+
if (status === 408 || status === 429) return true; // timeout / rate limit
|
|
50
|
+
if (status >= 500) return true; // server side
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extract an HTTP status from an unknown error value.
|
|
56
|
+
*/
|
|
57
|
+
export function extractStatus(error: unknown): number | undefined {
|
|
58
|
+
if (typeof error !== "object" || error === null) return undefined;
|
|
59
|
+
const status = (error as { status?: unknown }).status;
|
|
60
|
+
return typeof status === "number" ? status : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Error names of the known TRANSPORT failures: the request never reached a
|
|
65
|
+
* server that could reject it, so another provider can still serve it.
|
|
66
|
+
*/
|
|
67
|
+
const TRANSPORT_ERROR_NAMES = new Set([
|
|
68
|
+
"AbortError",
|
|
69
|
+
"TimeoutError",
|
|
70
|
+
"APIConnectionError",
|
|
71
|
+
"APIConnectionTimeoutError",
|
|
72
|
+
"APITimeoutError",
|
|
73
|
+
"FetchError",
|
|
74
|
+
"ECONNABORTED",
|
|
75
|
+
"ECONNREFUSED",
|
|
76
|
+
"ECONNRESET",
|
|
77
|
+
"EAI_AGAIN",
|
|
78
|
+
"ENOTFOUND",
|
|
79
|
+
"ETIMEDOUT",
|
|
80
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
81
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
82
|
+
"UND_ERR_SOCKET",
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
/** Error codes of the known transport failures. */
|
|
86
|
+
const TRANSPORT_ERROR_CODES = new Set([
|
|
87
|
+
"ECONNABORTED",
|
|
88
|
+
"ECONNREFUSED",
|
|
89
|
+
"ECONNRESET",
|
|
90
|
+
"EAI_AGAIN",
|
|
91
|
+
"ENETDOWN",
|
|
92
|
+
"ENETUNREACH",
|
|
93
|
+
"EHOSTDOWN",
|
|
94
|
+
"EHOSTUNREACH",
|
|
95
|
+
"ENOTFOUND",
|
|
96
|
+
"EPROTO",
|
|
97
|
+
"EPIPE",
|
|
98
|
+
"ETIMEDOUT",
|
|
99
|
+
"ERR_STREAM_PREMATURE_CLOSE",
|
|
100
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
101
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
102
|
+
"UND_ERR_SOCKET",
|
|
103
|
+
// Certificate verification failures: the endpoint is unreachable for us.
|
|
104
|
+
"CERT_HAS_EXPIRED",
|
|
105
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
106
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
107
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Prefixes of the error codes that always describe a transport failure.
|
|
112
|
+
*
|
|
113
|
+
* undici reports its own failures as UND_ERR_*, the TLS stack as ERR_TLS_*
|
|
114
|
+
* and the network stack as ECONN / EHOST / ENET.
|
|
115
|
+
*/
|
|
116
|
+
const TRANSPORT_ERROR_CODE_PREFIXES = [
|
|
117
|
+
"UND_ERR_",
|
|
118
|
+
"ERR_TLS_",
|
|
119
|
+
"ECONN",
|
|
120
|
+
"EHOST",
|
|
121
|
+
"ENET",
|
|
122
|
+
"CERT_",
|
|
123
|
+
"SELF_SIGNED_CERT_",
|
|
124
|
+
"DEPTH_ZERO_",
|
|
125
|
+
"UNABLE_TO_VERIFY_",
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* True when the error is a known transport failure (network, timeout, abort).
|
|
130
|
+
*
|
|
131
|
+
* Only these are retryable without an HTTP status: they never reached a
|
|
132
|
+
* server that could have rejected the request. Everything else without a
|
|
133
|
+
* status (a bug in our own code, a malformed response, a parse error) is NOT
|
|
134
|
+
* retryable - a contract bug must stay visible instead of being hidden behind
|
|
135
|
+
* a fallback to another provider.
|
|
136
|
+
*/
|
|
137
|
+
export function isTransportError(error: unknown): boolean {
|
|
138
|
+
if (typeof error !== "object" || error === null) return false;
|
|
139
|
+
|
|
140
|
+
const named = error as {
|
|
141
|
+
name?: unknown;
|
|
142
|
+
code?: unknown;
|
|
143
|
+
cause?: unknown;
|
|
144
|
+
errors?: unknown;
|
|
145
|
+
};
|
|
146
|
+
if (typeof named.name === "string" && TRANSPORT_ERROR_NAMES.has(named.name)) return true;
|
|
147
|
+
if (typeof named.code === "string" && isTransportCode(named.code)) return true;
|
|
148
|
+
|
|
149
|
+
// An AggregateError collects the failures of several addresses (undici
|
|
150
|
+
// tries them one after the other) - one of them being a transport
|
|
151
|
+
// failure makes the whole attempt a transport failure.
|
|
152
|
+
if (Array.isArray(named.errors) && named.errors.some((entry) => isTransportError(entry))) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Node/undici wrap the real cause (e.g. ECONNREFUSED) one level deeper.
|
|
157
|
+
if (named.cause !== undefined && named.cause !== error) {
|
|
158
|
+
return isTransportError(named.cause);
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* True when the error code describes a transport failure.
|
|
165
|
+
*/
|
|
166
|
+
function isTransportCode(code: string): boolean {
|
|
167
|
+
if (TRANSPORT_ERROR_CODES.has(code)) return true;
|
|
168
|
+
return TRANSPORT_ERROR_CODE_PREFIXES.some((prefix) => code.startsWith(prefix));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Wrap any thrown value into a JevProviderError.
|
|
173
|
+
*
|
|
174
|
+
* A failure with an HTTP status is retryable when the status says so (see
|
|
175
|
+
* `isRetryableStatus`). A failure WITHOUT a status is only retryable when it
|
|
176
|
+
* is a known transport failure (network, timeout, abort) - an unknown error
|
|
177
|
+
* is most likely a bug in our own code and must not disappear behind a
|
|
178
|
+
* fallback to another provider.
|
|
179
|
+
*/
|
|
180
|
+
export function toProviderError(error: unknown, provider: JevProviderName): JevProviderError {
|
|
181
|
+
if (error instanceof JevProviderError) return error;
|
|
182
|
+
const status = extractStatus(error);
|
|
183
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
184
|
+
return new JevProviderError(`${provider} provider failed: ${message}`, {
|
|
185
|
+
provider,
|
|
186
|
+
status,
|
|
187
|
+
retryable: status === undefined ? isTransportError(error) : isRetryableStatus(status),
|
|
188
|
+
cause: error,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
@@ -62,8 +62,9 @@ export class TaskIntakeController {
|
|
|
62
62
|
...config,
|
|
63
63
|
} as Required<TaskIntakeConfig>;
|
|
64
64
|
|
|
65
|
-
// Initialize Jev
|
|
66
|
-
|
|
65
|
+
// Initialize Jev when a provider is available (an explicit provider
|
|
66
|
+
// chain or a legacy single key)
|
|
67
|
+
if ((config.providers && config.providers.length > 0) || config.apiKey) {
|
|
67
68
|
this.jev = new JevJudge(config as JevJudgeConfig);
|
|
68
69
|
}
|
|
69
70
|
}
|
|
@@ -18,15 +18,42 @@ export interface JevNoulQuestion {
|
|
|
18
18
|
export interface JevChoiceQuestion<T extends string = string> {
|
|
19
19
|
type: "choice";
|
|
20
20
|
instructions: string;
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Candidate names.
|
|
23
|
+
*
|
|
24
|
+
* The TypeSafe API does not know about `options`: it expects `criteria` as a
|
|
25
|
+
* DICTIONARY of `name -> description` (a list is rejected by the API with
|
|
26
|
+
* 422 "Input should be a valid dictionary"). `options` stays the ergonomic
|
|
27
|
+
* way to name the candidates.
|
|
28
|
+
*/
|
|
29
|
+
options?: T[];
|
|
30
|
+
/**
|
|
31
|
+
* Choice criteria. A dictionary is used as-is; a list of names is converted
|
|
32
|
+
* to a dictionary whose description is the name itself. Takes precedence
|
|
33
|
+
* over `options`.
|
|
34
|
+
*/
|
|
35
|
+
criteria?: Record<string, string> | string[];
|
|
36
|
+
/** Optional description per option name (used together with `options`). */
|
|
37
|
+
optionDescriptions?: Record<string, string>;
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
// Score: Rate something
|
|
40
|
+
// Score: Rate something on a scale
|
|
25
41
|
export interface JevScoreQuestion {
|
|
26
42
|
type: "score";
|
|
27
43
|
instructions: string;
|
|
44
|
+
/** Lower bound of the reported scale (default 0). */
|
|
28
45
|
min?: number;
|
|
46
|
+
/** Upper bound of the reported scale (default 100). */
|
|
29
47
|
max?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Ordered rubric levels, lowest first.
|
|
50
|
+
*
|
|
51
|
+
* The TypeSafe API requires a rubric for score questions: the answer is the
|
|
52
|
+
* probability-weighted POSITION in this list (0 .. levels-1), never an
|
|
53
|
+
* arbitrary number. `min`/`max` only describe the scale the answer is
|
|
54
|
+
* reported on; when no rubric is given one is derived from them.
|
|
55
|
+
*/
|
|
56
|
+
criteria?: string[];
|
|
30
57
|
}
|
|
31
58
|
|
|
32
59
|
// Any question type
|
|
@@ -76,6 +103,8 @@ export interface JevEvaluationResult<T extends string = string> {
|
|
|
76
103
|
state: string;
|
|
77
104
|
latencyMs: number;
|
|
78
105
|
model: string;
|
|
106
|
+
/** The provider that served the request (when a provider chain is used). */
|
|
107
|
+
provider?: JevProviderName;
|
|
79
108
|
}
|
|
80
109
|
|
|
81
110
|
// Threshold-based action decision
|
|
@@ -86,11 +115,39 @@ export interface ThresholdDecision {
|
|
|
86
115
|
action: "proceed" | "review" | "block";
|
|
87
116
|
}
|
|
88
117
|
|
|
118
|
+
// Provider identity
|
|
119
|
+
export type JevProviderName = "typesafe" | "openrouter";
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A provider bound to its OWN credential.
|
|
123
|
+
*
|
|
124
|
+
* Credentials are never shared between providers: a TypeSafe key is only ever
|
|
125
|
+
* sent to the TypeSafe API and an OpenRouter key only to OpenRouter.
|
|
126
|
+
*/
|
|
127
|
+
export interface JevProviderConfig {
|
|
128
|
+
provider: JevProviderName;
|
|
129
|
+
apiKey: string;
|
|
130
|
+
baseUrl: string;
|
|
131
|
+
model: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
89
134
|
// Config
|
|
90
135
|
export interface JevJudgeConfig {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Legacy single-provider key. Prefer `providers`: a bare key cannot be
|
|
138
|
+
* bound to a provider, which is how an OpenRouter key used to end up being
|
|
139
|
+
* sent to the TypeSafe API.
|
|
140
|
+
*/
|
|
141
|
+
apiKey?: string;
|
|
142
|
+
baseUrl?: string; // Defaults to the TypeSafe API
|
|
143
|
+
model?: string; // Defaults to the provider's own model
|
|
144
|
+
/**
|
|
145
|
+
* Explicit provider chain, tried in order. A provider is only used for the
|
|
146
|
+
* next attempt when the previous one failed with a retryable error
|
|
147
|
+
* (network, timeout, rate limit, auth, 5xx) - never for a request contract
|
|
148
|
+
* error, which is a bug that must stay visible.
|
|
149
|
+
*/
|
|
150
|
+
providers?: JevProviderConfig[];
|
|
94
151
|
defaultThreshold?: number; // Default 0.7
|
|
95
152
|
timeoutMs?: number;
|
|
96
153
|
}
|