@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.
@@ -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';
@@ -162,9 +162,9 @@ var require_node_gyp_build = __commonJS({
162
162
  }
163
163
  function parseTags(file) {
164
164
  var arr = file.split(".");
165
- var extension = arr.pop();
165
+ var extension2 = arr.pop();
166
166
  var tags = { file, specificity: 0 };
167
- if (extension !== "node") return;
167
+ if (extension2 !== "node") return;
168
168
  for (var i = 0; i < arr.length; i++) {
169
169
  var tag = arr[i];
170
170
  if (tag === "node" || tag === "electron" || tag === "node-webkit") {
@@ -412,7 +412,7 @@ var require_permessage_deflate = __commonJS({
412
412
  var kBuffers = Symbol("buffers");
413
413
  var kError = Symbol("error");
414
414
  var zlibLimiter;
415
- var PerMessageDeflate = class {
415
+ var PerMessageDeflate2 = class {
416
416
  /**
417
417
  * Creates a PerMessageDeflate instance.
418
418
  *
@@ -423,6 +423,9 @@ var require_permessage_deflate = __commonJS({
423
423
  * acknowledge disabling of client context takeover
424
424
  * @param {Number} [options.concurrencyLimit=10] The number of concurrent
425
425
  * calls to zlib
426
+ * @param {Boolean} [options.isServer=false] Create the instance in either
427
+ * server or client mode
428
+ * @param {Number} [options.maxPayload=0] The maximum allowed message length
426
429
  * @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
427
430
  * use of a custom server window size
428
431
  * @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
@@ -433,15 +436,12 @@ var require_permessage_deflate = __commonJS({
433
436
  * deflate
434
437
  * @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
435
438
  * inflate
436
- * @param {Boolean} [isServer=false] Create the instance in either server or
437
- * client mode
438
- * @param {Number} [maxPayload=0] The maximum allowed message length
439
439
  */
440
- constructor(options, isServer, maxPayload) {
441
- this._maxPayload = maxPayload | 0;
440
+ constructor(options) {
442
441
  this._options = options || {};
443
442
  this._threshold = this._options.threshold !== void 0 ? this._options.threshold : 1024;
444
- this._isServer = !!isServer;
443
+ this._maxPayload = this._options.maxPayload | 0;
444
+ this._isServer = !!this._options.isServer;
445
445
  this._deflate = null;
446
446
  this._inflate = null;
447
447
  this.params = null;
@@ -750,7 +750,7 @@ var require_permessage_deflate = __commonJS({
750
750
  });
751
751
  }
752
752
  };
753
- module.exports = PerMessageDeflate;
753
+ module.exports = PerMessageDeflate2;
754
754
  function deflateOnData(chunk) {
755
755
  this[kBuffers].push(chunk);
756
756
  this[kTotalLength] += chunk.length;
@@ -1030,7 +1030,7 @@ var require_validation = __commonJS({
1030
1030
  var require_receiver = __commonJS({
1031
1031
  "../../node_modules/ws/lib/receiver.js"(exports, module) {
1032
1032
  var { Writable } = __require("stream");
1033
- var PerMessageDeflate = require_permessage_deflate();
1033
+ var PerMessageDeflate2 = require_permessage_deflate();
1034
1034
  var {
1035
1035
  BINARY_TYPES,
1036
1036
  EMPTY_BUFFER,
@@ -1060,6 +1060,10 @@ var require_receiver = __commonJS({
1060
1060
  * extensions
1061
1061
  * @param {Boolean} [options.isServer=false] Specifies whether to operate in
1062
1062
  * client or server mode
1063
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
1064
+ * buffered data chunks
1065
+ * @param {Number} [options.maxFragments=0] The maximum number of message
1066
+ * fragments
1063
1067
  * @param {Number} [options.maxPayload=0] The maximum allowed message length
1064
1068
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
1065
1069
  * not to skip UTF-8 validation for text and close messages
@@ -1070,6 +1074,8 @@ var require_receiver = __commonJS({
1070
1074
  this._binaryType = options.binaryType || BINARY_TYPES[0];
1071
1075
  this._extensions = options.extensions || {};
1072
1076
  this._isServer = !!options.isServer;
1077
+ this._maxBufferedChunks = options.maxBufferedChunks | 0;
1078
+ this._maxFragments = options.maxFragments | 0;
1073
1079
  this._maxPayload = options.maxPayload | 0;
1074
1080
  this._skipUTF8Validation = !!options.skipUTF8Validation;
1075
1081
  this[kWebSocket] = void 0;
@@ -1099,6 +1105,18 @@ var require_receiver = __commonJS({
1099
1105
  */
1100
1106
  _write(chunk, encoding, cb) {
1101
1107
  if (this._opcode === 8 && this._state == GET_INFO) return cb();
1108
+ if (this._maxBufferedChunks > 0 && this._buffers.length >= this._maxBufferedChunks) {
1109
+ cb(
1110
+ this.createError(
1111
+ RangeError,
1112
+ "Too many buffered chunks",
1113
+ false,
1114
+ 1008,
1115
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1116
+ )
1117
+ );
1118
+ return;
1119
+ }
1102
1120
  this._bufferedBytes += chunk.length;
1103
1121
  this._buffers.push(chunk);
1104
1122
  this.startLoop(cb);
@@ -1197,7 +1215,7 @@ var require_receiver = __commonJS({
1197
1215
  return;
1198
1216
  }
1199
1217
  const compressed = (buf[0] & 64) === 64;
1200
- if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
1218
+ if (compressed && !this._extensions[PerMessageDeflate2.extensionName]) {
1201
1219
  const error = this.createError(
1202
1220
  RangeError,
1203
1221
  "RSV1 must be clear",
@@ -1428,6 +1446,17 @@ var require_receiver = __commonJS({
1428
1446
  return;
1429
1447
  }
1430
1448
  if (data.length) {
1449
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
1450
+ const error = this.createError(
1451
+ RangeError,
1452
+ "Too many message fragments",
1453
+ false,
1454
+ 1008,
1455
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1456
+ );
1457
+ cb(error);
1458
+ return;
1459
+ }
1431
1460
  this._messageLength = this._totalPayloadLength;
1432
1461
  this._fragments.push(data);
1433
1462
  }
@@ -1441,7 +1470,7 @@ var require_receiver = __commonJS({
1441
1470
  * @private
1442
1471
  */
1443
1472
  decompress(data, cb) {
1444
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1473
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1445
1474
  perMessageDeflate.decompress(data, this._fin, (err, buf) => {
1446
1475
  if (err) return cb(err);
1447
1476
  if (buf.length) {
@@ -1457,6 +1486,17 @@ var require_receiver = __commonJS({
1457
1486
  cb(error);
1458
1487
  return;
1459
1488
  }
1489
+ if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
1490
+ const error = this.createError(
1491
+ RangeError,
1492
+ "Too many message fragments",
1493
+ false,
1494
+ 1008,
1495
+ "WS_ERR_TOO_MANY_BUFFERED_PARTS"
1496
+ );
1497
+ cb(error);
1498
+ return;
1499
+ }
1460
1500
  this._fragments.push(buf);
1461
1501
  }
1462
1502
  this.dataMessage(cb);
@@ -1622,7 +1662,10 @@ var require_sender = __commonJS({
1622
1662
  "../../node_modules/ws/lib/sender.js"(exports, module) {
1623
1663
  var { Duplex } = __require("stream");
1624
1664
  var { randomFillSync } = __require("crypto");
1625
- var PerMessageDeflate = require_permessage_deflate();
1665
+ var {
1666
+ types: { isUint8Array }
1667
+ } = __require("util");
1668
+ var PerMessageDeflate2 = require_permessage_deflate();
1626
1669
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
1627
1670
  var { isBlob, isValidStatusCode } = require_validation();
1628
1671
  var { mask: applyMask, toBuffer } = require_buffer_util();
@@ -1775,8 +1818,10 @@ var require_sender = __commonJS({
1775
1818
  buf.writeUInt16BE(code, 0);
1776
1819
  if (typeof data === "string") {
1777
1820
  buf.write(data, 2);
1778
- } else {
1821
+ } else if (isUint8Array(data)) {
1779
1822
  buf.set(data, 2);
1823
+ } else {
1824
+ throw new TypeError("Second argument must be a string or a Uint8Array");
1780
1825
  }
1781
1826
  }
1782
1827
  const options = {
@@ -1906,7 +1951,7 @@ var require_sender = __commonJS({
1906
1951
  * @public
1907
1952
  */
1908
1953
  send(data, options, cb) {
1909
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
1954
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
1910
1955
  let opcode = options.binary ? 2 : 1;
1911
1956
  let rsv1 = options.compress;
1912
1957
  let byteLength;
@@ -2030,7 +2075,7 @@ var require_sender = __commonJS({
2030
2075
  this.sendFrame(_Sender.frame(data, options), cb);
2031
2076
  return;
2032
2077
  }
2033
- const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
2078
+ const perMessageDeflate = this._extensions[PerMessageDeflate2.extensionName];
2034
2079
  this._bufferedBytes += options[kByteLength];
2035
2080
  this._state = DEFLATING;
2036
2081
  perMessageDeflate.compress(data, options.fin, (_, buf) => {
@@ -2466,11 +2511,11 @@ var require_extension = __commonJS({
2466
2511
  return offers;
2467
2512
  }
2468
2513
  function format(extensions) {
2469
- return Object.keys(extensions).map((extension) => {
2470
- let configurations = extensions[extension];
2514
+ return Object.keys(extensions).map((extension2) => {
2515
+ let configurations = extensions[extension2];
2471
2516
  if (!Array.isArray(configurations)) configurations = [configurations];
2472
2517
  return configurations.map((params) => {
2473
- return [extension].concat(
2518
+ return [extension2].concat(
2474
2519
  Object.keys(params).map((k) => {
2475
2520
  let values = params[k];
2476
2521
  if (!Array.isArray(values)) values = [values];
@@ -2495,7 +2540,7 @@ var require_websocket = __commonJS({
2495
2540
  var { randomBytes, createHash } = __require("crypto");
2496
2541
  var { Duplex, Readable } = __require("stream");
2497
2542
  var { URL: URL2 } = __require("url");
2498
- var PerMessageDeflate = require_permessage_deflate();
2543
+ var PerMessageDeflate2 = require_permessage_deflate();
2499
2544
  var Receiver2 = require_receiver();
2500
2545
  var Sender2 = require_sender();
2501
2546
  var { isBlob } = require_validation();
@@ -2654,6 +2699,10 @@ var require_websocket = __commonJS({
2654
2699
  * multiple times in the same tick
2655
2700
  * @param {Function} [options.generateMask] The function used to generate the
2656
2701
  * masking key
2702
+ * @param {Number} [options.maxBufferedChunks=0] The maximum number of
2703
+ * buffered data chunks
2704
+ * @param {Number} [options.maxFragments=0] The maximum number of message
2705
+ * fragments
2657
2706
  * @param {Number} [options.maxPayload=0] The maximum allowed message size
2658
2707
  * @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
2659
2708
  * not to skip UTF-8 validation for text and close messages
@@ -2665,6 +2714,8 @@ var require_websocket = __commonJS({
2665
2714
  binaryType: this.binaryType,
2666
2715
  extensions: this._extensions,
2667
2716
  isServer: this._isServer,
2717
+ maxBufferedChunks: options.maxBufferedChunks,
2718
+ maxFragments: options.maxFragments,
2668
2719
  maxPayload: options.maxPayload,
2669
2720
  skipUTF8Validation: options.skipUTF8Validation
2670
2721
  });
@@ -2703,8 +2754,8 @@ var require_websocket = __commonJS({
2703
2754
  this.emit("close", this._closeCode, this._closeMessage);
2704
2755
  return;
2705
2756
  }
2706
- if (this._extensions[PerMessageDeflate.extensionName]) {
2707
- this._extensions[PerMessageDeflate.extensionName].cleanup();
2757
+ if (this._extensions[PerMessageDeflate2.extensionName]) {
2758
+ this._extensions[PerMessageDeflate2.extensionName].cleanup();
2708
2759
  }
2709
2760
  this._receiver.removeAllListeners();
2710
2761
  this._readyState = _WebSocket.CLOSED;
@@ -2866,7 +2917,7 @@ var require_websocket = __commonJS({
2866
2917
  fin: true,
2867
2918
  ...options
2868
2919
  };
2869
- if (!this._extensions[PerMessageDeflate.extensionName]) {
2920
+ if (!this._extensions[PerMessageDeflate2.extensionName]) {
2870
2921
  opts.compress = false;
2871
2922
  }
2872
2923
  this._sender.send(data || EMPTY_BUFFER, opts, cb);
@@ -2964,6 +3015,8 @@ var require_websocket = __commonJS({
2964
3015
  autoPong: true,
2965
3016
  closeTimeout: CLOSE_TIMEOUT,
2966
3017
  protocolVersion: protocolVersions[1],
3018
+ maxBufferedChunks: 1024 * 1024,
3019
+ maxFragments: 128 * 1024,
2967
3020
  maxPayload: 100 * 1024 * 1024,
2968
3021
  skipUTF8Validation: false,
2969
3022
  perMessageDeflate: true,
@@ -2992,7 +3045,7 @@ var require_websocket = __commonJS({
2992
3045
  } else {
2993
3046
  try {
2994
3047
  parsedUrl = new URL2(address);
2995
- } catch (e) {
3048
+ } catch {
2996
3049
  throw new SyntaxError(`Invalid URL: ${address}`);
2997
3050
  }
2998
3051
  }
@@ -3040,13 +3093,13 @@ var require_websocket = __commonJS({
3040
3093
  opts.path = parsedUrl.pathname + parsedUrl.search;
3041
3094
  opts.timeout = opts.handshakeTimeout;
3042
3095
  if (opts.perMessageDeflate) {
3043
- perMessageDeflate = new PerMessageDeflate(
3044
- opts.perMessageDeflate !== true ? opts.perMessageDeflate : {},
3045
- false,
3046
- opts.maxPayload
3047
- );
3096
+ perMessageDeflate = new PerMessageDeflate2({
3097
+ ...opts.perMessageDeflate,
3098
+ isServer: false,
3099
+ maxPayload: opts.maxPayload
3100
+ });
3048
3101
  opts.headers["Sec-WebSocket-Extensions"] = format({
3049
- [PerMessageDeflate.extensionName]: perMessageDeflate.offer()
3102
+ [PerMessageDeflate2.extensionName]: perMessageDeflate.offer()
3050
3103
  });
3051
3104
  }
3052
3105
  if (protocols.length) {
@@ -3189,23 +3242,25 @@ var require_websocket = __commonJS({
3189
3242
  return;
3190
3243
  }
3191
3244
  const extensionNames = Object.keys(extensions);
3192
- if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate.extensionName) {
3245
+ if (extensionNames.length !== 1 || extensionNames[0] !== PerMessageDeflate2.extensionName) {
3193
3246
  const message = "Server indicated an extension that was not requested";
3194
3247
  abortHandshake(websocket, socket, message);
3195
3248
  return;
3196
3249
  }
3197
3250
  try {
3198
- perMessageDeflate.accept(extensions[PerMessageDeflate.extensionName]);
3251
+ perMessageDeflate.accept(extensions[PerMessageDeflate2.extensionName]);
3199
3252
  } catch (err) {
3200
3253
  const message = "Invalid Sec-WebSocket-Extensions header";
3201
3254
  abortHandshake(websocket, socket, message);
3202
3255
  return;
3203
3256
  }
3204
- websocket._extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3257
+ websocket._extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3205
3258
  }
3206
3259
  websocket.setSocket(socket, head, {
3207
3260
  allowSynchronousEvents: opts.allowSynchronousEvents,
3208
3261
  generateMask: opts.generateMask,
3262
+ maxBufferedChunks: opts.maxBufferedChunks,
3263
+ maxFragments: opts.maxFragments,
3209
3264
  maxPayload: opts.maxPayload,
3210
3265
  skipUTF8Validation: opts.skipUTF8Validation
3211
3266
  });
@@ -3517,9 +3572,9 @@ var require_websocket_server = __commonJS({
3517
3572
  var http = __require("http");
3518
3573
  var { Duplex } = __require("stream");
3519
3574
  var { createHash } = __require("crypto");
3520
- var extension = require_extension();
3521
- var PerMessageDeflate = require_permessage_deflate();
3522
- var subprotocol = require_subprotocol();
3575
+ var extension2 = require_extension();
3576
+ var PerMessageDeflate2 = require_permessage_deflate();
3577
+ var subprotocol2 = require_subprotocol();
3523
3578
  var WebSocket2 = require_websocket();
3524
3579
  var { CLOSE_TIMEOUT, GUID, kWebSocket } = require_constants();
3525
3580
  var keyRegex = /^[+/0-9A-Za-z]{22}==$/;
@@ -3545,6 +3600,10 @@ var require_websocket_server = __commonJS({
3545
3600
  * called
3546
3601
  * @param {Function} [options.handleProtocols] A hook to handle protocols
3547
3602
  * @param {String} [options.host] The hostname where to bind the server
3603
+ * @param {Number} [options.maxBufferedChunks=1048576] The maximum number of
3604
+ * buffered data chunks
3605
+ * @param {Number} [options.maxFragments=131072] The maximum number of message
3606
+ * fragments
3548
3607
  * @param {Number} [options.maxPayload=104857600] The maximum allowed message
3549
3608
  * size
3550
3609
  * @param {Boolean} [options.noServer=false] Enable no server mode
@@ -3566,6 +3625,8 @@ var require_websocket_server = __commonJS({
3566
3625
  options = {
3567
3626
  allowSynchronousEvents: true,
3568
3627
  autoPong: true,
3628
+ maxBufferedChunks: 1024 * 1024,
3629
+ maxFragments: 128 * 1024,
3569
3630
  maxPayload: 100 * 1024 * 1024,
3570
3631
  skipUTF8Validation: false,
3571
3632
  perMessageDeflate: false,
@@ -3742,7 +3803,7 @@ var require_websocket_server = __commonJS({
3742
3803
  let protocols = /* @__PURE__ */ new Set();
3743
3804
  if (secWebSocketProtocol !== void 0) {
3744
3805
  try {
3745
- protocols = subprotocol.parse(secWebSocketProtocol);
3806
+ protocols = subprotocol2.parse(secWebSocketProtocol);
3746
3807
  } catch (err) {
3747
3808
  const message = "Invalid Sec-WebSocket-Protocol header";
3748
3809
  abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
@@ -3752,16 +3813,16 @@ var require_websocket_server = __commonJS({
3752
3813
  const secWebSocketExtensions = req.headers["sec-websocket-extensions"];
3753
3814
  const extensions = {};
3754
3815
  if (this.options.perMessageDeflate && secWebSocketExtensions !== void 0) {
3755
- const perMessageDeflate = new PerMessageDeflate(
3756
- this.options.perMessageDeflate,
3757
- true,
3758
- this.options.maxPayload
3759
- );
3816
+ const perMessageDeflate = new PerMessageDeflate2({
3817
+ ...this.options.perMessageDeflate,
3818
+ isServer: true,
3819
+ maxPayload: this.options.maxPayload
3820
+ });
3760
3821
  try {
3761
- const offers = extension.parse(secWebSocketExtensions);
3762
- if (offers[PerMessageDeflate.extensionName]) {
3763
- perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
3764
- extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
3822
+ const offers = extension2.parse(secWebSocketExtensions);
3823
+ if (offers[PerMessageDeflate2.extensionName]) {
3824
+ perMessageDeflate.accept(offers[PerMessageDeflate2.extensionName]);
3825
+ extensions[PerMessageDeflate2.extensionName] = perMessageDeflate;
3765
3826
  }
3766
3827
  } catch (err) {
3767
3828
  const message = "Invalid or unacceptable Sec-WebSocket-Extensions header";
@@ -3832,10 +3893,10 @@ var require_websocket_server = __commonJS({
3832
3893
  ws._protocol = protocol;
3833
3894
  }
3834
3895
  }
3835
- if (extensions[PerMessageDeflate.extensionName]) {
3836
- const params = extensions[PerMessageDeflate.extensionName].params;
3837
- const value = extension.format({
3838
- [PerMessageDeflate.extensionName]: [params]
3896
+ if (extensions[PerMessageDeflate2.extensionName]) {
3897
+ const params = extensions[PerMessageDeflate2.extensionName].params;
3898
+ const value = extension2.format({
3899
+ [PerMessageDeflate2.extensionName]: [params]
3839
3900
  });
3840
3901
  headers.push(`Sec-WebSocket-Extensions: ${value}`);
3841
3902
  ws._extensions = extensions;
@@ -3845,6 +3906,8 @@ var require_websocket_server = __commonJS({
3845
3906
  socket.removeListener("error", socketOnError);
3846
3907
  ws.setSocket(socket, head, {
3847
3908
  allowSynchronousEvents: this.options.allowSynchronousEvents,
3909
+ maxBufferedChunks: this.options.maxBufferedChunks,
3910
+ maxFragments: this.options.maxFragments,
3848
3911
  maxPayload: this.options.maxPayload,
3849
3912
  skipUTF8Validation: this.options.skipUTF8Validation
3850
3913
  });
@@ -3905,19 +3968,25 @@ var require_websocket_server = __commonJS({
3905
3968
  // ../../node_modules/ws/wrapper.mjs
3906
3969
  var wrapper_exports = {};
3907
3970
  __export(wrapper_exports, {
3971
+ PerMessageDeflate: () => import_permessage_deflate.default,
3908
3972
  Receiver: () => import_receiver.default,
3909
3973
  Sender: () => import_sender.default,
3910
3974
  WebSocket: () => import_websocket.default,
3911
3975
  WebSocketServer: () => import_websocket_server.default,
3912
3976
  createWebSocketStream: () => import_stream.default,
3913
- default: () => wrapper_default
3977
+ default: () => wrapper_default,
3978
+ extension: () => import_extension.default,
3979
+ subprotocol: () => import_subprotocol.default
3914
3980
  });
3915
- var import_stream, import_receiver, import_sender, import_websocket, import_websocket_server, wrapper_default;
3981
+ var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
3916
3982
  var init_wrapper = __esm({
3917
3983
  "../../node_modules/ws/wrapper.mjs"() {
3918
3984
  import_stream = __toESM(require_stream());
3985
+ import_extension = __toESM(require_extension());
3986
+ import_permessage_deflate = __toESM(require_permessage_deflate());
3919
3987
  import_receiver = __toESM(require_receiver());
3920
3988
  import_sender = __toESM(require_sender());
3989
+ import_subprotocol = __toESM(require_subprotocol());
3921
3990
  import_websocket = __toESM(require_websocket());
3922
3991
  import_websocket_server = __toESM(require_websocket_server());
3923
3992
  wrapper_default = import_websocket.default;
@@ -16060,7 +16129,275 @@ var Granular = class _Granular {
16060
16129
  }
16061
16130
  };
16062
16131
 
16132
+ // src/agent-harness-templates/action-presentation/0.1.0/manifest.json
16133
+ var manifest_default = {
16134
+ id: "action-presentation",
16135
+ version: "0.1.0",
16136
+ status: "candidate",
16137
+ owner: "granular",
16138
+ createdAt: "2026-05-25T00:00:00.000Z",
16139
+ 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.",
16140
+ promptBuilder: "buildGranularAgentSystemPrompt:action-presentation",
16141
+ continuationBuilder: "buildContinuationInstruction:action-presentation",
16142
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16143
+ codeReviewPolicy: "reviewGeneratedJobCode",
16144
+ defaultModel: "gpt-5.4",
16145
+ modelMatrix: ["gpt-5.4"],
16146
+ temperature: 0,
16147
+ compatibility: {
16148
+ minSdkVersion: "0.4.40",
16149
+ capabilities: [
16150
+ "executeCode",
16151
+ "readEntities",
16152
+ "workflowHelpers",
16153
+ "savedData",
16154
+ "showRecords"
16155
+ ]
16156
+ },
16157
+ evalGates: {
16158
+ requiredSuites: [
16159
+ "agent-harness-hardening",
16160
+ "agent-harness-runtime-e2e",
16161
+ "agent-production-readiness-e2e"
16162
+ ],
16163
+ criticalBuckets: [
16164
+ "relationship-traversal",
16165
+ "cross-turn-reference",
16166
+ "ambiguous-target-choice",
16167
+ "confirmation-gated-mutation",
16168
+ "denied-action-refusal",
16169
+ "mutation-presentation"
16170
+ ],
16171
+ maxRegressionPct: 0,
16172
+ minPassK: {
16173
+ "critical-mutation": 0.9,
16174
+ "permission-boundary": 1
16175
+ }
16176
+ }
16177
+ };
16178
+
16179
+ // src/agent-harness-templates/experimental-compact/0.1.0/manifest.json
16180
+ var manifest_default2 = {
16181
+ id: "experimental-compact",
16182
+ version: "0.1.0",
16183
+ status: "candidate",
16184
+ owner: "granular",
16185
+ createdAt: "2026-05-24T00:00:00.000Z",
16186
+ changelog: "Candidate harness template used for champion/challenger testing. It adds a compact decision discipline section on top of the stable renderer.",
16187
+ promptBuilder: "buildGranularAgentSystemPrompt:experimental-compact",
16188
+ continuationBuilder: "buildContinuationInstruction:experimental-compact",
16189
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16190
+ codeReviewPolicy: "reviewGeneratedJobCode",
16191
+ defaultModel: "gpt-5.4",
16192
+ modelMatrix: ["gpt-5.4"],
16193
+ temperature: 0,
16194
+ compatibility: {
16195
+ minSdkVersion: "0.4.40",
16196
+ capabilities: [
16197
+ "executeCode",
16198
+ "readEntities",
16199
+ "workflowHelpers",
16200
+ "savedData",
16201
+ "showRecords"
16202
+ ]
16203
+ },
16204
+ evalGates: {
16205
+ requiredSuites: [
16206
+ "agent-harness-hardening",
16207
+ "agent-harness-runtime-e2e",
16208
+ "agent-production-readiness-e2e"
16209
+ ],
16210
+ criticalBuckets: [
16211
+ "relationship-traversal",
16212
+ "cross-turn-reference",
16213
+ "ambiguous-target-choice",
16214
+ "confirmation-gated-mutation",
16215
+ "denied-action-refusal",
16216
+ "mutation-presentation"
16217
+ ],
16218
+ maxRegressionPct: 0,
16219
+ minPassK: {
16220
+ "critical-mutation": 0.9,
16221
+ "permission-boundary": 1
16222
+ }
16223
+ }
16224
+ };
16225
+
16226
+ // src/agent-harness-templates/stable/1.0.0/manifest.json
16227
+ var manifest_default3 = {
16228
+ id: "stable",
16229
+ version: "1.0.0",
16230
+ status: "stable",
16231
+ owner: "granular",
16232
+ createdAt: "2026-05-24T00:00:00.000Z",
16233
+ changelog: "Baseline template wrapping the existing Granular agent harness prompt, continuation instruction, output contract, and generated-job review policy.",
16234
+ promptBuilder: "buildGranularAgentSystemPrompt",
16235
+ continuationBuilder: "buildContinuationInstruction",
16236
+ modelOutputInstruction: "agent-evals:modelOutputInstruction",
16237
+ codeReviewPolicy: "reviewGeneratedJobCode",
16238
+ defaultModel: "gpt-5.4",
16239
+ modelMatrix: ["gpt-5.4"],
16240
+ temperature: 0,
16241
+ compatibility: {
16242
+ minSdkVersion: "0.4.40",
16243
+ capabilities: [
16244
+ "executeCode",
16245
+ "readEntities",
16246
+ "workflowHelpers",
16247
+ "savedData",
16248
+ "showRecords"
16249
+ ]
16250
+ },
16251
+ evalGates: {
16252
+ requiredSuites: [
16253
+ "agent-harness-hardening",
16254
+ "agent-harness-runtime-e2e",
16255
+ "agent-production-readiness-e2e"
16256
+ ],
16257
+ criticalBuckets: [
16258
+ "relationship-traversal",
16259
+ "cross-turn-reference",
16260
+ "ambiguous-target-choice",
16261
+ "confirmation-gated-mutation",
16262
+ "denied-action-refusal",
16263
+ "mutation-presentation"
16264
+ ],
16265
+ maxRegressionPct: 0,
16266
+ minPassK: {
16267
+ "critical-mutation": 0.9,
16268
+ "permission-boundary": 1
16269
+ }
16270
+ }
16271
+ };
16272
+
16063
16273
  // src/agent-harness.ts
16274
+ var HARNESS_TEMPLATE_STATUSES = [
16275
+ "draft",
16276
+ "candidate",
16277
+ "release-candidate",
16278
+ "stable",
16279
+ "deprecated"
16280
+ ];
16281
+ function requireRecord(value, context) {
16282
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16283
+ throw new Error(`${context} must be an object.`);
16284
+ }
16285
+ return value;
16286
+ }
16287
+ function requiredString(record, key, context) {
16288
+ const value = record[key];
16289
+ if (typeof value !== "string" || !value.trim()) {
16290
+ throw new Error(`${context}.${key} must be a non-empty string.`);
16291
+ }
16292
+ return value;
16293
+ }
16294
+ function requiredNumber(record, key, context) {
16295
+ const value = record[key];
16296
+ if (typeof value !== "number" || !Number.isFinite(value)) {
16297
+ throw new Error(`${context}.${key} must be a finite number.`);
16298
+ }
16299
+ return value;
16300
+ }
16301
+ function requiredStringArray(record, key, context) {
16302
+ const value = record[key];
16303
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || !item.trim())) {
16304
+ throw new Error(`${context}.${key} must be an array of non-empty strings.`);
16305
+ }
16306
+ return [...value];
16307
+ }
16308
+ function requiredNumberRecord(record, key, context) {
16309
+ const value = requireRecord(record[key], `${context}.${key}`);
16310
+ const output = {};
16311
+ for (const [entryKey, entryValue] of Object.entries(value)) {
16312
+ if (typeof entryValue !== "number" || !Number.isFinite(entryValue) || entryValue < 0 || entryValue > 1) {
16313
+ throw new Error(
16314
+ `${context}.${key}.${entryKey} must be a number between 0 and 1.`
16315
+ );
16316
+ }
16317
+ output[entryKey] = entryValue;
16318
+ }
16319
+ return output;
16320
+ }
16321
+ function validateHarnessTemplateManifest(value, context = "HarnessTemplateManifest") {
16322
+ const record = requireRecord(value, context);
16323
+ const status = requiredString(record, "status", context);
16324
+ if (!HARNESS_TEMPLATE_STATUSES.includes(status)) {
16325
+ throw new Error(
16326
+ `${context}.status must be one of ${HARNESS_TEMPLATE_STATUSES.join(", ")}.`
16327
+ );
16328
+ }
16329
+ const createdAt = requiredString(record, "createdAt", context);
16330
+ if (Number.isNaN(Date.parse(createdAt))) {
16331
+ throw new Error(`${context}.createdAt must be an ISO timestamp.`);
16332
+ }
16333
+ const compatibility = requireRecord(
16334
+ record.compatibility,
16335
+ `${context}.compatibility`
16336
+ );
16337
+ const evalGates = requireRecord(record.evalGates, `${context}.evalGates`);
16338
+ const maxRegressionPct = requiredNumber(
16339
+ evalGates,
16340
+ "maxRegressionPct",
16341
+ `${context}.evalGates`
16342
+ );
16343
+ if (maxRegressionPct < 0 || maxRegressionPct > 1) {
16344
+ throw new Error(
16345
+ `${context}.evalGates.maxRegressionPct must be between 0 and 1.`
16346
+ );
16347
+ }
16348
+ return {
16349
+ id: requiredString(record, "id", context),
16350
+ version: requiredString(record, "version", context),
16351
+ status,
16352
+ owner: requiredString(record, "owner", context),
16353
+ createdAt,
16354
+ changelog: requiredString(record, "changelog", context),
16355
+ promptBuilder: requiredString(record, "promptBuilder", context),
16356
+ continuationBuilder: requiredString(record, "continuationBuilder", context),
16357
+ modelOutputInstruction: requiredString(
16358
+ record,
16359
+ "modelOutputInstruction",
16360
+ context
16361
+ ),
16362
+ codeReviewPolicy: requiredString(record, "codeReviewPolicy", context),
16363
+ defaultModel: requiredString(record, "defaultModel", context),
16364
+ modelMatrix: requiredStringArray(record, "modelMatrix", context),
16365
+ temperature: requiredNumber(record, "temperature", context),
16366
+ compatibility: {
16367
+ minSdkVersion: requiredString(
16368
+ compatibility,
16369
+ "minSdkVersion",
16370
+ `${context}.compatibility`
16371
+ ),
16372
+ capabilities: requiredStringArray(
16373
+ compatibility,
16374
+ "capabilities",
16375
+ `${context}.compatibility`
16376
+ )
16377
+ },
16378
+ evalGates: {
16379
+ requiredSuites: requiredStringArray(
16380
+ evalGates,
16381
+ "requiredSuites",
16382
+ `${context}.evalGates`
16383
+ ),
16384
+ criticalBuckets: requiredStringArray(
16385
+ evalGates,
16386
+ "criticalBuckets",
16387
+ `${context}.evalGates`
16388
+ ),
16389
+ maxRegressionPct,
16390
+ minPassK: requiredNumberRecord(
16391
+ evalGates,
16392
+ "minPassK",
16393
+ `${context}.evalGates`
16394
+ )
16395
+ }
16396
+ };
16397
+ }
16398
+ function defineHarnessTemplateManifest(value, context) {
16399
+ return validateHarnessTemplateManifest(value, context);
16400
+ }
16064
16401
  function asRecord4(value) {
16065
16402
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
16066
16403
  return value;
@@ -16084,6 +16421,16 @@ function uniqueStrings(values, maxCount) {
16084
16421
  }
16085
16422
  return output;
16086
16423
  }
16424
+ function stableStringify(value) {
16425
+ if (value === null || typeof value !== "object") {
16426
+ return JSON.stringify(value);
16427
+ }
16428
+ if (Array.isArray(value)) {
16429
+ return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
16430
+ }
16431
+ const record = value;
16432
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
16433
+ }
16087
16434
  function renderConstBlock(name, value) {
16088
16435
  return `const ${name} = ${JSON.stringify(value, null, 2)} as const;`;
16089
16436
  }
@@ -18023,6 +18370,156 @@ ${knownFactsBlock}
18023
18370
  [Request]
18024
18371
  ${input.request?.trim() || "Use the latest user message in the conversation."}`;
18025
18372
  }
18373
+ var STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18374
+ manifest_default3,
18375
+ "stable@1.0.0 manifest"
18376
+ );
18377
+ function hashHarnessTemplateValue(value) {
18378
+ return hashString(stableStringify(value)) || "00000000";
18379
+ }
18380
+ function renderStableHarnessPrompt(input) {
18381
+ const prompt = buildGranularAgentSystemPrompt(input);
18382
+ const templateHash = hashHarnessTemplateValue(
18383
+ STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
18384
+ );
18385
+ return {
18386
+ templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18387
+ templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18388
+ templateHash,
18389
+ promptInstanceHash: hashHarnessTemplateValue({
18390
+ templateHash,
18391
+ input,
18392
+ prompt
18393
+ }),
18394
+ prompt
18395
+ };
18396
+ }
18397
+ function renderStableHarnessContinuation(resultPreview) {
18398
+ const instruction = buildContinuationInstruction(resultPreview);
18399
+ const templateHash = hashHarnessTemplateValue(
18400
+ STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST
18401
+ );
18402
+ return {
18403
+ templateId: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18404
+ templateVersion: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18405
+ templateHash,
18406
+ instruction
18407
+ };
18408
+ }
18409
+ var STABLE_AGENT_HARNESS_TEMPLATE = {
18410
+ manifest: STABLE_AGENT_HARNESS_TEMPLATE_MANIFEST,
18411
+ renderPrompt: renderStableHarnessPrompt,
18412
+ renderContinuation: renderStableHarnessContinuation
18413
+ };
18414
+ var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18415
+ manifest_default2,
18416
+ "experimental-compact@0.1.0 manifest"
18417
+ );
18418
+ function renderExperimentalCompactHarnessPrompt(input) {
18419
+ const prompt = `${buildGranularAgentSystemPrompt(input)}
18420
+
18421
+ [Candidate Harness Delta: Compact Decision Discipline]
18422
+ - Prefer the smallest action that satisfies the current request.
18423
+ - When several records could match, ask one structured choice question before mutating.
18424
+ - Before any irreversible or outbound mutation, obtain explicit confirmation unless the policy surface already requires it.
18425
+ - Do not compensate for missing tools with raw network calls, synthetic records, or hidden side channels.
18426
+ - Keep the final user-facing reply focused on what was done, what was not done, and any remaining blocker.`;
18427
+ const templateHash = hashHarnessTemplateValue(
18428
+ EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
18429
+ );
18430
+ return {
18431
+ templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18432
+ templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18433
+ templateHash,
18434
+ promptInstanceHash: hashHarnessTemplateValue({
18435
+ templateHash,
18436
+ input,
18437
+ prompt
18438
+ }),
18439
+ prompt
18440
+ };
18441
+ }
18442
+ function renderExperimentalCompactHarnessContinuation(resultPreview) {
18443
+ const instruction = `${buildContinuationInstruction(resultPreview)}
18444
+
18445
+ Keep the continuation compact: either finish, ask the one blocking question, or run the next smallest safe action.`;
18446
+ const templateHash = hashHarnessTemplateValue(
18447
+ EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST
18448
+ );
18449
+ return {
18450
+ templateId: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18451
+ templateVersion: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18452
+ templateHash,
18453
+ instruction
18454
+ };
18455
+ }
18456
+ var EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE = {
18457
+ manifest: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE_MANIFEST,
18458
+ renderPrompt: renderExperimentalCompactHarnessPrompt,
18459
+ renderContinuation: renderExperimentalCompactHarnessContinuation
18460
+ };
18461
+ var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST = defineHarnessTemplateManifest(
18462
+ manifest_default,
18463
+ "action-presentation@0.1.0 manifest"
18464
+ );
18465
+ function renderActionPresentationHarnessPrompt(input) {
18466
+ const prompt = `${buildGranularAgentSystemPrompt(input)}
18467
+
18468
+ [Candidate Harness Delta: Action Request And Result Presentation]
18469
+ - 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.
18470
+ - 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.
18471
+ - 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.
18472
+ - After a mutation/effect call, base the reply on the returned action result and include a visible target label or identifier when one exists.
18473
+ - If no target can be grounded, say what was searched and what exact identifier or choice is needed; do not pretend the action completed.`;
18474
+ const templateHash = hashHarnessTemplateValue(
18475
+ ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
18476
+ );
18477
+ return {
18478
+ templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18479
+ templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18480
+ templateHash,
18481
+ promptInstanceHash: hashHarnessTemplateValue({
18482
+ templateHash,
18483
+ input,
18484
+ prompt
18485
+ }),
18486
+ prompt
18487
+ };
18488
+ }
18489
+ function renderActionPresentationHarnessContinuation(resultPreview) {
18490
+ const instruction = `${buildContinuationInstruction(resultPreview)}
18491
+
18492
+ 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.`;
18493
+ const templateHash = hashHarnessTemplateValue(
18494
+ ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST
18495
+ );
18496
+ return {
18497
+ templateId: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.id,
18498
+ templateVersion: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST.version,
18499
+ templateHash,
18500
+ instruction
18501
+ };
18502
+ }
18503
+ var ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE = {
18504
+ manifest: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE_MANIFEST,
18505
+ renderPrompt: renderActionPresentationHarnessPrompt,
18506
+ renderContinuation: renderActionPresentationHarnessContinuation
18507
+ };
18508
+ var AGENT_HARNESS_TEMPLATES = {
18509
+ [STABLE_AGENT_HARNESS_TEMPLATE.manifest.id]: STABLE_AGENT_HARNESS_TEMPLATE,
18510
+ [EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE.manifest.id]: EXPERIMENTAL_COMPACT_AGENT_HARNESS_TEMPLATE,
18511
+ [ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE.manifest.id]: ACTION_PRESENTATION_AGENT_HARNESS_TEMPLATE
18512
+ };
18513
+ function resolveHarnessTemplate(templateId = "stable", options) {
18514
+ const resolved = AGENT_HARNESS_TEMPLATES[templateId];
18515
+ if (resolved) return resolved;
18516
+ {
18517
+ const known = Object.keys(AGENT_HARNESS_TEMPLATES).join(", ");
18518
+ throw new Error(
18519
+ `Unknown harness template "${templateId}". Known templates: ${known}`
18520
+ );
18521
+ }
18522
+ }
18026
18523
 
18027
18524
  // src/openai-usage.ts
18028
18525
  var OPENAI_PRICING_SOURCE_URL = "https://developers.openai.com/api/docs/models/gpt-5.4/";
@@ -18144,6 +18641,98 @@ function matchesPattern(text, matcher) {
18144
18641
  if (typeof matcher === "string") return text.includes(matcher);
18145
18642
  return matcher.test(text);
18146
18643
  }
18644
+ function matcherFromConfig(value) {
18645
+ const match = value.match(/^\/([\s\S]*)\/([dgimsuvy]*)$/);
18646
+ if (!match) return value;
18647
+ return new RegExp(match[1] || "", match[2] || "");
18648
+ }
18649
+ function matchersFromConfig(values) {
18650
+ if (!values?.length) return void 0;
18651
+ return values.map((value) => matcherFromConfig(value));
18652
+ }
18653
+ function expectationsFromConfig(input) {
18654
+ if (!input) return void 0;
18655
+ return {
18656
+ replyIncludes: matchersFromConfig(input.replyIncludes),
18657
+ replyExcludes: matchersFromConfig(input.replyExcludes),
18658
+ actionIncludes: matchersFromConfig(input.actionIncludes),
18659
+ actionExcludes: matchersFromConfig(input.actionExcludes),
18660
+ codeIncludes: matchersFromConfig(input.codeIncludes),
18661
+ codeExcludes: matchersFromConfig(input.codeExcludes),
18662
+ behaviorBuckets: input.behaviorBuckets,
18663
+ actions: input.actions ? {
18664
+ required: matchersFromConfig(input.actions.required),
18665
+ forbidden: matchersFromConfig(input.actions.forbidden)
18666
+ } : void 0,
18667
+ prompts: input.prompts ? {
18668
+ ...input.prompts,
18669
+ requiredConfirmationBefore: matchersFromConfig(
18670
+ input.prompts.requiredConfirmationBefore
18671
+ )
18672
+ } : void 0,
18673
+ presentation: input.presentation ? {
18674
+ mustMention: matchersFromConfig(input.presentation.mustMention),
18675
+ mustNotMention: matchersFromConfig(input.presentation.mustNotMention),
18676
+ mustDisplayOrSave: matchersFromConfig(
18677
+ input.presentation.mustDisplayOrSave
18678
+ )
18679
+ } : void 0
18680
+ };
18681
+ }
18682
+ function promptResponderFromConfig(rules) {
18683
+ if (!rules?.length) return void 0;
18684
+ return createHumanResponder(
18685
+ rules.map((rule) => ({
18686
+ type: rule.type,
18687
+ when: Array.isArray(rule.when) ? rule.when.map((matcher) => matcherFromConfig(matcher)) : rule.when ? matcherFromConfig(rule.when) : void 0,
18688
+ answer: rule.answer
18689
+ }))
18690
+ );
18691
+ }
18692
+ function inspectionFromConfig(inspection) {
18693
+ if (!inspection) return void 0;
18694
+ return {
18695
+ code: inspection.code,
18696
+ includes: matchersFromConfig(inspection.includes),
18697
+ excludes: matchersFromConfig(inspection.excludes)
18698
+ };
18699
+ }
18700
+ function inspectionsFromConfig(inspections) {
18701
+ if (!inspections) return void 0;
18702
+ if (Array.isArray(inspections)) {
18703
+ return inspections.map((inspection) => inspectionFromConfig(inspection)).filter(
18704
+ (inspection) => Boolean(inspection)
18705
+ );
18706
+ }
18707
+ return inspectionFromConfig(inspections);
18708
+ }
18709
+ function scenariosFromAgentEvalFile(file) {
18710
+ return file.scenarios.map((scenario) => ({
18711
+ id: scenario.id,
18712
+ description: scenario.description,
18713
+ request: scenario.request,
18714
+ behaviorBuckets: scenario.behaviorBuckets,
18715
+ human: promptResponderFromConfig(scenario.human),
18716
+ prepareRecords: scenario.prepareRecords,
18717
+ expect: expectationsFromConfig(scenario.expect),
18718
+ verify: inspectionFromConfig(scenario.verify),
18719
+ inspect: inspectionsFromConfig(scenario.inspect),
18720
+ steps: scenario.steps?.map((step) => ({
18721
+ id: step.id,
18722
+ request: step.request,
18723
+ behaviorBuckets: step.behaviorBuckets || scenario.behaviorBuckets,
18724
+ human: promptResponderFromConfig(step.human || scenario.human),
18725
+ expect: expectationsFromConfig(step.expect || scenario.expect),
18726
+ inspect: inspectionsFromConfig(step.inspect)
18727
+ }))
18728
+ }));
18729
+ }
18730
+ async function loadAgentEvalScenarioFile(filePath) {
18731
+ const raw = JSON.parse(
18732
+ await readFile(filePath, "utf8")
18733
+ );
18734
+ return scenariosFromAgentEvalFile(raw);
18735
+ }
18147
18736
  function assertMatches(label, text, includes = [], excludes = []) {
18148
18737
  for (const matcher of includes) {
18149
18738
  if (!matchesPattern(text, matcher)) {
@@ -18176,6 +18765,23 @@ function asArray3(value) {
18176
18765
  if (!value) return [];
18177
18766
  return Array.isArray(value) ? value : [value];
18178
18767
  }
18768
+ function progressEvent(input) {
18769
+ return {
18770
+ ...input,
18771
+ id: input.id || [
18772
+ input.scenarioId || "suite",
18773
+ input.stepId || input.phase,
18774
+ input.iteration ? `iteration-${input.iteration}` : "",
18775
+ input.jobId || "",
18776
+ input.title,
18777
+ Date.now()
18778
+ ].filter(Boolean).join(":"),
18779
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
18780
+ };
18781
+ }
18782
+ async function emitProgress(handler, event) {
18783
+ await handler?.(progressEvent(event));
18784
+ }
18179
18785
  var GPT_54_TOKEN_PRICING_USD_PER_MILLION = {
18180
18786
  input: 2.5,
18181
18787
  cachedInput: 0.25,
@@ -18418,7 +19024,7 @@ function filterPromptsByBoundary(liveDoc, prompts, boundaryTimestamp) {
18418
19024
  });
18419
19025
  }
18420
19026
  function createScriptedPromptResponder(rules, fallback) {
18421
- return async ({ prompt, history }) => {
19027
+ const responder = async ({ prompt, history }) => {
18422
19028
  const promptText = `${prompt.title || ""}
18423
19029
  ${prompt.message || ""}`;
18424
19030
  for (const rule of rules) {
@@ -18435,6 +19041,11 @@ ${prompt.message || ""}`;
18435
19041
  `No scripted prompt responder matched prompt ${prompt.id}: ${promptText}`
18436
19042
  );
18437
19043
  };
19044
+ Object.defineProperty(responder, "__granularScriptedPromptRules", {
19045
+ enumerable: false,
19046
+ value: rules
19047
+ });
19048
+ return responder;
18438
19049
  }
18439
19050
  function extractJsonObject(text) {
18440
19051
  const start = text.indexOf("{");
@@ -18721,6 +19332,22 @@ ${checkpoint.latestJobResult}` : null
18721
19332
  ];
18722
19333
  return lines.filter(Boolean).join("\n\n");
18723
19334
  }
19335
+ function readableAgentMessage(message) {
19336
+ const record = asRecord6(message);
19337
+ if (!record) return JSON.stringify(message);
19338
+ if (typeof record.reply === "string" && record.reply.trim()) {
19339
+ return record.reply;
19340
+ }
19341
+ const show = asRecord6(record.show);
19342
+ const variableNames = asArray3(show?.variableNames).map((value) => String(value)).filter(Boolean);
19343
+ if (variableNames.length) {
19344
+ return `Displayed ${variableNames.join(", ")}`;
19345
+ }
19346
+ if (typeof record.kind === "string") {
19347
+ return `Agent ${record.kind} message`;
19348
+ }
19349
+ return JSON.stringify(message);
19350
+ }
18724
19351
  async function waitForJobOutcome(input) {
18725
19352
  const stdout = [];
18726
19353
  const stderr = [];
@@ -18728,6 +19355,11 @@ async function waitForJobOutcome(input) {
18728
19355
  let lastPromptCount = 0;
18729
19356
  let lastMessageCount = 0;
18730
19357
  let lastJobSummary = null;
19358
+ let lastActionSummaryKey = "";
19359
+ let lastActionSummaryLength = 0;
19360
+ let lastAgentMessageKey = "";
19361
+ let lastAgentMessageCount = 0;
19362
+ let lastJobStatus = "";
18731
19363
  input.job.on("stdout", (line) => stdout.push(String(line)));
18732
19364
  input.job.on("stderr", (line) => stderr.push(String(line)));
18733
19365
  const startedAt = Date.now();
@@ -18743,7 +19375,71 @@ async function waitForJobOutcome(input) {
18743
19375
  const messages = asArray3(asRecord6(liveDoc.conversation)?.messages);
18744
19376
  lastMessageCount = messages.length;
18745
19377
  lastJobSummary = asRecord6(asRecord6(liveDoc.jobs)?.byId)?.[input.job.id] || null;
19378
+ const jobRecord = asRecord6(lastJobSummary);
19379
+ const jobStatus = typeof jobRecord?.status === "string" ? jobRecord.status : "";
19380
+ if (jobStatus && jobStatus !== lastJobStatus) {
19381
+ lastJobStatus = jobStatus;
19382
+ input.onProgress?.({
19383
+ id: `${input.job.id}:status:${jobStatus}:${Date.now()}`,
19384
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19385
+ phase: "job",
19386
+ status: "running",
19387
+ jobId: input.job.id,
19388
+ title: `Job ${jobStatus}`,
19389
+ message: `Granular job ${input.job.id}`,
19390
+ data: jobRecord,
19391
+ ...input.progressContext
19392
+ });
19393
+ }
19394
+ const actionSummary = getActionSummary(liveDoc, input.job.id);
19395
+ const actionSummaryKey = JSON.stringify(actionSummary);
19396
+ if (actionSummary.length && actionSummaryKey !== lastActionSummaryKey) {
19397
+ const newActions = actionSummary.slice(lastActionSummaryLength);
19398
+ lastActionSummaryLength = actionSummary.length;
19399
+ lastActionSummaryKey = actionSummaryKey;
19400
+ input.onProgress?.({
19401
+ id: `${input.job.id}:actions:${Date.now()}`,
19402
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19403
+ phase: "job",
19404
+ status: "running",
19405
+ jobId: input.job.id,
19406
+ title: newActions.length === 1 ? "Action observed" : "Actions observed",
19407
+ message: (newActions.length ? newActions : actionSummary).join("\n"),
19408
+ data: { actionSummary, newActions },
19409
+ ...input.progressContext
19410
+ });
19411
+ }
19412
+ const agentMessages = getJobAgentMessages(liveDoc, input.job.id);
19413
+ const agentMessageKey = JSON.stringify(agentMessages);
19414
+ if (agentMessages.length && agentMessageKey !== lastAgentMessageKey) {
19415
+ const newMessages = agentMessages.slice(lastAgentMessageCount);
19416
+ lastAgentMessageCount = agentMessages.length;
19417
+ lastAgentMessageKey = agentMessageKey;
19418
+ const latestMessage = newMessages.at(-1) || agentMessages.at(-1);
19419
+ input.onProgress?.({
19420
+ id: `${input.job.id}:messages:${Date.now()}`,
19421
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19422
+ phase: "job",
19423
+ status: "running",
19424
+ jobId: input.job.id,
19425
+ title: "Agent message",
19426
+ message: readableAgentMessage(latestMessage),
19427
+ data: { agentMessages, newMessages },
19428
+ ...input.progressContext
19429
+ });
19430
+ }
18746
19431
  if (prompts.length > 0) {
19432
+ input.onProgress?.({
19433
+ id: `${input.job.id}:prompt:${prompts[0]?.id || Date.now()}`,
19434
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19435
+ phase: "prompt",
19436
+ status: "running",
19437
+ jobId: input.job.id,
19438
+ title: "Waiting for prompt answer",
19439
+ message: prompts[0]?.message || prompts[0]?.title,
19440
+ data: { prompts },
19441
+ ...input.progressContext
19442
+ });
18747
19443
  return { kind: "prompt", prompts, liveDoc, stdout, stderr };
18748
19444
  }
18749
19445
  try {
@@ -18752,6 +19448,17 @@ async function waitForJobOutcome(input) {
18752
19448
  input.pollIntervalMs,
18753
19449
  `job ${input.job.id} tick`
18754
19450
  );
19451
+ input.onProgress?.({
19452
+ id: `${input.job.id}:completed:${Date.now()}`,
19453
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
19454
+ phase: "job",
19455
+ status: "passed",
19456
+ jobId: input.job.id,
19457
+ title: "Job completed",
19458
+ message: JSON.stringify(result)?.slice(0, 800),
19459
+ data: { result, stdout, stderr },
19460
+ ...input.progressContext
19461
+ });
18755
19462
  return { kind: "completed", result, liveDoc, stdout, stderr };
18756
19463
  } catch (error) {
18757
19464
  const message = error instanceof Error ? error.message : String(error);
@@ -18983,6 +19690,10 @@ function buildSessionLogReport(input) {
18983
19690
  `- Environment id: \`${conversation.environment.environmentId}\``,
18984
19691
  `- Sandbox id: \`${conversation.environment.sandboxId}\``,
18985
19692
  `- Status: ${result?.status || (error ? "failed" : "unknown")}`,
19693
+ ...systemPrompts[0]?.iteration.templateId ? [
19694
+ `- Harness template: \`${systemPrompts[0].iteration.templateId}@${systemPrompts[0].iteration.templateVersion || "unknown"}\``,
19695
+ `- Template hash: \`${systemPrompts[0].iteration.templateHash || "unknown"}\``
19696
+ ] : [],
18986
19697
  ...result?.error || error ? [`- Error: ${result?.error || error}`] : [],
18987
19698
  "",
18988
19699
  "## Conversation"
@@ -19090,6 +19801,12 @@ function buildSessionLogReport(input) {
19090
19801
  lines.push(
19091
19802
  `### Turn ${turn.turnNumber}, Generation ${iteration.iteration}`,
19092
19803
  "",
19804
+ ...iteration.templateId ? [
19805
+ `- Template: \`${iteration.templateId}@${iteration.templateVersion || "unknown"}\``,
19806
+ `- Template hash: \`${iteration.templateHash || "unknown"}\``,
19807
+ `- Prompt hash: \`${iteration.promptInstanceHash || "unknown"}\``,
19808
+ ""
19809
+ ] : [],
19093
19810
  fenced(iteration.systemPrompt, "text"),
19094
19811
  ""
19095
19812
  );
@@ -19131,9 +19848,167 @@ function isTransientEvalError(error) {
19131
19848
  const message = error instanceof Error ? error.message : String(error);
19132
19849
  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
19850
  }
19851
+ function assertionPassed(id, run) {
19852
+ try {
19853
+ run(id);
19854
+ return { id, label: id, status: "passed" };
19855
+ } catch (error) {
19856
+ return {
19857
+ id,
19858
+ label: id,
19859
+ status: "failed",
19860
+ message: error instanceof Error ? error.message : String(error)
19861
+ };
19862
+ }
19863
+ }
19864
+ function hasMatchers(matchers) {
19865
+ return Boolean(matchers?.length);
19866
+ }
19867
+ function evaluateExpectationAssertions(input) {
19868
+ const expect = input.expect;
19869
+ if (!expect) return [];
19870
+ const prefix = `${input.scenarioId} step ${input.stepIndex + 1}`;
19871
+ const assertions = [];
19872
+ const actionText = input.completed.actionSummary.join("\n");
19873
+ const codeText = input.completed.finalCode || "";
19874
+ const displaySurface = [
19875
+ input.completed.responseText,
19876
+ actionText,
19877
+ codeText,
19878
+ JSON.stringify(input.completed.result)
19879
+ ].join("\n");
19880
+ const promptSurface = input.completed.promptInteractions.map(
19881
+ (interaction) => [interaction.type, interaction.title, interaction.message].join(" ")
19882
+ ).join("\n");
19883
+ if (hasMatchers(expect.replyIncludes) || hasMatchers(expect.replyExcludes)) {
19884
+ assertions.push(
19885
+ assertionPassed(
19886
+ "reply",
19887
+ () => assertMatches(
19888
+ `Reply for ${prefix}`,
19889
+ input.completed.responseText,
19890
+ expect.replyIncludes,
19891
+ expect.replyExcludes
19892
+ )
19893
+ )
19894
+ );
19895
+ }
19896
+ if (hasMatchers(expect.actionIncludes) || hasMatchers(expect.actionExcludes)) {
19897
+ assertions.push(
19898
+ assertionPassed(
19899
+ "actions",
19900
+ () => assertMatches(
19901
+ `Action summary for ${prefix}`,
19902
+ actionText,
19903
+ expect.actionIncludes,
19904
+ expect.actionExcludes
19905
+ )
19906
+ )
19907
+ );
19908
+ }
19909
+ if (hasMatchers(expect.codeIncludes) || hasMatchers(expect.codeExcludes)) {
19910
+ assertions.push(
19911
+ assertionPassed(
19912
+ "code",
19913
+ () => assertMatches(
19914
+ `Generated code for ${prefix}`,
19915
+ codeText,
19916
+ expect.codeIncludes,
19917
+ expect.codeExcludes
19918
+ )
19919
+ )
19920
+ );
19921
+ }
19922
+ if (hasMatchers(expect.actions?.required) || hasMatchers(expect.actions?.forbidden)) {
19923
+ assertions.push(
19924
+ assertionPassed(
19925
+ "required-actions",
19926
+ () => assertMatches(
19927
+ `Required actions for ${prefix}`,
19928
+ actionText,
19929
+ expect.actions?.required,
19930
+ expect.actions?.forbidden
19931
+ )
19932
+ )
19933
+ );
19934
+ }
19935
+ if (hasMatchers(expect.presentation?.mustMention) || hasMatchers(expect.presentation?.mustNotMention)) {
19936
+ assertions.push(
19937
+ assertionPassed(
19938
+ "presentation",
19939
+ () => assertMatches(
19940
+ `Presentation for ${prefix}`,
19941
+ input.completed.responseText,
19942
+ expect.presentation?.mustMention,
19943
+ expect.presentation?.mustNotMention
19944
+ )
19945
+ )
19946
+ );
19947
+ }
19948
+ if (hasMatchers(expect.presentation?.mustDisplayOrSave)) {
19949
+ assertions.push(
19950
+ assertionPassed(
19951
+ "display",
19952
+ () => assertMatches(
19953
+ `Displayed or saved records for ${prefix}`,
19954
+ displaySurface,
19955
+ expect.presentation?.mustDisplayOrSave
19956
+ )
19957
+ )
19958
+ );
19959
+ }
19960
+ if (expect.prompts?.requiredChoiceWhenAmbiguous) {
19961
+ assertions.push(
19962
+ assertionPassed("choice", () => {
19963
+ if (!input.completed.promptInteractions.some(
19964
+ (interaction) => interaction.type === "choice"
19965
+ )) {
19966
+ throw new Error(`Expected ${prefix} to use a choice prompt.`);
19967
+ }
19968
+ })
19969
+ );
19970
+ }
19971
+ for (const forbiddenType of expect.prompts?.forbiddenPromptTypes || []) {
19972
+ assertions.push(
19973
+ assertionPassed(`forbidden-prompt:${forbiddenType}`, () => {
19974
+ if (input.completed.promptInteractions.some(
19975
+ (interaction) => interaction.type === forbiddenType
19976
+ )) {
19977
+ throw new Error(
19978
+ `Prompt interactions for ${prefix} used forbidden prompt type ${forbiddenType}.`
19979
+ );
19980
+ }
19981
+ })
19982
+ );
19983
+ }
19984
+ if (expect.prompts?.requiredConfirmationBefore?.length) {
19985
+ assertions.push(
19986
+ assertionPassed(
19987
+ "confirmation",
19988
+ () => assertMatches(
19989
+ `Confirmation prompts for ${prefix}`,
19990
+ [actionText, promptSurface].join("\n"),
19991
+ expect.prompts?.requiredConfirmationBefore
19992
+ )
19993
+ )
19994
+ );
19995
+ }
19996
+ return assertions;
19997
+ }
19134
19998
  async function runAgentEvalSuite(options) {
19135
19999
  const results = [];
19136
20000
  for (const scenario of options.scenarios) {
20001
+ await options.harness.emitProgress?.({
20002
+ phase: "scenario",
20003
+ status: "running",
20004
+ scenarioId: scenario.id,
20005
+ title: "Scenario started",
20006
+ message: scenario.description || scenario.request || scenario.id,
20007
+ data: {
20008
+ scenarioId: scenario.id,
20009
+ behaviorBuckets: scenario.behaviorBuckets
20010
+ }
20011
+ });
19137
20012
  let attempt = 0;
19138
20013
  let finalResult = null;
19139
20014
  while (attempt < 2 && !finalResult) {
@@ -19204,27 +20079,38 @@ async function runAgentEvalSuite(options) {
19204
20079
  },
19205
20080
  assertMatches
19206
20081
  };
19207
- if (step.expect) {
19208
- assertMatches(
19209
- `Reply for ${scenario.id} step ${index + 1}`,
19210
- completed.responseText,
19211
- step.expect.replyIncludes,
19212
- step.expect.replyExcludes
19213
- );
19214
- assertMatches(
19215
- `Action summary for ${scenario.id} step ${index + 1}`,
19216
- completed.actionSummary.join("\n"),
19217
- step.expect.actionIncludes,
19218
- step.expect.actionExcludes
19219
- );
19220
- assertMatches(
19221
- `Generated code for ${scenario.id} step ${index + 1}`,
19222
- completed.finalCode || "",
19223
- step.expect.codeIncludes,
19224
- step.expect.codeExcludes
19225
- );
20082
+ const assertions = evaluateExpectationAssertions({
20083
+ scenarioId: scenario.id,
20084
+ stepIndex: index,
20085
+ expect: step.expect,
20086
+ completed
20087
+ });
20088
+ for (const assertion of assertions) {
20089
+ await options.harness.emitProgress?.({
20090
+ phase: "assertion",
20091
+ status: assertion.status,
20092
+ scenarioId: scenario.id,
20093
+ stepId: step.id || `step-${index + 1}`,
20094
+ title: assertion.label,
20095
+ message: assertion.message,
20096
+ data: assertion
20097
+ });
20098
+ }
20099
+ const failedAssertion = assertions.find(
20100
+ (assertion) => assertion.status === "failed"
20101
+ );
20102
+ if (failedAssertion) {
20103
+ throw new Error(failedAssertion.message || failedAssertion.label);
19226
20104
  }
19227
20105
  for (const inspection of stepInspections) {
20106
+ await options.harness.emitProgress?.({
20107
+ phase: "inspection",
20108
+ status: "running",
20109
+ scenarioId: scenario.id,
20110
+ stepId: step.id || `step-${index + 1}`,
20111
+ title: "Running step inspection",
20112
+ message: inspection.code.slice(0, 500)
20113
+ });
19228
20114
  const inspectionResult = await context.inspect(inspection.code);
19229
20115
  const inspectionText = JSON.stringify(inspectionResult, null, 2);
19230
20116
  assertMatches(
@@ -19240,9 +20126,32 @@ async function runAgentEvalSuite(options) {
19240
20126
  });
19241
20127
  }
19242
20128
  inspectionResults.push(inspectionResult);
20129
+ await options.harness.emitProgress?.({
20130
+ phase: "inspection",
20131
+ status: "passed",
20132
+ scenarioId: scenario.id,
20133
+ stepId: step.id || `step-${index + 1}`,
20134
+ title: "Step inspection passed",
20135
+ message: inspectionText.slice(0, 800),
20136
+ data: inspectionResult
20137
+ });
19243
20138
  }
19244
20139
  for (const check of stepChecks) {
20140
+ await options.harness.emitProgress?.({
20141
+ phase: "check",
20142
+ status: "running",
20143
+ scenarioId: scenario.id,
20144
+ stepId: step.id || `step-${index + 1}`,
20145
+ title: "Running custom check"
20146
+ });
19245
20147
  await check(context);
20148
+ await options.harness.emitProgress?.({
20149
+ phase: "check",
20150
+ status: "passed",
20151
+ scenarioId: scenario.id,
20152
+ stepId: step.id || `step-${index + 1}`,
20153
+ title: "Custom check passed"
20154
+ });
19246
20155
  }
19247
20156
  stepResults.push({
19248
20157
  id: step.id || `step-${index + 1}`,
@@ -19253,6 +20162,7 @@ async function runAgentEvalSuite(options) {
19253
20162
  actionSummary: completed.actionSummary,
19254
20163
  promptInteractions: completed.promptInteractions,
19255
20164
  inspectionResults,
20165
+ assertions,
19256
20166
  turnDir: completed.turnDir
19257
20167
  });
19258
20168
  }
@@ -19271,6 +20181,7 @@ async function runAgentEvalSuite(options) {
19271
20181
  actionSummary: lastStep.actionSummary,
19272
20182
  promptInteractions: lastStep.promptInteractions,
19273
20183
  verification: lastStep.inspectionResults.length <= 1 ? lastStep.inspectionResults[0] ?? null : lastStep.inspectionResults,
20184
+ assertions: stepResults.flatMap((step) => step.assertions || []),
19274
20185
  tokenUsage: aggregateConversationTokenUsage(conversation),
19275
20186
  steps: stepResults,
19276
20187
  turnDir: conversation.artifactDir
@@ -19292,6 +20203,14 @@ async function runAgentEvalSuite(options) {
19292
20203
  conversation,
19293
20204
  result
19294
20205
  });
20206
+ await options.harness.emitProgress?.({
20207
+ phase: "scenario",
20208
+ status: "passed",
20209
+ scenarioId: scenario.id,
20210
+ title: "Scenario passed",
20211
+ message: `${result.assertions?.filter((assertion) => assertion.status === "passed").length || 0}/${result.assertions?.length || 0} assertions`,
20212
+ data: result
20213
+ });
19295
20214
  finalResult = result;
19296
20215
  } catch (error) {
19297
20216
  const failureMessage = error instanceof Error ? error.message : String(error);
@@ -19332,6 +20251,14 @@ async function runAgentEvalSuite(options) {
19332
20251
  result: failed,
19333
20252
  error: failureMessage
19334
20253
  });
20254
+ await options.harness.emitProgress?.({
20255
+ phase: "scenario",
20256
+ status: "failed",
20257
+ scenarioId: scenario.id,
20258
+ title: "Scenario failed",
20259
+ message: failureMessage,
20260
+ data: failed
20261
+ });
19335
20262
  finalResult = failed;
19336
20263
  } finally {
19337
20264
  await options.harness.closeConversation(conversation);
@@ -19380,6 +20307,11 @@ function createAgentEvalHarness(options) {
19380
20307
  const chatTimeoutMs = options.chatTimeoutMs ?? 12e4;
19381
20308
  const jobTimeoutMs = options.jobTimeoutMs ?? 9e4;
19382
20309
  const pollIntervalMs = options.pollIntervalMs ?? 250;
20310
+ const resolvedTemplate = resolveHarnessTemplate(
20311
+ options.harnessTemplateId || process.env.GRANULAR_AGENT_HARNESS_TEMPLATE || "stable");
20312
+ const promptRenderer = options.promptRenderer || resolvedTemplate.renderPrompt;
20313
+ const continuationRenderer = options.continuationRenderer || resolvedTemplate.renderContinuation;
20314
+ const onProgress = options.onProgress;
19383
20315
  async function openConversation(label) {
19384
20316
  await ensureDir(artifactDir);
19385
20317
  const clientId = `${slugify(label)}-${Date.now()}`;
@@ -19448,6 +20380,13 @@ function createAgentEvalHarness(options) {
19448
20380
  };
19449
20381
  }
19450
20382
  async function runInspection(conversation, inspection, completed, turnDir) {
20383
+ await emitProgress(onProgress, {
20384
+ phase: "inspection",
20385
+ status: "running",
20386
+ scenarioId: conversation.label,
20387
+ title: "Running inspection",
20388
+ message: inspection.code.slice(0, 500)
20389
+ });
19451
20390
  let result = null;
19452
20391
  let lastError = null;
19453
20392
  for (let attempt = 0; attempt < 10; attempt += 1) {
@@ -19475,6 +20414,14 @@ function createAgentEvalHarness(options) {
19475
20414
  });
19476
20415
  }
19477
20416
  await writeJson(path.join(turnDir, "verification.json"), result);
20417
+ await emitProgress(onProgress, {
20418
+ phase: "inspection",
20419
+ status: "passed",
20420
+ scenarioId: conversation.label,
20421
+ title: "Inspection passed",
20422
+ message: JSON.stringify(result)?.slice(0, 800),
20423
+ data: result
20424
+ });
19478
20425
  return result;
19479
20426
  }
19480
20427
  async function resumePendingTurn(pending, responder) {
@@ -19484,6 +20431,16 @@ function createAgentEvalHarness(options) {
19484
20431
  prompt,
19485
20432
  history: pending.promptInteractions
19486
20433
  });
20434
+ await emitProgress(onProgress, {
20435
+ phase: "interaction",
20436
+ status: "running",
20437
+ scenarioId: pending.conversation.label,
20438
+ stepId: path.basename(pending.turnDir),
20439
+ jobId: pending.job.id,
20440
+ title: "Prompt answered",
20441
+ message: `${prompt.type}: ${prompt.message || prompt.title} -> ${JSON.stringify(answer)}`,
20442
+ data: { prompt, answer }
20443
+ });
19487
20444
  const session = pending.conversation.environment;
19488
20445
  await session.answerPrompt(prompt.id, answer);
19489
20446
  pending.promptInteractions.push({
@@ -19503,7 +20460,12 @@ function createAgentEvalHarness(options) {
19503
20460
  job: pending.job,
19504
20461
  boundaryTimestamp: pending.boundaryTimestamp,
19505
20462
  timeoutMs: jobTimeoutMs,
19506
- pollIntervalMs
20463
+ pollIntervalMs,
20464
+ onProgress: (event) => void onProgress?.(event),
20465
+ progressContext: {
20466
+ scenarioId: pending.conversation.label,
20467
+ stepId: path.basename(pending.turnDir)
20468
+ }
19507
20469
  });
19508
20470
  if (resumed.kind === "prompt") {
19509
20471
  return {
@@ -19584,12 +20546,44 @@ function createAgentEvalHarness(options) {
19584
20546
  };
19585
20547
  conversation.logTurns.push(turnLog);
19586
20548
  if (input.prepareRecords?.length) {
20549
+ await emitProgress(onProgress, {
20550
+ phase: "setup",
20551
+ status: "running",
20552
+ scenarioId: conversation.label,
20553
+ stepId: turnId,
20554
+ title: "Recording setup records",
20555
+ message: `${input.prepareRecords.length} records`,
20556
+ data: input.prepareRecords
20557
+ });
19587
20558
  await conversation.environment.recordObjects(input.prepareRecords);
20559
+ await emitProgress(onProgress, {
20560
+ phase: "setup",
20561
+ status: "passed",
20562
+ scenarioId: conversation.label,
20563
+ stepId: turnId,
20564
+ title: "Setup records recorded",
20565
+ message: `${input.prepareRecords.length} records`
20566
+ });
19588
20567
  }
19589
20568
  if (input.prepareTools?.length) {
20569
+ await emitProgress(onProgress, {
20570
+ phase: "setup",
20571
+ status: "running",
20572
+ scenarioId: conversation.label,
20573
+ stepId: turnId,
20574
+ title: "Registering effect handlers",
20575
+ message: `${input.prepareTools.length} handlers`
20576
+ });
19590
20577
  await options.granular.ontology(conversation.environment.sandboxId).effects.registerMany(input.prepareTools);
19591
20578
  }
19592
20579
  if (input.prepare) {
20580
+ await emitProgress(onProgress, {
20581
+ phase: "setup",
20582
+ status: "running",
20583
+ scenarioId: conversation.label,
20584
+ stepId: turnId,
20585
+ title: "Running custom setup"
20586
+ });
19593
20587
  await input.prepare({
19594
20588
  conversation,
19595
20589
  environment: conversation.environment,
@@ -19598,6 +20592,14 @@ function createAgentEvalHarness(options) {
19598
20592
  }
19599
20593
  const boundaryTimestamp = Date.now();
19600
20594
  conversation.history.push({ role: "user", content: input.request });
20595
+ await emitProgress(onProgress, {
20596
+ phase: "step",
20597
+ status: "running",
20598
+ scenarioId: conversation.label,
20599
+ stepId: turnId,
20600
+ title: "User request",
20601
+ message: input.request
20602
+ });
19601
20603
  await writeJson(path.join(turnDir, "request.json"), {
19602
20604
  request: input.request,
19603
20605
  boundaryTimestamp
@@ -19639,7 +20641,7 @@ function createAgentEvalHarness(options) {
19639
20641
  inputSchema: tool.inputSchema,
19640
20642
  outputSchema: tool.outputSchema
19641
20643
  }));
19642
- const systemPrompt = buildGranularAgentSystemPrompt({
20644
+ const renderedPrompt = promptRenderer({
19643
20645
  domainDocumentation: await conversation.environment.getDomainDocumentation(),
19644
20646
  sessionContext: {
19645
20647
  sandboxId: conversation.environment.sandboxId,
@@ -19660,9 +20662,39 @@ function createAgentEvalHarness(options) {
19660
20662
  tools,
19661
20663
  checkpoint: latestCheckpoint
19662
20664
  });
19663
- const request = iteration === 0 ? input.request : buildContinuationInstruction(
20665
+ const systemPrompt = renderedPrompt.prompt;
20666
+ await emitProgress(onProgress, {
20667
+ phase: "prompt",
20668
+ status: "passed",
20669
+ scenarioId: conversation.label,
20670
+ stepId: turnId,
20671
+ iteration: iteration + 1,
20672
+ templateId: renderedPrompt.templateId,
20673
+ templateVersion: renderedPrompt.templateVersion,
20674
+ title: "Rendered harness prompt",
20675
+ message: `${systemPrompt.split("\n").length} lines`,
20676
+ data: {
20677
+ templateId: renderedPrompt.templateId,
20678
+ templateVersion: renderedPrompt.templateVersion,
20679
+ templateHash: renderedPrompt.templateHash,
20680
+ promptInstanceHash: renderedPrompt.promptInstanceHash,
20681
+ prompt: systemPrompt
20682
+ }
20683
+ });
20684
+ const request = iteration === 0 ? input.request : continuationRenderer(
19664
20685
  buildContinuationPreview(latestCheckpoint, noProgressCount)
19665
- );
20686
+ ).instruction;
20687
+ await emitProgress(onProgress, {
20688
+ phase: "generation",
20689
+ status: "running",
20690
+ scenarioId: conversation.label,
20691
+ stepId: turnId,
20692
+ iteration: iteration + 1,
20693
+ templateId: renderedPrompt.templateId,
20694
+ templateVersion: renderedPrompt.templateVersion,
20695
+ title: iteration === 0 ? "Generating agent response" : "Generating continuation",
20696
+ message: request
20697
+ });
19666
20698
  const generation = await withTimeout2(
19667
20699
  generateTurnWithRepair(options.generator, {
19668
20700
  systemPrompt,
@@ -19681,10 +20713,31 @@ function createAgentEvalHarness(options) {
19681
20713
  chatTimeoutMs,
19682
20714
  `chat generation for ${conversation.label} iteration ${iteration + 1}`
19683
20715
  );
20716
+ await emitProgress(onProgress, {
20717
+ phase: "generation",
20718
+ status: "passed",
20719
+ scenarioId: conversation.label,
20720
+ stepId: turnId,
20721
+ iteration: iteration + 1,
20722
+ templateId: renderedPrompt.templateId,
20723
+ templateVersion: renderedPrompt.templateVersion,
20724
+ title: generation.code ? "Generated job code" : "Generated text reply",
20725
+ message: generation.code || generation.reply || "",
20726
+ data: {
20727
+ reply: generation.reply,
20728
+ code: generation.code,
20729
+ attempts: generation.generationAttempts,
20730
+ usage: tokenUsageForGenerationOutput(generation)
20731
+ }
20732
+ });
19684
20733
  const iterationLog = {
19685
20734
  iteration: iteration + 1,
19686
20735
  request,
19687
20736
  systemPrompt,
20737
+ templateId: renderedPrompt.templateId,
20738
+ templateVersion: renderedPrompt.templateVersion,
20739
+ templateHash: renderedPrompt.templateHash,
20740
+ promptInstanceHash: renderedPrompt.promptInstanceHash,
19688
20741
  generationReply: generation.reply,
19689
20742
  generatedCode: generation.code,
19690
20743
  rawGeneration: generation.raw,
@@ -19731,6 +20784,15 @@ function createAgentEvalHarness(options) {
19731
20784
  result: completed.result
19732
20785
  };
19733
20786
  await writeJson(path.join(turnDir, "result.json"), completed);
20787
+ await emitProgress(onProgress, {
20788
+ phase: "step",
20789
+ status: "passed",
20790
+ scenarioId: conversation.label,
20791
+ stepId: turnId,
20792
+ title: "Step completed with text reply",
20793
+ message: responseText2,
20794
+ data: completed
20795
+ });
19734
20796
  return completed;
19735
20797
  }
19736
20798
  const session = conversation.environment;
@@ -19751,12 +20813,33 @@ function createAgentEvalHarness(options) {
19751
20813
  )
19752
20814
  }
19753
20815
  });
20816
+ await emitProgress(onProgress, {
20817
+ phase: "job",
20818
+ status: "running",
20819
+ scenarioId: conversation.label,
20820
+ stepId: turnId,
20821
+ iteration: iteration + 1,
20822
+ jobId: job.id,
20823
+ templateId: renderedPrompt.templateId,
20824
+ templateVersion: renderedPrompt.templateVersion,
20825
+ title: "Submitted Granular job",
20826
+ message: job.id,
20827
+ data: { code: generation.code }
20828
+ });
19754
20829
  const outcome = await waitForJobOutcome({
19755
20830
  environment: conversation.environment,
19756
20831
  job,
19757
20832
  boundaryTimestamp,
19758
20833
  timeoutMs: jobTimeoutMs,
19759
- pollIntervalMs
20834
+ pollIntervalMs,
20835
+ onProgress: (event) => void onProgress?.(event),
20836
+ progressContext: {
20837
+ scenarioId: conversation.label,
20838
+ stepId: turnId,
20839
+ iteration: iteration + 1,
20840
+ templateId: renderedPrompt.templateId,
20841
+ templateVersion: renderedPrompt.templateVersion
20842
+ }
19760
20843
  });
19761
20844
  if (outcome.kind === "prompt") {
19762
20845
  if (!autoAnswerPrompts) {
@@ -19866,6 +20949,23 @@ function createAgentEvalHarness(options) {
19866
20949
  controllerReason: continuation.reason,
19867
20950
  noProgressCount: continuation.nextNoProgressCount
19868
20951
  };
20952
+ await emitProgress(onProgress, {
20953
+ phase: "continuation",
20954
+ status: continuation.shouldContinue ? "running" : "passed",
20955
+ scenarioId: conversation.label,
20956
+ stepId: turnId,
20957
+ iteration: iteration + 1,
20958
+ jobId: job.id,
20959
+ templateId: renderedPrompt.templateId,
20960
+ templateVersion: renderedPrompt.templateVersion,
20961
+ title: continuation.shouldContinue ? "Harness requested another loop" : "Harness accepted completion",
20962
+ message: `${continuation.reason}; ${continuation.outcome}`,
20963
+ data: {
20964
+ continuation,
20965
+ checkpoint: latestCheckpoint,
20966
+ verifierSnapshot
20967
+ }
20968
+ });
19869
20969
  previousSnapshot = verifierSnapshot;
19870
20970
  noProgressCount = continuation.nextNoProgressCount;
19871
20971
  conversation.history.push({
@@ -19919,6 +21019,17 @@ function createAgentEvalHarness(options) {
19919
21019
  result: outcome.result
19920
21020
  };
19921
21021
  await writeJson(path.join(turnDir, "result.json"), completed);
21022
+ await emitProgress(onProgress, {
21023
+ phase: "step",
21024
+ status: "passed",
21025
+ scenarioId: conversation.label,
21026
+ stepId: turnId,
21027
+ iteration: iteration + 1,
21028
+ jobId: job.id,
21029
+ title: "Step completed",
21030
+ message: responseText,
21031
+ data: completed
21032
+ });
19922
21033
  return completed;
19923
21034
  }
19924
21035
  iteration += 1;
@@ -19930,6 +21041,7 @@ function createAgentEvalHarness(options) {
19930
21041
  return {
19931
21042
  artifactDir,
19932
21043
  granular: options.granular,
21044
+ emitProgress: (event) => emitProgress(onProgress, event),
19933
21045
  openConversation,
19934
21046
  closeConversation,
19935
21047
  runTurn,
@@ -19995,7 +21107,11 @@ function createAgentTester(options) {
19995
21107
  controllerBudgets: options.controllerBudgets,
19996
21108
  chatTimeoutMs: options.chatTimeoutMs,
19997
21109
  jobTimeoutMs: options.jobTimeoutMs,
19998
- pollIntervalMs: options.pollIntervalMs
21110
+ pollIntervalMs: options.pollIntervalMs,
21111
+ harnessTemplateId: options.harnessTemplateId,
21112
+ promptRenderer: options.promptRenderer,
21113
+ continuationRenderer: options.continuationRenderer,
21114
+ onProgress: options.onProgress
19999
21115
  });
20000
21116
  return {
20001
21117
  ...harness,
@@ -20021,6 +21137,6 @@ var createHumanResponder = createScriptedPromptResponder;
20021
21137
  var createOpenAIGenerator = createOpenAIChatTurnGenerator;
20022
21138
  var createTestArtifactsDirectory = createTimestampedArtifactDirectory;
20023
21139
 
20024
- export { createAgentEvalHarness, createAgentTester, createHumanResponder, createOpenAIChatTurnGenerator, createOpenAIGenerator, createScriptedPromptResponder, createTestArtifactsDirectory, createTimestampedArtifactDirectory, generateTurnWithRepair, runAgentEvalSuite, runAgentTests };
21140
+ export { createAgentEvalHarness, createAgentTester, createHumanResponder, createOpenAIChatTurnGenerator, createOpenAIGenerator, createScriptedPromptResponder, createTestArtifactsDirectory, createTimestampedArtifactDirectory, generateTurnWithRepair, loadAgentEvalScenarioFile, runAgentEvalSuite, runAgentTests, scenariosFromAgentEvalFile };
20025
21141
  //# sourceMappingURL=agent-evals.mjs.map
20026
21142
  //# sourceMappingURL=agent-evals.mjs.map