@granular-software/sdk 0.4.41 → 0.4.42
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/agent-evals.d.mts +137 -3
- package/dist/agent-evals.d.ts +137 -3
- package/dist/agent-evals.js +1075 -26
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +1075 -28
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +60 -1
- package/dist/agent-harness.d.ts +60 -1
- package/dist/agent-harness.js +475 -0
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +467 -1
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +282 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +475 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +467 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/agent-evals.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFile, mkdir } from 'fs/promises';
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import OpenAI from 'openai';
|
|
4
4
|
import * as Automerge from '@automerge/automerge';
|
|
@@ -16060,7 +16060,275 @@ var Granular = class _Granular {
|
|
|
16060
16060
|
}
|
|
16061
16061
|
};
|
|
16062
16062
|
|
|
16063
|
+
// src/agent-harness-templates/action-presentation/0.1.0/manifest.json
|
|
16064
|
+
var manifest_default = {
|
|
16065
|
+
id: "action-presentation",
|
|
16066
|
+
version: "0.1.0",
|
|
16067
|
+
status: "candidate",
|
|
16068
|
+
owner: "granular",
|
|
16069
|
+
createdAt: "2026-05-25T00:00:00.000Z",
|
|
16070
|
+
changelog: "Candidate harness template focused on action-request detection and mutation result presentation. It exists to harden cases where the model answers with a terse completion such as Done instead of using tools and reporting the target/action/result.",
|
|
16071
|
+
promptBuilder: "buildGranularAgentSystemPrompt:action-presentation",
|
|
16072
|
+
continuationBuilder: "buildContinuationInstruction:action-presentation",
|
|
16073
|
+
modelOutputInstruction: "agent-evals:modelOutputInstruction",
|
|
16074
|
+
codeReviewPolicy: "reviewGeneratedJobCode",
|
|
16075
|
+
defaultModel: "gpt-5.4",
|
|
16076
|
+
modelMatrix: ["gpt-5.4"],
|
|
16077
|
+
temperature: 0,
|
|
16078
|
+
compatibility: {
|
|
16079
|
+
minSdkVersion: "0.4.40",
|
|
16080
|
+
capabilities: [
|
|
16081
|
+
"executeCode",
|
|
16082
|
+
"readEntities",
|
|
16083
|
+
"workflowHelpers",
|
|
16084
|
+
"savedData",
|
|
16085
|
+
"showRecords"
|
|
16086
|
+
]
|
|
16087
|
+
},
|
|
16088
|
+
evalGates: {
|
|
16089
|
+
requiredSuites: [
|
|
16090
|
+
"agent-harness-hardening",
|
|
16091
|
+
"agent-harness-runtime-e2e",
|
|
16092
|
+
"agent-production-readiness-e2e"
|
|
16093
|
+
],
|
|
16094
|
+
criticalBuckets: [
|
|
16095
|
+
"relationship-traversal",
|
|
16096
|
+
"cross-turn-reference",
|
|
16097
|
+
"ambiguous-target-choice",
|
|
16098
|
+
"confirmation-gated-mutation",
|
|
16099
|
+
"denied-action-refusal",
|
|
16100
|
+
"mutation-presentation"
|
|
16101
|
+
],
|
|
16102
|
+
maxRegressionPct: 0,
|
|
16103
|
+
minPassK: {
|
|
16104
|
+
"critical-mutation": 0.9,
|
|
16105
|
+
"permission-boundary": 1
|
|
16106
|
+
}
|
|
16107
|
+
}
|
|
16108
|
+
};
|
|
16109
|
+
|
|
16110
|
+
// src/agent-harness-templates/experimental-compact/0.1.0/manifest.json
|
|
16111
|
+
var manifest_default2 = {
|
|
16112
|
+
id: "experimental-compact",
|
|
16113
|
+
version: "0.1.0",
|
|
16114
|
+
status: "candidate",
|
|
16115
|
+
owner: "granular",
|
|
16116
|
+
createdAt: "2026-05-24T00:00:00.000Z",
|
|
16117
|
+
changelog: "Candidate harness template used for champion/challenger testing. It adds a compact decision discipline section on top of the stable renderer.",
|
|
16118
|
+
promptBuilder: "buildGranularAgentSystemPrompt:experimental-compact",
|
|
16119
|
+
continuationBuilder: "buildContinuationInstruction:experimental-compact",
|
|
16120
|
+
modelOutputInstruction: "agent-evals:modelOutputInstruction",
|
|
16121
|
+
codeReviewPolicy: "reviewGeneratedJobCode",
|
|
16122
|
+
defaultModel: "gpt-5.4",
|
|
16123
|
+
modelMatrix: ["gpt-5.4"],
|
|
16124
|
+
temperature: 0,
|
|
16125
|
+
compatibility: {
|
|
16126
|
+
minSdkVersion: "0.4.40",
|
|
16127
|
+
capabilities: [
|
|
16128
|
+
"executeCode",
|
|
16129
|
+
"readEntities",
|
|
16130
|
+
"workflowHelpers",
|
|
16131
|
+
"savedData",
|
|
16132
|
+
"showRecords"
|
|
16133
|
+
]
|
|
16134
|
+
},
|
|
16135
|
+
evalGates: {
|
|
16136
|
+
requiredSuites: [
|
|
16137
|
+
"agent-harness-hardening",
|
|
16138
|
+
"agent-harness-runtime-e2e",
|
|
16139
|
+
"agent-production-readiness-e2e"
|
|
16140
|
+
],
|
|
16141
|
+
criticalBuckets: [
|
|
16142
|
+
"relationship-traversal",
|
|
16143
|
+
"cross-turn-reference",
|
|
16144
|
+
"ambiguous-target-choice",
|
|
16145
|
+
"confirmation-gated-mutation",
|
|
16146
|
+
"denied-action-refusal",
|
|
16147
|
+
"mutation-presentation"
|
|
16148
|
+
],
|
|
16149
|
+
maxRegressionPct: 0,
|
|
16150
|
+
minPassK: {
|
|
16151
|
+
"critical-mutation": 0.9,
|
|
16152
|
+
"permission-boundary": 1
|
|
16153
|
+
}
|
|
16154
|
+
}
|
|
16155
|
+
};
|
|
16156
|
+
|
|
16157
|
+
// src/agent-harness-templates/stable/1.0.0/manifest.json
|
|
16158
|
+
var manifest_default3 = {
|
|
16159
|
+
id: "stable",
|
|
16160
|
+
version: "1.0.0",
|
|
16161
|
+
status: "stable",
|
|
16162
|
+
owner: "granular",
|
|
16163
|
+
createdAt: "2026-05-24T00:00:00.000Z",
|
|
16164
|
+
changelog: "Baseline template wrapping the existing Granular agent harness prompt, continuation instruction, output contract, and generated-job review policy.",
|
|
16165
|
+
promptBuilder: "buildGranularAgentSystemPrompt",
|
|
16166
|
+
continuationBuilder: "buildContinuationInstruction",
|
|
16167
|
+
modelOutputInstruction: "agent-evals:modelOutputInstruction",
|
|
16168
|
+
codeReviewPolicy: "reviewGeneratedJobCode",
|
|
16169
|
+
defaultModel: "gpt-5.4",
|
|
16170
|
+
modelMatrix: ["gpt-5.4"],
|
|
16171
|
+
temperature: 0,
|
|
16172
|
+
compatibility: {
|
|
16173
|
+
minSdkVersion: "0.4.40",
|
|
16174
|
+
capabilities: [
|
|
16175
|
+
"executeCode",
|
|
16176
|
+
"readEntities",
|
|
16177
|
+
"workflowHelpers",
|
|
16178
|
+
"savedData",
|
|
16179
|
+
"showRecords"
|
|
16180
|
+
]
|
|
16181
|
+
},
|
|
16182
|
+
evalGates: {
|
|
16183
|
+
requiredSuites: [
|
|
16184
|
+
"agent-harness-hardening",
|
|
16185
|
+
"agent-harness-runtime-e2e",
|
|
16186
|
+
"agent-production-readiness-e2e"
|
|
16187
|
+
],
|
|
16188
|
+
criticalBuckets: [
|
|
16189
|
+
"relationship-traversal",
|
|
16190
|
+
"cross-turn-reference",
|
|
16191
|
+
"ambiguous-target-choice",
|
|
16192
|
+
"confirmation-gated-mutation",
|
|
16193
|
+
"denied-action-refusal",
|
|
16194
|
+
"mutation-presentation"
|
|
16195
|
+
],
|
|
16196
|
+
maxRegressionPct: 0,
|
|
16197
|
+
minPassK: {
|
|
16198
|
+
"critical-mutation": 0.9,
|
|
16199
|
+
"permission-boundary": 1
|
|
16200
|
+
}
|
|
16201
|
+
}
|
|
16202
|
+
};
|
|
16203
|
+
|
|
16063
16204
|
// src/agent-harness.ts
|
|
16205
|
+
var HARNESS_TEMPLATE_STATUSES = [
|
|
16206
|
+
"draft",
|
|
16207
|
+
"candidate",
|
|
16208
|
+
"release-candidate",
|
|
16209
|
+
"stable",
|
|
16210
|
+
"deprecated"
|
|
16211
|
+
];
|
|
16212
|
+
function requireRecord(value, context) {
|
|
16213
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
16214
|
+
throw new Error(`${context} must be an object.`);
|
|
16215
|
+
}
|
|
16216
|
+
return value;
|
|
16217
|
+
}
|
|
16218
|
+
function requiredString(record, key, context) {
|
|
16219
|
+
const value = record[key];
|
|
16220
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
16221
|
+
throw new Error(`${context}.${key} must be a non-empty string.`);
|
|
16222
|
+
}
|
|
16223
|
+
return value;
|
|
16224
|
+
}
|
|
16225
|
+
function requiredNumber(record, key, context) {
|
|
16226
|
+
const value = record[key];
|
|
16227
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
16228
|
+
throw new Error(`${context}.${key} must be a finite number.`);
|
|
16229
|
+
}
|
|
16230
|
+
return value;
|
|
16231
|
+
}
|
|
16232
|
+
function requiredStringArray(record, key, context) {
|
|
16233
|
+
const value = record[key];
|
|
16234
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
|
|
16235
|
+
throw new Error(`${context}.${key} must be an array of non-empty strings.`);
|
|
16236
|
+
}
|
|
16237
|
+
return [...value];
|
|
16238
|
+
}
|
|
16239
|
+
function requiredNumberRecord(record, key, context) {
|
|
16240
|
+
const value = requireRecord(record[key], `${context}.${key}`);
|
|
16241
|
+
const output = {};
|
|
16242
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
16243
|
+
if (typeof entryValue !== "number" || !Number.isFinite(entryValue) || entryValue < 0 || entryValue > 1) {
|
|
16244
|
+
throw new Error(
|
|
16245
|
+
`${context}.${key}.${entryKey} must be a number between 0 and 1.`
|
|
16246
|
+
);
|
|
16247
|
+
}
|
|
16248
|
+
output[entryKey] = entryValue;
|
|
16249
|
+
}
|
|
16250
|
+
return output;
|
|
16251
|
+
}
|
|
16252
|
+
function validateHarnessTemplateManifest(value, context = "HarnessTemplateManifest") {
|
|
16253
|
+
const record = requireRecord(value, context);
|
|
16254
|
+
const status = requiredString(record, "status", context);
|
|
16255
|
+
if (!HARNESS_TEMPLATE_STATUSES.includes(status)) {
|
|
16256
|
+
throw new Error(
|
|
16257
|
+
`${context}.status must be one of ${HARNESS_TEMPLATE_STATUSES.join(", ")}.`
|
|
16258
|
+
);
|
|
16259
|
+
}
|
|
16260
|
+
const createdAt = requiredString(record, "createdAt", context);
|
|
16261
|
+
if (Number.isNaN(Date.parse(createdAt))) {
|
|
16262
|
+
throw new Error(`${context}.createdAt must be an ISO timestamp.`);
|
|
16263
|
+
}
|
|
16264
|
+
const compatibility = requireRecord(
|
|
16265
|
+
record.compatibility,
|
|
16266
|
+
`${context}.compatibility`
|
|
16267
|
+
);
|
|
16268
|
+
const evalGates = requireRecord(record.evalGates, `${context}.evalGates`);
|
|
16269
|
+
const maxRegressionPct = requiredNumber(
|
|
16270
|
+
evalGates,
|
|
16271
|
+
"maxRegressionPct",
|
|
16272
|
+
`${context}.evalGates`
|
|
16273
|
+
);
|
|
16274
|
+
if (maxRegressionPct < 0 || maxRegressionPct > 1) {
|
|
16275
|
+
throw new Error(
|
|
16276
|
+
`${context}.evalGates.maxRegressionPct must be between 0 and 1.`
|
|
16277
|
+
);
|
|
16278
|
+
}
|
|
16279
|
+
return {
|
|
16280
|
+
id: requiredString(record, "id", context),
|
|
16281
|
+
version: requiredString(record, "version", context),
|
|
16282
|
+
status,
|
|
16283
|
+
owner: requiredString(record, "owner", context),
|
|
16284
|
+
createdAt,
|
|
16285
|
+
changelog: requiredString(record, "changelog", context),
|
|
16286
|
+
promptBuilder: requiredString(record, "promptBuilder", context),
|
|
16287
|
+
continuationBuilder: requiredString(record, "continuationBuilder", context),
|
|
16288
|
+
modelOutputInstruction: requiredString(
|
|
16289
|
+
record,
|
|
16290
|
+
"modelOutputInstruction",
|
|
16291
|
+
context
|
|
16292
|
+
),
|
|
16293
|
+
codeReviewPolicy: requiredString(record, "codeReviewPolicy", context),
|
|
16294
|
+
defaultModel: requiredString(record, "defaultModel", context),
|
|
16295
|
+
modelMatrix: requiredStringArray(record, "modelMatrix", context),
|
|
16296
|
+
temperature: requiredNumber(record, "temperature", context),
|
|
16297
|
+
compatibility: {
|
|
16298
|
+
minSdkVersion: requiredString(
|
|
16299
|
+
compatibility,
|
|
16300
|
+
"minSdkVersion",
|
|
16301
|
+
`${context}.compatibility`
|
|
16302
|
+
),
|
|
16303
|
+
capabilities: requiredStringArray(
|
|
16304
|
+
compatibility,
|
|
16305
|
+
"capabilities",
|
|
16306
|
+
`${context}.compatibility`
|
|
16307
|
+
)
|
|
16308
|
+
},
|
|
16309
|
+
evalGates: {
|
|
16310
|
+
requiredSuites: requiredStringArray(
|
|
16311
|
+
evalGates,
|
|
16312
|
+
"requiredSuites",
|
|
16313
|
+
`${context}.evalGates`
|
|
16314
|
+
),
|
|
16315
|
+
criticalBuckets: requiredStringArray(
|
|
16316
|
+
evalGates,
|
|
16317
|
+
"criticalBuckets",
|
|
16318
|
+
`${context}.evalGates`
|
|
16319
|
+
),
|
|
16320
|
+
maxRegressionPct,
|
|
16321
|
+
minPassK: requiredNumberRecord(
|
|
16322
|
+
evalGates,
|
|
16323
|
+
"minPassK",
|
|
16324
|
+
`${context}.evalGates`
|
|
16325
|
+
)
|
|
16326
|
+
}
|
|
16327
|
+
};
|
|
16328
|
+
}
|
|
16329
|
+
function defineHarnessTemplateManifest(value, context) {
|
|
16330
|
+
return validateHarnessTemplateManifest(value, context);
|
|
16331
|
+
}
|
|
16064
16332
|
function asRecord4(value) {
|
|
16065
16333
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
16066
16334
|
return value;
|
|
@@ -16084,6 +16352,16 @@ function uniqueStrings(values, maxCount) {
|
|
|
16084
16352
|
}
|
|
16085
16353
|
return output;
|
|
16086
16354
|
}
|
|
16355
|
+
function stableStringify(value) {
|
|
16356
|
+
if (value === null || typeof value !== "object") {
|
|
16357
|
+
return JSON.stringify(value);
|
|
16358
|
+
}
|
|
16359
|
+
if (Array.isArray(value)) {
|
|
16360
|
+
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
16361
|
+
}
|
|
16362
|
+
const record = value;
|
|
16363
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
|
|
16364
|
+
}
|
|
16087
16365
|
function renderConstBlock(name, value) {
|
|
16088
16366
|
return `const ${name} = ${JSON.stringify(value, null, 2)} as const;`;
|
|
16089
16367
|
}
|
|
@@ -18023,6 +18301,156 @@ ${knownFactsBlock}
|
|
|
18023
18301
|
[Request]
|
|
18024
18302
|
${input.request?.trim() || "Use the latest user message in the conversation."}`;
|
|
18025
18303
|
}
|
|
18304
|
+
var STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
|
|
18305
|
+
manifest_default3,
|
|
18306
|
+
"stable@1.0.0 manifest"
|
|
18307
|
+
);
|
|
18308
|
+
function hashHarnessTemplateValue(value) {
|
|
18309
|
+
return hashString(stableStringify(value)) || "00000000";
|
|
18310
|
+
}
|
|
18311
|
+
function renderStableHarnessPrompt(input) {
|
|
18312
|
+
const prompt = buildGranularAgentSystemPrompt(input);
|
|
18313
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18314
|
+
STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18315
|
+
);
|
|
18316
|
+
return {
|
|
18317
|
+
templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18318
|
+
templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18319
|
+
templateHash,
|
|
18320
|
+
promptInstanceHash: hashHarnessTemplateValue({
|
|
18321
|
+
templateHash,
|
|
18322
|
+
input,
|
|
18323
|
+
prompt
|
|
18324
|
+
}),
|
|
18325
|
+
prompt
|
|
18326
|
+
};
|
|
18327
|
+
}
|
|
18328
|
+
function renderStableHarnessContinuation(resultPreview) {
|
|
18329
|
+
const instruction = buildContinuationInstruction(resultPreview);
|
|
18330
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18331
|
+
STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18332
|
+
);
|
|
18333
|
+
return {
|
|
18334
|
+
templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18335
|
+
templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18336
|
+
templateHash,
|
|
18337
|
+
instruction
|
|
18338
|
+
};
|
|
18339
|
+
}
|
|
18340
|
+
var STABLE_AGENT_HARNESS_TEMPLATE = {
|
|
18341
|
+
manifest: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST,
|
|
18342
|
+
renderPrompt: renderStableHarnessPrompt,
|
|
18343
|
+
renderContinuation: renderStableHarnessContinuation
|
|
18344
|
+
};
|
|
18345
|
+
var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
|
|
18346
|
+
manifest_default2,
|
|
18347
|
+
"experimental-compact@0.1.0 manifest"
|
|
18348
|
+
);
|
|
18349
|
+
function renderExperimentalCompactHarnessPrompt(input) {
|
|
18350
|
+
const prompt = `${buildGranularAgentSystemPrompt(input)}
|
|
18351
|
+
|
|
18352
|
+
[Candidate Harness Delta: Compact Decision Discipline]
|
|
18353
|
+
- Prefer the smallest action that satisfies the current request.
|
|
18354
|
+
- When several records could match, ask one structured choice question before mutating.
|
|
18355
|
+
- Before any irreversible or outbound mutation, obtain explicit confirmation unless the policy surface already requires it.
|
|
18356
|
+
- Do not compensate for missing tools with raw network calls, synthetic records, or hidden side channels.
|
|
18357
|
+
- Keep the final user-facing reply focused on what was done, what was not done, and any remaining blocker.`;
|
|
18358
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18359
|
+
EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18360
|
+
);
|
|
18361
|
+
return {
|
|
18362
|
+
templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18363
|
+
templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18364
|
+
templateHash,
|
|
18365
|
+
promptInstanceHash: hashHarnessTemplateValue({
|
|
18366
|
+
templateHash,
|
|
18367
|
+
input,
|
|
18368
|
+
prompt
|
|
18369
|
+
}),
|
|
18370
|
+
prompt
|
|
18371
|
+
};
|
|
18372
|
+
}
|
|
18373
|
+
function renderExperimentalCompactHarnessContinuation(resultPreview) {
|
|
18374
|
+
const instruction = `${buildContinuationInstruction(resultPreview)}
|
|
18375
|
+
|
|
18376
|
+
Keep the continuation compact: either finish, ask the one blocking question, or run the next smallest safe action.`;
|
|
18377
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18378
|
+
EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18379
|
+
);
|
|
18380
|
+
return {
|
|
18381
|
+
templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18382
|
+
templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18383
|
+
templateHash,
|
|
18384
|
+
instruction
|
|
18385
|
+
};
|
|
18386
|
+
}
|
|
18387
|
+
var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE = {
|
|
18388
|
+
manifest: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST,
|
|
18389
|
+
renderPrompt: renderExperimentalCompactHarnessPrompt,
|
|
18390
|
+
renderContinuation: renderExperimentalCompactHarnessContinuation
|
|
18391
|
+
};
|
|
18392
|
+
var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
|
|
18393
|
+
manifest_default,
|
|
18394
|
+
"action-presentation@0.1.0 manifest"
|
|
18395
|
+
);
|
|
18396
|
+
function renderActionPresentationHarnessPrompt(input) {
|
|
18397
|
+
const prompt = `${buildGranularAgentSystemPrompt(input)}
|
|
18398
|
+
|
|
18399
|
+
[Candidate Harness Delta: Action Request And Result Presentation]
|
|
18400
|
+
- Treat requests like "handle it", "take care of it", "send it", "update it", "fix it", "process it", or "do it" as action requests when the message names a business object, target, workflow, or mutation verb.
|
|
18401
|
+
- For action requests involving session data, generated files, records, effects, or workflows, do not use text-only completion. Generate and run code, ask the blocking human question, or clearly refuse if policy/tooling prevents the action.
|
|
18402
|
+
- Never answer only "Done", "OK", "Handled", or similar terse completion text for a mutation request. The final user-facing reply must name the action attempted, the grounded target or blocker, and the actual result.
|
|
18403
|
+
- After a mutation/effect call, base the reply on the returned action result and include a visible target label or identifier when one exists.
|
|
18404
|
+
- If no target can be grounded, say what was searched and what exact identifier or choice is needed; do not pretend the action completed.`;
|
|
18405
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18406
|
+
ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18407
|
+
);
|
|
18408
|
+
return {
|
|
18409
|
+
templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18410
|
+
templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18411
|
+
templateHash,
|
|
18412
|
+
promptInstanceHash: hashHarnessTemplateValue({
|
|
18413
|
+
templateHash,
|
|
18414
|
+
input,
|
|
18415
|
+
prompt
|
|
18416
|
+
}),
|
|
18417
|
+
prompt
|
|
18418
|
+
};
|
|
18419
|
+
}
|
|
18420
|
+
function renderActionPresentationHarnessContinuation(resultPreview) {
|
|
18421
|
+
const instruction = `${buildContinuationInstruction(resultPreview)}
|
|
18422
|
+
|
|
18423
|
+
Before finishing, check whether the latest user request asked for an action. If it did, do not finish with a bare completion token; either continue with the needed tool/code step, ask the blocking question, refuse with the policy reason, or report the grounded action result with the target label.`;
|
|
18424
|
+
const templateHash = hashHarnessTemplateValue(
|
|
18425
|
+
ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
|
|
18426
|
+
);
|
|
18427
|
+
return {
|
|
18428
|
+
templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
|
|
18429
|
+
templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
|
|
18430
|
+
templateHash,
|
|
18431
|
+
instruction
|
|
18432
|
+
};
|
|
18433
|
+
}
|
|
18434
|
+
var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE = {
|
|
18435
|
+
manifest: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST,
|
|
18436
|
+
renderPrompt: renderActionPresentationHarnessPrompt,
|
|
18437
|
+
renderContinuation: renderActionPresentationHarnessContinuation
|
|
18438
|
+
};
|
|
18439
|
+
var AGENT_HARNESS_TEMPLATES = {
|
|
18440
|
+
[STABLE_AGENT_HARNESS_TEMPLATE.manifest.id]: STABLE_AGENT_HARNESS_TEMPLATE,
|
|
18441
|
+
[EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE.manifest.id]: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE,
|
|
18442
|
+
[ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE.manifest.id]: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE
|
|
18443
|
+
};
|
|
18444
|
+
function resolveHarnessTemplate(templateId = "stable", options) {
|
|
18445
|
+
const resolved = AGENT_HARNESS_TEMPLATES[templateId];
|
|
18446
|
+
if (resolved) return resolved;
|
|
18447
|
+
{
|
|
18448
|
+
const known = Object.keys(AGENT_HARNESS_TEMPLATES).join(", ");
|
|
18449
|
+
throw new Error(
|
|
18450
|
+
`Unknown harness template "${templateId}". Known templates: ${known}`
|
|
18451
|
+
);
|
|
18452
|
+
}
|
|
18453
|
+
}
|
|
18026
18454
|
|
|
18027
18455
|
// src/openai-usage.ts
|
|
18028
18456
|
var OPENAI_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
|
|
@@ -18144,6 +18572,98 @@ function matchesPattern(text, matcher) {
|
|
|
18144
18572
|
if (typeof matcher === "string") return text.includes(matcher);
|
|
18145
18573
|
return matcher.test(text);
|
|
18146
18574
|
}
|
|
18575
|
+
function matcherFromConfig(value) {
|
|
18576
|
+
const match = value.match(/^\/([\s\S]*)\/([dgimsuvy]*)$/);
|
|
18577
|
+
if (!match) return value;
|
|
18578
|
+
return new RegExp(match[1] || "", match[2] || "");
|
|
18579
|
+
}
|
|
18580
|
+
function matchersFromConfig(values) {
|
|
18581
|
+
if (!values?.length) return void 0;
|
|
18582
|
+
return values.map((value) => matcherFromConfig(value));
|
|
18583
|
+
}
|
|
18584
|
+
function expectationsFromConfig(input) {
|
|
18585
|
+
if (!input) return void 0;
|
|
18586
|
+
return {
|
|
18587
|
+
replyIncludes: matchersFromConfig(input.replyIncludes),
|
|
18588
|
+
replyExcludes: matchersFromConfig(input.replyExcludes),
|
|
18589
|
+
actionIncludes: matchersFromConfig(input.actionIncludes),
|
|
18590
|
+
actionExcludes: matchersFromConfig(input.actionExcludes),
|
|
18591
|
+
codeIncludes: matchersFromConfig(input.codeIncludes),
|
|
18592
|
+
codeExcludes: matchersFromConfig(input.codeExcludes),
|
|
18593
|
+
behaviorBuckets: input.behaviorBuckets,
|
|
18594
|
+
actions: input.actions ? {
|
|
18595
|
+
required: matchersFromConfig(input.actions.required),
|
|
18596
|
+
forbidden: matchersFromConfig(input.actions.forbidden)
|
|
18597
|
+
} : void 0,
|
|
18598
|
+
prompts: input.prompts ? {
|
|
18599
|
+
...input.prompts,
|
|
18600
|
+
requiredConfirmationBefore: matchersFromConfig(
|
|
18601
|
+
input.prompts.requiredConfirmationBefore
|
|
18602
|
+
)
|
|
18603
|
+
} : void 0,
|
|
18604
|
+
presentation: input.presentation ? {
|
|
18605
|
+
mustMention: matchersFromConfig(input.presentation.mustMention),
|
|
18606
|
+
mustNotMention: matchersFromConfig(input.presentation.mustNotMention),
|
|
18607
|
+
mustDisplayOrSave: matchersFromConfig(
|
|
18608
|
+
input.presentation.mustDisplayOrSave
|
|
18609
|
+
)
|
|
18610
|
+
} : void 0
|
|
18611
|
+
};
|
|
18612
|
+
}
|
|
18613
|
+
function promptResponderFromConfig(rules) {
|
|
18614
|
+
if (!rules?.length) return void 0;
|
|
18615
|
+
return createHumanResponder(
|
|
18616
|
+
rules.map((rule) => ({
|
|
18617
|
+
type: rule.type,
|
|
18618
|
+
when: Array.isArray(rule.when) ? rule.when.map((matcher) => matcherFromConfig(matcher)) : rule.when ? matcherFromConfig(rule.when) : void 0,
|
|
18619
|
+
answer: rule.answer
|
|
18620
|
+
}))
|
|
18621
|
+
);
|
|
18622
|
+
}
|
|
18623
|
+
function inspectionFromConfig(inspection) {
|
|
18624
|
+
if (!inspection) return void 0;
|
|
18625
|
+
return {
|
|
18626
|
+
code: inspection.code,
|
|
18627
|
+
includes: matchersFromConfig(inspection.includes),
|
|
18628
|
+
excludes: matchersFromConfig(inspection.excludes)
|
|
18629
|
+
};
|
|
18630
|
+
}
|
|
18631
|
+
function inspectionsFromConfig(inspections) {
|
|
18632
|
+
if (!inspections) return void 0;
|
|
18633
|
+
if (Array.isArray(inspections)) {
|
|
18634
|
+
return inspections.map((inspection) => inspectionFromConfig(inspection)).filter(
|
|
18635
|
+
(inspection) => Boolean(inspection)
|
|
18636
|
+
);
|
|
18637
|
+
}
|
|
18638
|
+
return inspectionFromConfig(inspections);
|
|
18639
|
+
}
|
|
18640
|
+
function scenariosFromAgentEvalFile(file) {
|
|
18641
|
+
return file.scenarios.map((scenario) => ({
|
|
18642
|
+
id: scenario.id,
|
|
18643
|
+
description: scenario.description,
|
|
18644
|
+
request: scenario.request,
|
|
18645
|
+
behaviorBuckets: scenario.behaviorBuckets,
|
|
18646
|
+
human: promptResponderFromConfig(scenario.human),
|
|
18647
|
+
prepareRecords: scenario.prepareRecords,
|
|
18648
|
+
expect: expectationsFromConfig(scenario.expect),
|
|
18649
|
+
verify: inspectionFromConfig(scenario.verify),
|
|
18650
|
+
inspect: inspectionsFromConfig(scenario.inspect),
|
|
18651
|
+
steps: scenario.steps?.map((step) => ({
|
|
18652
|
+
id: step.id,
|
|
18653
|
+
request: step.request,
|
|
18654
|
+
behaviorBuckets: step.behaviorBuckets || scenario.behaviorBuckets,
|
|
18655
|
+
human: promptResponderFromConfig(step.human || scenario.human),
|
|
18656
|
+
expect: expectationsFromConfig(step.expect || scenario.expect),
|
|
18657
|
+
inspect: inspectionsFromConfig(step.inspect)
|
|
18658
|
+
}))
|
|
18659
|
+
}));
|
|
18660
|
+
}
|
|
18661
|
+
async function loadAgentEvalScenarioFile(filePath) {
|
|
18662
|
+
const raw = JSON.parse(
|
|
18663
|
+
await readFile(filePath, "utf8")
|
|
18664
|
+
);
|
|
18665
|
+
return scenariosFromAgentEvalFile(raw);
|
|
18666
|
+
}
|
|
18147
18667
|
function assertMatches(label, text, includes = [], excludes = []) {
|
|
18148
18668
|
for (const matcher of includes) {
|
|
18149
18669
|
if (!matchesPattern(text, matcher)) {
|
|
@@ -18176,6 +18696,23 @@ function asArray3(value) {
|
|
|
18176
18696
|
if (!value) return [];
|
|
18177
18697
|
return Array.isArray(value) ? value : [value];
|
|
18178
18698
|
}
|
|
18699
|
+
function progressEvent(input) {
|
|
18700
|
+
return {
|
|
18701
|
+
...input,
|
|
18702
|
+
id: input.id || [
|
|
18703
|
+
input.scenarioId || "suite",
|
|
18704
|
+
input.stepId || input.phase,
|
|
18705
|
+
input.iteration ? `iteration-${input.iteration}` : "",
|
|
18706
|
+
input.jobId || "",
|
|
18707
|
+
input.title,
|
|
18708
|
+
Date.now()
|
|
18709
|
+
].filter(Boolean).join(":"),
|
|
18710
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
18711
|
+
};
|
|
18712
|
+
}
|
|
18713
|
+
async function emitProgress(handler, event) {
|
|
18714
|
+
await handler?.(progressEvent(event));
|
|
18715
|
+
}
|
|
18179
18716
|
var GPT_54_TOKEN_PRICING_USD_PER_MILLION = {
|
|
18180
18717
|
input: 2.5,
|
|
18181
18718
|
cachedInput: 0.25,
|
|
@@ -18418,7 +18955,7 @@ function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
|
|
|
18418
18955
|
});
|
|
18419
18956
|
}
|
|
18420
18957
|
function createScriptedPromptResponder(rules, fallback) {
|
|
18421
|
-
|
|
18958
|
+
const responder = async ({ prompt, history }) => {
|
|
18422
18959
|
const promptText = `${prompt.title || ""}
|
|
18423
18960
|
${prompt.message || ""}`;
|
|
18424
18961
|
for (const rule of rules) {
|
|
@@ -18435,6 +18972,11 @@ ${prompt.message || ""}`;
|
|
|
18435
18972
|
`No scripted prompt responder matched prompt ${prompt.id}: ${promptText}`
|
|
18436
18973
|
);
|
|
18437
18974
|
};
|
|
18975
|
+
Object.defineProperty(responder, "__granularScriptedPromptRules", {
|
|
18976
|
+
enumerable: false,
|
|
18977
|
+
value: rules
|
|
18978
|
+
});
|
|
18979
|
+
return responder;
|
|
18438
18980
|
}
|
|
18439
18981
|
function extractJsonObject(text) {
|
|
18440
18982
|
const start = text.indexOf("{");
|
|
@@ -18721,6 +19263,22 @@ ${checkpoint.latestJobResult}` : null
|
|
|
18721
19263
|
];
|
|
18722
19264
|
return lines.filter(Boolean).join("\n\n");
|
|
18723
19265
|
}
|
|
19266
|
+
function readableAgentMessage(message) {
|
|
19267
|
+
const record = asRecord6(message);
|
|
19268
|
+
if (!record) return JSON.stringify(message);
|
|
19269
|
+
if (typeof record.reply === "string" && record.reply.trim()) {
|
|
19270
|
+
return record.reply;
|
|
19271
|
+
}
|
|
19272
|
+
const show = asRecord6(record.show);
|
|
19273
|
+
const variableNames = asArray3(show?.variableNames).map((value) => String(value)).filter(Boolean);
|
|
19274
|
+
if (variableNames.length) {
|
|
19275
|
+
return `Displayed ${variableNames.join(", ")}`;
|
|
19276
|
+
}
|
|
19277
|
+
if (typeof record.kind === "string") {
|
|
19278
|
+
return `Agent ${record.kind} message`;
|
|
19279
|
+
}
|
|
19280
|
+
return JSON.stringify(message);
|
|
19281
|
+
}
|
|
18724
19282
|
async function waitForJobOutcome(input) {
|
|
18725
19283
|
const stdout = [];
|
|
18726
19284
|
const stderr = [];
|
|
@@ -18728,6 +19286,11 @@ async function waitForJobOutcome(input) {
|
|
|
18728
19286
|
let lastPromptCount = 0;
|
|
18729
19287
|
let lastMessageCount = 0;
|
|
18730
19288
|
let lastJobSummary = null;
|
|
19289
|
+
let lastActionSummaryKey = "";
|
|
19290
|
+
let lastActionSummaryLength = 0;
|
|
19291
|
+
let lastAgentMessageKey = "";
|
|
19292
|
+
let lastAgentMessageCount = 0;
|
|
19293
|
+
let lastJobStatus = "";
|
|
18731
19294
|
input.job.on("stdout", (line) => stdout.push(String(line)));
|
|
18732
19295
|
input.job.on("stderr", (line) => stderr.push(String(line)));
|
|
18733
19296
|
const startedAt = Date.now();
|
|
@@ -18743,7 +19306,71 @@ async function waitForJobOutcome(input) {
|
|
|
18743
19306
|
const messages = asArray3(asRecord6(liveDoc.conversation)?.messages);
|
|
18744
19307
|
lastMessageCount = messages.length;
|
|
18745
19308
|
lastJobSummary = asRecord6(asRecord6(liveDoc.jobs)?.byId)?.[input.job.id] || null;
|
|
19309
|
+
const jobRecord = asRecord6(lastJobSummary);
|
|
19310
|
+
const jobStatus = typeof jobRecord?.status === "string" ? jobRecord.status : "";
|
|
19311
|
+
if (jobStatus && jobStatus !== lastJobStatus) {
|
|
19312
|
+
lastJobStatus = jobStatus;
|
|
19313
|
+
input.onProgress?.({
|
|
19314
|
+
id: `${input.job.id}:status:${jobStatus}:${Date.now()}`,
|
|
19315
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19316
|
+
phase: "job",
|
|
19317
|
+
status: "running",
|
|
19318
|
+
jobId: input.job.id,
|
|
19319
|
+
title: `Job ${jobStatus}`,
|
|
19320
|
+
message: `Granular job ${input.job.id}`,
|
|
19321
|
+
data: jobRecord,
|
|
19322
|
+
...input.progressContext
|
|
19323
|
+
});
|
|
19324
|
+
}
|
|
19325
|
+
const actionSummary = getActionSummary(liveDoc, input.job.id);
|
|
19326
|
+
const actionSummaryKey = JSON.stringify(actionSummary);
|
|
19327
|
+
if (actionSummary.length && actionSummaryKey !== lastActionSummaryKey) {
|
|
19328
|
+
const newActions = actionSummary.slice(lastActionSummaryLength);
|
|
19329
|
+
lastActionSummaryLength = actionSummary.length;
|
|
19330
|
+
lastActionSummaryKey = actionSummaryKey;
|
|
19331
|
+
input.onProgress?.({
|
|
19332
|
+
id: `${input.job.id}:actions:${Date.now()}`,
|
|
19333
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19334
|
+
phase: "job",
|
|
19335
|
+
status: "running",
|
|
19336
|
+
jobId: input.job.id,
|
|
19337
|
+
title: newActions.length === 1 ? "Action observed" : "Actions observed",
|
|
19338
|
+
message: (newActions.length ? newActions : actionSummary).join("\n"),
|
|
19339
|
+
data: { actionSummary, newActions },
|
|
19340
|
+
...input.progressContext
|
|
19341
|
+
});
|
|
19342
|
+
}
|
|
19343
|
+
const agentMessages = getJobAgentMessages(liveDoc, input.job.id);
|
|
19344
|
+
const agentMessageKey = JSON.stringify(agentMessages);
|
|
19345
|
+
if (agentMessages.length && agentMessageKey !== lastAgentMessageKey) {
|
|
19346
|
+
const newMessages = agentMessages.slice(lastAgentMessageCount);
|
|
19347
|
+
lastAgentMessageCount = agentMessages.length;
|
|
19348
|
+
lastAgentMessageKey = agentMessageKey;
|
|
19349
|
+
const latestMessage = newMessages.at(-1) || agentMessages.at(-1);
|
|
19350
|
+
input.onProgress?.({
|
|
19351
|
+
id: `${input.job.id}:messages:${Date.now()}`,
|
|
19352
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19353
|
+
phase: "job",
|
|
19354
|
+
status: "running",
|
|
19355
|
+
jobId: input.job.id,
|
|
19356
|
+
title: "Agent message",
|
|
19357
|
+
message: readableAgentMessage(latestMessage),
|
|
19358
|
+
data: { agentMessages, newMessages },
|
|
19359
|
+
...input.progressContext
|
|
19360
|
+
});
|
|
19361
|
+
}
|
|
18746
19362
|
if (prompts.length > 0) {
|
|
19363
|
+
input.onProgress?.({
|
|
19364
|
+
id: `${input.job.id}:prompt:${prompts[0]?.id || Date.now()}`,
|
|
19365
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19366
|
+
phase: "prompt",
|
|
19367
|
+
status: "running",
|
|
19368
|
+
jobId: input.job.id,
|
|
19369
|
+
title: "Waiting for prompt answer",
|
|
19370
|
+
message: prompts[0]?.message || prompts[0]?.title,
|
|
19371
|
+
data: { prompts },
|
|
19372
|
+
...input.progressContext
|
|
19373
|
+
});
|
|
18747
19374
|
return { kind: "prompt", prompts, liveDoc, stdout, stderr };
|
|
18748
19375
|
}
|
|
18749
19376
|
try {
|
|
@@ -18752,6 +19379,17 @@ async function waitForJobOutcome(input) {
|
|
|
18752
19379
|
input.pollIntervalMs,
|
|
18753
19380
|
`job ${input.job.id} tick`
|
|
18754
19381
|
);
|
|
19382
|
+
input.onProgress?.({
|
|
19383
|
+
id: `${input.job.id}:completed:${Date.now()}`,
|
|
19384
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19385
|
+
phase: "job",
|
|
19386
|
+
status: "passed",
|
|
19387
|
+
jobId: input.job.id,
|
|
19388
|
+
title: "Job completed",
|
|
19389
|
+
message: JSON.stringify(result)?.slice(0, 800),
|
|
19390
|
+
data: { result, stdout, stderr },
|
|
19391
|
+
...input.progressContext
|
|
19392
|
+
});
|
|
18755
19393
|
return { kind: "completed", result, liveDoc, stdout, stderr };
|
|
18756
19394
|
} catch (error) {
|
|
18757
19395
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -18983,6 +19621,10 @@ function buildSessionLogReport(input) {
|
|
|
18983
19621
|
`- Environment id: \`${conversation.environment.environmentId}\``,
|
|
18984
19622
|
`- Sandbox id: \`${conversation.environment.sandboxId}\``,
|
|
18985
19623
|
`- Status: ${result?.status || (error ? "failed" : "unknown")}`,
|
|
19624
|
+
...systemPrompts[0]?.iteration.templateId ? [
|
|
19625
|
+
`- Harness template: \`${systemPrompts[0].iteration.templateId}@${systemPrompts[0].iteration.templateVersion || "unknown"}\``,
|
|
19626
|
+
`- Template hash: \`${systemPrompts[0].iteration.templateHash || "unknown"}\``
|
|
19627
|
+
] : [],
|
|
18986
19628
|
...result?.error || error ? [`- Error: ${result?.error || error}`] : [],
|
|
18987
19629
|
"",
|
|
18988
19630
|
"## Conversation"
|
|
@@ -19090,6 +19732,12 @@ function buildSessionLogReport(input) {
|
|
|
19090
19732
|
lines.push(
|
|
19091
19733
|
`### Turn ${turn.turnNumber}, Generation ${iteration.iteration}`,
|
|
19092
19734
|
"",
|
|
19735
|
+
...iteration.templateId ? [
|
|
19736
|
+
`- Template: \`${iteration.templateId}@${iteration.templateVersion || "unknown"}\``,
|
|
19737
|
+
`- Template hash: \`${iteration.templateHash || "unknown"}\``,
|
|
19738
|
+
`- Prompt hash: \`${iteration.promptInstanceHash || "unknown"}\``,
|
|
19739
|
+
""
|
|
19740
|
+
] : [],
|
|
19093
19741
|
fenced(iteration.systemPrompt, "text"),
|
|
19094
19742
|
""
|
|
19095
19743
|
);
|
|
@@ -19131,9 +19779,167 @@ function isTransientEvalError(error) {
|
|
|
19131
19779
|
const message = error instanceof Error ? error.message : String(error);
|
|
19132
19780
|
return /socket connection was closed unexpectedly/i.test(message) || /timed out after/i.test(message) || /ECONNRESET/i.test(message) || /network/i.test(message) || /429/.test(message);
|
|
19133
19781
|
}
|
|
19782
|
+
function assertionPassed(id, run) {
|
|
19783
|
+
try {
|
|
19784
|
+
run(id);
|
|
19785
|
+
return { id, label: id, status: "passed" };
|
|
19786
|
+
} catch (error) {
|
|
19787
|
+
return {
|
|
19788
|
+
id,
|
|
19789
|
+
label: id,
|
|
19790
|
+
status: "failed",
|
|
19791
|
+
message: error instanceof Error ? error.message : String(error)
|
|
19792
|
+
};
|
|
19793
|
+
}
|
|
19794
|
+
}
|
|
19795
|
+
function hasMatchers(matchers) {
|
|
19796
|
+
return Boolean(matchers?.length);
|
|
19797
|
+
}
|
|
19798
|
+
function evaluateExpectationAssertions(input) {
|
|
19799
|
+
const expect = input.expect;
|
|
19800
|
+
if (!expect) return [];
|
|
19801
|
+
const prefix = `${input.scenarioId} step ${input.stepIndex + 1}`;
|
|
19802
|
+
const assertions = [];
|
|
19803
|
+
const actionText = input.completed.actionSummary.join("\n");
|
|
19804
|
+
const codeText = input.completed.finalCode || "";
|
|
19805
|
+
const displaySurface = [
|
|
19806
|
+
input.completed.responseText,
|
|
19807
|
+
actionText,
|
|
19808
|
+
codeText,
|
|
19809
|
+
JSON.stringify(input.completed.result)
|
|
19810
|
+
].join("\n");
|
|
19811
|
+
const promptSurface = input.completed.promptInteractions.map(
|
|
19812
|
+
(interaction) => [interaction.type, interaction.title, interaction.message].join(" ")
|
|
19813
|
+
).join("\n");
|
|
19814
|
+
if (hasMatchers(expect.replyIncludes) || hasMatchers(expect.replyExcludes)) {
|
|
19815
|
+
assertions.push(
|
|
19816
|
+
assertionPassed(
|
|
19817
|
+
"reply",
|
|
19818
|
+
() => assertMatches(
|
|
19819
|
+
`Reply for ${prefix}`,
|
|
19820
|
+
input.completed.responseText,
|
|
19821
|
+
expect.replyIncludes,
|
|
19822
|
+
expect.replyExcludes
|
|
19823
|
+
)
|
|
19824
|
+
)
|
|
19825
|
+
);
|
|
19826
|
+
}
|
|
19827
|
+
if (hasMatchers(expect.actionIncludes) || hasMatchers(expect.actionExcludes)) {
|
|
19828
|
+
assertions.push(
|
|
19829
|
+
assertionPassed(
|
|
19830
|
+
"actions",
|
|
19831
|
+
() => assertMatches(
|
|
19832
|
+
`Action summary for ${prefix}`,
|
|
19833
|
+
actionText,
|
|
19834
|
+
expect.actionIncludes,
|
|
19835
|
+
expect.actionExcludes
|
|
19836
|
+
)
|
|
19837
|
+
)
|
|
19838
|
+
);
|
|
19839
|
+
}
|
|
19840
|
+
if (hasMatchers(expect.codeIncludes) || hasMatchers(expect.codeExcludes)) {
|
|
19841
|
+
assertions.push(
|
|
19842
|
+
assertionPassed(
|
|
19843
|
+
"code",
|
|
19844
|
+
() => assertMatches(
|
|
19845
|
+
`Generated code for ${prefix}`,
|
|
19846
|
+
codeText,
|
|
19847
|
+
expect.codeIncludes,
|
|
19848
|
+
expect.codeExcludes
|
|
19849
|
+
)
|
|
19850
|
+
)
|
|
19851
|
+
);
|
|
19852
|
+
}
|
|
19853
|
+
if (hasMatchers(expect.actions?.required) || hasMatchers(expect.actions?.forbidden)) {
|
|
19854
|
+
assertions.push(
|
|
19855
|
+
assertionPassed(
|
|
19856
|
+
"required-actions",
|
|
19857
|
+
() => assertMatches(
|
|
19858
|
+
`Required actions for ${prefix}`,
|
|
19859
|
+
actionText,
|
|
19860
|
+
expect.actions?.required,
|
|
19861
|
+
expect.actions?.forbidden
|
|
19862
|
+
)
|
|
19863
|
+
)
|
|
19864
|
+
);
|
|
19865
|
+
}
|
|
19866
|
+
if (hasMatchers(expect.presentation?.mustMention) || hasMatchers(expect.presentation?.mustNotMention)) {
|
|
19867
|
+
assertions.push(
|
|
19868
|
+
assertionPassed(
|
|
19869
|
+
"presentation",
|
|
19870
|
+
() => assertMatches(
|
|
19871
|
+
`Presentation for ${prefix}`,
|
|
19872
|
+
input.completed.responseText,
|
|
19873
|
+
expect.presentation?.mustMention,
|
|
19874
|
+
expect.presentation?.mustNotMention
|
|
19875
|
+
)
|
|
19876
|
+
)
|
|
19877
|
+
);
|
|
19878
|
+
}
|
|
19879
|
+
if (hasMatchers(expect.presentation?.mustDisplayOrSave)) {
|
|
19880
|
+
assertions.push(
|
|
19881
|
+
assertionPassed(
|
|
19882
|
+
"display",
|
|
19883
|
+
() => assertMatches(
|
|
19884
|
+
`Displayed or saved records for ${prefix}`,
|
|
19885
|
+
displaySurface,
|
|
19886
|
+
expect.presentation?.mustDisplayOrSave
|
|
19887
|
+
)
|
|
19888
|
+
)
|
|
19889
|
+
);
|
|
19890
|
+
}
|
|
19891
|
+
if (expect.prompts?.requiredChoiceWhenAmbiguous) {
|
|
19892
|
+
assertions.push(
|
|
19893
|
+
assertionPassed("choice", () => {
|
|
19894
|
+
if (!input.completed.promptInteractions.some(
|
|
19895
|
+
(interaction) => interaction.type === "choice"
|
|
19896
|
+
)) {
|
|
19897
|
+
throw new Error(`Expected ${prefix} to use a choice prompt.`);
|
|
19898
|
+
}
|
|
19899
|
+
})
|
|
19900
|
+
);
|
|
19901
|
+
}
|
|
19902
|
+
for (const forbiddenType of expect.prompts?.forbiddenPromptTypes || []) {
|
|
19903
|
+
assertions.push(
|
|
19904
|
+
assertionPassed(`forbidden-prompt:${forbiddenType}`, () => {
|
|
19905
|
+
if (input.completed.promptInteractions.some(
|
|
19906
|
+
(interaction) => interaction.type === forbiddenType
|
|
19907
|
+
)) {
|
|
19908
|
+
throw new Error(
|
|
19909
|
+
`Prompt interactions for ${prefix} used forbidden prompt type ${forbiddenType}.`
|
|
19910
|
+
);
|
|
19911
|
+
}
|
|
19912
|
+
})
|
|
19913
|
+
);
|
|
19914
|
+
}
|
|
19915
|
+
if (expect.prompts?.requiredConfirmationBefore?.length) {
|
|
19916
|
+
assertions.push(
|
|
19917
|
+
assertionPassed(
|
|
19918
|
+
"confirmation",
|
|
19919
|
+
() => assertMatches(
|
|
19920
|
+
`Confirmation prompts for ${prefix}`,
|
|
19921
|
+
[actionText, promptSurface].join("\n"),
|
|
19922
|
+
expect.prompts?.requiredConfirmationBefore
|
|
19923
|
+
)
|
|
19924
|
+
)
|
|
19925
|
+
);
|
|
19926
|
+
}
|
|
19927
|
+
return assertions;
|
|
19928
|
+
}
|
|
19134
19929
|
async function runAgentEvalSuite(options) {
|
|
19135
19930
|
const results = [];
|
|
19136
19931
|
for (const scenario of options.scenarios) {
|
|
19932
|
+
await options.harness.emitProgress?.({
|
|
19933
|
+
phase: "scenario",
|
|
19934
|
+
status: "running",
|
|
19935
|
+
scenarioId: scenario.id,
|
|
19936
|
+
title: "Scenario started",
|
|
19937
|
+
message: scenario.description || scenario.request || scenario.id,
|
|
19938
|
+
data: {
|
|
19939
|
+
scenarioId: scenario.id,
|
|
19940
|
+
behaviorBuckets: scenario.behaviorBuckets
|
|
19941
|
+
}
|
|
19942
|
+
});
|
|
19137
19943
|
let attempt = 0;
|
|
19138
19944
|
let finalResult = null;
|
|
19139
19945
|
while (attempt < 2 && !finalResult) {
|
|
@@ -19204,27 +20010,38 @@ async function runAgentEvalSuite(options) {
|
|
|
19204
20010
|
},
|
|
19205
20011
|
assertMatches
|
|
19206
20012
|
};
|
|
19207
|
-
|
|
19208
|
-
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19212
|
-
|
|
19213
|
-
|
|
19214
|
-
|
|
19215
|
-
|
|
19216
|
-
|
|
19217
|
-
|
|
19218
|
-
step.
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
)
|
|
20013
|
+
const assertions = evaluateExpectationAssertions({
|
|
20014
|
+
scenarioId: scenario.id,
|
|
20015
|
+
stepIndex: index,
|
|
20016
|
+
expect: step.expect,
|
|
20017
|
+
completed
|
|
20018
|
+
});
|
|
20019
|
+
for (const assertion of assertions) {
|
|
20020
|
+
await options.harness.emitProgress?.({
|
|
20021
|
+
phase: "assertion",
|
|
20022
|
+
status: assertion.status,
|
|
20023
|
+
scenarioId: scenario.id,
|
|
20024
|
+
stepId: step.id || `step-${index + 1}`,
|
|
20025
|
+
title: assertion.label,
|
|
20026
|
+
message: assertion.message,
|
|
20027
|
+
data: assertion
|
|
20028
|
+
});
|
|
20029
|
+
}
|
|
20030
|
+
const failedAssertion = assertions.find(
|
|
20031
|
+
(assertion) => assertion.status === "failed"
|
|
20032
|
+
);
|
|
20033
|
+
if (failedAssertion) {
|
|
20034
|
+
throw new Error(failedAssertion.message || failedAssertion.label);
|
|
19226
20035
|
}
|
|
19227
20036
|
for (const inspection of stepInspections) {
|
|
20037
|
+
await options.harness.emitProgress?.({
|
|
20038
|
+
phase: "inspection",
|
|
20039
|
+
status: "running",
|
|
20040
|
+
scenarioId: scenario.id,
|
|
20041
|
+
stepId: step.id || `step-${index + 1}`,
|
|
20042
|
+
title: "Running step inspection",
|
|
20043
|
+
message: inspection.code.slice(0, 500)
|
|
20044
|
+
});
|
|
19228
20045
|
const inspectionResult = await context.inspect(inspection.code);
|
|
19229
20046
|
const inspectionText = JSON.stringify(inspectionResult, null, 2);
|
|
19230
20047
|
assertMatches(
|
|
@@ -19240,9 +20057,32 @@ async function runAgentEvalSuite(options) {
|
|
|
19240
20057
|
});
|
|
19241
20058
|
}
|
|
19242
20059
|
inspectionResults.push(inspectionResult);
|
|
20060
|
+
await options.harness.emitProgress?.({
|
|
20061
|
+
phase: "inspection",
|
|
20062
|
+
status: "passed",
|
|
20063
|
+
scenarioId: scenario.id,
|
|
20064
|
+
stepId: step.id || `step-${index + 1}`,
|
|
20065
|
+
title: "Step inspection passed",
|
|
20066
|
+
message: inspectionText.slice(0, 800),
|
|
20067
|
+
data: inspectionResult
|
|
20068
|
+
});
|
|
19243
20069
|
}
|
|
19244
20070
|
for (const check of stepChecks) {
|
|
20071
|
+
await options.harness.emitProgress?.({
|
|
20072
|
+
phase: "check",
|
|
20073
|
+
status: "running",
|
|
20074
|
+
scenarioId: scenario.id,
|
|
20075
|
+
stepId: step.id || `step-${index + 1}`,
|
|
20076
|
+
title: "Running custom check"
|
|
20077
|
+
});
|
|
19245
20078
|
await check(context);
|
|
20079
|
+
await options.harness.emitProgress?.({
|
|
20080
|
+
phase: "check",
|
|
20081
|
+
status: "passed",
|
|
20082
|
+
scenarioId: scenario.id,
|
|
20083
|
+
stepId: step.id || `step-${index + 1}`,
|
|
20084
|
+
title: "Custom check passed"
|
|
20085
|
+
});
|
|
19246
20086
|
}
|
|
19247
20087
|
stepResults.push({
|
|
19248
20088
|
id: step.id || `step-${index + 1}`,
|
|
@@ -19253,6 +20093,7 @@ async function runAgentEvalSuite(options) {
|
|
|
19253
20093
|
actionSummary: completed.actionSummary,
|
|
19254
20094
|
promptInteractions: completed.promptInteractions,
|
|
19255
20095
|
inspectionResults,
|
|
20096
|
+
assertions,
|
|
19256
20097
|
turnDir: completed.turnDir
|
|
19257
20098
|
});
|
|
19258
20099
|
}
|
|
@@ -19271,6 +20112,7 @@ async function runAgentEvalSuite(options) {
|
|
|
19271
20112
|
actionSummary: lastStep.actionSummary,
|
|
19272
20113
|
promptInteractions: lastStep.promptInteractions,
|
|
19273
20114
|
verification: lastStep.inspectionResults.length <= 1 ? lastStep.inspectionResults[0] ?? null : lastStep.inspectionResults,
|
|
20115
|
+
assertions: stepResults.flatMap((step) => step.assertions || []),
|
|
19274
20116
|
tokenUsage: aggregateConversationTokenUsage(conversation),
|
|
19275
20117
|
steps: stepResults,
|
|
19276
20118
|
turnDir: conversation.artifactDir
|
|
@@ -19292,6 +20134,14 @@ async function runAgentEvalSuite(options) {
|
|
|
19292
20134
|
conversation,
|
|
19293
20135
|
result
|
|
19294
20136
|
});
|
|
20137
|
+
await options.harness.emitProgress?.({
|
|
20138
|
+
phase: "scenario",
|
|
20139
|
+
status: "passed",
|
|
20140
|
+
scenarioId: scenario.id,
|
|
20141
|
+
title: "Scenario passed",
|
|
20142
|
+
message: `${result.assertions?.filter((assertion) => assertion.status === "passed").length || 0}/${result.assertions?.length || 0} assertions`,
|
|
20143
|
+
data: result
|
|
20144
|
+
});
|
|
19295
20145
|
finalResult = result;
|
|
19296
20146
|
} catch (error) {
|
|
19297
20147
|
const failureMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -19332,6 +20182,14 @@ async function runAgentEvalSuite(options) {
|
|
|
19332
20182
|
result: failed,
|
|
19333
20183
|
error: failureMessage
|
|
19334
20184
|
});
|
|
20185
|
+
await options.harness.emitProgress?.({
|
|
20186
|
+
phase: "scenario",
|
|
20187
|
+
status: "failed",
|
|
20188
|
+
scenarioId: scenario.id,
|
|
20189
|
+
title: "Scenario failed",
|
|
20190
|
+
message: failureMessage,
|
|
20191
|
+
data: failed
|
|
20192
|
+
});
|
|
19335
20193
|
finalResult = failed;
|
|
19336
20194
|
} finally {
|
|
19337
20195
|
await options.harness.closeConversation(conversation);
|
|
@@ -19380,6 +20238,11 @@ function createAgentEvalHarness(options) {
|
|
|
19380
20238
|
const chatTimeoutMs = options.chatTimeoutMs ?? 12e4;
|
|
19381
20239
|
const jobTimeoutMs = options.jobTimeoutMs ?? 9e4;
|
|
19382
20240
|
const pollIntervalMs = options.pollIntervalMs ?? 250;
|
|
20241
|
+
const resolvedTemplate = resolveHarnessTemplate(
|
|
20242
|
+
options.harnessTemplateId || process.env.GRANULAR_AGENT_HARNESS_TEMPLATE || "stable");
|
|
20243
|
+
const promptRenderer = options.promptRenderer || resolvedTemplate.renderPrompt;
|
|
20244
|
+
const continuationRenderer = options.continuationRenderer || resolvedTemplate.renderContinuation;
|
|
20245
|
+
const onProgress = options.onProgress;
|
|
19383
20246
|
async function openConversation(label) {
|
|
19384
20247
|
await ensureDir(artifactDir);
|
|
19385
20248
|
const clientId = `${slugify(label)}-${Date.now()}`;
|
|
@@ -19448,6 +20311,13 @@ function createAgentEvalHarness(options) {
|
|
|
19448
20311
|
};
|
|
19449
20312
|
}
|
|
19450
20313
|
async function runInspection(conversation, inspection, completed, turnDir) {
|
|
20314
|
+
await emitProgress(onProgress, {
|
|
20315
|
+
phase: "inspection",
|
|
20316
|
+
status: "running",
|
|
20317
|
+
scenarioId: conversation.label,
|
|
20318
|
+
title: "Running inspection",
|
|
20319
|
+
message: inspection.code.slice(0, 500)
|
|
20320
|
+
});
|
|
19451
20321
|
let result = null;
|
|
19452
20322
|
let lastError = null;
|
|
19453
20323
|
for (let attempt = 0; attempt < 10; attempt += 1) {
|
|
@@ -19475,6 +20345,14 @@ function createAgentEvalHarness(options) {
|
|
|
19475
20345
|
});
|
|
19476
20346
|
}
|
|
19477
20347
|
await writeJson(path.join(turnDir, "verification.json"), result);
|
|
20348
|
+
await emitProgress(onProgress, {
|
|
20349
|
+
phase: "inspection",
|
|
20350
|
+
status: "passed",
|
|
20351
|
+
scenarioId: conversation.label,
|
|
20352
|
+
title: "Inspection passed",
|
|
20353
|
+
message: JSON.stringify(result)?.slice(0, 800),
|
|
20354
|
+
data: result
|
|
20355
|
+
});
|
|
19478
20356
|
return result;
|
|
19479
20357
|
}
|
|
19480
20358
|
async function resumePendingTurn(pending, responder) {
|
|
@@ -19484,6 +20362,16 @@ function createAgentEvalHarness(options) {
|
|
|
19484
20362
|
prompt,
|
|
19485
20363
|
history: pending.promptInteractions
|
|
19486
20364
|
});
|
|
20365
|
+
await emitProgress(onProgress, {
|
|
20366
|
+
phase: "interaction",
|
|
20367
|
+
status: "running",
|
|
20368
|
+
scenarioId: pending.conversation.label,
|
|
20369
|
+
stepId: path.basename(pending.turnDir),
|
|
20370
|
+
jobId: pending.job.id,
|
|
20371
|
+
title: "Prompt answered",
|
|
20372
|
+
message: `${prompt.type}: ${prompt.message || prompt.title} -> ${JSON.stringify(answer)}`,
|
|
20373
|
+
data: { prompt, answer }
|
|
20374
|
+
});
|
|
19487
20375
|
const session = pending.conversation.environment;
|
|
19488
20376
|
await session.answerPrompt(prompt.id, answer);
|
|
19489
20377
|
pending.promptInteractions.push({
|
|
@@ -19503,7 +20391,12 @@ function createAgentEvalHarness(options) {
|
|
|
19503
20391
|
job: pending.job,
|
|
19504
20392
|
boundaryTimestamp: pending.boundaryTimestamp,
|
|
19505
20393
|
timeoutMs: jobTimeoutMs,
|
|
19506
|
-
pollIntervalMs
|
|
20394
|
+
pollIntervalMs,
|
|
20395
|
+
onProgress: (event) => void onProgress?.(event),
|
|
20396
|
+
progressContext: {
|
|
20397
|
+
scenarioId: pending.conversation.label,
|
|
20398
|
+
stepId: path.basename(pending.turnDir)
|
|
20399
|
+
}
|
|
19507
20400
|
});
|
|
19508
20401
|
if (resumed.kind === "prompt") {
|
|
19509
20402
|
return {
|
|
@@ -19584,12 +20477,44 @@ function createAgentEvalHarness(options) {
|
|
|
19584
20477
|
};
|
|
19585
20478
|
conversation.logTurns.push(turnLog);
|
|
19586
20479
|
if (input.prepareRecords?.length) {
|
|
20480
|
+
await emitProgress(onProgress, {
|
|
20481
|
+
phase: "setup",
|
|
20482
|
+
status: "running",
|
|
20483
|
+
scenarioId: conversation.label,
|
|
20484
|
+
stepId: turnId,
|
|
20485
|
+
title: "Recording setup records",
|
|
20486
|
+
message: `${input.prepareRecords.length} records`,
|
|
20487
|
+
data: input.prepareRecords
|
|
20488
|
+
});
|
|
19587
20489
|
await conversation.environment.recordObjects(input.prepareRecords);
|
|
20490
|
+
await emitProgress(onProgress, {
|
|
20491
|
+
phase: "setup",
|
|
20492
|
+
status: "passed",
|
|
20493
|
+
scenarioId: conversation.label,
|
|
20494
|
+
stepId: turnId,
|
|
20495
|
+
title: "Setup records recorded",
|
|
20496
|
+
message: `${input.prepareRecords.length} records`
|
|
20497
|
+
});
|
|
19588
20498
|
}
|
|
19589
20499
|
if (input.prepareTools?.length) {
|
|
20500
|
+
await emitProgress(onProgress, {
|
|
20501
|
+
phase: "setup",
|
|
20502
|
+
status: "running",
|
|
20503
|
+
scenarioId: conversation.label,
|
|
20504
|
+
stepId: turnId,
|
|
20505
|
+
title: "Registering effect handlers",
|
|
20506
|
+
message: `${input.prepareTools.length} handlers`
|
|
20507
|
+
});
|
|
19590
20508
|
await options.granular.ontology(conversation.environment.sandboxId).effects.registerMany(input.prepareTools);
|
|
19591
20509
|
}
|
|
19592
20510
|
if (input.prepare) {
|
|
20511
|
+
await emitProgress(onProgress, {
|
|
20512
|
+
phase: "setup",
|
|
20513
|
+
status: "running",
|
|
20514
|
+
scenarioId: conversation.label,
|
|
20515
|
+
stepId: turnId,
|
|
20516
|
+
title: "Running custom setup"
|
|
20517
|
+
});
|
|
19593
20518
|
await input.prepare({
|
|
19594
20519
|
conversation,
|
|
19595
20520
|
environment: conversation.environment,
|
|
@@ -19598,6 +20523,14 @@ function createAgentEvalHarness(options) {
|
|
|
19598
20523
|
}
|
|
19599
20524
|
const boundaryTimestamp = Date.now();
|
|
19600
20525
|
conversation.history.push({ role: "user", content: input.request });
|
|
20526
|
+
await emitProgress(onProgress, {
|
|
20527
|
+
phase: "step",
|
|
20528
|
+
status: "running",
|
|
20529
|
+
scenarioId: conversation.label,
|
|
20530
|
+
stepId: turnId,
|
|
20531
|
+
title: "User request",
|
|
20532
|
+
message: input.request
|
|
20533
|
+
});
|
|
19601
20534
|
await writeJson(path.join(turnDir, "request.json"), {
|
|
19602
20535
|
request: input.request,
|
|
19603
20536
|
boundaryTimestamp
|
|
@@ -19639,7 +20572,7 @@ function createAgentEvalHarness(options) {
|
|
|
19639
20572
|
inputSchema: tool.inputSchema,
|
|
19640
20573
|
outputSchema: tool.outputSchema
|
|
19641
20574
|
}));
|
|
19642
|
-
const
|
|
20575
|
+
const renderedPrompt = promptRenderer({
|
|
19643
20576
|
domainDocumentation: await conversation.environment.getDomainDocumentation(),
|
|
19644
20577
|
sessionContext: {
|
|
19645
20578
|
sandboxId: conversation.environment.sandboxId,
|
|
@@ -19660,9 +20593,39 @@ function createAgentEvalHarness(options) {
|
|
|
19660
20593
|
tools,
|
|
19661
20594
|
checkpoint: latestCheckpoint
|
|
19662
20595
|
});
|
|
19663
|
-
const
|
|
20596
|
+
const systemPrompt = renderedPrompt.prompt;
|
|
20597
|
+
await emitProgress(onProgress, {
|
|
20598
|
+
phase: "prompt",
|
|
20599
|
+
status: "passed",
|
|
20600
|
+
scenarioId: conversation.label,
|
|
20601
|
+
stepId: turnId,
|
|
20602
|
+
iteration: iteration + 1,
|
|
20603
|
+
templateId: renderedPrompt.templateId,
|
|
20604
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20605
|
+
title: "Rendered harness prompt",
|
|
20606
|
+
message: `${systemPrompt.split("\n").length} lines`,
|
|
20607
|
+
data: {
|
|
20608
|
+
templateId: renderedPrompt.templateId,
|
|
20609
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20610
|
+
templateHash: renderedPrompt.templateHash,
|
|
20611
|
+
promptInstanceHash: renderedPrompt.promptInstanceHash,
|
|
20612
|
+
prompt: systemPrompt
|
|
20613
|
+
}
|
|
20614
|
+
});
|
|
20615
|
+
const request = iteration === 0 ? input.request : continuationRenderer(
|
|
19664
20616
|
buildContinuationPreview(latestCheckpoint, noProgressCount)
|
|
19665
|
-
);
|
|
20617
|
+
).instruction;
|
|
20618
|
+
await emitProgress(onProgress, {
|
|
20619
|
+
phase: "generation",
|
|
20620
|
+
status: "running",
|
|
20621
|
+
scenarioId: conversation.label,
|
|
20622
|
+
stepId: turnId,
|
|
20623
|
+
iteration: iteration + 1,
|
|
20624
|
+
templateId: renderedPrompt.templateId,
|
|
20625
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20626
|
+
title: iteration === 0 ? "Generating agent response" : "Generating continuation",
|
|
20627
|
+
message: request
|
|
20628
|
+
});
|
|
19666
20629
|
const generation = await withTimeout2(
|
|
19667
20630
|
generateTurnWithRepair(options.generator, {
|
|
19668
20631
|
systemPrompt,
|
|
@@ -19681,10 +20644,31 @@ function createAgentEvalHarness(options) {
|
|
|
19681
20644
|
chatTimeoutMs,
|
|
19682
20645
|
`chat generation for ${conversation.label} iteration ${iteration + 1}`
|
|
19683
20646
|
);
|
|
20647
|
+
await emitProgress(onProgress, {
|
|
20648
|
+
phase: "generation",
|
|
20649
|
+
status: "passed",
|
|
20650
|
+
scenarioId: conversation.label,
|
|
20651
|
+
stepId: turnId,
|
|
20652
|
+
iteration: iteration + 1,
|
|
20653
|
+
templateId: renderedPrompt.templateId,
|
|
20654
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20655
|
+
title: generation.code ? "Generated job code" : "Generated text reply",
|
|
20656
|
+
message: generation.code || generation.reply || "",
|
|
20657
|
+
data: {
|
|
20658
|
+
reply: generation.reply,
|
|
20659
|
+
code: generation.code,
|
|
20660
|
+
attempts: generation.generationAttempts,
|
|
20661
|
+
usage: tokenUsageForGenerationOutput(generation)
|
|
20662
|
+
}
|
|
20663
|
+
});
|
|
19684
20664
|
const iterationLog = {
|
|
19685
20665
|
iteration: iteration + 1,
|
|
19686
20666
|
request,
|
|
19687
20667
|
systemPrompt,
|
|
20668
|
+
templateId: renderedPrompt.templateId,
|
|
20669
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20670
|
+
templateHash: renderedPrompt.templateHash,
|
|
20671
|
+
promptInstanceHash: renderedPrompt.promptInstanceHash,
|
|
19688
20672
|
generationReply: generation.reply,
|
|
19689
20673
|
generatedCode: generation.code,
|
|
19690
20674
|
rawGeneration: generation.raw,
|
|
@@ -19731,6 +20715,15 @@ function createAgentEvalHarness(options) {
|
|
|
19731
20715
|
result: completed.result
|
|
19732
20716
|
};
|
|
19733
20717
|
await writeJson(path.join(turnDir, "result.json"), completed);
|
|
20718
|
+
await emitProgress(onProgress, {
|
|
20719
|
+
phase: "step",
|
|
20720
|
+
status: "passed",
|
|
20721
|
+
scenarioId: conversation.label,
|
|
20722
|
+
stepId: turnId,
|
|
20723
|
+
title: "Step completed with text reply",
|
|
20724
|
+
message: responseText2,
|
|
20725
|
+
data: completed
|
|
20726
|
+
});
|
|
19734
20727
|
return completed;
|
|
19735
20728
|
}
|
|
19736
20729
|
const session = conversation.environment;
|
|
@@ -19751,12 +20744,33 @@ function createAgentEvalHarness(options) {
|
|
|
19751
20744
|
)
|
|
19752
20745
|
}
|
|
19753
20746
|
});
|
|
20747
|
+
await emitProgress(onProgress, {
|
|
20748
|
+
phase: "job",
|
|
20749
|
+
status: "running",
|
|
20750
|
+
scenarioId: conversation.label,
|
|
20751
|
+
stepId: turnId,
|
|
20752
|
+
iteration: iteration + 1,
|
|
20753
|
+
jobId: job.id,
|
|
20754
|
+
templateId: renderedPrompt.templateId,
|
|
20755
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20756
|
+
title: "Submitted Granular job",
|
|
20757
|
+
message: job.id,
|
|
20758
|
+
data: { code: generation.code }
|
|
20759
|
+
});
|
|
19754
20760
|
const outcome = await waitForJobOutcome({
|
|
19755
20761
|
environment: conversation.environment,
|
|
19756
20762
|
job,
|
|
19757
20763
|
boundaryTimestamp,
|
|
19758
20764
|
timeoutMs: jobTimeoutMs,
|
|
19759
|
-
pollIntervalMs
|
|
20765
|
+
pollIntervalMs,
|
|
20766
|
+
onProgress: (event) => void onProgress?.(event),
|
|
20767
|
+
progressContext: {
|
|
20768
|
+
scenarioId: conversation.label,
|
|
20769
|
+
stepId: turnId,
|
|
20770
|
+
iteration: iteration + 1,
|
|
20771
|
+
templateId: renderedPrompt.templateId,
|
|
20772
|
+
templateVersion: renderedPrompt.templateVersion
|
|
20773
|
+
}
|
|
19760
20774
|
});
|
|
19761
20775
|
if (outcome.kind === "prompt") {
|
|
19762
20776
|
if (!autoAnswerPrompts) {
|
|
@@ -19866,6 +20880,23 @@ function createAgentEvalHarness(options) {
|
|
|
19866
20880
|
controllerReason: continuation.reason,
|
|
19867
20881
|
noProgressCount: continuation.nextNoProgressCount
|
|
19868
20882
|
};
|
|
20883
|
+
await emitProgress(onProgress, {
|
|
20884
|
+
phase: "continuation",
|
|
20885
|
+
status: continuation.shouldContinue ? "running" : "passed",
|
|
20886
|
+
scenarioId: conversation.label,
|
|
20887
|
+
stepId: turnId,
|
|
20888
|
+
iteration: iteration + 1,
|
|
20889
|
+
jobId: job.id,
|
|
20890
|
+
templateId: renderedPrompt.templateId,
|
|
20891
|
+
templateVersion: renderedPrompt.templateVersion,
|
|
20892
|
+
title: continuation.shouldContinue ? "Harness requested another loop" : "Harness accepted completion",
|
|
20893
|
+
message: `${continuation.reason}; ${continuation.outcome}`,
|
|
20894
|
+
data: {
|
|
20895
|
+
continuation,
|
|
20896
|
+
checkpoint: latestCheckpoint,
|
|
20897
|
+
verifierSnapshot
|
|
20898
|
+
}
|
|
20899
|
+
});
|
|
19869
20900
|
previousSnapshot = verifierSnapshot;
|
|
19870
20901
|
noProgressCount = continuation.nextNoProgressCount;
|
|
19871
20902
|
conversation.history.push({
|
|
@@ -19919,6 +20950,17 @@ function createAgentEvalHarness(options) {
|
|
|
19919
20950
|
result: outcome.result
|
|
19920
20951
|
};
|
|
19921
20952
|
await writeJson(path.join(turnDir, "result.json"), completed);
|
|
20953
|
+
await emitProgress(onProgress, {
|
|
20954
|
+
phase: "step",
|
|
20955
|
+
status: "passed",
|
|
20956
|
+
scenarioId: conversation.label,
|
|
20957
|
+
stepId: turnId,
|
|
20958
|
+
iteration: iteration + 1,
|
|
20959
|
+
jobId: job.id,
|
|
20960
|
+
title: "Step completed",
|
|
20961
|
+
message: responseText,
|
|
20962
|
+
data: completed
|
|
20963
|
+
});
|
|
19922
20964
|
return completed;
|
|
19923
20965
|
}
|
|
19924
20966
|
iteration += 1;
|
|
@@ -19930,6 +20972,7 @@ function createAgentEvalHarness(options) {
|
|
|
19930
20972
|
return {
|
|
19931
20973
|
artifactDir,
|
|
19932
20974
|
granular: options.granular,
|
|
20975
|
+
emitProgress: (event) => emitProgress(onProgress, event),
|
|
19933
20976
|
openConversation,
|
|
19934
20977
|
closeConversation,
|
|
19935
20978
|
runTurn,
|
|
@@ -19995,7 +21038,11 @@ function createAgentTester(options) {
|
|
|
19995
21038
|
controllerBudgets: options.controllerBudgets,
|
|
19996
21039
|
chatTimeoutMs: options.chatTimeoutMs,
|
|
19997
21040
|
jobTimeoutMs: options.jobTimeoutMs,
|
|
19998
|
-
pollIntervalMs: options.pollIntervalMs
|
|
21041
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
21042
|
+
harnessTemplateId: options.harnessTemplateId,
|
|
21043
|
+
promptRenderer: options.promptRenderer,
|
|
21044
|
+
continuationRenderer: options.continuationRenderer,
|
|
21045
|
+
onProgress: options.onProgress
|
|
19999
21046
|
});
|
|
20000
21047
|
return {
|
|
20001
21048
|
...harness,
|
|
@@ -20021,6 +21068,6 @@ var createHumanResponder = createScriptedPromptResponder;
|
|
|
20021
21068
|
var createOpenAIGenerator = createOpenAIChatTurnGenerator;
|
|
20022
21069
|
var createTestArtifactsDirectory = createTimestampedArtifactDirectory;
|
|
20023
21070
|
|
|
20024
|
-
export { createAgentEvalHarness, createAgentTester, createHumanResponder, createOpenAIChatTurnGenerator, createOpenAIGenerator, createScriptedPromptResponder, createTestArtifactsDirectory, createTimestampedArtifactDirectory, generateTurnWithRepair, runAgentEvalSuite, runAgentTests };
|
|
21071
|
+
export { createAgentEvalHarness, createAgentTester, createHumanResponder, createOpenAIChatTurnGenerator, createOpenAIGenerator, createScriptedPromptResponder, createTestArtifactsDirectory, createTimestampedArtifactDirectory, generateTurnWithRepair, loadAgentEvalScenarioFile, runAgentEvalSuite, runAgentTests, scenariosFromAgentEvalFile };
|
|
20025
21072
|
//# sourceMappingURL=agent-evals.mjs.map
|
|
20026
21073
|
//# sourceMappingURL=agent-evals.mjs.map
|