@granular-software/sdk 0.4.41 → 0.4.43

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.
@@ -188,9 +188,9 @@ var require_node_gyp_build = __commonJS({
188
188
  }
189
189
  function parseTags(file) {
190
190
  var arr = file.split(".");
191
- var extension = arr.pop();
191
+ var extension2 = arr.pop();
192
192
  var tags = { file, specificity: 0 };
193
- if (extension !== "node") return;
193
+ if (extension2 !== "node") return;
194
194
  for (var i = 0; i < arr.length; i++) {
195
195
  var tag = arr[i];
196
196
  if (tag === "node" || tag === "electron" || tag === "node-webkit") {
@@ -438,7 +438,7 @@ var require_permessage_deflate = __commonJS({
438
438
  var kBuffers = Symbol("buffers");
439
439
  var kError = Symbol("error");
440
440
  var zlibLimiter;
441
- var PerMessageDeflate = class {
441
+ var PerMessageDeflate2 = class {
442
442
  /**
443
443
  * Creates a PerMessageDeflate instance.
444
444
  *
@@ -449,6 +449,9 @@ var require_permessage_deflate = __commonJS({
449
449
  * acknowledge disabling of client context takeover
450
450
  * @param {Number} [options.concurrencyLimit=10] The number of concurrent
451
451
  * calls to zlib
452
+ * @param {Boolean} [options.isServer=false] Create the instance in either
453
+ * server or client mode
454
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
452
455
  * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
453
456
  * use of a custom server window size
454
457
  * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
@@ -459,15 +462,12 @@ var require_permessage_deflate = __commonJS({
459
462
  * deflate
460
463
  * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
461
464
  * inflate
462
- * @param {Boolean} [isServer=false] Create the instance in either server or
463
- * client mode
464
- * @param {Number} [maxPayload=0] The maximum allowed message length
465
465
  */
466
- constructor(options, isServer, maxPayload) {
467
- this._maxPayload = maxPayload | 0;
466
+ constructor(options) {
468
467
  this._options = options || {};
469
468
  this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
470
- this._isServer = !!isServer;
469
+ this._maxPayload = this._options.maxPayload | 0;
470
+ this._isServer = !!this._options.isServer;
471
471
  this._deflate = null;
472
472
  this._inflate = null;
473
473
  this.params = null;
@@ -776,7 +776,7 @@ var require_permessage_deflate = __commonJS({
776
776
  });
777
777
  }
778
778
  };
779
- module.exports = PerMessageDeflate;
779
+ module.exports = PerMessageDeflate2;
780
780
  function deflateOnData(chunk) {
781
781
  this[kBuffers].push(chunk);
782
782
  this[kTotalLength] += chunk.length;
@@ -1056,7 +1056,7 @@ var require_validation = __commonJS({
1056
1056
  var require_receiver = __commonJS({
1057
1057
  "../../node_modules/ws/lib/receiver.js"(exports, module) {
1058
1058
  var { Writable } = __require("stream");
1059
- var PerMessageDeflate = require_permessage_deflate();
1059
+ var PerMessageDeflate2 = require_permessage_deflate();
1060
1060
  var {
1061
1061
  BINARY_TYPES,
1062
1062
  EMPTY_BUFFER,
@@ -1086,6 +1086,10 @@ var require_receiver = __commonJS({
1086
1086
  * extensions
1087
1087
  * @param {Boolean} [options.isServer=false] Specifies whether to operate in
1088
1088
  * client or server mode
1089
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
1090
+ * buffered data chunks
1091
+ * @param {Number} [options.maxFragments=0] The maximum number of message
1092
+ * fragments
1089
1093
  * @param {Number} [options.maxPayload=0] The maximum allowed message length
1090
1094
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
1091
1095
  * not to skip UTF-8 validation for text and close messages
@@ -1096,6 +1100,8 @@ var require_receiver = __commonJS({
1096
1100
  this._binaryType = options.binaryType || BINARY_TYPES[0];
1097
1101
  this._extensions = options.extensions || {};
1098
1102
  this._isServer = !!options.isServer;
1103
+ this._maxBufferedChunks = options.maxBufferedChunks | 0;
1104
+ this._maxFragments = options.maxFragments | 0;
1099
1105
  this._maxPayload = options.maxPayload | 0;
1100
1106
  this._skipUTF8Validation = !!options.skipUTF8Validation;
1101
1107
  this[kWebSocket] = void 0;
@@ -1125,6 +1131,18 @@ var require_receiver = __commonJS({
1125
1131
  */
1126
1132
  _write(chunk, encoding, cb) {
1127
1133
  if (this._opcode === 8 && this._state == GET_INFO) return cb();
1134
+ if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
1135
+ cb(
1136
+ this.createError(
1137
+ RangeError,
1138
+ "Too many buffered chunks",
1139
+ false,
1140
+ 1008,
1141
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1142
+ )
1143
+ );
1144
+ return;
1145
+ }
1128
1146
  this._bufferedBytes += chunk.length;
1129
1147
  this._buffers.push(chunk);
1130
1148
  this.startLoop(cb);
@@ -1223,7 +1241,7 @@ var require_receiver = __commonJS({
1223
1241
  return;
1224
1242
  }
1225
1243
  const compressed = (buf[0] & 64) === 64;
1226
- if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
1244
+ if (compressed && !this._extensions[PerMessageDeflate2.extensionName]) {
1227
1245
  const error = this.createError(
1228
1246
  RangeError,
1229
1247
  "RSV1 must be clear",
@@ -1454,6 +1472,17 @@ var require_receiver = __commonJS({
1454
1472
  return;
1455
1473
  }
1456
1474
  if (data.length) {
1475
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
1476
+ const error = this.createError(
1477
+ RangeError,
1478
+ "Too many message fragments",
1479
+ false,
1480
+ 1008,
1481
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1482
+ );
1483
+ cb(error);
1484
+ return;
1485
+ }
1457
1486
  this._messageLength = this._totalPayloadLength;
1458
1487
  this._fragments.push(data);
1459
1488
  }
@@ -1467,7 +1496,7 @@ var require_receiver = __commonJS({
1467
1496
  * @private
1468
1497
  */
1469
1498
  decompress(data, cb) {
1470
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1499
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1471
1500
  perMessageDeflate.decompress(data, this._fin, (err, buf) => {
1472
1501
  if (err) return cb(err);
1473
1502
  if (buf.length) {
@@ -1483,6 +1512,17 @@ var require_receiver = __commonJS({
1483
1512
  cb(error);
1484
1513
  return;
1485
1514
  }
1515
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
1516
+ const error = this.createError(
1517
+ RangeError,
1518
+ "Too many message fragments",
1519
+ false,
1520
+ 1008,
1521
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1522
+ );
1523
+ cb(error);
1524
+ return;
1525
+ }
1486
1526
  this._fragments.push(buf);
1487
1527
  }
1488
1528
  this.dataMessage(cb);
@@ -1648,7 +1688,10 @@ var require_sender = __commonJS({
1648
1688
  "../../node_modules/ws/lib/sender.js"(exports, module) {
1649
1689
  var { Duplex } = __require("stream");
1650
1690
  var { randomFillSync } = __require("crypto");
1651
- var PerMessageDeflate = require_permessage_deflate();
1691
+ var {
1692
+ types: { isUint8Array }
1693
+ } = __require("util");
1694
+ var PerMessageDeflate2 = require_permessage_deflate();
1652
1695
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
1653
1696
  var { isBlob, isValidStatusCode } = require_validation();
1654
1697
  var { mask: applyMask, toBuffer } = require_buffer_util();
@@ -1801,8 +1844,10 @@ var require_sender = __commonJS({
1801
1844
  buf.writeUInt16BE(code, 0);
1802
1845
  if (typeof data === "string") {
1803
1846
  buf.write(data, 2);
1804
- } else {
1847
+ } else if (isUint8Array(data)) {
1805
1848
  buf.set(data, 2);
1849
+ } else {
1850
+ throw new TypeError("Second argument must be a string or a Uint8Array");
1806
1851
  }
1807
1852
  }
1808
1853
  const options = {
@@ -1932,7 +1977,7 @@ var require_sender = __commonJS({
1932
1977
  * @public
1933
1978
  */
1934
1979
  send(data, options, cb) {
1935
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1980
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1936
1981
  let opcode = options.binary ? 2 : 1;
1937
1982
  let rsv1 = options.compress;
1938
1983
  let byteLength;
@@ -2056,7 +2101,7 @@ var require_sender = __commonJS({
2056
2101
  this.sendFrame(_Sender.frame(data, options), cb);
2057
2102
  return;
2058
2103
  }
2059
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
2104
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
2060
2105
  this._bufferedBytes += options[kByteLength];
2061
2106
  this._state = DEFLATING;
2062
2107
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
@@ -2492,11 +2537,11 @@ var require_extension = __commonJS({
2492
2537
  return offers;
2493
2538
  }
2494
2539
  function format(extensions) {
2495
- return Object.keys(extensions).map((extension) => {
2496
- let configurations = extensions[extension];
2540
+ return Object.keys(extensions).map((extension2) => {
2541
+ let configurations = extensions[extension2];
2497
2542
  if (!Array.isArray(configurations)) configurations = [configurations];
2498
2543
  return configurations.map((params) => {
2499
- return [extension].concat(
2544
+ return [extension2].concat(
2500
2545
  Object.keys(params).map((k) => {
2501
2546
  let values = params[k];
2502
2547
  if (!Array.isArray(values)) values = [values];
@@ -2521,7 +2566,7 @@ var require_websocket = __commonJS({
2521
2566
  var { randomBytes, createHash } = __require("crypto");
2522
2567
  var { Duplex, Readable } = __require("stream");
2523
2568
  var { URL: URL2 } = __require("url");
2524
- var PerMessageDeflate = require_permessage_deflate();
2569
+ var PerMessageDeflate2 = require_permessage_deflate();
2525
2570
  var Receiver2 = require_receiver();
2526
2571
  var Sender2 = require_sender();
2527
2572
  var { isBlob } = require_validation();
@@ -2680,6 +2725,10 @@ var require_websocket = __commonJS({
2680
2725
  * multiple times in the same tick
2681
2726
  * @param {Function} [options.generateMask] The function used to generate the
2682
2727
  * masking key
2728
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
2729
+ * buffered data chunks
2730
+ * @param {Number} [options.maxFragments=0] The maximum number of message
2731
+ * fragments
2683
2732
  * @param {Number} [options.maxPayload=0] The maximum allowed message size
2684
2733
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2685
2734
  * not to skip UTF-8 validation for text and close messages
@@ -2691,6 +2740,8 @@ var require_websocket = __commonJS({
2691
2740
  binaryType: this.binaryType,
2692
2741
  extensions: this._extensions,
2693
2742
  isServer: this._isServer,
2743
+ maxBufferedChunks: options.maxBufferedChunks,
2744
+ maxFragments: options.maxFragments,
2694
2745
  maxPayload: options.maxPayload,
2695
2746
  skipUTF8Validation: options.skipUTF8Validation
2696
2747
  });
@@ -2729,8 +2780,8 @@ var require_websocket = __commonJS({
2729
2780
  this.emit("close", this._closeCode, this._closeMessage);
2730
2781
  return;
2731
2782
  }
2732
- if (this._extensions[PerMessageDeflate.extensionName]) {
2733
- this._extensions[PerMessageDeflate.extensionName].cleanup();
2783
+ if (this._extensions[PerMessageDeflate2.extensionName]) {
2784
+ this._extensions[PerMessageDeflate2.extensionName].cleanup();
2734
2785
  }
2735
2786
  this._receiver.removeAllListeners();
2736
2787
  this._readyState = _WebSocket.CLOSED;
@@ -2892,7 +2943,7 @@ var require_websocket = __commonJS({
2892
2943
  fin: true,
2893
2944
  ...options
2894
2945
  };
2895
- if (!this._extensions[PerMessageDeflate.extensionName]) {
2946
+ if (!this._extensions[PerMessageDeflate2.extensionName]) {
2896
2947
  opts.compress = false;
2897
2948
  }
2898
2949
  this._sender.send(data || EMPTY_BUFFER, opts, cb);
@@ -2990,6 +3041,8 @@ var require_websocket = __commonJS({
2990
3041
  autoPong: true,
2991
3042
  closeTimeout: CLOSE_TIMEOUT,
2992
3043
  protocolVersion: protocolVersions[1],
3044
+ maxBufferedChunks: 1024 * 1024,
3045
+ maxFragments: 128 * 1024,
2993
3046
  maxPayload: 100 * 1024 * 1024,
2994
3047
  skipUTF8Validation: false,
2995
3048
  perMessageDeflate: true,
@@ -3018,7 +3071,7 @@ var require_websocket = __commonJS({
3018
3071
  } else {
3019
3072
  try {
3020
3073
  parsedUrl = new URL2(address);
3021
- } catch (e) {
3074
+ } catch {
3022
3075
  throw new SyntaxError(`Invalid URL: ${address}`);
3023
3076
  }
3024
3077
  }
@@ -3066,13 +3119,13 @@ var require_websocket = __commonJS({
3066
3119
  opts.path = parsedUrl.pathname + parsedUrl.search;
3067
3120
  opts.timeout = opts.handshakeTimeout;
3068
3121
  if (opts.perMessageDeflate) {
3069
- perMessageDeflate = new PerMessageDeflate(
3070
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
3071
- false,
3072
- opts.maxPayload
3073
- );
3122
+ perMessageDeflate = new PerMessageDeflate2({
3123
+ ...opts.perMessageDeflate,
3124
+ isServer: false,
3125
+ maxPayload: opts.maxPayload
3126
+ });
3074
3127
  opts.headers["Sec-WebSocket-Extensions"] = format({
3075
- [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
3128
+ [PerMessageDeflate2.extensionName]: perMessageDeflate.offer()
3076
3129
  });
3077
3130
  }
3078
3131
  if (protocols.length) {
@@ -3215,23 +3268,25 @@ var require_websocket = __commonJS({
3215
3268
  return;
3216
3269
  }
3217
3270
  const extensionNames = Object.keys(extensions);
3218
- if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
3271
+ if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate2.extensionName) {
3219
3272
  const message = "Server indicated an extension that was not requested";
3220
3273
  abortHandshake(websocket, socket, message);
3221
3274
  return;
3222
3275
  }
3223
3276
  try {
3224
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
3277
+ perMessageDeflate.accept(extensions[PerMessageDeflate2.extensionName]);
3225
3278
  } catch (err) {
3226
3279
  const message = "Invalid Sec-WebSocket-Extensions header";
3227
3280
  abortHandshake(websocket, socket, message);
3228
3281
  return;
3229
3282
  }
3230
- websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3283
+ websocket._extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3231
3284
  }
3232
3285
  websocket.setSocket(socket, head, {
3233
3286
  allowSynchronousEvents: opts.allowSynchronousEvents,
3234
3287
  generateMask: opts.generateMask,
3288
+ maxBufferedChunks: opts.maxBufferedChunks,
3289
+ maxFragments: opts.maxFragments,
3235
3290
  maxPayload: opts.maxPayload,
3236
3291
  skipUTF8Validation: opts.skipUTF8Validation
3237
3292
  });
@@ -3543,9 +3598,9 @@ var require_websocket_server = __commonJS({
3543
3598
  var http = __require("http");
3544
3599
  var { Duplex } = __require("stream");
3545
3600
  var { createHash } = __require("crypto");
3546
- var extension = require_extension();
3547
- var PerMessageDeflate = require_permessage_deflate();
3548
- var subprotocol = require_subprotocol();
3601
+ var extension2 = require_extension();
3602
+ var PerMessageDeflate2 = require_permessage_deflate();
3603
+ var subprotocol2 = require_subprotocol();
3549
3604
  var WebSocket2 = require_websocket();
3550
3605
  var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
3551
3606
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
@@ -3571,6 +3626,10 @@ var require_websocket_server = __commonJS({
3571
3626
  * called
3572
3627
  * @param {Function} [options.handleProtocols] A hook to handle protocols
3573
3628
  * @param {String} [options.host] The hostname where to bind the server
3629
+ * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
3630
+ * buffered data chunks
3631
+ * @param {Number} [options.maxFragments=131072] The maximum number of message
3632
+ * fragments
3574
3633
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3575
3634
  * size
3576
3635
  * @param {Boolean} [options.noServer=false] Enable no server mode
@@ -3592,6 +3651,8 @@ var require_websocket_server = __commonJS({
3592
3651
  options = {
3593
3652
  allowSynchronousEvents: true,
3594
3653
  autoPong: true,
3654
+ maxBufferedChunks: 1024 * 1024,
3655
+ maxFragments: 128 * 1024,
3595
3656
  maxPayload: 100 * 1024 * 1024,
3596
3657
  skipUTF8Validation: false,
3597
3658
  perMessageDeflate: false,
@@ -3768,7 +3829,7 @@ var require_websocket_server = __commonJS({
3768
3829
  let protocols = /* @__PURE__ */ new Set();
3769
3830
  if (secWebSocketProtocol !== void 0) {
3770
3831
  try {
3771
- protocols = subprotocol.parse(secWebSocketProtocol);
3832
+ protocols = subprotocol2.parse(secWebSocketProtocol);
3772
3833
  } catch (err) {
3773
3834
  const message = "Invalid Sec-WebSocket-Protocol header";
3774
3835
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
@@ -3778,16 +3839,16 @@ var require_websocket_server = __commonJS({
3778
3839
  const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
3779
3840
  const extensions = {};
3780
3841
  if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
3781
- const perMessageDeflate = new PerMessageDeflate(
3782
- this.options.perMessageDeflate,
3783
- true,
3784
- this.options.maxPayload
3785
- );
3842
+ const perMessageDeflate = new PerMessageDeflate2({
3843
+ ...this.options.perMessageDeflate,
3844
+ isServer: true,
3845
+ maxPayload: this.options.maxPayload
3846
+ });
3786
3847
  try {
3787
- const offers = extension.parse(secWebSocketExtensions);
3788
- if (offers[PerMessageDeflate.extensionName]) {
3789
- perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
3790
- extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3848
+ const offers = extension2.parse(secWebSocketExtensions);
3849
+ if (offers[PerMessageDeflate2.extensionName]) {
3850
+ perMessageDeflate.accept(offers[PerMessageDeflate2.extensionName]);
3851
+ extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3791
3852
  }
3792
3853
  } catch (err) {
3793
3854
  const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
@@ -3858,10 +3919,10 @@ var require_websocket_server = __commonJS({
3858
3919
  ws._protocol = protocol;
3859
3920
  }
3860
3921
  }
3861
- if (extensions[PerMessageDeflate.extensionName]) {
3862
- const params = extensions[PerMessageDeflate.extensionName].params;
3863
- const value = extension.format({
3864
- [PerMessageDeflate.extensionName]: [params]
3922
+ if (extensions[PerMessageDeflate2.extensionName]) {
3923
+ const params = extensions[PerMessageDeflate2.extensionName].params;
3924
+ const value = extension2.format({
3925
+ [PerMessageDeflate2.extensionName]: [params]
3865
3926
  });
3866
3927
  headers.push(`Sec-WebSocket-Extensions: ${value}`);
3867
3928
  ws._extensions = extensions;
@@ -3871,6 +3932,8 @@ var require_websocket_server = __commonJS({
3871
3932
  socket.removeListener("error", socketOnError);
3872
3933
  ws.setSocket(socket, head, {
3873
3934
  allowSynchronousEvents: this.options.allowSynchronousEvents,
3935
+ maxBufferedChunks: this.options.maxBufferedChunks,
3936
+ maxFragments: this.options.maxFragments,
3874
3937
  maxPayload: this.options.maxPayload,
3875
3938
  skipUTF8Validation: this.options.skipUTF8Validation
3876
3939
  });
@@ -3931,19 +3994,25 @@ var require_websocket_server = __commonJS({
3931
3994
  // ../../node_modules/ws/wrapper.mjs
3932
3995
  var wrapper_exports = {};
3933
3996
  __export(wrapper_exports, {
3997
+ PerMessageDeflate: () => import_permessage_deflate.default,
3934
3998
  Receiver: () => import_receiver.default,
3935
3999
  Sender: () => import_sender.default,
3936
4000
  WebSocket: () => import_websocket.default,
3937
4001
  WebSocketServer: () => import_websocket_server.default,
3938
4002
  createWebSocketStream: () => import_stream.default,
3939
- default: () => wrapper_default
4003
+ default: () => wrapper_default,
4004
+ extension: () => import_extension.default,
4005
+ subprotocol: () => import_subprotocol.default
3940
4006
  });
3941
- var import_stream, import_receiver, import_sender, import_websocket, import_websocket_server, wrapper_default;
4007
+ var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
3942
4008
  var init_wrapper = __esm({
3943
4009
  "../../node_modules/ws/wrapper.mjs"() {
3944
4010
  import_stream = __toESM(require_stream());
4011
+ import_extension = __toESM(require_extension());
4012
+ import_permessage_deflate = __toESM(require_permessage_deflate());
3945
4013
  import_receiver = __toESM(require_receiver());
3946
4014
  import_sender = __toESM(require_sender());
4015
+ import_subprotocol = __toESM(require_subprotocol());
3947
4016
  import_websocket = __toESM(require_websocket());
3948
4017
  import_websocket_server = __toESM(require_websocket_server());
3949
4018
  wrapper_default = import_websocket.default;
@@ -16086,7 +16155,275 @@ var Granular = class _Granular {
16086
16155
  }
16087
16156
  };
16088
16157
 
16158
+ // src/agent-harness-templates/action-presentation/0.1.0/manifest.json
16159
+ var manifest_default = {
16160
+ id: "action-presentation",
16161
+ version: "0.1.0",
16162
+ status: "candidate",
16163
+ owner: "granular",
16164
+ createdAt: "2026-05-25T00:00:00.000Z",
16165
+ 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.",
16166
+ promptBuilder: "buildGranularAgentSystemPrompt:action-presentation",
16167
+ continuationBuilder: "buildContinuationInstruction:action-presentation",
16168
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16169
+ codeReviewPolicy: "reviewGeneratedJobCode",
16170
+ defaultModel: "gpt-5.4",
16171
+ modelMatrix: ["gpt-5.4"],
16172
+ temperature: 0,
16173
+ compatibility: {
16174
+ minSdkVersion: "0.4.40",
16175
+ capabilities: [
16176
+ "executeCode",
16177
+ "readEntities",
16178
+ "workflowHelpers",
16179
+ "savedData",
16180
+ "showRecords"
16181
+ ]
16182
+ },
16183
+ evalGates: {
16184
+ requiredSuites: [
16185
+ "agent-harness-hardening",
16186
+ "agent-harness-runtime-e2e",
16187
+ "agent-production-readiness-e2e"
16188
+ ],
16189
+ criticalBuckets: [
16190
+ "relationship-traversal",
16191
+ "cross-turn-reference",
16192
+ "ambiguous-target-choice",
16193
+ "confirmation-gated-mutation",
16194
+ "denied-action-refusal",
16195
+ "mutation-presentation"
16196
+ ],
16197
+ maxRegressionPct: 0,
16198
+ minPassK: {
16199
+ "critical-mutation": 0.9,
16200
+ "permission-boundary": 1
16201
+ }
16202
+ }
16203
+ };
16204
+
16205
+ // src/agent-harness-templates/experimental-compact/0.1.0/manifest.json
16206
+ var manifest_default2 = {
16207
+ id: "experimental-compact",
16208
+ version: "0.1.0",
16209
+ status: "candidate",
16210
+ owner: "granular",
16211
+ createdAt: "2026-05-24T00:00:00.000Z",
16212
+ changelog: "Candidate harness template used for champion/challenger testing. It adds a compact decision discipline section on top of the stable renderer.",
16213
+ promptBuilder: "buildGranularAgentSystemPrompt:experimental-compact",
16214
+ continuationBuilder: "buildContinuationInstruction:experimental-compact",
16215
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16216
+ codeReviewPolicy: "reviewGeneratedJobCode",
16217
+ defaultModel: "gpt-5.4",
16218
+ modelMatrix: ["gpt-5.4"],
16219
+ temperature: 0,
16220
+ compatibility: {
16221
+ minSdkVersion: "0.4.40",
16222
+ capabilities: [
16223
+ "executeCode",
16224
+ "readEntities",
16225
+ "workflowHelpers",
16226
+ "savedData",
16227
+ "showRecords"
16228
+ ]
16229
+ },
16230
+ evalGates: {
16231
+ requiredSuites: [
16232
+ "agent-harness-hardening",
16233
+ "agent-harness-runtime-e2e",
16234
+ "agent-production-readiness-e2e"
16235
+ ],
16236
+ criticalBuckets: [
16237
+ "relationship-traversal",
16238
+ "cross-turn-reference",
16239
+ "ambiguous-target-choice",
16240
+ "confirmation-gated-mutation",
16241
+ "denied-action-refusal",
16242
+ "mutation-presentation"
16243
+ ],
16244
+ maxRegressionPct: 0,
16245
+ minPassK: {
16246
+ "critical-mutation": 0.9,
16247
+ "permission-boundary": 1
16248
+ }
16249
+ }
16250
+ };
16251
+
16252
+ // src/agent-harness-templates/stable/1.0.0/manifest.json
16253
+ var manifest_default3 = {
16254
+ id: "stable",
16255
+ version: "1.0.0",
16256
+ status: "stable",
16257
+ owner: "granular",
16258
+ createdAt: "2026-05-24T00:00:00.000Z",
16259
+ changelog: "Baseline template wrapping the existing Granular agent harness prompt, continuation instruction, output contract, and generated-job review policy.",
16260
+ promptBuilder: "buildGranularAgentSystemPrompt",
16261
+ continuationBuilder: "buildContinuationInstruction",
16262
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16263
+ codeReviewPolicy: "reviewGeneratedJobCode",
16264
+ defaultModel: "gpt-5.4",
16265
+ modelMatrix: ["gpt-5.4"],
16266
+ temperature: 0,
16267
+ compatibility: {
16268
+ minSdkVersion: "0.4.40",
16269
+ capabilities: [
16270
+ "executeCode",
16271
+ "readEntities",
16272
+ "workflowHelpers",
16273
+ "savedData",
16274
+ "showRecords"
16275
+ ]
16276
+ },
16277
+ evalGates: {
16278
+ requiredSuites: [
16279
+ "agent-harness-hardening",
16280
+ "agent-harness-runtime-e2e",
16281
+ "agent-production-readiness-e2e"
16282
+ ],
16283
+ criticalBuckets: [
16284
+ "relationship-traversal",
16285
+ "cross-turn-reference",
16286
+ "ambiguous-target-choice",
16287
+ "confirmation-gated-mutation",
16288
+ "denied-action-refusal",
16289
+ "mutation-presentation"
16290
+ ],
16291
+ maxRegressionPct: 0,
16292
+ minPassK: {
16293
+ "critical-mutation": 0.9,
16294
+ "permission-boundary": 1
16295
+ }
16296
+ }
16297
+ };
16298
+
16089
16299
  // src/agent-harness.ts
16300
+ var HARNESS_TEMPLATE_STATUSES = [
16301
+ "draft",
16302
+ "candidate",
16303
+ "release-candidate",
16304
+ "stable",
16305
+ "deprecated"
16306
+ ];
16307
+ function requireRecord(value, context) {
16308
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16309
+ throw new Error(`${context} must be an object.`);
16310
+ }
16311
+ return value;
16312
+ }
16313
+ function requiredString(record, key, context) {
16314
+ const value = record[key];
16315
+ if (typeof value !== "string" || !value.trim()) {
16316
+ throw new Error(`${context}.${key} must be a non-empty string.`);
16317
+ }
16318
+ return value;
16319
+ }
16320
+ function requiredNumber(record, key, context) {
16321
+ const value = record[key];
16322
+ if (typeof value !== "number" || !Number.isFinite(value)) {
16323
+ throw new Error(`${context}.${key} must be a finite number.`);
16324
+ }
16325
+ return value;
16326
+ }
16327
+ function requiredStringArray(record, key, context) {
16328
+ const value = record[key];
16329
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
16330
+ throw new Error(`${context}.${key} must be an array of non-empty strings.`);
16331
+ }
16332
+ return [...value];
16333
+ }
16334
+ function requiredNumberRecord(record, key, context) {
16335
+ const value = requireRecord(record[key], `${context}.${key}`);
16336
+ const output = {};
16337
+ for (const [entryKey, entryValue] of Object.entries(value)) {
16338
+ if (typeof entryValue !== "number" || !Number.isFinite(entryValue) || entryValue < 0 || entryValue > 1) {
16339
+ throw new Error(
16340
+ `${context}.${key}.${entryKey} must be a number between 0 and 1.`
16341
+ );
16342
+ }
16343
+ output[entryKey] = entryValue;
16344
+ }
16345
+ return output;
16346
+ }
16347
+ function validateHarnessTemplateManifest(value, context = "HarnessTemplateManifest") {
16348
+ const record = requireRecord(value, context);
16349
+ const status = requiredString(record, "status", context);
16350
+ if (!HARNESS_TEMPLATE_STATUSES.includes(status)) {
16351
+ throw new Error(
16352
+ `${context}.status must be one of ${HARNESS_TEMPLATE_STATUSES.join(", ")}.`
16353
+ );
16354
+ }
16355
+ const createdAt = requiredString(record, "createdAt", context);
16356
+ if (Number.isNaN(Date.parse(createdAt))) {
16357
+ throw new Error(`${context}.createdAt must be an ISO timestamp.`);
16358
+ }
16359
+ const compatibility = requireRecord(
16360
+ record.compatibility,
16361
+ `${context}.compatibility`
16362
+ );
16363
+ const evalGates = requireRecord(record.evalGates, `${context}.evalGates`);
16364
+ const maxRegressionPct = requiredNumber(
16365
+ evalGates,
16366
+ "maxRegressionPct",
16367
+ `${context}.evalGates`
16368
+ );
16369
+ if (maxRegressionPct < 0 || maxRegressionPct > 1) {
16370
+ throw new Error(
16371
+ `${context}.evalGates.maxRegressionPct must be between 0 and 1.`
16372
+ );
16373
+ }
16374
+ return {
16375
+ id: requiredString(record, "id", context),
16376
+ version: requiredString(record, "version", context),
16377
+ status,
16378
+ owner: requiredString(record, "owner", context),
16379
+ createdAt,
16380
+ changelog: requiredString(record, "changelog", context),
16381
+ promptBuilder: requiredString(record, "promptBuilder", context),
16382
+ continuationBuilder: requiredString(record, "continuationBuilder", context),
16383
+ modelOutputInstruction: requiredString(
16384
+ record,
16385
+ "modelOutputInstruction",
16386
+ context
16387
+ ),
16388
+ codeReviewPolicy: requiredString(record, "codeReviewPolicy", context),
16389
+ defaultModel: requiredString(record, "defaultModel", context),
16390
+ modelMatrix: requiredStringArray(record, "modelMatrix", context),
16391
+ temperature: requiredNumber(record, "temperature", context),
16392
+ compatibility: {
16393
+ minSdkVersion: requiredString(
16394
+ compatibility,
16395
+ "minSdkVersion",
16396
+ `${context}.compatibility`
16397
+ ),
16398
+ capabilities: requiredStringArray(
16399
+ compatibility,
16400
+ "capabilities",
16401
+ `${context}.compatibility`
16402
+ )
16403
+ },
16404
+ evalGates: {
16405
+ requiredSuites: requiredStringArray(
16406
+ evalGates,
16407
+ "requiredSuites",
16408
+ `${context}.evalGates`
16409
+ ),
16410
+ criticalBuckets: requiredStringArray(
16411
+ evalGates,
16412
+ "criticalBuckets",
16413
+ `${context}.evalGates`
16414
+ ),
16415
+ maxRegressionPct,
16416
+ minPassK: requiredNumberRecord(
16417
+ evalGates,
16418
+ "minPassK",
16419
+ `${context}.evalGates`
16420
+ )
16421
+ }
16422
+ };
16423
+ }
16424
+ function defineHarnessTemplateManifest(value, context) {
16425
+ return validateHarnessTemplateManifest(value, context);
16426
+ }
16090
16427
  function asRecord4(value) {
16091
16428
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
16092
16429
  return value;
@@ -16110,6 +16447,16 @@ function uniqueStrings(values, maxCount) {
16110
16447
  }
16111
16448
  return output;
16112
16449
  }
16450
+ function stableStringify(value) {
16451
+ if (value === null || typeof value !== "object") {
16452
+ return JSON.stringify(value);
16453
+ }
16454
+ if (Array.isArray(value)) {
16455
+ return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
16456
+ }
16457
+ const record = value;
16458
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
16459
+ }
16113
16460
  function renderConstBlock(name, value) {
16114
16461
  return `const ${name} = ${JSON.stringify(value, null, 2)} as const;`;
16115
16462
  }
@@ -18049,6 +18396,156 @@ ${knownFactsBlock}
18049
18396
  [Request]
18050
18397
  ${input.request?.trim() || "Use the latest user message in the conversation."}`;
18051
18398
  }
18399
+ var STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18400
+ manifest_default3,
18401
+ "stable@1.0.0 manifest"
18402
+ );
18403
+ function hashHarnessTemplateValue(value) {
18404
+ return hashString(stableStringify(value)) || "00000000";
18405
+ }
18406
+ function renderStableHarnessPrompt(input) {
18407
+ const prompt = buildGranularAgentSystemPrompt(input);
18408
+ const templateHash = hashHarnessTemplateValue(
18409
+ STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
18410
+ );
18411
+ return {
18412
+ templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18413
+ templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18414
+ templateHash,
18415
+ promptInstanceHash: hashHarnessTemplateValue({
18416
+ templateHash,
18417
+ input,
18418
+ prompt
18419
+ }),
18420
+ prompt
18421
+ };
18422
+ }
18423
+ function renderStableHarnessContinuation(resultPreview) {
18424
+ const instruction = buildContinuationInstruction(resultPreview);
18425
+ const templateHash = hashHarnessTemplateValue(
18426
+ STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
18427
+ );
18428
+ return {
18429
+ templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18430
+ templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18431
+ templateHash,
18432
+ instruction
18433
+ };
18434
+ }
18435
+ var STABLE_AGENT_HARNESS_TEMPLATE = {
18436
+ manifest: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST,
18437
+ renderPrompt: renderStableHarnessPrompt,
18438
+ renderContinuation: renderStableHarnessContinuation
18439
+ };
18440
+ var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18441
+ manifest_default2,
18442
+ "experimental-compact@0.1.0 manifest"
18443
+ );
18444
+ function renderExperimentalCompactHarnessPrompt(input) {
18445
+ const prompt = `${buildGranularAgentSystemPrompt(input)}
18446
+
18447
+ [Candidate Harness Delta: Compact Decision Discipline]
18448
+ - Prefer the smallest action that satisfies the current request.
18449
+ - When several records could match, ask one structured choice question before mutating.
18450
+ - Before any irreversible or outbound mutation, obtain explicit confirmation unless the policy surface already requires it.
18451
+ - Do not compensate for missing tools with raw network calls, synthetic records, or hidden side channels.
18452
+ - Keep the final user-facing reply focused on what was done, what was not done, and any remaining blocker.`;
18453
+ const templateHash = hashHarnessTemplateValue(
18454
+ EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
18455
+ );
18456
+ return {
18457
+ templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18458
+ templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18459
+ templateHash,
18460
+ promptInstanceHash: hashHarnessTemplateValue({
18461
+ templateHash,
18462
+ input,
18463
+ prompt
18464
+ }),
18465
+ prompt
18466
+ };
18467
+ }
18468
+ function renderExperimentalCompactHarnessContinuation(resultPreview) {
18469
+ const instruction = `${buildContinuationInstruction(resultPreview)}
18470
+
18471
+ Keep the continuation compact: either finish, ask the one blocking question, or run the next smallest safe action.`;
18472
+ const templateHash = hashHarnessTemplateValue(
18473
+ EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
18474
+ );
18475
+ return {
18476
+ templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18477
+ templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18478
+ templateHash,
18479
+ instruction
18480
+ };
18481
+ }
18482
+ var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE = {
18483
+ manifest: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST,
18484
+ renderPrompt: renderExperimentalCompactHarnessPrompt,
18485
+ renderContinuation: renderExperimentalCompactHarnessContinuation
18486
+ };
18487
+ var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18488
+ manifest_default,
18489
+ "action-presentation@0.1.0 manifest"
18490
+ );
18491
+ function renderActionPresentationHarnessPrompt(input) {
18492
+ const prompt = `${buildGranularAgentSystemPrompt(input)}
18493
+
18494
+ [Candidate Harness Delta: Action Request And Result Presentation]
18495
+ - 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.
18496
+ - 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.
18497
+ - 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.
18498
+ - After a mutation/effect call, base the reply on the returned action result and include a visible target label or identifier when one exists.
18499
+ - If no target can be grounded, say what was searched and what exact identifier or choice is needed; do not pretend the action completed.`;
18500
+ const templateHash = hashHarnessTemplateValue(
18501
+ ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
18502
+ );
18503
+ return {
18504
+ templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18505
+ templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18506
+ templateHash,
18507
+ promptInstanceHash: hashHarnessTemplateValue({
18508
+ templateHash,
18509
+ input,
18510
+ prompt
18511
+ }),
18512
+ prompt
18513
+ };
18514
+ }
18515
+ function renderActionPresentationHarnessContinuation(resultPreview) {
18516
+ const instruction = `${buildContinuationInstruction(resultPreview)}
18517
+
18518
+ 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.`;
18519
+ const templateHash = hashHarnessTemplateValue(
18520
+ ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
18521
+ );
18522
+ return {
18523
+ templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18524
+ templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18525
+ templateHash,
18526
+ instruction
18527
+ };
18528
+ }
18529
+ var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE = {
18530
+ manifest: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST,
18531
+ renderPrompt: renderActionPresentationHarnessPrompt,
18532
+ renderContinuation: renderActionPresentationHarnessContinuation
18533
+ };
18534
+ var AGENT_HARNESS_TEMPLATES = {
18535
+ [STABLE_AGENT_HARNESS_TEMPLATE.manifest.id]: STABLE_AGENT_HARNESS_TEMPLATE,
18536
+ [EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE.manifest.id]: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE,
18537
+ [ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE.manifest.id]: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE
18538
+ };
18539
+ function resolveHarnessTemplate(templateId = "stable", options) {
18540
+ const resolved = AGENT_HARNESS_TEMPLATES[templateId];
18541
+ if (resolved) return resolved;
18542
+ {
18543
+ const known = Object.keys(AGENT_HARNESS_TEMPLATES).join(", ");
18544
+ throw new Error(
18545
+ `Unknown harness template "${templateId}". Known templates: ${known}`
18546
+ );
18547
+ }
18548
+ }
18052
18549
 
18053
18550
  // src/openai-usage.ts
18054
18551
  var OPENAI_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
@@ -18170,6 +18667,98 @@ function matchesPattern(text, matcher) {
18170
18667
  if (typeof matcher === "string") return text.includes(matcher);
18171
18668
  return matcher.test(text);
18172
18669
  }
18670
+ function matcherFromConfig(value) {
18671
+ const match = value.match(/^\/([\s\S]*)\/([dgimsuvy]*)$/);
18672
+ if (!match) return value;
18673
+ return new RegExp(match[1] || "", match[2] || "");
18674
+ }
18675
+ function matchersFromConfig(values) {
18676
+ if (!values?.length) return void 0;
18677
+ return values.map((value) => matcherFromConfig(value));
18678
+ }
18679
+ function expectationsFromConfig(input) {
18680
+ if (!input) return void 0;
18681
+ return {
18682
+ replyIncludes: matchersFromConfig(input.replyIncludes),
18683
+ replyExcludes: matchersFromConfig(input.replyExcludes),
18684
+ actionIncludes: matchersFromConfig(input.actionIncludes),
18685
+ actionExcludes: matchersFromConfig(input.actionExcludes),
18686
+ codeIncludes: matchersFromConfig(input.codeIncludes),
18687
+ codeExcludes: matchersFromConfig(input.codeExcludes),
18688
+ behaviorBuckets: input.behaviorBuckets,
18689
+ actions: input.actions ? {
18690
+ required: matchersFromConfig(input.actions.required),
18691
+ forbidden: matchersFromConfig(input.actions.forbidden)
18692
+ } : void 0,
18693
+ prompts: input.prompts ? {
18694
+ ...input.prompts,
18695
+ requiredConfirmationBefore: matchersFromConfig(
18696
+ input.prompts.requiredConfirmationBefore
18697
+ )
18698
+ } : void 0,
18699
+ presentation: input.presentation ? {
18700
+ mustMention: matchersFromConfig(input.presentation.mustMention),
18701
+ mustNotMention: matchersFromConfig(input.presentation.mustNotMention),
18702
+ mustDisplayOrSave: matchersFromConfig(
18703
+ input.presentation.mustDisplayOrSave
18704
+ )
18705
+ } : void 0
18706
+ };
18707
+ }
18708
+ function promptResponderFromConfig(rules) {
18709
+ if (!rules?.length) return void 0;
18710
+ return createHumanResponder(
18711
+ rules.map((rule) => ({
18712
+ type: rule.type,
18713
+ when: Array.isArray(rule.when) ? rule.when.map((matcher) => matcherFromConfig(matcher)) : rule.when ? matcherFromConfig(rule.when) : void 0,
18714
+ answer: rule.answer
18715
+ }))
18716
+ );
18717
+ }
18718
+ function inspectionFromConfig(inspection) {
18719
+ if (!inspection) return void 0;
18720
+ return {
18721
+ code: inspection.code,
18722
+ includes: matchersFromConfig(inspection.includes),
18723
+ excludes: matchersFromConfig(inspection.excludes)
18724
+ };
18725
+ }
18726
+ function inspectionsFromConfig(inspections) {
18727
+ if (!inspections) return void 0;
18728
+ if (Array.isArray(inspections)) {
18729
+ return inspections.map((inspection) => inspectionFromConfig(inspection)).filter(
18730
+ (inspection) => Boolean(inspection)
18731
+ );
18732
+ }
18733
+ return inspectionFromConfig(inspections);
18734
+ }
18735
+ function scenariosFromAgentEvalFile(file) {
18736
+ return file.scenarios.map((scenario) => ({
18737
+ id: scenario.id,
18738
+ description: scenario.description,
18739
+ request: scenario.request,
18740
+ behaviorBuckets: scenario.behaviorBuckets,
18741
+ human: promptResponderFromConfig(scenario.human),
18742
+ prepareRecords: scenario.prepareRecords,
18743
+ expect: expectationsFromConfig(scenario.expect),
18744
+ verify: inspectionFromConfig(scenario.verify),
18745
+ inspect: inspectionsFromConfig(scenario.inspect),
18746
+ steps: scenario.steps?.map((step) => ({
18747
+ id: step.id,
18748
+ request: step.request,
18749
+ behaviorBuckets: step.behaviorBuckets || scenario.behaviorBuckets,
18750
+ human: promptResponderFromConfig(step.human || scenario.human),
18751
+ expect: expectationsFromConfig(step.expect || scenario.expect),
18752
+ inspect: inspectionsFromConfig(step.inspect)
18753
+ }))
18754
+ }));
18755
+ }
18756
+ async function loadAgentEvalScenarioFile(filePath) {
18757
+ const raw = JSON.parse(
18758
+ await promises.readFile(filePath, "utf8")
18759
+ );
18760
+ return scenariosFromAgentEvalFile(raw);
18761
+ }
18173
18762
  function assertMatches(label, text, includes = [], excludes = []) {
18174
18763
  for (const matcher of includes) {
18175
18764
  if (!matchesPattern(text, matcher)) {
@@ -18202,6 +18791,23 @@ function asArray3(value) {
18202
18791
  if (!value) return [];
18203
18792
  return Array.isArray(value) ? value : [value];
18204
18793
  }
18794
+ function progressEvent(input) {
18795
+ return {
18796
+ ...input,
18797
+ id: input.id || [
18798
+ input.scenarioId || "suite",
18799
+ input.stepId || input.phase,
18800
+ input.iteration ? `iteration-${input.iteration}` : "",
18801
+ input.jobId || "",
18802
+ input.title,
18803
+ Date.now()
18804
+ ].filter(Boolean).join(":"),
18805
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
18806
+ };
18807
+ }
18808
+ async function emitProgress(handler, event) {
18809
+ await handler?.(progressEvent(event));
18810
+ }
18205
18811
  var GPT_54_TOKEN_PRICING_USD_PER_MILLION = {
18206
18812
  input: 2.5,
18207
18813
  cachedInput: 0.25,
@@ -18444,7 +19050,7 @@ function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
18444
19050
  });
18445
19051
  }
18446
19052
  function createScriptedPromptResponder(rules, fallback) {
18447
- return async ({ prompt, history }) => {
19053
+ const responder = async ({ prompt, history }) => {
18448
19054
  const promptText = `${prompt.title || ""}
18449
19055
  ${prompt.message || ""}`;
18450
19056
  for (const rule of rules) {
@@ -18461,6 +19067,11 @@ ${prompt.message || ""}`;
18461
19067
  `No scripted prompt responder matched prompt ${prompt.id}: ${promptText}`
18462
19068
  );
18463
19069
  };
19070
+ Object.defineProperty(responder, "__granularScriptedPromptRules", {
19071
+ enumerable: false,
19072
+ value: rules
19073
+ });
19074
+ return responder;
18464
19075
  }
18465
19076
  function extractJsonObject(text) {
18466
19077
  const start = text.indexOf("{");
@@ -18747,6 +19358,22 @@ ${checkpoint.latestJobResult}` : null
18747
19358
  ];
18748
19359
  return lines.filter(Boolean).join("\n\n");
18749
19360
  }
19361
+ function readableAgentMessage(message) {
19362
+ const record = asRecord6(message);
19363
+ if (!record) return JSON.stringify(message);
19364
+ if (typeof record.reply === "string" && record.reply.trim()) {
19365
+ return record.reply;
19366
+ }
19367
+ const show = asRecord6(record.show);
19368
+ const variableNames = asArray3(show?.variableNames).map((value) => String(value)).filter(Boolean);
19369
+ if (variableNames.length) {
19370
+ return `Displayed ${variableNames.join(", ")}`;
19371
+ }
19372
+ if (typeof record.kind === "string") {
19373
+ return `Agent ${record.kind} message`;
19374
+ }
19375
+ return JSON.stringify(message);
19376
+ }
18750
19377
  async function waitForJobOutcome(input) {
18751
19378
  const stdout = [];
18752
19379
  const stderr = [];
@@ -18754,6 +19381,11 @@ async function waitForJobOutcome(input) {
18754
19381
  let lastPromptCount = 0;
18755
19382
  let lastMessageCount = 0;
18756
19383
  let lastJobSummary = null;
19384
+ let lastActionSummaryKey = "";
19385
+ let lastActionSummaryLength = 0;
19386
+ let lastAgentMessageKey = "";
19387
+ let lastAgentMessageCount = 0;
19388
+ let lastJobStatus = "";
18757
19389
  input.job.on("stdout", (line) => stdout.push(String(line)));
18758
19390
  input.job.on("stderr", (line) => stderr.push(String(line)));
18759
19391
  const startedAt = Date.now();
@@ -18769,7 +19401,71 @@ async function waitForJobOutcome(input) {
18769
19401
  const messages = asArray3(asRecord6(liveDoc.conversation)?.messages);
18770
19402
  lastMessageCount = messages.length;
18771
19403
  lastJobSummary = asRecord6(asRecord6(liveDoc.jobs)?.byId)?.[input.job.id] || null;
19404
+ const jobRecord = asRecord6(lastJobSummary);
19405
+ const jobStatus = typeof jobRecord?.status === "string" ? jobRecord.status : "";
19406
+ if (jobStatus && jobStatus !== lastJobStatus) {
19407
+ lastJobStatus = jobStatus;
19408
+ input.onProgress?.({
19409
+ id: `${input.job.id}:status:${jobStatus}:${Date.now()}`,
19410
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19411
+ phase: "job",
19412
+ status: "running",
19413
+ jobId: input.job.id,
19414
+ title: `Job ${jobStatus}`,
19415
+ message: `Granular job ${input.job.id}`,
19416
+ data: jobRecord,
19417
+ ...input.progressContext
19418
+ });
19419
+ }
19420
+ const actionSummary = getActionSummary(liveDoc, input.job.id);
19421
+ const actionSummaryKey = JSON.stringify(actionSummary);
19422
+ if (actionSummary.length && actionSummaryKey !== lastActionSummaryKey) {
19423
+ const newActions = actionSummary.slice(lastActionSummaryLength);
19424
+ lastActionSummaryLength = actionSummary.length;
19425
+ lastActionSummaryKey = actionSummaryKey;
19426
+ input.onProgress?.({
19427
+ id: `${input.job.id}:actions:${Date.now()}`,
19428
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19429
+ phase: "job",
19430
+ status: "running",
19431
+ jobId: input.job.id,
19432
+ title: newActions.length === 1 ? "Action observed" : "Actions observed",
19433
+ message: (newActions.length ? newActions : actionSummary).join("\n"),
19434
+ data: { actionSummary, newActions },
19435
+ ...input.progressContext
19436
+ });
19437
+ }
19438
+ const agentMessages = getJobAgentMessages(liveDoc, input.job.id);
19439
+ const agentMessageKey = JSON.stringify(agentMessages);
19440
+ if (agentMessages.length && agentMessageKey !== lastAgentMessageKey) {
19441
+ const newMessages = agentMessages.slice(lastAgentMessageCount);
19442
+ lastAgentMessageCount = agentMessages.length;
19443
+ lastAgentMessageKey = agentMessageKey;
19444
+ const latestMessage = newMessages.at(-1) || agentMessages.at(-1);
19445
+ input.onProgress?.({
19446
+ id: `${input.job.id}:messages:${Date.now()}`,
19447
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19448
+ phase: "job",
19449
+ status: "running",
19450
+ jobId: input.job.id,
19451
+ title: "Agent message",
19452
+ message: readableAgentMessage(latestMessage),
19453
+ data: { agentMessages, newMessages },
19454
+ ...input.progressContext
19455
+ });
19456
+ }
18772
19457
  if (prompts.length > 0) {
19458
+ input.onProgress?.({
19459
+ id: `${input.job.id}:prompt:${prompts[0]?.id || Date.now()}`,
19460
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19461
+ phase: "prompt",
19462
+ status: "running",
19463
+ jobId: input.job.id,
19464
+ title: "Waiting for prompt answer",
19465
+ message: prompts[0]?.message || prompts[0]?.title,
19466
+ data: { prompts },
19467
+ ...input.progressContext
19468
+ });
18773
19469
  return { kind: "prompt", prompts, liveDoc, stdout, stderr };
18774
19470
  }
18775
19471
  try {
@@ -18778,6 +19474,17 @@ async function waitForJobOutcome(input) {
18778
19474
  input.pollIntervalMs,
18779
19475
  `job ${input.job.id} tick`
18780
19476
  );
19477
+ input.onProgress?.({
19478
+ id: `${input.job.id}:completed:${Date.now()}`,
19479
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19480
+ phase: "job",
19481
+ status: "passed",
19482
+ jobId: input.job.id,
19483
+ title: "Job completed",
19484
+ message: JSON.stringify(result)?.slice(0, 800),
19485
+ data: { result, stdout, stderr },
19486
+ ...input.progressContext
19487
+ });
18781
19488
  return { kind: "completed", result, liveDoc, stdout, stderr };
18782
19489
  } catch (error) {
18783
19490
  const message = error instanceof Error ? error.message : String(error);
@@ -19009,6 +19716,10 @@ function buildSessionLogReport(input) {
19009
19716
  `- Environment id: \`${conversation.environment.environmentId}\``,
19010
19717
  `- Sandbox id: \`${conversation.environment.sandboxId}\``,
19011
19718
  `- Status: ${result?.status || (error ? "failed" : "unknown")}`,
19719
+ ...systemPrompts[0]?.iteration.templateId ? [
19720
+ `- Harness template: \`${systemPrompts[0].iteration.templateId}@${systemPrompts[0].iteration.templateVersion || "unknown"}\``,
19721
+ `- Template hash: \`${systemPrompts[0].iteration.templateHash || "unknown"}\``
19722
+ ] : [],
19012
19723
  ...result?.error || error ? [`- Error: ${result?.error || error}`] : [],
19013
19724
  "",
19014
19725
  "## Conversation"
@@ -19116,6 +19827,12 @@ function buildSessionLogReport(input) {
19116
19827
  lines.push(
19117
19828
  `### Turn ${turn.turnNumber}, Generation ${iteration.iteration}`,
19118
19829
  "",
19830
+ ...iteration.templateId ? [
19831
+ `- Template: \`${iteration.templateId}@${iteration.templateVersion || "unknown"}\``,
19832
+ `- Template hash: \`${iteration.templateHash || "unknown"}\``,
19833
+ `- Prompt hash: \`${iteration.promptInstanceHash || "unknown"}\``,
19834
+ ""
19835
+ ] : [],
19119
19836
  fenced(iteration.systemPrompt, "text"),
19120
19837
  ""
19121
19838
  );
@@ -19157,9 +19874,167 @@ function isTransientEvalError(error) {
19157
19874
  const message = error instanceof Error ? error.message : String(error);
19158
19875
  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);
19159
19876
  }
19877
+ function assertionPassed(id, run) {
19878
+ try {
19879
+ run(id);
19880
+ return { id, label: id, status: "passed" };
19881
+ } catch (error) {
19882
+ return {
19883
+ id,
19884
+ label: id,
19885
+ status: "failed",
19886
+ message: error instanceof Error ? error.message : String(error)
19887
+ };
19888
+ }
19889
+ }
19890
+ function hasMatchers(matchers) {
19891
+ return Boolean(matchers?.length);
19892
+ }
19893
+ function evaluateExpectationAssertions(input) {
19894
+ const expect = input.expect;
19895
+ if (!expect) return [];
19896
+ const prefix = `${input.scenarioId} step ${input.stepIndex + 1}`;
19897
+ const assertions = [];
19898
+ const actionText = input.completed.actionSummary.join("\n");
19899
+ const codeText = input.completed.finalCode || "";
19900
+ const displaySurface = [
19901
+ input.completed.responseText,
19902
+ actionText,
19903
+ codeText,
19904
+ JSON.stringify(input.completed.result)
19905
+ ].join("\n");
19906
+ const promptSurface = input.completed.promptInteractions.map(
19907
+ (interaction) => [interaction.type, interaction.title, interaction.message].join(" ")
19908
+ ).join("\n");
19909
+ if (hasMatchers(expect.replyIncludes) || hasMatchers(expect.replyExcludes)) {
19910
+ assertions.push(
19911
+ assertionPassed(
19912
+ "reply",
19913
+ () => assertMatches(
19914
+ `Reply for ${prefix}`,
19915
+ input.completed.responseText,
19916
+ expect.replyIncludes,
19917
+ expect.replyExcludes
19918
+ )
19919
+ )
19920
+ );
19921
+ }
19922
+ if (hasMatchers(expect.actionIncludes) || hasMatchers(expect.actionExcludes)) {
19923
+ assertions.push(
19924
+ assertionPassed(
19925
+ "actions",
19926
+ () => assertMatches(
19927
+ `Action summary for ${prefix}`,
19928
+ actionText,
19929
+ expect.actionIncludes,
19930
+ expect.actionExcludes
19931
+ )
19932
+ )
19933
+ );
19934
+ }
19935
+ if (hasMatchers(expect.codeIncludes) || hasMatchers(expect.codeExcludes)) {
19936
+ assertions.push(
19937
+ assertionPassed(
19938
+ "code",
19939
+ () => assertMatches(
19940
+ `Generated code for ${prefix}`,
19941
+ codeText,
19942
+ expect.codeIncludes,
19943
+ expect.codeExcludes
19944
+ )
19945
+ )
19946
+ );
19947
+ }
19948
+ if (hasMatchers(expect.actions?.required) || hasMatchers(expect.actions?.forbidden)) {
19949
+ assertions.push(
19950
+ assertionPassed(
19951
+ "required-actions",
19952
+ () => assertMatches(
19953
+ `Required actions for ${prefix}`,
19954
+ actionText,
19955
+ expect.actions?.required,
19956
+ expect.actions?.forbidden
19957
+ )
19958
+ )
19959
+ );
19960
+ }
19961
+ if (hasMatchers(expect.presentation?.mustMention) || hasMatchers(expect.presentation?.mustNotMention)) {
19962
+ assertions.push(
19963
+ assertionPassed(
19964
+ "presentation",
19965
+ () => assertMatches(
19966
+ `Presentation for ${prefix}`,
19967
+ input.completed.responseText,
19968
+ expect.presentation?.mustMention,
19969
+ expect.presentation?.mustNotMention
19970
+ )
19971
+ )
19972
+ );
19973
+ }
19974
+ if (hasMatchers(expect.presentation?.mustDisplayOrSave)) {
19975
+ assertions.push(
19976
+ assertionPassed(
19977
+ "display",
19978
+ () => assertMatches(
19979
+ `Displayed or saved records for ${prefix}`,
19980
+ displaySurface,
19981
+ expect.presentation?.mustDisplayOrSave
19982
+ )
19983
+ )
19984
+ );
19985
+ }
19986
+ if (expect.prompts?.requiredChoiceWhenAmbiguous) {
19987
+ assertions.push(
19988
+ assertionPassed("choice", () => {
19989
+ if (!input.completed.promptInteractions.some(
19990
+ (interaction) => interaction.type === "choice"
19991
+ )) {
19992
+ throw new Error(`Expected ${prefix} to use a choice prompt.`);
19993
+ }
19994
+ })
19995
+ );
19996
+ }
19997
+ for (const forbiddenType of expect.prompts?.forbiddenPromptTypes || []) {
19998
+ assertions.push(
19999
+ assertionPassed(`forbidden-prompt:${forbiddenType}`, () => {
20000
+ if (input.completed.promptInteractions.some(
20001
+ (interaction) => interaction.type === forbiddenType
20002
+ )) {
20003
+ throw new Error(
20004
+ `Prompt interactions for ${prefix} used forbidden prompt type ${forbiddenType}.`
20005
+ );
20006
+ }
20007
+ })
20008
+ );
20009
+ }
20010
+ if (expect.prompts?.requiredConfirmationBefore?.length) {
20011
+ assertions.push(
20012
+ assertionPassed(
20013
+ "confirmation",
20014
+ () => assertMatches(
20015
+ `Confirmation prompts for ${prefix}`,
20016
+ [actionText, promptSurface].join("\n"),
20017
+ expect.prompts?.requiredConfirmationBefore
20018
+ )
20019
+ )
20020
+ );
20021
+ }
20022
+ return assertions;
20023
+ }
19160
20024
  async function runAgentEvalSuite(options) {
19161
20025
  const results = [];
19162
20026
  for (const scenario of options.scenarios) {
20027
+ await options.harness.emitProgress?.({
20028
+ phase: "scenario",
20029
+ status: "running",
20030
+ scenarioId: scenario.id,
20031
+ title: "Scenario started",
20032
+ message: scenario.description || scenario.request || scenario.id,
20033
+ data: {
20034
+ scenarioId: scenario.id,
20035
+ behaviorBuckets: scenario.behaviorBuckets
20036
+ }
20037
+ });
19163
20038
  let attempt = 0;
19164
20039
  let finalResult = null;
19165
20040
  while (attempt < 2 && !finalResult) {
@@ -19230,27 +20105,38 @@ async function runAgentEvalSuite(options) {
19230
20105
  },
19231
20106
  assertMatches
19232
20107
  };
19233
- if (step.expect) {
19234
- assertMatches(
19235
- `Reply for ${scenario.id} step ${index + 1}`,
19236
- completed.responseText,
19237
- step.expect.replyIncludes,
19238
- step.expect.replyExcludes
19239
- );
19240
- assertMatches(
19241
- `Action summary for ${scenario.id} step ${index + 1}`,
19242
- completed.actionSummary.join("\n"),
19243
- step.expect.actionIncludes,
19244
- step.expect.actionExcludes
19245
- );
19246
- assertMatches(
19247
- `Generated code for ${scenario.id} step ${index + 1}`,
19248
- completed.finalCode || "",
19249
- step.expect.codeIncludes,
19250
- step.expect.codeExcludes
19251
- );
20108
+ const assertions = evaluateExpectationAssertions({
20109
+ scenarioId: scenario.id,
20110
+ stepIndex: index,
20111
+ expect: step.expect,
20112
+ completed
20113
+ });
20114
+ for (const assertion of assertions) {
20115
+ await options.harness.emitProgress?.({
20116
+ phase: "assertion",
20117
+ status: assertion.status,
20118
+ scenarioId: scenario.id,
20119
+ stepId: step.id || `step-${index + 1}`,
20120
+ title: assertion.label,
20121
+ message: assertion.message,
20122
+ data: assertion
20123
+ });
20124
+ }
20125
+ const failedAssertion = assertions.find(
20126
+ (assertion) => assertion.status === "failed"
20127
+ );
20128
+ if (failedAssertion) {
20129
+ throw new Error(failedAssertion.message || failedAssertion.label);
19252
20130
  }
19253
20131
  for (const inspection of stepInspections) {
20132
+ await options.harness.emitProgress?.({
20133
+ phase: "inspection",
20134
+ status: "running",
20135
+ scenarioId: scenario.id,
20136
+ stepId: step.id || `step-${index + 1}`,
20137
+ title: "Running step inspection",
20138
+ message: inspection.code.slice(0, 500)
20139
+ });
19254
20140
  const inspectionResult = await context.inspect(inspection.code);
19255
20141
  const inspectionText = JSON.stringify(inspectionResult, null, 2);
19256
20142
  assertMatches(
@@ -19266,9 +20152,32 @@ async function runAgentEvalSuite(options) {
19266
20152
  });
19267
20153
  }
19268
20154
  inspectionResults.push(inspectionResult);
20155
+ await options.harness.emitProgress?.({
20156
+ phase: "inspection",
20157
+ status: "passed",
20158
+ scenarioId: scenario.id,
20159
+ stepId: step.id || `step-${index + 1}`,
20160
+ title: "Step inspection passed",
20161
+ message: inspectionText.slice(0, 800),
20162
+ data: inspectionResult
20163
+ });
19269
20164
  }
19270
20165
  for (const check of stepChecks) {
20166
+ await options.harness.emitProgress?.({
20167
+ phase: "check",
20168
+ status: "running",
20169
+ scenarioId: scenario.id,
20170
+ stepId: step.id || `step-${index + 1}`,
20171
+ title: "Running custom check"
20172
+ });
19271
20173
  await check(context);
20174
+ await options.harness.emitProgress?.({
20175
+ phase: "check",
20176
+ status: "passed",
20177
+ scenarioId: scenario.id,
20178
+ stepId: step.id || `step-${index + 1}`,
20179
+ title: "Custom check passed"
20180
+ });
19272
20181
  }
19273
20182
  stepResults.push({
19274
20183
  id: step.id || `step-${index + 1}`,
@@ -19279,6 +20188,7 @@ async function runAgentEvalSuite(options) {
19279
20188
  actionSummary: completed.actionSummary,
19280
20189
  promptInteractions: completed.promptInteractions,
19281
20190
  inspectionResults,
20191
+ assertions,
19282
20192
  turnDir: completed.turnDir
19283
20193
  });
19284
20194
  }
@@ -19297,6 +20207,7 @@ async function runAgentEvalSuite(options) {
19297
20207
  actionSummary: lastStep.actionSummary,
19298
20208
  promptInteractions: lastStep.promptInteractions,
19299
20209
  verification: lastStep.inspectionResults.length <= 1 ? lastStep.inspectionResults[0] ?? null : lastStep.inspectionResults,
20210
+ assertions: stepResults.flatMap((step) => step.assertions || []),
19300
20211
  tokenUsage: aggregateConversationTokenUsage(conversation),
19301
20212
  steps: stepResults,
19302
20213
  turnDir: conversation.artifactDir
@@ -19318,6 +20229,14 @@ async function runAgentEvalSuite(options) {
19318
20229
  conversation,
19319
20230
  result
19320
20231
  });
20232
+ await options.harness.emitProgress?.({
20233
+ phase: "scenario",
20234
+ status: "passed",
20235
+ scenarioId: scenario.id,
20236
+ title: "Scenario passed",
20237
+ message: `${result.assertions?.filter((assertion) => assertion.status === "passed").length || 0}/${result.assertions?.length || 0} assertions`,
20238
+ data: result
20239
+ });
19321
20240
  finalResult = result;
19322
20241
  } catch (error) {
19323
20242
  const failureMessage = error instanceof Error ? error.message : String(error);
@@ -19358,6 +20277,14 @@ async function runAgentEvalSuite(options) {
19358
20277
  result: failed,
19359
20278
  error: failureMessage
19360
20279
  });
20280
+ await options.harness.emitProgress?.({
20281
+ phase: "scenario",
20282
+ status: "failed",
20283
+ scenarioId: scenario.id,
20284
+ title: "Scenario failed",
20285
+ message: failureMessage,
20286
+ data: failed
20287
+ });
19361
20288
  finalResult = failed;
19362
20289
  } finally {
19363
20290
  await options.harness.closeConversation(conversation);
@@ -19406,6 +20333,11 @@ function createAgentEvalHarness(options) {
19406
20333
  const chatTimeoutMs = options.chatTimeoutMs ?? 12e4;
19407
20334
  const jobTimeoutMs = options.jobTimeoutMs ?? 9e4;
19408
20335
  const pollIntervalMs = options.pollIntervalMs ?? 250;
20336
+ const resolvedTemplate = resolveHarnessTemplate(
20337
+ options.harnessTemplateId || process.env.GRANULAR_AGENT_HARNESS_TEMPLATE || "stable");
20338
+ const promptRenderer = options.promptRenderer || resolvedTemplate.renderPrompt;
20339
+ const continuationRenderer = options.continuationRenderer || resolvedTemplate.renderContinuation;
20340
+ const onProgress = options.onProgress;
19409
20341
  async function openConversation(label) {
19410
20342
  await ensureDir(artifactDir);
19411
20343
  const clientId = `${slugify(label)}-${Date.now()}`;
@@ -19474,6 +20406,13 @@ function createAgentEvalHarness(options) {
19474
20406
  };
19475
20407
  }
19476
20408
  async function runInspection(conversation, inspection, completed, turnDir) {
20409
+ await emitProgress(onProgress, {
20410
+ phase: "inspection",
20411
+ status: "running",
20412
+ scenarioId: conversation.label,
20413
+ title: "Running inspection",
20414
+ message: inspection.code.slice(0, 500)
20415
+ });
19477
20416
  let result = null;
19478
20417
  let lastError = null;
19479
20418
  for (let attempt = 0; attempt < 10; attempt += 1) {
@@ -19501,6 +20440,14 @@ function createAgentEvalHarness(options) {
19501
20440
  });
19502
20441
  }
19503
20442
  await writeJson(path__default.default.join(turnDir, "verification.json"), result);
20443
+ await emitProgress(onProgress, {
20444
+ phase: "inspection",
20445
+ status: "passed",
20446
+ scenarioId: conversation.label,
20447
+ title: "Inspection passed",
20448
+ message: JSON.stringify(result)?.slice(0, 800),
20449
+ data: result
20450
+ });
19504
20451
  return result;
19505
20452
  }
19506
20453
  async function resumePendingTurn(pending, responder) {
@@ -19510,6 +20457,16 @@ function createAgentEvalHarness(options) {
19510
20457
  prompt,
19511
20458
  history: pending.promptInteractions
19512
20459
  });
20460
+ await emitProgress(onProgress, {
20461
+ phase: "interaction",
20462
+ status: "running",
20463
+ scenarioId: pending.conversation.label,
20464
+ stepId: path__default.default.basename(pending.turnDir),
20465
+ jobId: pending.job.id,
20466
+ title: "Prompt answered",
20467
+ message: `${prompt.type}: ${prompt.message || prompt.title} -> ${JSON.stringify(answer)}`,
20468
+ data: { prompt, answer }
20469
+ });
19513
20470
  const session = pending.conversation.environment;
19514
20471
  await session.answerPrompt(prompt.id, answer);
19515
20472
  pending.promptInteractions.push({
@@ -19529,7 +20486,12 @@ function createAgentEvalHarness(options) {
19529
20486
  job: pending.job,
19530
20487
  boundaryTimestamp: pending.boundaryTimestamp,
19531
20488
  timeoutMs: jobTimeoutMs,
19532
- pollIntervalMs
20489
+ pollIntervalMs,
20490
+ onProgress: (event) => void onProgress?.(event),
20491
+ progressContext: {
20492
+ scenarioId: pending.conversation.label,
20493
+ stepId: path__default.default.basename(pending.turnDir)
20494
+ }
19533
20495
  });
19534
20496
  if (resumed.kind === "prompt") {
19535
20497
  return {
@@ -19610,12 +20572,44 @@ function createAgentEvalHarness(options) {
19610
20572
  };
19611
20573
  conversation.logTurns.push(turnLog);
19612
20574
  if (input.prepareRecords?.length) {
20575
+ await emitProgress(onProgress, {
20576
+ phase: "setup",
20577
+ status: "running",
20578
+ scenarioId: conversation.label,
20579
+ stepId: turnId,
20580
+ title: "Recording setup records",
20581
+ message: `${input.prepareRecords.length} records`,
20582
+ data: input.prepareRecords
20583
+ });
19613
20584
  await conversation.environment.recordObjects(input.prepareRecords);
20585
+ await emitProgress(onProgress, {
20586
+ phase: "setup",
20587
+ status: "passed",
20588
+ scenarioId: conversation.label,
20589
+ stepId: turnId,
20590
+ title: "Setup records recorded",
20591
+ message: `${input.prepareRecords.length} records`
20592
+ });
19614
20593
  }
19615
20594
  if (input.prepareTools?.length) {
20595
+ await emitProgress(onProgress, {
20596
+ phase: "setup",
20597
+ status: "running",
20598
+ scenarioId: conversation.label,
20599
+ stepId: turnId,
20600
+ title: "Registering effect handlers",
20601
+ message: `${input.prepareTools.length} handlers`
20602
+ });
19616
20603
  await options.granular.ontology(conversation.environment.sandboxId).effects.registerMany(input.prepareTools);
19617
20604
  }
19618
20605
  if (input.prepare) {
20606
+ await emitProgress(onProgress, {
20607
+ phase: "setup",
20608
+ status: "running",
20609
+ scenarioId: conversation.label,
20610
+ stepId: turnId,
20611
+ title: "Running custom setup"
20612
+ });
19619
20613
  await input.prepare({
19620
20614
  conversation,
19621
20615
  environment: conversation.environment,
@@ -19624,6 +20618,14 @@ function createAgentEvalHarness(options) {
19624
20618
  }
19625
20619
  const boundaryTimestamp = Date.now();
19626
20620
  conversation.history.push({ role: "user", content: input.request });
20621
+ await emitProgress(onProgress, {
20622
+ phase: "step",
20623
+ status: "running",
20624
+ scenarioId: conversation.label,
20625
+ stepId: turnId,
20626
+ title: "User request",
20627
+ message: input.request
20628
+ });
19627
20629
  await writeJson(path__default.default.join(turnDir, "request.json"), {
19628
20630
  request: input.request,
19629
20631
  boundaryTimestamp
@@ -19665,7 +20667,7 @@ function createAgentEvalHarness(options) {
19665
20667
  inputSchema: tool.inputSchema,
19666
20668
  outputSchema: tool.outputSchema
19667
20669
  }));
19668
- const systemPrompt = buildGranularAgentSystemPrompt({
20670
+ const renderedPrompt = promptRenderer({
19669
20671
  domainDocumentation: await conversation.environment.getDomainDocumentation(),
19670
20672
  sessionContext: {
19671
20673
  sandboxId: conversation.environment.sandboxId,
@@ -19686,9 +20688,39 @@ function createAgentEvalHarness(options) {
19686
20688
  tools,
19687
20689
  checkpoint: latestCheckpoint
19688
20690
  });
19689
- const request = iteration === 0 ? input.request : buildContinuationInstruction(
20691
+ const systemPrompt = renderedPrompt.prompt;
20692
+ await emitProgress(onProgress, {
20693
+ phase: "prompt",
20694
+ status: "passed",
20695
+ scenarioId: conversation.label,
20696
+ stepId: turnId,
20697
+ iteration: iteration + 1,
20698
+ templateId: renderedPrompt.templateId,
20699
+ templateVersion: renderedPrompt.templateVersion,
20700
+ title: "Rendered harness prompt",
20701
+ message: `${systemPrompt.split("\n").length} lines`,
20702
+ data: {
20703
+ templateId: renderedPrompt.templateId,
20704
+ templateVersion: renderedPrompt.templateVersion,
20705
+ templateHash: renderedPrompt.templateHash,
20706
+ promptInstanceHash: renderedPrompt.promptInstanceHash,
20707
+ prompt: systemPrompt
20708
+ }
20709
+ });
20710
+ const request = iteration === 0 ? input.request : continuationRenderer(
19690
20711
  buildContinuationPreview(latestCheckpoint, noProgressCount)
19691
- );
20712
+ ).instruction;
20713
+ await emitProgress(onProgress, {
20714
+ phase: "generation",
20715
+ status: "running",
20716
+ scenarioId: conversation.label,
20717
+ stepId: turnId,
20718
+ iteration: iteration + 1,
20719
+ templateId: renderedPrompt.templateId,
20720
+ templateVersion: renderedPrompt.templateVersion,
20721
+ title: iteration === 0 ? "Generating agent response" : "Generating continuation",
20722
+ message: request
20723
+ });
19692
20724
  const generation = await withTimeout2(
19693
20725
  generateTurnWithRepair(options.generator, {
19694
20726
  systemPrompt,
@@ -19707,10 +20739,31 @@ function createAgentEvalHarness(options) {
19707
20739
  chatTimeoutMs,
19708
20740
  `chat generation for ${conversation.label} iteration ${iteration + 1}`
19709
20741
  );
20742
+ await emitProgress(onProgress, {
20743
+ phase: "generation",
20744
+ status: "passed",
20745
+ scenarioId: conversation.label,
20746
+ stepId: turnId,
20747
+ iteration: iteration + 1,
20748
+ templateId: renderedPrompt.templateId,
20749
+ templateVersion: renderedPrompt.templateVersion,
20750
+ title: generation.code ? "Generated job code" : "Generated text reply",
20751
+ message: generation.code || generation.reply || "",
20752
+ data: {
20753
+ reply: generation.reply,
20754
+ code: generation.code,
20755
+ attempts: generation.generationAttempts,
20756
+ usage: tokenUsageForGenerationOutput(generation)
20757
+ }
20758
+ });
19710
20759
  const iterationLog = {
19711
20760
  iteration: iteration + 1,
19712
20761
  request,
19713
20762
  systemPrompt,
20763
+ templateId: renderedPrompt.templateId,
20764
+ templateVersion: renderedPrompt.templateVersion,
20765
+ templateHash: renderedPrompt.templateHash,
20766
+ promptInstanceHash: renderedPrompt.promptInstanceHash,
19714
20767
  generationReply: generation.reply,
19715
20768
  generatedCode: generation.code,
19716
20769
  rawGeneration: generation.raw,
@@ -19757,6 +20810,15 @@ function createAgentEvalHarness(options) {
19757
20810
  result: completed.result
19758
20811
  };
19759
20812
  await writeJson(path__default.default.join(turnDir, "result.json"), completed);
20813
+ await emitProgress(onProgress, {
20814
+ phase: "step",
20815
+ status: "passed",
20816
+ scenarioId: conversation.label,
20817
+ stepId: turnId,
20818
+ title: "Step completed with text reply",
20819
+ message: responseText2,
20820
+ data: completed
20821
+ });
19760
20822
  return completed;
19761
20823
  }
19762
20824
  const session = conversation.environment;
@@ -19777,12 +20839,33 @@ function createAgentEvalHarness(options) {
19777
20839
  )
19778
20840
  }
19779
20841
  });
20842
+ await emitProgress(onProgress, {
20843
+ phase: "job",
20844
+ status: "running",
20845
+ scenarioId: conversation.label,
20846
+ stepId: turnId,
20847
+ iteration: iteration + 1,
20848
+ jobId: job.id,
20849
+ templateId: renderedPrompt.templateId,
20850
+ templateVersion: renderedPrompt.templateVersion,
20851
+ title: "Submitted Granular job",
20852
+ message: job.id,
20853
+ data: { code: generation.code }
20854
+ });
19780
20855
  const outcome = await waitForJobOutcome({
19781
20856
  environment: conversation.environment,
19782
20857
  job,
19783
20858
  boundaryTimestamp,
19784
20859
  timeoutMs: jobTimeoutMs,
19785
- pollIntervalMs
20860
+ pollIntervalMs,
20861
+ onProgress: (event) => void onProgress?.(event),
20862
+ progressContext: {
20863
+ scenarioId: conversation.label,
20864
+ stepId: turnId,
20865
+ iteration: iteration + 1,
20866
+ templateId: renderedPrompt.templateId,
20867
+ templateVersion: renderedPrompt.templateVersion
20868
+ }
19786
20869
  });
19787
20870
  if (outcome.kind === "prompt") {
19788
20871
  if (!autoAnswerPrompts) {
@@ -19892,6 +20975,23 @@ function createAgentEvalHarness(options) {
19892
20975
  controllerReason: continuation.reason,
19893
20976
  noProgressCount: continuation.nextNoProgressCount
19894
20977
  };
20978
+ await emitProgress(onProgress, {
20979
+ phase: "continuation",
20980
+ status: continuation.shouldContinue ? "running" : "passed",
20981
+ scenarioId: conversation.label,
20982
+ stepId: turnId,
20983
+ iteration: iteration + 1,
20984
+ jobId: job.id,
20985
+ templateId: renderedPrompt.templateId,
20986
+ templateVersion: renderedPrompt.templateVersion,
20987
+ title: continuation.shouldContinue ? "Harness requested another loop" : "Harness accepted completion",
20988
+ message: `${continuation.reason}; ${continuation.outcome}`,
20989
+ data: {
20990
+ continuation,
20991
+ checkpoint: latestCheckpoint,
20992
+ verifierSnapshot
20993
+ }
20994
+ });
19895
20995
  previousSnapshot = verifierSnapshot;
19896
20996
  noProgressCount = continuation.nextNoProgressCount;
19897
20997
  conversation.history.push({
@@ -19945,6 +21045,17 @@ function createAgentEvalHarness(options) {
19945
21045
  result: outcome.result
19946
21046
  };
19947
21047
  await writeJson(path__default.default.join(turnDir, "result.json"), completed);
21048
+ await emitProgress(onProgress, {
21049
+ phase: "step",
21050
+ status: "passed",
21051
+ scenarioId: conversation.label,
21052
+ stepId: turnId,
21053
+ iteration: iteration + 1,
21054
+ jobId: job.id,
21055
+ title: "Step completed",
21056
+ message: responseText,
21057
+ data: completed
21058
+ });
19948
21059
  return completed;
19949
21060
  }
19950
21061
  iteration += 1;
@@ -19956,6 +21067,7 @@ function createAgentEvalHarness(options) {
19956
21067
  return {
19957
21068
  artifactDir,
19958
21069
  granular: options.granular,
21070
+ emitProgress: (event) => emitProgress(onProgress, event),
19959
21071
  openConversation,
19960
21072
  closeConversation,
19961
21073
  runTurn,
@@ -20021,7 +21133,11 @@ function createAgentTester(options) {
20021
21133
  controllerBudgets: options.controllerBudgets,
20022
21134
  chatTimeoutMs: options.chatTimeoutMs,
20023
21135
  jobTimeoutMs: options.jobTimeoutMs,
20024
- pollIntervalMs: options.pollIntervalMs
21136
+ pollIntervalMs: options.pollIntervalMs,
21137
+ harnessTemplateId: options.harnessTemplateId,
21138
+ promptRenderer: options.promptRenderer,
21139
+ continuationRenderer: options.continuationRenderer,
21140
+ onProgress: options.onProgress
20025
21141
  });
20026
21142
  return {
20027
21143
  ...harness,
@@ -20056,7 +21172,9 @@ exports.createScriptedPromptResponder = createScriptedPromptResponder;
20056
21172
  exports.createTestArtifactsDirectory = createTestArtifactsDirectory;
20057
21173
  exports.createTimestampedArtifactDirectory = createTimestampedArtifactDirectory;
20058
21174
  exports.generateTurnWithRepair = generateTurnWithRepair;
21175
+ exports.loadAgentEvalScenarioFile = loadAgentEvalScenarioFile;
20059
21176
  exports.runAgentEvalSuite = runAgentEvalSuite;
20060
21177
  exports.runAgentTests = runAgentTests;
21178
+ exports.scenariosFromAgentEvalFile = scenariosFromAgentEvalFile;
20061
21179
  //# sourceMappingURL=agent-evals.js.map
20062
21180
  //# sourceMappingURL=agent-evals.js.map