@runtypelabs/sdk 5.4.0 → 5.6.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/dist/index.cjs +939 -767
- package/dist/index.d.cts +464 -317
- package/dist/index.d.ts +464 -317
- package/dist/index.mjs +930 -767
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1887,315 +1887,755 @@ function resolveBatchExecutionId(pausedTools) {
|
|
|
1887
1887
|
return "";
|
|
1888
1888
|
}
|
|
1889
1889
|
|
|
1890
|
-
// src/
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1890
|
+
// src/evals-ensure.ts
|
|
1891
|
+
var CHECK_GRADER_KINDS = /* @__PURE__ */ new Set([
|
|
1892
|
+
"contains",
|
|
1893
|
+
"not_contains",
|
|
1894
|
+
"matches_expected",
|
|
1895
|
+
"regex",
|
|
1896
|
+
"valid_json",
|
|
1897
|
+
"json_field",
|
|
1898
|
+
"length",
|
|
1899
|
+
"latency",
|
|
1900
|
+
"no_error",
|
|
1901
|
+
// Trace checks.
|
|
1902
|
+
"called_tool",
|
|
1903
|
+
"not_called_tool",
|
|
1904
|
+
"used_no_tools",
|
|
1905
|
+
"max_tool_calls",
|
|
1906
|
+
"tool_order",
|
|
1907
|
+
"ran_step",
|
|
1908
|
+
"step_order",
|
|
1909
|
+
"completed",
|
|
1910
|
+
"cost"
|
|
1911
|
+
]);
|
|
1912
|
+
function gradeable(data) {
|
|
1913
|
+
const obj = { ...data };
|
|
1914
|
+
const rebuild = (patch) => gradeable({ ...obj, ...patch });
|
|
1915
|
+
Object.defineProperty(obj, "gate", {
|
|
1916
|
+
value: () => rebuild({ severity: "gate" }),
|
|
1917
|
+
enumerable: false
|
|
1918
|
+
});
|
|
1919
|
+
Object.defineProperty(obj, "soft", {
|
|
1920
|
+
value: () => rebuild({ severity: "soft" }),
|
|
1921
|
+
enumerable: false
|
|
1922
|
+
});
|
|
1923
|
+
if (data.kind === "ai") {
|
|
1924
|
+
Object.defineProperty(obj, "atLeast", {
|
|
1925
|
+
value: (threshold) => {
|
|
1926
|
+
if (typeof threshold !== "number" || !Number.isFinite(threshold) || threshold < 1 || threshold > 5) {
|
|
1927
|
+
throw new Error("atLeast() requires a numeric judge threshold between 1 and 5");
|
|
1906
1928
|
}
|
|
1907
|
-
return
|
|
1908
|
-
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
}
|
|
1929
|
+
return rebuild({ threshold });
|
|
1930
|
+
},
|
|
1931
|
+
enumerable: false
|
|
1932
|
+
});
|
|
1912
1933
|
}
|
|
1913
|
-
return
|
|
1934
|
+
return obj;
|
|
1914
1935
|
}
|
|
1915
|
-
function
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
|
|
1922
|
-
config: normalizeConfigForHash(stepObj.config),
|
|
1923
|
-
order: typeof stepObj.order === "number" ? stepObj.order : 0
|
|
1924
|
-
};
|
|
1936
|
+
function contains(value, opts) {
|
|
1937
|
+
return gradeable({
|
|
1938
|
+
kind: "contains",
|
|
1939
|
+
value,
|
|
1940
|
+
...opts?.caseSensitive ? { caseSensitive: true } : {}
|
|
1941
|
+
});
|
|
1925
1942
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
})
|
|
1932
|
-
const serialized = JSON.stringify(normalized);
|
|
1933
|
-
const encoded = new TextEncoder().encode(serialized);
|
|
1934
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
1935
|
-
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1943
|
+
function notContains(value, opts) {
|
|
1944
|
+
return gradeable({
|
|
1945
|
+
kind: "not_contains",
|
|
1946
|
+
value,
|
|
1947
|
+
...opts?.caseSensitive ? { caseSensitive: true } : {}
|
|
1948
|
+
});
|
|
1936
1949
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
"type",
|
|
1940
|
-
"name",
|
|
1941
|
-
"order",
|
|
1942
|
-
"enabled",
|
|
1943
|
-
"when",
|
|
1944
|
-
"config"
|
|
1945
|
-
]);
|
|
1946
|
-
function collectStepNonPortableToolRefs(config, path) {
|
|
1947
|
-
const found = [];
|
|
1948
|
-
const tools = config.tools;
|
|
1949
|
-
const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
|
|
1950
|
-
const isRawId = (ref, prefix) => typeof ref === "string" && ref.startsWith(prefix);
|
|
1951
|
-
const scanArray = (value, subPath) => {
|
|
1952
|
-
if (!Array.isArray(value)) return;
|
|
1953
|
-
value.forEach((ref, i) => {
|
|
1954
|
-
if (isAccountScoped(ref)) found.push(`${subPath}[${i}]`);
|
|
1955
|
-
});
|
|
1956
|
-
};
|
|
1957
|
-
const scanKeys = (value, subPath) => {
|
|
1958
|
-
if (!isPlainObject(value)) return;
|
|
1959
|
-
for (const key of Object.keys(value)) {
|
|
1960
|
-
if (isAccountScoped(key)) found.push(`${subPath}.${key}`);
|
|
1961
|
-
}
|
|
1962
|
-
};
|
|
1963
|
-
if (isPlainObject(tools)) {
|
|
1964
|
-
scanArray(tools.toolIds, `${path}.tools.toolIds`);
|
|
1965
|
-
scanKeys(tools.toolConfigs, `${path}.tools.toolConfigs`);
|
|
1966
|
-
scanKeys(tools.perToolLimits, `${path}.tools.perToolLimits`);
|
|
1967
|
-
if (isPlainObject(tools.approval)) {
|
|
1968
|
-
scanArray(tools.approval.require, `${path}.tools.approval.require`);
|
|
1969
|
-
}
|
|
1970
|
-
if (isPlainObject(tools.subagentConfig)) {
|
|
1971
|
-
scanArray(tools.subagentConfig.toolPool, `${path}.tools.subagentConfig.toolPool`);
|
|
1972
|
-
}
|
|
1973
|
-
if (isPlainObject(tools.codeModeConfig)) {
|
|
1974
|
-
scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
|
|
1975
|
-
}
|
|
1976
|
-
if (Array.isArray(tools.runtimeTools)) {
|
|
1977
|
-
tools.runtimeTools.forEach((runtimeTool, i) => {
|
|
1978
|
-
if (!isPlainObject(runtimeTool) || !isPlainObject(runtimeTool.config)) return;
|
|
1979
|
-
const base = `${path}.tools.runtimeTools[${i}].config`;
|
|
1980
|
-
const rtConfig = runtimeTool.config;
|
|
1981
|
-
if (runtimeTool.toolType === "subagent" && isRawId(rtConfig.agentId, "agent_")) {
|
|
1982
|
-
found.push(`${base}.agentId`);
|
|
1983
|
-
} else if (runtimeTool.toolType === "flow" && isRawId(rtConfig.flowId, "flow_")) {
|
|
1984
|
-
found.push(`${base}.flowId`);
|
|
1985
|
-
}
|
|
1986
|
-
});
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
if (isAccountScoped(config.toolId)) {
|
|
1990
|
-
found.push(`${path}.toolId`);
|
|
1991
|
-
}
|
|
1992
|
-
if (isRawId(config.agentId, "agent_")) {
|
|
1993
|
-
found.push(`${path}.agentId`);
|
|
1994
|
-
}
|
|
1995
|
-
for (const branch of ["trueSteps", "falseSteps"]) {
|
|
1996
|
-
const nested = config[branch];
|
|
1997
|
-
if (!Array.isArray(nested)) continue;
|
|
1998
|
-
nested.forEach((nestedStep, i) => {
|
|
1999
|
-
if (isPlainObject(nestedStep) && isPlainObject(nestedStep.config)) {
|
|
2000
|
-
found.push(
|
|
2001
|
-
...collectStepNonPortableToolRefs(
|
|
2002
|
-
nestedStep.config,
|
|
2003
|
-
`${path}.${branch}[${i}].config`
|
|
2004
|
-
)
|
|
2005
|
-
);
|
|
2006
|
-
}
|
|
2007
|
-
});
|
|
2008
|
-
}
|
|
2009
|
-
return found;
|
|
1950
|
+
function matchesExpected() {
|
|
1951
|
+
return gradeable({ kind: "matches_expected" });
|
|
2010
1952
|
}
|
|
2011
|
-
function
|
|
2012
|
-
|
|
2013
|
-
|
|
1953
|
+
function regex(pattern, flags) {
|
|
1954
|
+
return gradeable({ kind: "regex", pattern, ...flags ? { flags } : {} });
|
|
1955
|
+
}
|
|
1956
|
+
function validJson() {
|
|
1957
|
+
return gradeable({ kind: "valid_json" });
|
|
1958
|
+
}
|
|
1959
|
+
function jsonField(path, opts) {
|
|
1960
|
+
return gradeable({
|
|
1961
|
+
kind: "json_field",
|
|
1962
|
+
path,
|
|
1963
|
+
...opts && "equals" in opts && opts.equals !== void 0 ? { equals: opts.equals } : {},
|
|
1964
|
+
...opts && typeof opts.exists === "boolean" ? { exists: opts.exists } : {}
|
|
1965
|
+
});
|
|
1966
|
+
}
|
|
1967
|
+
function length(opts) {
|
|
1968
|
+
if (!opts || opts.minChars === void 0 && opts.maxChars === void 0) {
|
|
1969
|
+
throw new Error("length() requires at least one of minChars or maxChars");
|
|
2014
1970
|
}
|
|
2015
|
-
|
|
2016
|
-
|
|
1971
|
+
return gradeable({
|
|
1972
|
+
kind: "length",
|
|
1973
|
+
...opts.minChars !== void 0 ? { minChars: opts.minChars } : {},
|
|
1974
|
+
...opts.maxChars !== void 0 ? { maxChars: opts.maxChars } : {}
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
function latency(maxMs) {
|
|
1978
|
+
if (!Number.isFinite(maxMs) || maxMs <= 0) {
|
|
1979
|
+
throw new Error("latency() requires a positive maxMs");
|
|
2017
1980
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
1981
|
+
return gradeable({ kind: "latency", maxMs });
|
|
1982
|
+
}
|
|
1983
|
+
function noError() {
|
|
1984
|
+
return gradeable({ kind: "no_error" });
|
|
1985
|
+
}
|
|
1986
|
+
function calledTool(name, opts) {
|
|
1987
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
1988
|
+
throw new Error("calledTool() requires a non-empty tool name");
|
|
2023
1989
|
}
|
|
2024
|
-
if (!
|
|
2025
|
-
throw new Error('
|
|
1990
|
+
if (opts?.times !== void 0 && (!Number.isInteger(opts.times) || opts.times <= 0)) {
|
|
1991
|
+
throw new Error('calledTool() "times" must be a positive integer');
|
|
2026
1992
|
}
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
}
|
|
2034
|
-
if (typeof step.name !== "string" || step.name.length === 0) {
|
|
2035
|
-
throw new Error(`defineFlow: steps[${index}] requires a non-empty string "name"`);
|
|
2036
|
-
}
|
|
2037
|
-
const unknownStepKeys = Object.keys(step).filter((key) => !DEFINE_FLOW_STEP_KEYS.has(key));
|
|
2038
|
-
if (unknownStepKeys.length > 0) {
|
|
2039
|
-
throw new Error(
|
|
2040
|
-
`defineFlow: steps[${index}] has unknown field(s): ${unknownStepKeys.join(", ")}. Allowed step fields are type, name, order, enabled, when, config. (Step ids are server artifacts and not part of a portable definition.)`
|
|
2041
|
-
);
|
|
2042
|
-
}
|
|
2043
|
-
const config = isPlainObject(step.config) ? step.config : void 0;
|
|
2044
|
-
if (config) {
|
|
2045
|
-
const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
|
|
2046
|
-
if (nonPortable.length > 0) {
|
|
2047
|
-
throw new Error(
|
|
2048
|
-
`defineFlow: account-scoped reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026/agent_\u2026/flow_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references, or reference a saved resource by name \u2014 tool:<name>, agent:<name>, or flow:<name> instead.`
|
|
2049
|
-
);
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
return {
|
|
2053
|
-
type: step.type,
|
|
2054
|
-
name: step.name,
|
|
2055
|
-
// Explicit 1-based order (the flow builder's convention) so the local
|
|
2056
|
-
// probe hash agrees with the server's persisted step order.
|
|
2057
|
-
order: typeof step.order === "number" ? step.order : index + 1,
|
|
2058
|
-
...step.enabled !== void 0 ? { enabled: step.enabled } : {},
|
|
2059
|
-
...typeof step.when === "string" ? { when: step.when } : {},
|
|
2060
|
-
...config ? { config } : {}
|
|
2061
|
-
};
|
|
1993
|
+
return gradeable({
|
|
1994
|
+
kind: "called_tool",
|
|
1995
|
+
name,
|
|
1996
|
+
...opts && "input" in opts && opts.input !== void 0 ? { input: opts.input } : {},
|
|
1997
|
+
...opts && "output" in opts && opts.output !== void 0 ? { output: opts.output } : {},
|
|
1998
|
+
...opts && typeof opts.isError === "boolean" ? { isError: opts.isError } : {},
|
|
1999
|
+
...opts?.times !== void 0 ? { times: opts.times } : {}
|
|
2062
2000
|
});
|
|
2063
|
-
return { name: input.name, steps };
|
|
2064
2001
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
this.name = "FlowEnsureConflictError";
|
|
2069
|
-
this.code = body.code;
|
|
2070
|
-
this.lastModifiedSource = body.lastModifiedSource;
|
|
2071
|
-
this.modifiedAt = body.modifiedAt;
|
|
2072
|
-
this.currentHash = body.currentHash;
|
|
2002
|
+
function notCalledTool(name) {
|
|
2003
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
2004
|
+
throw new Error("notCalledTool() requires a non-empty tool name");
|
|
2073
2005
|
}
|
|
2074
|
-
};
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2006
|
+
return gradeable({ kind: "not_called_tool", name });
|
|
2007
|
+
}
|
|
2008
|
+
function usedNoTools() {
|
|
2009
|
+
return gradeable({ kind: "used_no_tools" });
|
|
2010
|
+
}
|
|
2011
|
+
function maxToolCalls(max) {
|
|
2012
|
+
if (!Number.isInteger(max) || max < 0) {
|
|
2013
|
+
throw new Error("maxToolCalls() requires a non-negative integer");
|
|
2082
2014
|
}
|
|
2083
|
-
};
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
try {
|
|
2089
|
-
return { status: Number(match[1]), body: JSON.parse(match[2]) };
|
|
2090
|
-
} catch {
|
|
2091
|
-
return { status: Number(match[1]), body: null };
|
|
2015
|
+
return gradeable({ kind: "max_tool_calls", max });
|
|
2016
|
+
}
|
|
2017
|
+
function toolOrder(tools) {
|
|
2018
|
+
if (!Array.isArray(tools) || tools.length === 0) {
|
|
2019
|
+
throw new Error("toolOrder() requires a non-empty array of tool names");
|
|
2092
2020
|
}
|
|
2021
|
+
return gradeable({ kind: "tool_order", tools });
|
|
2093
2022
|
}
|
|
2094
|
-
function
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
return new FlowEnsureConflictError(
|
|
2100
|
-
body
|
|
2101
|
-
);
|
|
2023
|
+
function ranStep(name) {
|
|
2024
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
2025
|
+
throw new Error("ranStep() requires a non-empty step name");
|
|
2026
|
+
}
|
|
2027
|
+
return gradeable({ kind: "ran_step", name });
|
|
2102
2028
|
}
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
if (!memo) {
|
|
2107
|
-
memo = /* @__PURE__ */ new Map();
|
|
2108
|
-
serverHashMemo.set(client, memo);
|
|
2029
|
+
function stepOrder(steps) {
|
|
2030
|
+
if (!Array.isArray(steps) || steps.length === 0) {
|
|
2031
|
+
throw new Error("stepOrder() requires a non-empty array of step names");
|
|
2109
2032
|
}
|
|
2110
|
-
return
|
|
2033
|
+
return gradeable({ kind: "step_order", steps });
|
|
2111
2034
|
}
|
|
2112
|
-
function
|
|
2113
|
-
|
|
2035
|
+
function completed() {
|
|
2036
|
+
return gradeable({ kind: "completed" });
|
|
2114
2037
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
"/flows/ensure",
|
|
2119
|
-
body
|
|
2120
|
-
);
|
|
2121
|
-
} catch (err) {
|
|
2122
|
-
const conflict = toConflictError(err);
|
|
2123
|
-
if (conflict) throw conflict;
|
|
2124
|
-
throw err;
|
|
2038
|
+
function cost(maxUsd) {
|
|
2039
|
+
if (!Number.isFinite(maxUsd) || maxUsd <= 0) {
|
|
2040
|
+
throw new Error("cost() requires a positive maxUsd");
|
|
2125
2041
|
}
|
|
2042
|
+
return gradeable({ kind: "cost", maxUsd });
|
|
2126
2043
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2044
|
+
function judge(criteria, opts) {
|
|
2045
|
+
if (typeof criteria !== "string" || criteria.trim().length === 0) {
|
|
2046
|
+
throw new Error("judge() requires non-empty criteria");
|
|
2047
|
+
}
|
|
2048
|
+
return gradeable({
|
|
2049
|
+
kind: "ai",
|
|
2050
|
+
criteria,
|
|
2051
|
+
...opts?.preset ? { preset: opts.preset } : {},
|
|
2052
|
+
...opts?.useExpected ? { useExpected: true } : {},
|
|
2053
|
+
...opts?.model ? { model: opts.model } : {},
|
|
2054
|
+
...opts?.threshold !== void 0 ? { threshold: opts.threshold } : {}
|
|
2055
|
+
});
|
|
2056
|
+
}
|
|
2057
|
+
var judges = {
|
|
2058
|
+
answersQuestion: () => judge(
|
|
2059
|
+
"The response directly addresses what the user asked, without dodging or answering a different question.",
|
|
2060
|
+
{ preset: "answersQuestion" }
|
|
2061
|
+
),
|
|
2062
|
+
matchesExpected: () => judge(
|
|
2063
|
+
"The response conveys the same facts and conclusion as the expected answer. Wording may differ.",
|
|
2064
|
+
{ preset: "matchesExpected", useExpected: true }
|
|
2065
|
+
),
|
|
2066
|
+
followsInstructions: () => judge(
|
|
2067
|
+
"The response obeys every instruction in the system prompt (format, tone, constraints, refusals).",
|
|
2068
|
+
{ preset: "followsInstructions" }
|
|
2069
|
+
),
|
|
2070
|
+
grounded: () => judge(
|
|
2071
|
+
"Every factual claim in the response is supported by the provided context or the expected answer. Flag anything invented.",
|
|
2072
|
+
{ preset: "grounded" }
|
|
2073
|
+
),
|
|
2074
|
+
rightTone: (voice = "{describe the voice you want}") => judge(`The response matches this voice: ${voice}.`, { preset: "rightTone" }),
|
|
2075
|
+
safeToSend: () => judge(
|
|
2076
|
+
"The response contains nothing embarrassing to show a customer: no leaked internals, no hostile tone, no policy violations.",
|
|
2077
|
+
{ preset: "safeToSend" }
|
|
2078
|
+
)
|
|
2079
|
+
};
|
|
2080
|
+
var DEFINE_EVAL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
2081
|
+
"name",
|
|
2082
|
+
"target",
|
|
2083
|
+
"graders",
|
|
2084
|
+
"cases",
|
|
2085
|
+
"virtual"
|
|
2086
|
+
]);
|
|
2087
|
+
var DEFINE_EVAL_CASE_KEYS = /* @__PURE__ */ new Set(["name", "input", "expected", "expect"]);
|
|
2088
|
+
function isPlainObject(value) {
|
|
2089
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2090
|
+
}
|
|
2091
|
+
function normalizeTarget(target) {
|
|
2092
|
+
if (!isPlainObject(target)) {
|
|
2093
|
+
throw new Error('defineEval requires a "target" object: { flow: name } or { agent: name }');
|
|
2094
|
+
}
|
|
2095
|
+
const hasFlow = typeof target.flow === "string" && target.flow.length > 0;
|
|
2096
|
+
const hasAgent = typeof target.agent === "string" && target.agent.length > 0;
|
|
2097
|
+
if (hasFlow === hasAgent) {
|
|
2098
|
+
throw new Error(
|
|
2099
|
+
'defineEval "target" must name exactly one of flow or agent: { flow: "name" } XOR { agent: "name" }'
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
2102
|
+
const extraKeys = Object.keys(target).filter((k) => k !== "flow" && k !== "agent");
|
|
2103
|
+
if (extraKeys.length > 0) {
|
|
2104
|
+
throw new Error(`defineEval "target" has unknown field(s): ${extraKeys.join(", ")}`);
|
|
2105
|
+
}
|
|
2106
|
+
return hasFlow ? { flow: target.flow } : { agent: target.agent };
|
|
2107
|
+
}
|
|
2108
|
+
function validateGrader(grader, where) {
|
|
2109
|
+
if (!isPlainObject(grader) || typeof grader.kind !== "string") {
|
|
2110
|
+
throw new Error(`defineEval: ${where} must be a grader object with a string "kind"`);
|
|
2111
|
+
}
|
|
2112
|
+
if (grader.kind === "ai") {
|
|
2113
|
+
if (typeof grader.criteria !== "string" || grader.criteria.trim().length === 0) {
|
|
2114
|
+
throw new Error(`defineEval: ${where} is an AI grader and requires non-empty "criteria"`);
|
|
2115
|
+
}
|
|
2116
|
+
return grader;
|
|
2117
|
+
}
|
|
2118
|
+
if (!CHECK_GRADER_KINDS.has(grader.kind)) {
|
|
2119
|
+
throw new Error(
|
|
2120
|
+
`defineEval: ${where} has unknown grader kind "${grader.kind}". Known kinds: ${[...CHECK_GRADER_KINDS].join(", ")}, ai.`
|
|
2121
|
+
);
|
|
2122
|
+
}
|
|
2123
|
+
return grader;
|
|
2124
|
+
}
|
|
2125
|
+
function normalizeCaseInput(input, where) {
|
|
2126
|
+
if (input === void 0) return {};
|
|
2127
|
+
if (!isPlainObject(input)) {
|
|
2128
|
+
throw new Error(`defineEval: ${where} "input" must be an object`);
|
|
2129
|
+
}
|
|
2130
|
+
const out = {};
|
|
2131
|
+
if (input.variables !== void 0) {
|
|
2132
|
+
if (!isPlainObject(input.variables)) {
|
|
2133
|
+
throw new Error(`defineEval: ${where} "input.variables" must be an object`);
|
|
2134
|
+
}
|
|
2135
|
+
out.variables = input.variables;
|
|
2136
|
+
}
|
|
2137
|
+
if (input.messages !== void 0) {
|
|
2138
|
+
if (!Array.isArray(input.messages)) {
|
|
2139
|
+
throw new Error(`defineEval: ${where} "input.messages" must be an array`);
|
|
2140
|
+
}
|
|
2141
|
+
out.messages = input.messages.map((m, i) => {
|
|
2142
|
+
if (!isPlainObject(m) || typeof m.role !== "string" || typeof m.content !== "string") {
|
|
2143
|
+
throw new Error(`defineEval: ${where} "input.messages[${i}]" must be { role, content }`);
|
|
2144
|
+
}
|
|
2145
|
+
return { role: m.role, content: m.content };
|
|
2140
2146
|
});
|
|
2141
|
-
|
|
2142
|
-
|
|
2147
|
+
}
|
|
2148
|
+
return out;
|
|
2149
|
+
}
|
|
2150
|
+
function defineEval(input) {
|
|
2151
|
+
if (!input || typeof input !== "object") {
|
|
2152
|
+
throw new Error("defineEval requires a definition object");
|
|
2153
|
+
}
|
|
2154
|
+
const unknownKeys = Object.keys(input).filter((k) => !DEFINE_EVAL_TOP_LEVEL_KEYS.has(k));
|
|
2155
|
+
if (unknownKeys.length > 0) {
|
|
2156
|
+
throw new Error(
|
|
2157
|
+
`defineEval: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are target, graders, cases, virtual.`
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
const target = normalizeTarget(input.target);
|
|
2161
|
+
if (input.name !== void 0 && (typeof input.name !== "string" || input.name.length === 0)) {
|
|
2162
|
+
throw new Error('defineEval "name" must be a non-empty string when provided');
|
|
2163
|
+
}
|
|
2164
|
+
const name = input.name ?? ("flow" in target ? `flow:${target.flow}` : `agent:${target.agent}`);
|
|
2165
|
+
const suiteGraders = (input.graders ?? []).map((g, i) => validateGrader(g, `graders[${i}]`));
|
|
2166
|
+
if (!Array.isArray(input.cases) || input.cases.length === 0) {
|
|
2167
|
+
throw new Error('defineEval requires a non-empty "cases" array');
|
|
2168
|
+
}
|
|
2169
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
2170
|
+
const cases = input.cases.map((c, index) => {
|
|
2171
|
+
if (!isPlainObject(c)) {
|
|
2172
|
+
throw new Error(`defineEval: cases[${index}] must be an object`);
|
|
2143
2173
|
}
|
|
2144
|
-
if (
|
|
2145
|
-
throw new
|
|
2174
|
+
if (typeof c.name !== "string" || c.name.length === 0) {
|
|
2175
|
+
throw new Error(`defineEval: cases[${index}] requires a non-empty string "name"`);
|
|
2146
2176
|
}
|
|
2147
|
-
|
|
2177
|
+
if (seenNames.has(c.name)) {
|
|
2178
|
+
throw new Error(`defineEval: duplicate case name "${c.name}" (case names are the identity)`);
|
|
2179
|
+
}
|
|
2180
|
+
seenNames.add(c.name);
|
|
2181
|
+
const unknownCaseKeys = Object.keys(c).filter((k) => !DEFINE_EVAL_CASE_KEYS.has(k));
|
|
2182
|
+
if (unknownCaseKeys.length > 0) {
|
|
2183
|
+
throw new Error(
|
|
2184
|
+
`defineEval: cases[${index}] ("${c.name}") has unknown field(s): ${unknownCaseKeys.join(
|
|
2185
|
+
", "
|
|
2186
|
+
)}. Allowed case fields are name, input, expected, expect.`
|
|
2187
|
+
);
|
|
2188
|
+
}
|
|
2189
|
+
const caseGraders = (c.expect ?? []).map(
|
|
2190
|
+
(g, i) => validateGrader(g, `cases[${index}].expect[${i}]`)
|
|
2191
|
+
);
|
|
2192
|
+
const expect = [...suiteGraders, ...caseGraders];
|
|
2193
|
+
if (expect.length === 0) {
|
|
2194
|
+
throw new Error(
|
|
2195
|
+
`defineEval: cases[${index}] ("${c.name}") has no graders. Add suite-level "graders" or case-level "expect" so there is something to score.`
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
if (c.expected !== void 0 && !isPlainObject(c.expected)) {
|
|
2199
|
+
throw new Error(`defineEval: cases[${index}] ("${c.name}") "expected" must be an object`);
|
|
2200
|
+
}
|
|
2201
|
+
return {
|
|
2202
|
+
name: c.name,
|
|
2203
|
+
input: normalizeCaseInput(c.input, `cases[${index}] ("${c.name}")`),
|
|
2204
|
+
...c.expected !== void 0 ? { expected: c.expected } : {},
|
|
2205
|
+
expect
|
|
2206
|
+
};
|
|
2207
|
+
});
|
|
2208
|
+
return { name, target, cases, virtual: input.virtual === true };
|
|
2209
|
+
}
|
|
2210
|
+
function normalizeForHash(value) {
|
|
2211
|
+
if (Array.isArray(value)) return value.map(normalizeForHash);
|
|
2212
|
+
if (isPlainObject(value)) {
|
|
2213
|
+
const out = {};
|
|
2214
|
+
for (const key of Object.keys(value).sort()) {
|
|
2215
|
+
const v = value[key];
|
|
2216
|
+
if (v === void 0) continue;
|
|
2217
|
+
out[key] = normalizeForHash(v);
|
|
2218
|
+
}
|
|
2219
|
+
return out;
|
|
2220
|
+
}
|
|
2221
|
+
return value;
|
|
2222
|
+
}
|
|
2223
|
+
async function computeEvalContentHash(definition) {
|
|
2224
|
+
const canonical = {
|
|
2225
|
+
target: normalizeForHash(definition.target),
|
|
2226
|
+
virtual: definition.virtual,
|
|
2227
|
+
cases: [...definition.cases].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map((c) => ({
|
|
2228
|
+
name: c.name,
|
|
2229
|
+
input: normalizeForHash(c.input),
|
|
2230
|
+
...c.expected !== void 0 ? { expected: normalizeForHash(c.expected) } : {},
|
|
2231
|
+
// Grader order preserved on purpose (it maps to the result index).
|
|
2232
|
+
expect: c.expect.map((g) => normalizeForHash(g))
|
|
2233
|
+
}))
|
|
2234
|
+
};
|
|
2235
|
+
const serialized = JSON.stringify(canonical);
|
|
2236
|
+
const encoded = new TextEncoder().encode(serialized);
|
|
2237
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
2238
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2239
|
+
}
|
|
2240
|
+
var serverHashMemo = /* @__PURE__ */ new WeakMap();
|
|
2241
|
+
function memoFor(client) {
|
|
2242
|
+
let memo = serverHashMemo.get(client);
|
|
2243
|
+
if (!memo) {
|
|
2244
|
+
memo = /* @__PURE__ */ new Map();
|
|
2245
|
+
serverHashMemo.set(client, memo);
|
|
2246
|
+
}
|
|
2247
|
+
return memo;
|
|
2248
|
+
}
|
|
2249
|
+
async function ensureEval(client, definition) {
|
|
2250
|
+
if (definition.virtual) {
|
|
2251
|
+
throw new Error(
|
|
2252
|
+
"Cannot ensure a virtual eval: virtual evals are ephemeral (nothing is persisted to converge). Remove `virtual: true` to converge a durable suite, or run it directly."
|
|
2253
|
+
);
|
|
2148
2254
|
}
|
|
2149
2255
|
const memo = memoFor(client);
|
|
2150
|
-
const localHash = await
|
|
2256
|
+
const localHash = await computeEvalContentHash(definition);
|
|
2151
2257
|
const memoKey = `${definition.name} ${localHash}`;
|
|
2152
2258
|
const contentHash = memo.get(memoKey) ?? localHash;
|
|
2153
|
-
const probe = await
|
|
2154
|
-
|
|
2155
|
-
contentHash
|
|
2156
|
-
|
|
2157
|
-
});
|
|
2259
|
+
const probe = await client.post(
|
|
2260
|
+
"/eval/ensure",
|
|
2261
|
+
{ name: definition.name, contentHash }
|
|
2262
|
+
);
|
|
2158
2263
|
if (probe.result !== "definitionRequired") {
|
|
2159
|
-
|
|
2264
|
+
memo.set(memoKey, probe.contentHash);
|
|
2160
2265
|
return probe;
|
|
2161
2266
|
}
|
|
2162
|
-
const converged = await
|
|
2163
|
-
|
|
2164
|
-
definition,
|
|
2165
|
-
|
|
2166
|
-
});
|
|
2267
|
+
const converged = await client.post(
|
|
2268
|
+
"/eval/ensure",
|
|
2269
|
+
{ name: definition.name, definition }
|
|
2270
|
+
);
|
|
2167
2271
|
if (converged.result === "definitionRequired") {
|
|
2168
2272
|
throw new Error("Server reported definitionRequired for a full-definition request");
|
|
2169
2273
|
}
|
|
2170
|
-
|
|
2274
|
+
memo.set(memoKey, converged.contentHash);
|
|
2171
2275
|
return converged;
|
|
2172
2276
|
}
|
|
2173
|
-
async function
|
|
2174
|
-
return client.get("/
|
|
2277
|
+
async function pullEval(client, name) {
|
|
2278
|
+
return client.get("/eval/pull", { name });
|
|
2279
|
+
}
|
|
2280
|
+
async function runEvalSuite(client, input) {
|
|
2281
|
+
return client.post("/eval/run", input);
|
|
2175
2282
|
}
|
|
2176
2283
|
|
|
2177
|
-
// src/flows-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2284
|
+
// src/flows-ensure.ts
|
|
2285
|
+
function isPlainObject2(value) {
|
|
2286
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2287
|
+
}
|
|
2288
|
+
function normalizeConfigForHash(config) {
|
|
2289
|
+
if (!isPlainObject2(config)) return {};
|
|
2290
|
+
const normalized = {};
|
|
2291
|
+
for (const key of Object.keys(config).sort()) {
|
|
2292
|
+
const value = config[key];
|
|
2293
|
+
if (value === void 0) continue;
|
|
2294
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
2295
|
+
normalized[key] = normalizeConfigForHash(value);
|
|
2296
|
+
} else if (Array.isArray(value)) {
|
|
2297
|
+
normalized[key] = value.map((item) => {
|
|
2298
|
+
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
2299
|
+
return normalizeConfigForHash(item);
|
|
2300
|
+
}
|
|
2301
|
+
return item;
|
|
2302
|
+
});
|
|
2303
|
+
} else {
|
|
2304
|
+
normalized[key] = value;
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return normalized;
|
|
2308
|
+
}
|
|
2309
|
+
function normalizeStepForHash(step) {
|
|
2310
|
+
const stepObj = isPlainObject2(step) ? step : {};
|
|
2311
|
+
return {
|
|
2312
|
+
type: typeof stepObj.type === "string" ? stepObj.type : "",
|
|
2313
|
+
name: typeof stepObj.name === "string" ? stepObj.name : "",
|
|
2314
|
+
enabled: stepObj.enabled !== false,
|
|
2315
|
+
...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
|
|
2316
|
+
config: normalizeConfigForHash(stepObj.config),
|
|
2317
|
+
order: typeof stepObj.order === "number" ? stepObj.order : 0
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
async function computeFlowContentHash(steps) {
|
|
2321
|
+
const normalized = [...steps].sort((a, b) => {
|
|
2322
|
+
const orderA = isPlainObject2(a) && typeof a.order === "number" ? a.order : 0;
|
|
2323
|
+
const orderB = isPlainObject2(b) && typeof b.order === "number" ? b.order : 0;
|
|
2324
|
+
return orderA - orderB;
|
|
2325
|
+
}).map(normalizeStepForHash);
|
|
2326
|
+
const serialized = JSON.stringify(normalized);
|
|
2327
|
+
const encoded = new TextEncoder().encode(serialized);
|
|
2328
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
2329
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2330
|
+
}
|
|
2331
|
+
var DEFINE_FLOW_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "steps", "evals"]);
|
|
2332
|
+
var DEFINE_FLOW_STEP_KEYS = /* @__PURE__ */ new Set([
|
|
2333
|
+
"type",
|
|
2334
|
+
"name",
|
|
2335
|
+
"order",
|
|
2336
|
+
"enabled",
|
|
2337
|
+
"when",
|
|
2338
|
+
"config"
|
|
2339
|
+
]);
|
|
2340
|
+
function collectStepNonPortableToolRefs(config, path) {
|
|
2341
|
+
const found = [];
|
|
2342
|
+
const tools = config.tools;
|
|
2343
|
+
const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
|
|
2344
|
+
const isRawId = (ref, prefix) => typeof ref === "string" && ref.startsWith(prefix);
|
|
2345
|
+
const scanArray = (value, subPath) => {
|
|
2346
|
+
if (!Array.isArray(value)) return;
|
|
2347
|
+
value.forEach((ref, i) => {
|
|
2348
|
+
if (isAccountScoped(ref)) found.push(`${subPath}[${i}]`);
|
|
2349
|
+
});
|
|
2350
|
+
};
|
|
2351
|
+
const scanKeys = (value, subPath) => {
|
|
2352
|
+
if (!isPlainObject2(value)) return;
|
|
2353
|
+
for (const key of Object.keys(value)) {
|
|
2354
|
+
if (isAccountScoped(key)) found.push(`${subPath}.${key}`);
|
|
2355
|
+
}
|
|
2356
|
+
};
|
|
2357
|
+
if (isPlainObject2(tools)) {
|
|
2358
|
+
scanArray(tools.toolIds, `${path}.tools.toolIds`);
|
|
2359
|
+
scanKeys(tools.toolConfigs, `${path}.tools.toolConfigs`);
|
|
2360
|
+
scanKeys(tools.perToolLimits, `${path}.tools.perToolLimits`);
|
|
2361
|
+
if (isPlainObject2(tools.approval)) {
|
|
2362
|
+
scanArray(tools.approval.require, `${path}.tools.approval.require`);
|
|
2363
|
+
}
|
|
2364
|
+
if (isPlainObject2(tools.subagentConfig)) {
|
|
2365
|
+
scanArray(tools.subagentConfig.toolPool, `${path}.tools.subagentConfig.toolPool`);
|
|
2366
|
+
}
|
|
2367
|
+
if (isPlainObject2(tools.codeModeConfig)) {
|
|
2368
|
+
scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
|
|
2369
|
+
}
|
|
2370
|
+
if (Array.isArray(tools.runtimeTools)) {
|
|
2371
|
+
tools.runtimeTools.forEach((runtimeTool, i) => {
|
|
2372
|
+
if (!isPlainObject2(runtimeTool) || !isPlainObject2(runtimeTool.config)) return;
|
|
2373
|
+
const base = `${path}.tools.runtimeTools[${i}].config`;
|
|
2374
|
+
const rtConfig = runtimeTool.config;
|
|
2375
|
+
if (runtimeTool.toolType === "subagent" && isRawId(rtConfig.agentId, "agent_")) {
|
|
2376
|
+
found.push(`${base}.agentId`);
|
|
2377
|
+
} else if (runtimeTool.toolType === "flow" && isRawId(rtConfig.flowId, "flow_")) {
|
|
2378
|
+
found.push(`${base}.flowId`);
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
if (isAccountScoped(config.toolId)) {
|
|
2384
|
+
found.push(`${path}.toolId`);
|
|
2385
|
+
}
|
|
2386
|
+
if (isRawId(config.agentId, "agent_")) {
|
|
2387
|
+
found.push(`${path}.agentId`);
|
|
2388
|
+
}
|
|
2389
|
+
for (const branch of ["trueSteps", "falseSteps"]) {
|
|
2390
|
+
const nested = config[branch];
|
|
2391
|
+
if (!Array.isArray(nested)) continue;
|
|
2392
|
+
nested.forEach((nestedStep, i) => {
|
|
2393
|
+
if (isPlainObject2(nestedStep) && isPlainObject2(nestedStep.config)) {
|
|
2394
|
+
found.push(
|
|
2395
|
+
...collectStepNonPortableToolRefs(nestedStep.config, `${path}.${branch}[${i}].config`)
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
return found;
|
|
2401
|
+
}
|
|
2402
|
+
function defineFlow(input) {
|
|
2403
|
+
if (!input || typeof input !== "object") {
|
|
2404
|
+
throw new Error("defineFlow requires a definition object");
|
|
2405
|
+
}
|
|
2406
|
+
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
2407
|
+
throw new Error('defineFlow requires a non-empty string "name"');
|
|
2408
|
+
}
|
|
2409
|
+
const unknownKeys = Object.keys(input).filter((key) => !DEFINE_FLOW_TOP_LEVEL_KEYS.has(key));
|
|
2410
|
+
if (unknownKeys.length > 0) {
|
|
2411
|
+
throw new Error(
|
|
2412
|
+
`defineFlow: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, steps, and evals. (Description is not part of the v1 ensure surface.)`
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
if (!Array.isArray(input.steps) || input.steps.length === 0) {
|
|
2416
|
+
throw new Error('defineFlow requires a non-empty "steps" array');
|
|
2417
|
+
}
|
|
2418
|
+
const steps = input.steps.map((step, index) => {
|
|
2419
|
+
if (!isPlainObject2(step)) {
|
|
2420
|
+
throw new Error(`defineFlow: steps[${index}] must be an object`);
|
|
2421
|
+
}
|
|
2422
|
+
if (typeof step.type !== "string" || step.type.length === 0) {
|
|
2423
|
+
throw new Error(`defineFlow: steps[${index}] requires a non-empty string "type"`);
|
|
2424
|
+
}
|
|
2425
|
+
if (typeof step.name !== "string" || step.name.length === 0) {
|
|
2426
|
+
throw new Error(`defineFlow: steps[${index}] requires a non-empty string "name"`);
|
|
2427
|
+
}
|
|
2428
|
+
const unknownStepKeys = Object.keys(step).filter((key) => !DEFINE_FLOW_STEP_KEYS.has(key));
|
|
2429
|
+
if (unknownStepKeys.length > 0) {
|
|
2430
|
+
throw new Error(
|
|
2431
|
+
`defineFlow: steps[${index}] has unknown field(s): ${unknownStepKeys.join(", ")}. Allowed step fields are type, name, order, enabled, when, config. (Step ids are server artifacts and not part of a portable definition.)`
|
|
2432
|
+
);
|
|
2433
|
+
}
|
|
2434
|
+
const config = isPlainObject2(step.config) ? step.config : void 0;
|
|
2435
|
+
if (config) {
|
|
2436
|
+
const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
|
|
2437
|
+
if (nonPortable.length > 0) {
|
|
2438
|
+
throw new Error(
|
|
2439
|
+
`defineFlow: account-scoped reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026/agent_\u2026/flow_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references, or reference a saved resource by name \u2014 tool:<name>, agent:<name>, or flow:<name> instead.`
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
return {
|
|
2444
|
+
type: step.type,
|
|
2445
|
+
name: step.name,
|
|
2446
|
+
// Explicit 1-based order (the flow builder's convention) so the local
|
|
2447
|
+
// probe hash agrees with the server's persisted step order.
|
|
2448
|
+
order: typeof step.order === "number" ? step.order : index + 1,
|
|
2449
|
+
...step.enabled !== void 0 ? { enabled: step.enabled } : {},
|
|
2450
|
+
...typeof step.when === "string" ? { when: step.when } : {},
|
|
2451
|
+
...config ? { config } : {}
|
|
2452
|
+
};
|
|
2453
|
+
});
|
|
2454
|
+
let evals;
|
|
2455
|
+
if (input.evals !== void 0) {
|
|
2456
|
+
if (!Array.isArray(input.evals)) {
|
|
2457
|
+
throw new Error('defineFlow: "evals" must be an array');
|
|
2458
|
+
}
|
|
2459
|
+
const seenEvalNames = /* @__PURE__ */ new Set();
|
|
2460
|
+
evals = input.evals.map((evalInput, i) => {
|
|
2461
|
+
if (!isPlainObject2(evalInput)) {
|
|
2462
|
+
throw new Error(`defineFlow: evals[${i}] must be an object`);
|
|
2463
|
+
}
|
|
2464
|
+
if (evalInput.virtual === true) {
|
|
2465
|
+
throw new Error(
|
|
2466
|
+
`defineFlow: evals[${i}] cannot be virtual (inline evals converge with the flow; run a virtual eval directly instead).`
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
const withTarget = evalInput.target === void 0 ? { ...evalInput, target: { flow: input.name } } : evalInput;
|
|
2470
|
+
let def;
|
|
2471
|
+
try {
|
|
2472
|
+
def = defineEval(withTarget);
|
|
2473
|
+
} catch (err) {
|
|
2474
|
+
throw new Error(
|
|
2475
|
+
`defineFlow: evals[${i}] \u2014 ${err instanceof Error ? err.message : String(err)}`,
|
|
2476
|
+
{ cause: err }
|
|
2477
|
+
);
|
|
2478
|
+
}
|
|
2479
|
+
if (seenEvalNames.has(def.name)) {
|
|
2480
|
+
throw new Error(
|
|
2481
|
+
`defineFlow: evals[${i}] resolves to the duplicate suite name "${def.name}". Inline eval suites must have distinct names \u2014 give each a \`name\` (two unnamed evals targeting the same flow both default to the same name and would overwrite each other).`
|
|
2482
|
+
);
|
|
2483
|
+
}
|
|
2484
|
+
seenEvalNames.add(def.name);
|
|
2485
|
+
return def;
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
return {
|
|
2489
|
+
name: input.name,
|
|
2490
|
+
steps,
|
|
2491
|
+
...evals && evals.length > 0 ? { evals } : {}
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
var FlowEnsureConflictError = class extends Error {
|
|
2495
|
+
constructor(body) {
|
|
2496
|
+
super(body.error ?? `Flow ensure conflict: ${body.code}`);
|
|
2497
|
+
this.name = "FlowEnsureConflictError";
|
|
2498
|
+
this.code = body.code;
|
|
2499
|
+
this.lastModifiedSource = body.lastModifiedSource;
|
|
2500
|
+
this.modifiedAt = body.modifiedAt;
|
|
2501
|
+
this.currentHash = body.currentHash;
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
var FlowDriftError = class extends Error {
|
|
2505
|
+
constructor(plan) {
|
|
2506
|
+
super(
|
|
2507
|
+
`Flow "${plan.flowId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.flows.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
|
|
2508
|
+
);
|
|
2509
|
+
this.name = "FlowDriftError";
|
|
2510
|
+
this.plan = plan;
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
function parseRequestError(err) {
|
|
2514
|
+
if (!(err instanceof Error)) return { status: null, body: null };
|
|
2515
|
+
const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
|
|
2516
|
+
if (!match) return { status: null, body: null };
|
|
2517
|
+
try {
|
|
2518
|
+
return { status: Number(match[1]), body: JSON.parse(match[2]) };
|
|
2519
|
+
} catch {
|
|
2520
|
+
return { status: Number(match[1]), body: null };
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
function toConflictError(err) {
|
|
2524
|
+
const { status, body } = parseRequestError(err);
|
|
2525
|
+
if (status !== 409 || !isPlainObject2(body)) return null;
|
|
2526
|
+
const code = body.code;
|
|
2527
|
+
if (code !== "external_modification" && code !== "remote_changed") return null;
|
|
2528
|
+
return new FlowEnsureConflictError(
|
|
2529
|
+
body
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
|
|
2533
|
+
function memoFor2(client) {
|
|
2534
|
+
let memo = serverHashMemo2.get(client);
|
|
2535
|
+
if (!memo) {
|
|
2536
|
+
memo = /* @__PURE__ */ new Map();
|
|
2537
|
+
serverHashMemo2.set(client, memo);
|
|
2538
|
+
}
|
|
2539
|
+
return memo;
|
|
2540
|
+
}
|
|
2541
|
+
function memoize(memo, memoKey, result) {
|
|
2542
|
+
if (result.result !== "plan") memo.set(memoKey, result.contentHash);
|
|
2543
|
+
}
|
|
2544
|
+
async function request(client, body) {
|
|
2545
|
+
try {
|
|
2546
|
+
return await client.post(
|
|
2547
|
+
"/flows/ensure",
|
|
2548
|
+
body
|
|
2549
|
+
);
|
|
2550
|
+
} catch (err) {
|
|
2551
|
+
const conflict = toConflictError(err);
|
|
2552
|
+
if (conflict) throw conflict;
|
|
2553
|
+
throw err;
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
async function ensureFlow(client, definition, options = {}) {
|
|
2557
|
+
const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
|
|
2558
|
+
const passthrough = {
|
|
2559
|
+
...onConflict ? { onConflict } : {},
|
|
2560
|
+
...release ? { release } : {},
|
|
2561
|
+
...expectedRemoteHash ? { expectedRemoteHash } : {}
|
|
2562
|
+
};
|
|
2563
|
+
const wireDefinition = { name: definition.name, steps: definition.steps };
|
|
2564
|
+
if (dryRun || expectNoChanges) {
|
|
2565
|
+
const plan = await request(client, {
|
|
2566
|
+
name: definition.name,
|
|
2567
|
+
definition: wireDefinition,
|
|
2568
|
+
dryRun: true,
|
|
2569
|
+
...passthrough
|
|
2570
|
+
});
|
|
2571
|
+
if (plan.result !== "plan") {
|
|
2572
|
+
throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
|
|
2573
|
+
}
|
|
2574
|
+
if (expectNoChanges && plan.changes !== "none") {
|
|
2575
|
+
throw new FlowDriftError(plan);
|
|
2576
|
+
}
|
|
2577
|
+
return plan;
|
|
2578
|
+
}
|
|
2579
|
+
const memo = memoFor2(client);
|
|
2580
|
+
const localHash = await computeFlowContentHash(definition.steps);
|
|
2581
|
+
const memoKey = `${definition.name} ${localHash}`;
|
|
2582
|
+
const contentHash = memo.get(memoKey) ?? localHash;
|
|
2583
|
+
const probe = await request(client, {
|
|
2584
|
+
name: definition.name,
|
|
2585
|
+
contentHash,
|
|
2586
|
+
...passthrough
|
|
2587
|
+
});
|
|
2588
|
+
if (probe.result !== "definitionRequired") {
|
|
2589
|
+
memoize(memo, memoKey, probe);
|
|
2590
|
+
return convergeInlineEvals(client, definition, probe);
|
|
2591
|
+
}
|
|
2592
|
+
const converged = await request(client, {
|
|
2593
|
+
name: definition.name,
|
|
2594
|
+
definition: wireDefinition,
|
|
2595
|
+
...passthrough
|
|
2596
|
+
});
|
|
2597
|
+
if (converged.result === "definitionRequired") {
|
|
2598
|
+
throw new Error("Server reported definitionRequired for a full-definition request");
|
|
2599
|
+
}
|
|
2600
|
+
memoize(memo, memoKey, converged);
|
|
2601
|
+
return convergeInlineEvals(client, definition, converged);
|
|
2602
|
+
}
|
|
2603
|
+
async function convergeInlineEvals(client, definition, result) {
|
|
2604
|
+
if (result.result === "plan" || !definition.evals?.length) {
|
|
2605
|
+
return result;
|
|
2606
|
+
}
|
|
2607
|
+
const evals = [];
|
|
2608
|
+
for (const evalDef of definition.evals) {
|
|
2609
|
+
evals.push(await ensureEval(client, evalDef));
|
|
2610
|
+
}
|
|
2611
|
+
return { ...result, evals };
|
|
2612
|
+
}
|
|
2613
|
+
async function pullFlow(client, name) {
|
|
2614
|
+
return client.get("/flows/pull", { name });
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// src/flows-namespace.ts
|
|
2618
|
+
var FlowsNamespace = class {
|
|
2619
|
+
constructor(getClient) {
|
|
2620
|
+
this.getClient = getClient;
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Create or update a flow by name (upsert mode)
|
|
2624
|
+
*
|
|
2625
|
+
* The recommended pattern for code-first flow management when you want to
|
|
2626
|
+
* save AND run in one dispatch. For a deploy-time, non-executing converge
|
|
2627
|
+
* (CI/CD config-as-code), use {@link ensure} instead — upsert and ensure
|
|
2628
|
+
* are siblings, not versions of each other: upsert is the runtime verb
|
|
2629
|
+
* (save-and-run), ensure is the deploy verb (converge only).
|
|
2630
|
+
*
|
|
2631
|
+
* @example
|
|
2632
|
+
* ```typescript
|
|
2633
|
+
* const result = await Runtype.flows.upsert({
|
|
2634
|
+
* name: 'My Flow',
|
|
2635
|
+
* createVersionOnChange: true
|
|
2636
|
+
* })
|
|
2637
|
+
* .prompt({ name: 'Analyze', model: 'gpt-4o', userPrompt: '...' })
|
|
2638
|
+
* .stream()
|
|
2199
2639
|
* ```
|
|
2200
2640
|
*/
|
|
2201
2641
|
upsert(config) {
|
|
@@ -2207,11 +2647,19 @@ var FlowsNamespace = class {
|
|
|
2207
2647
|
* the steady state is one tiny probe request. Creates an immutable version
|
|
2208
2648
|
* snapshot on every change; never deletes; never executes the flow.
|
|
2209
2649
|
*
|
|
2650
|
+
* When the definition carries inline `evals`, each suite is converged via
|
|
2651
|
+
* `/eval/ensure` after the flow itself (real converge path only — not on
|
|
2652
|
+
* dryRun/`expectNoChanges`), and the outcomes are returned as `result.evals`.
|
|
2653
|
+
*
|
|
2210
2654
|
* @example
|
|
2211
2655
|
* ```typescript
|
|
2212
|
-
* const def = defineFlow({
|
|
2656
|
+
* const def = defineFlow({
|
|
2657
|
+
* name: 'Onboarding Digest',
|
|
2658
|
+
* steps: [...],
|
|
2659
|
+
* evals: [{ cases: [{ name: 'smoke', input: {...}, expect: [contains('ok')] }] }],
|
|
2660
|
+
* })
|
|
2213
2661
|
*
|
|
2214
|
-
* // Converge (CI/deploy).
|
|
2662
|
+
* // Converge the flow AND its inline eval suites (CI/deploy).
|
|
2215
2663
|
* const result = await Runtype.flows.ensure(def)
|
|
2216
2664
|
*
|
|
2217
2665
|
* // PR drift gate.
|
|
@@ -3045,530 +3493,236 @@ var RuntypeFlowBuilder = class {
|
|
|
3045
3493
|
const flowMode = this.mode === "existing" ? "existing" : this.mode;
|
|
3046
3494
|
const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
|
|
3047
3495
|
const request6 = { flow };
|
|
3048
|
-
if (this.recordConfig) {
|
|
3049
|
-
request6.record = this.recordConfig;
|
|
3050
|
-
}
|
|
3051
|
-
if (this.messagesConfig) {
|
|
3052
|
-
request6.messages = this.messagesConfig;
|
|
3053
|
-
}
|
|
3054
|
-
if (this.inputsConfig) {
|
|
3055
|
-
request6.inputs = this.inputsConfig;
|
|
3056
|
-
}
|
|
3057
|
-
const options = {
|
|
3058
|
-
flowMode,
|
|
3059
|
-
...this.dispatchOptions
|
|
3060
|
-
};
|
|
3061
|
-
if (this.recordConfig && !this.dispatchOptions.recordMode) {
|
|
3062
|
-
if (this.recordConfig.id) {
|
|
3063
|
-
options.recordMode = "existing";
|
|
3064
|
-
} else if (this.recordConfig.name || this.recordConfig.type) {
|
|
3065
|
-
options.recordMode = "create";
|
|
3066
|
-
} else {
|
|
3067
|
-
options.recordMode = "virtual";
|
|
3068
|
-
}
|
|
3069
|
-
}
|
|
3070
|
-
if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
|
|
3071
|
-
options.upsertOptions = this.upsertOptions;
|
|
3072
|
-
}
|
|
3073
|
-
request6.options = options;
|
|
3074
|
-
return request6;
|
|
3075
|
-
}
|
|
3076
|
-
/**
|
|
3077
|
-
* Validate this prospective flow against the public validation endpoint
|
|
3078
|
-
* (`POST /v1/public/flows/validate`) WITHOUT creating it, using the bound
|
|
3079
|
-
* client. Returns the same `errors` / `warnings` / `recommendations` envelope
|
|
3080
|
-
* the API, dashboard, and MCP `validate_flow` tool use, so structural issues,
|
|
3081
|
-
* the upsert-record JSON foot-gun, undeclared-variable warnings, and
|
|
3082
|
-
* sub-optimal model selections surface at author time. The bound client
|
|
3083
|
-
* carries authentication; an authenticated client additionally runs
|
|
3084
|
-
* account-scoped checks (`result.context` reports whether they ran). Mirrors
|
|
3085
|
-
* {@link FlowBuilder.validate}.
|
|
3086
|
-
*
|
|
3087
|
-
* Only valid for prospective flows (`Runtype.flows.virtual(...)` /
|
|
3088
|
-
* `Runtype.flows.upsert(...)`). An existing-flow builder
|
|
3089
|
-
* (`Runtype.flows.use(id)`) has no inline steps to validate, so this throws —
|
|
3090
|
-
* the validation endpoint validates a `{ name, steps }` payload, not a saved
|
|
3091
|
-
* flow by id (which was already validated at create time).
|
|
3092
|
-
*
|
|
3093
|
-
* @example
|
|
3094
|
-
* ```typescript
|
|
3095
|
-
* const result = await Runtype.flows.virtual({ name: 'Temp Flow' })
|
|
3096
|
-
* .prompt({ name: 'Process', model: 'gpt-5-mini', userPrompt: '...' })
|
|
3097
|
-
* .validate()
|
|
3098
|
-
*
|
|
3099
|
-
* if (!result.valid) console.error(result.errors)
|
|
3100
|
-
* ```
|
|
3101
|
-
*/
|
|
3102
|
-
async validate() {
|
|
3103
|
-
return validateInlineFlow(
|
|
3104
|
-
this.getClient(),
|
|
3105
|
-
{ name: this.flowConfig.name, steps: this.steps, existingFlowId: this.existingFlowId },
|
|
3106
|
-
"Use Runtype.flows.virtual(...) or Runtype.flows.upsert(...) with inline steps to validate a flow before saving."
|
|
3107
|
-
);
|
|
3108
|
-
}
|
|
3109
|
-
// ============================================================================
|
|
3110
|
-
// Private Helpers
|
|
3111
|
-
// ============================================================================
|
|
3112
|
-
/**
|
|
3113
|
-
* Persisted flow protocol (APQ-style): send hash-only first, retry with
|
|
3114
|
-
* full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
|
|
3115
|
-
* dispatches directly.
|
|
3116
|
-
*/
|
|
3117
|
-
async dispatchWithPersistedFlow(client, config) {
|
|
3118
|
-
if (this.mode !== "upsert" || !this.steps.length) {
|
|
3119
|
-
return client.dispatch(config);
|
|
3120
|
-
}
|
|
3121
|
-
const contentHash = await this.computeContentHash();
|
|
3122
|
-
const hashOnlyConfig = {
|
|
3123
|
-
...config,
|
|
3124
|
-
flow: { name: config.flow.name, contentHash }
|
|
3125
|
-
};
|
|
3126
|
-
try {
|
|
3127
|
-
return await client.dispatch(hashOnlyConfig);
|
|
3128
|
-
} catch (err) {
|
|
3129
|
-
const is422 = err != null && typeof err === "object" && "statusCode" in err && err.statusCode === 422 || err instanceof Error && /\b422\b/.test(err.message);
|
|
3130
|
-
if (!is422) {
|
|
3131
|
-
throw err;
|
|
3132
|
-
}
|
|
3133
|
-
}
|
|
3134
|
-
const fullConfig = {
|
|
3135
|
-
...config,
|
|
3136
|
-
flow: { ...config.flow, contentHash }
|
|
3137
|
-
};
|
|
3138
|
-
return client.dispatch(fullConfig);
|
|
3139
|
-
}
|
|
3140
|
-
async computeContentHash() {
|
|
3141
|
-
return computeFlowContentHash(this.steps);
|
|
3142
|
-
}
|
|
3143
|
-
addRawStep(type, config) {
|
|
3144
|
-
const { name, enabled, when, ...stepConfig } = config;
|
|
3145
|
-
this.addStep(type, name, stepConfig, enabled, when);
|
|
3146
|
-
return this;
|
|
3147
|
-
}
|
|
3148
|
-
addStep(type, name, config, enabled = true, when) {
|
|
3149
|
-
this.stepCounter++;
|
|
3150
|
-
const cleanConfig = {};
|
|
3151
|
-
for (const [key, value] of Object.entries(config)) {
|
|
3152
|
-
if (value !== void 0) {
|
|
3153
|
-
cleanConfig[key] = value;
|
|
3154
|
-
}
|
|
3155
|
-
}
|
|
3156
|
-
this.steps.push({
|
|
3157
|
-
id: `step-${this.stepCounter}`,
|
|
3158
|
-
type,
|
|
3159
|
-
name,
|
|
3160
|
-
order: this.stepCounter,
|
|
3161
|
-
enabled,
|
|
3162
|
-
...when ? { when } : {},
|
|
3163
|
-
config: cleanConfig
|
|
3164
|
-
});
|
|
3165
|
-
}
|
|
3166
|
-
};
|
|
3167
|
-
|
|
3168
|
-
// src/batches-namespace.ts
|
|
3169
|
-
var BatchesNamespace = class {
|
|
3170
|
-
constructor(getClient) {
|
|
3171
|
-
this.getClient = getClient;
|
|
3172
|
-
}
|
|
3173
|
-
/**
|
|
3174
|
-
* Schedule a batch operation
|
|
3175
|
-
*
|
|
3176
|
-
* Creates and schedules a batch to run a flow on all records of a type.
|
|
3177
|
-
* By default, runs immediately. Use `at` to schedule for a specific time.
|
|
3178
|
-
*
|
|
3179
|
-
* @example
|
|
3180
|
-
* ```typescript
|
|
3181
|
-
* // Run immediately
|
|
3182
|
-
* const batch = await Runtype.batches.schedule({
|
|
3183
|
-
* flowId: 'flow_123',
|
|
3184
|
-
* recordType: 'customers',
|
|
3185
|
-
* })
|
|
3186
|
-
*
|
|
3187
|
-
* // Schedule for later
|
|
3188
|
-
* const batch = await Runtype.batches.schedule({
|
|
3189
|
-
* flowId: 'flow_123',
|
|
3190
|
-
* recordType: 'customers',
|
|
3191
|
-
* at: new Date('2024-01-15T09:00:00Z'),
|
|
3192
|
-
* })
|
|
3193
|
-
*
|
|
3194
|
-
* // With options
|
|
3195
|
-
* const batch = await Runtype.batches.schedule({
|
|
3196
|
-
* flowId: 'flow_123',
|
|
3197
|
-
* recordType: 'customers',
|
|
3198
|
-
* concurrency: 5,
|
|
3199
|
-
* continueOnError: true,
|
|
3200
|
-
* filter: { status: 'active' },
|
|
3201
|
-
* limit: 100,
|
|
3202
|
-
* })
|
|
3203
|
-
* ```
|
|
3204
|
-
*/
|
|
3205
|
-
async schedule(config) {
|
|
3206
|
-
const client = this.getClient();
|
|
3207
|
-
const payload = {
|
|
3208
|
-
flowId: config.flowId,
|
|
3209
|
-
recordType: config.recordType
|
|
3210
|
-
};
|
|
3211
|
-
if (config.at) {
|
|
3212
|
-
payload.scheduledAt = config.at.toISOString();
|
|
3496
|
+
if (this.recordConfig) {
|
|
3497
|
+
request6.record = this.recordConfig;
|
|
3213
3498
|
}
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
if (config.concurrency !== void 0) options.concurrency = config.concurrency;
|
|
3217
|
-
if (config.continueOnError !== void 0) options.continueOnError = config.continueOnError;
|
|
3218
|
-
if (config.storeResults !== void 0) options.storeResults = config.storeResults;
|
|
3219
|
-
if (config.modelOverride !== void 0) options.modelOverride = config.modelOverride;
|
|
3220
|
-
if (Object.keys(options).length > 0) {
|
|
3221
|
-
payload.options = options;
|
|
3499
|
+
if (this.messagesConfig) {
|
|
3500
|
+
request6.messages = this.messagesConfig;
|
|
3222
3501
|
}
|
|
3223
|
-
if (
|
|
3224
|
-
|
|
3502
|
+
if (this.inputsConfig) {
|
|
3503
|
+
request6.inputs = this.inputsConfig;
|
|
3225
3504
|
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3505
|
+
const options = {
|
|
3506
|
+
flowMode,
|
|
3507
|
+
...this.dispatchOptions
|
|
3508
|
+
};
|
|
3509
|
+
if (this.recordConfig && !this.dispatchOptions.recordMode) {
|
|
3510
|
+
if (this.recordConfig.id) {
|
|
3511
|
+
options.recordMode = "existing";
|
|
3512
|
+
} else if (this.recordConfig.name || this.recordConfig.type) {
|
|
3513
|
+
options.recordMode = "create";
|
|
3514
|
+
} else {
|
|
3515
|
+
options.recordMode = "virtual";
|
|
3516
|
+
}
|
|
3228
3517
|
}
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
* @example
|
|
3235
|
-
* ```typescript
|
|
3236
|
-
* const status = await Runtype.batches.get('batch_456')
|
|
3237
|
-
* console.log(status.status, status.processedRecords, '/', status.totalRecords)
|
|
3238
|
-
* ```
|
|
3239
|
-
*/
|
|
3240
|
-
async get(batchId) {
|
|
3241
|
-
const client = this.getClient();
|
|
3242
|
-
return client.get(`/batches/${batchId}`);
|
|
3518
|
+
if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
|
|
3519
|
+
options.upsertOptions = this.upsertOptions;
|
|
3520
|
+
}
|
|
3521
|
+
request6.options = options;
|
|
3522
|
+
return request6;
|
|
3243
3523
|
}
|
|
3244
3524
|
/**
|
|
3245
|
-
*
|
|
3246
|
-
*
|
|
3247
|
-
*
|
|
3525
|
+
* Validate this prospective flow against the public validation endpoint
|
|
3526
|
+
* (`POST /v1/public/flows/validate`) WITHOUT creating it, using the bound
|
|
3527
|
+
* client. Returns the same `errors` / `warnings` / `recommendations` envelope
|
|
3528
|
+
* the API, dashboard, and MCP `validate_flow` tool use, so structural issues,
|
|
3529
|
+
* the upsert-record JSON foot-gun, undeclared-variable warnings, and
|
|
3530
|
+
* sub-optimal model selections surface at author time. The bound client
|
|
3531
|
+
* carries authentication; an authenticated client additionally runs
|
|
3532
|
+
* account-scoped checks (`result.context` reports whether they ran). Mirrors
|
|
3533
|
+
* {@link FlowBuilder.validate}.
|
|
3248
3534
|
*
|
|
3249
|
-
*
|
|
3250
|
-
*
|
|
3251
|
-
*
|
|
3252
|
-
*
|
|
3253
|
-
|
|
3254
|
-
async cancel(batchId) {
|
|
3255
|
-
const client = this.getClient();
|
|
3256
|
-
return client.post(`/batches/${batchId}/cancel`);
|
|
3257
|
-
}
|
|
3258
|
-
/**
|
|
3259
|
-
* List batch operations
|
|
3535
|
+
* Only valid for prospective flows (`Runtype.flows.virtual(...)` /
|
|
3536
|
+
* `Runtype.flows.upsert(...)`). An existing-flow builder
|
|
3537
|
+
* (`Runtype.flows.use(id)`) has no inline steps to validate, so this throws —
|
|
3538
|
+
* the validation endpoint validates a `{ name, steps }` payload, not a saved
|
|
3539
|
+
* flow by id (which was already validated at create time).
|
|
3260
3540
|
*
|
|
3261
3541
|
* @example
|
|
3262
3542
|
* ```typescript
|
|
3263
|
-
*
|
|
3264
|
-
*
|
|
3265
|
-
*
|
|
3266
|
-
* // Filter by status
|
|
3267
|
-
* const running = await Runtype.batches.list({ status: 'running' })
|
|
3543
|
+
* const result = await Runtype.flows.virtual({ name: 'Temp Flow' })
|
|
3544
|
+
* .prompt({ name: 'Process', model: 'gpt-5-mini', userPrompt: '...' })
|
|
3545
|
+
* .validate()
|
|
3268
3546
|
*
|
|
3269
|
-
*
|
|
3270
|
-
* const flowBatches = await Runtype.batches.list({ flowId: 'flow_123' })
|
|
3547
|
+
* if (!result.valid) console.error(result.errors)
|
|
3271
3548
|
* ```
|
|
3272
3549
|
*/
|
|
3273
|
-
async
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
// src/evals-ensure.ts
|
|
3280
|
-
var CHECK_GRADER_KINDS = /* @__PURE__ */ new Set([
|
|
3281
|
-
"contains",
|
|
3282
|
-
"not_contains",
|
|
3283
|
-
"matches_expected",
|
|
3284
|
-
"regex",
|
|
3285
|
-
"valid_json",
|
|
3286
|
-
"json_field",
|
|
3287
|
-
"length",
|
|
3288
|
-
"latency",
|
|
3289
|
-
"no_error"
|
|
3290
|
-
]);
|
|
3291
|
-
function contains(value, opts) {
|
|
3292
|
-
return { kind: "contains", value, ...opts?.caseSensitive ? { caseSensitive: true } : {} };
|
|
3293
|
-
}
|
|
3294
|
-
function notContains(value, opts) {
|
|
3295
|
-
return { kind: "not_contains", value, ...opts?.caseSensitive ? { caseSensitive: true } : {} };
|
|
3296
|
-
}
|
|
3297
|
-
function matchesExpected() {
|
|
3298
|
-
return { kind: "matches_expected" };
|
|
3299
|
-
}
|
|
3300
|
-
function regex(pattern, flags) {
|
|
3301
|
-
return { kind: "regex", pattern, ...flags ? { flags } : {} };
|
|
3302
|
-
}
|
|
3303
|
-
function validJson() {
|
|
3304
|
-
return { kind: "valid_json" };
|
|
3305
|
-
}
|
|
3306
|
-
function jsonField(path, opts) {
|
|
3307
|
-
return {
|
|
3308
|
-
kind: "json_field",
|
|
3309
|
-
path,
|
|
3310
|
-
...opts && "equals" in opts && opts.equals !== void 0 ? { equals: opts.equals } : {},
|
|
3311
|
-
...opts && typeof opts.exists === "boolean" ? { exists: opts.exists } : {}
|
|
3312
|
-
};
|
|
3313
|
-
}
|
|
3314
|
-
function length(opts) {
|
|
3315
|
-
if (!opts || opts.minChars === void 0 && opts.maxChars === void 0) {
|
|
3316
|
-
throw new Error("length() requires at least one of minChars or maxChars");
|
|
3317
|
-
}
|
|
3318
|
-
return {
|
|
3319
|
-
kind: "length",
|
|
3320
|
-
...opts.minChars !== void 0 ? { minChars: opts.minChars } : {},
|
|
3321
|
-
...opts.maxChars !== void 0 ? { maxChars: opts.maxChars } : {}
|
|
3322
|
-
};
|
|
3323
|
-
}
|
|
3324
|
-
function latency(maxMs) {
|
|
3325
|
-
if (!Number.isFinite(maxMs) || maxMs <= 0) {
|
|
3326
|
-
throw new Error("latency() requires a positive maxMs");
|
|
3327
|
-
}
|
|
3328
|
-
return { kind: "latency", maxMs };
|
|
3329
|
-
}
|
|
3330
|
-
function noError() {
|
|
3331
|
-
return { kind: "no_error" };
|
|
3332
|
-
}
|
|
3333
|
-
function judge(criteria, opts) {
|
|
3334
|
-
if (typeof criteria !== "string" || criteria.trim().length === 0) {
|
|
3335
|
-
throw new Error("judge() requires non-empty criteria");
|
|
3336
|
-
}
|
|
3337
|
-
return {
|
|
3338
|
-
kind: "ai",
|
|
3339
|
-
criteria,
|
|
3340
|
-
...opts?.preset ? { preset: opts.preset } : {},
|
|
3341
|
-
...opts?.useExpected ? { useExpected: true } : {},
|
|
3342
|
-
...opts?.model ? { model: opts.model } : {},
|
|
3343
|
-
...opts?.threshold !== void 0 ? { threshold: opts.threshold } : {}
|
|
3344
|
-
};
|
|
3345
|
-
}
|
|
3346
|
-
var judges = {
|
|
3347
|
-
answersQuestion: () => judge(
|
|
3348
|
-
"The response directly addresses what the user asked, without dodging or answering a different question.",
|
|
3349
|
-
{ preset: "answersQuestion" }
|
|
3350
|
-
),
|
|
3351
|
-
matchesExpected: () => judge(
|
|
3352
|
-
"The response conveys the same facts and conclusion as the expected answer. Wording may differ.",
|
|
3353
|
-
{ preset: "matchesExpected", useExpected: true }
|
|
3354
|
-
),
|
|
3355
|
-
followsInstructions: () => judge(
|
|
3356
|
-
"The response obeys every instruction in the system prompt (format, tone, constraints, refusals).",
|
|
3357
|
-
{ preset: "followsInstructions" }
|
|
3358
|
-
),
|
|
3359
|
-
grounded: () => judge(
|
|
3360
|
-
"Every factual claim in the response is supported by the provided context or the expected answer. Flag anything invented.",
|
|
3361
|
-
{ preset: "grounded" }
|
|
3362
|
-
),
|
|
3363
|
-
rightTone: (voice = "{describe the voice you want}") => judge(`The response matches this voice: ${voice}.`, { preset: "rightTone" }),
|
|
3364
|
-
safeToSend: () => judge(
|
|
3365
|
-
"The response contains nothing embarrassing to show a customer: no leaked internals, no hostile tone, no policy violations.",
|
|
3366
|
-
{ preset: "safeToSend" }
|
|
3367
|
-
)
|
|
3368
|
-
};
|
|
3369
|
-
var DEFINE_EVAL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
3370
|
-
"name",
|
|
3371
|
-
"target",
|
|
3372
|
-
"graders",
|
|
3373
|
-
"cases",
|
|
3374
|
-
"virtual"
|
|
3375
|
-
]);
|
|
3376
|
-
var DEFINE_EVAL_CASE_KEYS = /* @__PURE__ */ new Set(["name", "input", "expected", "expect"]);
|
|
3377
|
-
function isPlainObject2(value) {
|
|
3378
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3379
|
-
}
|
|
3380
|
-
function normalizeTarget(target) {
|
|
3381
|
-
if (!isPlainObject2(target)) {
|
|
3382
|
-
throw new Error('defineEval requires a "target" object: { flow: name } or { agent: name }');
|
|
3383
|
-
}
|
|
3384
|
-
const hasFlow = typeof target.flow === "string" && target.flow.length > 0;
|
|
3385
|
-
const hasAgent = typeof target.agent === "string" && target.agent.length > 0;
|
|
3386
|
-
if (hasFlow === hasAgent) {
|
|
3387
|
-
throw new Error(
|
|
3388
|
-
'defineEval "target" must name exactly one of flow or agent: { flow: "name" } XOR { agent: "name" }'
|
|
3389
|
-
);
|
|
3390
|
-
}
|
|
3391
|
-
const extraKeys = Object.keys(target).filter((k) => k !== "flow" && k !== "agent");
|
|
3392
|
-
if (extraKeys.length > 0) {
|
|
3393
|
-
throw new Error(`defineEval "target" has unknown field(s): ${extraKeys.join(", ")}`);
|
|
3394
|
-
}
|
|
3395
|
-
return hasFlow ? { flow: target.flow } : { agent: target.agent };
|
|
3396
|
-
}
|
|
3397
|
-
function validateGrader(grader, where) {
|
|
3398
|
-
if (!isPlainObject2(grader) || typeof grader.kind !== "string") {
|
|
3399
|
-
throw new Error(`defineEval: ${where} must be a grader object with a string "kind"`);
|
|
3400
|
-
}
|
|
3401
|
-
if (grader.kind === "ai") {
|
|
3402
|
-
if (typeof grader.criteria !== "string" || grader.criteria.trim().length === 0) {
|
|
3403
|
-
throw new Error(`defineEval: ${where} is an AI grader and requires non-empty "criteria"`);
|
|
3404
|
-
}
|
|
3405
|
-
return grader;
|
|
3406
|
-
}
|
|
3407
|
-
if (!CHECK_GRADER_KINDS.has(grader.kind)) {
|
|
3408
|
-
throw new Error(
|
|
3409
|
-
`defineEval: ${where} has unknown grader kind "${grader.kind}". Known kinds: ${[...CHECK_GRADER_KINDS].join(", ")}, ai. (Trace graders such as called_tool/step_order are not available yet.)`
|
|
3550
|
+
async validate() {
|
|
3551
|
+
return validateInlineFlow(
|
|
3552
|
+
this.getClient(),
|
|
3553
|
+
{ name: this.flowConfig.name, steps: this.steps, existingFlowId: this.existingFlowId },
|
|
3554
|
+
"Use Runtype.flows.virtual(...) or Runtype.flows.upsert(...) with inline steps to validate a flow before saving."
|
|
3410
3555
|
);
|
|
3411
3556
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
if (!
|
|
3422
|
-
|
|
3423
|
-
}
|
|
3424
|
-
out.variables = input.variables;
|
|
3425
|
-
}
|
|
3426
|
-
if (input.messages !== void 0) {
|
|
3427
|
-
if (!Array.isArray(input.messages)) {
|
|
3428
|
-
throw new Error(`defineEval: ${where} "input.messages" must be an array`);
|
|
3557
|
+
// ============================================================================
|
|
3558
|
+
// Private Helpers
|
|
3559
|
+
// ============================================================================
|
|
3560
|
+
/**
|
|
3561
|
+
* Persisted flow protocol (APQ-style): send hash-only first, retry with
|
|
3562
|
+
* full definition on FLOW_DEFINITION_REQUIRED. For non-upsert modes,
|
|
3563
|
+
* dispatches directly.
|
|
3564
|
+
*/
|
|
3565
|
+
async dispatchWithPersistedFlow(client, config) {
|
|
3566
|
+
if (this.mode !== "upsert" || !this.steps.length) {
|
|
3567
|
+
return client.dispatch(config);
|
|
3429
3568
|
}
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3569
|
+
const contentHash = await this.computeContentHash();
|
|
3570
|
+
const hashOnlyConfig = {
|
|
3571
|
+
...config,
|
|
3572
|
+
flow: { name: config.flow.name, contentHash }
|
|
3573
|
+
};
|
|
3574
|
+
try {
|
|
3575
|
+
return await client.dispatch(hashOnlyConfig);
|
|
3576
|
+
} catch (err) {
|
|
3577
|
+
const is422 = err != null && typeof err === "object" && "statusCode" in err && err.statusCode === 422 || err instanceof Error && /\b422\b/.test(err.message);
|
|
3578
|
+
if (!is422) {
|
|
3579
|
+
throw err;
|
|
3433
3580
|
}
|
|
3434
|
-
|
|
3435
|
-
|
|
3581
|
+
}
|
|
3582
|
+
const fullConfig = {
|
|
3583
|
+
...config,
|
|
3584
|
+
flow: { ...config.flow, contentHash }
|
|
3585
|
+
};
|
|
3586
|
+
return client.dispatch(fullConfig);
|
|
3436
3587
|
}
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
function defineEval(input) {
|
|
3440
|
-
if (!input || typeof input !== "object") {
|
|
3441
|
-
throw new Error("defineEval requires a definition object");
|
|
3588
|
+
async computeContentHash() {
|
|
3589
|
+
return computeFlowContentHash(this.steps);
|
|
3442
3590
|
}
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
);
|
|
3591
|
+
addRawStep(type, config) {
|
|
3592
|
+
const { name, enabled, when, ...stepConfig } = config;
|
|
3593
|
+
this.addStep(type, name, stepConfig, enabled, when);
|
|
3594
|
+
return this;
|
|
3448
3595
|
}
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3596
|
+
addStep(type, name, config, enabled = true, when) {
|
|
3597
|
+
this.stepCounter++;
|
|
3598
|
+
const cleanConfig = {};
|
|
3599
|
+
for (const [key, value] of Object.entries(config)) {
|
|
3600
|
+
if (value !== void 0) {
|
|
3601
|
+
cleanConfig[key] = value;
|
|
3602
|
+
}
|
|
3603
|
+
}
|
|
3604
|
+
this.steps.push({
|
|
3605
|
+
id: `step-${this.stepCounter}`,
|
|
3606
|
+
type,
|
|
3607
|
+
name,
|
|
3608
|
+
order: this.stepCounter,
|
|
3609
|
+
enabled,
|
|
3610
|
+
...when ? { when } : {},
|
|
3611
|
+
config: cleanConfig
|
|
3612
|
+
});
|
|
3452
3613
|
}
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3614
|
+
};
|
|
3615
|
+
|
|
3616
|
+
// src/batches-namespace.ts
|
|
3617
|
+
var BatchesNamespace = class {
|
|
3618
|
+
constructor(getClient) {
|
|
3619
|
+
this.getClient = getClient;
|
|
3457
3620
|
}
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3621
|
+
/**
|
|
3622
|
+
* Schedule a batch operation
|
|
3623
|
+
*
|
|
3624
|
+
* Creates and schedules a batch to run a flow on all records of a type.
|
|
3625
|
+
* By default, runs immediately. Use `at` to schedule for a specific time.
|
|
3626
|
+
*
|
|
3627
|
+
* @example
|
|
3628
|
+
* ```typescript
|
|
3629
|
+
* // Run immediately
|
|
3630
|
+
* const batch = await Runtype.batches.schedule({
|
|
3631
|
+
* flowId: 'flow_123',
|
|
3632
|
+
* recordType: 'customers',
|
|
3633
|
+
* })
|
|
3634
|
+
*
|
|
3635
|
+
* // Schedule for later
|
|
3636
|
+
* const batch = await Runtype.batches.schedule({
|
|
3637
|
+
* flowId: 'flow_123',
|
|
3638
|
+
* recordType: 'customers',
|
|
3639
|
+
* at: new Date('2024-01-15T09:00:00Z'),
|
|
3640
|
+
* })
|
|
3641
|
+
*
|
|
3642
|
+
* // With options
|
|
3643
|
+
* const batch = await Runtype.batches.schedule({
|
|
3644
|
+
* flowId: 'flow_123',
|
|
3645
|
+
* recordType: 'customers',
|
|
3646
|
+
* concurrency: 5,
|
|
3647
|
+
* continueOnError: true,
|
|
3648
|
+
* filter: { status: 'active' },
|
|
3649
|
+
* limit: 100,
|
|
3650
|
+
* })
|
|
3651
|
+
* ```
|
|
3652
|
+
*/
|
|
3653
|
+
async schedule(config) {
|
|
3654
|
+
const client = this.getClient();
|
|
3655
|
+
const payload = {
|
|
3656
|
+
flowId: config.flowId,
|
|
3657
|
+
recordType: config.recordType
|
|
3658
|
+
};
|
|
3659
|
+
if (config.at) {
|
|
3660
|
+
payload.scheduledAt = config.at.toISOString();
|
|
3477
3661
|
}
|
|
3478
|
-
const
|
|
3479
|
-
|
|
3480
|
-
);
|
|
3481
|
-
|
|
3482
|
-
if (
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3662
|
+
const options = {};
|
|
3663
|
+
if (config.async !== void 0) options.async = config.async;
|
|
3664
|
+
if (config.concurrency !== void 0) options.concurrency = config.concurrency;
|
|
3665
|
+
if (config.continueOnError !== void 0) options.continueOnError = config.continueOnError;
|
|
3666
|
+
if (config.storeResults !== void 0) options.storeResults = config.storeResults;
|
|
3667
|
+
if (config.modelOverride !== void 0) options.modelOverride = config.modelOverride;
|
|
3668
|
+
if (Object.keys(options).length > 0) {
|
|
3669
|
+
payload.options = options;
|
|
3486
3670
|
}
|
|
3487
|
-
if (
|
|
3488
|
-
|
|
3671
|
+
if (config.filter) {
|
|
3672
|
+
payload.filter = config.filter;
|
|
3489
3673
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
input: normalizeCaseInput(c.input, `cases[${index}] ("${c.name}")`),
|
|
3493
|
-
...c.expected !== void 0 ? { expected: c.expected } : {},
|
|
3494
|
-
expect
|
|
3495
|
-
};
|
|
3496
|
-
});
|
|
3497
|
-
return { name, target, cases, virtual: input.virtual === true };
|
|
3498
|
-
}
|
|
3499
|
-
function normalizeForHash(value) {
|
|
3500
|
-
if (Array.isArray(value)) return value.map(normalizeForHash);
|
|
3501
|
-
if (isPlainObject2(value)) {
|
|
3502
|
-
const out = {};
|
|
3503
|
-
for (const key of Object.keys(value).sort()) {
|
|
3504
|
-
const v = value[key];
|
|
3505
|
-
if (v === void 0) continue;
|
|
3506
|
-
out[key] = normalizeForHash(v);
|
|
3674
|
+
if (config.limit !== void 0) {
|
|
3675
|
+
payload.limit = config.limit;
|
|
3507
3676
|
}
|
|
3508
|
-
return
|
|
3509
|
-
}
|
|
3510
|
-
return value;
|
|
3511
|
-
}
|
|
3512
|
-
async function computeEvalContentHash(definition) {
|
|
3513
|
-
const canonical = {
|
|
3514
|
-
target: normalizeForHash(definition.target),
|
|
3515
|
-
virtual: definition.virtual,
|
|
3516
|
-
cases: [...definition.cases].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0).map((c) => ({
|
|
3517
|
-
name: c.name,
|
|
3518
|
-
input: normalizeForHash(c.input),
|
|
3519
|
-
...c.expected !== void 0 ? { expected: normalizeForHash(c.expected) } : {},
|
|
3520
|
-
// Grader order preserved on purpose (it maps to the result index).
|
|
3521
|
-
expect: c.expect.map((g) => normalizeForHash(g))
|
|
3522
|
-
}))
|
|
3523
|
-
};
|
|
3524
|
-
const serialized = JSON.stringify(canonical);
|
|
3525
|
-
const encoded = new TextEncoder().encode(serialized);
|
|
3526
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
3527
|
-
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3528
|
-
}
|
|
3529
|
-
var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
|
|
3530
|
-
function memoFor2(client) {
|
|
3531
|
-
let memo = serverHashMemo2.get(client);
|
|
3532
|
-
if (!memo) {
|
|
3533
|
-
memo = /* @__PURE__ */ new Map();
|
|
3534
|
-
serverHashMemo2.set(client, memo);
|
|
3677
|
+
return client.post("/batches", payload);
|
|
3535
3678
|
}
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3679
|
+
/**
|
|
3680
|
+
* Get batch status by ID
|
|
3681
|
+
*
|
|
3682
|
+
* @example
|
|
3683
|
+
* ```typescript
|
|
3684
|
+
* const status = await Runtype.batches.get('batch_456')
|
|
3685
|
+
* console.log(status.status, status.processedRecords, '/', status.totalRecords)
|
|
3686
|
+
* ```
|
|
3687
|
+
*/
|
|
3688
|
+
async get(batchId) {
|
|
3689
|
+
const client = this.getClient();
|
|
3690
|
+
return client.get(`/batches/${batchId}`);
|
|
3543
3691
|
}
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3692
|
+
/**
|
|
3693
|
+
* Cancel a batch operation
|
|
3694
|
+
*
|
|
3695
|
+
* Cancels a queued or running batch. Records already processed are not rolled back.
|
|
3696
|
+
*
|
|
3697
|
+
* @example
|
|
3698
|
+
* ```typescript
|
|
3699
|
+
* await Runtype.batches.cancel('batch_456')
|
|
3700
|
+
* ```
|
|
3701
|
+
*/
|
|
3702
|
+
async cancel(batchId) {
|
|
3703
|
+
const client = this.getClient();
|
|
3704
|
+
return client.post(`/batches/${batchId}/cancel`);
|
|
3555
3705
|
}
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3706
|
+
/**
|
|
3707
|
+
* List batch operations
|
|
3708
|
+
*
|
|
3709
|
+
* @example
|
|
3710
|
+
* ```typescript
|
|
3711
|
+
* // List all batches
|
|
3712
|
+
* const batches = await Runtype.batches.list()
|
|
3713
|
+
*
|
|
3714
|
+
* // Filter by status
|
|
3715
|
+
* const running = await Runtype.batches.list({ status: 'running' })
|
|
3716
|
+
*
|
|
3717
|
+
* // Filter by flow
|
|
3718
|
+
* const flowBatches = await Runtype.batches.list({ flowId: 'flow_123' })
|
|
3719
|
+
* ```
|
|
3720
|
+
*/
|
|
3721
|
+
async list(params) {
|
|
3722
|
+
const client = this.getClient();
|
|
3723
|
+
return client.get("/batches", params);
|
|
3562
3724
|
}
|
|
3563
|
-
|
|
3564
|
-
return converged;
|
|
3565
|
-
}
|
|
3566
|
-
async function pullEval(client, name) {
|
|
3567
|
-
return client.get("/eval/pull", { name });
|
|
3568
|
-
}
|
|
3569
|
-
async function runEvalSuite(client, input) {
|
|
3570
|
-
return client.post("/eval/run", input);
|
|
3571
|
-
}
|
|
3725
|
+
};
|
|
3572
3726
|
|
|
3573
3727
|
// src/evals-namespace.ts
|
|
3574
3728
|
var EvalRunner = class {
|
|
@@ -5848,7 +6002,7 @@ var Runtype = class {
|
|
|
5848
6002
|
|
|
5849
6003
|
// src/version.ts
|
|
5850
6004
|
var FALLBACK_VERSION = "0.0.0";
|
|
5851
|
-
var SDK_VERSION = "5.
|
|
6005
|
+
var SDK_VERSION = "5.6.0".length > 0 ? "5.6.0" : FALLBACK_VERSION;
|
|
5852
6006
|
var RUNTYPE_CLIENT_KIND = "sdk";
|
|
5853
6007
|
var SDK_USER_AGENT = `runtype-sdk/${SDK_VERSION} (typescript)`;
|
|
5854
6008
|
|
|
@@ -13558,7 +13712,9 @@ export {
|
|
|
13558
13712
|
buildLedgerOffloadReference,
|
|
13559
13713
|
buildPolicyGuidance,
|
|
13560
13714
|
buildSendViewOffloadMarker,
|
|
13715
|
+
calledTool,
|
|
13561
13716
|
compileWorkflowConfig,
|
|
13717
|
+
completed,
|
|
13562
13718
|
computeAgentContentHash,
|
|
13563
13719
|
computeEvalContentHash,
|
|
13564
13720
|
computeFlowContentHash,
|
|
@@ -13568,6 +13724,7 @@ export {
|
|
|
13568
13724
|
computeSurfaceContentHash,
|
|
13569
13725
|
computeToolContentHash,
|
|
13570
13726
|
contains,
|
|
13727
|
+
cost,
|
|
13571
13728
|
createAgentEventTranslator,
|
|
13572
13729
|
createClient,
|
|
13573
13730
|
createExternalTool,
|
|
@@ -13605,6 +13762,7 @@ export {
|
|
|
13605
13762
|
length,
|
|
13606
13763
|
listWorkflowHooks,
|
|
13607
13764
|
matchesExpected,
|
|
13765
|
+
maxToolCalls,
|
|
13608
13766
|
noError,
|
|
13609
13767
|
normalizeAgentDefinition,
|
|
13610
13768
|
normalizeCandidatePath,
|
|
@@ -13613,6 +13771,7 @@ export {
|
|
|
13613
13771
|
normalizeSkillDefinition,
|
|
13614
13772
|
normalizeSurfaceDefinition,
|
|
13615
13773
|
normalizeToolDefinition,
|
|
13774
|
+
notCalledTool,
|
|
13616
13775
|
notContains,
|
|
13617
13776
|
parseFinalBuffer,
|
|
13618
13777
|
parseLedgerArtifactRelativePath,
|
|
@@ -13621,6 +13780,7 @@ export {
|
|
|
13621
13780
|
processStream,
|
|
13622
13781
|
pullEval,
|
|
13623
13782
|
pullFpo,
|
|
13783
|
+
ranStep,
|
|
13624
13784
|
regex,
|
|
13625
13785
|
registerWorkflowHook,
|
|
13626
13786
|
resolveStallStopAfter,
|
|
@@ -13629,8 +13789,11 @@ export {
|
|
|
13629
13789
|
sanitizeTaskSlug,
|
|
13630
13790
|
shouldInjectEmptySessionNudge,
|
|
13631
13791
|
shouldRequestModelEscalation,
|
|
13792
|
+
stepOrder,
|
|
13632
13793
|
streamEvents,
|
|
13794
|
+
toolOrder,
|
|
13633
13795
|
unregisterWorkflowHook,
|
|
13796
|
+
usedNoTools,
|
|
13634
13797
|
validJson,
|
|
13635
13798
|
withUnifiedEvents
|
|
13636
13799
|
};
|