aipou-mcp-server 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +7 -0
  2. package/dist/bounded-input.d.ts +8 -0
  3. package/dist/bounded-input.js +21 -0
  4. package/dist/bounded-input.test.d.ts +1 -0
  5. package/dist/bounded-input.test.js +24 -0
  6. package/dist/claims.js +119 -31
  7. package/dist/doctor.d.ts +1 -1
  8. package/dist/doctor.js +1 -1
  9. package/dist/doctor.test.js +2 -0
  10. package/dist/evidence.d.ts +21 -0
  11. package/dist/evidence.js +14 -0
  12. package/dist/feedback.d.ts +17 -0
  13. package/dist/feedback.js +24 -0
  14. package/dist/identity.d.ts +3 -0
  15. package/dist/identity.js +25 -1
  16. package/dist/index.js +1842 -523
  17. package/dist/init.d.ts +6 -2
  18. package/dist/init.js +16 -3
  19. package/dist/init.test.js +18 -4
  20. package/dist/lock.js +47 -9
  21. package/dist/lock.test.d.ts +1 -0
  22. package/dist/lock.test.js +26 -0
  23. package/dist/observed-outcome-pilot.d.ts +16 -0
  24. package/dist/observed-outcome-pilot.js +59 -0
  25. package/dist/observed-outcome-pilot.test.d.ts +1 -0
  26. package/dist/observed-outcome-pilot.test.js +25 -0
  27. package/dist/paybox-link.d.ts +34 -0
  28. package/dist/paybox-link.js +52 -0
  29. package/dist/paybox-link.test.d.ts +1 -0
  30. package/dist/paybox-link.test.js +36 -0
  31. package/dist/private-json.d.ts +2 -0
  32. package/dist/private-json.js +39 -0
  33. package/dist/protocol.test.js +23 -0
  34. package/dist/receipt-metadata.d.ts +8 -0
  35. package/dist/receipt-metadata.js +12 -0
  36. package/dist/receipt-metadata.test.js +18 -1
  37. package/dist/receipt-verifier.d.ts +13 -0
  38. package/dist/receipt-verifier.js +125 -0
  39. package/dist/receipt-verifier.test.d.ts +1 -0
  40. package/dist/receipt-verifier.test.js +54 -0
  41. package/dist/receipts.js +11 -2
  42. package/dist/settlement-driver.d.ts +3 -0
  43. package/dist/settlement-driver.js +120 -0
  44. package/dist/settlement-driver.test.d.ts +1 -0
  45. package/dist/settlement-driver.test.js +78 -0
  46. package/dist/settlement-runner.d.ts +34 -0
  47. package/dist/settlement-runner.js +40 -0
  48. package/dist/settlement-runner.test.d.ts +1 -0
  49. package/dist/settlement-runner.test.js +128 -0
  50. package/dist/status.js +2 -0
  51. package/dist/store.d.ts +2 -0
  52. package/dist/store.js +33 -9
  53. package/dist/technocore-link.d.ts +34 -0
  54. package/dist/technocore-link.js +49 -0
  55. package/dist/technocore-link.test.d.ts +1 -0
  56. package/dist/technocore-link.test.js +36 -0
  57. package/dist/tool-metadata.test.d.ts +1 -0
  58. package/dist/tool-metadata.test.js +86 -0
  59. package/dist/types.d.ts +6 -0
  60. package/dist/usage-recovery.test.d.ts +1 -0
  61. package/dist/usage-recovery.test.js +121 -0
  62. package/package.json +4 -4
  63. package/server.json +2 -2
