metergraph-cli 0.2.2 → 0.2.4
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/README.md +121 -13
- package/assets/skill/SKILL.md +124 -31
- package/assets/skill/manifest.json +3 -3
- package/assets/skills/manifest.json +31 -31
- package/assets/skills/metergraph-analyze/SKILL.md +161 -58
- package/assets/skills/metergraph-candidates/SKILL.md +50 -8
- package/assets/skills/metergraph-evals/SKILL.md +83 -16
- package/assets/skills/metergraph-investigate/SKILL.md +90 -34
- package/assets/skills/metergraph-iterate/SKILL.md +82 -12
- package/assets/skills/metergraph-local-bundle/SKILL.md +146 -128
- package/assets/skills/metergraph-model-swap/SKILL.md +142 -20
- package/assets/skills/metergraph-onboarding/SKILL.md +93 -49
- package/assets/skills/metergraph-report/SKILL.md +60 -15
- package/assets/skills/metergraph-workloads/SKILL.md +97 -13
- package/bin/metergraph.js +1 -1
- package/native/README.md +85 -0
- package/native/anchored-fs.c +243 -0
- package/native/bin/anchored-fs-darwin-arm64 +0 -0
- package/native/bin/anchored-fs-darwin-x64 +0 -0
- package/native/bin/anchored-fs-linux-arm64 +0 -0
- package/native/bin/anchored-fs-linux-x64 +0 -0
- package/native/build.mjs +55 -0
- package/native/manifest.json +9 -0
- package/native/targets.mjs +24 -0
- package/package.json +10 -3
- package/src/action-help.js +130 -0
- package/src/agent-key.js +22 -12
- package/src/anchored-fs.js +163 -0
- package/src/args.js +335 -31
- package/src/auth-binding.js +12 -7
- package/src/auth-callback.js +10 -2
- package/src/auth-login.js +111 -28
- package/src/auth-oauth.js +133 -36
- package/src/auth-session.js +19 -12
- package/src/auth-store.js +28 -7
- package/src/cli.js +147 -15
- package/src/constants.js +70 -1
- package/src/experiments-contract.js +462 -0
- package/src/experiments-output.js +190 -0
- package/src/experiments.js +406 -0
- package/src/output.js +374 -68
- package/src/pricing-args.js +163 -0
- package/src/pricing-output.js +206 -0
- package/src/pricing-reference.js +195 -0
- package/src/pricing.js +307 -0
- package/src/read-contract.js +64 -10
- package/src/read-output.js +66 -13
- package/src/read.js +105 -16
- package/src/repository-identity.js +2 -2
- package/src/setup-deployment.js +7 -0
- package/src/setup-env-git.js +1 -1
- package/src/setup-env.js +16 -9
- package/src/setup-interactive.js +356 -0
- package/src/setup-refusal.js +2 -2
- package/src/setup-state.js +9 -4
- package/src/setup.js +15 -3
- package/src/skill-bundle.js +2 -2
- package/src/skill-trusted.js +56 -0
- package/src/skill.js +28 -9
- package/src/style.js +33 -7
- package/src/tools-output.js +73 -0
- package/src/tools.js +249 -0
- package/src/trace-contract.js +1 -1
- package/src/transport.js +26 -3
- package/src/verify-output.js +2 -2
- package/src/verify.js +5 -2
package/src/cli.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { runPricing } from "./pricing.js";
|
|
2
|
+
import { pricingText, pricingHtml } from "./pricing-output.js";
|
|
1
3
|
import { READ_COMMANDS, parseArgs } from "./args.js";
|
|
2
4
|
import { runLogin, runLogout } from "./auth-login.js";
|
|
3
5
|
import { runDoctor } from "./doctor.js";
|
|
@@ -15,10 +17,23 @@ import {
|
|
|
15
17
|
versionData,
|
|
16
18
|
} from "./output.js";
|
|
17
19
|
import { runRead } from "./read.js";
|
|
20
|
+
import { runExperimentStart } from "./experiments.js";
|
|
21
|
+
import { experimentsMessage, experimentsText } from "./experiments-output.js";
|
|
22
|
+
import { toolsMessage, toolsText } from "./tools-output.js";
|
|
18
23
|
import { readMessage, readText } from "./read-output.js";
|
|
19
24
|
import { listSkillPack, runSkill, runSkillPack } from "./skill.js";
|
|
20
25
|
import { runSetup } from "./setup.js";
|
|
21
26
|
import { containsKnownCredential, preflightNonHostedSetup, prerequisiteGuide, resumeNonHostedRoute } from "./setup-deployment.js";
|
|
27
|
+
import {
|
|
28
|
+
ASKABLE,
|
|
29
|
+
agentAccessOffer,
|
|
30
|
+
canAsk,
|
|
31
|
+
commandLine,
|
|
32
|
+
fillSetupArgv,
|
|
33
|
+
fillLoginArgv,
|
|
34
|
+
offerAgentAccess,
|
|
35
|
+
terminalPrompter,
|
|
36
|
+
} from "./setup-interactive.js";
|
|
22
37
|
import { VERSION } from "./constants.js";
|
|
23
38
|
import { createStyle, fields, headline, paragraph, render, steps, toneFor } from "./style.js";
|
|
24
39
|
import { runVerify } from "./verify.js";
|
|
@@ -26,12 +41,13 @@ import { verifyMessage, verifyText } from "./verify-output.js";
|
|
|
26
41
|
|
|
27
42
|
// Runs one command and resolves with the exit code. With --json, stdout gets
|
|
28
43
|
// exactly one JSON line and stderr stays empty. Without it, results go to
|
|
29
|
-
// stdout and usage or internal errors go to stderr.
|
|
30
|
-
|
|
44
|
+
// stdout and usage or internal errors go to stderr. Only setup reads stdin,
|
|
45
|
+
// and only to ask a person at a terminal (see setup-interactive.js).
|
|
46
|
+
export async function main(argv, { stdout, stderr, stdin = null }) {
|
|
31
47
|
const json = argv.includes("--json");
|
|
32
48
|
let result;
|
|
33
49
|
try {
|
|
34
|
-
result = await run(argv, { stdout, stderr });
|
|
50
|
+
result = await run(argv, { stdout, stderr, stdin });
|
|
35
51
|
} catch {
|
|
36
52
|
result = envelope({
|
|
37
53
|
command: null,
|
|
@@ -45,9 +61,38 @@ export async function main(argv, { stdout, stderr }) {
|
|
|
45
61
|
return result.exit_code;
|
|
46
62
|
}
|
|
47
63
|
|
|
48
|
-
async function run(argv, { stdout, stderr }) {
|
|
49
|
-
|
|
64
|
+
async function run(argv, { stdout, stderr, stdin }) {
|
|
65
|
+
let parsed = parseArgs(argv);
|
|
50
66
|
const style = createStyle(stdout);
|
|
67
|
+
const say = (...lines) => stdout.write(lines.map((line) => `${line}\n`).join(""));
|
|
68
|
+
const asking = canAsk(argv, { stdin, stdout, stderr });
|
|
69
|
+
|
|
70
|
+
if (asking && parsed.command === "login") {
|
|
71
|
+
argv = fillLoginArgv(argv);
|
|
72
|
+
parsed = parseArgs(argv);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// A person at a terminal is asked for what setup is missing; the answers
|
|
76
|
+
// become flags and go through the same parsing as a script's.
|
|
77
|
+
if (parsed.command === "setup" && !parsed.ok && ASKABLE.has(parsed.code) && asking) {
|
|
78
|
+
const prompter = terminalPrompter(stdin, stdout);
|
|
79
|
+
let filled;
|
|
80
|
+
try {
|
|
81
|
+
filled = await fillSetupArgv(argv, { ask: prompter.ask, say });
|
|
82
|
+
} finally {
|
|
83
|
+
prompter.close();
|
|
84
|
+
}
|
|
85
|
+
if (filled === null) {
|
|
86
|
+
stderr.write("Setup was cancelled. Nothing was changed.\n");
|
|
87
|
+
return envelope({ command: "setup", outcome: "cancelled", reason: "questions_cancelled",
|
|
88
|
+
message: "Setup was cancelled. Nothing was changed." });
|
|
89
|
+
}
|
|
90
|
+
argv = filled;
|
|
91
|
+
parsed = parseArgs(argv);
|
|
92
|
+
// Only a command that parses is shown: a value the parser refuses may be
|
|
93
|
+
// a mistyped credential, and its error never echoes input.
|
|
94
|
+
if (parsed.ok) say("", `Running: ${commandLine(filled)}`, "");
|
|
95
|
+
}
|
|
51
96
|
|
|
52
97
|
if (!parsed.ok) {
|
|
53
98
|
const result = envelope({
|
|
@@ -57,13 +102,20 @@ async function run(argv, { stdout, stderr }) {
|
|
|
57
102
|
message: parsed.message,
|
|
58
103
|
});
|
|
59
104
|
if (parsed.json) stdout.write(toJsonLine(result));
|
|
60
|
-
else
|
|
105
|
+
else {
|
|
106
|
+
const errorStyle = createStyle(stderr);
|
|
107
|
+
const [first, ...rest] = paragraph(errorStyle, `Error: ${parsed.message}`, "");
|
|
108
|
+
const help = parsed.command && parsed.command !== "help"
|
|
109
|
+
? `metergraph ${parsed.command} --help` : "metergraph --help";
|
|
110
|
+
stderr.write(render([first.replace(/^Error:/, errorStyle.red("Error:")), ...rest,
|
|
111
|
+
"", ...steps(errorStyle, [["Help", help]])]));
|
|
112
|
+
}
|
|
61
113
|
return result;
|
|
62
114
|
}
|
|
63
115
|
|
|
64
116
|
if (parsed.command === "help") {
|
|
65
|
-
const result = envelope({ command: "help", outcome: "ok", data: helpData(parsed.topic) });
|
|
66
|
-
stdout.write(parsed.json ? toJsonLine(result) : helpText(parsed.topic, style));
|
|
117
|
+
const result = envelope({ command: "help", outcome: "ok", data: helpData(parsed.topic, parsed.action) });
|
|
118
|
+
stdout.write(parsed.json ? toJsonLine(result) : helpText(parsed.topic, style, parsed.action, parsed.advanced));
|
|
67
119
|
return result;
|
|
68
120
|
}
|
|
69
121
|
|
|
@@ -73,6 +125,14 @@ async function run(argv, { stdout, stderr }) {
|
|
|
73
125
|
return result;
|
|
74
126
|
}
|
|
75
127
|
|
|
128
|
+
if (parsed.command === "pricing") {
|
|
129
|
+
const response = await runPricing(parsed);
|
|
130
|
+
const result = envelope({ command: `pricing ${parsed.action}`, ...response });
|
|
131
|
+
stdout.write(parsed.json ? toJsonLine(result) :
|
|
132
|
+
parsed.format === "html" && result.ok ? pricingHtml(result.data) : pricingText(result, parsed.format, style));
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
76
136
|
if (parsed.command === "login" || parsed.command === "logout") {
|
|
77
137
|
// Human progress goes to stderr, never with --json, so stdout carries
|
|
78
138
|
// only the final result.
|
|
@@ -106,24 +166,36 @@ async function run(argv, { stdout, stderr }) {
|
|
|
106
166
|
? await runSetup({ ...options, expectedProfile: routed.profile ?? null, announce: pending.announce }, progress)
|
|
107
167
|
: routed;
|
|
108
168
|
if (pending.result) return pending.result;
|
|
109
|
-
const { outcome, reason
|
|
169
|
+
const { outcome, reason } = response;
|
|
170
|
+
// The customer-local bundle can give the agent more than Metadata
|
|
171
|
+
// without a sign in; say how.
|
|
172
|
+
const offer = outcome === "ok" && routed.profile === "local";
|
|
173
|
+
const data = offer ? { ...response.data, agent_access: agentAccessOffer(options.origin) } : response.data;
|
|
110
174
|
let result = envelope({ command: "setup", outcome, reason, data,
|
|
111
175
|
message: outcome === "ok" ? null : "Setup did not complete. Review the reason and retry safely." });
|
|
112
176
|
if (containsKnownCredential(result, routed)) {
|
|
113
177
|
result = envelope({ command: "setup", outcome: "unsupported", reason: "credential_echo" });
|
|
114
178
|
}
|
|
115
|
-
const human = setupText(result, style);
|
|
179
|
+
const human = setupText(result, style, { asking });
|
|
116
180
|
if (containsKnownCredential(human, routed)) {
|
|
117
181
|
result = envelope({ command: "setup", outcome: "unsupported", reason: "credential_echo" });
|
|
118
182
|
stdout.write(parsed.json ? toJsonLine(result) : "Setup stopped.\n");
|
|
119
|
-
|
|
120
|
-
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
stdout.write(parsed.json ? toJsonLine(result) : human);
|
|
186
|
+
if (offer && asking && result.ok) {
|
|
187
|
+
const prompter = terminalPrompter(stdin, stdout);
|
|
188
|
+
try {
|
|
189
|
+
await offerAgentAccess({ client: options.client, origin: options.origin, prompter, say });
|
|
190
|
+
} finally {
|
|
191
|
+
prompter.close();
|
|
192
|
+
}
|
|
121
193
|
}
|
|
122
194
|
return result;
|
|
123
195
|
}
|
|
124
196
|
|
|
125
197
|
if (READ_COMMANDS.includes(parsed.command)) {
|
|
126
|
-
const { outcome, reason, data } = await runRead(parsed);
|
|
198
|
+
const { outcome, reason, data, validateOutput } = await runRead(parsed);
|
|
127
199
|
const message = readMessage(outcome, reason);
|
|
128
200
|
const result = envelope({
|
|
129
201
|
command: parsed.command,
|
|
@@ -132,7 +204,44 @@ async function run(argv, { stdout, stderr }) {
|
|
|
132
204
|
data,
|
|
133
205
|
message: outcome === "ok" ? null : message,
|
|
134
206
|
});
|
|
135
|
-
|
|
207
|
+
const text = parsed.json ? toJsonLine(result) : readText(result, message, style);
|
|
208
|
+
if (validateOutput && !validateOutput(text)) return refuseCredentialOutput(parsed, stdout, style, validateOutput);
|
|
209
|
+
stdout.write(text);
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (parsed.command === "experiments") {
|
|
214
|
+
// start is the one write: its own key session (experiments.js). The other
|
|
215
|
+
// actions are Metadata reads through runRead's shared session.
|
|
216
|
+
const { outcome, reason, data, validateOutput } = parsed.action === "start" ? await runExperimentStart(parsed) : await runRead(parsed);
|
|
217
|
+
const message = experimentsMessage(outcome, reason);
|
|
218
|
+
const result = envelope({
|
|
219
|
+
command: `experiments ${parsed.action}`,
|
|
220
|
+
outcome,
|
|
221
|
+
reason,
|
|
222
|
+
data,
|
|
223
|
+
message: outcome === "ok" ? null : message,
|
|
224
|
+
});
|
|
225
|
+
const text = parsed.json ? toJsonLine(result) : experimentsText(result, message, style, parsed.action);
|
|
226
|
+
if (validateOutput && !validateOutput(text)) return refuseCredentialOutput(parsed, stdout, style, validateOutput);
|
|
227
|
+
stdout.write(text);
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (parsed.command === "tools") {
|
|
232
|
+
// Any Agent Access tool, through runRead's verified session (tools.js).
|
|
233
|
+
const { outcome, reason, data, validateOutput } = await runRead(parsed);
|
|
234
|
+
const message = toolsMessage(outcome, reason);
|
|
235
|
+
const result = envelope({
|
|
236
|
+
command: `tools ${parsed.action}`,
|
|
237
|
+
outcome,
|
|
238
|
+
reason,
|
|
239
|
+
data,
|
|
240
|
+
message: outcome === "ok" ? null : message,
|
|
241
|
+
});
|
|
242
|
+
const text = parsed.json ? toJsonLine(result) : toolsText(result, message, style, parsed.action);
|
|
243
|
+
if (validateOutput && !validateOutput(text)) return refuseCredentialOutput(parsed, stdout, style, validateOutput);
|
|
244
|
+
stdout.write(text);
|
|
136
245
|
return result;
|
|
137
246
|
}
|
|
138
247
|
|
|
@@ -212,7 +321,7 @@ const SETUP_TITLES = {
|
|
|
212
321
|
operator_handoff: "Setup is waiting on the operator",
|
|
213
322
|
};
|
|
214
323
|
|
|
215
|
-
function setupText(result, style) {
|
|
324
|
+
function setupText(result, style, { asking = false } = {}) {
|
|
216
325
|
const data = result.data;
|
|
217
326
|
const status = data?.status ?? "not_ready";
|
|
218
327
|
const title = SETUP_TITLES[status] ?? (result.ok ? `Setup: ${status}` : "Setup did not complete");
|
|
@@ -241,9 +350,23 @@ function setupText(result, style) {
|
|
|
241
350
|
lines.push(...remoteLines(data, style));
|
|
242
351
|
lines.push(...budgetLines(data, style));
|
|
243
352
|
if (result.ok) lines.push(...steps(style, [["", "Instrument your application and verify an exact trace."]]));
|
|
353
|
+
// At a terminal the offer is a question asked next instead.
|
|
354
|
+
if (data?.agent_access && !asking) lines.push(...agentAccessLines(data.agent_access, style));
|
|
244
355
|
return render(lines);
|
|
245
356
|
}
|
|
246
357
|
|
|
358
|
+
// How to give the agent more than Metadata on the customer-local bundle.
|
|
359
|
+
function agentAccessLines(access, style) {
|
|
360
|
+
const offer = access.offer;
|
|
361
|
+
return ["", ...steps(style, [
|
|
362
|
+
["", "Your coding agent has Metadata access. To let it write evaluations and start analyses, " +
|
|
363
|
+
"run this in the bundle directory. It shows what it grants and asks you to confirm, then writes " +
|
|
364
|
+
"the key to that file without printing it."],
|
|
365
|
+
["In the bundle directory", style.bold(offer.command)],
|
|
366
|
+
["Bundle without bin/agent-key", offer.keys_page_url],
|
|
367
|
+
]), ""];
|
|
368
|
+
}
|
|
369
|
+
|
|
247
370
|
// What an operator handoff is waiting for, in fixed text: each unconfirmed
|
|
248
371
|
// prerequisite with its guidance, and the next action.
|
|
249
372
|
function handoffLines(data, style) {
|
|
@@ -302,3 +425,12 @@ function repositoryValue(repository) {
|
|
|
302
425
|
default: return "not recorded (no owner/name git remote; pass --repository OWNER/NAME)";
|
|
303
426
|
}
|
|
304
427
|
}
|
|
428
|
+
|
|
429
|
+
function refuseCredentialOutput(parsed, stdout, style, validateOutput) {
|
|
430
|
+
const reason = "credential_in_metadata_response";
|
|
431
|
+
const message = readMessage("verification_failed", reason);
|
|
432
|
+
const result = envelope({ command: parsed.command, outcome: "verification_failed", reason, message });
|
|
433
|
+
const text = parsed.json ? toJsonLine(result) : `${message}\n`;
|
|
434
|
+
if (validateOutput(text)) stdout.write(text);
|
|
435
|
+
return result;
|
|
436
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -64,9 +64,38 @@ export const LOGIN_MIN_TIMEOUT_MS = 1000;
|
|
|
64
64
|
export const LOGIN_MAX_TIMEOUT_MS = 900000;
|
|
65
65
|
export const AUTH_HTTP_TIMEOUT_MS = 15000;
|
|
66
66
|
|
|
67
|
-
// The
|
|
67
|
+
// The scope sign in requests. It never asks for agent:read (Debug) or
|
|
68
68
|
// agent:replay, and never falls back to them.
|
|
69
69
|
export const METADATA_SCOPE = "agent:metadata";
|
|
70
|
+
// The Debug role and the workflow permissions an owner adds to it, by the
|
|
71
|
+
// names the Keys page uses. Writing and testing evaluations and starting
|
|
72
|
+
// analyses need Debug with all three. Replay is never used by this CLI.
|
|
73
|
+
export const DEBUG_SCOPE = "agent:read";
|
|
74
|
+
export const REPLAY_SCOPE = "agent:replay";
|
|
75
|
+
export const WORKFLOW_PERMISSIONS = Object.freeze([
|
|
76
|
+
Object.freeze({ field: "allow_evaluation_writes", scope: "agent:eval-write", label: "Allow evaluation writes" }),
|
|
77
|
+
Object.freeze({ field: "allow_evaluation_previews", scope: "agent:eval-preview", label: "Allow evaluation previews" }),
|
|
78
|
+
Object.freeze({ field: "allow_analysis_starts", scope: "agent:analysis-run", label: "Allow analysis starts" }),
|
|
79
|
+
]);
|
|
80
|
+
export const WORKFLOW_SCOPES = Object.freeze(WORKFLOW_PERMISSIONS.map(({ scope }) => scope).sort());
|
|
81
|
+
// The permissions are offered where the deployment reports managed
|
|
82
|
+
// evaluations; customer-owned AWS (byoc-core) and the open-source server do not.
|
|
83
|
+
export const WORKFLOW_DEPLOYMENT_CAPABILITY = "evaluations";
|
|
84
|
+
|
|
85
|
+
// The scope login asks for and a saved sign in records: Metadata, or Debug
|
|
86
|
+
// followed by the chosen workflow permissions in WORKFLOW_SCOPES order.
|
|
87
|
+
export function loginScope(role, permissions = []) {
|
|
88
|
+
if (role !== "debug") return METADATA_SCOPE;
|
|
89
|
+
return [DEBUG_SCOPE, ...WORKFLOW_SCOPES.filter((scope) => permissions.includes(scope))].join(" ");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Whether a saved sign in's scope is one login asks for, in that exact form.
|
|
93
|
+
export function isLoginScope(scope) {
|
|
94
|
+
if (scope === METADATA_SCOPE) return true;
|
|
95
|
+
if (typeof scope !== "string") return false;
|
|
96
|
+
const [first, ...rest] = scope.split(" ");
|
|
97
|
+
return first === DEBUG_SCOPE && scope === loginScope("debug", rest);
|
|
98
|
+
}
|
|
70
99
|
|
|
71
100
|
// The service's agent access contract version, as sent in schema_version by
|
|
72
101
|
// GET /v1/agent/workspace and GET /v1/agent/capabilities. It is the
|
|
@@ -99,8 +128,15 @@ export const READ_PATHS = Object.freeze({
|
|
|
99
128
|
routes: "/v1/agent/routes",
|
|
100
129
|
traces: "/v1/agent/traces",
|
|
101
130
|
});
|
|
131
|
+
const PRICING_QUERY_KEYS = Object.freeze(["model", "provider", "channel", "region", "at", "limit",
|
|
132
|
+
"input_tokens", "output_tokens", "cache_read_tokens", "cache_write_tokens", "cache_write_5m_tokens", "cache_write_1h_tokens", "batch"]);
|
|
102
133
|
export const READ_QUERY_KEYS = Object.freeze({
|
|
134
|
+
"/v1/pricing/lookup": PRICING_QUERY_KEYS,
|
|
135
|
+
"/v1/pricing/cost": PRICING_QUERY_KEYS,
|
|
136
|
+
"/v1/pricing/history": PRICING_QUERY_KEYS,
|
|
137
|
+
"/v1/pricing/market": PRICING_QUERY_KEYS,
|
|
103
138
|
"/v1/agent/usage": Object.freeze(["days", "limit", "metadata"]),
|
|
139
|
+
"/v1/agent/experiments": Object.freeze(["workload_id", "limit"]),
|
|
104
140
|
"/v1/agent/usage/aggregates": Object.freeze([
|
|
105
141
|
"group_by", "days", "since", "until", "limit", "route", "provider", "model", "environment", "metadata",
|
|
106
142
|
]),
|
|
@@ -124,6 +160,31 @@ export const USAGE_MAX_FILTER_VALUES = 20;
|
|
|
124
160
|
export const USAGE_SORTS = Object.freeze(["cost", "cost-per-call", "calls", "errors", "avg-latency", "p95-latency", "day"]);
|
|
125
161
|
export const USAGE_COMPARISONS = Object.freeze(["previous", "previous-same-time"]);
|
|
126
162
|
|
|
163
|
+
// Experiments: replay a workload's recent production calls on 1 to 3
|
|
164
|
+
// treatments and compare them with production. The list, estimate and
|
|
165
|
+
// summary are Metadata reads at these fixed paths (one experiment is
|
|
166
|
+
// EXPERIMENT_PATHS.list plus "/" and its UUID). Starting one needs a
|
|
167
|
+
// coding-agent key with Debug access and the EXPERIMENT_START_SCOPE grant.
|
|
168
|
+
// The limits match the service's own.
|
|
169
|
+
export const EXPERIMENT_PATHS = Object.freeze({
|
|
170
|
+
list: "/v1/agent/experiments",
|
|
171
|
+
estimate: "/v1/agent/experiments/estimate",
|
|
172
|
+
start: "/v1/agent/experiments",
|
|
173
|
+
});
|
|
174
|
+
export const EXPERIMENT_START_SCOPE = "agent:analysis-run";
|
|
175
|
+
// The scopes a start key must carry: Debug and the start grant. It may also
|
|
176
|
+
// carry the other workflow permissions; a key with
|
|
177
|
+
// Replay, Metadata only or any other scope is refused.
|
|
178
|
+
export const EXPERIMENT_START_KEY_SCOPES = Object.freeze([DEBUG_SCOPE, EXPERIMENT_START_SCOPE]);
|
|
179
|
+
export const EXPERIMENT_LAST_N = Object.freeze({ min: 10, max: 40, default: 20 });
|
|
180
|
+
export const EXPERIMENT_MAX_TREATMENTS = 3;
|
|
181
|
+
export const EXPERIMENT_MAX_TRACKED_FIELDS = 10;
|
|
182
|
+
export const EXPERIMENT_LIST_DEFAULT_LIMIT = 20;
|
|
183
|
+
export const EXPERIMENT_LIST_MAX_LIMIT = 50;
|
|
184
|
+
export const EXPERIMENT_TEXT_LIMITS = Object.freeze({ workload: 500, provider: 60, model: 200, label: 40, path: 200 });
|
|
185
|
+
export const EXPERIMENT_MAX_TOKENS = 32000;
|
|
186
|
+
export const EXPERIMENT_REASONING_EFFORTS = Object.freeze(["none", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
187
|
+
|
|
127
188
|
// Read commands share one total deadline for every request they make,
|
|
128
189
|
// including a token refresh. It is never reset per request or page.
|
|
129
190
|
export const READ_DEFAULT_TIMEOUT_MS = 15000;
|
|
@@ -171,6 +232,8 @@ export const EXIT_CODES = Object.freeze({
|
|
|
171
232
|
rate_limited: 16,
|
|
172
233
|
cancelled: 17,
|
|
173
234
|
action_required: 18,
|
|
235
|
+
refused: 19,
|
|
236
|
+
unconfirmed: 20,
|
|
174
237
|
});
|
|
175
238
|
|
|
176
239
|
export const EXIT_CODE_MEANINGS = Object.freeze({
|
|
@@ -211,4 +274,10 @@ export const EXIT_CODE_MEANINGS = Object.freeze({
|
|
|
211
274
|
"A person must act before this command can finish; data.next_action says what to do. " +
|
|
212
275
|
"With --json --no-browser the command exits as soon as the approval URL exists, and a background " +
|
|
213
276
|
"process waits for approval until --timeout-ms. After approval, run the same command again to continue.",
|
|
277
|
+
refused:
|
|
278
|
+
"The service refused the experiment for a stated reason, such as its budget, the daily cap, experiments " +
|
|
279
|
+
"already running or a request it cannot price; data.refusal says which. Nothing was started.",
|
|
280
|
+
unconfirmed:
|
|
281
|
+
"An experiment start reached the service but its result was not confirmed, so an experiment may have " +
|
|
282
|
+
"been recorded and may run. List the workload's experiments before starting again: each start creates one.",
|
|
214
283
|
});
|