modelrig 0.11.1 → 0.12.0
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/CHANGELOG.md +34 -0
- package/dist/adapters/anthropic.d.ts +4 -4
- package/dist/adapters/anthropic.d.ts.map +1 -1
- package/dist/adapters/anthropic.js +8 -1
- package/dist/adapters/deepseek.d.ts +2 -4
- package/dist/adapters/deepseek.d.ts.map +1 -1
- package/dist/adapters/deepseek.js +9 -5
- package/dist/adapters/gemini.d.ts +4 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +16 -1
- package/dist/adapters/grok.d.ts +2 -4
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +8 -4
- package/dist/adapters/history-mapping.d.ts +126 -0
- package/dist/adapters/history-mapping.d.ts.map +1 -0
- package/dist/adapters/history-mapping.js +230 -0
- package/dist/adapters/openai-compat.d.ts +2 -4
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +9 -5
- package/dist/adapters/openai.d.ts +2 -4
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +8 -4
- package/dist/adapters/responses-api.d.ts.map +1 -1
- package/dist/adapters/responses-api.js +9 -4
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +79 -0
- package/dist/artifacts/conversation.d.ts +128 -0
- package/dist/artifacts/conversation.d.ts.map +1 -0
- package/dist/artifacts/conversation.js +172 -0
- package/dist/artifacts/index.d.ts +1 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/media.d.ts +46 -0
- package/dist/artifacts/media.d.ts.map +1 -0
- package/dist/artifacts/media.js +88 -0
- package/dist/artifacts/run-context.d.ts +61 -2
- package/dist/artifacts/run-context.d.ts.map +1 -1
- package/dist/artifacts/run-context.js +183 -12
- package/dist/artifacts/scrub.d.ts +3 -0
- package/dist/artifacts/scrub.d.ts.map +1 -1
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +34 -7
- package/dist/bakeoff/eval-cases.d.ts +5 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -1
- package/dist/bakeoff/eval-cases.js +10 -1
- package/dist/bakeoff/replay.d.ts +15 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +19 -1
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +9 -2
- package/dist/bakeoff/turn-replay.d.ts +93 -0
- package/dist/bakeoff/turn-replay.d.ts.map +1 -0
- package/dist/bakeoff/turn-replay.js +150 -0
- package/dist/bakeoff/types.d.ts +12 -0
- package/dist/bakeoff/types.d.ts.map +1 -1
- package/dist/feedback/grade.d.ts +7 -2
- package/dist/feedback/grade.d.ts.map +1 -1
- package/dist/feedback/grade.js +14 -0
- package/dist/feedback/outcome.d.ts +103 -0
- package/dist/feedback/outcome.d.ts.map +1 -0
- package/dist/feedback/outcome.js +175 -0
- package/dist/gateway/history-bridge.d.ts +32 -0
- package/dist/gateway/history-bridge.d.ts.map +1 -0
- package/dist/gateway/history-bridge.js +64 -0
- package/dist/gateway/serve-client.d.ts.map +1 -1
- package/dist/gateway/serve-client.js +6 -0
- package/dist/index.d.ts +44 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +106 -6
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +3 -3
- package/dist/registry/build.d.ts +4 -0
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/generation-models.d.ts +68 -0
- package/dist/registry/generation-models.d.ts.map +1 -0
- package/dist/registry/generation-models.js +168 -0
- package/dist/registry/model-kind.d.ts +34 -0
- package/dist/registry/model-kind.d.ts.map +1 -0
- package/dist/registry/model-kind.js +40 -0
- package/dist/registry/pricing-shape.d.ts +29 -1
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +9 -0
- package/dist/registry/pricing.d.ts +26 -0
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +29 -0
- package/dist/registry/rows.d.ts.map +1 -1
- package/dist/registry/rows.js +3 -1
- package/dist/registry/unit-pricing.d.ts +32 -0
- package/dist/registry/unit-pricing.d.ts.map +1 -0
- package/dist/registry/unit-pricing.js +62 -0
- package/dist/routes/input-loader.d.ts +38 -0
- package/dist/routes/input-loader.d.ts.map +1 -0
- package/dist/routes/input-loader.js +80 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +3 -0
- package/dist/routes/resolved-bundle.d.ts +2 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +3 -0
- package/dist/run/cost.d.ts +28 -0
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +74 -0
- package/dist/run/dispatch.d.ts +4 -1
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +3 -0
- package/dist/run/limits.d.ts +88 -0
- package/dist/run/limits.d.ts.map +1 -0
- package/dist/run/limits.js +155 -0
- package/dist/run/messages.d.ts +199 -0
- package/dist/run/messages.d.ts.map +1 -0
- package/dist/run/messages.js +430 -0
- package/dist/run/raw-finalize.d.ts +5 -2
- package/dist/run/raw-finalize.d.ts.map +1 -1
- package/dist/run/raw-finalize.js +8 -5
- package/dist/run/raw-request.d.ts.map +1 -1
- package/dist/run/raw-request.js +2 -1
- package/dist/run/raw.d.ts +10 -4
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +11 -3
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +16 -6
- package/dist/run/record-call.d.ts +13 -2
- package/dist/run/record-call.d.ts.map +1 -1
- package/dist/run/record-call.js +50 -7
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +12 -8
- package/dist/telemetry/attempt-meta.d.ts +49 -1
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +45 -1
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +2 -1
- package/dist/telemetry/grade-mappers.d.ts +1 -1
- package/dist/telemetry/grade-mappers.d.ts.map +1 -1
- package/dist/telemetry/grade-mappers.js +7 -1
- package/dist/telemetry/grade-task.d.ts +49 -0
- package/dist/telemetry/grade-task.d.ts.map +1 -0
- package/dist/telemetry/grade-task.js +85 -0
- package/dist/telemetry/grades-sql.d.ts +16 -1
- package/dist/telemetry/grades-sql.d.ts.map +1 -1
- package/dist/telemetry/grades-sql.js +64 -4
- package/dist/telemetry/ingest-protocol.d.ts +24 -1
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +72 -43
- package/dist/telemetry/otel-content.d.ts +31 -0
- package/dist/telemetry/otel-content.d.ts.map +1 -0
- package/dist/telemetry/otel-content.js +106 -0
- package/dist/telemetry/otel-hierarchy.d.ts +57 -0
- package/dist/telemetry/otel-hierarchy.d.ts.map +1 -0
- package/dist/telemetry/otel-hierarchy.js +155 -0
- package/dist/telemetry/otel-normalize.d.ts +15 -1
- package/dist/telemetry/otel-normalize.d.ts.map +1 -1
- package/dist/telemetry/otel-normalize.js +24 -39
- package/dist/telemetry/sinks.d.ts +4 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +53 -34
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +8 -0
- package/dist/telemetry/store.d.ts +9 -0
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +10 -0
- package/dist/telemetry/uniform-rows.d.ts +43 -0
- package/dist/telemetry/uniform-rows.d.ts.map +1 -0
- package/dist/telemetry/uniform-rows.js +139 -0
- package/dist/types/failures-teaching.d.ts.map +1 -1
- package/dist/types/failures-teaching.js +11 -0
- package/dist/types/failures.d.ts +1 -1
- package/dist/types/failures.d.ts.map +1 -1
- package/dist/types/failures.js +2 -0
- package/dist/types/provider.d.ts +12 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/route.d.ts +13 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +14 -1
- package/dist/types/telemetry.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +16 -3
- package/docs/how-it-fits.md +3 -3
- package/docs/instrumentation.md +15 -6
- package/docs/llms.txt +55 -7
- package/docs/mcp-oracle.md +15 -4
- package/docs/quickstart.md +6 -5
- package/package.json +4 -2
- package/registry/registry.json +456 -0
- package/templates/creative-approval.json +81 -0
- package/templates/support-conversation.json +82 -0
package/dist/bakeoff/report.js
CHANGED
|
@@ -12,6 +12,7 @@ exports.formatDisagreementSummary = formatDisagreementSummary;
|
|
|
12
12
|
exports.formatSwapDirection = formatSwapDirection;
|
|
13
13
|
exports.printMatrix = printMatrix;
|
|
14
14
|
exports.runEvalCaseBakeoffCli = runEvalCaseBakeoffCli;
|
|
15
|
+
const turn_replay_1 = require("./turn-replay");
|
|
15
16
|
const node_fs_1 = require("node:fs");
|
|
16
17
|
const node_os_1 = require("node:os");
|
|
17
18
|
const node_path_1 = require("node:path");
|
|
@@ -75,7 +76,7 @@ function bakeoffHeadline(artifact) {
|
|
|
75
76
|
: "cost n/a";
|
|
76
77
|
const ceiling = fmtCeiling(artifact);
|
|
77
78
|
const r = p.regret;
|
|
78
|
-
return (`${p.variant} — regret ${fmtPts(r.regretMean)} pts ` +
|
|
79
|
+
return (`${p.variant}${arm?.armLabel !== undefined ? ` (${arm.armLabel})` : ""} — regret ${fmtPts(r.regretMean)} pts ` +
|
|
79
80
|
`(CI ${fmtPts(r.regretCi95[0])}…${fmtPts(r.regretCi95[1])}, n=${r.nPaired}) vs reference ${ref.model}, ` +
|
|
80
81
|
`${cost}${ceiling !== null ? ` vs ceiling ${ceiling}` : ""} — proposed`);
|
|
81
82
|
}
|
|
@@ -178,7 +179,7 @@ function printMatrix(artifact, cacheAdjusted = []) {
|
|
|
178
179
|
header.push("pairwise-win");
|
|
179
180
|
const rows = artifact.variants.map((m) => {
|
|
180
181
|
const base = [
|
|
181
|
-
m.isReference === true ? `${m.variant} [ref]` : m.variant,
|
|
182
|
+
m.isReference === true ? `${m.variant} [ref]` : m.armLabel !== undefined ? `${m.variant} (${m.armLabel})` : m.variant, // ARC-β R3-1
|
|
182
183
|
String(m.samples),
|
|
183
184
|
`${(m.conformRate * 100).toFixed(0)}%`,
|
|
184
185
|
`[${(m.conformCi95[0] * 100).toFixed(0)},${(m.conformCi95[1] * 100).toFixed(0)}]`,
|
|
@@ -225,6 +226,12 @@ function printMatrix(artifact, cacheAdjusted = []) {
|
|
|
225
226
|
// byte-identical to the pre-N6 matrix output.
|
|
226
227
|
for (const l of formatDisagreementSummary(artifact))
|
|
227
228
|
console.log(l);
|
|
229
|
+
for (const m of artifact.variants)
|
|
230
|
+
for (const l of (0, turn_replay_1.formatIncomparable)(m.incomparable ?? []))
|
|
231
|
+
console.log(`[${m.variant}] ${l}`); // ARC-β M3(c)
|
|
232
|
+
for (const m of artifact.variants)
|
|
233
|
+
for (const l of (0, turn_replay_1.formatFallbackServed)(m.servedByFallback ?? []))
|
|
234
|
+
console.log(`[${m.variant}] ${l}`); // ARC-β R3-1
|
|
228
235
|
// Cache-adjusted section (spec §5.4) — printed only when the caller supplied
|
|
229
236
|
// simulator rows; absent ⇒ the matrix output is byte-identical to pre-WS-B.
|
|
230
237
|
for (const l of formatCacheAdjustedSection(cacheAdjusted))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turn replay — ARC-β (agent-archetypes spec §4, builder-prompt-02 §1.7).
|
|
3
|
+
*
|
|
4
|
+
* "Would a cheaper model have answered THIS turn as well, given the same
|
|
5
|
+
* conversation?" A messages route's capture row holds the history up to and
|
|
6
|
+
* including the turn's last user/tool message (run/messages.ts capture codec)
|
|
7
|
+
* and the served reply. The bake-off replays that history against each
|
|
8
|
+
* candidate, which answers the FINAL turn only, graded by the route's existing
|
|
9
|
+
* grader path with the regret bound unchanged. Replay NEVER continues a
|
|
10
|
+
* conversation past the captured turn — there is no simulated next user turn
|
|
11
|
+
* (spec §9 refuses user simulators).
|
|
12
|
+
*
|
|
13
|
+
* M15 GUARD (unruled → conservative, roadmap §2): turn replay re-sends real
|
|
14
|
+
* end-user text to candidate models and graders, so it runs only for routes
|
|
15
|
+
* whose capture is EXPLICITLY on under the existing capture posture
|
|
16
|
+
* (`effectiveCapture`: the control-plane flip wins, else the YAML flag).
|
|
17
|
+
* Otherwise the bake-off refuses with a teaching 422.
|
|
18
|
+
*/
|
|
19
|
+
import { RouteConfigError } from "../routes/loader";
|
|
20
|
+
import type { TaskSettingsResolver } from "../settings/task-settings";
|
|
21
|
+
import type { CaptureRow } from "../telemetry/capture";
|
|
22
|
+
import type { RouteBundle } from "../types/route";
|
|
23
|
+
import type { HistoryMessage } from "../run/messages";
|
|
24
|
+
import type { CaptureReplayInput } from "../run/messages";
|
|
25
|
+
import type { RigFailure } from "../types/failures";
|
|
26
|
+
import type { ReplaySampleResult } from "./replay";
|
|
27
|
+
/** The bake-off refused a messages route because capture is not explicitly on.
|
|
28
|
+
* A RouteConfigError (refused before any spend), carrying the HTTP status a
|
|
29
|
+
* server surface should answer with. */
|
|
30
|
+
export declare class TurnReplayRefusedError extends RouteConfigError {
|
|
31
|
+
readonly httpStatus = 422;
|
|
32
|
+
constructor(route: string);
|
|
33
|
+
}
|
|
34
|
+
/** Throw {@link TurnReplayRefusedError} for a messages route whose capture is
|
|
35
|
+
* not explicitly on; a no-op for every variables route (byte-identical). */
|
|
36
|
+
export declare function assertTurnReplayAllowed(route: Pick<RouteBundle, "route" | "inputKind" | "capture">, settings: TaskSettingsResolver | null): void;
|
|
37
|
+
/** One replayable turn of a captured conversation. */
|
|
38
|
+
export interface TurnReplayCase {
|
|
39
|
+
/** The route variables the system template renders from. */
|
|
40
|
+
readonly variables: Record<string, unknown>;
|
|
41
|
+
/** The conversation up to and including the turn's last user/tool message. */
|
|
42
|
+
readonly history: readonly HistoryMessage[];
|
|
43
|
+
/** The reply that was served for this turn (the capture's output). */
|
|
44
|
+
readonly reference: string;
|
|
45
|
+
/** The live call's tools (R2-1), when it declared any. */
|
|
46
|
+
readonly tools?: CaptureReplayInput["tools"];
|
|
47
|
+
readonly toolChoice?: CaptureReplayInput["toolChoice"];
|
|
48
|
+
}
|
|
49
|
+
/** The turn a capture row encodes, or null for a variables (non-messages) row. */
|
|
50
|
+
export declare function turnReplayCase(capture: Pick<CaptureRow, "variablesJson" | "outputText">): TurnReplayCase | null;
|
|
51
|
+
/** The incomparable marker for a replay failure, or null for an ordinary one. */
|
|
52
|
+
export declare function incomparableFrom(failure: RigFailure): {
|
|
53
|
+
readonly model: string;
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
} | null;
|
|
56
|
+
/** One "n cases incomparable for <model>: reason" line's data. */
|
|
57
|
+
export interface IncomparableSummary {
|
|
58
|
+
readonly model: string;
|
|
59
|
+
readonly reason: string;
|
|
60
|
+
readonly n: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Split an arm's samples into the COMPARABLE ones (scored, paired for regret)
|
|
64
|
+
* and a per-(model, reason) count of the incomparable ones. An incomparable
|
|
65
|
+
* sample is neither a pass nor a failure: dropping it from both sides of the
|
|
66
|
+
* pairing keeps it from biasing the verdict.
|
|
67
|
+
*/
|
|
68
|
+
export declare function splitIncomparable(samples: readonly ReplaySampleResult[]): {
|
|
69
|
+
readonly comparable: readonly ReplaySampleResult[];
|
|
70
|
+
readonly incomparable: readonly IncomparableSummary[];
|
|
71
|
+
};
|
|
72
|
+
/** The report lines: "n cases incomparable for <model>: reason". */
|
|
73
|
+
export declare function formatIncomparable(rows: readonly IncomparableSummary[]): string[];
|
|
74
|
+
/** The `history_skipped` value on a replayed call's telemetry rows, if any. */
|
|
75
|
+
export declare function skippedTagOf(rows: readonly {
|
|
76
|
+
readonly tagsJson: string;
|
|
77
|
+
}[]): string | undefined;
|
|
78
|
+
/** One "n cases served by <servedBy> (<skipped> skipped: <reason>)" row. */
|
|
79
|
+
export interface FallbackServed {
|
|
80
|
+
readonly servedBy: string;
|
|
81
|
+
/** The skipped candidates, `provider/model=code` comma-joined (the tag value). */
|
|
82
|
+
readonly skipped: string;
|
|
83
|
+
readonly n: number;
|
|
84
|
+
}
|
|
85
|
+
/** Count an arm's comparable samples answered by a fallback, grouped by
|
|
86
|
+
* (served model, skipped set). Empty when nothing was skipped. */
|
|
87
|
+
export declare function fallbackServed(samples: readonly ReplaySampleResult[]): FallbackServed[];
|
|
88
|
+
/** "<first> → <fallback…> (fallback served n/N)" — the label an arm's numbers
|
|
89
|
+
* carry when any of its cases was served by a fallback. */
|
|
90
|
+
export declare function fallbackArmLabel(first: string, rows: readonly FallbackServed[], total: number): string;
|
|
91
|
+
/** The report lines for an arm's fallback-served cases. */
|
|
92
|
+
export declare function formatFallbackServed(rows: readonly FallbackServed[]): string[];
|
|
93
|
+
//# sourceMappingURL=turn-replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"turn-replay.d.ts","sourceRoot":"","sources":["../../src/bakeoff/turn-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Turn replay — ARC-β (agent-archetypes spec §4, builder-prompt-02 §1.7).
|
|
4
|
+
*
|
|
5
|
+
* "Would a cheaper model have answered THIS turn as well, given the same
|
|
6
|
+
* conversation?" A messages route's capture row holds the history up to and
|
|
7
|
+
* including the turn's last user/tool message (run/messages.ts capture codec)
|
|
8
|
+
* and the served reply. The bake-off replays that history against each
|
|
9
|
+
* candidate, which answers the FINAL turn only, graded by the route's existing
|
|
10
|
+
* grader path with the regret bound unchanged. Replay NEVER continues a
|
|
11
|
+
* conversation past the captured turn — there is no simulated next user turn
|
|
12
|
+
* (spec §9 refuses user simulators).
|
|
13
|
+
*
|
|
14
|
+
* M15 GUARD (unruled → conservative, roadmap §2): turn replay re-sends real
|
|
15
|
+
* end-user text to candidate models and graders, so it runs only for routes
|
|
16
|
+
* whose capture is EXPLICITLY on under the existing capture posture
|
|
17
|
+
* (`effectiveCapture`: the control-plane flip wins, else the YAML flag).
|
|
18
|
+
* Otherwise the bake-off refuses with a teaching 422.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.TurnReplayRefusedError = void 0;
|
|
22
|
+
exports.assertTurnReplayAllowed = assertTurnReplayAllowed;
|
|
23
|
+
exports.turnReplayCase = turnReplayCase;
|
|
24
|
+
exports.incomparableFrom = incomparableFrom;
|
|
25
|
+
exports.splitIncomparable = splitIncomparable;
|
|
26
|
+
exports.formatIncomparable = formatIncomparable;
|
|
27
|
+
exports.skippedTagOf = skippedTagOf;
|
|
28
|
+
exports.fallbackServed = fallbackServed;
|
|
29
|
+
exports.fallbackArmLabel = fallbackArmLabel;
|
|
30
|
+
exports.formatFallbackServed = formatFallbackServed;
|
|
31
|
+
const loader_1 = require("../routes/loader");
|
|
32
|
+
const task_settings_1 = require("../settings/task-settings");
|
|
33
|
+
const messages_1 = require("../run/messages");
|
|
34
|
+
/** The bake-off refused a messages route because capture is not explicitly on.
|
|
35
|
+
* A RouteConfigError (refused before any spend), carrying the HTTP status a
|
|
36
|
+
* server surface should answer with. */
|
|
37
|
+
class TurnReplayRefusedError extends loader_1.RouteConfigError {
|
|
38
|
+
constructor(route) {
|
|
39
|
+
super(`turn replay refused for route "${route}": it serves conversations (input: messages), and replaying them ` +
|
|
40
|
+
"re-sends end-user text to candidate models — allowed only where capture is explicitly on. Turn capture on " +
|
|
41
|
+
"for this route (`capture: true`, or the console capture flip), let it record real turns, then re-run the " +
|
|
42
|
+
"bake-off (see messages-lane.md#turn-replay)");
|
|
43
|
+
this.httpStatus = 422;
|
|
44
|
+
this.name = "TurnReplayRefusedError";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.TurnReplayRefusedError = TurnReplayRefusedError;
|
|
48
|
+
/** Throw {@link TurnReplayRefusedError} for a messages route whose capture is
|
|
49
|
+
* not explicitly on; a no-op for every variables route (byte-identical). */
|
|
50
|
+
function assertTurnReplayAllowed(route, settings) {
|
|
51
|
+
if (route.inputKind !== "messages")
|
|
52
|
+
return;
|
|
53
|
+
if (!(0, task_settings_1.effectiveCapture)(settings, route.route, route.capture))
|
|
54
|
+
throw new TurnReplayRefusedError(route.route);
|
|
55
|
+
}
|
|
56
|
+
/** The turn a capture row encodes, or null for a variables (non-messages) row. */
|
|
57
|
+
function turnReplayCase(capture) {
|
|
58
|
+
const parsed = (0, messages_1.parseCaptureInput)(capture.variablesJson);
|
|
59
|
+
if (parsed.messages === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
return {
|
|
62
|
+
variables: parsed.input,
|
|
63
|
+
history: parsed.messages,
|
|
64
|
+
reference: capture.outputText,
|
|
65
|
+
...(parsed.tools !== undefined ? { tools: parsed.tools } : {}),
|
|
66
|
+
...(parsed.toolChoice !== undefined ? { toolChoice: parsed.toolChoice } : {}),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ── Incomparable cases (architect ruling M3(c), 2026-10-24) ──────────────────
|
|
70
|
+
/** The incomparable marker for a replay failure, or null for an ordinary one. */
|
|
71
|
+
function incomparableFrom(failure) {
|
|
72
|
+
if (!failure.message.startsWith(messages_1.HISTORY_INCOMPATIBLE_PREFIX))
|
|
73
|
+
return null;
|
|
74
|
+
const model = failure.provider !== undefined && failure.model !== undefined ? `${failure.provider}/${failure.model}` : "every candidate of the arm";
|
|
75
|
+
return { model, reason: failure.message.slice(messages_1.HISTORY_INCOMPATIBLE_PREFIX.length).replace(/^:\s*/, "") };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Split an arm's samples into the COMPARABLE ones (scored, paired for regret)
|
|
79
|
+
* and a per-(model, reason) count of the incomparable ones. An incomparable
|
|
80
|
+
* sample is neither a pass nor a failure: dropping it from both sides of the
|
|
81
|
+
* pairing keeps it from biasing the verdict.
|
|
82
|
+
*/
|
|
83
|
+
function splitIncomparable(samples) {
|
|
84
|
+
const counts = new Map();
|
|
85
|
+
const comparable = [];
|
|
86
|
+
for (const s of samples) {
|
|
87
|
+
if (s.incomparable === undefined) {
|
|
88
|
+
comparable.push(s);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const key = `${s.incomparable.model}\u0000${s.incomparable.reason}`;
|
|
92
|
+
const prev = counts.get(key);
|
|
93
|
+
counts.set(key, { model: s.incomparable.model, reason: s.incomparable.reason, n: (prev?.n ?? 0) + 1 });
|
|
94
|
+
}
|
|
95
|
+
return { comparable, incomparable: [...counts.values()] };
|
|
96
|
+
}
|
|
97
|
+
/** The report lines: "n cases incomparable for <model>: reason". */
|
|
98
|
+
function formatIncomparable(rows) {
|
|
99
|
+
return rows.map((r) => `${r.n} case${r.n === 1 ? "" : "s"} incomparable for ${r.model}: ${r.reason}`);
|
|
100
|
+
}
|
|
101
|
+
// ── Fallback attribution (QA R3-1, 2026-10-24) ────────────────────────────────
|
|
102
|
+
// Under ruling R2-2 a multi-candidate arm whose first candidate cannot take a
|
|
103
|
+
// tool-loop history is served by a later one. The bake-off must say so — an
|
|
104
|
+
// arm's numbers are never presented under a model that never saw the turn.
|
|
105
|
+
/** The `history_skipped` value on a replayed call's telemetry rows, if any. */
|
|
106
|
+
function skippedTagOf(rows) {
|
|
107
|
+
for (const r of rows) {
|
|
108
|
+
try {
|
|
109
|
+
const v = JSON.parse(r.tagsJson)[messages_1.HISTORY_SKIPPED_TAG];
|
|
110
|
+
if (typeof v === "string" && v !== "")
|
|
111
|
+
return v;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// a malformed tags blob carries no skip
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
/** Count an arm's comparable samples answered by a fallback, grouped by
|
|
120
|
+
* (served model, skipped set). Empty when nothing was skipped. */
|
|
121
|
+
function fallbackServed(samples) {
|
|
122
|
+
const counts = new Map();
|
|
123
|
+
for (const s of samples) {
|
|
124
|
+
if (s.historySkipped === undefined || s.servedBy === undefined)
|
|
125
|
+
continue;
|
|
126
|
+
const key = `${s.servedBy}\u0000${s.historySkipped}`;
|
|
127
|
+
counts.set(key, { servedBy: s.servedBy, skipped: s.historySkipped, n: (counts.get(key)?.n ?? 0) + 1 });
|
|
128
|
+
}
|
|
129
|
+
return [...counts.values()];
|
|
130
|
+
}
|
|
131
|
+
/** "<first> → <fallback…> (fallback served n/N)" — the label an arm's numbers
|
|
132
|
+
* carry when any of its cases was served by a fallback. */
|
|
133
|
+
function fallbackArmLabel(first, rows, total) {
|
|
134
|
+
const servedBy = [...new Set(rows.map((r) => r.servedBy))].join(", ");
|
|
135
|
+
const n = rows.reduce((a, r) => a + r.n, 0);
|
|
136
|
+
return `${first} → ${servedBy} (fallback served ${n}/${total})`;
|
|
137
|
+
}
|
|
138
|
+
/** The report lines for an arm's fallback-served cases. */
|
|
139
|
+
function formatFallbackServed(rows) {
|
|
140
|
+
return rows.map((r) => {
|
|
141
|
+
const skipped = r.skipped
|
|
142
|
+
.split(",")
|
|
143
|
+
.map((entry) => {
|
|
144
|
+
const at = entry.lastIndexOf("=");
|
|
145
|
+
return at > 0 ? `${entry.slice(0, at)} skipped: ${entry.slice(at + 1)}` : `${entry} skipped`;
|
|
146
|
+
})
|
|
147
|
+
.join("; ");
|
|
148
|
+
return `${r.n} case${r.n === 1 ? "" : "s"} served by ${r.servedBy} (${skipped})`;
|
|
149
|
+
});
|
|
150
|
+
}
|
package/dist/bakeoff/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* effective $ per 1K conformant outputs (F1-corrected); RB-1 adds per-sample
|
|
5
5
|
* value accuracy, paired regret vs a reference arm, and an objective-aware gate.
|
|
6
6
|
*/
|
|
7
|
+
import type { FallbackServed, IncomparableSummary } from "./turn-replay";
|
|
7
8
|
import type { ObjectiveMode } from "../types/route";
|
|
8
9
|
import type { PairwiseStats } from "./pairwise";
|
|
9
10
|
import type { DisagreementReport } from "./disagreement";
|
|
@@ -55,6 +56,17 @@ export interface VariantMetrics {
|
|
|
55
56
|
* winning variant so the mirror/console read the artifact's decision rather
|
|
56
57
|
* than recomputing a (mode-blind) winner client-side. */
|
|
57
58
|
readonly proposed?: boolean;
|
|
59
|
+
/** ARC-β ruling M3(c): captured turns this arm's model could not accept
|
|
60
|
+
* natively — excluded from every number above and from regret; reported as
|
|
61
|
+
* "n cases incomparable for <model>: reason". Absent ⇒ none. */
|
|
62
|
+
readonly incomparable?: readonly IncomparableSummary[];
|
|
63
|
+
/** ARC-β R3-1: cases this arm's ladder answered with a FALLBACK because its
|
|
64
|
+
* earlier candidates were skipped for the history ("n cases served by
|
|
65
|
+
* <provider/model> (<skipped> skipped: <reason>)"). Absent ⇒ none. */
|
|
66
|
+
readonly servedByFallback?: readonly FallbackServed[];
|
|
67
|
+
/** ARC-β R3-1: present with `servedByFallback` — "<first> → <fallback>
|
|
68
|
+
* (fallback served n/N)", the label these numbers must be shown under. */
|
|
69
|
+
readonly armLabel?: string;
|
|
58
70
|
/** RB-1: this arm's regret vs the reference; null on the reference arm and
|
|
59
71
|
* when there is no reference. */
|
|
60
72
|
readonly regret?: RegretStats | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bakeoff/types.ts"],"names":[],"mappings":"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"}
|
package/dist/feedback/grade.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* (`rig.feedback` / `artifact.evaluate` / `run.evaluate`) and their adapters were
|
|
12
12
|
* removed in GP-3 — every judgment now builds a grade directly.
|
|
13
13
|
*/
|
|
14
|
-
import type { GradeCriterion, GradeRow, GradeSubject } from "../types/telemetry";
|
|
14
|
+
import type { GradeCriterion, GradeKind, GradeRow, GradeSubject } from "../types/telemetry";
|
|
15
15
|
/** Options for `rig.grade(subject, opts)`. `score` is the 0–1 primitive and is
|
|
16
16
|
* required; everything else is optional display / provenance. */
|
|
17
17
|
export interface GradeOpts {
|
|
@@ -26,7 +26,9 @@ export interface GradeOpts {
|
|
|
26
26
|
};
|
|
27
27
|
/** Written guidance saved for a later improvement path (was note/explanation). */
|
|
28
28
|
readonly comment?: string;
|
|
29
|
-
|
|
29
|
+
/** `outcome` (ARC-3) is normally written through `rig.outcome`, which derives
|
|
30
|
+
* the subject, `grader = outcome:<name>` and the score for you. */
|
|
31
|
+
readonly kind: GradeKind;
|
|
30
32
|
/** Optional specific grader name (e.g. "gpt-5-judge", "schema-check"). */
|
|
31
33
|
readonly grader?: string;
|
|
32
34
|
/** Provenance (e.g. "sdk", "console", a review-agent name). */
|
|
@@ -38,6 +40,9 @@ export interface GradeOpts {
|
|
|
38
40
|
* the scrub + dedup/drop-empty-id/clamp guard at persist; this is carried
|
|
39
41
|
* verbatim. */
|
|
40
42
|
readonly criteria?: readonly GradeCriterion[];
|
|
43
|
+
/** ARC-3: the raw business value — allowed ONLY on `kind: "outcome"` (a value
|
|
44
|
+
* on any other kind throws: it would be refused server-side). */
|
|
45
|
+
readonly value?: number;
|
|
41
46
|
/** The grade's own org — server-supplied ONLY, already authorized (T3). Omit
|
|
42
47
|
* from application code (a rig has one org and the exporter knows it). */
|
|
43
48
|
readonly orgId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../src/feedback/grade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"grade.d.ts","sourceRoot":"","sources":["../../src/feedback/grade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE5F;iEACiE;AACjE,MAAM,WAAW,SAAS;IACxB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;6EACyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,kFAAkF;IAClF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;mBAKe;IACf,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC9C;qEACiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;8EAC0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAQD;oFACoF;AACpF,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAoC9E;AAED;;+DAE+D;AAC/D,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAAE,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,GAAG;IACzE,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;CACzD,CAiBA"}
|
package/dist/feedback/grade.js
CHANGED
|
@@ -32,6 +32,11 @@ function buildGradeRow(subject, opts) {
|
|
|
32
32
|
if (!Number.isFinite(opts.score) || opts.score < 0 || opts.score > 1) {
|
|
33
33
|
throw new Error(`grade score must be in [0, 1], got ${opts.score}`);
|
|
34
34
|
}
|
|
35
|
+
if (opts.value !== undefined && (opts.kind !== "outcome" || !Number.isFinite(opts.value))) {
|
|
36
|
+
throw new Error(opts.kind !== "outcome"
|
|
37
|
+
? `grade value is only allowed on kind "outcome" (got kind "${opts.kind}") — put the number in score (0–1)`
|
|
38
|
+
: `outcome value must be a finite number, got ${String(opts.value)}`);
|
|
39
|
+
}
|
|
35
40
|
const { scaleMax, scaleLabel } = scaleFields(opts.scale);
|
|
36
41
|
return {
|
|
37
42
|
id: (0, node_crypto_1.randomUUID)(),
|
|
@@ -51,6 +56,8 @@ function buildGradeRow(subject, opts) {
|
|
|
51
56
|
// a single-score grade's row is byte-identical to pre-F4). Structural
|
|
52
57
|
// normalization (dedup/clamp/scrub) is the server's job at persist.
|
|
53
58
|
...(opts.criteria !== undefined ? { criteria: opts.criteria } : {}),
|
|
59
|
+
// ARC-3: spread-iff so a non-outcome row is byte-identical to pre-ARC.
|
|
60
|
+
...(opts.value !== undefined ? { value: opts.value } : {}),
|
|
54
61
|
};
|
|
55
62
|
}
|
|
56
63
|
/** Build the rig's `grade` surface over a grade writer (the telemetry store).
|
|
@@ -59,6 +66,13 @@ function buildGradeRow(subject, opts) {
|
|
|
59
66
|
function makeGradeApi(store) {
|
|
60
67
|
return {
|
|
61
68
|
grade(subject, opts) {
|
|
69
|
+
// L4: an outcome is written through rig.outcome (it derives the subject,
|
|
70
|
+
// the outcome:<name> grader, the score and the idempotent id) — a bare
|
|
71
|
+
// rig.grade({ kind: "outcome" }) would be dropped server-side, silently.
|
|
72
|
+
if (opts.kind === "outcome") {
|
|
73
|
+
throw new Error('rig.grade does not record outcomes — use rig.outcome({ key, name, score }) ' +
|
|
74
|
+
"(it files the outcome under outcome:<name> with an idempotent id)");
|
|
75
|
+
}
|
|
62
76
|
const row = buildGradeRow(subject, opts);
|
|
63
77
|
store.recordGrade(row);
|
|
64
78
|
return row;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARC-3 — outcomes: the delayed business ground truth, written as a GRADE.
|
|
3
|
+
*
|
|
4
|
+
* An outcome (resolved, reopened, booked, approved, CTR …) arrives hours to
|
|
5
|
+
* weeks after the call that earned it. It is stored as ONE `grades` row with
|
|
6
|
+
* `kind: "outcome"`, `grader: "outcome:<name>"`, `score` 0–1 (the primitive the
|
|
7
|
+
* verdict is derived from) and an optional raw `value` (00089). No new table,
|
|
8
|
+
* no pending store: a conversation-keyed outcome is stored
|
|
9
|
+
* `subject_kind = "run"`, `subject_id = <episode_key>` (the 00070 precedent) and
|
|
10
|
+
* the console joins it lazily at read time — so an outcome may arrive before,
|
|
11
|
+
* during or after its telemetry and nothing is ever dropped.
|
|
12
|
+
*
|
|
13
|
+
* This module is the ONE definition of the key mapping, the name rule, the
|
|
14
|
+
* score derivation and the idempotent id — shared by `rig.outcome` (SDK),
|
|
15
|
+
* `POST /v1/outcomes` (server) and the oracle's `record_outcome` (MCP), so the
|
|
16
|
+
* three surfaces cannot disagree about what an outcome is.
|
|
17
|
+
*
|
|
18
|
+
* An outcome is a RECEIPT, never a calibration gate (ARC-D3): outcome agreement
|
|
19
|
+
* never moves a grader to CALIBRATED on its own.
|
|
20
|
+
*/
|
|
21
|
+
import type { GradeRow, GradeSubject } from "../types/telemetry";
|
|
22
|
+
/** What the outcome is ABOUT. A `conversation` key is the caller's own
|
|
23
|
+
* conversation id (= the run's `episode_key`); the others are ModelRig ids. */
|
|
24
|
+
export type OutcomeKey = {
|
|
25
|
+
readonly run: string;
|
|
26
|
+
} | {
|
|
27
|
+
readonly conversation: string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly inference: string;
|
|
30
|
+
} | {
|
|
31
|
+
readonly artifact: string;
|
|
32
|
+
};
|
|
33
|
+
export interface OutcomeInput {
|
|
34
|
+
readonly key: OutcomeKey;
|
|
35
|
+
/** `^[a-z][a-z0-9_.-]{0,63}$` — e.g. `resolved`, `meeting_booked`, `ctr.7d`. */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** 0–1, or a boolean (true ⇒ 1, false ⇒ 0). Required unless `value` and
|
|
38
|
+
* `scale.max` are both given, in which case score = clamp(value / max, 0, 1). */
|
|
39
|
+
readonly score?: number | boolean;
|
|
40
|
+
/** The raw business number (revenue, CTR, handle time). Stored as-is. */
|
|
41
|
+
readonly value?: number;
|
|
42
|
+
/** Display scale; `max` also drives the score derivation from `value`. */
|
|
43
|
+
readonly scale?: {
|
|
44
|
+
readonly max: number;
|
|
45
|
+
} | {
|
|
46
|
+
readonly label: string;
|
|
47
|
+
};
|
|
48
|
+
/** When the outcome happened (ISO-8601). Default: now. */
|
|
49
|
+
readonly at?: string;
|
|
50
|
+
/** Retry-safe id: the same (org, name, idempotencyKey) always yields the same
|
|
51
|
+
* grade id — UUIDv5(org, `${name}:${idempotencyKey}`), identical on the SDK and
|
|
52
|
+
* the API paths — so a retry never duplicates, and two DIFFERENT outcome names
|
|
53
|
+
* sent under one key (one webhook event) both land. One key = one outcome per name. */
|
|
54
|
+
readonly idempotencyKey?: string;
|
|
55
|
+
/** Optional free-text note (scrubbed server-side like every grade comment). */
|
|
56
|
+
readonly comment?: string;
|
|
57
|
+
}
|
|
58
|
+
/** The name rule — lowercase identifier, ≤64 chars. */
|
|
59
|
+
export declare const OUTCOME_NAME_PATTERN: RegExp;
|
|
60
|
+
export declare const OUTCOME_GRADER_PREFIX = "outcome:";
|
|
61
|
+
/** A teaching validation failure (the server maps it to a 422). */
|
|
62
|
+
export declare class OutcomeValidationError extends Error {
|
|
63
|
+
readonly fixHint: string;
|
|
64
|
+
constructor(message: string, fixHint: string);
|
|
65
|
+
}
|
|
66
|
+
/** The grade subject an outcome key maps to. `conversation` → a RUN subject whose
|
|
67
|
+
* id is the episode key (00070 precedent; resolved lazily at read time). */
|
|
68
|
+
export declare function outcomeSubject(key: OutcomeKey): GradeSubject;
|
|
69
|
+
/** Validate the outcome name; returns the grader name `outcome:<name>`. */
|
|
70
|
+
export declare function outcomeGraderName(name: string): string;
|
|
71
|
+
/** Derive the 0–1 score: an explicit score (or boolean) wins; else value/scale.max
|
|
72
|
+
* clamped to [0, 1]; else a teaching error. */
|
|
73
|
+
export declare function deriveOutcomeScore(input: Pick<OutcomeInput, "score" | "value" | "scale">): number;
|
|
74
|
+
/** RFC 4122 UUIDv5 (SHA-1, name-based) — deterministic, so an idempotency key
|
|
75
|
+
* maps to the same grade id on every retry. `namespace` must be a UUID. */
|
|
76
|
+
export declare function uuidV5(name: string, namespace: string): string;
|
|
77
|
+
/**
|
|
78
|
+
* THE id function for an idempotent outcome (fix round 1, L3) — shared by
|
|
79
|
+
* `rig.outcome` (SDK), `POST /v1/outcomes` and `/v1/ingest`:
|
|
80
|
+
* UUIDv5(namespace = org id, name = `${name}:${idempotencyKey}`). The SDK does
|
|
81
|
+
* not know its org locally, so it mints a PROVISIONAL id (null org → a fixed
|
|
82
|
+
* namespace) and ships the idempotency key on the wire; the server re-derives
|
|
83
|
+
* the id with the key's org at ingest, so both doors land on the same row.
|
|
84
|
+
*/
|
|
85
|
+
export declare function outcomeGradeId(orgId: string | null, name: string, idempotencyKey: string): string;
|
|
86
|
+
/** Build the outcome's GradeRow. `orgId` keys the idempotent id (and names the
|
|
87
|
+
* grade's org on a server write); `source` is provenance ("sdk", "outcomes-api", …). */
|
|
88
|
+
export declare function buildOutcomeGradeRow(input: OutcomeInput, ctx: {
|
|
89
|
+
readonly orgId?: string | null;
|
|
90
|
+
readonly source: string;
|
|
91
|
+
}): GradeRow;
|
|
92
|
+
/** `rig.outcome` — record one outcome locally; the existing grade exporter ships
|
|
93
|
+
* it (no new exporter). Returns the subject it was filed under. Throws an
|
|
94
|
+
* {@link OutcomeValidationError} on bad input (it is caller code, not telemetry). */
|
|
95
|
+
export declare function makeOutcomeApi(store: {
|
|
96
|
+
recordGrade(row: GradeRow): void;
|
|
97
|
+
}): {
|
|
98
|
+
outcome(input: OutcomeInput): {
|
|
99
|
+
readonly id: string;
|
|
100
|
+
readonly subject: GradeSubject;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=outcome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcome.d.ts","sourceRoot":"","sources":["../../src/feedback/outcome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEjE;+EAC+E;AAC/E,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACxB;IAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;qFACiF;IACjF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC,yEAAyE;IACzE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACvE,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB;;;2FAGuF;IACvF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,QAA6B,CAAC;AAC/D,eAAO,MAAM,qBAAqB,aAAa,CAAC;AAIhD,mEAAmE;AACnE,qBAAa,sBAAuB,SAAQ,KAAK;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK7C;AAED;4EAC4E;AAC5E,wBAAgB,cAAc,CAAC,GAAG,EAAE,UAAU,GAAG,YAAY,CA6B5D;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQtD;AAED;+CAC+C;AAC/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC,GAAG,MAAM,CAyBjG;AAED;2EAC2E;AAC3E,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAS9D;AAMD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAGjG;AAcD;wFACwF;AACxF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC/D,QAAQ,CA8BV;AAED;;qFAEqF;AACrF,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,GAAG;IAC3E,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;KAAE,CAAC;CACvF,CAQA"}
|