package/dist/index.js CHANGED
@@ -3105,9 +3105,28 @@ var require_utils = __commonJS({
3105
3105
  "use strict";
3106
3106
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3107
3107
  var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
3108
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3108
3109
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3109
3110
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3110
- var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3111
+ var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
3112
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
3113
+ var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
3114
+ var BYTE_HEX = new Array(256);
3115
+ {
3116
+ const HEX_DIGITS = "0123456789ABCDEF";
3117
+ for (let i = 0; i < 256; i++) {
3118
+ BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
3119
+ }
3120
+ }
3121
+ function percentEncodeNonAscii(cp) {
3122
+ if (cp < 2048) {
3123
+ return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
3124
+ }
3125
+ if (cp < 65536) {
3126
+ return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3127
+ }
3128
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3129
+ }
3111
3130
  function stringArrayToHexStripped(input) {
3112
3131
  let acc = "";
3113
3132
  let code = 0;
@@ -3132,91 +3151,105 @@ var require_utils = __commonJS({
3132
3151
  }
3133
3152
  return acc;
3134
3153
  }
3154
+ var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
3155
+ var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
3156
+ var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
3135
3157
  var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
3136
- function consumeIsZone(buffer) {
3137
- buffer.length = 0;
3138
- return true;
3139
- }
3140
- function consumeHextets(buffer, address, output) {
3141
- if (buffer.length) {
3142
- const hex = stringArrayToHexStripped(buffer);
3143
- if (hex !== "") {
3144
- address.push(hex);
3145
- } else {
3146
- output.error = true;
3147
- return false;
3158
+ function isZoneIdentifier(zone) {
3159
+ if (zone.length === 0) return false;
3160
+ for (let i = 0; i < zone.length; i++) {
3161
+ if (isZoneCharacter(zone[i])) continue;
3162
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
3163
+ i += 2;
3164
+ continue;
3148
3165
  }
3149
- buffer.length = 0;
3166
+ return false;
3150
3167
  }
3151
3168
  return true;
3152
3169
  }
3153
- function getIPV6(input) {
3154
- let tokenCount = 0;
3155
- const output = { error: false, address: "", zone: "" };
3156
- const address = [];
3157
- const buffer = [];
3158
- let endipv6Encountered = false;
3159
- let endIpv6 = false;
3160
- let consume = consumeHextets;
3161
- for (let i = 0; i < input.length; i++) {
3162
- const cursor = input[i];
3163
- if (cursor === "[" || cursor === "]") {
3164
- continue;
3165
- }
3166
- if (cursor === ":") {
3167
- if (endipv6Encountered === true) {
3168
- endIpv6 = true;
3169
- }
3170
- if (!consume(buffer, address, output)) {
3171
- break;
3172
- }
3173
- if (++tokenCount > 7) {
3174
- output.error = true;
3175
- break;
3176
- }
3177
- if (i > 0 && input[i - 1] === ":") {
3178
- endipv6Encountered = true;
3170
+ function compressIPv6ZeroRun(hextets) {
3171
+ let bestStart = -1;
3172
+ let bestLength = 0;
3173
+ let runStart = -1;
3174
+ let runLength = 0;
3175
+ for (let i = 0; i < hextets.length; i++) {
3176
+ if (hextets[i] === "0") {
3177
+ if (runStart === -1) runStart = i;
3178
+ runLength++;
3179
+ if (runLength > bestLength) {
3180
+ bestLength = runLength;
3181
+ bestStart = runStart;
3179
3182
  }
3180
- address.push(":");
3181
- continue;
3182
- } else if (cursor === "%") {
3183
- if (!consume(buffer, address, output)) {
3184
- break;
3185
- }
3186
- consume = consumeIsZone;
3187
3183
  } else {
3188
- buffer.push(cursor);
3184
+ runStart = -1;
3185
+ runLength = 0;
3186
+ }
3187
+ }
3188
+ if (bestLength < 2) return hextets.join(":");
3189
+ const head = hextets.slice(0, bestStart).join(":");
3190
+ const tail = hextets.slice(bestStart + bestLength).join(":");
3191
+ return head + "::" + tail;
3192
+ }
3193
+ function normalizeIPv6Address(input) {
3194
+ const compression = input.indexOf("::");
3195
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
3196
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
3197
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
3198
+ if (compression !== -1) {
3199
+ if (left.length === 1 && left[0] === "") left.length = 0;
3200
+ if (right.length === 1 && right[0] === "") right.length = 0;
3201
+ }
3202
+ const parts = left.concat(right);
3203
+ let hextetCount = 0;
3204
+ for (let i = 0; i < parts.length; i++) {
3205
+ const part = parts[i];
3206
+ if (part === "") return void 0;
3207
+ if (part.indexOf(".") !== -1) {
3208
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
3209
+ hextetCount += 2;
3189
3210
  continue;
3190
3211
  }
3212
+ if (!isHextet(part)) return void 0;
3213
+ parts[i] = parseInt(part, 16).toString(16);
3214
+ hextetCount++;
3191
3215
  }
3192
- if (buffer.length) {
3193
- if (consume === consumeIsZone) {
3194
- output.zone = buffer.join("");
3195
- } else if (endIpv6) {
3196
- address.push(buffer.join(""));
3197
- } else {
3198
- address.push(stringArrayToHexStripped(buffer));
3199
- }
3216
+ if (compression === -1) {
3217
+ if (hextetCount !== 8) return void 0;
3218
+ return compressIPv6ZeroRun(parts);
3200
3219
  }
3201
- output.address = address.join("");
3202
- return output;
3220
+ if (hextetCount >= 8) return void 0;
3221
+ const expanded = parts.slice(0, left.length);
3222
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
3223
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
3224
+ return compressIPv6ZeroRun(expanded);
3203
3225
  }
3204
3226
  function normalizeIPv6(host) {
3205
- if (findToken(host, ":") < 2) {
3206
- return { host, isIPV6: false };
3207
- }
3208
- const ipv6 = getIPV6(host);
3209
- if (!ipv6.error) {
3210
- let newHost = ipv6.address;
3211
- let escapedHost = ipv6.address;
3212
- if (ipv6.zone) {
3213
- newHost += "%" + ipv6.zone;
3214
- escapedHost += "%25" + ipv6.zone;
3215
- }
3216
- return { host: newHost, isIPV6: true, escapedHost };
3217
- } else {
3218
- return { host, isIPV6: false };
3219
- }
3227
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
3228
+ const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
3229
+ if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
3230
+ let input = bracketed ? host.slice(1, -1) : host;
3231
+ if (bracketed && isIPvFuture(input)) {
3232
+ input = input.toLowerCase();
3233
+ return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
3234
+ }
3235
+ if (findToken(input, ":") < 2) {
3236
+ return { host, isIPV6: false, error: bracketed };
3237
+ }
3238
+ let zoneIdentifier = "";
3239
+ const zoneSeparator = input.indexOf("%");
3240
+ if (zoneSeparator !== -1) {
3241
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
3242
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
3243
+ if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
3244
+ input = input.slice(0, zoneSeparator);
3245
+ }
3246
+ const address = normalizeIPv6Address(input);
3247
+ if (address === void 0) return { host, isIPV6: false, error: true };
3248
+ return {
3249
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
3250
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
3251
+ isIPV6: true
3252
+ };
3220
3253
  }
3221
3254
  function findToken(str, token) {
3222
3255
  let ind = 0;
@@ -3225,8 +3258,8 @@ var require_utils = __commonJS({
3225
3258
  }
3226
3259
  return ind;
3227
3260
  }
3228
- function removeDotSegments(path10) {
3229
- let input = path10;
3261
+ function removeDotSegments(path12) {
3262
+ let input = path12;
3230
3263
  const output = [];
3231
3264
  let nextSlash = -1;
3232
3265
  let len = 0;
@@ -3300,6 +3333,37 @@ var require_utils = __commonJS({
3300
3333
  }
3301
3334
  return output.join("");
3302
3335
  }
3336
+ function removeDotSegmentsRelative(path12) {
3337
+ if (path12 === "" || path12[0] === "/") {
3338
+ return removeDotSegments(path12);
3339
+ }
3340
+ const segments = path12.split("/");
3341
+ const last = segments.length - 1;
3342
+ const output = [];
3343
+ let trailingDirectory = false;
3344
+ for (let i = 0; i <= last; i++) {
3345
+ const segment = segments[i];
3346
+ if (segment === "." || segment === "..") {
3347
+ if (segment === "..") {
3348
+ if (output.length !== 0 && output[output.length - 1] !== "..") {
3349
+ output.pop();
3350
+ } else {
3351
+ output.push("..");
3352
+ }
3353
+ }
3354
+ trailingDirectory = i === last;
3355
+ } else {
3356
+ output.push(segment);
3357
+ }
3358
+ }
3359
+ if (trailingDirectory && output[output.length - 1] !== "..") {
3360
+ output.push("");
3361
+ }
3362
+ if (output.length === 0 || output[0] === "" || output[0].includes(":")) {
3363
+ output.unshift(".");
3364
+ }
3365
+ return output.join("/");
3366
+ }
3303
3367
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3304
3368
  var HOST_DELIM_RE = /[@/?#:]/g;
3305
3369
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
@@ -3335,7 +3399,8 @@ var require_utils = __commonJS({
3335
3399
  function normalizePathEncoding(input) {
3336
3400
  let output = "";
3337
3401
  for (let i = 0; i < input.length; i++) {
3338
- if (input[i] === "%" && i + 2 < input.length) {
3402
+ const ch = input[i];
3403
+ if (ch === "%" && i + 2 < input.length) {
3339
3404
  const hex = input.slice(i + 1, i + 3);
3340
3405
  if (isHexPair(hex)) {
3341
3406
  const normalizedHex = hex.toUpperCase();
@@ -3349,10 +3414,152 @@ var require_utils = __commonJS({
3349
3414
  continue;
3350
3415
  }
3351
3416
  }
3352
- if (isPathCharacter(input[i])) {
3353
- output += input[i];
3417
+ if (isPathCharacter(ch)) {
3418
+ output += ch;
3419
+ } else {
3420
+ const code = input.charCodeAt(i);
3421
+ if (code < 128) {
3422
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3423
+ } else if (code < 55296 || code > 57343) {
3424
+ output += percentEncodeNonAscii(code);
3425
+ } else if (code <= 56319 && i + 1 < input.length) {
3426
+ const low = input.charCodeAt(i + 1);
3427
+ if (low >= 56320 && low <= 57343) {
3428
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3429
+ i++;
3430
+ } else {
3431
+ output += percentEncodeNonAscii(65533);
3432
+ }
3433
+ } else {
3434
+ output += percentEncodeNonAscii(65533);
3435
+ }
3436
+ }
3437
+ }
3438
+ return output;
3439
+ }
3440
+ function serializePathEncoding(input, pathNoScheme = false) {
3441
+ let output = "";
3442
+ let firstSegment = pathNoScheme && input[0] !== "/";
3443
+ for (let i = 0; i < input.length; i++) {
3444
+ const ch = input[i];
3445
+ if (ch === "%" && i + 2 < input.length) {
3446
+ const hex = input.slice(i + 1, i + 3);
3447
+ if (isHexPair(hex)) {
3448
+ output += "%" + hex.toUpperCase();
3449
+ i += 2;
3450
+ continue;
3451
+ }
3452
+ }
3453
+ if (ch === "/") {
3454
+ firstSegment = false;
3455
+ }
3456
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
3457
+ output += ch;
3458
+ } else {
3459
+ const code = input.charCodeAt(i);
3460
+ if (code < 128) {
3461
+ output += BYTE_HEX[code];
3462
+ } else if (code < 55296 || code > 57343) {
3463
+ output += percentEncodeNonAscii(code);
3464
+ } else if (code <= 56319 && i + 1 < input.length) {
3465
+ const low = input.charCodeAt(i + 1);
3466
+ if (low >= 56320 && low <= 57343) {
3467
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3468
+ i++;
3469
+ } else {
3470
+ output += percentEncodeNonAscii(65533);
3471
+ }
3472
+ } else {
3473
+ output += percentEncodeNonAscii(65533);
3474
+ }
3475
+ }
3476
+ }
3477
+ return output;
3478
+ }
3479
+ function encodeComponent(input, isAllowed) {
3480
+ let output = "";
3481
+ for (let i = 0; i < input.length; i++) {
3482
+ const ch = input[i];
3483
+ if (ch === "%" && i + 2 < input.length) {
3484
+ const hex = input.slice(i + 1, i + 3);
3485
+ if (isHexPair(hex)) {
3486
+ output += "%" + hex.toUpperCase();
3487
+ i += 2;
3488
+ continue;
3489
+ }
3490
+ }
3491
+ if (isAllowed(ch)) {
3492
+ output += ch;
3493
+ } else {
3494
+ const code = input.charCodeAt(i);
3495
+ if (code < 128) {
3496
+ output += BYTE_HEX[code];
3497
+ } else if (code < 55296 || code > 57343) {
3498
+ output += percentEncodeNonAscii(code);
3499
+ } else if (code <= 56319 && i + 1 < input.length) {
3500
+ const low = input.charCodeAt(i + 1);
3501
+ if (low >= 56320 && low <= 57343) {
3502
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3503
+ i++;
3504
+ } else {
3505
+ output += percentEncodeNonAscii(65533);
3506
+ }
3507
+ } else {
3508
+ output += percentEncodeNonAscii(65533);
3509
+ }
3510
+ }
3511
+ }
3512
+ return output;
3513
+ }
3514
+ function encodeUserinfo(input) {
3515
+ return encodeComponent(input, isUserinfoCharacter);
3516
+ }
3517
+ function encodeQuery(input) {
3518
+ return encodeComponent(input, isQueryFragmentCharacter);
3519
+ }
3520
+ function encodeFragment(input) {
3521
+ return encodeComponent(input, isQueryFragmentCharacter);
3522
+ }
3523
+ function isEscapeSafe(cp) {
3524
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
3525
+ }
3526
+ function normalizeQueryFragmentEncoding(input) {
3527
+ let output = "";
3528
+ for (let i = 0; i < input.length; i++) {
3529
+ const ch = input[i];
3530
+ if (ch === "%" && i + 2 < input.length) {
3531
+ const hex = input.slice(i + 1, i + 3);
3532
+ if (isHexPair(hex)) {
3533
+ const normalizedHex = hex.toUpperCase();
3534
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3535
+ if (isUnreserved(decoded)) {
3536
+ output += decoded;
3537
+ } else {
3538
+ output += "%" + normalizedHex;
3539
+ }
3540
+ i += 2;
3541
+ continue;
3542
+ }
3543
+ }
3544
+ if (isQueryFragmentCharacter(ch)) {
3545
+ output += ch;
3354
3546
  } else {
3355
- output += escape(input[i]);
3547
+ const code = input.charCodeAt(i);
3548
+ if (code < 128) {
3549
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3550
+ } else if (code < 55296 || code > 57343) {
3551
+ output += percentEncodeNonAscii(code);
3552
+ } else if (code <= 56319 && i + 1 < input.length) {
3553
+ const low = input.charCodeAt(i + 1);
3554
+ if (low >= 56320 && low <= 57343) {
3555
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3556
+ i++;
3557
+ } else {
3558
+ output += percentEncodeNonAscii(65533);
3559
+ }
3560
+ } else {
3561
+ output += percentEncodeNonAscii(65533);
3562
+ }
3356
3563
  }
3357
3564
  }
3358
3565
  return output;
@@ -3375,14 +3582,18 @@ var require_utils = __commonJS({
3375
3582
  function recomposeAuthority(component) {
3376
3583
  const uriTokens = [];
3377
3584
  if (component.userinfo !== void 0) {
3378
- uriTokens.push(component.userinfo);
3585
+ uriTokens.push(encodeUserinfo(component.userinfo));
3379
3586
  uriTokens.push("@");
3380
3587
  }
3381
3588
  if (component.host !== void 0) {
3382
- let host = unescape(component.host);
3589
+ let host = component.host;
3383
3590
  if (!isIPv4(host)) {
3384
- const ipV6res = normalizeIPv6(host);
3385
- if (ipV6res.isIPV6 === true) {
3591
+ let ipV6res = normalizeIPv6(host);
3592
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
3593
+ host = normalizePercentEncoding(host, true);
3594
+ ipV6res = normalizeIPv6(host);
3595
+ }
3596
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
3386
3597
  host = `[${ipV6res.escapedHost}]`;
3387
3598
  } else {
3388
3599
  host = reescapeHostDelimiters(host, false);
@@ -3391,8 +3602,12 @@ var require_utils = __commonJS({
3391
3602
  uriTokens.push(host);
3392
3603
  }
3393
3604
  if (typeof component.port === "number" || typeof component.port === "string") {
3605
+ const port = String(component.port);
3606
+ if (!isPort(port)) {
3607
+ throw new TypeError("URI port is malformed.");
3608
+ }
3394
3609
  uriTokens.push(":");
3395
- uriTokens.push(String(component.port));
3610
+ uriTokens.push(port);
3396
3611
  }
3397
3612
  return uriTokens.length ? uriTokens.join("") : void 0;
3398
3613
  }
@@ -3402,8 +3617,14 @@ var require_utils = __commonJS({
3402
3617
  reescapeHostDelimiters,
3403
3618
  normalizePercentEncoding,
3404
3619
  normalizePathEncoding,
3620
+ serializePathEncoding,
3621
+ normalizeQueryFragmentEncoding,
3622
+ encodeUserinfo,
3623
+ encodeQuery,
3624
+ encodeFragment,
3405
3625
  escapePreservingEscapes,
3406
3626
  removeDotSegments,
3627
+ removeDotSegmentsRelative,
3407
3628
  isIPv4,
3408
3629
  isUUID,
3409
3630
  normalizeIPv6,
@@ -3417,7 +3638,7 @@ var require_schemes = __commonJS({
3417
3638
  "../node_modules/fast-uri/lib/schemes.js"(exports, module) {
3418
3639
  "use strict";
3419
3640
  var { isUUID } = require_utils();
3420
- var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
3641
+ var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
3421
3642
  var supportedSchemeNames = (
3422
3643
  /** @type {const} */
3423
3644
  [
@@ -3478,9 +3699,10 @@ var require_schemes = __commonJS({
3478
3699
  wsComponent.secure = void 0;
3479
3700
  }
3480
3701
  if (wsComponent.resourceName) {
3481
- const [path10, query] = wsComponent.resourceName.split("?");
3482
- wsComponent.path = path10 && path10 !== "/" ? path10 : void 0;
3483
- wsComponent.query = query;
3702
+ const queryIndex = wsComponent.resourceName.indexOf("?");
3703
+ const path12 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3704
+ wsComponent.path = path12 && path12 !== "/" ? path12 : void 0;
3705
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
3484
3706
  wsComponent.resourceName = void 0;
3485
3707
  }
3486
3708
  wsComponent.fragment = void 0;
@@ -3492,7 +3714,7 @@ var require_schemes = __commonJS({
3492
3714
  return urnComponent;
3493
3715
  }
3494
3716
  const matches = urnComponent.path.match(URN_REG);
3495
- if (matches) {
3717
+ if (matches && matches[0] === urnComponent.path) {
3496
3718
  const scheme = options.scheme || urnComponent.scheme || "urn";
3497
3719
  urnComponent.nid = matches[1].toLowerCase();
3498
3720
  urnComponent.nss = matches[2];
@@ -3626,8 +3848,19 @@ var require_schemes = __commonJS({
3626
3848
  var require_fast_uri = __commonJS({
3627
3849
  "../node_modules/fast-uri/index.js"(exports, module) {
3628
3850
  "use strict";
3629
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3851
+ var { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3630
3852
  var { SCHEMES, getSchemeHandler } = require_schemes();
3853
+ var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
3854
+ var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
3855
+ var MALFORMED_HOST_ERROR = "URI host is malformed.";
3856
+ var FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
3857
+ function validateScheme(scheme) {
3858
+ const stringScheme = String(scheme);
3859
+ if (!VALID_SCHEME.test(stringScheme)) {
3860
+ throw new TypeError(MALFORMED_SCHEME_ERROR);
3861
+ }
3862
+ return stringScheme;
3863
+ }
3631
3864
  function normalize(uri, options) {
3632
3865
  if (typeof uri === "string") {
3633
3866
  uri = /** @type {T} */
@@ -3640,7 +3873,37 @@ var require_fast_uri = __commonJS({
3640
3873
  }
3641
3874
  function resolve(baseURI, relativeURI, options) {
3642
3875
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3643
- const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
3876
+ const {
3877
+ parsed: baseParsed,
3878
+ malformedAuthorityOrPort: baseMalformed,
3879
+ malformedPercentEncoding: baseMalformedPercentEncoding,
3880
+ malformedSchemeSpecific: baseMalformedSchemeSpecific,
3881
+ malformedHost: baseMalformedHost,
3882
+ malformedScheme: baseMalformedScheme
3883
+ } = parseWithStatus(baseURI, schemelessOptions);
3884
+ const {
3885
+ parsed: relativeParsed,
3886
+ malformedAuthorityOrPort: relativeMalformed,
3887
+ malformedPercentEncoding: relativeMalformedPercentEncoding,
3888
+ malformedSchemeSpecific: relativeMalformedSchemeSpecific,
3889
+ malformedHost: relativeMalformedHost,
3890
+ malformedScheme: relativeMalformedScheme
3891
+ } = parseWithStatus(relativeURI, schemelessOptions);
3892
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
3893
+ throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3894
+ }
3895
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
3896
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
3897
+ const resolvedHost = resolved.host;
3898
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
3899
+ if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) {
3900
+ throw new Error(MALFORMED_HOST_ERROR);
3901
+ }
3902
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
3903
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
3904
+ if (resolved.error && !encodedASCIIHost) {
3905
+ throw new Error(resolved.error);
3906
+ }
3644
3907
  schemelessOptions.skipEscape = true;
3645
3908
  return serialize(resolved, schemelessOptions);
3646
3909
  }
@@ -3677,14 +3940,15 @@ var require_fast_uri = __commonJS({
3677
3940
  if (relative.path[0] === "/") {
3678
3941
  target.path = removeDotSegments(relative.path);
3679
3942
  } else {
3680
- if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3943
+ const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
3944
+ if (baseHasAuthority && !base.path) {
3681
3945
  target.path = "/" + relative.path;
3682
3946
  } else if (!base.path) {
3683
3947
  target.path = relative.path;
3684
3948
  } else {
3685
3949
  target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
3686
3950
  }
3687
- target.path = removeDotSegments(target.path);
3951
+ target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
3688
3952
  }
3689
3953
  target.query = relative.query;
3690
3954
  }
@@ -3700,7 +3964,7 @@ var require_fast_uri = __commonJS({
3700
3964
  function equal(uriA, uriB, options) {
3701
3965
  const normalizedA = normalizeComparableURI(uriA, options);
3702
3966
  const normalizedB = normalizeComparableURI(uriB, options);
3703
- return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3967
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
3704
3968
  }
3705
3969
  function serialize(cmpts, opts) {
3706
3970
  const component = {
@@ -3721,19 +3985,23 @@ var require_fast_uri = __commonJS({
3721
3985
  };
3722
3986
  const options = Object.assign({}, opts);
3723
3987
  const uriTokens = [];
3988
+ if (component.scheme) {
3989
+ component.scheme = validateScheme(component.scheme);
3990
+ }
3724
3991
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
3725
3992
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
3993
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
3994
+ const schemelessReference = (component.scheme === void 0 || options.reference === "suffix") && !hasAuthority;
3995
+ const pathNoScheme = !options.skipEscape && schemelessReference;
3726
3996
  if (component.path !== void 0) {
3727
3997
  if (!options.skipEscape) {
3728
- component.path = escapePreservingEscapes(component.path);
3729
- if (component.scheme !== void 0) {
3730
- component.path = component.path.split("%3A").join(":");
3731
- }
3998
+ component.path = serializePathEncoding(component.path, pathNoScheme);
3732
3999
  } else {
3733
4000
  component.path = normalizePercentEncoding(component.path);
3734
4001
  }
3735
4002
  }
3736
4003
  if (options.reference !== "suffix" && component.scheme) {
4004
+ component.scheme = validateScheme(component.scheme);
3737
4005
  uriTokens.push(component.scheme, ":");
3738
4006
  }
3739
4007
  const authority = recomposeAuthority(component);
@@ -3749,7 +4017,10 @@ var require_fast_uri = __commonJS({
3749
4017
  if (component.path !== void 0) {
3750
4018
  let s = component.path;
3751
4019
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
3752
- s = removeDotSegments(s);
4020
+ s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
4021
+ }
4022
+ if (schemelessReference) {
4023
+ s = serializePathEncoding(s, true);
3753
4024
  }
3754
4025
  if (authority === void 0 && s[0] === "/" && s[1] === "/") {
3755
4026
  s = "/%2F" + s.slice(2);
@@ -3757,15 +4028,16 @@ var require_fast_uri = __commonJS({
3757
4028
  uriTokens.push(s);
3758
4029
  }
3759
4030
  if (component.query !== void 0) {
3760
- uriTokens.push("?", component.query);
4031
+ uriTokens.push("?", encodeQuery(component.query));
3761
4032
  }
3762
4033
  if (component.fragment !== void 0) {
3763
- uriTokens.push("#", component.fragment);
4034
+ uriTokens.push("#", encodeFragment(component.fragment));
3764
4035
  }
3765
4036
  return uriTokens.join("");
3766
4037
  }
3767
4038
  var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
3768
4039
  var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
4040
+ var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
3769
4041
  function getParseError(parsed, matches) {
3770
4042
  if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
3771
4043
  return 'URI path must start with "/" when authority is present.';
@@ -3775,6 +4047,53 @@ var require_fast_uri = __commonJS({
3775
4047
  }
3776
4048
  return void 0;
3777
4049
  }
4050
+ function hasMalformedPercentEncoding(component) {
4051
+ if (component === void 0) return false;
4052
+ let percent = component.indexOf("%");
4053
+ while (percent !== -1) {
4054
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
4055
+ return true;
4056
+ }
4057
+ percent = component.indexOf("%", percent + 3);
4058
+ }
4059
+ return false;
4060
+ }
4061
+ function isIPLiteral(host) {
4062
+ return host[0] === "[" && host[host.length - 1] === "]";
4063
+ }
4064
+ function hasMalformedComponentPercentEncoding(matches) {
4065
+ const host = matches[4];
4066
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4067
+ }
4068
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4069
+ if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
4070
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
4071
+ return true;
4072
+ }
4073
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4074
+ let url;
4075
+ try {
4076
+ url = new URL("http://" + parsed.host);
4077
+ } catch (e) {
4078
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
4079
+ return true;
4080
+ }
4081
+ if (!isHostOnlyURL(url)) {
4082
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
4083
+ return true;
4084
+ }
4085
+ parsed.host = url.hostname;
4086
+ }
4087
+ return false;
4088
+ }
4089
+ function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
4090
+ return Boolean(
4091
+ parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host)
4092
+ );
4093
+ }
4094
+ function isHostOnlyURL(url) {
4095
+ return url.username === "" && url.password === "" && url.port === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname !== "";
4096
+ }
3778
4097
  function parseWithStatus(uri, opts) {
3779
4098
  const options = Object.assign({}, opts);
3780
4099
  const parsed = {
@@ -3787,6 +4106,11 @@ var require_fast_uri = __commonJS({
3787
4106
  fragment: void 0
3788
4107
  };
3789
4108
  let malformedAuthorityOrPort = false;
4109
+ let malformedPercentEncoding = false;
4110
+ let malformedSchemeSpecific = false;
4111
+ let malformedHost = false;
4112
+ let malformedIPLiteral = false;
4113
+ let malformedScheme = false;
3790
4114
  let isIP = false;
3791
4115
  if (options.reference === "suffix") {
3792
4116
  if (options.scheme) {
@@ -3800,6 +4124,20 @@ var require_fast_uri = __commonJS({
3800
4124
  parsed.error = "URI authority must not contain a literal backslash.";
3801
4125
  malformedAuthorityOrPort = true;
3802
4126
  }
4127
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
4128
+ if (introducerMatch !== null) {
4129
+ const region = introducerMatch[1];
4130
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
4131
+ if (normalizedRegion.length >= 2) {
4132
+ if (normalizedRegion.slice(0, 2) !== "//") {
4133
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
4134
+ malformedAuthorityOrPort = true;
4135
+ } else if (region.length !== normalizedRegion.length) {
4136
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
4137
+ malformedAuthorityOrPort = true;
4138
+ }
4139
+ }
4140
+ }
3803
4141
  const matches = uri.match(URI_PARSE);
3804
4142
  if (matches) {
3805
4143
  parsed.scheme = matches[1];
@@ -3809,6 +4147,18 @@ var require_fast_uri = __commonJS({
3809
4147
  parsed.path = matches[6] || "";
3810
4148
  parsed.query = matches[7];
3811
4149
  parsed.fragment = matches[8];
4150
+ if (parsed.scheme !== void 0) {
4151
+ if (VALID_SCHEME.test(parsed.scheme)) {
4152
+ parsed.scheme = parsed.scheme.toLowerCase();
4153
+ } else {
4154
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
4155
+ malformedScheme = true;
4156
+ }
4157
+ }
4158
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
4159
+ if (malformedPercentEncoding) {
4160
+ parsed.error = parsed.error || "URI contains malformed percent-encoding.";
4161
+ }
3812
4162
  if (isNaN(parsed.port)) {
3813
4163
  parsed.port = matches[5];
3814
4164
  }
@@ -3820,9 +4170,16 @@ var require_fast_uri = __commonJS({
3820
4170
  if (parsed.host) {
3821
4171
  const ipv4result = isIPv4(parsed.host);
3822
4172
  if (ipv4result === false) {
4173
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4174
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
3823
4175
  const ipv6result = normalizeIPv6(parsed.host);
3824
- parsed.host = ipv6result.host.toLowerCase();
3825
- isIP = ipv6result.isIPV6;
4176
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4177
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4178
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4179
+ if (malformedIPLiteral) {
4180
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
4181
+ malformedAuthorityOrPort = true;
4182
+ }
3826
4183
  } else {
3827
4184
  isIP = true;
3828
4185
  }
@@ -3840,42 +4197,37 @@ var require_fast_uri = __commonJS({
3840
4197
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
3841
4198
  }
3842
4199
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
3843
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3844
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3845
- try {
3846
- parsed.host = new URL("http://" + parsed.host).hostname;
3847
- } catch (e) {
3848
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3849
- }
4200
+ if (!malformedIPLiteral) {
4201
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4202
+ }
4203
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4204
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4205
+ if (!isIP) {
4206
+ host = normalizePercentEncoding(host.toLowerCase());
3850
4207
  }
4208
+ parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host, isIP) : host;
3851
4209
  }
3852
4210
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
3853
- if (uri.indexOf("%") !== -1) {
3854
- if (parsed.scheme !== void 0) {
3855
- parsed.scheme = unescape(parsed.scheme);
3856
- }
3857
- if (parsed.host !== void 0) {
3858
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
3859
- }
3860
- }
3861
4211
  if (parsed.path) {
3862
4212
  parsed.path = normalizePathEncoding(parsed.path);
3863
4213
  }
4214
+ if (parsed.query) {
4215
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
4216
+ }
3864
4217
  if (parsed.fragment) {
3865
- try {
3866
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3867
- } catch {
3868
- parsed.error = parsed.error || "URI malformed";
3869
- }
4218
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3870
4219
  }
3871
4220
  }
3872
4221
  if (schemeHandler && schemeHandler.parse) {
3873
4222
  schemeHandler.parse(parsed, options);
4223
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
4224
+ malformedSchemeSpecific = true;
4225
+ }
3874
4226
  }
3875
4227
  } else {
3876
4228
  parsed.error = parsed.error || "URI can not be parsed.";
3877
4229
  }
3878
- return { parsed, malformedAuthorityOrPort };
4230
+ return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
3879
4231
  }
3880
4232
  function parse(uri, opts) {
3881
4233
  return parseWithStatus(uri, opts).parsed;
@@ -3884,20 +4236,28 @@ var require_fast_uri = __commonJS({
3884
4236
  return normalizeStringWithStatus(uri, opts).normalized;
3885
4237
  }
3886
4238
  function normalizeStringWithStatus(uri, opts) {
3887
- const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
4239
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
3888
4240
  return {
3889
- normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3890
- malformedAuthorityOrPort
4241
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
4242
+ malformedAuthorityOrPort,
4243
+ malformedPercentEncoding,
4244
+ malformedSchemeSpecific,
4245
+ malformedHost,
4246
+ malformedScheme
3891
4247
  };
3892
4248
  }
3893
4249
  function normalizeComparableURI(uri, opts) {
3894
- if (typeof uri === "string") {
3895
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3896
- return malformedAuthorityOrPort ? void 0 : normalized;
4250
+ if (typeof uri !== "string" && typeof uri !== "object") {
4251
+ return void 0;
3897
4252
  }
3898
- if (typeof uri === "object") {
3899
- return serialize(uri, opts);
4253
+ let value;
4254
+ try {
4255
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
4256
+ } catch {
4257
+ return void 0;
3900
4258
  }
4259
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
4260
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
3901
4261
  }
3902
4262
  var fastUri = {
3903
4263
  SCHEMES,
@@ -6980,17 +7340,33 @@ function normalizeObjectSchema(schema) {
6980
7340
  }
6981
7341
  return void 0;
6982
7342
  }
7343
+ function getDotPath(path12) {
7344
+ if (path12.length === 0) {
7345
+ return "object root";
7346
+ }
7347
+ return path12.reduce((acc, seg, index) => {
7348
+ if (index === 0) {
7349
+ return String(seg);
7350
+ }
7351
+ if (typeof seg === "number") {
7352
+ return `${acc}[${seg}]`;
7353
+ }
7354
+ return `${acc}.${seg}`;
7355
+ }, "");
7356
+ }
6983
7357
  function getParseErrorMessage(error) {
6984
7358
  if (error && typeof error === "object") {
7359
+ if ("issues" in error && Array.isArray(error.issues) && error.issues.length > 0) {
7360
+ return error.issues.map((i) => {
7361
+ if (!i.path?.length) {
7362
+ return i.message;
7363
+ }
7364
+ return `${i.message} at ${getDotPath(i.path)}`;
7365
+ }).join("\n");
7366
+ }
6985
7367
  if ("message" in error && typeof error.message === "string") {
6986
7368
  return error.message;
6987
7369
  }
6988
- if ("issues" in error && Array.isArray(error.issues) && error.issues.length > 0) {
6989
- const firstIssue = error.issues[0];
6990
- if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
6991
- return String(firstIssue.message);
6992
- }
6993
- }
6994
7370
  try {
6995
7371
  return JSON.stringify(error);
6996
7372
  } catch {
@@ -9954,6 +10330,9 @@ var Protocol = class {
9954
10330
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
9955
10331
  const handleTaskResult = async () => {
9956
10332
  const taskId = request.params.taskId;
10333
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
10334
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
10335
+ }
9957
10336
  if (this._taskMessageQueue) {
9958
10337
  let queuedMessage;
9959
10338
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -9984,12 +10363,12 @@ var Protocol = class {
9984
10363
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
9985
10364
  }
9986
10365
  if (!isTerminal(task.status)) {
9987
- await this._waitForTaskUpdate(taskId, extra.signal);
10366
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
9988
10367
  return await handleTaskResult();
9989
10368
  }
9990
10369
  if (isTerminal(task.status)) {
9991
10370
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
9992
- this._clearTaskQueue(taskId);
10371
+ this._clearTaskQueue(taskId, extra.sessionId);
9993
10372
  return {
9994
10373
  ...result,
9995
10374
  _meta: {
@@ -10026,7 +10405,7 @@ var Protocol = class {
10026
10405
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
10027
10406
  }
10028
10407
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
10029
- this._clearTaskQueue(request.params.taskId);
10408
+ this._clearTaskQueue(request.params.taskId, extra.sessionId);
10030
10409
  const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
10031
10410
  if (!cancelledTask) {
10032
10411
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
@@ -10154,6 +10533,19 @@ var Protocol = class {
10154
10533
  const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
10155
10534
  const capturedTransport = this._transport;
10156
10535
  const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
10536
+ const sessionId = capturedTransport?.sessionId;
10537
+ const store = this._taskStore;
10538
+ let relatedTaskFound = true;
10539
+ let relatedTaskLookup;
10540
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
10541
+ relatedTaskFound = false;
10542
+ relatedTaskLookup = (async () => {
10543
+ if (!await store.getTask(relatedTaskId, sessionId)) {
10544
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
10545
+ }
10546
+ relatedTaskFound = true;
10547
+ })();
10548
+ }
10157
10549
  if (handler === void 0) {
10158
10550
  const errorResponse = {
10159
10551
  jsonrpc: "2.0",
@@ -10163,7 +10555,10 @@ var Protocol = class {
10163
10555
  message: "Method not found"
10164
10556
  }
10165
10557
  };
10166
- if (relatedTaskId && this._taskMessageQueue) {
10558
+ if (relatedTaskId && relatedTaskLookup) {
10559
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
10560
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error) => this._onerror(new Error(`Failed to send an error response: ${error}`)));
10561
+ } else if (relatedTaskId && this._taskMessageQueue) {
10167
10562
  this._enqueueTaskMessage(relatedTaskId, {
10168
10563
  type: "error",
10169
10564
  message: errorResponse,
@@ -10214,7 +10609,10 @@ var Protocol = class {
10214
10609
  closeSSEStream: extra?.closeSSEStream,
10215
10610
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
10216
10611
  };
10217
- Promise.resolve().then(() => {
10612
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
10613
+ if (relatedTaskLookup && abortController.signal.aborted) {
10614
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
10615
+ }
10218
10616
  if (taskCreationParams) {
10219
10617
  this.assertTaskHandlerCapability(request.method);
10220
10618
  }
@@ -10249,7 +10647,7 @@ var Protocol = class {
10249
10647
  ...error["data"] !== void 0 && { data: error["data"] }
10250
10648
  }
10251
10649
  };
10252
- if (relatedTaskId && this._taskMessageQueue) {
10650
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
10253
10651
  await this._enqueueTaskMessage(relatedTaskId, {
10254
10652
  type: "error",
10255
10653
  message: errorResponse,
@@ -10729,7 +11127,7 @@ var Protocol = class {
10729
11127
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
10730
11128
  }
10731
11129
  const maxQueueSize = this._options?.maxTaskQueueSize;
10732
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
11130
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
10733
11131
  }
10734
11132
  /**
10735
11133
  * Clears the message queue for a task and rejects any pending request resolvers.
@@ -10758,12 +11156,13 @@ var Protocol = class {
10758
11156
  * Uses polling to check for updates at the task's configured poll interval.
10759
11157
  * @param taskId The task ID to wait for
10760
11158
  * @param signal Abort signal to cancel the wait
11159
+ * @param sessionId Session of the request that waits, passed to the task store
10761
11160
  * @returns Promise that resolves when an update occurs or rejects if aborted
10762
11161
  */
10763
- async _waitForTaskUpdate(taskId, signal) {
11162
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
10764
11163
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
10765
11164
  try {
10766
- const task = await this._taskStore?.getTask(taskId);
11165
+ const task = await this._taskStore?.getTask(taskId, sessionId);
10767
11166
  if (task?.pollInterval) {
10768
11167
  interval = task.pollInterval;
10769
11168
  }
@@ -11249,16 +11648,7 @@ var Server = class extends Protocol {
11249
11648
  if (!methodSchema) {
11250
11649
  throw new Error("Schema is missing a method literal");
11251
11650
  }
11252
- let methodValue;
11253
- if (isZ4Schema(methodSchema)) {
11254
- const v4Schema = methodSchema;
11255
- const v4Def = v4Schema._zod?.def;
11256
- methodValue = v4Def?.value ?? v4Schema.value;
11257
- } else {
11258
- const v3Schema = methodSchema;
11259
- const legacyDef = v3Schema._def;
11260
- methodValue = legacyDef?.value ?? v3Schema.value;
11261
- }
11651
+ const methodValue = getLiteralValue(methodSchema);
11262
11652
  if (typeof methodValue !== "string") {
11263
11653
  throw new Error("Schema method literal must be a string");
11264
11654
  }
@@ -11652,6 +12042,42 @@ var ExperimentalMcpServerTasks = class {
11652
12042
 
11653
12043
  // ../node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
11654
12044
  import { ZodOptional } from "zod";
12045
+ function toolInputElementCount(value, max) {
12046
+ let count = 0;
12047
+ const stack = [value];
12048
+ while (stack.length > 0) {
12049
+ const node = stack.pop();
12050
+ if (node === null || typeof node !== "object")
12051
+ continue;
12052
+ if (Array.isArray(node)) {
12053
+ for (const child of node) {
12054
+ if (++count > max)
12055
+ return count;
12056
+ if (child !== null && typeof child === "object")
12057
+ stack.push(child);
12058
+ }
12059
+ } else {
12060
+ for (const key in node) {
12061
+ if (!Object.prototype.hasOwnProperty.call(node, key))
12062
+ continue;
12063
+ if (++count > max)
12064
+ return count;
12065
+ const child = node[key];
12066
+ if (child !== null && typeof child === "object")
12067
+ stack.push(child);
12068
+ }
12069
+ }
12070
+ }
12071
+ return count;
12072
+ }
12073
+ function resolveMaxToolInputElements(value) {
12074
+ if (value === void 0 || value === Infinity)
12075
+ return void 0;
12076
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
12077
+ throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
12078
+ }
12079
+ return value;
12080
+ }
11655
12081
  var McpServer = class {
11656
12082
  constructor(serverInfo, options) {
11657
12083
  this._registeredResources = {};
@@ -11663,6 +12089,16 @@ var McpServer = class {
11663
12089
  this._resourceHandlersInitialized = false;
11664
12090
  this._promptHandlersInitialized = false;
11665
12091
  this.server = new Server(serverInfo, options);
12092
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
12093
+ if (options?.capabilities?.tools) {
12094
+ this.setToolRequestHandlers();
12095
+ }
12096
+ if (options?.capabilities?.resources) {
12097
+ this.setResourceRequestHandlers();
12098
+ }
12099
+ if (options?.capabilities?.prompts) {
12100
+ this.setPromptRequestHandlers();
12101
+ }
11666
12102
  }
11667
12103
  /**
11668
12104
  * Access experimental features.
@@ -11793,12 +12229,15 @@ var McpServer = class {
11793
12229
  * Validates tool input arguments against the tool's input schema.
11794
12230
  */
11795
12231
  async validateToolInput(tool, args, toolName) {
12232
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
12233
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
12234
+ }
11796
12235
  if (!tool.inputSchema) {
11797
12236
  return void 0;
11798
12237
  }
11799
12238
  const inputObj = normalizeObjectSchema(tool.inputSchema);
11800
12239
  const schemaToParse = inputObj ?? tool.inputSchema;
11801
- const parseResult = await safeParseAsync2(schemaToParse, args);
12240
+ const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
11802
12241
  if (!parseResult.success) {
11803
12242
  const error = "error" in parseResult ? parseResult.error : "Unknown error";
11804
12243
  const errorMessage = getParseErrorMessage(error);
@@ -12036,7 +12475,7 @@ var McpServer = class {
12036
12475
  }
12037
12476
  if (prompt.argsSchema) {
12038
12477
  const argsObj = normalizeObjectSchema(prompt.argsSchema);
12039
- const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
12478
+ const parseResult = await safeParseAsync2(argsObj, request.params.arguments ?? {});
12040
12479
  if (!parseResult.success) {
12041
12480
  const error = "error" in parseResult ? parseResult.error : "Unknown error";
12042
12481
  const errorMessage = getParseErrorMessage(error);
@@ -12447,8 +12886,17 @@ var EMPTY_COMPLETION_RESULT = {
12447
12886
  import process2 from "node:process";
12448
12887
 
12449
12888
  // ../node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
12889
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
12450
12890
  var ReadBuffer = class {
12891
+ constructor(options) {
12892
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
12893
+ }
12451
12894
  append(chunk) {
12895
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
12896
+ if (newSize > this._maxBufferSize) {
12897
+ this.clear();
12898
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
12899
+ }
12452
12900
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
12453
12901
  }
12454
12902
  readMessage() {
@@ -12476,18 +12924,24 @@ function serializeMessage(message) {
12476
12924
 
12477
12925
  // ../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
12478
12926
  var StdioServerTransport = class {
12479
- constructor(_stdin = process2.stdin, _stdout = process2.stdout) {
12927
+ constructor(_stdin = process2.stdin, _stdout = process2.stdout, options) {
12480
12928
  this._stdin = _stdin;
12481
12929
  this._stdout = _stdout;
12482
- this._readBuffer = new ReadBuffer();
12483
12930
  this._started = false;
12484
12931
  this._ondata = (chunk) => {
12485
- this._readBuffer.append(chunk);
12486
- this.processReadBuffer();
12932
+ try {
12933
+ this._readBuffer.append(chunk);
12934
+ this.processReadBuffer();
12935
+ } catch (error) {
12936
+ this.onerror?.(error);
12937
+ this.close().catch(() => {
12938
+ });
12939
+ }
12487
12940
  };
12488
12941
  this._onerror = (error) => {
12489
12942
  this.onerror?.(error);
12490
12943
  };
12944
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
12491
12945
  }
12492
12946
  /**
12493
12947
  * Starts listening for messages on stdin.
@@ -12538,98 +12992,119 @@ var StdioServerTransport = class {
12538
12992
  // src/index.ts
12539
12993
  import { z as z2 } from "zod";
12540
12994
 
12541
- // src/claims.ts
12542
- import { MerkleTree } from "merkletreejs";
12543
- import { AbiCoder, Contract, JsonRpcProvider, Wallet as Wallet2, concat, getAddress as getAddress2, getBytes, keccak256, parseUnits } from "ethers";
12544
- import { readFile as readFile4 } from "node:fs/promises";
12545
- import path5 from "node:path";
12546
-
12547
- // src/collector.ts
12548
- import { createHash as createHash2, createPublicKey, generateKeyPairSync, sign, verify } from "node:crypto";
12549
- import { mkdir, readFile, writeFile } from "node:fs/promises";
12550
- import path from "node:path";
12551
-
12552
- // src/canonical.ts
12553
- import { createHash } from "node:crypto";
12554
- function canonicalJson(value) {
12555
- if (value === null || typeof value !== "object") {
12556
- return JSON.stringify(value);
12557
- }
12558
- if (Array.isArray(value)) {
12559
- return `[${value.map(canonicalJson).join(",")}]`;
12995
+ // src/bounded-input.ts
12996
+ import { Transform } from "node:stream";
12997
+ var BoundedStdioInput = class extends Transform {
12998
+ constructor(maxLineBytes = 1024 * 1024) {
12999
+ super();
13000
+ this.maxLineBytes = maxLineBytes;
13001
+ }
13002
+ maxLineBytes;
13003
+ bytes = 0;
13004
+ _transform(chunk, _encoding, callback) {
13005
+ for (const byte of chunk) {
13006
+ if (byte === 10) this.bytes = 0;
13007
+ else if (++this.bytes > this.maxLineBytes) {
13008
+ callback(
13009
+ new Error("AIPOU_INPUT_TOO_LARGE: JSON-RPC line exceeds 1 MiB")
13010
+ );
13011
+ return;
13012
+ }
13013
+ }
13014
+ callback(null, chunk);
12560
13015
  }
12561
- const record2 = value;
12562
- return `{${Object.keys(record2).filter((key) => record2[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
12563
- }
12564
- function sha256Hex(value) {
12565
- return `0x${createHash("sha256").update(value).digest("hex")}`;
12566
- }
13016
+ };
12567
13017
 
12568
- // src/secure-file.ts
12569
- import { execFile } from "node:child_process";
12570
- import { promisify } from "node:util";
12571
- var execFileAsync = promisify(execFile);
12572
- async function restrictToCurrentUser(filePath) {
12573
- if (process.platform !== "win32") return true;
12574
- const user = process.env.USERNAME;
12575
- if (!user) return false;
12576
- const principal = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${user}` : user;
12577
- try {
12578
- await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${principal}:F`]);
12579
- return true;
12580
- } catch {
12581
- return false;
13018
+ // src/version.ts
13019
+ import { readFile } from "node:fs/promises";
13020
+ var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
13021
+ async function getPackageVersion() {
13022
+ const packageUrl = new URL("../package.json", import.meta.url);
13023
+ const metadata = JSON.parse(await readFile(packageUrl, "utf8"));
13024
+ if (typeof metadata.version !== "string" || !SEMVER.test(metadata.version)) {
13025
+ throw new Error("mcp-server/package.json must contain a valid semantic version");
12582
13026
  }
13027
+ return metadata.version;
12583
13028
  }
12584
13029
 
12585
- // src/collector.ts
12586
- function dataDir() {
12587
- return path.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
12588
- }
12589
- function privateKeyPath() {
12590
- return path.join(dataDir(), "collector-ed25519-private.pem");
12591
- }
12592
- function publicKeyPath() {
12593
- return path.join(dataDir(), "collector-ed25519-public.pem");
12594
- }
12595
- async function ensureCollectorKeys() {
12596
- await mkdir(dataDir(), { recursive: true });
13030
+ // src/store.ts
13031
+ import { mkdir as mkdir2, readFile as readFile4, readdir } from "node:fs/promises";
13032
+ import path3 from "node:path";
13033
+
13034
+ // src/lock.ts
13035
+ import { readFile as readFile2, rm, stat, writeFile } from "node:fs/promises";
13036
+ var STALE_LOCK_MS = 15e3;
13037
+ var ACQUIRE_TIMEOUT_MS = 1e4;
13038
+ async function reclaimDeadLock(lockPath2) {
13039
+ const gate = `${lockPath2}.reclaim`;
12597
13040
  try {
12598
- const [privateKey2, publicKey2] = await Promise.all([
12599
- readFile(privateKeyPath(), "utf8"),
12600
- readFile(publicKeyPath(), "utf8")
12601
- ]);
12602
- return { privateKey: privateKey2, publicKey: publicKey2 };
13041
+ await writeFile(gate, `${process.pid}
13042
+ `, { flag: "wx", mode: 384 });
13043
+ } catch (error) {
13044
+ if (error.code === "EEXIST") return;
13045
+ throw error;
13046
+ }
13047
+ try {
13048
+ const info = await stat(lockPath2);
13049
+ if (Date.now() - info.mtimeMs <= STALE_LOCK_MS) return;
13050
+ const pid = Number((await readFile2(lockPath2, "utf8")).trim());
13051
+ if (!Number.isSafeInteger(pid) || pid <= 0) return;
13052
+ try {
13053
+ process.kill(pid, 0);
13054
+ } catch (error) {
13055
+ if (error.code === "ESRCH") await rm(lockPath2);
13056
+ }
12603
13057
  } catch (error) {
12604
13058
  if (error.code !== "ENOENT") throw error;
13059
+ } finally {
13060
+ await rm(gate, { force: true });
12605
13061
  }
12606
- const pair = generateKeyPairSync("ed25519");
12607
- const privateKey = pair.privateKey.export({ format: "pem", type: "pkcs8" }).toString();
12608
- const publicKey = pair.publicKey.export({ format: "pem", type: "spki" }).toString();
12609
- await writeFile(privateKeyPath(), privateKey, { encoding: "utf8", mode: 384, flag: "wx" });
12610
- await restrictToCurrentUser(privateKeyPath());
12611
- await writeFile(publicKeyPath(), publicKey, { encoding: "utf8", flag: "wx" });
12612
- return { privateKey, publicKey };
12613
13062
  }
12614
- async function getCollectorPublicKey() {
12615
- return (await ensureCollectorKeys()).publicKey;
12616
- }
12617
- function collectorFingerprint(publicKey) {
12618
- const der = createPublicKey(publicKey).export({ format: "der", type: "spki" });
12619
- return `sha256:${createHash2("sha256").update(der).digest("hex")}`;
12620
- }
12621
- async function signCollectorPayload(payload) {
12622
- const { privateKey } = await ensureCollectorKeys();
12623
- return sign(null, Buffer.from(canonicalJson(payload)), privateKey).toString("base64");
13063
+ async function acquire(lockPath2) {
13064
+ const deadline = Date.now() + ACQUIRE_TIMEOUT_MS;
13065
+ for (; ; ) {
13066
+ try {
13067
+ await writeFile(lockPath2, `${process.pid}
13068
+ `, {
13069
+ encoding: "utf8",
13070
+ flag: "wx"
13071
+ });
13072
+ return;
13073
+ } catch (error) {
13074
+ if (error.code !== "EEXIST") throw error;
13075
+ }
13076
+ try {
13077
+ const info = await stat(lockPath2);
13078
+ if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
13079
+ await reclaimDeadLock(lockPath2);
13080
+ }
13081
+ } catch (error) {
13082
+ if (error.code === "ENOENT") continue;
13083
+ throw error;
13084
+ }
13085
+ if (Date.now() > deadline) {
13086
+ throw new Error(
13087
+ "AIPOU operation is busy; retry when the other process finishes. Never remove an active lock."
13088
+ );
13089
+ }
13090
+ await new Promise(
13091
+ (resolve) => setTimeout(resolve, 25 + Math.floor(Math.random() * 75))
13092
+ );
13093
+ }
12624
13094
  }
12625
- function verifyCollectorPayload(payload, signature, publicKey) {
12626
- return verify(null, Buffer.from(canonicalJson(payload)), publicKey, Buffer.from(signature, "base64"));
13095
+ async function withFileLock(lockPath2, operation) {
13096
+ await acquire(lockPath2);
13097
+ try {
13098
+ return await operation();
13099
+ } finally {
13100
+ await rm(lockPath2, { force: true });
13101
+ }
12627
13102
  }
12628
13103
 
12629
13104
  // src/identity.ts
12630
13105
  import { randomBytes } from "node:crypto";
12631
13106
  import { readFileSync } from "node:fs";
12632
- import path2 from "node:path";
13107
+ import path from "node:path";
12633
13108
  import { Wallet, getAddress, hexlify, isAddress, verifyTypedData } from "ethers";
12634
13109
  var authorizationTypes = {
12635
13110
  TaskAuthorization: [
@@ -12641,9 +13116,25 @@ var authorizationTypes = {
12641
13116
  { name: "issuedAt", type: "uint256" }
12642
13117
  ]
12643
13118
  };
13119
+ var DEFAULT_TASK_SESSION_TTL_SECONDS = 24 * 60 * 60;
13120
+ function taskSessionTtlSeconds() {
13121
+ const configured = process.env.AIPOU_TASK_SESSION_TTL_SECONDS;
13122
+ if (!configured) return DEFAULT_TASK_SESSION_TTL_SECONDS;
13123
+ const value = Number.parseInt(configured, 10);
13124
+ if (!Number.isInteger(value) || value < 60 || value > 7 * DEFAULT_TASK_SESSION_TTL_SECONDS) {
13125
+ throw new Error("AIPOU_TASK_SESSION_TTL_SECONDS must be an integer from 60 through 604800");
13126
+ }
13127
+ return value;
13128
+ }
13129
+ function taskSessionExpiry(session) {
13130
+ return session.expiresAt ?? session.issuedAt + taskSessionTtlSeconds();
13131
+ }
13132
+ function isTaskSessionExpired(session, nowSeconds = Math.floor(Date.now() / 1e3)) {
13133
+ return nowSeconds >= taskSessionExpiry(session);
13134
+ }
12644
13135
  function readKeyFile(keyFile) {
12645
13136
  try {
12646
- return readFileSync(path2.resolve(keyFile), "utf8").trim();
13137
+ return readFileSync(path.resolve(keyFile), "utf8").trim();
12647
13138
  } catch {
12648
13139
  throw new Error(
12649
13140
  `AIPOU_AGENT_KEY_FILE could not be read (${keyFile}); run aipou-mcp --init to create a dedicated wallet key`
@@ -12681,7 +13172,14 @@ async function createTaskSession(input) {
12681
13172
  issuedAt
12682
13173
  };
12683
13174
  const walletAuthorization = await wallet.signTypedData(domain, authorizationTypes, value);
12684
- return { ...input, wallet: wallet.address, nonce, issuedAt, walletAuthorization };
13175
+ return {
13176
+ ...input,
13177
+ wallet: wallet.address,
13178
+ nonce,
13179
+ issuedAt,
13180
+ expiresAt: issuedAt + taskSessionTtlSeconds(),
13181
+ walletAuthorization
13182
+ };
12685
13183
  }
12686
13184
  function verifyTaskAuthorization(session) {
12687
13185
  const domain = {
@@ -12701,6 +13199,315 @@ function verifyTaskAuthorization(session) {
12701
13199
  return verifyTypedData(domain, authorizationTypes, value, session.walletAuthorization) === session.wallet;
12702
13200
  }
12703
13201
 
13202
+ // src/canonical.ts
13203
+ import { createHash } from "node:crypto";
13204
+ function canonicalJson(value) {
13205
+ if (value === null || typeof value !== "object") {
13206
+ return JSON.stringify(value);
13207
+ }
13208
+ if (Array.isArray(value)) {
13209
+ return `[${value.map(canonicalJson).join(",")}]`;
13210
+ }
13211
+ const record2 = value;
13212
+ return `{${Object.keys(record2).filter((key) => record2[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record2[key])}`).join(",")}}`;
13213
+ }
13214
+ function sha256Hex(value) {
13215
+ return `0x${createHash("sha256").update(value).digest("hex")}`;
13216
+ }
13217
+
13218
+ // src/private-json.ts
13219
+ import { mkdir, open, rename, readFile as readFile3 } from "node:fs/promises";
13220
+ import path2 from "node:path";
13221
+
13222
+ // src/secure-file.ts
13223
+ import { execFile } from "node:child_process";
13224
+ import { promisify } from "node:util";
13225
+ var execFileAsync = promisify(execFile);
13226
+ async function restrictToCurrentUser(filePath) {
13227
+ if (process.platform !== "win32") return true;
13228
+ const user = process.env.USERNAME;
13229
+ if (!user) return false;
13230
+ const principal = process.env.USERDOMAIN ? `${process.env.USERDOMAIN}\\${user}` : user;
13231
+ try {
13232
+ await execFileAsync("icacls", [filePath, "/inheritance:r", "/grant:r", `${principal}:F`]);
13233
+ return true;
13234
+ } catch {
13235
+ return false;
13236
+ }
13237
+ }
13238
+
13239
+ // src/private-json.ts
13240
+ async function writePrivateJson(file, value) {
13241
+ await mkdir(path2.dirname(file), { recursive: true, mode: 448 });
13242
+ const temp = `${file}.${process.pid}.tmp`;
13243
+ const handle = await open(temp, "w", 384);
13244
+ try {
13245
+ if (process.platform !== "win32") await handle.chmod(384);
13246
+ await handle.writeFile(JSON.stringify(value, null, 2) + "\n");
13247
+ await handle.sync();
13248
+ } finally {
13249
+ await handle.close();
13250
+ }
13251
+ if (!await restrictToCurrentUser(temp))
13252
+ throw new Error(
13253
+ "Could not restrict private state file access; update was not committed"
13254
+ );
13255
+ await rename(temp, file);
13256
+ if (process.platform !== "win32") {
13257
+ const directory = await open(path2.dirname(file), "r");
13258
+ try {
13259
+ await directory.sync();
13260
+ } finally {
13261
+ await directory.close();
13262
+ }
13263
+ }
13264
+ }
13265
+ async function readOptionalJson(file) {
13266
+ try {
13267
+ return JSON.parse(await readFile3(file, "utf8"));
13268
+ } catch (error) {
13269
+ if (error.code === "ENOENT") return null;
13270
+ throw error;
13271
+ }
13272
+ }
13273
+
13274
+ // src/store.ts
13275
+ var emptyState = () => ({ sessions: {}, receipts: [], completedEvidence: {}, batches: [] });
13276
+ var mutationQueue = Promise.resolve();
13277
+ function dataDir() {
13278
+ return path3.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
13279
+ }
13280
+ function statePath() {
13281
+ return path3.join(dataDir(), "state.json");
13282
+ }
13283
+ function lockPath() {
13284
+ return `${statePath()}.lock`;
13285
+ }
13286
+ function archiveDir() {
13287
+ return path3.join(dataDir(), "settled");
13288
+ }
13289
+ async function readState() {
13290
+ try {
13291
+ return JSON.parse(await readFile4(statePath(), "utf8"));
13292
+ } catch (error) {
13293
+ if (error.code === "ENOENT") return emptyState();
13294
+ throw error;
13295
+ }
13296
+ }
13297
+ async function writeState(state) {
13298
+ await writePrivateJson(statePath(), state);
13299
+ }
13300
+ async function mutate(operation) {
13301
+ const next = mutationQueue.then(async () => {
13302
+ await mkdir2(dataDir(), { recursive: true });
13303
+ return withFileLock(lockPath(), async () => {
13304
+ const state = await readState();
13305
+ const result = await operation(state);
13306
+ await writeState(state);
13307
+ return result;
13308
+ });
13309
+ });
13310
+ mutationQueue = next.catch(() => void 0);
13311
+ return next;
13312
+ }
13313
+ async function saveSession(session) {
13314
+ await mutate((state) => {
13315
+ if (state.sessions[session.nonce]) throw new Error("Task nonce already exists");
13316
+ state.sessions[session.nonce] = session;
13317
+ });
13318
+ }
13319
+ async function getSession(nonce) {
13320
+ return (await readState()).sessions[nonce] || null;
13321
+ }
13322
+ async function saveCompletedReceipt(evidenceKey, receipt) {
13323
+ await mutate((state) => {
13324
+ const session = state.sessions[receipt.nonce];
13325
+ if (!session) throw new Error("Unknown task nonce");
13326
+ if (session.completedAt) throw new Error("Task nonce has already been completed; recover its stored receipt with export_ai_receipts and match the nonce.");
13327
+ if (state.completedEvidence[evidenceKey]) {
13328
+ throw new Error(`Duplicate task/output evidence already recorded as ${state.completedEvidence[evidenceKey]}`);
13329
+ }
13330
+ if (state.receipts.some((item) => item.receiptId === receipt.receiptId)) {
13331
+ throw new Error("Receipt id has already been recorded");
13332
+ }
13333
+ if ((state.settledReceiptIds ?? []).includes(receipt.receiptId)) {
13334
+ throw new Error("Receipt id has already been settled and archived");
13335
+ }
13336
+ session.completedAt = receipt.recordedAt;
13337
+ state.completedEvidence[evidenceKey] = receipt.receiptId;
13338
+ state.receipts.push(receipt);
13339
+ });
13340
+ }
13341
+ async function readArchivedReceipts() {
13342
+ let files;
13343
+ try {
13344
+ files = await readdir(archiveDir());
13345
+ } catch (error) {
13346
+ if (error.code === "ENOENT") return [];
13347
+ throw error;
13348
+ }
13349
+ const receipts = [];
13350
+ for (const file of files.filter((name) => name.endsWith(".json")).sort()) {
13351
+ receipts.push(...JSON.parse(await readFile4(path3.join(archiveDir(), file), "utf8")));
13352
+ }
13353
+ return receipts;
13354
+ }
13355
+ async function exportStoredReceipts(wallet) {
13356
+ const unique = /* @__PURE__ */ new Map();
13357
+ for (const receipt of [...(await readState()).receipts, ...await readArchivedReceipts()]) {
13358
+ const prior = unique.get(receipt.receiptId);
13359
+ if (!prior?.claimTransaction) unique.set(receipt.receiptId, receipt);
13360
+ }
13361
+ const receipts = [...unique.values()];
13362
+ if (!wallet) return receipts;
13363
+ return receipts.filter((receipt) => receipt.wallet.toLowerCase() === wallet.toLowerCase());
13364
+ }
13365
+ async function unsettledReceipts(limit) {
13366
+ return (await readState()).receipts.filter((receipt) => !receipt.claimTransaction).slice(0, limit);
13367
+ }
13368
+ async function protocolStateCounts() {
13369
+ const state = await readState();
13370
+ const sessions = Object.values(state.sessions);
13371
+ return {
13372
+ sessions: sessions.length,
13373
+ completedSessions: sessions.filter((session) => session.completedAt).length,
13374
+ incompleteSessions: sessions.filter((session) => !session.completedAt).length,
13375
+ activeSessions: sessions.filter((session) => !session.completedAt && !isTaskSessionExpired(session)).length,
13376
+ expiredSessions: sessions.filter((session) => !session.completedAt && isTaskSessionExpired(session)).length,
13377
+ batches: state.batches.length
13378
+ };
13379
+ }
13380
+ async function settledReceiptsSince(sinceMs) {
13381
+ const receipts = await exportStoredReceipts();
13382
+ return receipts.filter(
13383
+ (receipt) => receipt.claimTransaction && Date.parse(receipt.recordedAt) >= sinceMs
13384
+ );
13385
+ }
13386
+ async function markBatchSettled(batch) {
13387
+ await mutate(async (state) => {
13388
+ const existing = state.batches.find((item) => item.root === batch.root);
13389
+ if (existing) {
13390
+ if (existing.claimTransaction !== batch.claimTransaction) throw new Error("Settlement archive transaction mismatch");
13391
+ return;
13392
+ }
13393
+ const included = new Set(batch.receiptIds);
13394
+ const settled = [];
13395
+ for (const receipt of state.receipts) {
13396
+ if (included.has(receipt.receiptId)) {
13397
+ if (receipt.claimTransaction) throw new Error(`Receipt ${receipt.receiptId} is already settled`);
13398
+ receipt.batchRoot = batch.root;
13399
+ receipt.claimTransaction = batch.claimTransaction;
13400
+ settled.push(receipt);
13401
+ }
13402
+ }
13403
+ await mkdir2(archiveDir(), { recursive: true });
13404
+ const archivePath = path3.join(archiveDir(), `batch-${batch.root.slice(2)}.json`);
13405
+ try {
13406
+ const prior = JSON.parse(await readFile4(archivePath, "utf8"));
13407
+ if (canonicalJson(prior) !== canonicalJson(settled)) throw new Error("Settlement archive does not match saved batch");
13408
+ } catch (error) {
13409
+ if (error.code !== "ENOENT") throw error;
13410
+ await writePrivateJson(archivePath, settled);
13411
+ }
13412
+ state.receipts = state.receipts.filter((receipt) => !included.has(receipt.receiptId));
13413
+ state.settledReceiptIds = [...state.settledReceiptIds ?? [], ...batch.receiptIds];
13414
+ state.batches.push(batch);
13415
+ });
13416
+ }
13417
+
13418
+ // src/feedback.ts
13419
+ async function localFeedbackSummary() {
13420
+ const receipts = await exportStoredReceipts();
13421
+ const counts = await protocolStateCounts();
13422
+ const activeDays = new Set(receipts.map((r) => r.recordedAt.slice(0, 10)));
13423
+ return {
13424
+ schema: "aipou-local-feedback-v1",
13425
+ version: await getPackageVersion(),
13426
+ platform: process.platform,
13427
+ node: process.versions.node,
13428
+ localReceiptCount: receipts.length,
13429
+ localActiveDays: activeDays.size,
13430
+ completedSessions: counts.completedSessions,
13431
+ activeSessions: counts.activeSessions,
13432
+ expiredSessions: counts.expiredSessions,
13433
+ measuredUsageReceipts: receipts.filter(
13434
+ (r) => r.tokenUsageMeasurement === "measured"
13435
+ ).length,
13436
+ unmeasuredUsageReceipts: receipts.filter(
13437
+ (r) => r.tokenUsageMeasurement === "unmeasured"
13438
+ ).length,
13439
+ unspecifiedUsageReceipts: receipts.filter(
13440
+ (r) => !r.tokenUsageMeasurement || r.tokenUsageMeasurement === "unspecified"
13441
+ ).length,
13442
+ transmission: "none; review before sharing voluntarily",
13443
+ interpretation: "one local installation; not unique-user, organic-adoption or revenue metrics"
13444
+ };
13445
+ }
13446
+
13447
+ // src/claims.ts
13448
+ import { MerkleTree } from "merkletreejs";
13449
+ import {
13450
+ AbiCoder,
13451
+ Contract as Contract2,
13452
+ JsonRpcProvider as JsonRpcProvider2,
13453
+ Transaction as Transaction2,
13454
+ Wallet as Wallet3,
13455
+ concat,
13456
+ getAddress as getAddress2,
13457
+ getBytes,
13458
+ keccak256,
13459
+ parseUnits
13460
+ } from "ethers";
13461
+ import { mkdir as mkdir4, readFile as readFile7, rm as rm2 } from "node:fs/promises";
13462
+ import path6 from "node:path";
13463
+
13464
+ // src/collector.ts
13465
+ import { createHash as createHash2, createPublicKey, generateKeyPairSync, sign, verify } from "node:crypto";
13466
+ import { mkdir as mkdir3, readFile as readFile5, writeFile as writeFile2 } from "node:fs/promises";
13467
+ import path4 from "node:path";
13468
+ function dataDir2() {
13469
+ return path4.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
13470
+ }
13471
+ function privateKeyPath() {
13472
+ return path4.join(dataDir2(), "collector-ed25519-private.pem");
13473
+ }
13474
+ function publicKeyPath() {
13475
+ return path4.join(dataDir2(), "collector-ed25519-public.pem");
13476
+ }
13477
+ async function ensureCollectorKeys() {
13478
+ await mkdir3(dataDir2(), { recursive: true });
13479
+ try {
13480
+ const [privateKey2, publicKey2] = await Promise.all([
13481
+ readFile5(privateKeyPath(), "utf8"),
13482
+ readFile5(publicKeyPath(), "utf8")
13483
+ ]);
13484
+ return { privateKey: privateKey2, publicKey: publicKey2 };
13485
+ } catch (error) {
13486
+ if (error.code !== "ENOENT") throw error;
13487
+ }
13488
+ const pair = generateKeyPairSync("ed25519");
13489
+ const privateKey = pair.privateKey.export({ format: "pem", type: "pkcs8" }).toString();
13490
+ const publicKey = pair.publicKey.export({ format: "pem", type: "spki" }).toString();
13491
+ await writeFile2(privateKeyPath(), privateKey, { encoding: "utf8", mode: 384, flag: "wx" });
13492
+ await restrictToCurrentUser(privateKeyPath());
13493
+ await writeFile2(publicKeyPath(), publicKey, { encoding: "utf8", flag: "wx" });
13494
+ return { privateKey, publicKey };
13495
+ }
13496
+ async function getCollectorPublicKey() {
13497
+ return (await ensureCollectorKeys()).publicKey;
13498
+ }
13499
+ function collectorFingerprint(publicKey) {
13500
+ const der = createPublicKey(publicKey).export({ format: "der", type: "spki" });
13501
+ return `sha256:${createHash2("sha256").update(der).digest("hex")}`;
13502
+ }
13503
+ async function signCollectorPayload(payload) {
13504
+ const { privateKey } = await ensureCollectorKeys();
13505
+ return sign(null, Buffer.from(canonicalJson(payload)), privateKey).toString("base64");
13506
+ }
13507
+ function verifyCollectorPayload(payload, signature, publicKey) {
13508
+ return verify(null, Buffer.from(canonicalJson(payload)), publicKey, Buffer.from(signature, "base64"));
13509
+ }
13510
+
12704
13511
  // src/policy.ts
12705
13512
  var DAY_MS = 24 * 60 * 60 * 1e3;
12706
13513
  function positiveIntFromEnv(name, fallback) {
@@ -12755,12 +13562,12 @@ function filterEligibleReceipts(candidates, recentlySettled, policy, now = Date.
12755
13562
 
12756
13563
  // src/providers.ts
12757
13564
  import { verify as verify2 } from "node:crypto";
12758
- import { readFile as readFile2 } from "node:fs/promises";
12759
- import path3 from "node:path";
13565
+ import { readFile as readFile6 } from "node:fs/promises";
13566
+ import path5 from "node:path";
12760
13567
  async function configuredKeys() {
12761
13568
  const file = process.env.AIPOU_PROVIDER_KEYS_FILE;
12762
13569
  if (!file) return {};
12763
- return JSON.parse(await readFile2(path3.resolve(file), "utf8"));
13570
+ return JSON.parse(await readFile6(path5.resolve(file), "utf8"));
12764
13571
  }
12765
13572
  function providerAssertion(session, input) {
12766
13573
  return {
@@ -12787,196 +13594,303 @@ async function deriveTrustTier(session, input, evidence) {
12787
13594
  return "provider_signed";
12788
13595
  }
12789
13596
 
12790
- // src/store.ts
12791
- import { mkdir as mkdir2, readFile as readFile3, readdir, rename, writeFile as writeFile3 } from "node:fs/promises";
12792
- import path4 from "node:path";
12793
-
12794
- // src/lock.ts
12795
- import { rm, stat, writeFile as writeFile2 } from "node:fs/promises";
12796
- var STALE_LOCK_MS = 15e3;
12797
- var ACQUIRE_TIMEOUT_MS = 1e4;
12798
- async function acquire(lockPath2) {
12799
- const deadline = Date.now() + ACQUIRE_TIMEOUT_MS;
12800
- for (; ; ) {
12801
- try {
12802
- await writeFile2(lockPath2, `${process.pid}
12803
- `, { encoding: "utf8", flag: "wx" });
12804
- return;
12805
- } catch (error) {
12806
- if (error.code !== "EEXIST") throw error;
12807
- }
12808
- try {
12809
- const info = await stat(lockPath2);
12810
- if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {
12811
- await rm(lockPath2, { force: true });
12812
- continue;
12813
- }
12814
- } catch {
12815
- continue;
12816
- }
12817
- if (Date.now() > deadline) {
12818
- throw new Error(`Timed out waiting for lock ${lockPath2}; remove it manually if no other AIPOU process is running`);
12819
- }
12820
- await new Promise((resolve) => setTimeout(resolve, 25 + Math.floor(Math.random() * 75)));
12821
- }
12822
- }
12823
- async function withFileLock(lockPath2, operation) {
12824
- await acquire(lockPath2);
12825
- try {
12826
- return await operation();
12827
- } finally {
12828
- await rm(lockPath2, { force: true });
12829
- }
12830
- }
12831
-
12832
- // src/store.ts
12833
- var emptyState = () => ({ sessions: {}, receipts: [], completedEvidence: {}, batches: [] });
12834
- var mutationQueue = Promise.resolve();
12835
- function dataDir2() {
12836
- return path4.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
12837
- }
12838
- function statePath() {
12839
- return path4.join(dataDir2(), "state.json");
12840
- }
12841
- function lockPath() {
12842
- return `${statePath()}.lock`;
12843
- }
12844
- function archiveDir() {
12845
- return path4.join(dataDir2(), "settled");
12846
- }
12847
- async function readState() {
12848
- try {
12849
- return JSON.parse(await readFile3(statePath(), "utf8"));
12850
- } catch (error) {
12851
- if (error.code === "ENOENT") return emptyState();
12852
- throw error;
13597
+ // src/settlement-runner.ts
13598
+ async function executeSettlementPlan(plan, driver, persist) {
13599
+ const execute = async (kind, indices2) => {
13600
+ let tx = plan[kind];
13601
+ if (!tx) {
13602
+ tx = await driver.prepare(kind, plan, indices2);
13603
+ plan[kind] = tx;
13604
+ await persist(plan);
13605
+ }
13606
+ let result = await driver.receipt(tx.hash);
13607
+ if (!result) {
13608
+ await driver.broadcast(tx);
13609
+ result = await driver.wait(tx.hash);
13610
+ }
13611
+ if (!result)
13612
+ throw new Error(
13613
+ "SETTLEMENT_UNCONFIRMED: rerun to reconcile the saved transaction; do not replace it."
13614
+ );
13615
+ if (result.hash.toLowerCase() !== tx.hash.toLowerCase() || result.status !== 1) {
13616
+ throw new Error(
13617
+ "SETTLEMENT_REVERTED: inspect the saved hash before an operator resets the journal."
13618
+ );
13619
+ }
13620
+ return result.hash;
13621
+ };
13622
+ if (plan.claim) {
13623
+ const claimTransaction2 = await execute("claim", []);
13624
+ return { publishTransaction: plan.publish?.hash ?? "", claimTransaction: claimTransaction2 };
12853
13625
  }
12854
- }
12855
- async function writeState(state) {
12856
- const temporary = `${statePath()}.${process.pid}.tmp`;
12857
- await writeFile3(temporary, `${JSON.stringify(state, null, 2)}
12858
- `, { encoding: "utf8", mode: 384 });
12859
- await rename(temporary, statePath());
12860
- }
12861
- async function mutate(operation) {
12862
- const next = mutationQueue.then(async () => {
12863
- await mkdir2(dataDir2(), { recursive: true });
12864
- return withFileLock(lockPath(), async () => {
12865
- const state = await readState();
12866
- const result = await operation(state);
12867
- await writeState(state);
12868
- return result;
12869
- });
12870
- });
12871
- mutationQueue = next.catch(() => void 0);
12872
- return next;
12873
- }
12874
- async function saveSession(session) {
12875
- await mutate((state) => {
12876
- if (state.sessions[session.nonce]) throw new Error("Task nonce already exists");
12877
- state.sessions[session.nonce] = session;
12878
- });
12879
- }
12880
- async function getSession(nonce) {
12881
- return (await readState()).sessions[nonce] || null;
12882
- }
12883
- async function saveCompletedReceipt(evidenceKey, receipt) {
12884
- await mutate((state) => {
12885
- const session = state.sessions[receipt.nonce];
12886
- if (!session) throw new Error("Unknown task nonce");
12887
- if (session.completedAt) throw new Error("Task nonce has already been completed");
12888
- if (state.completedEvidence[evidenceKey]) {
12889
- throw new Error(`Duplicate task/output evidence already recorded as ${state.completedEvidence[evidenceKey]}`);
12890
- }
12891
- if (state.receipts.some((item) => item.receiptId === receipt.receiptId)) {
12892
- throw new Error("Receipt id has already been recorded");
13626
+ const claimed = await Promise.all(
13627
+ plan.receiptIds.map((id) => driver.claimed(id))
13628
+ );
13629
+ if (claimed.some(Boolean))
13630
+ throw new Error(
13631
+ "SETTLEMENT_RECONCILIATION_REQUIRED: onchain claims exist without a saved transaction; inspect events before changing local state."
13632
+ );
13633
+ const indices = plan.receiptIds.map((_, i) => i);
13634
+ const rootApproved = await driver.approved(plan.root);
13635
+ await driver.preflight(plan, !rootApproved, indices);
13636
+ const publishTransaction = rootApproved ? plan.publish?.hash ?? "" : await execute("publish", []);
13637
+ if (!await driver.approved(plan.root))
13638
+ throw new Error("SETTLEMENT_ROOT_UNCONFIRMED: rerun to reconcile.");
13639
+ const claimTransaction = await execute("claim", indices);
13640
+ return { publishTransaction, claimTransaction };
13641
+ }
13642
+
13643
+ // src/settlement-driver.ts
13644
+ import { Contract, Transaction } from "ethers";
13645
+ var abi = [
13646
+ "function approvedRoots(bytes32) view returns(bool)",
13647
+ "function claimedReceipts(bytes32) view returns(bool)",
13648
+ "function publishRoot(bytes32)",
13649
+ "function claimBatch(bytes32,address[],uint256[],bytes32[],bytes32[][])"
13650
+ ];
13651
+ function createSettlementDriver(signer, contract) {
13652
+ const provider = signer.provider;
13653
+ const claims = new Contract(contract, abi, signer);
13654
+ const data = (kind, plan) => kind === "publish" ? claims.interface.encodeFunctionData("publishRoot", [plan.root]) : claims.interface.encodeFunctionData("claimBatch", [
13655
+ plan.root,
13656
+ plan.accounts,
13657
+ plan.amounts,
13658
+ plan.receiptIds,
13659
+ plan.proofs
13660
+ ]);
13661
+ const fees = async () => {
13662
+ const fee = await provider.getFeeData();
13663
+ const gasPrice = fee.maxFeePerGas ?? fee.gasPrice;
13664
+ if (gasPrice === null || gasPrice <= 0n)
13665
+ throw new Error(
13666
+ "SETTLEMENT_FEE_UNAVAILABLE: RPC did not supply a gas price."
13667
+ );
13668
+ return fee.maxFeePerGas !== null && fee.maxPriorityFeePerGas !== null ? {
13669
+ maxFeePerGas: fee.maxFeePerGas,
13670
+ maxPriorityFeePerGas: fee.maxPriorityFeePerGas
13671
+ } : { gasPrice };
13672
+ };
13673
+ const budget = async (cost) => {
13674
+ const cap = process.env.AIPOU_MAX_SETTLEMENT_FEE_WEI;
13675
+ if (cap && (!/^\d+$/.test(cap) || cost > BigInt(cap)))
13676
+ throw new Error(
13677
+ "SETTLEMENT_FEE_CAP: required fee exceeds AIPOU_MAX_SETTLEMENT_FEE_WEI."
13678
+ );
13679
+ if (await provider.getBalance(signer.address) < cost) {
13680
+ throw new Error(
13681
+ `SETTLEMENT_INSUFFICIENT_GAS: validator needs up to ${cost} wei for this batch including a conservative Base data-fee allowance. Fund the validator on Base and retry; no automatic transfer is performed.`
13682
+ );
12893
13683
  }
12894
- if ((state.settledReceiptIds ?? []).includes(receipt.receiptId)) {
12895
- throw new Error("Receipt id has already been settled and archived");
13684
+ };
13685
+ return {
13686
+ approved: (root) => claims.approvedRoots(root),
13687
+ claimed: (id) => claims.claimedReceipts(id),
13688
+ preflight: async (plan, publishNeeded) => {
13689
+ const fee = await fees();
13690
+ const price = "gasPrice" in fee ? fee.gasPrice : fee.maxFeePerGas;
13691
+ const claimGas = publishNeeded ? 120000n + BigInt(plan.receiptIds.length) * 100000n : await provider.estimateGas({
13692
+ from: signer.address,
13693
+ to: contract,
13694
+ data: data("claim", plan)
13695
+ });
13696
+ const pubGas = publishNeeded ? await provider.estimateGas({
13697
+ from: signer.address,
13698
+ to: contract,
13699
+ data: data("publish", plan)
13700
+ }) : 0n;
13701
+ await budget(
13702
+ (claimGas + pubGas) * price * 120n / 100n + 20000000000000n
13703
+ );
13704
+ },
13705
+ prepare: async (kind, plan) => {
13706
+ const request = {
13707
+ to: contract,
13708
+ data: data(kind, plan),
13709
+ value: 0n,
13710
+ ...await fees()
13711
+ };
13712
+ const gasLimit = await provider.estimateGas({ ...request, from: signer.address }) * 120n / 100n;
13713
+ const price = "gasPrice" in request ? request.gasPrice : request.maxFeePerGas;
13714
+ await budget(gasLimit * price + 20000000000000n);
13715
+ const populated = await signer.populateTransaction({
13716
+ ...request,
13717
+ gasLimit
13718
+ });
13719
+ const raw = await signer.signTransaction(populated);
13720
+ const hash = Transaction.from(raw).hash;
13721
+ return { hash, raw };
13722
+ },
13723
+ receipt: async (hash) => {
13724
+ const result = await provider.getTransactionReceipt(hash);
13725
+ return result ? { hash: result.hash, status: result.status } : null;
13726
+ },
13727
+ broadcast: async (tx) => {
13728
+ const decoded = Transaction.from(tx.raw);
13729
+ if (decoded.hash !== tx.hash || decoded.from !== signer.address || decoded.to?.toLowerCase() !== contract.toLowerCase() || decoded.chainId !== 8453n || decoded.value !== 0n) {
13730
+ throw new Error(
13731
+ "SETTLEMENT_JOURNAL_INVALID: saved transaction identity does not match this validator and contract."
13732
+ );
13733
+ }
13734
+ if (await provider.getTransaction(tx.hash)) return;
13735
+ try {
13736
+ await provider.broadcastTransaction(tx.raw);
13737
+ } catch {
13738
+ if (await provider.getTransaction(tx.hash)) return;
13739
+ throw new Error(
13740
+ "SETTLEMENT_BROADCAST_UNCONFIRMED: saved transaction retained; retry to reconcile. No replacement transaction was signed."
13741
+ );
13742
+ }
13743
+ },
13744
+ wait: async (hash) => {
13745
+ try {
13746
+ const result = await provider.waitForTransaction(hash, 1, 9e4);
13747
+ return result ? { hash: result.hash, status: result.status } : null;
13748
+ } catch {
13749
+ throw new Error(
13750
+ "SETTLEMENT_CONFIRMATION_UNAVAILABLE: saved transaction retained; retry to reconcile."
13751
+ );
13752
+ }
12896
13753
  }
12897
- session.completedAt = receipt.recordedAt;
12898
- state.completedEvidence[evidenceKey] = receipt.receiptId;
12899
- state.receipts.push(receipt);
12900
- });
13754
+ };
12901
13755
  }
12902
- async function readArchivedReceipts() {
12903
- let files;
12904
- try {
12905
- files = await readdir(archiveDir());
12906
- } catch (error) {
12907
- if (error.code === "ENOENT") return [];
12908
- throw error;
12909
- }
12910
- const receipts = [];
12911
- for (const file of files.filter((name) => name.endsWith(".json")).sort()) {
12912
- receipts.push(...JSON.parse(await readFile3(path4.join(archiveDir(), file), "utf8")));
12913
- }
12914
- return receipts;
13756
+
13757
+ // src/evidence.ts
13758
+ function receiptEvidenceReport(input) {
13759
+ return {
13760
+ receiptIntegrity: input.receiptIntegrity ? "VERIFIED" : "UNKNOWN",
13761
+ walletAuthorization: input.walletAuthorization ? "VERIFIED" : "UNKNOWN",
13762
+ collectorSignature: input.collectorSignature ? "VERIFIED" : "UNKNOWN",
13763
+ hostExecution: input.hostExecution ?? "UNKNOWN",
13764
+ // These require their own artifacts and verifiers. AIPOU receipts do not
13765
+ // infer them from a completed local task.
13766
+ delivery: "UNKNOWN",
13767
+ businessAcceptance: "ABSENT",
13768
+ settlement: "ABSENT",
13769
+ externalEffect: "UNKNOWN"
13770
+ };
12915
13771
  }
12916
- async function exportStoredReceipts(wallet) {
12917
- const receipts = [...await readArchivedReceipts(), ...(await readState()).receipts];
12918
- if (!wallet) return receipts;
12919
- return receipts.filter((receipt) => receipt.wallet.toLowerCase() === wallet.toLowerCase());
13772
+
13773
+ // src/receipt-verifier.ts
13774
+ function isBytes32(value) {
13775
+ return typeof value === "string" && /^0x[0-9a-f]{64}$/i.test(value);
12920
13776
  }
12921
- async function unsettledReceipts(limit) {
12922
- return (await readState()).receipts.filter((receipt) => !receipt.claimTransaction).slice(0, limit);
13777
+ function assertReceiptShape(receipt) {
13778
+ const errors = [];
13779
+ const fields = /* @__PURE__ */ new Set(["receiptId", "nonce", "wallet", "provider", "model", "inputTokens", "outputTokens", "durationSeconds", "taskHash", "outputHash", "client", "trustTier", "walletAuthorization", "authorizationIssuedAt", "authorizationChainId", "authorizationContract", "providerEvidence", "collectorPublicKey", "collectorSignature", "recordedAt", "estimatedReward", "batchRoot", "claimTransaction", "tokenUsageMeasurement"]);
13780
+ if (Object.keys(receipt).some((key) => !fields.has(key))) return ["receipt contains unsupported fields"];
13781
+ for (const [key, value] of Object.entries(receipt)) {
13782
+ if (key === "providerEvidence") {
13783
+ if (value !== void 0 && (!value || typeof value !== "object" || Array.isArray(value) || Object.keys(value).some((k) => !["keyId", "signature"].includes(k)) || typeof value.keyId !== "string" || typeof value.signature !== "string" || value.keyId.length > 128 || value.signature.length > 2048)) return ["providerEvidence must contain bounded keyId and signature strings"];
13784
+ } else if (value !== void 0 && typeof value !== "string" && typeof value !== "number") return ["receipt fields must be scalar values"];
13785
+ if (typeof value === "string" && value.length > 8192) return ["receipt field exceeds length limit"];
13786
+ }
13787
+ for (const field of ["receiptId", "nonce", "taskHash", "outputHash"]) {
13788
+ if (!isBytes32(receipt[field])) errors.push(`${field} must be a bytes32 hex value`);
13789
+ }
13790
+ if (typeof receipt.walletAuthorization !== "string" || !/^0x[0-9a-f]+$/i.test(receipt.walletAuthorization)) {
13791
+ errors.push("walletAuthorization must be a hex EIP-712 signature");
13792
+ }
13793
+ if (typeof receipt.wallet !== "string" || !/^0x[0-9a-f]{40}$/i.test(receipt.wallet)) errors.push("wallet must be an address");
13794
+ if (typeof receipt.authorizationContract !== "string" || !/^0x[0-9a-f]{40}$/i.test(receipt.authorizationContract)) {
13795
+ errors.push("authorizationContract must be an address");
13796
+ }
13797
+ for (const field of ["provider", "model", "client", "collectorPublicKey", "collectorSignature", "recordedAt", "estimatedReward"]) {
13798
+ if (typeof receipt[field] !== "string" || receipt[field].length === 0) errors.push(`${field} must be a non-empty string`);
13799
+ }
13800
+ for (const field of ["inputTokens", "outputTokens", "durationSeconds", "authorizationIssuedAt", "authorizationChainId"]) {
13801
+ if (!Number.isSafeInteger(receipt[field]) || Number(receipt[field]) < 0) errors.push(`${field} must be a non-negative safe integer`);
13802
+ }
13803
+ if (receipt.trustTier !== "client_signed" && receipt.trustTier !== "provider_signed") errors.push("trustTier is unsupported");
13804
+ if (receipt.tokenUsageMeasurement !== void 0 && !["measured", "unmeasured", "unspecified"].includes(receipt.tokenUsageMeasurement)) errors.push("tokenUsageMeasurement is unsupported");
13805
+ if (receipt.tokenUsageMeasurement === "unmeasured" && (receipt.inputTokens !== 0 || receipt.outputTokens !== 0)) errors.push("unmeasured token usage must have zero counts");
13806
+ if (Number.isNaN(Date.parse(String(receipt.recordedAt)))) errors.push("recordedAt must be an ISO date");
13807
+ return errors;
12923
13808
  }
12924
- async function protocolStateCounts() {
12925
- const state = await readState();
12926
- const sessions = Object.values(state.sessions);
13809
+ function verifyAipouReceipt(candidate) {
13810
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
13811
+ return {
13812
+ valid: false,
13813
+ errors: ["receipt must be an object"],
13814
+ workReceiptId: null,
13815
+ claimedWorkReceiptId: null,
13816
+ evidence: receiptEvidenceReport({ receiptIntegrity: false, walletAuthorization: false, collectorSignature: false })
13817
+ };
13818
+ }
13819
+ const receipt = candidate;
13820
+ const errors = assertReceiptShape(receipt);
13821
+ if (errors.length > 0) {
13822
+ return {
13823
+ valid: false,
13824
+ errors,
13825
+ workReceiptId: null,
13826
+ claimedWorkReceiptId: isBytes32(receipt.receiptId) ? receipt.receiptId.toLowerCase() : null,
13827
+ evidence: receiptEvidenceReport({ receiptIntegrity: false, walletAuthorization: false, collectorSignature: false })
13828
+ };
13829
+ }
13830
+ const { receiptId, collectorSignature: signature, batchRoot: _batchRoot, claimTransaction: _claimTransaction, ...unsigned } = receipt;
13831
+ const receiptIntegrity = sha256Hex(canonicalJson(unsigned)).toLowerCase() === receiptId.toLowerCase();
13832
+ if (!receiptIntegrity) errors.push("receiptId does not match canonical unsigned receipt payload");
13833
+ let collectorSignatureValid = false;
13834
+ try {
13835
+ collectorSignatureValid = verifyCollectorPayload({ receiptId, ...unsigned }, signature, receipt.collectorPublicKey);
13836
+ } catch {
13837
+ collectorSignatureValid = false;
13838
+ }
13839
+ if (!collectorSignatureValid) errors.push("collector signature is invalid");
13840
+ const session = {
13841
+ nonce: receipt.nonce,
13842
+ wallet: receipt.wallet,
13843
+ provider: receipt.provider,
13844
+ model: receipt.model,
13845
+ taskHash: receipt.taskHash,
13846
+ client: receipt.client,
13847
+ issuedAt: receipt.authorizationIssuedAt,
13848
+ chainId: receipt.authorizationChainId,
13849
+ verifyingContract: receipt.authorizationContract,
13850
+ walletAuthorization: receipt.walletAuthorization,
13851
+ completedAt: receipt.recordedAt
13852
+ };
13853
+ let walletAuthorization = false;
13854
+ try {
13855
+ walletAuthorization = verifyTaskAuthorization(session);
13856
+ } catch {
13857
+ walletAuthorization = false;
13858
+ }
13859
+ if (!walletAuthorization) errors.push("wallet authorization is invalid");
12927
13860
  return {
12928
- sessions: sessions.length,
12929
- completedSessions: sessions.filter((session) => session.completedAt).length,
12930
- incompleteSessions: sessions.filter((session) => !session.completedAt).length,
12931
- batches: state.batches.length
13861
+ valid: errors.length === 0,
13862
+ errors,
13863
+ workReceiptId: errors.length === 0 ? receiptId.toLowerCase() : null,
13864
+ claimedWorkReceiptId: errors.length === 0 ? null : receiptId.toLowerCase(),
13865
+ evidence: receiptEvidenceReport({
13866
+ receiptIntegrity,
13867
+ walletAuthorization,
13868
+ collectorSignature: collectorSignatureValid,
13869
+ hostExecution: receiptIntegrity && collectorSignatureValid ? "OBSERVED" : "UNKNOWN"
13870
+ })
12932
13871
  };
12933
13872
  }
12934
- async function settledReceiptsSince(sinceMs) {
12935
- const receipts = [...await readArchivedReceipts(), ...(await readState()).receipts];
12936
- return receipts.filter(
12937
- (receipt) => receipt.claimTransaction && Date.parse(receipt.recordedAt) >= sinceMs
12938
- );
12939
- }
12940
- async function markBatchSettled(batch) {
12941
- await mutate(async (state) => {
12942
- const included = new Set(batch.receiptIds);
12943
- const settled = [];
12944
- for (const receipt of state.receipts) {
12945
- if (included.has(receipt.receiptId)) {
12946
- if (receipt.claimTransaction) throw new Error(`Receipt ${receipt.receiptId} is already settled`);
12947
- receipt.batchRoot = batch.root;
12948
- receipt.claimTransaction = batch.claimTransaction;
12949
- settled.push(receipt);
12950
- }
12951
- }
12952
- await mkdir2(archiveDir(), { recursive: true });
12953
- const archivePath = path4.join(archiveDir(), `batch-${batch.root.slice(2, 18)}.json`);
12954
- await writeFile3(archivePath, `${JSON.stringify(settled, null, 2)}
12955
- `, { encoding: "utf8", flag: "wx" });
12956
- state.receipts = state.receipts.filter((receipt) => !included.has(receipt.receiptId));
12957
- state.settledReceiptIds = [...state.settledReceiptIds ?? [], ...batch.receiptIds];
12958
- state.batches.push(batch);
12959
- });
12960
- }
12961
13873
 
12962
13874
  // src/claims.ts
12963
13875
  var claimsAbi = [
12964
13876
  "function token() view returns (address)",
12965
13877
  "function validator() view returns (address)",
12966
13878
  "function approvedRoots(bytes32 root) view returns (bool)",
13879
+ "function claimedReceipts(bytes32 receiptId) view returns (bool)",
12967
13880
  "function publishRoot(bytes32 root)",
12968
13881
  "function claimBatch(bytes32 root, address[] accounts, uint256[] amounts, bytes32[] receiptIds, bytes32[][] proofs)"
12969
13882
  ];
12970
13883
  var tokenAbi = ["function emissionController() view returns (address)"];
12971
13884
  function claimsAddress() {
12972
13885
  const value = process.env.AIPOU_CLAIMS_ADDRESS;
12973
- if (!value) throw new Error("AIPOU_CLAIMS_ADDRESS is required to settle rewards");
13886
+ if (!value)
13887
+ throw new Error("AIPOU_CLAIMS_ADDRESS is required to settle rewards");
12974
13888
  return getAddress2(value);
12975
13889
  }
12976
13890
  async function validatorPrivateKey() {
12977
13891
  const keyFile = process.env.AIPOU_VALIDATOR_KEY_FILE;
12978
13892
  if (keyFile) {
12979
- const key = (await readFile4(path5.resolve(keyFile), "utf8")).trim();
13893
+ const key = (await readFile7(path6.resolve(keyFile), "utf8")).trim();
12980
13894
  if (!key) throw new Error(`AIPOU_VALIDATOR_KEY_FILE ${keyFile} is empty`);
12981
13895
  return key;
12982
13896
  }
@@ -12987,26 +13901,46 @@ async function validatorPrivateKey() {
12987
13901
  );
12988
13902
  return envKey;
12989
13903
  }
12990
- throw new Error("AIPOU_VALIDATOR_KEY_FILE (or legacy AIPOU_VALIDATOR_PRIVATE_KEY) is required only on the protocol validator server");
13904
+ throw new Error(
13905
+ "AIPOU_VALIDATOR_KEY_FILE (or legacy AIPOU_VALIDATOR_PRIVATE_KEY) is required only on the protocol validator server"
13906
+ );
12991
13907
  }
12992
13908
  async function validatorWallet() {
12993
- const provider = new JsonRpcProvider(process.env.AIPOU_RPC_URL || "https://mainnet.base.org");
12994
- return new Wallet2(await validatorPrivateKey(), provider);
13909
+ const provider = new JsonRpcProvider2(
13910
+ process.env.AIPOU_RPC_URL || "https://mainnet.base.org"
13911
+ );
13912
+ return new Wallet3(await validatorPrivateKey(), provider);
12995
13913
  }
12996
13914
  async function trustedCollectorFingerprints() {
12997
13915
  const file = process.env.AIPOU_TRUSTED_COLLECTORS_FILE;
12998
- if (!file) throw new Error("AIPOU_TRUSTED_COLLECTORS_FILE is required on the protocol validator server");
12999
- const content = JSON.parse(await readFile4(path5.resolve(file), "utf8"));
13000
- if (!Array.isArray(content.fingerprints)) throw new Error("Trusted collectors file must contain a fingerprints array");
13916
+ if (!file)
13917
+ throw new Error(
13918
+ "AIPOU_TRUSTED_COLLECTORS_FILE is required on the protocol validator server"
13919
+ );
13920
+ const content = JSON.parse(await readFile7(path6.resolve(file), "utf8"));
13921
+ if (!Array.isArray(content.fingerprints))
13922
+ throw new Error(
13923
+ "Trusted collectors file must contain a fingerprints array"
13924
+ );
13001
13925
  return new Set(content.fingerprints);
13002
13926
  }
13003
13927
  async function verifyReceipt(receipt, trustedCollectors) {
13928
+ if (!verifyAipouReceipt(receipt).valid)
13929
+ throw new Error(
13930
+ "Invalid receipt integrity or signature; settlement stopped before submission"
13931
+ );
13004
13932
  const fingerprint = collectorFingerprint(receipt.collectorPublicKey);
13005
13933
  if (!trustedCollectors.has(fingerprint)) {
13006
- throw new Error(`Untrusted collector ${fingerprint} for ${receipt.receiptId}`);
13934
+ throw new Error(
13935
+ `Untrusted collector ${fingerprint} for ${receipt.receiptId}`
13936
+ );
13007
13937
  }
13008
13938
  const { collectorSignature, batchRoot, claimTransaction, ...signedPayload } = receipt;
13009
- if (!verifyCollectorPayload(signedPayload, collectorSignature, receipt.collectorPublicKey)) {
13939
+ if (!verifyCollectorPayload(
13940
+ signedPayload,
13941
+ collectorSignature,
13942
+ receipt.collectorPublicKey
13943
+ )) {
13010
13944
  throw new Error(`Invalid collector signature for ${receipt.receiptId}`);
13011
13945
  }
13012
13946
  const session = {
@@ -13022,7 +13956,8 @@ async function verifyReceipt(receipt, trustedCollectors) {
13022
13956
  walletAuthorization: receipt.walletAuthorization,
13023
13957
  completedAt: receipt.recordedAt
13024
13958
  };
13025
- if (!verifyTaskAuthorization(session)) throw new Error(`Invalid wallet authorization for ${receipt.receiptId}`);
13959
+ if (!verifyTaskAuthorization(session))
13960
+ throw new Error(`Invalid wallet authorization for ${receipt.receiptId}`);
13026
13961
  const derivedTier = await deriveTrustTier(
13027
13962
  session,
13028
13963
  {
@@ -13031,31 +13966,81 @@ async function verifyReceipt(receipt, trustedCollectors) {
13031
13966
  outputTokens: receipt.outputTokens,
13032
13967
  durationSeconds: receipt.durationSeconds,
13033
13968
  outputHash: receipt.outputHash,
13969
+ tokenUsageMeasurement: receipt.tokenUsageMeasurement,
13034
13970
  providerEvidence: receipt.providerEvidence
13035
13971
  },
13036
13972
  receipt.providerEvidence
13037
13973
  );
13038
- if (derivedTier !== receipt.trustTier) throw new Error(`Invalid trust tier for ${receipt.receiptId}`);
13974
+ if (derivedTier !== receipt.trustTier)
13975
+ throw new Error(`Invalid trust tier for ${receipt.receiptId}`);
13039
13976
  }
13040
13977
  async function settleRewards(maxReceipts = 25) {
13978
+ if (!Number.isInteger(maxReceipts) || maxReceipts < 1 || maxReceipts > 100)
13979
+ throw new Error("Batch size must be 1 through 100");
13980
+ const directory = path6.resolve(process.env.AIPOU_DATA_DIR || ".aipou");
13981
+ await mkdir4(directory, { recursive: true, mode: 448 });
13982
+ try {
13983
+ return await withFileLock(
13984
+ path6.join(directory, "settlement.lock"),
13985
+ () => settleRewardsLocked(maxReceipts, directory)
13986
+ );
13987
+ } catch (error) {
13988
+ const message = error instanceof Error ? error.message : "";
13989
+ if (message.startsWith("SETTLEMENT_") || message === "There are no unsettled receipts" || message.startsWith("No receipts pass the settlement policy"))
13990
+ throw error;
13991
+ throw new Error(
13992
+ "SETTLEMENT_FAILED: inspect local configuration and saved settlement state before retrying; private RPC and transaction details are withheld."
13993
+ );
13994
+ }
13995
+ }
13996
+ async function settleRewardsLocked(maxReceipts, directory) {
13997
+ const journalPath = path6.join(directory, "settlement-pending.json");
13998
+ const pending = await readOptionalJson(journalPath);
13041
13999
  const candidates = await unsettledReceipts(Number.MAX_SAFE_INTEGER);
13042
- if (candidates.length === 0) throw new Error("There are no unsettled receipts");
14000
+ if (candidates.length === 0 && !pending)
14001
+ throw new Error("There are no unsettled receipts");
13043
14002
  const policy = settlementPolicyFromEnv();
13044
14003
  const recentlySettled = await settledReceiptsSince(Date.now() - DAY_MS);
13045
- const { eligible, skipped } = filterEligibleReceipts(candidates, recentlySettled, policy);
13046
- if (eligible.length === 0) {
14004
+ const { eligible, skipped } = filterEligibleReceipts(
14005
+ candidates,
14006
+ recentlySettled,
14007
+ policy
14008
+ );
14009
+ if (eligible.length === 0 && !pending) {
13047
14010
  const reasons = skipped.map((item) => `${item.receiptId.slice(0, 10)}\u2026: ${item.reason}`).join("; ");
13048
- throw new Error(`No receipts pass the settlement policy. Skipped: ${reasons}`);
14011
+ throw new Error(
14012
+ `No receipts pass the settlement policy. Skipped: ${reasons}`
14013
+ );
14014
+ }
14015
+ let receipts = eligible.slice(0, maxReceipts);
14016
+ if (pending) {
14017
+ if (pending.version !== 1 || pending.chainId !== 8453 || !Array.isArray(pending.receiptIds) || pending.receiptIds.length < 1 || pending.receiptIds.length > 100) {
14018
+ throw new Error("SETTLEMENT_JOURNAL_INVALID: invalid saved batch");
14019
+ }
14020
+ const stored = await exportStoredReceipts();
14021
+ receipts = pending.receiptIds.map((id) => {
14022
+ const receipt = stored.find((item) => item.receiptId === id);
14023
+ if (!receipt)
14024
+ throw new Error("SETTLEMENT_JOURNAL_INVALID: saved receipt is missing");
14025
+ return receipt;
14026
+ });
13049
14027
  }
13050
- const receipts = eligible.slice(0, maxReceipts);
13051
14028
  const trustedCollectors = await trustedCollectorFingerprints();
13052
- for (const receipt of receipts) await verifyReceipt(receipt, trustedCollectors);
14029
+ for (const receipt of receipts)
14030
+ await verifyReceipt(receipt, trustedCollectors);
13053
14031
  const accounts = receipts.map((receipt) => receipt.wallet);
13054
- const amounts = receipts.map((receipt) => parseUnits(receipt.estimatedReward, 18));
14032
+ const amounts = receipts.map(
14033
+ (receipt) => parseUnits(receipt.estimatedReward, 18)
14034
+ );
13055
14035
  const receiptIds = receipts.map((receipt) => receipt.receiptId);
13056
14036
  const coder = AbiCoder.defaultAbiCoder();
13057
14037
  const leaves = receipts.map((receipt, index) => {
13058
- const inner = keccak256(coder.encode(["address", "uint256", "bytes32"], [receipt.wallet, amounts[index], receipt.receiptId]));
14038
+ const inner = keccak256(
14039
+ coder.encode(
14040
+ ["address", "uint256", "bytes32"],
14041
+ [receipt.wallet, amounts[index], receipt.receiptId]
14042
+ )
14043
+ );
13059
14044
  return Buffer.from(getBytes(keccak256(concat([inner]))));
13060
14045
  });
13061
14046
  const tree = new MerkleTree(
@@ -13067,35 +14052,77 @@ async function settleRewards(maxReceipts = 25) {
13067
14052
  const proofs = leaves.map((leaf) => tree.getHexProof(leaf));
13068
14053
  const signer = await validatorWallet();
13069
14054
  const address = claimsAddress();
13070
- const claims = new Contract(address, claimsAbi, signer);
14055
+ if ((await signer.provider.getNetwork()).chainId !== 8453n)
14056
+ throw new Error("Settlement requires Base mainnet (8453)");
14057
+ const claims = new Contract2(address, claimsAbi, signer);
13071
14058
  const configuredValidator = getAddress2(await claims.validator());
13072
14059
  if (configuredValidator !== signer.address) {
13073
- throw new Error(`Validator mismatch: contract expects ${configuredValidator}, server uses ${signer.address}`);
14060
+ throw new Error(
14061
+ `Validator mismatch: contract expects ${configuredValidator}, server uses ${signer.address}`
14062
+ );
13074
14063
  }
13075
14064
  const tokenAddress = await claims.token();
13076
- const token = new Contract(tokenAddress, tokenAbi, signer.provider);
14065
+ const token = new Contract2(tokenAddress, tokenAbi, signer.provider);
13077
14066
  if (getAddress2(await token.emissionController()) !== address) {
13078
14067
  throw new Error("AIPOUClaims is not the token emission controller");
13079
14068
  }
13080
- const publish = await claims.publishRoot(root);
13081
- const publishReceipt = await publish.wait();
13082
- let rootApproved = false;
13083
- for (let attempt = 0; attempt < 10 && !rootApproved; attempt += 1) {
13084
- rootApproved = await claims.approvedRoots(root);
13085
- if (!rootApproved) await new Promise((resolve) => setTimeout(resolve, 3e3));
13086
- }
13087
- if (!rootApproved) throw new Error("Published Merkle root was not visible through the RPC after 30 seconds");
13088
- const claim = await claims.claimBatch(root, accounts, amounts, receiptIds, proofs);
13089
- const claimReceipt = await claim.wait();
13090
- if (!publishReceipt || !claimReceipt) throw new Error("Settlement transaction receipt is unavailable");
14069
+ const expected = {
14070
+ version: 1,
14071
+ chainId: 8453,
14072
+ contract: address,
14073
+ root,
14074
+ accounts,
14075
+ amounts: amounts.map(String),
14076
+ receiptIds,
14077
+ proofs
14078
+ };
14079
+ if (pending) {
14080
+ const { publish: _publish, claim: _claim, ...saved } = pending;
14081
+ if (canonicalJson(saved) !== canonicalJson(expected))
14082
+ throw new Error(
14083
+ "SETTLEMENT_JOURNAL_INVALID: batch does not match verified receipts and configured contract"
14084
+ );
14085
+ }
14086
+ const plan = pending ?? expected;
14087
+ for (const kind of ["publish", "claim"]) {
14088
+ const saved = plan[kind];
14089
+ if (!saved) continue;
14090
+ const transaction = Transaction2.from(saved.raw);
14091
+ const expectedData = kind === "publish" ? claims.interface.encodeFunctionData("publishRoot", [root]) : claims.interface.encodeFunctionData("claimBatch", [
14092
+ root,
14093
+ accounts,
14094
+ amounts,
14095
+ receiptIds,
14096
+ proofs
14097
+ ]);
14098
+ if (transaction.hash !== saved.hash || transaction.from !== signer.address || transaction.chainId !== 8453n || transaction.to?.toLowerCase() !== address.toLowerCase() || transaction.value !== 0n || transaction.data !== expectedData) {
14099
+ throw new Error(
14100
+ "SETTLEMENT_JOURNAL_INVALID: saved transaction does not match the verified batch"
14101
+ );
14102
+ }
14103
+ }
14104
+ const persist = (value) => writePrivateJson(journalPath, value);
14105
+ await persist(plan);
14106
+ const transactions = await executeSettlementPlan(
14107
+ plan,
14108
+ createSettlementDriver(signer, address),
14109
+ persist
14110
+ );
14111
+ const confirmations = await Promise.all(
14112
+ receiptIds.map((id) => claims.claimedReceipts(id))
14113
+ );
14114
+ if (!confirmations.every(Boolean))
14115
+ throw new Error(
14116
+ "SETTLEMENT_CLAIMS_UNCONFIRMED: receipt flags are not visible; journal retained for reconciliation"
14117
+ );
13091
14118
  const batch = {
13092
14119
  root,
13093
14120
  receiptIds,
13094
- publishTransaction: publishReceipt.hash,
13095
- claimTransaction: claimReceipt.hash,
14121
+ ...transactions,
13096
14122
  settledAt: (/* @__PURE__ */ new Date()).toISOString()
13097
14123
  };
13098
14124
  await markBatchSettled(batch);
14125
+ await rm2(journalPath, { force: true });
13099
14126
  return { ...batch, skippedReceipts: skipped };
13100
14127
  }
13101
14128
  async function settleAllRewards(batchSize = 100, maxBatches = 20) {
@@ -13110,7 +14137,8 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
13110
14137
  skippedByReceiptId.set(skipped.receiptId, skipped);
13111
14138
  }
13112
14139
  } catch (error) {
13113
- const message = error instanceof Error ? error.message : String(error);
14140
+ const raw = error instanceof Error ? error.message : "Settlement failed";
14141
+ const message = raw.startsWith("SETTLEMENT_") || raw === "There are no unsettled receipts" ? raw.slice(0, 400) : "SETTLEMENT_FAILED: inspect local diagnostics and saved settlement state before retrying; no private transaction payload is returned.";
13114
14142
  if (message === "There are no unsettled receipts") break;
13115
14143
  stoppedReason = message;
13116
14144
  break;
@@ -13124,10 +14152,13 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
13124
14152
  return {
13125
14153
  batches,
13126
14154
  batchCount: batches.length,
13127
- settledReceiptCount: batches.reduce((total, batch) => total + batch.receiptIds.length, 0),
14155
+ settledReceiptCount: batches.reduce(
14156
+ (total, batch) => total + batch.receiptIds.length,
14157
+ 0
14158
+ ),
13128
14159
  skippedReceipts,
13129
14160
  pendingReceiptCount,
13130
- publishTransactions: batches.map((batch) => batch.publishTransaction),
14161
+ publishTransactions: batches.map((batch) => batch.publishTransaction).filter(Boolean),
13131
14162
  claimTransactions: batches.map((batch) => batch.claimTransaction),
13132
14163
  stoppedReason,
13133
14164
  settledAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13135,8 +14166,8 @@ async function settleAllRewards(batchSize = 100, maxBatches = 20) {
13135
14166
  }
13136
14167
 
13137
14168
  // src/contract.ts
13138
- import { readFile as readFile5 } from "node:fs/promises";
13139
- import path6 from "node:path";
14169
+ import { readFile as readFile8 } from "node:fs/promises";
14170
+ import path7 from "node:path";
13140
14171
  var DEFAULT_LOGO_URI = "https://raw.githubusercontent.com/0xddneto/AI-Proof-of-Us/main/assets/token/aipou.png";
13141
14172
  var aipouTokenAbi = [
13142
14173
  "function name() view returns (string)",
@@ -13155,9 +14186,9 @@ var aipouClaimsAbi = [
13155
14186
  ];
13156
14187
  async function configuredClaimsAddress() {
13157
14188
  if (process.env.AIPOU_CLAIMS_ADDRESS) return process.env.AIPOU_CLAIMS_ADDRESS;
13158
- const claimsPath = path6.resolve(process.env.AIPOU_CLAIMS_DEPLOYMENT_FILE || "deployments/base-claims.json");
14189
+ const claimsPath = path7.resolve(process.env.AIPOU_CLAIMS_DEPLOYMENT_FILE || "deployments/base-claims.json");
13159
14190
  try {
13160
- const deployment = JSON.parse(await readFile5(claimsPath, "utf8"));
14191
+ const deployment = JSON.parse(await readFile8(claimsPath, "utf8"));
13161
14192
  return deployment.address || null;
13162
14193
  } catch (error) {
13163
14194
  if (error.code === "ENOENT") return null;
@@ -13185,9 +14216,9 @@ function envConfig() {
13185
14216
  };
13186
14217
  }
13187
14218
  async function deploymentFileConfig() {
13188
- const deploymentPath = path6.resolve(process.env.AIPOU_DEPLOYMENT_FILE || "deployments/base.json");
14219
+ const deploymentPath = path7.resolve(process.env.AIPOU_DEPLOYMENT_FILE || "deployments/base.json");
13189
14220
  try {
13190
- const deployment = JSON.parse(await readFile5(deploymentPath, "utf8"));
14221
+ const deployment = JSON.parse(await readFile8(deploymentPath, "utf8"));
13191
14222
  const address = deployment.token?.address;
13192
14223
  if (!address) {
13193
14224
  return null;
@@ -13244,10 +14275,10 @@ async function getTokenContractConfig() {
13244
14275
 
13245
14276
  // src/demo.ts
13246
14277
  import { createHash as createHash3 } from "node:crypto";
13247
- import { mkdtemp, rm as rm2 } from "node:fs/promises";
14278
+ import { mkdtemp, rm as rm3 } from "node:fs/promises";
13248
14279
  import os from "node:os";
13249
- import path7 from "node:path";
13250
- import { Wallet as Wallet3 } from "ethers";
14280
+ import path8 from "node:path";
14281
+ import { Wallet as Wallet4 } from "ethers";
13251
14282
 
13252
14283
  // src/rewards.ts
13253
14284
  var tierMultiplier = {
@@ -13272,12 +14303,20 @@ async function beginTask(input) {
13272
14303
  async function completeTask(input) {
13273
14304
  const session = await getSession(input.nonce);
13274
14305
  if (!session) throw new Error("Unknown task nonce");
13275
- if (session.completedAt) throw new Error("Task nonce has already been completed");
14306
+ if (session.completedAt) throw new Error("Task nonce has already been completed; use export_ai_receipts and find this nonce to recover its stored receipt. Do not rerun the work or begin a new task to recover a lost response.");
14307
+ if (isTaskSessionExpired(session)) throw new Error("Task session has expired; begin a new task instead of completing stale authority");
13276
14308
  if (!verifyTaskAuthorization(session)) throw new Error("Invalid EIP-712 wallet authorization");
14309
+ if (input.tokenUsageMeasurement !== void 0 && !["measured", "unmeasured", "unspecified"].includes(input.tokenUsageMeasurement)) {
14310
+ throw new Error("Unsupported tokenUsageMeasurement");
14311
+ }
14312
+ if (input.tokenUsageMeasurement === "unmeasured" && (input.inputTokens !== 0 || input.outputTokens !== 0)) {
14313
+ throw new Error("Unmeasured token usage must use zero counts");
14314
+ }
13277
14315
  const trustTier = await deriveTrustTier(session, input, input.providerEvidence);
13278
14316
  const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
13279
14317
  const collectorPublicKey = await getCollectorPublicKey();
13280
14318
  const unsigned = {
14319
+ tokenUsageMeasurement: input.tokenUsageMeasurement ?? "unspecified",
13281
14320
  nonce: session.nonce,
13282
14321
  wallet: session.wallet,
13283
14322
  provider: session.provider,
@@ -13330,8 +14369,8 @@ async function runLocalReceiptDemo() {
13330
14369
  const previousEnvironment = new Map(
13331
14370
  DEMO_ENVIRONMENT_KEYS.map((key) => [key, process.env[key]])
13332
14371
  );
13333
- const dataDir3 = await mkdtemp(path7.join(os.tmpdir(), "aipou-one-command-demo-"));
13334
- const wallet = Wallet3.createRandom();
14372
+ const dataDir3 = await mkdtemp(path8.join(os.tmpdir(), "aipou-one-command-demo-"));
14373
+ const wallet = Wallet4.createRandom();
13335
14374
  process.env.AIPOU_AGENT_PRIVATE_KEY = wallet.privateKey;
13336
14375
  process.env.AIPOU_CONTRACT_ADDRESS = TOKEN_ADDRESS;
13337
14376
  process.env.AIPOU_CLAIMS_ADDRESS = CLAIMS_ADDRESS;
@@ -13395,7 +14434,7 @@ async function runLocalReceiptDemo() {
13395
14434
  }
13396
14435
  };
13397
14436
  } finally {
13398
- await rm2(dataDir3, { recursive: true, force: true });
14437
+ await rm3(dataDir3, { recursive: true, force: true });
13399
14438
  for (const key of DEMO_ENVIRONMENT_KEYS) {
13400
14439
  const previous = previousEnvironment.get(key);
13401
14440
  if (previous === void 0) delete process.env[key];
@@ -13404,25 +14443,92 @@ async function runLocalReceiptDemo() {
13404
14443
  }
13405
14444
  }
13406
14445
 
14446
+ // src/observed-outcome-pilot.ts
14447
+ import { mkdtemp as mkdtemp2, rm as rm4 } from "node:fs/promises";
14448
+ import os2 from "node:os";
14449
+ import path9 from "node:path";
14450
+ import { Wallet as Wallet5 } from "ethers";
14451
+ var CLAIMS_ADDRESS2 = "0x4ca4C98fB784D20EdC8E2A7F531dAab4c6e53058";
14452
+ var ENVIRONMENT_KEYS = ["AIPOU_AGENT_PRIVATE_KEY", "AIPOU_DATA_DIR"];
14453
+ async function runObservedOutcomePilot() {
14454
+ const previous = new Map(ENVIRONMENT_KEYS.map((key) => [key, process.env[key]]));
14455
+ const dataDir3 = await mkdtemp2(path9.join(os2.tmpdir(), "aipou-observed-outcome-pilot-"));
14456
+ process.env.AIPOU_DATA_DIR = dataDir3;
14457
+ process.env.AIPOU_AGENT_PRIVATE_KEY = Wallet5.createRandom().privateKey;
14458
+ try {
14459
+ const session = await beginTask({
14460
+ provider: "local-pilot",
14461
+ model: "observed-outcome-pilot",
14462
+ client: "aipou-cli",
14463
+ taskHash: sha256Hex(canonicalJson({ kind: "pilot", action: "bounded_local_execution" })),
14464
+ chainId: 8453,
14465
+ verifyingContract: CLAIMS_ADDRESS2
14466
+ });
14467
+ const receipt = await completeTask({
14468
+ nonce: session.nonce,
14469
+ inputTokens: 24,
14470
+ outputTokens: 16,
14471
+ durationSeconds: 1,
14472
+ outputHash: sha256Hex(canonicalJson({ artifact: "pilot-output-reference", status: "host-observed" }))
14473
+ });
14474
+ const verification = verifyAipouReceipt(receipt);
14475
+ if (!verification.valid) throw new Error(`Pilot receipt verification failed: ${verification.errors.join("; ")}`);
14476
+ return {
14477
+ mode: "observed_outcome_pilot",
14478
+ storage: "removed_after_pilot",
14479
+ workReceiptId: receipt.receiptId,
14480
+ evidence: verification.evidence,
14481
+ safety: {
14482
+ networkRequired: false,
14483
+ fundsMoved: false,
14484
+ claimSubmitted: false,
14485
+ deliveryClaimed: false,
14486
+ acceptanceClaimed: false
14487
+ }
14488
+ };
14489
+ } finally {
14490
+ await rm4(dataDir3, { recursive: true, force: true });
14491
+ for (const key of ENVIRONMENT_KEYS) {
14492
+ const value = previous.get(key);
14493
+ if (value === void 0) delete process.env[key];
14494
+ else process.env[key] = value;
14495
+ }
14496
+ }
14497
+ }
14498
+
13407
14499
  // src/doctor.ts
13408
14500
  import { constants } from "node:fs";
13409
- import { access, readFile as readFile8, stat as stat2 } from "node:fs/promises";
13410
- import os3 from "node:os";
13411
- import path9 from "node:path";
13412
- import { Wallet as Wallet5, isAddress as isAddress2 } from "ethers";
14501
+ import { access, readFile as readFile10, stat as stat2 } from "node:fs/promises";
14502
+ import os4 from "node:os";
14503
+ import path11 from "node:path";
14504
+ import { Wallet as Wallet7, isAddress as isAddress2 } from "ethers";
13413
14505
 
13414
14506
  // src/init.ts
13415
- import { mkdir as mkdir3, readFile as readFile6, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
13416
- import os2 from "node:os";
13417
- import path8 from "node:path";
13418
- import { Wallet as Wallet4 } from "ethers";
14507
+ import { mkdir as mkdir5, readFile as readFile9, rm as rm5, writeFile as writeFile3 } from "node:fs/promises";
14508
+ import os3 from "node:os";
14509
+ import path10 from "node:path";
14510
+ import { fileURLToPath } from "node:url";
14511
+ import { Wallet as Wallet6 } from "ethers";
13419
14512
  var AIPOU_TOKEN_ADDRESS = "0x55f0Cc5e51A1284D20337d6cbb18938C8A1ABCbB";
13420
14513
  var AIPOU_CLAIMS_ADDRESS = "0x4ca4C98fB784D20EdC8E2A7F531dAab4c6e53058";
13421
14514
  function identityPaths(dataDirInput) {
13422
- const dataDir3 = path8.resolve(dataDirInput || path8.join(os2.homedir(), ".aipou"));
13423
- return { dataDir: dataDir3, keyFile: path8.join(dataDir3, "agent-wallet.key") };
14515
+ const dataDir3 = path10.resolve(dataDirInput || path10.join(os3.homedir(), ".aipou"));
14516
+ return { dataDir: dataDir3, keyFile: path10.join(dataDir3, "agent-wallet.key") };
14517
+ }
14518
+ async function installationCommand(moduleUrl = import.meta.url) {
14519
+ const directory = path10.dirname(fileURLToPath(moduleUrl));
14520
+ try {
14521
+ const workspace = JSON.parse(await readFile9(path10.resolve(directory, "../../package.json"), "utf8"));
14522
+ if (workspace.name === "ai-proof-of-us" && workspace.workspaces?.includes("mcp-server")) {
14523
+ return { command: process.execPath, args: [path10.join(directory, "index.js")] };
14524
+ }
14525
+ } catch {
14526
+ }
14527
+ const pkg = JSON.parse(await readFile9(path10.resolve(directory, "../package.json"), "utf8"));
14528
+ return { command: "npx", args: ["-y", `aipou-mcp-server@${pkg.version}`] };
13424
14529
  }
13425
- function buildResult(mode, walletAddress, dataDir3, keyFile) {
14530
+ async function buildResult(mode, walletAddress, dataDir3, keyFile) {
14531
+ const launch = await installationCommand();
13426
14532
  return {
13427
14533
  mode,
13428
14534
  wallet: walletAddress,
@@ -13432,8 +14538,7 @@ function buildResult(mode, walletAddress, dataDir3, keyFile) {
13432
14538
  mcpConfig: {
13433
14539
  mcpServers: {
13434
14540
  aipou: {
13435
- command: "npx",
13436
- args: ["-y", "aipou-mcp-server"],
14541
+ ...launch,
13437
14542
  env: {
13438
14543
  AIPOU_AGENT_KEY_FILE: keyFile,
13439
14544
  AIPOU_DATA_DIR: dataDir3,
@@ -13454,9 +14559,9 @@ function buildResult(mode, walletAddress, dataDir3, keyFile) {
13454
14559
  }
13455
14560
  async function initializePersistentIdentity(dataDirInput) {
13456
14561
  const { dataDir: dataDir3, keyFile } = identityPaths(dataDirInput);
13457
- const wallet = Wallet4.createRandom();
13458
- await mkdir3(dataDir3, { recursive: true, mode: 448 });
13459
- await writeFile4(keyFile, `${wallet.privateKey}
14562
+ const wallet = Wallet6.createRandom();
14563
+ await mkdir5(dataDir3, { recursive: true, mode: 448 });
14564
+ await writeFile3(keyFile, `${wallet.privateKey}
13460
14565
  `, {
13461
14566
  encoding: "utf8",
13462
14567
  mode: 384,
@@ -13464,15 +14569,15 @@ async function initializePersistentIdentity(dataDirInput) {
13464
14569
  });
13465
14570
  const platformAccessRestricted = await restrictToCurrentUser(keyFile);
13466
14571
  if (!platformAccessRestricted) {
13467
- await rm3(keyFile, { force: true });
14572
+ await rm5(keyFile, { force: true });
13468
14573
  throw new Error("Could not restrict dedicated-wallet key access to the current user");
13469
14574
  }
13470
14575
  return buildResult("persistent_identity_initialized", wallet.address, dataDir3, keyFile);
13471
14576
  }
13472
14577
  async function describeExistingIdentity(dataDirInput) {
13473
14578
  const { dataDir: dataDir3, keyFile } = identityPaths(dataDirInput);
13474
- const privateKey = (await readFile6(keyFile, "utf8")).trim();
13475
- const wallet = new Wallet4(privateKey);
14579
+ const privateKey = (await readFile9(keyFile, "utf8")).trim();
14580
+ const wallet = new Wallet6(privateKey);
13476
14581
  const platformAccessRestricted = await restrictToCurrentUser(keyFile);
13477
14582
  if (!platformAccessRestricted) {
13478
14583
  throw new Error("Existing dedicated-wallet key found, but its access could not be restricted to the current user");
@@ -13480,18 +14585,6 @@ async function describeExistingIdentity(dataDirInput) {
13480
14585
  return buildResult("persistent_identity_already_initialized", wallet.address, dataDir3, keyFile);
13481
14586
  }
13482
14587
 
13483
- // src/version.ts
13484
- import { readFile as readFile7 } from "node:fs/promises";
13485
- var SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
13486
- async function getPackageVersion() {
13487
- const packageUrl = new URL("../package.json", import.meta.url);
13488
- const metadata = JSON.parse(await readFile7(packageUrl, "utf8"));
13489
- if (typeof metadata.version !== "string" || !SEMVER.test(metadata.version)) {
13490
- throw new Error("mcp-server/package.json must contain a valid semantic version");
13491
- }
13492
- return metadata.version;
13493
- }
13494
-
13495
14588
  // src/doctor.ts
13496
14589
  function pass(detail) {
13497
14590
  return { status: "pass", detail };
@@ -13503,10 +14596,10 @@ function warning(detail) {
13503
14596
  return { status: "warning", detail };
13504
14597
  }
13505
14598
  async function runInstallationDoctor(dataDirInput) {
13506
- const dataDir3 = path9.resolve(dataDirInput || process.env.AIPOU_DATA_DIR || path9.join(os3.homedir(), ".aipou"));
14599
+ const dataDir3 = path11.resolve(dataDirInput || process.env.AIPOU_DATA_DIR || path11.join(os4.homedir(), ".aipou"));
13507
14600
  const configuredKeyFile = dataDirInput ? void 0 : process.env.AIPOU_AGENT_KEY_FILE?.trim();
13508
14601
  const inlinePrivateKey = dataDirInput ? void 0 : process.env.AIPOU_AGENT_PRIVATE_KEY?.trim();
13509
- const keyFile = inlinePrivateKey ? null : path9.resolve(configuredKeyFile || path9.join(dataDir3, "agent-wallet.key"));
14602
+ const keyFile = inlinePrivateKey ? null : path11.resolve(configuredKeyFile || path11.join(dataDir3, "agent-wallet.key"));
13510
14603
  const nodeMajor = Number(process.versions.node.split(".")[0]);
13511
14604
  const tokenAddress = process.env.AIPOU_CONTRACT_ADDRESS?.trim() || AIPOU_TOKEN_ADDRESS;
13512
14605
  const claimsAddress2 = process.env.AIPOU_CLAIMS_ADDRESS?.trim() || AIPOU_CLAIMS_ADDRESS;
@@ -13521,9 +14614,9 @@ async function runInstallationDoctor(dataDirInput) {
13521
14614
  let identity;
13522
14615
  let keySource;
13523
14616
  try {
13524
- const privateKey = inlinePrivateKey || (keyFile ? (await readFile8(keyFile, "utf8")).trim() : "");
14617
+ const privateKey = inlinePrivateKey || (keyFile ? (await readFile10(keyFile, "utf8")).trim() : "");
13525
14618
  if (!privateKey) throw new Error("missing");
13526
- wallet = new Wallet5(privateKey).address;
14619
+ wallet = new Wallet7(privateKey).address;
13527
14620
  keySource = inlinePrivateKey ? "inline_private_key" : "key_file";
13528
14621
  identity = inlinePrivateKey ? warning("Dedicated wallet key is valid, but a protected key file is safer than an inline environment variable.") : pass("Dedicated-wallet key file is readable and valid; its secret was not returned.");
13529
14622
  } catch {
@@ -13539,7 +14632,7 @@ async function runInstallationDoctor(dataDirInput) {
13539
14632
  try {
13540
14633
  const keyStat = await stat2(keyFile);
13541
14634
  if (process.platform === "win32") {
13542
- keyPermissions = warning("Key file exists; Windows ACLs were not changed or re-applied by this read-only diagnostic.");
14635
+ keyPermissions = { status: "not_checked", detail: "Windows ACLs are not inspected by this read-only doctor. This is not a detected permission failure; init reports whether it successfully applied access restrictions." };
13543
14636
  } else if ((keyStat.mode & 63) === 0) {
13544
14637
  keyPermissions = pass("Key file has no group or other permission bits.");
13545
14638
  } else {
@@ -13594,8 +14687,103 @@ function buildReceiptResultMeta(receipt) {
13594
14687
  };
13595
14688
  }
13596
14689
 
14690
+ // src/paybox-link.ts
14691
+ var PAYBOX_OPERATION_SCHEME = "paybox-operation-v1";
14692
+ var EXTERNAL_EVIDENCE_LINK_SCHEME = "external-evidence-link-v1";
14693
+ var receiptIdPattern = /^0x[0-9a-f]{64}$/;
14694
+ var sha256Pattern = /^sha256:[0-9a-f]{64}$/;
14695
+ function assertValidInput(input) {
14696
+ if (!receiptIdPattern.test(input.workReceiptId)) {
14697
+ throw new Error("workReceiptId must be a lowercase 0x-prefixed SHA-256 identifier");
14698
+ }
14699
+ if (!sha256Pattern.test(input.payboxOperationDigest)) {
14700
+ throw new Error("payboxOperationDigest must be a lowercase sha256 digest");
14701
+ }
14702
+ if (input.payboxOperationId.length < 1 || input.payboxOperationId.length > 256) {
14703
+ throw new Error("payboxOperationId must be an opaque reference between 1 and 256 characters");
14704
+ }
14705
+ if (!Number.isFinite(Date.parse(input.issuedAt))) {
14706
+ throw new Error("issuedAt must be an ISO-8601 timestamp");
14707
+ }
14708
+ }
14709
+ function createPayboxWorkLink(input) {
14710
+ assertValidInput(input);
14711
+ const identity = {
14712
+ scheme: EXTERNAL_EVIDENCE_LINK_SCHEME,
14713
+ relation: "supports",
14714
+ source: {
14715
+ kind: "receipt",
14716
+ scheme: "aipou-receipt-v1",
14717
+ id: input.workReceiptId,
14718
+ digest: `sha256:${input.workReceiptId.slice(2)}`
14719
+ },
14720
+ target: {
14721
+ kind: "payment-operation",
14722
+ scheme: PAYBOX_OPERATION_SCHEME,
14723
+ id: input.payboxOperationId,
14724
+ digest: input.payboxOperationDigest
14725
+ },
14726
+ privacy: "digest_only"
14727
+ };
14728
+ const payload = {
14729
+ ...identity,
14730
+ issuedAt: input.issuedAt
14731
+ };
14732
+ return {
14733
+ ...payload,
14734
+ linkId: `sha256:${sha256Hex(canonicalJson(identity)).slice(2)}`,
14735
+ linkDigest: `sha256:${sha256Hex(canonicalJson(payload)).slice(2)}`
14736
+ };
14737
+ }
14738
+
14739
+ // src/technocore-link.ts
14740
+ var TECHNOCORE_TRANSPORT_SCHEME = "technocore-room-receipt-v1";
14741
+ var EXTERNAL_EVIDENCE_LINK_SCHEME2 = "external-evidence-link-v1";
14742
+ var receiptIdPattern2 = /^0x[0-9a-f]{64}$/;
14743
+ var sha256Pattern2 = /^sha256:[0-9a-f]{64}$/;
14744
+ function assertValidInput2(input) {
14745
+ if (!receiptIdPattern2.test(input.workReceiptId)) {
14746
+ throw new Error("workReceiptId must be a lowercase 0x-prefixed SHA-256 identifier");
14747
+ }
14748
+ if (!sha256Pattern2.test(input.transportArtifactDigest)) {
14749
+ throw new Error("transportArtifactDigest must be a lowercase sha256 digest");
14750
+ }
14751
+ if (input.transportArtifactId.length < 1 || input.transportArtifactId.length > 256) {
14752
+ throw new Error("transportArtifactId must be an opaque reference between 1 and 256 characters");
14753
+ }
14754
+ if (!Number.isFinite(Date.parse(input.issuedAt))) {
14755
+ throw new Error("issuedAt must be an ISO-8601 timestamp");
14756
+ }
14757
+ }
14758
+ function createTechnocoreWorkLink(input) {
14759
+ assertValidInput2(input);
14760
+ const identity = {
14761
+ scheme: EXTERNAL_EVIDENCE_LINK_SCHEME2,
14762
+ relation: "supports",
14763
+ source: {
14764
+ kind: "receipt",
14765
+ scheme: "aipou-receipt-v1",
14766
+ id: input.workReceiptId,
14767
+ digest: `sha256:${input.workReceiptId.slice(2)}`
14768
+ },
14769
+ target: {
14770
+ kind: "transport-record",
14771
+ scheme: TECHNOCORE_TRANSPORT_SCHEME,
14772
+ id: input.transportArtifactId,
14773
+ digest: input.transportArtifactDigest
14774
+ },
14775
+ privacy: "digest_only"
14776
+ };
14777
+ const payload = { ...identity, issuedAt: input.issuedAt };
14778
+ return {
14779
+ ...payload,
14780
+ linkId: `sha256:${sha256Hex(canonicalJson(identity)).slice(2)}`,
14781
+ linkDigest: `sha256:${sha256Hex(canonicalJson(payload)).slice(2)}`
14782
+ };
14783
+ }
14784
+
13597
14785
  // src/status.ts
13598
- import { Contract as Contract2, JsonRpcProvider as JsonRpcProvider2, formatUnits } from "ethers";
14786
+ import { Contract as Contract3, JsonRpcProvider as JsonRpcProvider3, formatUnits } from "ethers";
13599
14787
  function sumRewards(receipts) {
13600
14788
  const total = receipts.reduce((sum, receipt) => sum + Number(receipt.estimatedReward), 0);
13601
14789
  return Number.isInteger(total) ? total.toFixed(0) : total.toFixed(6).replace(/0+$/, "").replace(/\.$/, "");
@@ -13611,8 +14799,8 @@ async function getAipouStatus() {
13611
14799
  let onchain = { available: false };
13612
14800
  if (contract.address) {
13613
14801
  try {
13614
- const provider = new JsonRpcProvider2(contract.rpcUrl);
13615
- const token = new Contract2(contract.address, aipouTokenAbi, provider);
14802
+ const provider = new JsonRpcProvider3(contract.rpcUrl);
14803
+ const token = new Contract3(contract.address, aipouTokenAbi, provider);
13616
14804
  const [decimals, balance, totalSupply] = await Promise.all([
13617
14805
  token.decimals(),
13618
14806
  token.balanceOf(wallet),
@@ -13643,6 +14831,8 @@ async function getAipouStatus() {
13643
14831
  claimed: claimedReceipts.length,
13644
14832
  pending: walletPendingReceipts.length,
13645
14833
  incompleteSessions: counts.incompleteSessions,
14834
+ activeSessions: counts.activeSessions,
14835
+ expiredSessions: counts.expiredSessions,
13646
14836
  batches: counts.batches
13647
14837
  },
13648
14838
  rewards: {
@@ -13664,9 +14854,9 @@ async function getAipouStatus() {
13664
14854
  // src/index.ts
13665
14855
  var bytes32 = z2.string().regex(/^0x[a-fA-F0-9]{64}$/);
13666
14856
  var usageCounts = {
13667
- inputTokens: z2.number().int().min(0).max(1e7),
13668
- outputTokens: z2.number().int().min(0).max(1e7),
13669
- durationSeconds: z2.number().int().min(0).max(86400)
14857
+ inputTokens: z2.number().int().min(0).max(1e7).describe("Non-negative input-token count for this task, capped at 10,000,000."),
14858
+ outputTokens: z2.number().int().min(0).max(1e7).describe("Non-negative output-token count for this task, capped at 10,000,000."),
14859
+ durationSeconds: z2.number().int().min(0).max(86400).describe("Task duration in whole seconds from 0 through 86,400.")
13670
14860
  };
13671
14861
  var server = new McpServer(
13672
14862
  { name: "aipou-mcp", version: await getPackageVersion() },
@@ -13676,8 +14866,14 @@ var server = new McpServer(
13676
14866
  );
13677
14867
  server.tool(
13678
14868
  "get_aipou_contract",
13679
- "Return the configured AIPOU token contract address, Base network details, explorer URL, and minimal ABI.",
14869
+ "Read the configured AIPOU token and claims contract details for display or client setup. Returns Base chain metadata, explorer URLs, and minimal ABIs; it does not contact a wallet, submit a transaction, or change local state.",
13680
14870
  {},
14871
+ {
14872
+ readOnlyHint: true,
14873
+ destructiveHint: false,
14874
+ idempotentHint: true,
14875
+ openWorldHint: false
14876
+ },
13681
14877
  async () => {
13682
14878
  const contract = await getTokenContractConfig();
13683
14879
  return {
@@ -13687,8 +14883,14 @@ server.tool(
13687
14883
  );
13688
14884
  server.tool(
13689
14885
  "get_aipou_identity",
13690
- "Return the dedicated farming wallet address and public Ed25519 collector key. Private keys are never returned.",
14886
+ "Read the public identity used by this MCP installation when a client needs to display or verify its farming wallet and collector. Returns the wallet address, Ed25519 public key, and fingerprint; it never returns private keys, submits transactions, or changes state.",
13691
14887
  {},
14888
+ {
14889
+ readOnlyHint: true,
14890
+ destructiveHint: false,
14891
+ idempotentHint: true,
14892
+ openWorldHint: false
14893
+ },
13692
14894
  async () => {
13693
14895
  const wallet = agentWallet();
13694
14896
  const collectorPublicKey = await getCollectorPublicKey();
@@ -13702,8 +14904,14 @@ server.tool(
13702
14904
  );
13703
14905
  server.tool(
13704
14906
  "get_aipou_status",
13705
- "Show recorded, pending, and already claimed AIPOU receipts plus the farming wallet's on-chain AIPOU balance. Does not reveal private keys or full receipt payloads.",
14907
+ "Use this first when the user asks for pending, claimed, or wallet-balance information; use a settlement tool only after an explicit claim request. Returns a read-only JSON summary of receipt counts, estimated pending AIPOU, prior settlement state, and the farming wallet's on-chain AIPOU balance. It may read Base through the configured RPC, but it never signs or submits a transaction, changes receipts, reveals private keys, or returns full receipt payloads.",
13706
14908
  {},
14909
+ {
14910
+ readOnlyHint: true,
14911
+ destructiveHint: false,
14912
+ idempotentHint: true,
14913
+ openWorldHint: true
14914
+ },
13707
14915
  async () => {
13708
14916
  const status = await getAipouStatus();
13709
14917
  return { content: [{ type: "text", text: JSON.stringify(status, null, 2) }] };
@@ -13711,8 +14919,14 @@ server.tool(
13711
14919
  );
13712
14920
  server.tool(
13713
14921
  "estimate_ai_reward",
13714
- "Estimate the client-signed AIPOU reward before a task is completed. The final tier is derived by the validator.",
14922
+ "Use before complete_ai_task only when a preview is useful. Provide non-negative inputTokens, outputTokens, and durationSeconds for one task; it returns JSON with estimatedReward and unit for a client-signed estimate. It creates no task or receipt, changes no state, and submits no transaction. The result is informational: complete_ai_task derives the receipt evidence, and the validator determines final eligibility and trust tier.",
13715
14923
  usageCounts,
14924
+ {
14925
+ readOnlyHint: true,
14926
+ destructiveHint: false,
14927
+ idempotentHint: true,
14928
+ openWorldHint: false
14929
+ },
13716
14930
  async (input) => ({
13717
14931
  content: [{
13718
14932
  type: "text",
@@ -13722,12 +14936,18 @@ server.tool(
13722
14936
  );
13723
14937
  server.tool(
13724
14938
  "begin_ai_task",
13725
- "Create a unique task nonce and EIP-712 authorization signed by the dedicated farming wallet.",
14939
+ "Use once before meaningful AI work to create a task session. Provide the provider, model, client, and a 32-byte hash of the task description; returns a unique nonce and EIP-712 authorization signed by the dedicated farming wallet. This writes a pending local task session but does not publish, mint, or submit an on-chain transaction. Pass the returned nonce to complete_ai_task; do not reuse it for another task.",
14940
+ {
14941
+ provider: z2.string().min(1).max(64).describe("AI provider name for this task session, from 1 through 64 characters."),
14942
+ model: z2.string().min(1).max(128).describe("Provider model identifier used for this task, from 1 through 128 characters."),
14943
+ taskHash: bytes32.describe("32-byte hash of the task description; never send the raw prompt or task text."),
14944
+ client: z2.string().min(1).max(64).describe("Host client or agent identifier that starts this task session, from 1 through 64 characters.")
14945
+ },
13726
14946
  {
13727
- provider: z2.string().min(1).max(64),
13728
- model: z2.string().min(1).max(128),
13729
- taskHash: bytes32,
13730
- client: z2.string().min(1).max(64)
14947
+ readOnlyHint: false,
14948
+ destructiveHint: false,
14949
+ idempotentHint: false,
14950
+ openWorldHint: false
13731
14951
  },
13732
14952
  async (input) => {
13733
14953
  const contract = await getTokenContractConfig();
@@ -13743,15 +14963,22 @@ server.tool(
13743
14963
  );
13744
14964
  server.tool(
13745
14965
  "complete_ai_task",
13746
- "Complete a unique task, derive its trust tier, reject duplicate evidence, and create an Ed25519-signed receipt.",
14966
+ "Use exactly once after begin_ai_task finishes meaningful work. Supply that session's nonce, non-negative inputTokens and outputTokens, whole-second durationSeconds, and a 32-byte outputHash; providerEvidence is optional and only for a configured provider key. It validates the nonce and bounded usage, derives the evidence tier, rejects replay, then writes the local receipt store and returns an Ed25519-signed receipt plus compact workReceiptId metadata. It does not publish a Merkle root, mint tokens, or submit a blockchain transaction. Use get_aipou_status to inspect recorded work; repeating a nonce or evidence fails closed instead of creating another receipt.",
13747
14967
  {
13748
- nonce: bytes32,
14968
+ nonce: bytes32.describe("Unique 32-byte nonce returned by begin_ai_task for this exact task."),
13749
14969
  ...usageCounts,
13750
- outputHash: bytes32,
14970
+ tokenUsageMeasurement: z2.enum(["measured", "unmeasured", "unspecified"]).describe("Whether token counts were measured. Use unmeasured with zero counts when usage is unavailable; omitted means unspecified, including legacy clients.").optional(),
14971
+ outputHash: bytes32.describe("SHA-256-style 32-byte hash of the task output; never send the raw output."),
13751
14972
  providerEvidence: z2.object({
13752
- keyId: z2.string().min(1).max(128),
13753
- signature: z2.string().min(32)
13754
- }).optional()
14973
+ keyId: z2.string().min(1).max(128).describe("Configured provider-verification key identifier, between 1 and 128 characters."),
14974
+ signature: z2.string().min(32).describe("Provider signature over the canonical evidence payload; invalid evidence fails closed.")
14975
+ }).describe("Optional provider-signed evidence. Omit it for a client-signed receipt.").optional()
14976
+ },
14977
+ {
14978
+ readOnlyHint: false,
14979
+ destructiveHint: false,
14980
+ idempotentHint: false,
14981
+ openWorldHint: false
13755
14982
  },
13756
14983
  async (input) => {
13757
14984
  const receipt = await completeTask(input);
@@ -13763,17 +14990,87 @@ server.tool(
13763
14990
  );
13764
14991
  server.tool(
13765
14992
  "export_ai_receipts",
13766
- "Export stored signed AI task receipts, optionally filtered by wallet.",
13767
- { wallet: z2.string().regex(/^0x[a-fA-F0-9]{40}$/).optional() },
14993
+ "Export signed receipts already stored in this MCP installation, optionally limited to one farming wallet address. Returns JSON with count and receipt payloads, so use it only where local receipt data is appropriate to expose. It does not create a receipt, validate a claim, contact Base, or submit a transaction; use get_aipou_status instead for a compact private summary.",
14994
+ {
14995
+ wallet: z2.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Optional 20-byte farming wallet address filter. Omit it to export every locally stored receipt.").optional()
14996
+ },
14997
+ {
14998
+ readOnlyHint: true,
14999
+ destructiveHint: false,
15000
+ idempotentHint: true,
15001
+ openWorldHint: false
15002
+ },
13768
15003
  async ({ wallet }) => {
13769
15004
  const receipts = await exportReceipts(wallet);
13770
15005
  return { content: [{ type: "text", text: JSON.stringify({ count: receipts.length, receipts }, null, 2) }] };
13771
15006
  }
13772
15007
  );
15008
+ server.tool(
15009
+ "verify_aipou_receipt",
15010
+ "Verify the self-contained integrity, wallet authorization, and collector signature of one supplied AIPOU receipt. Returns separately typed evidence states; it does not trust a collector, query a registry, submit a claim, prove delivery, business acceptance, settlement, or external effect.",
15011
+ {
15012
+ receipt: z2.record(z2.string(), z2.unknown()).describe("A receipt object containing hashes and signatures only. Do not include raw prompts, outputs, private keys, or customer data.")
15013
+ },
15014
+ {
15015
+ readOnlyHint: true,
15016
+ destructiveHint: false,
15017
+ idempotentHint: true,
15018
+ openWorldHint: false
15019
+ },
15020
+ async ({ receipt }) => ({
15021
+ content: [{ type: "text", text: JSON.stringify(verifyAipouReceipt(receipt), null, 2) }]
15022
+ })
15023
+ );
15024
+ server.tool(
15025
+ "create_paybox_work_link",
15026
+ "Derive and return a digest-only external evidence link between an existing AIPOU workReceiptId and an opaque Paybox operation artifact. It is a pure local calculation: it does not persist the link, call Paybox, unlock a wallet, create a payment, sign a transaction, or change AIPOU claim eligibility. Use it for portable correlation only, and verify both artifacts with their native systems before relying on the returned link.",
15027
+ {
15028
+ workReceiptId: z2.string().regex(/^0x[a-f0-9]{64}$/).describe("Existing lowercase AIPOU workReceiptId."),
15029
+ payboxOperationId: z2.string().min(1).max(256).describe("Opaque Paybox operation reference. Never send a credential, wallet address, payment token, or secret."),
15030
+ payboxOperationDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).describe("Lowercase SHA-256 digest of the Paybox operation artifact bytes."),
15031
+ issuedAt: z2.string().datetime().describe("ISO-8601 time at which this correlation artifact is issued.")
15032
+ },
15033
+ {
15034
+ readOnlyHint: true,
15035
+ destructiveHint: false,
15036
+ idempotentHint: true,
15037
+ openWorldHint: false
15038
+ },
15039
+ async (input) => ({
15040
+ content: [{ type: "text", text: JSON.stringify(createPayboxWorkLink(input), null, 2) }]
15041
+ })
15042
+ );
15043
+ server.tool(
15044
+ "create_technocore_work_link",
15045
+ "Derive and return a digest-only external evidence link between an existing AIPOU workReceiptId and a Technocore transport artifact. It is a pure local calculation: it does not persist the link, call Technocore, sign a message, read a key, verify a transport signature, change AIPOU claim eligibility, or establish delivery. Use it for portable correlation only, and verify the Technocore artifact with its native verifier before relying on the returned link.",
15046
+ {
15047
+ workReceiptId: z2.string().regex(/^0x[a-f0-9]{64}$/).describe("Existing lowercase AIPOU workReceiptId."),
15048
+ transportArtifactId: z2.string().min(1).max(256).describe("Opaque Technocore transport artifact reference. Never send a DID private key, seed, passphrase, wallet, or secret."),
15049
+ transportArtifactDigest: z2.string().regex(/^sha256:[a-f0-9]{64}$/).describe("Lowercase SHA-256 digest of the canonical Technocore transport artifact bytes."),
15050
+ issuedAt: z2.string().datetime().describe("ISO-8601 time at which this correlation artifact is issued.")
15051
+ },
15052
+ {
15053
+ readOnlyHint: true,
15054
+ destructiveHint: false,
15055
+ idempotentHint: true,
15056
+ openWorldHint: false
15057
+ },
15058
+ async (input) => ({
15059
+ content: [{ type: "text", text: JSON.stringify(createTechnocoreWorkLink(input), null, 2) }]
15060
+ })
15061
+ );
13773
15062
  server.tool(
13774
15063
  "settle_ai_rewards",
13775
- "After an explicit user claim request for one limited batch, validate eligible receipts against the settlement policy, publish a Merkle root, and mint the included rewards. Submits two on-chain transactions; the host's own confirmation policy applies.",
13776
- { maxReceipts: z2.number().int().min(1).max(100).default(25) },
15064
+ "Use only after an explicit user request to claim one limited batch, not for status or reward previews. maxReceipts defaults to 25 and is capped at 100; the tool validates eligible local receipts against settlement policy, publishes one Merkle root, and mints the included rewards. It submits two Base transactions, so the host client and user must apply their own confirmation policy. Use settle_all_ai_rewards instead for an explicit request to claim all pending eligible receipts.",
15065
+ {
15066
+ maxReceipts: z2.number().int().min(1).max(100).default(25).describe("Maximum eligible receipts to include in this one settlement batch, from 1 through 100; defaults to 25.")
15067
+ },
15068
+ {
15069
+ readOnlyHint: false,
15070
+ destructiveHint: false,
15071
+ idempotentHint: false,
15072
+ openWorldHint: true
15073
+ },
13777
15074
  async ({ maxReceipts }) => {
13778
15075
  const batch = await settleRewards(maxReceipts);
13779
15076
  return { content: [{ type: "text", text: JSON.stringify(batch, null, 2) }] };
@@ -13781,10 +15078,16 @@ server.tool(
13781
15078
  );
13782
15079
  server.tool(
13783
15080
  "settle_all_ai_rewards",
13784
- "After an explicit broad claim request, settle all currently pending eligible receipts from the shared AIPOU data directory by submitting as many bounded batches as needed. Each batch publishes one Merkle root and submits one claim transaction.",
15081
+ "Use only after an explicit broad request such as 'claim my AIPOU' or 'settle all pending AIPOU'; do not use it for a status check. batchSize defaults to 100 and maxBatches to 20, with bounds of 1-100 and 1-50, so large queues remain bounded. It validates all currently eligible local receipts and submits Base transactions for each batch: one Merkle-root publication and one claim transaction per batch. The host client and user must apply their own confirmation policy; use settle_ai_rewards when the user asks for one limited batch.",
15082
+ {
15083
+ batchSize: z2.number().int().min(1).max(100).default(100).describe("Eligible receipts per settlement batch, from 1 through 100; defaults to 100."),
15084
+ maxBatches: z2.number().int().min(1).max(50).default(20).describe("Maximum settlement batches to submit in this call, from 1 through 50; defaults to 20.")
15085
+ },
13785
15086
  {
13786
- batchSize: z2.number().int().min(1).max(100).default(100),
13787
- maxBatches: z2.number().int().min(1).max(50).default(20)
15087
+ readOnlyHint: false,
15088
+ destructiveHint: false,
15089
+ idempotentHint: false,
15090
+ openWorldHint: true
13788
15091
  },
13789
15092
  async ({ batchSize, maxBatches }) => {
13790
15093
  const result = await settleAllRewards(batchSize, maxBatches);
@@ -13803,6 +15106,10 @@ function cliValue(name) {
13803
15106
  }
13804
15107
  if (cliArguments.has("--demo")) {
13805
15108
  console.log(JSON.stringify(await runLocalReceiptDemo(), null, 2));
15109
+ } else if (cliArguments.has("--pilot")) {
15110
+ console.log(JSON.stringify(await runObservedOutcomePilot(), null, 2));
15111
+ } else if (cliArguments.has("--feedback")) {
15112
+ console.log(JSON.stringify(await localFeedbackSummary(), null, 2));
13806
15113
  } else if (cliArguments.has("--doctor")) {
13807
15114
  const result = await runInstallationDoctor(cliValue("--data-dir"));
13808
15115
  console.log(JSON.stringify(result, null, 2));
@@ -13825,10 +15132,12 @@ if (cliArguments.has("--demo")) {
13825
15132
  Usage:
13826
15133
  aipou-mcp Start the MCP stdio server
13827
15134
  aipou-mcp --demo Create and verify one disposable local receipt
15135
+ aipou-mcp --pilot Rehearse a bounded lifecycle and report typed evidence states
13828
15136
  aipou-mcp --init Create a protected persistent farming identity
13829
15137
  aipou-mcp --init --data-dir <path>
13830
15138
  Initialize in a specific directory
13831
15139
  aipou-mcp --doctor Validate a persistent installation without changes
15140
+ aipou-mcp --feedback Print local usage counts without identifiers or uploads
13832
15141
  aipou-mcp --doctor --data-dir <path>
13833
15142
  Diagnose a specific identity directory
13834
15143
  aipou-mcp --help Show this help
@@ -13838,8 +15147,18 @@ wallet, collector key, and receipt state are removed before the command exits.
13838
15147
  Init writes a new dedicated-wallet key with restricted access and never prints
13839
15148
  the secret. Re-running init reports the existing identity instead of
13840
15149
  overwriting it. Doctor reads configuration without writing files, making
13841
- network requests, moving funds, or submitting claims.`);
15150
+ network requests, moving funds, or submitting claims. The pilot is also
15151
+ offline and disposable: it does not claim delivery, acceptance, settlement,
15152
+ or external effect.`);
13842
15153
  } else {
13843
- const transport = new StdioServerTransport();
15154
+ const input = new BoundedStdioInput();
15155
+ input.on("error", () => {
15156
+ console.error("AIPOU_INPUT_TOO_LARGE: input connection closed; limit each JSON-RPC message to 1 MiB.");
15157
+ process.exitCode = 1;
15158
+ void server.close();
15159
+ process.stdin.destroy();
15160
+ });
15161
+ process.stdin.pipe(input);
15162
+ const transport = new StdioServerTransport(input, process.stdout);
13844
15163
  await server.connect(transport);
13845
15164
  }