@octocodeai/freellama-mcp-server 0.1.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3109,6 +3109,7 @@ var require_utils = __commonJS({
3109
3109
  "use strict";
3110
3110
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3111
3111
  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);
3112
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3112
3113
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3113
3114
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3114
3115
  var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
@@ -3261,8 +3262,8 @@ var require_utils = __commonJS({
3261
3262
  }
3262
3263
  return ind;
3263
3264
  }
3264
- function removeDotSegments(path5) {
3265
- let input = path5;
3265
+ function removeDotSegments(path6) {
3266
+ let input = path6;
3266
3267
  const output = [];
3267
3268
  let nextSlash = -1;
3268
3269
  let len = 0;
@@ -3574,8 +3575,12 @@ var require_utils = __commonJS({
3574
3575
  uriTokens.push(host);
3575
3576
  }
3576
3577
  if (typeof component.port === "number" || typeof component.port === "string") {
3578
+ const port = String(component.port);
3579
+ if (!isPort(port)) {
3580
+ throw new TypeError("URI port is malformed.");
3581
+ }
3577
3582
  uriTokens.push(":");
3578
- uriTokens.push(String(component.port));
3583
+ uriTokens.push(port);
3579
3584
  }
3580
3585
  return uriTokens.length ? uriTokens.join("") : void 0;
3581
3586
  }
@@ -3667,8 +3672,8 @@ var require_schemes = __commonJS({
3667
3672
  }
3668
3673
  if (wsComponent.resourceName) {
3669
3674
  const queryIndex = wsComponent.resourceName.indexOf("?");
3670
- const path5 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3671
- wsComponent.path = path5 && path5 !== "/" ? path5 : void 0;
3675
+ const path6 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3676
+ wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
3672
3677
  wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
3673
3678
  wsComponent.resourceName = void 0;
3674
3679
  }
@@ -4018,12 +4023,15 @@ var require_fast_uri = __commonJS({
4018
4023
  }
4019
4024
  return false;
4020
4025
  }
4026
+ function isIPLiteral(host) {
4027
+ return host[0] === "[" && host[host.length - 1] === "]";
4028
+ }
4021
4029
  function hasMalformedComponentPercentEncoding(matches) {
4022
4030
  const host = matches[4];
4023
- return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !(host[0] === "[" && host[host.length - 1] === "]") && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4031
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4024
4032
  }
4025
4033
  function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4026
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && parsed.host[0] !== "[" && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4034
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4027
4035
  try {
4028
4036
  parsed.host = new URL("http://" + parsed.host).hostname;
4029
4037
  } catch (e) {
@@ -4110,10 +4118,11 @@ var require_fast_uri = __commonJS({
4110
4118
  if (parsed.host) {
4111
4119
  const ipv4result = isIPv4(parsed.host);
4112
4120
  if (ipv4result === false) {
4113
- const bracketedIPLiteral = parsed.host[0] === "[" && parsed.host[parsed.host.length - 1] === "]";
4121
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4122
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
4114
4123
  const ipv6result = normalizeIPv6(parsed.host);
4115
4124
  isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4116
- malformedIPLiteral = bracketedIPLiteral && ipv6result.error === true;
4125
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4117
4126
  parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4118
4127
  if (malformedIPLiteral) {
4119
4128
  parsed.error = parsed.error || "URI host is malformed.";
@@ -4136,14 +4145,17 @@ var require_fast_uri = __commonJS({
4136
4145
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
4137
4146
  }
4138
4147
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
4139
- malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4140
- if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4141
- if (uri.indexOf("%") !== -1) {
4142
- if (parsed.host !== void 0 && !malformedIPLiteral) {
4143
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4144
- parsed.host = reescapeHostDelimiters(host, isIP);
4145
- }
4148
+ if (!malformedIPLiteral) {
4149
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4150
+ }
4151
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4152
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4153
+ if (!isIP) {
4154
+ host = normalizePercentEncoding(host.toLowerCase());
4146
4155
  }
4156
+ parsed.host = reescapeHostDelimiters(host, isIP);
4157
+ }
4158
+ if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4147
4159
  if (parsed.path) {
4148
4160
  parsed.path = normalizePathEncoding(parsed.path);
4149
4161
  }
@@ -7189,12 +7201,12 @@ var require_dist = __commonJS({
7189
7201
 
7190
7202
  // src/index.ts
7191
7203
  import { execFile } from "node:child_process";
7192
- import { existsSync as existsSync4, readdirSync } from "node:fs";
7204
+ import { existsSync as existsSync5, readdirSync } from "node:fs";
7193
7205
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
7194
7206
  import { createHash } from "node:crypto";
7195
7207
  import { tmpdir } from "node:os";
7196
- import path4 from "node:path";
7197
- import { fileURLToPath as fileURLToPath2 } from "node:url";
7208
+ import path5 from "node:path";
7209
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
7198
7210
  import { promisify } from "node:util";
7199
7211
 
7200
7212
  // ../../node_modules/zod/v3/external.js
@@ -7675,8 +7687,8 @@ function getErrorMap() {
7675
7687
 
7676
7688
  // ../../node_modules/zod/v3/helpers/parseUtil.js
7677
7689
  var makeIssue = (params) => {
7678
- const { data, path: path5, errorMaps, issueData } = params;
7679
- const fullPath = [...path5, ...issueData.path || []];
7690
+ const { data, path: path6, errorMaps, issueData } = params;
7691
+ const fullPath = [...path6, ...issueData.path || []];
7680
7692
  const fullIssue = {
7681
7693
  ...issueData,
7682
7694
  path: fullPath
@@ -7731,18 +7743,18 @@ var ParseStatus = class _ParseStatus {
7731
7743
  if (this.value !== "aborted")
7732
7744
  this.value = "aborted";
7733
7745
  }
7734
- static mergeArray(status, results) {
7746
+ static mergeArray(status2, results) {
7735
7747
  const arrayValue = [];
7736
7748
  for (const s of results) {
7737
7749
  if (s.status === "aborted")
7738
7750
  return INVALID;
7739
7751
  if (s.status === "dirty")
7740
- status.dirty();
7752
+ status2.dirty();
7741
7753
  arrayValue.push(s.value);
7742
7754
  }
7743
- return { status: status.value, value: arrayValue };
7755
+ return { status: status2.value, value: arrayValue };
7744
7756
  }
7745
- static async mergeObjectAsync(status, pairs) {
7757
+ static async mergeObjectAsync(status2, pairs) {
7746
7758
  const syncPairs = [];
7747
7759
  for (const pair of pairs) {
7748
7760
  const key = await pair.key;
@@ -7752,9 +7764,9 @@ var ParseStatus = class _ParseStatus {
7752
7764
  value
7753
7765
  });
7754
7766
  }
7755
- return _ParseStatus.mergeObjectSync(status, syncPairs);
7767
+ return _ParseStatus.mergeObjectSync(status2, syncPairs);
7756
7768
  }
7757
- static mergeObjectSync(status, pairs) {
7769
+ static mergeObjectSync(status2, pairs) {
7758
7770
  const finalObject = {};
7759
7771
  for (const pair of pairs) {
7760
7772
  const { key, value } = pair;
@@ -7763,14 +7775,14 @@ var ParseStatus = class _ParseStatus {
7763
7775
  if (value.status === "aborted")
7764
7776
  return INVALID;
7765
7777
  if (key.status === "dirty")
7766
- status.dirty();
7778
+ status2.dirty();
7767
7779
  if (value.status === "dirty")
7768
- status.dirty();
7780
+ status2.dirty();
7769
7781
  if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) {
7770
7782
  finalObject[key.value] = value.value;
7771
7783
  }
7772
7784
  }
7773
- return { status: status.value, value: finalObject };
7785
+ return { status: status2.value, value: finalObject };
7774
7786
  }
7775
7787
  };
7776
7788
  var INVALID = Object.freeze({
@@ -7792,11 +7804,11 @@ var errorUtil;
7792
7804
 
7793
7805
  // ../../node_modules/zod/v3/types.js
7794
7806
  var ParseInputLazyPath = class {
7795
- constructor(parent, value, path5, key) {
7807
+ constructor(parent, value, path6, key) {
7796
7808
  this._cachedPath = [];
7797
7809
  this.parent = parent;
7798
7810
  this.data = value;
7799
- this._path = path5;
7811
+ this._path = path6;
7800
7812
  this._key = key;
7801
7813
  }
7802
7814
  get path() {
@@ -8230,7 +8242,7 @@ var ZodString = class _ZodString2 extends ZodType {
8230
8242
  });
8231
8243
  return INVALID;
8232
8244
  }
8233
- const status = new ParseStatus();
8245
+ const status2 = new ParseStatus();
8234
8246
  let ctx = void 0;
8235
8247
  for (const check2 of this._def.checks) {
8236
8248
  if (check2.kind === "min") {
@@ -8244,7 +8256,7 @@ var ZodString = class _ZodString2 extends ZodType {
8244
8256
  exact: false,
8245
8257
  message: check2.message
8246
8258
  });
8247
- status.dirty();
8259
+ status2.dirty();
8248
8260
  }
8249
8261
  } else if (check2.kind === "max") {
8250
8262
  if (input.data.length > check2.value) {
@@ -8257,7 +8269,7 @@ var ZodString = class _ZodString2 extends ZodType {
8257
8269
  exact: false,
8258
8270
  message: check2.message
8259
8271
  });
8260
- status.dirty();
8272
+ status2.dirty();
8261
8273
  }
8262
8274
  } else if (check2.kind === "length") {
8263
8275
  const tooBig = input.data.length > check2.value;
@@ -8283,7 +8295,7 @@ var ZodString = class _ZodString2 extends ZodType {
8283
8295
  message: check2.message
8284
8296
  });
8285
8297
  }
8286
- status.dirty();
8298
+ status2.dirty();
8287
8299
  }
8288
8300
  } else if (check2.kind === "email") {
8289
8301
  if (!emailRegex.test(input.data)) {
@@ -8293,7 +8305,7 @@ var ZodString = class _ZodString2 extends ZodType {
8293
8305
  code: ZodIssueCode.invalid_string,
8294
8306
  message: check2.message
8295
8307
  });
8296
- status.dirty();
8308
+ status2.dirty();
8297
8309
  }
8298
8310
  } else if (check2.kind === "emoji") {
8299
8311
  if (!emojiRegex) {
@@ -8306,7 +8318,7 @@ var ZodString = class _ZodString2 extends ZodType {
8306
8318
  code: ZodIssueCode.invalid_string,
8307
8319
  message: check2.message
8308
8320
  });
8309
- status.dirty();
8321
+ status2.dirty();
8310
8322
  }
8311
8323
  } else if (check2.kind === "uuid") {
8312
8324
  if (!uuidRegex.test(input.data)) {
@@ -8316,7 +8328,7 @@ var ZodString = class _ZodString2 extends ZodType {
8316
8328
  code: ZodIssueCode.invalid_string,
8317
8329
  message: check2.message
8318
8330
  });
8319
- status.dirty();
8331
+ status2.dirty();
8320
8332
  }
8321
8333
  } else if (check2.kind === "nanoid") {
8322
8334
  if (!nanoidRegex.test(input.data)) {
@@ -8326,7 +8338,7 @@ var ZodString = class _ZodString2 extends ZodType {
8326
8338
  code: ZodIssueCode.invalid_string,
8327
8339
  message: check2.message
8328
8340
  });
8329
- status.dirty();
8341
+ status2.dirty();
8330
8342
  }
8331
8343
  } else if (check2.kind === "cuid") {
8332
8344
  if (!cuidRegex.test(input.data)) {
@@ -8336,7 +8348,7 @@ var ZodString = class _ZodString2 extends ZodType {
8336
8348
  code: ZodIssueCode.invalid_string,
8337
8349
  message: check2.message
8338
8350
  });
8339
- status.dirty();
8351
+ status2.dirty();
8340
8352
  }
8341
8353
  } else if (check2.kind === "cuid2") {
8342
8354
  if (!cuid2Regex.test(input.data)) {
@@ -8346,7 +8358,7 @@ var ZodString = class _ZodString2 extends ZodType {
8346
8358
  code: ZodIssueCode.invalid_string,
8347
8359
  message: check2.message
8348
8360
  });
8349
- status.dirty();
8361
+ status2.dirty();
8350
8362
  }
8351
8363
  } else if (check2.kind === "ulid") {
8352
8364
  if (!ulidRegex.test(input.data)) {
@@ -8356,7 +8368,7 @@ var ZodString = class _ZodString2 extends ZodType {
8356
8368
  code: ZodIssueCode.invalid_string,
8357
8369
  message: check2.message
8358
8370
  });
8359
- status.dirty();
8371
+ status2.dirty();
8360
8372
  }
8361
8373
  } else if (check2.kind === "url") {
8362
8374
  try {
@@ -8368,7 +8380,7 @@ var ZodString = class _ZodString2 extends ZodType {
8368
8380
  code: ZodIssueCode.invalid_string,
8369
8381
  message: check2.message
8370
8382
  });
8371
- status.dirty();
8383
+ status2.dirty();
8372
8384
  }
8373
8385
  } else if (check2.kind === "regex") {
8374
8386
  check2.regex.lastIndex = 0;
@@ -8380,7 +8392,7 @@ var ZodString = class _ZodString2 extends ZodType {
8380
8392
  code: ZodIssueCode.invalid_string,
8381
8393
  message: check2.message
8382
8394
  });
8383
- status.dirty();
8395
+ status2.dirty();
8384
8396
  }
8385
8397
  } else if (check2.kind === "trim") {
8386
8398
  input.data = input.data.trim();
@@ -8392,7 +8404,7 @@ var ZodString = class _ZodString2 extends ZodType {
8392
8404
  validation: { includes: check2.value, position: check2.position },
8393
8405
  message: check2.message
8394
8406
  });
8395
- status.dirty();
8407
+ status2.dirty();
8396
8408
  }
8397
8409
  } else if (check2.kind === "toLowerCase") {
8398
8410
  input.data = input.data.toLowerCase();
@@ -8406,7 +8418,7 @@ var ZodString = class _ZodString2 extends ZodType {
8406
8418
  validation: { startsWith: check2.value },
8407
8419
  message: check2.message
8408
8420
  });
8409
- status.dirty();
8421
+ status2.dirty();
8410
8422
  }
8411
8423
  } else if (check2.kind === "endsWith") {
8412
8424
  if (!input.data.endsWith(check2.value)) {
@@ -8416,7 +8428,7 @@ var ZodString = class _ZodString2 extends ZodType {
8416
8428
  validation: { endsWith: check2.value },
8417
8429
  message: check2.message
8418
8430
  });
8419
- status.dirty();
8431
+ status2.dirty();
8420
8432
  }
8421
8433
  } else if (check2.kind === "datetime") {
8422
8434
  const regex = datetimeRegex(check2);
@@ -8427,7 +8439,7 @@ var ZodString = class _ZodString2 extends ZodType {
8427
8439
  validation: "datetime",
8428
8440
  message: check2.message
8429
8441
  });
8430
- status.dirty();
8442
+ status2.dirty();
8431
8443
  }
8432
8444
  } else if (check2.kind === "date") {
8433
8445
  const regex = dateRegex;
@@ -8438,7 +8450,7 @@ var ZodString = class _ZodString2 extends ZodType {
8438
8450
  validation: "date",
8439
8451
  message: check2.message
8440
8452
  });
8441
- status.dirty();
8453
+ status2.dirty();
8442
8454
  }
8443
8455
  } else if (check2.kind === "time") {
8444
8456
  const regex = timeRegex(check2);
@@ -8449,7 +8461,7 @@ var ZodString = class _ZodString2 extends ZodType {
8449
8461
  validation: "time",
8450
8462
  message: check2.message
8451
8463
  });
8452
- status.dirty();
8464
+ status2.dirty();
8453
8465
  }
8454
8466
  } else if (check2.kind === "duration") {
8455
8467
  if (!durationRegex.test(input.data)) {
@@ -8459,7 +8471,7 @@ var ZodString = class _ZodString2 extends ZodType {
8459
8471
  code: ZodIssueCode.invalid_string,
8460
8472
  message: check2.message
8461
8473
  });
8462
- status.dirty();
8474
+ status2.dirty();
8463
8475
  }
8464
8476
  } else if (check2.kind === "ip") {
8465
8477
  if (!isValidIP(input.data, check2.version)) {
@@ -8469,7 +8481,7 @@ var ZodString = class _ZodString2 extends ZodType {
8469
8481
  code: ZodIssueCode.invalid_string,
8470
8482
  message: check2.message
8471
8483
  });
8472
- status.dirty();
8484
+ status2.dirty();
8473
8485
  }
8474
8486
  } else if (check2.kind === "jwt") {
8475
8487
  if (!isValidJWT(input.data, check2.alg)) {
@@ -8479,7 +8491,7 @@ var ZodString = class _ZodString2 extends ZodType {
8479
8491
  code: ZodIssueCode.invalid_string,
8480
8492
  message: check2.message
8481
8493
  });
8482
- status.dirty();
8494
+ status2.dirty();
8483
8495
  }
8484
8496
  } else if (check2.kind === "cidr") {
8485
8497
  if (!isValidCidr(input.data, check2.version)) {
@@ -8489,7 +8501,7 @@ var ZodString = class _ZodString2 extends ZodType {
8489
8501
  code: ZodIssueCode.invalid_string,
8490
8502
  message: check2.message
8491
8503
  });
8492
- status.dirty();
8504
+ status2.dirty();
8493
8505
  }
8494
8506
  } else if (check2.kind === "base64") {
8495
8507
  if (!base64Regex.test(input.data)) {
@@ -8499,7 +8511,7 @@ var ZodString = class _ZodString2 extends ZodType {
8499
8511
  code: ZodIssueCode.invalid_string,
8500
8512
  message: check2.message
8501
8513
  });
8502
- status.dirty();
8514
+ status2.dirty();
8503
8515
  }
8504
8516
  } else if (check2.kind === "base64url") {
8505
8517
  if (!base64urlRegex.test(input.data)) {
@@ -8509,13 +8521,13 @@ var ZodString = class _ZodString2 extends ZodType {
8509
8521
  code: ZodIssueCode.invalid_string,
8510
8522
  message: check2.message
8511
8523
  });
8512
- status.dirty();
8524
+ status2.dirty();
8513
8525
  }
8514
8526
  } else {
8515
8527
  util.assertNever(check2);
8516
8528
  }
8517
8529
  }
8518
- return { status: status.value, value: input.data };
8530
+ return { status: status2.value, value: input.data };
8519
8531
  }
8520
8532
  _regex(regex, validation, message) {
8521
8533
  return this.refinement((data) => regex.test(data), {
@@ -8791,7 +8803,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8791
8803
  return INVALID;
8792
8804
  }
8793
8805
  let ctx = void 0;
8794
- const status = new ParseStatus();
8806
+ const status2 = new ParseStatus();
8795
8807
  for (const check2 of this._def.checks) {
8796
8808
  if (check2.kind === "int") {
8797
8809
  if (!util.isInteger(input.data)) {
@@ -8802,7 +8814,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8802
8814
  received: "float",
8803
8815
  message: check2.message
8804
8816
  });
8805
- status.dirty();
8817
+ status2.dirty();
8806
8818
  }
8807
8819
  } else if (check2.kind === "min") {
8808
8820
  const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
@@ -8816,7 +8828,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8816
8828
  exact: false,
8817
8829
  message: check2.message
8818
8830
  });
8819
- status.dirty();
8831
+ status2.dirty();
8820
8832
  }
8821
8833
  } else if (check2.kind === "max") {
8822
8834
  const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
@@ -8830,7 +8842,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8830
8842
  exact: false,
8831
8843
  message: check2.message
8832
8844
  });
8833
- status.dirty();
8845
+ status2.dirty();
8834
8846
  }
8835
8847
  } else if (check2.kind === "multipleOf") {
8836
8848
  if (floatSafeRemainder(input.data, check2.value) !== 0) {
@@ -8840,7 +8852,7 @@ var ZodNumber = class _ZodNumber extends ZodType {
8840
8852
  multipleOf: check2.value,
8841
8853
  message: check2.message
8842
8854
  });
8843
- status.dirty();
8855
+ status2.dirty();
8844
8856
  }
8845
8857
  } else if (check2.kind === "finite") {
8846
8858
  if (!Number.isFinite(input.data)) {
@@ -8849,13 +8861,13 @@ var ZodNumber = class _ZodNumber extends ZodType {
8849
8861
  code: ZodIssueCode.not_finite,
8850
8862
  message: check2.message
8851
8863
  });
8852
- status.dirty();
8864
+ status2.dirty();
8853
8865
  }
8854
8866
  } else {
8855
8867
  util.assertNever(check2);
8856
8868
  }
8857
8869
  }
8858
- return { status: status.value, value: input.data };
8870
+ return { status: status2.value, value: input.data };
8859
8871
  }
8860
8872
  gte(value, message) {
8861
8873
  return this.setLimit("min", value, true, errorUtil.toString(message));
@@ -9020,7 +9032,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
9020
9032
  return this._getInvalidInput(input);
9021
9033
  }
9022
9034
  let ctx = void 0;
9023
- const status = new ParseStatus();
9035
+ const status2 = new ParseStatus();
9024
9036
  for (const check2 of this._def.checks) {
9025
9037
  if (check2.kind === "min") {
9026
9038
  const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value;
@@ -9033,7 +9045,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
9033
9045
  inclusive: check2.inclusive,
9034
9046
  message: check2.message
9035
9047
  });
9036
- status.dirty();
9048
+ status2.dirty();
9037
9049
  }
9038
9050
  } else if (check2.kind === "max") {
9039
9051
  const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value;
@@ -9046,7 +9058,7 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
9046
9058
  inclusive: check2.inclusive,
9047
9059
  message: check2.message
9048
9060
  });
9049
- status.dirty();
9061
+ status2.dirty();
9050
9062
  }
9051
9063
  } else if (check2.kind === "multipleOf") {
9052
9064
  if (input.data % check2.value !== BigInt(0)) {
@@ -9056,13 +9068,13 @@ var ZodBigInt = class _ZodBigInt extends ZodType {
9056
9068
  multipleOf: check2.value,
9057
9069
  message: check2.message
9058
9070
  });
9059
- status.dirty();
9071
+ status2.dirty();
9060
9072
  }
9061
9073
  } else {
9062
9074
  util.assertNever(check2);
9063
9075
  }
9064
9076
  }
9065
- return { status: status.value, value: input.data };
9077
+ return { status: status2.value, value: input.data };
9066
9078
  }
9067
9079
  _getInvalidInput(input) {
9068
9080
  const ctx = this._getOrReturnCtx(input);
@@ -9220,7 +9232,7 @@ var ZodDate = class _ZodDate extends ZodType {
9220
9232
  });
9221
9233
  return INVALID;
9222
9234
  }
9223
- const status = new ParseStatus();
9235
+ const status2 = new ParseStatus();
9224
9236
  let ctx = void 0;
9225
9237
  for (const check2 of this._def.checks) {
9226
9238
  if (check2.kind === "min") {
@@ -9234,7 +9246,7 @@ var ZodDate = class _ZodDate extends ZodType {
9234
9246
  minimum: check2.value,
9235
9247
  type: "date"
9236
9248
  });
9237
- status.dirty();
9249
+ status2.dirty();
9238
9250
  }
9239
9251
  } else if (check2.kind === "max") {
9240
9252
  if (input.data.getTime() > check2.value) {
@@ -9247,14 +9259,14 @@ var ZodDate = class _ZodDate extends ZodType {
9247
9259
  maximum: check2.value,
9248
9260
  type: "date"
9249
9261
  });
9250
- status.dirty();
9262
+ status2.dirty();
9251
9263
  }
9252
9264
  } else {
9253
9265
  util.assertNever(check2);
9254
9266
  }
9255
9267
  }
9256
9268
  return {
9257
- status: status.value,
9269
+ status: status2.value,
9258
9270
  value: new Date(input.data.getTime())
9259
9271
  };
9260
9272
  }
@@ -9440,7 +9452,7 @@ ZodVoid.create = (params) => {
9440
9452
  };
9441
9453
  var ZodArray = class _ZodArray extends ZodType {
9442
9454
  _parse(input) {
9443
- const { ctx, status } = this._processInputParams(input);
9455
+ const { ctx, status: status2 } = this._processInputParams(input);
9444
9456
  const def = this._def;
9445
9457
  if (ctx.parsedType !== ZodParsedType.array) {
9446
9458
  addIssueToContext(ctx, {
@@ -9463,7 +9475,7 @@ var ZodArray = class _ZodArray extends ZodType {
9463
9475
  exact: true,
9464
9476
  message: def.exactLength.message
9465
9477
  });
9466
- status.dirty();
9478
+ status2.dirty();
9467
9479
  }
9468
9480
  }
9469
9481
  if (def.minLength !== null) {
@@ -9476,7 +9488,7 @@ var ZodArray = class _ZodArray extends ZodType {
9476
9488
  exact: false,
9477
9489
  message: def.minLength.message
9478
9490
  });
9479
- status.dirty();
9491
+ status2.dirty();
9480
9492
  }
9481
9493
  }
9482
9494
  if (def.maxLength !== null) {
@@ -9489,20 +9501,20 @@ var ZodArray = class _ZodArray extends ZodType {
9489
9501
  exact: false,
9490
9502
  message: def.maxLength.message
9491
9503
  });
9492
- status.dirty();
9504
+ status2.dirty();
9493
9505
  }
9494
9506
  }
9495
9507
  if (ctx.common.async) {
9496
9508
  return Promise.all([...ctx.data].map((item, i) => {
9497
9509
  return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i));
9498
9510
  })).then((result2) => {
9499
- return ParseStatus.mergeArray(status, result2);
9511
+ return ParseStatus.mergeArray(status2, result2);
9500
9512
  });
9501
9513
  }
9502
9514
  const result = [...ctx.data].map((item, i) => {
9503
9515
  return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i));
9504
9516
  });
9505
- return ParseStatus.mergeArray(status, result);
9517
+ return ParseStatus.mergeArray(status2, result);
9506
9518
  }
9507
9519
  get element() {
9508
9520
  return this._def.type;
@@ -9591,7 +9603,7 @@ var ZodObject = class _ZodObject extends ZodType {
9591
9603
  });
9592
9604
  return INVALID;
9593
9605
  }
9594
- const { status, ctx } = this._processInputParams(input);
9606
+ const { status: status2, ctx } = this._processInputParams(input);
9595
9607
  const { shape, keys: shapeKeys } = this._getCached();
9596
9608
  const extraKeys = [];
9597
9609
  if (!(this._def.catchall instanceof ZodNever && this._def.unknownKeys === "strip")) {
@@ -9626,7 +9638,7 @@ var ZodObject = class _ZodObject extends ZodType {
9626
9638
  code: ZodIssueCode.unrecognized_keys,
9627
9639
  keys: extraKeys
9628
9640
  });
9629
- status.dirty();
9641
+ status2.dirty();
9630
9642
  }
9631
9643
  } else if (unknownKeys === "strip") {
9632
9644
  } else {
@@ -9660,10 +9672,10 @@ var ZodObject = class _ZodObject extends ZodType {
9660
9672
  }
9661
9673
  return syncPairs;
9662
9674
  }).then((syncPairs) => {
9663
- return ParseStatus.mergeObjectSync(status, syncPairs);
9675
+ return ParseStatus.mergeObjectSync(status2, syncPairs);
9664
9676
  });
9665
9677
  } else {
9666
- return ParseStatus.mergeObjectSync(status, pairs);
9678
+ return ParseStatus.mergeObjectSync(status2, pairs);
9667
9679
  }
9668
9680
  }
9669
9681
  get shape() {
@@ -10141,7 +10153,7 @@ function mergeValues(a, b) {
10141
10153
  }
10142
10154
  var ZodIntersection = class extends ZodType {
10143
10155
  _parse(input) {
10144
- const { status, ctx } = this._processInputParams(input);
10156
+ const { status: status2, ctx } = this._processInputParams(input);
10145
10157
  const handleParsed = (parsedLeft, parsedRight) => {
10146
10158
  if (isAborted(parsedLeft) || isAborted(parsedRight)) {
10147
10159
  return INVALID;
@@ -10154,9 +10166,9 @@ var ZodIntersection = class extends ZodType {
10154
10166
  return INVALID;
10155
10167
  }
10156
10168
  if (isDirty(parsedLeft) || isDirty(parsedRight)) {
10157
- status.dirty();
10169
+ status2.dirty();
10158
10170
  }
10159
- return { status: status.value, value: merged.data };
10171
+ return { status: status2.value, value: merged.data };
10160
10172
  };
10161
10173
  if (ctx.common.async) {
10162
10174
  return Promise.all([
@@ -10194,7 +10206,7 @@ ZodIntersection.create = (left, right, params) => {
10194
10206
  };
10195
10207
  var ZodTuple = class _ZodTuple extends ZodType {
10196
10208
  _parse(input) {
10197
- const { status, ctx } = this._processInputParams(input);
10209
+ const { status: status2, ctx } = this._processInputParams(input);
10198
10210
  if (ctx.parsedType !== ZodParsedType.array) {
10199
10211
  addIssueToContext(ctx, {
10200
10212
  code: ZodIssueCode.invalid_type,
@@ -10222,7 +10234,7 @@ var ZodTuple = class _ZodTuple extends ZodType {
10222
10234
  exact: false,
10223
10235
  type: "array"
10224
10236
  });
10225
- status.dirty();
10237
+ status2.dirty();
10226
10238
  }
10227
10239
  const items = [...ctx.data].map((item, itemIndex) => {
10228
10240
  const schema = this._def.items[itemIndex] || this._def.rest;
@@ -10232,10 +10244,10 @@ var ZodTuple = class _ZodTuple extends ZodType {
10232
10244
  }).filter((x) => !!x);
10233
10245
  if (ctx.common.async) {
10234
10246
  return Promise.all(items).then((results) => {
10235
- return ParseStatus.mergeArray(status, results);
10247
+ return ParseStatus.mergeArray(status2, results);
10236
10248
  });
10237
10249
  } else {
10238
- return ParseStatus.mergeArray(status, items);
10250
+ return ParseStatus.mergeArray(status2, items);
10239
10251
  }
10240
10252
  }
10241
10253
  get items() {
@@ -10267,7 +10279,7 @@ var ZodRecord = class _ZodRecord extends ZodType {
10267
10279
  return this._def.valueType;
10268
10280
  }
10269
10281
  _parse(input) {
10270
- const { status, ctx } = this._processInputParams(input);
10282
+ const { status: status2, ctx } = this._processInputParams(input);
10271
10283
  if (ctx.parsedType !== ZodParsedType.object) {
10272
10284
  addIssueToContext(ctx, {
10273
10285
  code: ZodIssueCode.invalid_type,
@@ -10287,9 +10299,9 @@ var ZodRecord = class _ZodRecord extends ZodType {
10287
10299
  });
10288
10300
  }
10289
10301
  if (ctx.common.async) {
10290
- return ParseStatus.mergeObjectAsync(status, pairs);
10302
+ return ParseStatus.mergeObjectAsync(status2, pairs);
10291
10303
  } else {
10292
- return ParseStatus.mergeObjectSync(status, pairs);
10304
+ return ParseStatus.mergeObjectSync(status2, pairs);
10293
10305
  }
10294
10306
  }
10295
10307
  get element() {
@@ -10320,7 +10332,7 @@ var ZodMap = class extends ZodType {
10320
10332
  return this._def.valueType;
10321
10333
  }
10322
10334
  _parse(input) {
10323
- const { status, ctx } = this._processInputParams(input);
10335
+ const { status: status2, ctx } = this._processInputParams(input);
10324
10336
  if (ctx.parsedType !== ZodParsedType.map) {
10325
10337
  addIssueToContext(ctx, {
10326
10338
  code: ZodIssueCode.invalid_type,
@@ -10347,11 +10359,11 @@ var ZodMap = class extends ZodType {
10347
10359
  return INVALID;
10348
10360
  }
10349
10361
  if (key.status === "dirty" || value.status === "dirty") {
10350
- status.dirty();
10362
+ status2.dirty();
10351
10363
  }
10352
10364
  finalMap.set(key.value, value.value);
10353
10365
  }
10354
- return { status: status.value, value: finalMap };
10366
+ return { status: status2.value, value: finalMap };
10355
10367
  });
10356
10368
  } else {
10357
10369
  const finalMap = /* @__PURE__ */ new Map();
@@ -10362,11 +10374,11 @@ var ZodMap = class extends ZodType {
10362
10374
  return INVALID;
10363
10375
  }
10364
10376
  if (key.status === "dirty" || value.status === "dirty") {
10365
- status.dirty();
10377
+ status2.dirty();
10366
10378
  }
10367
10379
  finalMap.set(key.value, value.value);
10368
10380
  }
10369
- return { status: status.value, value: finalMap };
10381
+ return { status: status2.value, value: finalMap };
10370
10382
  }
10371
10383
  }
10372
10384
  };
@@ -10380,7 +10392,7 @@ ZodMap.create = (keyType, valueType, params) => {
10380
10392
  };
10381
10393
  var ZodSet = class _ZodSet extends ZodType {
10382
10394
  _parse(input) {
10383
- const { status, ctx } = this._processInputParams(input);
10395
+ const { status: status2, ctx } = this._processInputParams(input);
10384
10396
  if (ctx.parsedType !== ZodParsedType.set) {
10385
10397
  addIssueToContext(ctx, {
10386
10398
  code: ZodIssueCode.invalid_type,
@@ -10400,7 +10412,7 @@ var ZodSet = class _ZodSet extends ZodType {
10400
10412
  exact: false,
10401
10413
  message: def.minSize.message
10402
10414
  });
10403
- status.dirty();
10415
+ status2.dirty();
10404
10416
  }
10405
10417
  }
10406
10418
  if (def.maxSize !== null) {
@@ -10413,7 +10425,7 @@ var ZodSet = class _ZodSet extends ZodType {
10413
10425
  exact: false,
10414
10426
  message: def.maxSize.message
10415
10427
  });
10416
- status.dirty();
10428
+ status2.dirty();
10417
10429
  }
10418
10430
  }
10419
10431
  const valueType = this._def.valueType;
@@ -10423,10 +10435,10 @@ var ZodSet = class _ZodSet extends ZodType {
10423
10435
  if (element.status === "aborted")
10424
10436
  return INVALID;
10425
10437
  if (element.status === "dirty")
10426
- status.dirty();
10438
+ status2.dirty();
10427
10439
  parsedSet.add(element.value);
10428
10440
  }
10429
- return { status: status.value, value: parsedSet };
10441
+ return { status: status2.value, value: parsedSet };
10430
10442
  }
10431
10443
  const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i)));
10432
10444
  if (ctx.common.async) {
@@ -10757,15 +10769,15 @@ var ZodEffects = class extends ZodType {
10757
10769
  return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema;
10758
10770
  }
10759
10771
  _parse(input) {
10760
- const { status, ctx } = this._processInputParams(input);
10772
+ const { status: status2, ctx } = this._processInputParams(input);
10761
10773
  const effect = this._def.effect || null;
10762
10774
  const checkCtx = {
10763
10775
  addIssue: (arg) => {
10764
10776
  addIssueToContext(ctx, arg);
10765
10777
  if (arg.fatal) {
10766
- status.abort();
10778
+ status2.abort();
10767
10779
  } else {
10768
- status.dirty();
10780
+ status2.dirty();
10769
10781
  }
10770
10782
  },
10771
10783
  get path() {
@@ -10777,7 +10789,7 @@ var ZodEffects = class extends ZodType {
10777
10789
  const processed = effect.transform(ctx.data, checkCtx);
10778
10790
  if (ctx.common.async) {
10779
10791
  return Promise.resolve(processed).then(async (processed2) => {
10780
- if (status.value === "aborted")
10792
+ if (status2.value === "aborted")
10781
10793
  return INVALID;
10782
10794
  const result = await this._def.schema._parseAsync({
10783
10795
  data: processed2,
@@ -10788,12 +10800,12 @@ var ZodEffects = class extends ZodType {
10788
10800
  return INVALID;
10789
10801
  if (result.status === "dirty")
10790
10802
  return DIRTY(result.value);
10791
- if (status.value === "dirty")
10803
+ if (status2.value === "dirty")
10792
10804
  return DIRTY(result.value);
10793
10805
  return result;
10794
10806
  });
10795
10807
  } else {
10796
- if (status.value === "aborted")
10808
+ if (status2.value === "aborted")
10797
10809
  return INVALID;
10798
10810
  const result = this._def.schema._parseSync({
10799
10811
  data: processed,
@@ -10804,7 +10816,7 @@ var ZodEffects = class extends ZodType {
10804
10816
  return INVALID;
10805
10817
  if (result.status === "dirty")
10806
10818
  return DIRTY(result.value);
10807
- if (status.value === "dirty")
10819
+ if (status2.value === "dirty")
10808
10820
  return DIRTY(result.value);
10809
10821
  return result;
10810
10822
  }
@@ -10829,17 +10841,17 @@ var ZodEffects = class extends ZodType {
10829
10841
  if (inner.status === "aborted")
10830
10842
  return INVALID;
10831
10843
  if (inner.status === "dirty")
10832
- status.dirty();
10844
+ status2.dirty();
10833
10845
  executeRefinement(inner.value);
10834
- return { status: status.value, value: inner.value };
10846
+ return { status: status2.value, value: inner.value };
10835
10847
  } else {
10836
10848
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => {
10837
10849
  if (inner.status === "aborted")
10838
10850
  return INVALID;
10839
10851
  if (inner.status === "dirty")
10840
- status.dirty();
10852
+ status2.dirty();
10841
10853
  return executeRefinement(inner.value).then(() => {
10842
- return { status: status.value, value: inner.value };
10854
+ return { status: status2.value, value: inner.value };
10843
10855
  });
10844
10856
  });
10845
10857
  }
@@ -10857,13 +10869,13 @@ var ZodEffects = class extends ZodType {
10857
10869
  if (result instanceof Promise) {
10858
10870
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
10859
10871
  }
10860
- return { status: status.value, value: result };
10872
+ return { status: status2.value, value: result };
10861
10873
  } else {
10862
10874
  return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => {
10863
10875
  if (!isValid(base))
10864
10876
  return INVALID;
10865
10877
  return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({
10866
- status: status.value,
10878
+ status: status2.value,
10867
10879
  value: result
10868
10880
  }));
10869
10881
  });
@@ -11042,7 +11054,7 @@ var ZodBranded = class extends ZodType {
11042
11054
  };
11043
11055
  var ZodPipeline = class _ZodPipeline extends ZodType {
11044
11056
  _parse(input) {
11045
- const { status, ctx } = this._processInputParams(input);
11057
+ const { status: status2, ctx } = this._processInputParams(input);
11046
11058
  if (ctx.common.async) {
11047
11059
  const handleAsync = async () => {
11048
11060
  const inResult = await this._def.in._parseAsync({
@@ -11053,7 +11065,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
11053
11065
  if (inResult.status === "aborted")
11054
11066
  return INVALID;
11055
11067
  if (inResult.status === "dirty") {
11056
- status.dirty();
11068
+ status2.dirty();
11057
11069
  return DIRTY(inResult.value);
11058
11070
  } else {
11059
11071
  return this._def.out._parseAsync({
@@ -11073,7 +11085,7 @@ var ZodPipeline = class _ZodPipeline extends ZodType {
11073
11085
  if (inResult.status === "aborted")
11074
11086
  return INVALID;
11075
11087
  if (inResult.status === "dirty") {
11076
- status.dirty();
11088
+ status2.dirty();
11077
11089
  return {
11078
11090
  status: "dirty",
11079
11091
  value: inResult.value
@@ -11433,10 +11445,10 @@ function assignProp(target, prop, value) {
11433
11445
  configurable: true
11434
11446
  });
11435
11447
  }
11436
- function getElementAtPath(obj, path5) {
11437
- if (!path5)
11448
+ function getElementAtPath(obj, path6) {
11449
+ if (!path6)
11438
11450
  return obj;
11439
- return path5.reduce((acc, key) => acc?.[key], obj);
11451
+ return path6.reduce((acc, key) => acc?.[key], obj);
11440
11452
  }
11441
11453
  function promiseAllObject(promisesObj) {
11442
11454
  const keys = Object.keys(promisesObj);
@@ -11756,11 +11768,11 @@ function aborted(x, startIndex = 0) {
11756
11768
  }
11757
11769
  return false;
11758
11770
  }
11759
- function prefixIssues(path5, issues) {
11771
+ function prefixIssues(path6, issues) {
11760
11772
  return issues.map((iss) => {
11761
11773
  var _a;
11762
11774
  (_a = iss).path ?? (_a.path = []);
11763
- iss.path.unshift(path5);
11775
+ iss.path.unshift(path6);
11764
11776
  return iss;
11765
11777
  });
11766
11778
  }
@@ -15171,11 +15183,11 @@ function normalizeObjectSchema(schema) {
15171
15183
  }
15172
15184
  return void 0;
15173
15185
  }
15174
- function getDotPath(path5) {
15175
- if (path5.length === 0) {
15186
+ function getDotPath(path6) {
15187
+ if (path6.length === 0) {
15176
15188
  return "object root";
15177
15189
  }
15178
- return path5.reduce((acc, seg, index) => {
15190
+ return path6.reduce((acc, seg, index) => {
15179
15191
  if (index === 0) {
15180
15192
  return String(seg);
15181
15193
  }
@@ -17474,8 +17486,8 @@ var UrlElicitationRequiredError = class extends McpError {
17474
17486
  };
17475
17487
 
17476
17488
  // ../../node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
17477
- function isTerminal(status) {
17478
- return status === "completed" || status === "failed" || status === "cancelled";
17489
+ function isTerminal(status2) {
17490
+ return status2 === "completed" || status2 === "failed" || status2 === "cancelled";
17479
17491
  }
17480
17492
 
17481
17493
  // ../../node_modules/zod-to-json-schema/dist/esm/Options.js
@@ -19694,8 +19706,8 @@ var Protocol = class {
19694
19706
  }
19695
19707
  return task;
19696
19708
  },
19697
- storeTaskResult: async (taskId, status, result) => {
19698
- await taskStore.storeTaskResult(taskId, status, result, sessionId);
19709
+ storeTaskResult: async (taskId, status2, result) => {
19710
+ await taskStore.storeTaskResult(taskId, status2, result, sessionId);
19699
19711
  const task = await taskStore.getTask(taskId, sessionId);
19700
19712
  if (task) {
19701
19713
  const notification = TaskStatusNotificationSchema.parse({
@@ -19711,15 +19723,15 @@ var Protocol = class {
19711
19723
  getTaskResult: (taskId) => {
19712
19724
  return taskStore.getTaskResult(taskId, sessionId);
19713
19725
  },
19714
- updateTaskStatus: async (taskId, status, statusMessage) => {
19726
+ updateTaskStatus: async (taskId, status2, statusMessage) => {
19715
19727
  const task = await taskStore.getTask(taskId, sessionId);
19716
19728
  if (!task) {
19717
19729
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
19718
19730
  }
19719
19731
  if (isTerminal(task.status)) {
19720
- throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
19732
+ throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status2}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
19721
19733
  }
19722
- await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
19734
+ await taskStore.updateTaskStatus(taskId, status2, statusMessage, sessionId);
19723
19735
  const updatedTask = await taskStore.getTask(taskId, sessionId);
19724
19736
  if (updatedTask) {
19725
19737
  const notification = TaskStatusNotificationSchema.parse({
@@ -21438,9 +21450,10 @@ import path from "node:path";
21438
21450
  import { fileURLToPath } from "node:url";
21439
21451
  import { homedir } from "node:os";
21440
21452
  function findRepoRoot() {
21453
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
21441
21454
  let dir = path.dirname(fileURLToPath(import.meta.url));
21442
21455
  for (let depth = 0; depth < 10; depth += 1) {
21443
- if (existsSync(path.join(dir, "Cargo.toml"))) return dir;
21456
+ if (existsSync(path.join(dir, "Cargo.toml")) && path.join(dir, "packages", "mcp") === packageRoot) return dir;
21444
21457
  const parent = path.dirname(dir);
21445
21458
  if (parent === dir) break;
21446
21459
  dir = parent;
@@ -21467,7 +21480,7 @@ function envInt(name, fallback) {
21467
21480
  }
21468
21481
  var DEFAULT_OLLAMA_ENDPOINT = process.env.FREELLAMA_OLLAMA_ENDPOINT ?? "http://127.0.0.1:11434";
21469
21482
  var DEFAULT_SERVE_ENDPOINT = process.env.FREELLAMA_SERVE_ENDPOINT ?? "http://127.0.0.1:11435";
21470
- var DEFAULT_DELEGATE_MODEL = process.env.FREELLAMA_MCP_DEFAULT_MODEL ?? "qwen3.8:27b-mlx";
21483
+ var DEFAULT_DELEGATE_MODEL = process.env.FREELLAMA_MCP_DEFAULT_MODEL?.trim() || void 0;
21471
21484
  var DEFAULT_DELEGATE_MAX_TURNS = envInt("FREELLAMA_MCP_MAX_TURNS", 8);
21472
21485
  var DEFAULT_DELEGATE_TIMEOUT_SECONDS = envInt("FREELLAMA_MCP_DELEGATE_TIMEOUT_SECONDS", 180);
21473
21486
  var DEFAULT_PULL_TIMEOUT_SECONDS = envInt("FREELLAMA_MCP_PULL_TIMEOUT_SECONDS", 1200);
@@ -21521,16 +21534,159 @@ async function assertAllowedWorkspace(workspacePath) {
21521
21534
  }
21522
21535
  return resolved;
21523
21536
  }
21537
+ var DELEGATE_ENV_ALLOWLIST = /* @__PURE__ */ new Set([
21538
+ "PATH",
21539
+ "HOME",
21540
+ "USER",
21541
+ "LOGNAME",
21542
+ "LANG",
21543
+ "LC_ALL",
21544
+ "LC_CTYPE",
21545
+ "TZ",
21546
+ "TMPDIR",
21547
+ "TEMP",
21548
+ "TMP",
21549
+ "SYSTEMROOT",
21550
+ "SYSTEMDRIVE",
21551
+ "WINDIR",
21552
+ "COMSPEC",
21553
+ "PATHEXT",
21554
+ "APPDATA",
21555
+ "LOCALAPPDATA",
21556
+ "USERPROFILE",
21557
+ "HTTP_PROXY",
21558
+ "HTTPS_PROXY",
21559
+ "NO_PROXY",
21560
+ "http_proxy",
21561
+ "https_proxy",
21562
+ "no_proxy",
21563
+ "NODE_EXTRA_CA_CERTS",
21564
+ "SSL_CERT_FILE",
21565
+ "SSL_CERT_DIR",
21566
+ "REQUESTS_CA_BUNDLE",
21567
+ "XDG_CACHE_HOME",
21568
+ "XDG_DATA_HOME",
21569
+ "npm_config_cache",
21570
+ "PYTHONPATH"
21571
+ ]);
21572
+ function delegateEnvironment(source = process.env) {
21573
+ const env = {};
21574
+ for (const [key, value] of Object.entries(source)) {
21575
+ if (value !== void 0 && (DELEGATE_ENV_ALLOWLIST.has(key) || key.startsWith("FREELLAMA_"))) env[key] = value;
21576
+ }
21577
+ return env;
21578
+ }
21524
21579
 
21525
21580
  // src/native.ts
21526
21581
  import { createRequire } from "node:module";
21527
21582
  import native from "../native/index.js";
21528
- var { doctor, machine, health, createSession, deleteSession, listModels, route, runTask, runTaskRequest, runTaskBatchRequest } = native;
21583
+ var { createScope, getScope, forkScope, deleteScope, warmModelRequest, doctor, machine, health, status, usage, createSession, deleteSession, killSession, listModels, route, runTask, runTaskRequest, runTaskBatchRequest, listTaskJobs, getTaskJob, cancelTaskJob, removeTaskJob } = native;
21529
21584
  var { version: SERVER_VERSION } = createRequire(import.meta.url)("../package.json");
21530
21585
 
21531
- // src/helpers.ts
21532
- import { existsSync as existsSync2, realpathSync, statSync } from "node:fs";
21586
+ // src/serve.ts
21587
+ import { spawn } from "node:child_process";
21588
+ import { existsSync as existsSync2 } from "node:fs";
21589
+ import { createRequire as createRequire2 } from "node:module";
21533
21590
  import path2 from "node:path";
21591
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
21592
+ var HEALTH_PATH = "/_freellama/v1/health";
21593
+ var STARTUP_TIMEOUT_MS = 15e3;
21594
+ var child = null;
21595
+ var starting = null;
21596
+ function withServe(call) {
21597
+ return async (endpoint, ...args) => {
21598
+ await ensureServe(endpoint ?? void 0);
21599
+ return call(endpoint, ...args);
21600
+ };
21601
+ }
21602
+ function autostartEnabled() {
21603
+ return process.env.FREELLAMA_MCP_AUTOSTART_SERVE !== "0";
21604
+ }
21605
+ function loopbackListen(endpoint) {
21606
+ try {
21607
+ const url = new URL(endpoint);
21608
+ if (!["127.0.0.1", "localhost", "[::1]"].includes(url.hostname)) return null;
21609
+ return `${url.hostname === "localhost" ? "127.0.0.1" : url.hostname}:${url.port || "80"}`;
21610
+ } catch {
21611
+ return null;
21612
+ }
21613
+ }
21614
+ async function answers(endpoint, timeoutMs) {
21615
+ try {
21616
+ await fetch(`${endpoint.replace(/\/$/, "")}${HEALTH_PATH}`, { signal: AbortSignal.timeout(timeoutMs) });
21617
+ return true;
21618
+ } catch {
21619
+ return false;
21620
+ }
21621
+ }
21622
+ function serveBinaryCandidates() {
21623
+ const exe = process.platform === "win32" ? "freellama.exe" : "freellama";
21624
+ const candidates = [
21625
+ path2.join(REPO_ROOT, "target", "release", exe),
21626
+ path2.join(REPO_ROOT, "target", "debug", exe)
21627
+ ];
21628
+ const requireFromHere = createRequire2(fileURLToPath2(import.meta.url));
21629
+ const suffixes = process.platform === "linux" ? ["-gnu", "-musl"] : process.platform === "win32" ? ["-msvc"] : [""];
21630
+ for (const suffix of suffixes) {
21631
+ try {
21632
+ const manifest = requireFromHere.resolve(`@octocodeai/freellama-native-${process.platform}-${process.arch}${suffix}/package.json`);
21633
+ candidates.push(path2.join(path2.dirname(manifest), exe));
21634
+ } catch {
21635
+ }
21636
+ }
21637
+ if (process.env.FREELLAMA_SERVE_BINARY) candidates.unshift(process.env.FREELLAMA_SERVE_BINARY);
21638
+ return candidates;
21639
+ }
21640
+ function stopAutostartedServe() {
21641
+ if (child && child.exitCode === null) child.kill("SIGTERM");
21642
+ child = null;
21643
+ }
21644
+ async function ensureServe(endpoint) {
21645
+ const target = endpoint ?? DEFAULT_SERVE_ENDPOINT;
21646
+ if (!autostartEnabled() || target !== DEFAULT_SERVE_ENDPOINT) return;
21647
+ const listen = loopbackListen(target);
21648
+ if (!listen) return;
21649
+ if (child && child.exitCode === null && await answers(target, 1e3)) return;
21650
+ starting ??= (async () => {
21651
+ try {
21652
+ if (await answers(target, 1e3)) return;
21653
+ const binary = serveBinaryCandidates().find((candidate) => existsSync2(candidate));
21654
+ if (!binary) return;
21655
+ const spawned = spawn(binary, ["serve", "--listen", listen, "--upstream", DEFAULT_OLLAMA_ENDPOINT], {
21656
+ stdio: ["ignore", "ignore", "pipe"]
21657
+ });
21658
+ child = spawned;
21659
+ spawned.stderr?.on("data", (chunk) => process.stderr.write(`[freellama serve] ${chunk}`));
21660
+ spawned.once("exit", () => {
21661
+ if (child === spawned) child = null;
21662
+ });
21663
+ await new Promise((resolve, reject) => {
21664
+ spawned.once("spawn", resolve);
21665
+ spawned.once("error", (error2) => {
21666
+ if (child === spawned) child = null;
21667
+ reject(new Error(`Could not start freellama serve at ${target}: ${error2.message}`));
21668
+ });
21669
+ });
21670
+ const deadline = Date.now() + STARTUP_TIMEOUT_MS;
21671
+ while (Date.now() < deadline && child === spawned) {
21672
+ if (await answers(target, 500)) return;
21673
+ await new Promise((resolve) => setTimeout(resolve, 200));
21674
+ }
21675
+ if (child === spawned) {
21676
+ stopAutostartedServe();
21677
+ throw new Error(`freellama serve did not become ready at ${target} within ${STARTUP_TIMEOUT_MS}ms.`);
21678
+ }
21679
+ throw new Error(`freellama serve exited before becoming ready at ${target} (exit ${spawned.exitCode}, signal ${spawned.signalCode}).`);
21680
+ } finally {
21681
+ starting = null;
21682
+ }
21683
+ })();
21684
+ await starting;
21685
+ }
21686
+
21687
+ // src/helpers.ts
21688
+ import { existsSync as existsSync3, realpathSync, statSync } from "node:fs";
21689
+ import path3 from "node:path";
21534
21690
  function extractExistingWorkspacePath(command, workspace) {
21535
21691
  let root;
21536
21692
  try {
@@ -21545,35 +21701,36 @@ function extractExistingWorkspacePath(command, workspace) {
21545
21701
  if (index > 0 && patternFlags.has(tokens[index - 1] ?? "")) continue;
21546
21702
  const token = raw.replace(/^["']|["']$/g, "").replace(/[|;&)]+$/g, "").replace(/:\d+(?::\d+)?$/, "");
21547
21703
  if (!token || token.startsWith("-") || /[*?$`]/.test(token)) continue;
21548
- const candidate = path2.isAbsolute(token) ? token : path2.resolve(root, token);
21549
- if (!existsSync2(candidate)) continue;
21704
+ const candidate = path3.isAbsolute(token) ? token : path3.resolve(root, token);
21705
+ if (!existsSync3(candidate)) continue;
21550
21706
  try {
21551
21707
  const resolved = realpathSync(candidate);
21552
- const relative = path2.relative(root, resolved);
21553
- const withinRoot = relative && relative !== ".." && !relative.startsWith(`..${path2.sep}`);
21708
+ const relative = path3.relative(root, resolved);
21709
+ const withinRoot = relative && relative !== ".." && !relative.startsWith(`..${path3.sep}`);
21554
21710
  if (withinRoot && statSync(resolved).isFile()) return relative;
21555
21711
  } catch {
21556
21712
  }
21557
21713
  }
21558
21714
  return null;
21559
21715
  }
21560
- async function ollamaFetch(endpoint, path5, init = {}) {
21716
+ async function ollamaFetch(endpoint, path6, init = {}) {
21561
21717
  const base = (endpoint ?? DEFAULT_OLLAMA_ENDPOINT).replace(/\/$/, "");
21562
21718
  const controller = new AbortController();
21563
21719
  const timeout = setTimeout(
21564
21720
  () => controller.abort(),
21565
21721
  init.timeoutMs ?? DEFAULT_OLLAMA_FETCH_TIMEOUT_SECONDS * 1e3
21566
21722
  );
21723
+ const signal = init.signal ? AbortSignal.any([controller.signal, init.signal]) : controller.signal;
21567
21724
  try {
21568
- const response = await fetch(`${base}${path5}`, {
21725
+ const response = await fetch(`${base}${path6}`, {
21569
21726
  method: init.method ?? "GET",
21570
21727
  headers: init.body ? { "content-type": "application/json" } : void 0,
21571
21728
  body: init.body ? JSON.stringify(init.body) : void 0,
21572
- signal: controller.signal
21729
+ signal
21573
21730
  });
21574
- const text = await response.text();
21731
+ const text = init.onLine && response.ok && response.body ? await readNdjson(response.body, init.onLine) : await response.text();
21575
21732
  if (!response.ok) {
21576
- throw new Error(`Ollama ${init.method ?? "GET"} ${path5} -> HTTP ${response.status}: ${text}`);
21733
+ throw new Error(`Ollama ${init.method ?? "GET"} ${path6} -> HTTP ${response.status}: ${text}`);
21577
21734
  }
21578
21735
  if (!text) return {};
21579
21736
  if (init.parse === "ndjson") return text;
@@ -21582,6 +21739,26 @@ async function ollamaFetch(endpoint, path5, init = {}) {
21582
21739
  clearTimeout(timeout);
21583
21740
  }
21584
21741
  }
21742
+ async function readNdjson(body, onLine) {
21743
+ const decoder = new TextDecoder();
21744
+ let text = "";
21745
+ let pending = "";
21746
+ for await (const chunk of body) {
21747
+ const piece = decoder.decode(chunk, { stream: true });
21748
+ text += piece;
21749
+ pending += piece;
21750
+ const lines = pending.split("\n");
21751
+ pending = lines.pop() ?? "";
21752
+ for (const line of lines) {
21753
+ try {
21754
+ const parsed = JSON.parse(line);
21755
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) onLine(parsed);
21756
+ } catch {
21757
+ }
21758
+ }
21759
+ }
21760
+ return text + decoder.decode();
21761
+ }
21585
21762
  function summarizeOllamaPullStream(text) {
21586
21763
  const events = [];
21587
21764
  for (const line of text.split(/\r?\n/)) {
@@ -21612,10 +21789,10 @@ function summarizeOllamaPullStream(text) {
21612
21789
  const completed = typeof withBytes?.completed === "number" ? withBytes.completed : null;
21613
21790
  const total = typeof withBytes?.total === "number" ? withBytes.total : null;
21614
21791
  const percent = completed != null && total != null && total > 0 ? Math.round(completed / total * 1e3) / 10 : null;
21615
- const status = typeof last.error === "string" && last.error ? "error" : last.status;
21792
+ const status2 = typeof last.error === "string" && last.error ? "error" : last.status;
21616
21793
  return {
21617
21794
  ...last,
21618
- ...status === void 0 ? {} : { status },
21795
+ ...status2 === void 0 ? {} : { status: status2 },
21619
21796
  progress: {
21620
21797
  events: events.length,
21621
21798
  completed,
@@ -21625,19 +21802,21 @@ function summarizeOllamaPullStream(text) {
21625
21802
  }
21626
21803
  };
21627
21804
  }
21628
- async function ollamaPull(endpoint, model, timeoutSeconds) {
21805
+ async function ollamaPull(endpoint, model, timeoutSeconds, options = {}) {
21629
21806
  const raw = await ollamaFetch(endpoint, "/api/pull", {
21630
21807
  method: "POST",
21631
- body: { name: model, stream: true },
21808
+ body: { model, stream: true },
21632
21809
  timeoutMs: (timeoutSeconds ?? DEFAULT_PULL_TIMEOUT_SECONDS) * 1e3,
21633
- parse: "ndjson"
21810
+ parse: "ndjson",
21811
+ signal: options.signal,
21812
+ onLine: options.onProgress
21634
21813
  });
21635
21814
  if (typeof raw === "string") return summarizeOllamaPullStream(raw);
21636
21815
  if (raw && typeof raw === "object" && !Array.isArray(raw)) return raw;
21637
21816
  return { status: "empty" };
21638
21817
  }
21639
- var endpointParam = external_exports.string().min(1).optional().describe("serve endpoint, default :11435");
21640
- var ollamaEndpointParam = external_exports.string().min(1).optional().describe("Ollama endpoint, default :11434");
21818
+ var endpointParam = external_exports.string().min(1).optional();
21819
+ var ollamaEndpointParam = external_exports.string().min(1).optional();
21641
21820
  var TASK_KINDS = [
21642
21821
  "completion",
21643
21822
  "coding",
@@ -21649,17 +21828,15 @@ var TASK_KINDS = [
21649
21828
  "long_context"
21650
21829
  ];
21651
21830
  var MCP_TASK_KINDS = [...TASK_KINDS, "code_review"];
21652
- var taskParam = external_exports.enum(MCP_TASK_KINDS).describe("code_review=coding.");
21831
+ var taskParam = external_exports.enum(MCP_TASK_KINDS).default("completion").describe("caller owns prompts and output format.");
21653
21832
  function canonicalTaskKind(task) {
21654
21833
  return task === "code_review" ? "coding" : task;
21655
21834
  }
21656
- var objectiveParam = external_exports.enum(["fastest", "balanced", "quality"]).optional().describe('"balanced"/"quality" need a policy; "fastest" does not');
21657
- var executionPreferenceParam = external_exports.enum(["auto", "prefer_cpu", "prefer_gpu"]).optional().describe(
21658
- 'Backend hint. "auto" uses guarded runtime feedback; prefer_* falls back when no eligible operator-assigned model exists.'
21659
- );
21660
- var minPlacementEvidenceParam = external_exports.enum(["configured", "observed"]).optional().describe('"observed" fails closed unless Ollama /api/ps confirms the selected processor');
21835
+ var objectiveParam = external_exports.enum(["fastest", "balanced", "quality"]).optional();
21836
+ var executionPreferenceParam = external_exports.enum(["auto", "prefer_cpu", "prefer_gpu"]).optional();
21837
+ var minPlacementEvidenceParam = external_exports.enum(["configured", "observed"]).optional();
21661
21838
  var CONFIDENCE_RANK = { low: 1, medium: 2 };
21662
- var minConfidenceParam = external_exports.enum(["low", "medium"]).optional().describe('Fail closed below this. "medium" needs policy AND benchmark; "low" accepts capability metadata.');
21839
+ var minConfidenceParam = external_exports.enum(["low", "medium"]).optional();
21663
21840
  function belowConfidence(decision, minConfidence) {
21664
21841
  if (!minConfidence) return null;
21665
21842
  const actual = typeof decision.confidence === "string" ? decision.confidence : "low";
@@ -21679,22 +21856,49 @@ var REQUIRED_CAPABILITIES = [
21679
21856
  "thinking",
21680
21857
  "embedding"
21681
21858
  ];
21682
- var requiredCapabilitiesParam = external_exports.array(external_exports.enum(REQUIRED_CAPABILITIES)).max(REQUIRED_CAPABILITIES.length).optional().describe('Additional hard requirements, e.g. ["vision"] or ["tools"]. Fails closed if unmet.');
21859
+ var requiredCapabilitiesParam = external_exports.array(external_exports.enum(REQUIRED_CAPABILITIES)).max(REQUIRED_CAPABILITIES.length).optional();
21860
+ var systemPromptParam = external_exports.string().optional().describe("Local model instructions; verbatim.");
21861
+ var messagesParam = external_exports.array(external_exports.object({
21862
+ role: external_exports.enum(["system", "user", "assistant", "tool"]),
21863
+ content: external_exports.string().optional()
21864
+ }).passthrough()).min(1).optional();
21865
+ var localToolsParam = external_exports.array(external_exports.object({
21866
+ type: external_exports.literal("function"),
21867
+ function: external_exports.object({
21868
+ name: external_exports.string().min(1),
21869
+ description: external_exports.string().optional(),
21870
+ parameters: external_exports.record(external_exports.unknown()).optional()
21871
+ }).passthrough()
21872
+ }).passthrough()).min(1).optional();
21873
+ function taskMessages({ systemPrompt, messages, prompt, images }) {
21874
+ if (systemPrompt === void 0) return messages ?? [];
21875
+ const conversation = messages ?? (prompt === void 0 ? [] : [{
21876
+ role: "user",
21877
+ content: prompt,
21878
+ ...images === void 0 ? {} : { images }
21879
+ }]);
21880
+ return [{ role: "system", content: systemPrompt }, ...conversation];
21881
+ }
21683
21882
  var batchTaskParam = external_exports.object({
21684
- task: taskParam,
21883
+ task: taskParam.removeDefault().optional().describe("caller owns prompts and output format."),
21685
21884
  objective: objectiveParam,
21686
21885
  model: external_exports.string().min(1).optional(),
21687
21886
  sessionId: external_exports.string().uuid().optional(),
21887
+ scopeId: external_exports.string().uuid().optional(),
21888
+ scopeRevision: external_exports.number().int().nonnegative().optional(),
21688
21889
  contextTokens: external_exports.number().int().positive().optional(),
21689
21890
  executionPreference: executionPreferenceParam,
21690
21891
  minPlacementEvidence: minPlacementEvidenceParam,
21691
21892
  requiredCapabilities: requiredCapabilitiesParam,
21692
21893
  priority: external_exports.enum(["interactive", "normal", "background"]).optional(),
21894
+ maxWaitSeconds: external_exports.number().int().positive().optional(),
21895
+ timeoutSeconds: external_exports.number().int().positive().optional(),
21693
21896
  prompt: external_exports.string().min(1).optional(),
21694
- messages: external_exports.array(external_exports.object({ role: external_exports.enum(["system", "user", "assistant", "tool"]), content: external_exports.string() }).passthrough()).min(1).optional(),
21897
+ systemPrompt: systemPromptParam,
21898
+ messages: messagesParam,
21695
21899
  images: external_exports.array(external_exports.string().min(1)).min(1).optional(),
21696
21900
  input: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]).optional(),
21697
- tools: external_exports.array(external_exports.record(external_exports.unknown())).min(1).optional(),
21901
+ tools: localToolsParam,
21698
21902
  keepAlive: external_exports.string().min(1).optional(),
21699
21903
  format: external_exports.union([external_exports.literal("json"), external_exports.record(external_exports.unknown())]).optional(),
21700
21904
  think: external_exports.union([external_exports.boolean(), external_exports.enum(["low", "medium", "high"])]).optional(),
@@ -21716,7 +21920,9 @@ var doctorResultSchema = external_exports.object({
21716
21920
  }).passthrough();
21717
21921
  var sessionResultSchema = external_exports.object({
21718
21922
  session_id: external_exports.string().optional(),
21719
- deleted: external_exports.boolean().optional()
21923
+ deleted: external_exports.boolean().optional(),
21924
+ killed: external_exports.boolean().optional(),
21925
+ runner_stop_confirmed: external_exports.boolean().optional()
21720
21926
  }).passthrough();
21721
21927
  var manageResultSchema = external_exports.object({
21722
21928
  status: external_exports.string().optional(),
@@ -21728,7 +21934,9 @@ var deleteResultSchema = external_exports.object({
21728
21934
  var modelsResultSchema = external_exports.object({
21729
21935
  models: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
21730
21936
  tags: external_exports.array(external_exports.record(external_exports.unknown())).optional(),
21731
- page: external_exports.unknown().optional()
21937
+ page: external_exports.unknown().optional(),
21938
+ knowledge: external_exports.unknown().optional(),
21939
+ library: external_exports.unknown().optional()
21732
21940
  }).passthrough();
21733
21941
  var taskResultSchema = external_exports.object({
21734
21942
  selected_model: external_exports.string().optional(),
@@ -21763,22 +21971,22 @@ function configuredExternalCost(env = process.env) {
21763
21971
  }
21764
21972
  return { model, input, output };
21765
21973
  }
21766
- function costTelemetry(usage, externalCost) {
21974
+ function costTelemetry(usage2, externalCost) {
21767
21975
  const local = {
21768
- inputTokens: usage.inputTokens,
21769
- outputTokens: usage.outputTokens,
21770
- totalTokens: usage.inputTokens === null || usage.outputTokens === null ? null : usage.inputTokens + usage.outputTokens,
21771
- totalDurationNs: usage.totalDurationNs ?? null
21976
+ inputTokens: usage2.inputTokens,
21977
+ outputTokens: usage2.outputTokens,
21978
+ totalTokens: usage2.inputTokens === null || usage2.outputTokens === null ? null : usage2.inputTokens + usage2.outputTokens,
21979
+ totalDurationNs: usage2.totalDurationNs ?? null
21772
21980
  };
21773
- if (!externalCost || usage.inputTokens === null || usage.outputTokens === null) {
21981
+ if (!externalCost || usage2.inputTokens === null || usage2.outputTokens === null) {
21774
21982
  return {
21775
21983
  local,
21776
21984
  externalEquivalent: null,
21777
21985
  note: externalCost ? "External equivalent unavailable because Ollama did not report both input and output token counts." : "No external rate card supplied; local usage is observed, but avoided external cost is not estimated."
21778
21986
  };
21779
21987
  }
21780
- const inputUsd = usage.inputTokens * externalCost.input / 1e6;
21781
- const outputUsd = usage.outputTokens * externalCost.output / 1e6;
21988
+ const inputUsd = usage2.inputTokens * externalCost.input / 1e6;
21989
+ const outputUsd = usage2.outputTokens * externalCost.output / 1e6;
21782
21990
  return {
21783
21991
  local,
21784
21992
  externalEquivalent: {
@@ -21816,9 +22024,52 @@ function resultSummary(value) {
21816
22024
  const keys = Object.keys(value);
21817
22025
  return `Structured result available (${keys.slice(0, 8).join(", ") || "empty object"}).`;
21818
22026
  }
22027
+ var ANSWER_TEXT_MAX_CHARS = 8 * 1024;
22028
+ function record2(value) {
22029
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
22030
+ }
22031
+ function taskAnswerText(payload) {
22032
+ const response = record2(payload.response);
22033
+ if (!response) return void 0;
22034
+ const model = record2(payload.route)?.selected_model ?? payload.selected_model ?? response.model;
22035
+ const header = typeof model === "string" && model ? `[${model}] ` : "";
22036
+ const message = record2(response.message);
22037
+ if (message) {
22038
+ const parts = [];
22039
+ if (typeof message.content === "string" && message.content.trim()) parts.push(message.content.trim());
22040
+ if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
22041
+ parts.push(`tool_calls: ${JSON.stringify(message.tool_calls)}`);
22042
+ }
22043
+ if (parts.length === 0) parts.push("(empty response)");
22044
+ return clipText(header + parts.join("\n"), ANSWER_TEXT_MAX_CHARS);
22045
+ }
22046
+ if (typeof response.response === "string") return clipText(header + response.response.trim(), ANSWER_TEXT_MAX_CHARS);
22047
+ const omitted = record2(response.embeddings_omitted);
22048
+ if (Array.isArray(response.embeddings) || omitted) {
22049
+ const count = Array.isArray(response.embeddings) ? response.embeddings.length : omitted?.count;
22050
+ const where = omitted ? "vectors withheld; pass returnEmbeddings:true" : "vectors are in structuredContent";
22051
+ return `${header}embedding complete (${String(count ?? "?")} vectors); ${where}.`;
22052
+ }
22053
+ return void 0;
22054
+ }
22055
+ function batchAnswerText(payload) {
22056
+ if (!Array.isArray(payload.results)) return void 0;
22057
+ const perItem = Math.max(512, Math.floor(ANSWER_TEXT_MAX_CHARS / Math.max(payload.results.length, 1)));
22058
+ const lines = payload.results.map((row) => {
22059
+ const item = record2(row) ?? {};
22060
+ const id = String(item.id ?? "?");
22061
+ if (item.ok !== true) return `## ${id} (failed)
22062
+ ${clipText(JSON.stringify(item.error ?? item), perItem)}`;
22063
+ const answer = taskAnswerText(record2(item.response) ?? {}) ?? "(no answer)";
22064
+ return `## ${id}
22065
+ ${clipText(answer, perItem)}`;
22066
+ });
22067
+ return lines.join("\n\n");
22068
+ }
21819
22069
  function structuredResult(value, options = {}) {
22070
+ const text = options.text ?? (options.legacyJson ? serialize(value) : resultSummary(value));
21820
22071
  return {
21821
- content: [{ type: "text", text: options.legacyJson ? serialize(value) : resultSummary(value) }],
22072
+ content: [{ type: "text", text }],
21822
22073
  structuredContent: value
21823
22074
  };
21824
22075
  }
@@ -21839,13 +22090,31 @@ ${clipText(raw, 2e3)}`
21839
22090
  }
21840
22091
  return structuredResult(value);
21841
22092
  }
22093
+ function isStructuredSuccess(result) {
22094
+ return !("isError" in result && result.isError) && "structuredContent" in result && result.structuredContent !== void 0;
22095
+ }
21842
22096
  function errorResult(error2) {
21843
22097
  const message = error2 instanceof Error ? error2.message : String(error2);
22098
+ let receipt;
22099
+ try {
22100
+ const parsed = JSON.parse(message);
22101
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.error === "string") {
22102
+ receipt = parsed;
22103
+ }
22104
+ } catch {
22105
+ }
21844
22106
  const managedServeUnavailable = /(?:connection refused|connect error|failed to connect|error sending request)/i.test(message) && /(?:_freellama|127\.0\.0\.1:11435|localhost:11435)/i.test(message);
22107
+ const ollamaUnavailable = !managedServeUnavailable && /(?:connection refused|connect error|failed to connect|error sending request)/i.test(message) && /\/api\/(?:tags|ps|chat|generate|embed|show)/.test(message);
21845
22108
  const actionableMessage = managedServeUnavailable ? `${message}
21846
22109
 
21847
- FreeLlama managed serve is unreachable. Start it with \`freellama serve\`, or set \`FREELLAMA_SERVE_ENDPOINT\` to a running managed endpoint; then retry. Run \`doctor\` to inspect the configured endpoint.` : message;
21848
- return { content: [{ type: "text", text: actionableMessage }], isError: true };
22110
+ FreeLlama managed serve is unreachable. Start it with \`freellama serve\`, or set \`FREELLAMA_SERVE_ENDPOINT\` to a running managed endpoint; then retry. Run \`doctor\` to inspect the configured endpoint.` : ollamaUnavailable ? `${message}
22111
+
22112
+ Ollama is not running at that address. Ask the user to start Ollama (the app, or \`ollama serve\`); retrying before that cannot succeed.` : message;
22113
+ return {
22114
+ content: [{ type: "text", text: actionableMessage }],
22115
+ isError: true,
22116
+ ...receipt ? { structuredContent: receipt } : {}
22117
+ };
21849
22118
  }
21850
22119
  var adapterCallSchema = external_exports.object({
21851
22120
  raw_name: external_exports.string().optional(),
@@ -21900,11 +22169,64 @@ function summarizeEmbeddings(payload) {
21900
22169
  }
21901
22170
 
21902
22171
  // src/model-search.ts
22172
+ function decodeHtml(text) {
22173
+ const entities = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: " ", middot: "\xB7", bull: "\u2022" };
22174
+ return text.replace(/&(#x[\da-f]+|#\d+|[a-z]+);/gi, (raw, entity) => {
22175
+ if (!entity.startsWith("#")) return entities[entity.toLowerCase()] ?? raw;
22176
+ const code = entity[1].toLowerCase() === "x" ? Number.parseInt(entity.slice(2), 16) : Number(entity.slice(1));
22177
+ return code > 0 && code <= 1114111 && !(code >= 55296 && code <= 57343) ? String.fromCodePoint(code) : raw;
22178
+ });
22179
+ }
22180
+ function attribute(tag, name) {
22181
+ return tag.match(new RegExp(`(?:^|\\s)${name}\\s*=\\s*(["'])([\\s\\S]*?)\\1`, "i"))?.[2];
22182
+ }
22183
+ function htmlText(html) {
22184
+ return decodeHtml(html.replace(/<(script|style|svg)\b[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<\/(?:p|li|div|h[1-6]|pre|tr)>|<br\s*\/?\s*>/gi, "\n").replace(/<[^>]*>/g, " ")).split("\n").map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
22185
+ }
22186
+ function divContent(html, id) {
22187
+ const tags = /<\/?div\b[^>]*>/gi;
22188
+ let start;
22189
+ let depth = 0;
22190
+ for (const match of html.matchAll(tags)) {
22191
+ if (start === void 0) {
22192
+ if (attribute(match[0], "id") !== id) continue;
22193
+ start = match.index + match[0].length;
22194
+ depth = 1;
22195
+ } else {
22196
+ depth += /^<\//.test(match[0]) ? -1 : 1;
22197
+ if (depth === 0) return html.slice(start, match.index);
22198
+ }
22199
+ }
22200
+ return void 0;
22201
+ }
22202
+ function parseModelCard(html) {
22203
+ const meta = [...html.matchAll(/<meta\b[^>]*>/gi)].find((m) => attribute(m[0], "name") === "description");
22204
+ const description = decodeHtml(meta ? attribute(meta[0], "content") ?? "" : "").trim();
22205
+ const editor = [...html.matchAll(/<textarea\b([^>]*)>([\s\S]*?)<\/textarea>/gi)].find((m) => attribute(m[1], "id") === "editor");
22206
+ const rendered = divContent(html, "display");
22207
+ const readme = editor ? decodeHtml(editor[2]).trim() : rendered === void 0 ? "" : htmlText(rendered);
22208
+ if (!description && !editor && rendered === void 0) throw new Error("Ollama library markup was not recognized.");
22209
+ const header = html.split(/<div[^>]*\bid=["']readme["']/i)[0];
22210
+ const capabilities = [...new Set([...header.matchAll(/<span\b[^>]*class=["'][^"']*text-(?:indigo-600|cyan-500)[^"']*["'][^>]*>\s*([a-z]+)\s*<\/span>/g)].map((m) => m[1]))];
22211
+ return { description, readme, readmeFormat: editor ? "markdown" : "text", capabilities };
22212
+ }
22213
+ function libraryTrialCandidate(tag, embedding) {
22214
+ return {
22215
+ tag,
22216
+ evidence: "download_size_preflight_only",
22217
+ taskQuality: "unmeasured",
22218
+ why: embedding ? "Smallest listed download inside the conservative host-memory budget. This is a trial candidate; smaller size does not establish better embedding quality." : "Largest listed download inside the conservative host-memory budget. This is a trial candidate; download size establishes neither accelerator fit nor task quality.",
22219
+ configure: 'Set contextTokens and output limits for the workload, then inspect preview and execution receipts. Ollama defaults depend on its configuration and hardware. Use keepAlive:"0" for one-off work; keep related repeated work warm when resources permit.',
22220
+ caution: "Measure this exact tag on representative tasks before choosing it. Include resident runners, context/KV caches and OS headroom in memory fit. Discovery does not authorize a pull."
22221
+ };
22222
+ }
21903
22223
  function parseModelSearch(html) {
21904
22224
  const results = [];
21905
22225
  for (const block of html.split(/<li\s/).slice(1)) {
21906
- const name = block.match(/href="\/library\/([^"]+)"/)?.[1];
22226
+ const link = block.match(/href="\/(?:library\/([\w.-]+)|([\w.-]+\/[\w.-]+))"/);
22227
+ const name = link?.[1] ?? link?.[2];
21907
22228
  if (!name) continue;
22229
+ if (!/<p\b[^>]*class="max-w-lg/.test(block)) continue;
21908
22230
  const description = block.match(/<p class="max-w-lg[^"]*">([\s\S]*?)<\/p>/)?.[1]?.replace(/<[^>]+>/g, "").replace(/&#39;/g, "'").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/\s+/g, " ").trim() ?? "";
21909
22231
  const capabilities = [...block.matchAll(/text-(?:indigo-600|cyan-500)[^>]*>([a-z]+)<\/span>/g)].map(
21910
22232
  (m) => m[1]
@@ -21918,7 +22240,8 @@ function parseModelSearch(html) {
21918
22240
  capabilities,
21919
22241
  pulls: stat("Pulls"),
21920
22242
  tags: stat("Tag"),
21921
- cloudOnly: capabilities.includes("cloud")
22243
+ cloudAvailable: capabilities.includes("cloud"),
22244
+ cloudOnly: null
21922
22245
  });
21923
22246
  }
21924
22247
  return results;
@@ -21926,38 +22249,330 @@ function parseModelSearch(html) {
21926
22249
  function parseModelTags(html, family) {
21927
22250
  const tags = [];
21928
22251
  const seen = /* @__PURE__ */ new Set();
21929
- const row = /<a href="\/library\/([^"]+)" class="sm:hidden[\s\S]*?<p class="flex text-neutral-500">([^<]*)<\/p>/g;
22252
+ const row = /<a\b([^>]*)>([\s\S]*?)<\/a>/gi;
21930
22253
  for (const m of html.matchAll(row)) {
21931
- const tag = m[1];
22254
+ let tag;
22255
+ try {
22256
+ tag = decodeURIComponent(attribute(m[1], "href") ?? "").replace(/^\/(?:library\/)?/, "");
22257
+ } catch {
22258
+ continue;
22259
+ }
22260
+ if (!tag.startsWith(`${family}:`)) continue;
22261
+ const text = htmlText(m[2]).replace(/\n/g, " ");
22262
+ if (!/context window|[\d.]\s*[MG]B\b|\bcloud\b/i.test(text)) continue;
21932
22263
  if (seen.has(tag)) continue;
21933
22264
  seen.add(tag);
21934
- const meta = m[2].replace(/&middot;/g, "\xB7").split("\xB7").map((x) => x.trim());
22265
+ const meta = text.split(/[·•]/).map((x) => x.trim());
21935
22266
  const sizeText = meta.find((x) => /^[\d.]+\s?[MG]B$/i.test(x)) ?? null;
21936
22267
  const bytes = sizeText ? Number.parseFloat(sizeText) * (/GB/i.test(sizeText) ? 1e9 : 1e6) : null;
21937
22268
  tags.push({
21938
22269
  tag,
22270
+ digest: text.replace(tag, "").match(/\b[\da-f]{12,64}\b/i)?.[0]?.toLowerCase() ?? null,
22271
+ cloud: /-cloud$/i.test(tag) || meta.some((part) => /^cloud$/i.test(part)),
21939
22272
  size: sizeText,
21940
22273
  sizeBytes: bytes,
21941
22274
  context: meta.find((x) => /context window/i.test(x))?.replace(/\s*context window\s*/i, "") ?? null,
21942
- modalities: meta.find((x) => /^(Text|Image|Audio)/i.test(x)) ?? null,
22275
+ modalities: meta.find((x) => /^(Text|Image|Audio)/i.test(x))?.replace(/\s+input.*$/i, "") ?? null,
21943
22276
  updated: meta.at(-1) ?? null
21944
22277
  });
21945
22278
  }
21946
22279
  return { family, tags };
21947
22280
  }
21948
22281
 
22282
+ // src/model-knowledge.ts
22283
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
22284
+ var FAILURE_TTL_MS = 60 * 1e3;
22285
+ var MAX_PAGE_BYTES = 2 * 1024 * 1024;
22286
+ var MAX_README_CHARS = 64 * 1024;
22287
+ function libraryReference(model) {
22288
+ const match = model.match(/^(?:library\/)?([a-zA-Z0-9][\w.-]*(?:\/[a-zA-Z0-9][\w.-]*)?)(?::([\w.-]+))?$/);
22289
+ if (!match || match[1].split("/").some((part) => part === "." || part === "..")) return null;
22290
+ const family = match[1];
22291
+ const pathname = family.includes("/") ? family : `library/${family}`;
22292
+ return { family, tag: `${family}:${match[2] ?? "latest"}`, url: `https://ollama.com/${pathname}` };
22293
+ }
22294
+ var LibraryFetchError = class extends Error {
22295
+ constructor(status2, message) {
22296
+ super(message);
22297
+ this.status = status2;
22298
+ }
22299
+ status;
22300
+ };
22301
+ var OllamaLibraryClient = class {
22302
+ constructor(options = {}) {
22303
+ this.options = options;
22304
+ }
22305
+ options;
22306
+ cache = /* @__PURE__ */ new Map();
22307
+ pending = /* @__PURE__ */ new Map();
22308
+ async search(params) {
22309
+ const url = `https://ollama.com/search?${params.toString()}`;
22310
+ const models = parseModelSearch(await this.page(url));
22311
+ return { url, models, fetchedAt: new Date((this.options.now ?? Date.now)()).toISOString() };
22312
+ }
22313
+ async lookup(model) {
22314
+ const ref = libraryReference(model);
22315
+ if (!ref) return {
22316
+ status: "unmatched",
22317
+ sourceUrl: null,
22318
+ tagsSourceUrl: null,
22319
+ fetchedAt: null,
22320
+ expiresAt: null,
22321
+ cached: false,
22322
+ message: "No exact Ollama public name could be resolved. Base-model provenance is unknown."
22323
+ };
22324
+ const now = this.options.now ?? Date.now;
22325
+ const cached2 = this.cache.get(ref.family);
22326
+ if (cached2 && Date.parse(cached2.expiresAt) > now()) {
22327
+ this.cache.delete(ref.family);
22328
+ this.cache.set(ref.family, cached2);
22329
+ return { ...cached2, cached: true };
22330
+ }
22331
+ const pending = this.pending.get(ref.family);
22332
+ if (pending) return { ...await pending, cached: true };
22333
+ const work = this.load(ref).then((record3) => {
22334
+ this.cache.delete(ref.family);
22335
+ this.cache.set(ref.family, record3);
22336
+ while (this.cache.size > (this.options.maxEntries ?? 128)) this.cache.delete(this.cache.keys().next().value);
22337
+ return record3;
22338
+ });
22339
+ this.pending.set(ref.family, work);
22340
+ try {
22341
+ return await work;
22342
+ } finally {
22343
+ this.pending.delete(ref.family);
22344
+ }
22345
+ }
22346
+ async page(url) {
22347
+ const response = await (this.options.fetch ?? fetch)(url, {
22348
+ method: "GET",
22349
+ redirect: "error",
22350
+ headers: { accept: "text/html" },
22351
+ signal: AbortSignal.timeout(this.options.timeoutMs ?? Math.min(DEFAULT_OLLAMA_FETCH_TIMEOUT_SECONDS * 1e3, 1e4))
22352
+ });
22353
+ if (!response.ok) {
22354
+ await response.body?.cancel();
22355
+ throw new LibraryFetchError(response.status === 404 ? "not_found" : "unavailable", `Ollama library returned HTTP ${response.status}.`);
22356
+ }
22357
+ if (!(response.headers.get("content-type") ?? "").includes("text/html")) {
22358
+ await response.body?.cancel();
22359
+ throw new LibraryFetchError("parse_error", "Ollama library did not return HTML.");
22360
+ }
22361
+ if (Number(response.headers.get("content-length")) > MAX_PAGE_BYTES) {
22362
+ await response.body?.cancel();
22363
+ throw new LibraryFetchError("unavailable", "Ollama library page exceeds the 2 MiB limit.");
22364
+ }
22365
+ const reader = response.body?.getReader();
22366
+ if (!reader) throw new LibraryFetchError("parse_error", "Ollama library returned an empty body.");
22367
+ const chunks = [];
22368
+ let bytes = 0;
22369
+ try {
22370
+ while (true) {
22371
+ const { done, value } = await reader.read();
22372
+ if (done) break;
22373
+ bytes += value.byteLength;
22374
+ if (bytes > MAX_PAGE_BYTES) throw new LibraryFetchError("unavailable", "Ollama library page exceeds the 2 MiB limit.");
22375
+ chunks.push(value);
22376
+ }
22377
+ return Buffer.concat(chunks).toString("utf8");
22378
+ } finally {
22379
+ await reader.cancel();
22380
+ }
22381
+ }
22382
+ async load(ref) {
22383
+ let result;
22384
+ try {
22385
+ const pages = await Promise.allSettled([this.page(ref.url), this.page(`${ref.url}/tags`)]);
22386
+ if (pages[0].status === "rejected") throw pages[0].reason;
22387
+ if (pages[1].status === "rejected") throw pages[1].reason;
22388
+ let card;
22389
+ try {
22390
+ card = parseModelCard(pages[0].value);
22391
+ } catch {
22392
+ throw new LibraryFetchError("parse_error", "Ollama model-card markup was not recognized.");
22393
+ }
22394
+ const { tags } = parseModelTags(pages[1].value, ref.family);
22395
+ if (tags.length === 0) throw new LibraryFetchError("parse_error", "No tag rows were recognized; check the source page before concluding a model is absent.");
22396
+ result = {
22397
+ status: "available",
22398
+ tags,
22399
+ readmeTruncated: card.readme.length > MAX_README_CHARS,
22400
+ card: { ...card, readme: card.readme.slice(0, MAX_README_CHARS) }
22401
+ };
22402
+ } catch (error2) {
22403
+ result = {
22404
+ status: error2 instanceof LibraryFetchError ? error2.status : "unavailable",
22405
+ message: error2 instanceof LibraryFetchError ? error2.message : "Ollama library lookup failed or timed out. Local metadata remains available."
22406
+ };
22407
+ }
22408
+ const now = (this.options.now ?? Date.now)();
22409
+ return {
22410
+ ...result,
22411
+ sourceUrl: ref.url,
22412
+ tagsSourceUrl: `${ref.url}/tags`,
22413
+ fetchedAt: new Date(now).toISOString(),
22414
+ expiresAt: new Date(now + (result.status === "available" ? CACHE_TTL_MS : FAILURE_TTL_MS)).toISOString(),
22415
+ cached: false
22416
+ };
22417
+ }
22418
+ };
22419
+ var supportedUses = {
22420
+ completion: { use: "text_generation", reason: "Ollama advertises completion support; task accuracy still needs evaluation." },
22421
+ tools: { use: "tool_calling", reason: "Ollama advertises tool-call support; this does not establish reliable agent execution." },
22422
+ vision: { use: "image_understanding", reason: "Ollama advertises image-input support; OCR and visual accuracy are unmeasured here." },
22423
+ audio: { use: "audio_input", reason: "Ollama advertises audio support; inspect the model instructions for the supported audio workflow." },
22424
+ embedding: { use: "embeddings", reason: "Ollama advertises embedding support for retrieval and similarity; retrieval quality needs evaluation." },
22425
+ thinking: { use: "thinking", reason: "Ollama advertises a thinking mode; it does not establish reasoning accuracy." },
22426
+ insert: { use: "fill_in_middle", reason: "Ollama advertises suffix-based insertion support." },
22427
+ image: { use: "image_generation", reason: "Ollama advertises image-generation support." }
22428
+ };
22429
+ var advertisedUses = [
22430
+ { task: "coding", pattern: /\b(coding|code generation|software engineering)\b/i, requires: "completion" },
22431
+ { task: "tool_calling", pattern: /\b(tool (?:use|calling)|function calling)\b/i, requires: "tools" },
22432
+ { task: "image_understanding", pattern: /\b(vision|image understanding|processing text and images|multimodal|OCR|optical character recognition)\b/i, requires: "vision" },
22433
+ { task: "reasoning", pattern: /\b(reasoning|mathematical|math tasks)\b/i, requires: "completion" },
22434
+ { task: "embeddings", pattern: /\b(embedding|embeddings|text encoder)\b/i, requires: "embedding" },
22435
+ { task: "summarization", pattern: /\b(summarization|summarisation|summarize|summarise)\b/i, requires: "completion" },
22436
+ { task: "question_answering", pattern: /\b(question answering|question-answering)\b/i, requires: "completion" },
22437
+ { task: "translation", pattern: /\b(translation|translate)\b/i, requires: "completion" },
22438
+ { task: "long_context", pattern: /\b(long[- ]context|repository[- ]scale understanding)\b/i, requires: "completion" }
22439
+ ];
22440
+ function prose(card) {
22441
+ return `${card.description}
22442
+ ${card.readme}`.replace(/```[\s\S]*?```|~~~[\s\S]*?~~~/g, "").replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "").replace(/!\[[^\]]*\]\([^)]*\)/g, "").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").split(/\n|(?<=[.!?])\s+/).map((line) => line.replace(/^[\s#>*-]+/, "").trim()).filter(Boolean);
22443
+ }
22444
+ function publicModelGuidance(record3, capabilities, includeReadme = false) {
22445
+ const card = record3.card;
22446
+ if (!card) return { advertisedUseCases: [], usageNotes: [] };
22447
+ const sentences = prose(card);
22448
+ const advertisedUseCases = advertisedUses.flatMap(({ task, pattern, requires }) => {
22449
+ if (capabilities && !capabilities.includes(requires)) return [];
22450
+ const sentence = sentences.find((sentence2) => pattern.test(sentence2) && !/\b(not|never|cannot|unsupported|without)\b|doesn['’]t/i.test(sentence2));
22451
+ const start = sentence ? Math.max(0, sentence.search(pattern) - 120) : 0;
22452
+ return sentence ? [{
22453
+ task,
22454
+ basis: "advertised",
22455
+ scope: "family",
22456
+ quality: "unmeasured",
22457
+ evidence: { excerpt: sentence.slice(start, start + 320), sourceUrl: record3.sourceUrl, fetchedAt: record3.fetchedAt }
22458
+ }] : [];
22459
+ });
22460
+ const usageNotes = sentences.filter((sentence) => /\b(requires?|only|cannot|limitations?|must|memory|languages?|prefix)\b|not support|context (window|length)/i.test(sentence)).slice(0, 8).map((text) => ({ text: text.slice(0, 400), scope: "family", sourceUrl: record3.sourceUrl }));
22461
+ return {
22462
+ description: card.description,
22463
+ advertisedFamilyCapabilities: card.capabilities,
22464
+ advertisedUseCases,
22465
+ usageNotes,
22466
+ readmeAvailable: card.readme.length > 0,
22467
+ ...includeReadme ? { readme: { text: card.readme, format: card.readmeFormat, truncated: record3.readmeTruncated ?? false } } : {}
22468
+ };
22469
+ }
22470
+ function provenance(record3) {
22471
+ const { card: _card, tags: _tags, readmeTruncated: _truncated, ...source } = record3;
22472
+ return source;
22473
+ }
22474
+ function modelKnowledge(model, data, record3, includeReadme = false, supportedFeaturesSource = "ollama_api") {
22475
+ const capabilities = Array.isArray(data.capabilities) ? data.capabilities.filter((item) => typeof item === "string") : [];
22476
+ const supportedUseCases = capabilities.flatMap((capability) => Object.hasOwn(supportedUses, capability) ? [{ capability, ...supportedUses[capability], basis: "supported_feature" }] : []);
22477
+ const local = {
22478
+ supportedFeatures: capabilities,
22479
+ supportedFeaturesSource,
22480
+ supportedUseCases,
22481
+ quality: "metadata_only",
22482
+ note: "Supported features and advertised uses do not establish task quality. Keep exact-model benchmark and policy evidence separate."
22483
+ };
22484
+ if (data.remote_host || data.remote_model || /-cloud$/i.test(model)) {
22485
+ return { ...local, localModel: false, library: { status: "remote_model", message: "This Ollama entry points to a remote model." } };
22486
+ }
22487
+ if (!record3) return { ...local, localModel: true, library: { status: "not_requested" } };
22488
+ if (record3.status !== "available") return { ...local, localModel: true, library: provenance(record3) };
22489
+ const ref = libraryReference(model);
22490
+ const tag = record3.tags?.find((tag2) => tag2.tag === ref.tag);
22491
+ const digest = typeof data.digest === "string" ? data.digest.replace(/^sha256:/, "").toLowerCase() : null;
22492
+ const validDigest = digest && /^[\da-f]{64}$/.test(digest);
22493
+ const matched = validDigest && tag?.digest && digest.startsWith(tag.digest);
22494
+ const status2 = !tag ? "tag_not_found" : !validDigest || !tag.digest ? "unverified" : matched ? "matched" : "digest_mismatch";
22495
+ if (!matched || tag?.cloud) return { ...local, localModel: true, library: {
22496
+ ...provenance(record3),
22497
+ status: tag?.cloud ? "remote_tag" : status2,
22498
+ message: "Public guidance was withheld because an exact local tag and digest match could not be verified.",
22499
+ tag: tag ?? null
22500
+ } };
22501
+ return { ...local, localModel: true, library: {
22502
+ ...provenance(record3),
22503
+ status: status2,
22504
+ tag,
22505
+ ...publicModelGuidance(record3, capabilities, includeReadme)
22506
+ } };
22507
+ }
22508
+ async function enrichInstalledModels(models, client, includeLibrary, loadTags) {
22509
+ const results = new Array(models.length);
22510
+ const inventories = /* @__PURE__ */ new Map();
22511
+ let next = 0;
22512
+ await Promise.all(Array.from({ length: Math.min(4, models.length) }, async () => {
22513
+ while (next < models.length) {
22514
+ const index = next++;
22515
+ const model = models[index];
22516
+ const name = String(model.name ?? model.model ?? "");
22517
+ let local = model;
22518
+ let featureSource = "freellama_catalog";
22519
+ let localError;
22520
+ if (includeLibrary && loadTags) {
22521
+ const upstream = model.execution?.upstream;
22522
+ let inventory = inventories.get(upstream);
22523
+ if (!inventory) {
22524
+ inventory = loadTags(upstream);
22525
+ inventories.set(upstream, inventory);
22526
+ }
22527
+ try {
22528
+ const entry = (await inventory).find((entry2) => String(entry2.name ?? entry2.model) === name);
22529
+ if (!entry || entry.digest !== model.digest) localError = "Local inventory changed; inspect models again before matching public guidance.";
22530
+ else {
22531
+ local = { ...model, ...entry };
22532
+ if (Array.isArray(entry.capabilities)) featureSource = "ollama_api";
22533
+ }
22534
+ } catch {
22535
+ localError = "The selected Ollama backend's inventory could not be verified; public guidance was withheld.";
22536
+ }
22537
+ }
22538
+ const record3 = includeLibrary && !localError && !local.remote_host && !local.remote_model && !/-cloud$/i.test(name) ? await client.lookup(name) : void 0;
22539
+ const knowledge = modelKnowledge(name, local, record3, false, featureSource);
22540
+ results[index] = { ...model, knowledge: localError ? { ...knowledge, library: { status: "unverified", message: localError } } : knowledge };
22541
+ }
22542
+ }));
22543
+ return results;
22544
+ }
22545
+
21949
22546
  // src/delegate.ts
21950
- import { existsSync as existsSync3, readFileSync } from "node:fs";
21951
- import path3 from "node:path";
22547
+ import { existsSync as existsSync4, readFileSync } from "node:fs";
22548
+ import path4 from "node:path";
22549
+ function researchErrorResult(error2, diagnostic) {
22550
+ const result = errorResult(error2);
22551
+ const summary = diagnostic ?? result.content[0].text;
22552
+ return {
22553
+ ...result,
22554
+ content: [{ type: "text", text: summary }],
22555
+ structuredContent: {
22556
+ ...result.structuredContent ?? { error: summary },
22557
+ answer: "",
22558
+ summary,
22559
+ verification: {
22560
+ recommendation: "escalate",
22561
+ grounded: false,
22562
+ why: "The research request failed; no usable answer was produced."
22563
+ }
22564
+ }
22565
+ };
22566
+ }
21952
22567
  var MODEL_EVIDENCE = (() => {
21953
22568
  const configured = process.env.FREELLAMA_MCP_MODEL_EVIDENCE;
21954
22569
  const candidates = [
21955
22570
  configured,
21956
- path3.join(REPO_ROOT, "benchmark/evidence/model-evidence.json")
22571
+ path4.join(REPO_ROOT, "benchmark/evidence/model-evidence.json")
21957
22572
  ].filter((x) => Boolean(x));
21958
22573
  for (const file of candidates) {
21959
22574
  try {
21960
- if (!existsSync3(file)) continue;
22575
+ if (!existsSync4(file)) continue;
21961
22576
  const parsed = JSON.parse(readFileSync(file, "utf8"));
21962
22577
  return parsed.models ?? {};
21963
22578
  } catch {
@@ -22025,11 +22640,135 @@ function assessDelegatedAnswer(question, evidenceCount, model) {
22025
22640
  };
22026
22641
  }
22027
22642
 
22643
+ // src/context-tools.ts
22644
+ var routeDefaults = external_exports.object({
22645
+ task: taskParam.removeDefault().optional(),
22646
+ objective: objectiveParam,
22647
+ model: external_exports.string().min(1).optional(),
22648
+ contextTokens: external_exports.number().int().positive().optional(),
22649
+ executionPreference: executionPreferenceParam,
22650
+ minPlacementEvidence: minPlacementEvidenceParam,
22651
+ minConfidence: minConfidenceParam,
22652
+ requiredCapabilities: requiredCapabilitiesParam
22653
+ }).strict();
22654
+ var limits = external_exports.object({
22655
+ maxMessages: external_exports.number().int().positive().optional(),
22656
+ maxBytes: external_exports.number().int().positive().optional(),
22657
+ maxEstimatedTokens: external_exports.number().int().positive().optional(),
22658
+ ttlSeconds: external_exports.number().int().positive().optional()
22659
+ }).strict();
22660
+ function scopeBody(messages, defaults, bounds) {
22661
+ return {
22662
+ messages,
22663
+ route_defaults: defaults === void 0 ? void 0 : {
22664
+ task: defaults.task === void 0 ? void 0 : canonicalTaskKind(defaults.task),
22665
+ objective: defaults.objective,
22666
+ model: defaults.model,
22667
+ context_tokens: defaults.contextTokens,
22668
+ execution_preference: defaults.executionPreference,
22669
+ min_placement_evidence: defaults.minPlacementEvidence,
22670
+ min_confidence: defaults.minConfidence,
22671
+ required_capabilities: defaults.requiredCapabilities
22672
+ },
22673
+ limits: bounds === void 0 ? void 0 : {
22674
+ max_messages: bounds.maxMessages,
22675
+ max_bytes: bounds.maxBytes,
22676
+ max_estimated_tokens: bounds.maxEstimatedTokens,
22677
+ ttl_seconds: bounds.ttlSeconds
22678
+ }
22679
+ };
22680
+ }
22681
+ function registerContextTools(server2) {
22682
+ const createScope2 = withServe(createScope);
22683
+ const getScope2 = withServe(getScope);
22684
+ const forkScope2 = withServe(forkScope);
22685
+ const deleteScope2 = withServe(deleteScope);
22686
+ const warmModel = withServe(warmModelRequest);
22687
+ server2.registerTool("scope", {
22688
+ description: "Use when: saved history. Do not use when: KV control. Returns: process-local scope metadata; history is opt-in.",
22689
+ inputSchema: external_exports.object({
22690
+ action: external_exports.enum(["create", "get", "fork", "delete"]),
22691
+ endpoint: endpointParam,
22692
+ scopeId: external_exports.string().uuid().optional(),
22693
+ revision: external_exports.number().int().nonnegative().optional(),
22694
+ messages: messagesParam.unwrap().min(0).optional(),
22695
+ routeDefaults: routeDefaults.optional(),
22696
+ limits: limits.optional(),
22697
+ includeMessages: external_exports.boolean().optional()
22698
+ }).strict(),
22699
+ outputSchema: objectResultSchema,
22700
+ annotations: { destructiveHint: true }
22701
+ }, async ({ action, endpoint, scopeId, revision, messages, routeDefaults: defaults, limits: bounds, includeMessages }) => {
22702
+ try {
22703
+ if (action === "create" !== (scopeId === void 0)) throw new Error("scopeId is required for get/fork/delete, forbidden for create.");
22704
+ if (action === "fork" !== (revision !== void 0)) throw new Error("revision is required only for fork.");
22705
+ if (includeMessages !== void 0 && action !== "get") throw new Error("includeMessages is valid only for get.");
22706
+ if (messages !== void 0 && action !== "create") throw new Error("messages is valid only for create.");
22707
+ if ((defaults !== void 0 || bounds !== void 0) && action !== "create" && action !== "fork") throw new Error("routeDefaults and limits are valid only for create/fork.");
22708
+ if (action === "delete") {
22709
+ await deleteScope2(endpoint, scopeId);
22710
+ return structuredResult({ deleted: true, scope_id: scopeId });
22711
+ }
22712
+ return parsedResult(action === "create" ? await createScope2(endpoint, scopeBody(messages, defaults, bounds)) : action === "fork" ? await forkScope2(endpoint, scopeId, { ...scopeBody(void 0, defaults, bounds), revision }) : await getScope2(endpoint, scopeId, includeMessages));
22713
+ } catch (error2) {
22714
+ return errorResult(error2);
22715
+ }
22716
+ });
22717
+ server2.registerTool("warm_model", {
22718
+ description: "Use when: preload an installed model. Do not use when: generation or pull. Returns: managed residency receipt or job ID.",
22719
+ inputSchema: external_exports.object({
22720
+ endpoint: endpointParam,
22721
+ model: external_exports.string().min(1),
22722
+ task: taskParam.removeDefault().optional(),
22723
+ contextTokens: external_exports.number().int().positive().optional(),
22724
+ executionPreference: executionPreferenceParam,
22725
+ minPlacementEvidence: minPlacementEvidenceParam,
22726
+ priority: external_exports.enum(["interactive", "normal", "background"]).optional(),
22727
+ keepAlive: external_exports.string().min(1).optional().describe("Omitted = adaptive finite TTL."),
22728
+ maxWaitSeconds: external_exports.number().int().positive().optional(),
22729
+ timeoutSeconds: external_exports.number().int().positive().optional(),
22730
+ defer: external_exports.boolean().optional()
22731
+ }).strict(),
22732
+ outputSchema: objectResultSchema,
22733
+ annotations: { destructiveHint: false }
22734
+ }, async ({ endpoint, model, task, contextTokens, executionPreference, minPlacementEvidence, priority, keepAlive, maxWaitSeconds, timeoutSeconds, defer }) => {
22735
+ try {
22736
+ if (task === "embedding") throw new Error("warm_model supports chat models; embedding warming is unsupported.");
22737
+ return parsedResult(await warmModel(endpoint, {
22738
+ model,
22739
+ task: task === void 0 ? void 0 : canonicalTaskKind(task),
22740
+ context_tokens: contextTokens,
22741
+ execution_preference: executionPreference,
22742
+ min_placement_evidence: minPlacementEvidence,
22743
+ priority,
22744
+ keep_alive: keepAlive,
22745
+ max_wait_seconds: maxWaitSeconds,
22746
+ timeout_seconds: timeoutSeconds,
22747
+ defer
22748
+ }));
22749
+ } catch (error2) {
22750
+ return errorResult(error2);
22751
+ }
22752
+ });
22753
+ }
22754
+
22028
22755
  // src/index.ts
22029
22756
  var execFileAsync = promisify(execFile);
22757
+ var createSession2 = withServe(createSession);
22758
+ var deleteSession2 = withServe(deleteSession);
22759
+ var killSession2 = withServe(killSession);
22760
+ var listModels2 = withServe(listModels);
22761
+ var ollamaLibrary = new OllamaLibraryClient();
22762
+ var route2 = withServe(route);
22763
+ var runTaskRequest2 = withServe(runTaskRequest);
22764
+ var runTaskBatchRequest2 = withServe(runTaskBatchRequest);
22765
+ var listTaskJobs2 = withServe(listTaskJobs);
22766
+ var getTaskJob2 = withServe(getTaskJob);
22767
+ var cancelTaskJob2 = withServe(cancelTaskJob);
22768
+ var removeTaskJob2 = withServe(removeTaskJob);
22030
22769
  var EXTERNAL_COST = configuredExternalCost();
22031
22770
  function withTaskTelemetry(result) {
22032
- if (!("structuredContent" in result)) return result;
22771
+ if (!isStructuredSuccess(result)) return result;
22033
22772
  const payload = result.structuredContent;
22034
22773
  const metrics = payload.metrics;
22035
22774
  return structuredResult({
@@ -22039,10 +22778,10 @@ function withTaskTelemetry(result) {
22039
22778
  outputTokens: typeof metrics?.output_tokens === "number" ? metrics.output_tokens : null,
22040
22779
  totalDurationNs: typeof metrics?.total_duration_ns === "number" ? metrics.total_duration_ns : null
22041
22780
  }, EXTERNAL_COST)
22042
- });
22781
+ }, { text: taskAnswerText(payload) });
22043
22782
  }
22044
22783
  function withBatchTelemetry(result) {
22045
- if (!("structuredContent" in result)) return result;
22784
+ if (!isStructuredSuccess(result)) return result;
22046
22785
  const payload = result.structuredContent;
22047
22786
  const rows = Array.isArray(payload.results) ? payload.results : [];
22048
22787
  let inputTokens = 0;
@@ -22067,7 +22806,7 @@ function withBatchTelemetry(result) {
22067
22806
  ...payload,
22068
22807
  results,
22069
22808
  telemetry: complete === rows.filter((row) => row?.ok === true).length ? costTelemetry({ inputTokens, outputTokens }, EXTERNAL_COST) : { local: null, externalEquivalent: null, note: "Batch aggregate unavailable because one or more successful items omitted token counts." }
22070
- });
22809
+ }, { text: batchAnswerText(payload) });
22071
22810
  }
22072
22811
  function pageLiveList(items, limit, cursor, identity) {
22073
22812
  const pageSize = limit ?? 20;
@@ -22095,9 +22834,9 @@ function pageLiveList(items, limit, cursor, identity) {
22095
22834
  }
22096
22835
  var liveDelegates = /* @__PURE__ */ new Set();
22097
22836
  function killLiveDelegates() {
22098
- for (const child of liveDelegates) {
22837
+ for (const child2 of liveDelegates) {
22099
22838
  try {
22100
- child.kill("SIGKILL");
22839
+ child2.kill("SIGKILL");
22101
22840
  } catch {
22102
22841
  }
22103
22842
  }
@@ -22109,63 +22848,107 @@ for (const stream of [process.stdout, process.stderr]) {
22109
22848
  throw error2;
22110
22849
  });
22111
22850
  }
22112
- process.on("exit", killLiveDelegates);
22851
+ process.on("exit", () => {
22852
+ killLiveDelegates();
22853
+ stopAutostartedServe();
22854
+ });
22855
+ process.stdin.on("close", () => {
22856
+ killLiveDelegates();
22857
+ stopAutostartedServe();
22858
+ process.exit(0);
22859
+ });
22860
+ function pullProgressReporter(extra) {
22861
+ const token = extra._meta?.progressToken;
22862
+ if (token === void 0) return void 0;
22863
+ let lastSent = 0;
22864
+ let step = 0;
22865
+ return (event) => {
22866
+ const now = Date.now();
22867
+ if (now - lastSent < 500 && event.status !== "success") return;
22868
+ lastSent = now;
22869
+ step += 1;
22870
+ const completed = typeof event.completed === "number" ? event.completed : void 0;
22871
+ const total = typeof event.total === "number" ? event.total : void 0;
22872
+ void extra.sendNotification({
22873
+ method: "notifications/progress",
22874
+ params: {
22875
+ progressToken: token,
22876
+ progress: completed ?? step,
22877
+ ...completed !== void 0 && total !== void 0 ? { total } : {},
22878
+ message: String(event.status ?? "pulling")
22879
+ }
22880
+ }).catch(() => void 0);
22881
+ };
22882
+ }
22113
22883
  for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
22114
22884
  process.on(signal, () => {
22115
22885
  killLiveDelegates();
22116
22886
  process.exit(0);
22117
22887
  });
22118
22888
  }
22119
- var INSTRUCTIONS = `caller owns task decomposition; operator owns endpoints, exact --cpu-model assignments, lifecycle.
22120
- Ollama plus the OS/driver run physical CPU/GPU.
22121
- Efficient loop: models{view:"installed"}, then models{view:"resident"}; doctor only for runtime diagnosis. Preview consequential work before executing.
22122
- delegate_research is only for narrow allowed-workspace research. Keep full diagnostics, raw embeddings, and long evidence out of active context; read freellama://docs/index on demand.
22123
- ask approval for one exact tag and reported size before ollama_manage; search or recommendation is never download permission.
22124
- run_task preview never executes; code_review aliases coding.
22125
- Use requiredCapabilities:["tools"] to preview tool eligibility; omit preview and supply the payload to execute.
22126
- Docs: freellama://docs/index.`;
22889
+ var INSTRUCTIONS = `models{view:"installed"}; models{view:"resident"} for residency.
22890
+ run_task preview never executes; code_review aliases coding. Preview routing only; requiredCapabilities:["tools"] previews tools; omit preview and supply the payload. Caller executes tools.
22891
+ quality needs policy/model; medium needs policy+benchmark; observed needs /api/ps proof.
22892
+ Scoped run_task needs scopeId+scopeRevision. Scopes store history; sessions affinity, not messages/KV. defer:true: job.id\u2192task_jobs.jobId unchanged. Restart loses scopes/sessions/jobs.
22893
+ Check isError first; prefer structuredContent, else content[].text. page.next_cursor\u2192cursor unchanged.
22894
+ caller owns task decomposition/prompts/format/verification; findings are candidates, not accepted defects. operator owns endpoints, exact --cpu-model assignments/lifecycle. Ollama plus the OS/driver run physical CPU/GPU.
22895
+ ask approval for one exact tag and reported size before ollama_manage pull; search or recommendation is never download permission. Stop/delete need exact-tag approval.
22896
+ serve :11435; default-endpoint autostart optional. Docs: freellama://docs/index.`;
22127
22897
  var server = new McpServer(
22128
22898
  { name: "freellama", version: SERVER_VERSION },
22129
22899
  { instructions: INSTRUCTIONS }
22130
22900
  );
22131
- var PACKAGED_DOCS_DIR = path4.resolve(path4.dirname(fileURLToPath2(import.meta.url)), "..", "docs");
22132
- if (!existsSync4(PACKAGED_DOCS_DIR)) {
22901
+ var PACKAGED_DOCS_DIR = path5.resolve(path5.dirname(fileURLToPath3(import.meta.url)), "..", "docs");
22902
+ if (!existsSync5(PACKAGED_DOCS_DIR)) {
22133
22903
  throw new Error(`FreeLlama MCP documentation is missing at ${PACKAGED_DOCS_DIR}; run the package build.`);
22134
22904
  }
22135
- var packagedDocs = readdirSync(PACKAGED_DOCS_DIR).filter((name) => name.endsWith(".md")).sort();
22905
+ var PACKAGED_DEV_DOCS_DIR = path5.join(PACKAGED_DOCS_DIR, "dev");
22906
+ var packagedDocs = [
22907
+ ...readdirSync(PACKAGED_DOCS_DIR).filter((name) => name.endsWith(".md")).sort(),
22908
+ ...existsSync5(PACKAGED_DEV_DOCS_DIR) ? readdirSync(PACKAGED_DEV_DOCS_DIR).filter((name) => name.endsWith(".md")).sort().map((name) => `dev/${name}`) : []
22909
+ ];
22910
+ var packagedDocNames = packagedDocs.map((name) => path5.basename(name));
22911
+ if (new Set(packagedDocNames).size !== packagedDocNames.length) {
22912
+ throw new Error(`FreeLlama MCP documentation has duplicate names across docs/ and docs/dev/: ${packagedDocs.join(", ")}`);
22913
+ }
22136
22914
  if (!packagedDocs.includes("INDEX.md")) {
22137
22915
  throw new Error(`FreeLlama MCP documentation index is missing at ${PACKAGED_DOCS_DIR}/INDEX.md; run the package build.`);
22138
22916
  }
22139
22917
  for (const name of packagedDocs) {
22140
- const uri = `freellama://docs/${name === "INDEX.md" ? "index" : name.replace(/\.md$/, "")}`;
22918
+ const base = path5.basename(name);
22919
+ const uri = `freellama://docs/${base === "INDEX.md" ? "index" : base.replace(/\.md$/, "")}`;
22141
22920
  server.registerResource(
22142
- `freellama-docs-${name.toLowerCase().replace(/\.md$/, "")}`,
22921
+ `freellama-docs-${base.toLowerCase().replace(/\.md$/, "")}`,
22143
22922
  uri,
22144
22923
  {
22145
22924
  mimeType: "text/markdown",
22146
22925
  description: name === "INDEX.md" ? "Index of packaged FreeLlama operator and agent documentation. Read this first, then fetch one relevant document." : `Packaged FreeLlama documentation: ${name}.`
22147
22926
  },
22148
22927
  async () => ({
22149
- contents: [{ uri, mimeType: "text/markdown", text: await readFile(path4.join(PACKAGED_DOCS_DIR, name), "utf8") }]
22928
+ contents: [{ uri, mimeType: "text/markdown", text: await readFile(path5.join(PACKAGED_DOCS_DIR, name), "utf8") }]
22150
22929
  })
22151
22930
  );
22152
22931
  }
22153
22932
  server.registerTool(
22154
22933
  "doctor",
22155
22934
  {
22156
- description: "Use when: runtime/config diagnosis. Do not use when: model selection. Returns: compact summary by default; config/full are opt-in.",
22157
- inputSchema: {
22935
+ description: "Use when: diagnosis/status/usage. Do not use when: choosing models. Returns: summary; opt into detail.",
22936
+ inputSchema: external_exports.object({
22158
22937
  endpoint: ollamaEndpointParam,
22159
22938
  serveEndpoint: endpointParam,
22160
- view: external_exports.enum(["summary", "scheduler", "config", "full"]).optional().describe("summary default; scheduler/config/full are verbose")
22161
- },
22939
+ view: external_exports.enum(["summary", "scheduler", "config", "full", "status", "usage"]).optional(),
22940
+ days: external_exports.number().int().min(1).max(366).optional()
22941
+ }).strict(),
22162
22942
  outputSchema: doctorResultSchema,
22163
22943
  annotations: { readOnlyHint: true }
22164
22944
  },
22165
- async ({ endpoint, serveEndpoint, view }) => {
22945
+ async ({ endpoint, serveEndpoint, view, days }) => {
22166
22946
  try {
22947
+ if (days !== void 0 && view !== "usage") return errorResult(new Error("days is only valid with view: usage."));
22948
+ if (view === "status") return parsedResult(await status(serveEndpoint));
22949
+ if (view === "usage") return parsedResult(await usage(serveEndpoint, days));
22167
22950
  const report = parsedResult(await doctor(endpoint));
22168
- if (!("structuredContent" in report)) return report;
22951
+ if (!isStructuredSuccess(report)) return report;
22169
22952
  try {
22170
22953
  report.structuredContent.machine = JSON.parse(await machine(serveEndpoint));
22171
22954
  delete report.structuredContent.machine_unavailable;
@@ -22224,23 +23007,24 @@ server.registerTool(
22224
23007
  server.registerTool(
22225
23008
  "session",
22226
23009
  {
22227
- description: "Use when: retaining model affinity. Do not use when: storing history or KV. Inputs: action, sessionId for delete. Returns: affinity handle or confirmation.",
22228
- inputSchema: {
22229
- action: external_exports.enum(["create", "delete"]).describe("create | release"),
22230
- sessionId: external_exports.string().uuid().optional().describe("delete only"),
23010
+ description: "Use when: affinity/session kill. Do not use when: job cancel/history/unload. Returns: receipt.",
23011
+ inputSchema: external_exports.object({
23012
+ action: external_exports.enum(["create", "delete", "kill"]),
23013
+ sessionId: external_exports.string().uuid().optional(),
22231
23014
  endpoint: endpointParam
22232
- },
23015
+ }).strict(),
22233
23016
  outputSchema: sessionResultSchema,
22234
- annotations: { destructiveHint: false }
23017
+ annotations: { destructiveHint: true }
22235
23018
  },
22236
23019
  async ({ action, sessionId, endpoint }) => {
22237
23020
  try {
22238
23021
  if (action === "create") {
22239
- if (sessionId !== void 0) return errorResult(new Error("sessionId is only valid for action: delete."));
22240
- return structuredResult(JSON.parse(await createSession(endpoint)));
23022
+ if (sessionId !== void 0) return errorResult(new Error("sessionId is only valid for action: delete or kill."));
23023
+ return structuredResult(JSON.parse(await createSession2(endpoint)));
22241
23024
  }
22242
- if (sessionId === void 0) return errorResult(new Error("action: delete requires sessionId."));
22243
- await deleteSession(endpoint, sessionId);
23025
+ if (sessionId === void 0) return errorResult(new Error(`action: ${action} requires sessionId.`));
23026
+ if (action === "kill") return parsedResult(await killSession2(endpoint, sessionId));
23027
+ await deleteSession2(endpoint, sessionId);
22244
23028
  return structuredResult({ session_id: sessionId, deleted: true });
22245
23029
  } catch (error2) {
22246
23030
  return errorResult(error2);
@@ -22250,25 +23034,33 @@ server.registerTool(
22250
23034
  server.registerTool(
22251
23035
  "models",
22252
23036
  {
22253
- description: 'Use when: inspecting models. Do not use when: executing or changing state; search never permits a pull. Inputs: one view and its fields. Returns: inventory/detail, placement, or candidates. Next: library family, then model:"<family>" for tags/fit.',
22254
- inputSchema: {
22255
- view: external_exports.enum(["installed", "resident", "detail", "raw", "library"]).optional().describe("installed (default, needs serve) | resident (needs serve) | detail | raw | library"),
22256
- model: external_exports.string().min(1).optional().describe('required for view "detail"; for "library", step 2 family name'),
22257
- includeVerbose: external_exports.boolean().optional().describe('"detail" only. Adds license/modelfile \u2014 the bulk of that payload, never routing-relevant'),
22258
- query: external_exports.string().min(1).optional().describe('"library" step 1: free text, e.g. "qwen", "embed"'),
22259
- capabilities: external_exports.array(external_exports.enum(["vision", "tools", "thinking", "embedding", "cloud"])).min(1).max(5).optional().describe('"library" step 1: filter chips; combined as AND by the site'),
22260
- order: external_exports.enum(["popular", "newest"]).optional().describe('"library" step 1. default "popular" \u2014 prefer it'),
22261
- limit: external_exports.number().int().positive().max(50).optional().describe('"library" search or raw/tag page size; raw default 20'),
22262
- cursor: external_exports.string().min(1).optional().describe("opaque continuation cursor for raw models or library tags"),
23037
+ description: "Use when: inventory/library. Do not use when: execution/mutation. Returns: models; library finds families, then one family's tags.",
23038
+ inputSchema: external_exports.object({
23039
+ view: external_exports.enum(["installed", "resident", "detail", "raw", "library"]).optional(),
23040
+ model: external_exports.string().min(1).optional(),
23041
+ includeVerbose: external_exports.boolean().optional(),
23042
+ includeLibrary: external_exports.boolean().optional(),
23043
+ includeReadme: external_exports.boolean().optional(),
23044
+ query: external_exports.string().min(1).optional(),
23045
+ capabilities: external_exports.array(external_exports.enum(["vision", "tools", "thinking", "embedding", "cloud"])).min(1).max(5).optional(),
23046
+ order: external_exports.enum(["popular", "newest"]).optional(),
23047
+ limit: external_exports.number().int().positive().max(50).optional(),
23048
+ cursor: external_exports.string().min(1).optional(),
22263
23049
  endpoint: endpointParam,
22264
23050
  ollamaEndpoint: ollamaEndpointParam
22265
- },
23051
+ }).strict(),
22266
23052
  outputSchema: modelsResultSchema,
22267
23053
  annotations: { readOnlyHint: true }
22268
23054
  },
22269
- async ({ view, model, includeVerbose, query, capabilities, order, limit, cursor, endpoint, ollamaEndpoint }) => {
23055
+ async ({ view, model, includeVerbose, includeLibrary, includeReadme, query, capabilities, order, limit, cursor, endpoint, ollamaEndpoint }) => {
22270
23056
  try {
22271
23057
  const selectedView = view ?? "installed";
23058
+ if (includeLibrary !== void 0 && selectedView !== "installed" && selectedView !== "detail") {
23059
+ return errorResult(new Error('`includeLibrary` is valid only for views "installed" and "detail".'));
23060
+ }
23061
+ if (includeReadme !== void 0 && !(selectedView === "library" && model || selectedView === "detail" && includeLibrary === true)) {
23062
+ return errorResult(new Error('`includeReadme` needs library step 2, or view "detail" with `includeLibrary:true`.'));
23063
+ }
22272
23064
  if (selectedView !== "library" && [query, capabilities, order].some((value) => value !== void 0)) {
22273
23065
  return errorResult(new Error(`view "${selectedView}" does not accept library search fields.`));
22274
23066
  }
@@ -22294,11 +23086,11 @@ server.registerTool(
22294
23086
  return structuredResult({ ...raw, models: page.items, page: { returned: page.returned, total: page.total, next_cursor: page.next_cursor } });
22295
23087
  }
22296
23088
  case "resident": {
22297
- const managed = parsedResult(await listModels(endpoint));
22298
- if (!("structuredContent" in managed)) return managed;
23089
+ const managed = parsedResult(await listModels2(endpoint));
23090
+ if (!isStructuredSuccess(managed)) return managed;
22299
23091
  const data = managed.structuredContent;
22300
23092
  const models = (data.models ?? []).filter((entry) => entry.resident === true).map((entry) => {
22301
- const size = typeof entry.size === "number" ? entry.size : null;
23093
+ const size = typeof entry.resident_size === "number" ? entry.resident_size : null;
22302
23094
  const vram = typeof entry.resident_vram === "number" ? entry.resident_vram : null;
22303
23095
  if (size === null || vram === null || size === 0) return entry;
22304
23096
  const gpuPercent = Math.round(vram / size * 100);
@@ -22331,6 +23123,17 @@ server.registerTool(
22331
23123
  method: "POST",
22332
23124
  body: { model }
22333
23125
  });
23126
+ let local = data;
23127
+ if (includeLibrary && !data.remote_host && !data.remote_model && !/-cloud$/i.test(model)) {
23128
+ try {
23129
+ const inventory = await ollamaFetch(ollamaEndpoint, "/api/tags");
23130
+ const canonical = libraryReference(model)?.tag;
23131
+ const entry = canonical && inventory.models?.find((entry2) => libraryReference(String(entry2.name ?? entry2.model))?.tag === canonical);
23132
+ if (entry) local = { ...entry, ...data, digest: entry.digest };
23133
+ } catch {
23134
+ }
23135
+ }
23136
+ const library = includeLibrary && !local.remote_host && !local.remote_model && !/-cloud$/i.test(model) ? await ollamaLibrary.lookup(model) : void 0;
22334
23137
  const { license, modelfile, ...rest } = data;
22335
23138
  const modelInfo = rest.model_info ?? {};
22336
23139
  const contextEntry = Object.entries(modelInfo).find(
@@ -22339,13 +23142,23 @@ server.registerTool(
22339
23142
  return structuredResult({
22340
23143
  ...rest,
22341
23144
  max_context_length: contextEntry?.[1] ?? null,
23145
+ knowledge: modelKnowledge(model, local, library, includeReadme),
22342
23146
  ...includeVerbose ? { license, modelfile } : {}
22343
23147
  });
22344
23148
  }
22345
23149
  case "library":
22346
- return await libraryLookup({ model, query, capabilities, order, limit, cursor, endpoint, ollamaEndpoint });
22347
- default:
22348
- return parsedResult(await listModels(endpoint));
23150
+ return await libraryLookup({ model, query, capabilities, order, limit, cursor, includeReadme, endpoint, ollamaEndpoint });
23151
+ default: {
23152
+ const result = parsedResult(await listModels2(endpoint));
23153
+ if (!isStructuredSuccess(result)) return result;
23154
+ const data = result.structuredContent;
23155
+ return structuredResult({ ...data, models: await enrichInstalledModels(
23156
+ data.models ?? [],
23157
+ ollamaLibrary,
23158
+ includeLibrary ?? false,
23159
+ async (upstream) => (await ollamaFetch(upstream ?? ollamaEndpoint, "/api/tags")).models ?? []
23160
+ ) });
23161
+ }
22349
23162
  }
22350
23163
  } catch (error2) {
22351
23164
  return errorResult(error2);
@@ -22359,20 +23172,10 @@ async function libraryLookup({
22359
23172
  order,
22360
23173
  limit,
22361
23174
  cursor,
23175
+ includeReadme,
22362
23176
  endpoint,
22363
23177
  ollamaEndpoint
22364
23178
  }) {
22365
- const fetchPage = async (url) => {
22366
- const controller = new AbortController();
22367
- const timer = setTimeout(() => controller.abort(), DEFAULT_OLLAMA_FETCH_TIMEOUT_SECONDS * 1e3);
22368
- try {
22369
- const response = await fetch(url, { signal: controller.signal });
22370
- if (!response.ok) throw new Error(`ollama.com returned HTTP ${response.status}`);
22371
- return await response.text();
22372
- } finally {
22373
- clearTimeout(timer);
22374
- }
22375
- };
22376
23179
  const localState = async () => {
22377
23180
  let installed = /* @__PURE__ */ new Set();
22378
23181
  let memoryBytes = null;
@@ -22390,9 +23193,19 @@ async function libraryLookup({
22390
23193
  };
22391
23194
  try {
22392
23195
  if (model) {
22393
- const family = model.split(":")[0];
22394
- const familyPage = await fetchPage(`https://ollama.com/library/${encodeURIComponent(family)}`);
22395
- const { tags } = parseModelTags(familyPage, family);
23196
+ const ref = libraryReference(model);
23197
+ if (!ref) throw new Error("Use an exact Ollama family or namespace/model name, not a URL or path.");
23198
+ const { family } = ref;
23199
+ const library = await ollamaLibrary.lookup(model);
23200
+ if (library.status !== "available") return structuredResult({
23201
+ family,
23202
+ sourcePage: ref.url,
23203
+ tags: [],
23204
+ tagsUnavailable: library.message,
23205
+ library,
23206
+ recommendation: null
23207
+ });
23208
+ const tags = library.tags;
22396
23209
  const { installed: installed2, memoryBytes } = await localState();
22397
23210
  const budget = memoryBytes ? memoryBytes * 0.6 : null;
22398
23211
  const annotated = tags.map((t) => ({
@@ -22401,15 +23214,23 @@ async function libraryLookup({
22401
23214
  fitsInMemory: budget && t.sizeBytes ? t.sizeBytes <= budget : null,
22402
23215
  fitScope: "host_memory_budget_only"
22403
23216
  }));
22404
- const runnable = annotated.filter((t) => t.fitsInMemory === true && !/cloud/.test(t.tag));
22405
- const embeddingMentions = (familyPage.match(/embedding/gi) ?? []).length;
22406
- const isEmbeddingFamily = embeddingMentions >= 3 || /embed/i.test(family);
23217
+ const runnable = annotated.filter((t) => t.fitsInMemory === true && !t.cloud);
23218
+ const isEmbeddingFamily = library.card.capabilities.includes("embedding");
22407
23219
  const sized = runnable.filter((t) => t.sizeBytes);
22408
23220
  const best = budget ? isEmbeddingFamily ? sized.sort((a, b) => (a.sizeBytes ?? 0) - (b.sizeBytes ?? 0))[0] : sized.sort((a, b) => (b.sizeBytes ?? 0) - (a.sizeBytes ?? 0))[0] : void 0;
22409
23221
  const tagPage = pageLiveList(annotated, limit, cursor, (tag) => tag.tag);
22410
23222
  return structuredResult({
22411
23223
  family,
22412
- sourcePage: `https://ollama.com/library/${encodeURIComponent(family)}`,
23224
+ sourcePage: ref.url,
23225
+ library: {
23226
+ status: library.status,
23227
+ sourceUrl: library.sourceUrl,
23228
+ tagsSourceUrl: library.tagsSourceUrl,
23229
+ fetchedAt: library.fetchedAt,
23230
+ expiresAt: library.expiresAt,
23231
+ cached: library.cached,
23232
+ ...publicModelGuidance(library, void 0, includeReadme)
23233
+ },
22413
23234
  ...tags.length === 0 ? {
22414
23235
  tagsUnavailable: `No pullable tags found for "${family}". Either it is cloud-only (no local download), the family name is wrong, or ollama.com changed its markup. Open the page above to check before concluding the model does not exist.`
22415
23236
  } : {},
@@ -22418,29 +23239,22 @@ async function libraryLookup({
22418
23239
  tags: tagPage.items,
22419
23240
  page: { returned: tagPage.returned, total: tagPage.total, next_cursor: tagPage.next_cursor },
22420
23241
  recommendationUnavailable: budget ? void 0 : "No machine profile (freellama serve unreachable), so memory fit could not be checked and no tag is recommended. Start serve, or read the sizes yourself.",
22421
- recommendation: best ? isEmbeddingFamily ? {
22422
- tag: best.tag,
22423
- why: "Smallest tag that fits \u2014 this is an embedding family, where bigger is NOT better. Measured on real retrieval over this repo: nomic-embed-text (274MB) scored 5/6 recall@3 in 4.2s, while qwen3-embedding:0.6b (2.3x the size) scored 4/6 at 3.5x the indexing cost. Embeddings do no sampling, so there is no accuracy cliff for size to buy you past.",
22424
- configure: 'Batch your inputs in one `run_task` call and use keepAlive:"0" \u2014 an embedding model has no reason to hold memory after the vectors are computed. Leave returnEmbeddings off unless you are storing the vectors yourself.',
22425
- caution: "Index once, query many times, and own the staleness: FreeLlama stores no vectors, and a stale index fails silently by returning confidently wrong files. Also, if you know the keyword, grep beat embedding search here on accuracy, latency and cost at the same time \u2014 reach for embeddings when there is no keyword to search for."
22426
- } : {
22427
- tag: best.tag,
22428
- why: `Largest tag inside the conservative ~60% host-memory preflight${memoryBytes ? ` of ${Math.round(memoryBytes / 1e9)}GB` : ""}. This proves neither accelerator fit nor task quality; preview and benchmark before pulling.`,
22429
- configure: `Send an explicit num_ctx rather than inheriting Ollama's default, which is 4096 tokens by default unless the Ollama service or request sets num_ctx. Use keepAlive:"0" for one-off calls so it does not hold memory after.`,
22430
- caution: 'Do not co-resident large models until their combined runner sizes, contexts, KV caches, and OS headroom fit this host. Check `models{view:"resident"}` and accelerator telemetry first; benchmark packaging rather than inferring quality or speed from a model suffix.'
22431
- } : null
23242
+ recommendation: best ? libraryTrialCandidate(best.tag, isEmbeddingFamily) : null
22432
23243
  });
22433
23244
  }
22434
23245
  const params = new URLSearchParams();
22435
23246
  for (const c of capabilities ?? []) params.append("c", c);
22436
23247
  if (query) params.set("q", query);
22437
23248
  if (order === "newest") params.set("o", "newest");
22438
- const url = `https://ollama.com/search?${params.toString()}`;
22439
- const parsed = parseModelSearch(await fetchPage(url)).slice(0, limit ?? 10);
23249
+ const search = await ollamaLibrary.search(params);
23250
+ const url = search.url;
23251
+ const parsed = search.models.slice(0, limit ?? 10);
22440
23252
  const { installed } = await localState();
22441
23253
  const installedFamilies = new Set([...installed].map((n) => n.split(":")[0]));
22442
23254
  return structuredResult({
22443
23255
  query: url,
23256
+ fetchedAt: search.fetchedAt,
23257
+ ...parsed.length === 0 ? { searchUnavailable: "No result cards were recognized. The query may have no matches or Ollama's markup may have changed; inspect the source URL." } : {},
22444
23258
  order: order ?? "popular",
22445
23259
  count: parsed.length,
22446
23260
  nextStep: 'Not pullable yet. Call again with view:"library", model:"<name>" to get tags, sizes and memory fit.',
@@ -22450,42 +23264,47 @@ async function libraryLookup({
22450
23264
  return errorResult(error2);
22451
23265
  }
22452
23266
  }
23267
+ registerContextTools(server);
22453
23268
  server.registerTool(
22454
23269
  "run_task",
22455
23270
  {
22456
- description: "Use when: routing or executing supplied content. Do not use when: workspace files must be read; use delegate_research. Preview consequential work first; it never generates. Returns: decision or response with receipts. Next: inspect structured observation and verification.",
22457
- inputSchema: {
23271
+ description: "Use when: supplied chat/tools/embeddings. Do not use when: files. Returns: response/receipts.",
23272
+ inputSchema: external_exports.object({
22458
23273
  endpoint: endpointParam,
22459
- task: taskParam,
23274
+ task: taskParam.removeDefault().optional().describe("caller owns prompts and output format."),
22460
23275
  objective: objectiveParam,
22461
- model: external_exports.string().min(1).optional().describe("Force this exact installed model name."),
22462
- sessionId: external_exports.string().min(1).optional().describe("Session id for model affinity across calls."),
22463
- contextTokens: external_exports.number().int().positive().optional().describe("Total Ollama context window (num_ctx), including input and output."),
23276
+ model: external_exports.string().min(1).optional(),
23277
+ sessionId: external_exports.string().uuid().optional(),
23278
+ scopeId: external_exports.string().uuid().optional(),
23279
+ scopeRevision: external_exports.number().int().nonnegative().optional(),
23280
+ contextTokens: external_exports.number().int().positive().optional().describe("Total input + output window (num_ctx)."),
22464
23281
  executionPreference: executionPreferenceParam,
22465
23282
  minPlacementEvidence: minPlacementEvidenceParam,
22466
23283
  requiredCapabilities: requiredCapabilitiesParam,
22467
- prompt: external_exports.string().min(1).optional().describe("Chat input when messages is omitted."),
23284
+ prompt: external_exports.string().min(1).optional(),
23285
+ systemPrompt: systemPromptParam,
22468
23286
  images: external_exports.array(external_exports.string().min(1)).min(1).optional().describe("base64, no data-URI prefix; prompt mode only; requires an explicit tested vision model"),
22469
- messages: external_exports.array(
22470
- external_exports.object({ role: external_exports.enum(["system", "user", "assistant", "tool"]), content: external_exports.string() }).passthrough()
22471
- ).min(1).optional().describe(
22472
- "wins over prompt; preserves Ollama images, thinking, tool_calls, tool_name, and other message fields"
23287
+ messages: messagesParam.describe(
23288
+ "Caller system prompts; no injected task instructions. Overrides prompt; extra fields preserved."
22473
23289
  ),
22474
- input: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]).optional().describe("embedding only; batch strings because one call is far cheaper than one call per item"),
22475
- tools: external_exports.array(external_exports.record(external_exports.unknown())).min(1).optional().describe("Ollama function definitions for chat tasks."),
22476
- keepAlive: external_exports.string().min(1).optional().describe('"0" unloads now, "-1" pins, default 5m'),
22477
- format: external_exports.union([external_exports.literal("json"), external_exports.record(external_exports.unknown())]).optional().describe('Ollama structured output: "json" or a JSON schema object'),
22478
- think: external_exports.union([external_exports.boolean(), external_exports.enum(["low", "medium", "high"])]).optional().describe("Override the task profile for thinking-capable models"),
22479
- options: external_exports.record(external_exports.unknown()).optional().describe("Advanced Ollama options; num_ctx/contextTokens and num_gpu/placement are routing-owned. num_predict caps output."),
23290
+ input: external_exports.union([external_exports.string().min(1), external_exports.array(external_exports.string().min(1)).min(1)]).optional(),
23291
+ tools: localToolsParam,
23292
+ keepAlive: external_exports.string().min(1).optional().describe('"0" unloads now, "-1" pins; omitted = adaptive finite TTL'),
23293
+ format: external_exports.union([external_exports.literal("json"), external_exports.record(external_exports.unknown())]).optional(),
23294
+ think: external_exports.union([external_exports.boolean(), external_exports.enum(["low", "medium", "high"])]).optional(),
23295
+ options: external_exports.record(external_exports.unknown()).optional().describe("num_ctx/contextTokens and num_gpu/placement are routing-owned; num_predict caps output."),
22480
23296
  logprobs: external_exports.boolean().optional(),
22481
- topLogprobs: external_exports.number().int().nonnegative().optional().describe("Requires logprobs:true"),
23297
+ topLogprobs: external_exports.number().int().nonnegative().optional(),
22482
23298
  minConfidence: minConfidenceParam,
22483
- priority: external_exports.enum(["interactive", "normal", "background"]).optional().describe("Admission class only; normal default. Fair scheduling prevents background starvation."),
22484
- returnEmbeddings: external_exports.boolean().optional().describe("false (default) withholds the raw vectors; they are large and unreadable to a model"),
23299
+ priority: external_exports.enum(["interactive", "normal", "background"]).optional(),
23300
+ maxWaitSeconds: external_exports.number().int().positive().optional(),
23301
+ timeoutSeconds: external_exports.number().int().positive().optional(),
23302
+ defer: external_exports.boolean().optional(),
23303
+ returnEmbeddings: external_exports.boolean().optional(),
22485
23304
  preview: external_exports.boolean().optional().describe(
22486
23305
  "true = routing fields only; rejects payloads and runtime controls; never executes"
22487
23306
  )
22488
- },
23307
+ }).strict(),
22489
23308
  outputSchema: taskResultSchema,
22490
23309
  annotations: { destructiveHint: false }
22491
23310
  },
@@ -22495,11 +23314,14 @@ server.registerTool(
22495
23314
  objective,
22496
23315
  model,
22497
23316
  sessionId,
23317
+ scopeId,
23318
+ scopeRevision,
22498
23319
  contextTokens,
22499
23320
  executionPreference,
22500
23321
  minPlacementEvidence,
22501
23322
  requiredCapabilities,
22502
23323
  prompt,
23324
+ systemPrompt,
22503
23325
  images,
22504
23326
  messages,
22505
23327
  input,
@@ -22512,14 +23334,23 @@ server.registerTool(
22512
23334
  topLogprobs,
22513
23335
  minConfidence,
22514
23336
  priority,
23337
+ maxWaitSeconds,
23338
+ timeoutSeconds,
23339
+ defer,
22515
23340
  returnEmbeddings,
22516
23341
  preview
22517
23342
  }) => {
22518
23343
  try {
22519
- const canonicalTask = canonicalTaskKind(task);
23344
+ const canonicalTask = canonicalTaskKind(task ?? "completion");
23345
+ if (scopeId === void 0 !== (scopeRevision === void 0)) {
23346
+ return errorResult(new Error("scopeId and scopeRevision must be supplied together."));
23347
+ }
22520
23348
  if (preview) {
22521
23349
  const executionOnlyFields = [
23350
+ ["scopeId", scopeId],
23351
+ ["scopeRevision", scopeRevision],
22522
23352
  ["prompt", prompt],
23353
+ ["systemPrompt", systemPrompt],
22523
23354
  ["images", images],
22524
23355
  ["messages", messages],
22525
23356
  ["input", input],
@@ -22530,7 +23361,11 @@ server.registerTool(
22530
23361
  ["options", options],
22531
23362
  ["logprobs", logprobs],
22532
23363
  ["topLogprobs", topLogprobs],
22533
- ["returnEmbeddings", returnEmbeddings]
23364
+ ["returnEmbeddings", returnEmbeddings],
23365
+ ["maxWaitSeconds", maxWaitSeconds],
23366
+ ["priority", priority],
23367
+ ["timeoutSeconds", timeoutSeconds],
23368
+ ["defer", defer]
22534
23369
  ].filter(([, value]) => value !== void 0).map(([name]) => `\`${name}\``);
22535
23370
  if (executionOnlyFields.length > 0) {
22536
23371
  return errorResult(
@@ -22546,7 +23381,7 @@ server.registerTool(
22546
23381
  }
22547
23382
  if (!preview && task === "embedding") {
22548
23383
  if (input === void 0) return errorResult(new Error('task "embedding" requires `input`.'));
22549
- if ([prompt, images, messages, tools, format, think, logprobs, topLogprobs].some(
23384
+ if ([prompt, systemPrompt, images, messages, tools, format, think, logprobs, topLogprobs].some(
22550
23385
  (value) => value !== void 0
22551
23386
  )) {
22552
23387
  return errorResult(
@@ -22558,10 +23393,10 @@ server.registerTool(
22558
23393
  if (input !== void 0 || returnEmbeddings !== void 0) {
22559
23394
  return errorResult(new Error('`input` and `returnEmbeddings` are valid only for task "embedding".'));
22560
23395
  }
22561
- if (prompt === void 0 && (messages === void 0 || messages.length === 0)) {
22562
- return errorResult(new Error(`task "${task}" requires \`prompt\` or \`messages\`.`));
23396
+ if (prompt === void 0 && systemPrompt === void 0 && (messages === void 0 || messages.length === 0)) {
23397
+ return errorResult(new Error(`task "${task}" requires \`prompt\`, \`systemPrompt\`, or \`messages\`.`));
22563
23398
  }
22564
- if (images !== void 0 && messages !== void 0) {
23399
+ if (images !== void 0 && (messages !== void 0 || prompt === void 0)) {
22565
23400
  return errorResult(
22566
23401
  new Error("Top-level `images` is valid only with `prompt`; put images inside messages instead.")
22567
23402
  );
@@ -22572,44 +23407,48 @@ server.registerTool(
22572
23407
  }
22573
23408
  if (preview) {
22574
23409
  const result2 = parsedResult(
22575
- await route(endpoint, canonicalTask, objective, model, sessionId, contextTokens, effectiveRequiredCapabilities, minConfidence, executionPreference, minPlacementEvidence)
23410
+ await route2(endpoint, canonicalTask, objective, model, sessionId, contextTokens, effectiveRequiredCapabilities, minConfidence, executionPreference, minPlacementEvidence)
22576
23411
  );
22577
- if ("structuredContent" in result2) {
23412
+ if (isStructuredSuccess(result2)) {
22578
23413
  const refusal = belowConfidence(result2.structuredContent, minConfidence);
22579
23414
  if (refusal) return refusal;
22580
23415
  }
22581
23416
  return result2;
22582
23417
  }
22583
- if (minConfidence) {
23418
+ if (minConfidence && scopeId === void 0) {
22584
23419
  const decision = parsedResult(
22585
23420
  // minConfidence is forwarded so the CORE gate refuses, with its actionable message naming
22586
23421
  // the two commands that raise the grade. The belowConfidence() check below stays only as a
22587
23422
  // fallback for servers older than the core gate.
22588
- await route(endpoint, canonicalTask, objective, model, sessionId, contextTokens, effectiveRequiredCapabilities, minConfidence, executionPreference, minPlacementEvidence)
23423
+ await route2(endpoint, canonicalTask, objective, model, sessionId, contextTokens, effectiveRequiredCapabilities, minConfidence, executionPreference, minPlacementEvidence)
22589
23424
  );
22590
- if ("structuredContent" in decision) {
22591
- const refusal = belowConfidence(decision.structuredContent, minConfidence);
22592
- if (refusal) return refusal;
22593
- }
23425
+ if (!isStructuredSuccess(decision)) return decision;
23426
+ const refusal = belowConfidence(decision.structuredContent, minConfidence);
23427
+ if (refusal) return refusal;
22594
23428
  }
22595
23429
  const result = parsedResult(
22596
- await runTaskRequest(endpoint ?? DEFAULT_SERVE_ENDPOINT, {
22597
- task: canonicalTask,
22598
- objective: objective ?? "balanced",
23430
+ await runTaskRequest2(endpoint ?? DEFAULT_SERVE_ENDPOINT, {
23431
+ task: scopeId !== void 0 && task === void 0 ? void 0 : canonicalTask,
23432
+ objective: objective ?? (scopeId === void 0 ? "balanced" : void 0),
22599
23433
  model,
22600
23434
  session_id: sessionId,
23435
+ scope_id: scopeId,
23436
+ scope_revision: scopeRevision,
22601
23437
  context_tokens: contextTokens,
22602
- required_capabilities: effectiveRequiredCapabilities ?? [],
23438
+ required_capabilities: effectiveRequiredCapabilities,
22603
23439
  prompt,
22604
23440
  images,
22605
- messages: messages ?? [],
23441
+ messages: taskMessages({ systemPrompt, messages, prompt, images }),
22606
23442
  input,
22607
23443
  tools,
22608
23444
  keep_alive: keepAlive,
22609
23445
  min_confidence: minConfidence,
22610
23446
  priority: priority ?? "normal",
22611
- execution_preference: executionPreference ?? "auto",
22612
- min_placement_evidence: minPlacementEvidence ?? "configured",
23447
+ max_wait_seconds: maxWaitSeconds,
23448
+ timeout_seconds: timeoutSeconds,
23449
+ defer: defer ?? false,
23450
+ execution_preference: executionPreference,
23451
+ min_placement_evidence: minPlacementEvidence,
22613
23452
  request_options: {
22614
23453
  format,
22615
23454
  think,
@@ -22619,7 +23458,8 @@ server.registerTool(
22619
23458
  }
22620
23459
  })
22621
23460
  );
22622
- if (!returnEmbeddings && "structuredContent" in result) {
23461
+ if (isStructuredSuccess(result) && result.structuredContent.deferred === true) return result;
23462
+ if (!returnEmbeddings && isStructuredSuccess(result)) {
22623
23463
  const trimmed = summarizeEmbeddings(result.structuredContent);
22624
23464
  if (trimmed) return withTaskTelemetry(structuredResult(trimmed));
22625
23465
  }
@@ -22629,15 +23469,50 @@ server.registerTool(
22629
23469
  }
22630
23470
  }
22631
23471
  );
23472
+ server.registerTool(
23473
+ "task_jobs",
23474
+ {
23475
+ description: "Use when: task jobs. Do not use when: new work/session kill. Returns: status/result; remove cancels first.",
23476
+ inputSchema: external_exports.object({
23477
+ endpoint: endpointParam,
23478
+ action: external_exports.enum(["list", "get", "cancel", "remove"]),
23479
+ jobId: external_exports.string().uuid().optional(),
23480
+ returnEmbeddings: external_exports.boolean().optional()
23481
+ }).strict(),
23482
+ outputSchema: objectResultSchema,
23483
+ annotations: { destructiveHint: true }
23484
+ },
23485
+ async ({ endpoint, action, jobId, returnEmbeddings }) => {
23486
+ try {
23487
+ if (action === "list" ? jobId !== void 0 : jobId === void 0) {
23488
+ return errorResult(new Error("Use jobId only with get/cancel/remove; all require a jobId."));
23489
+ }
23490
+ if (returnEmbeddings !== void 0 && action !== "get") {
23491
+ return errorResult(new Error("returnEmbeddings is valid only with get."));
23492
+ }
23493
+ const result = parsedResult(action === "list" ? await listTaskJobs2(endpoint) : action === "get" ? await getTaskJob2(endpoint, jobId) : action === "cancel" ? await cancelTaskJob2(endpoint, jobId) : await removeTaskJob2(endpoint, jobId));
23494
+ if (!returnEmbeddings && isStructuredSuccess(result)) {
23495
+ const job = result.structuredContent.job;
23496
+ if (job?.result && typeof job.result === "object") {
23497
+ const trimmed = summarizeEmbeddings(job.result);
23498
+ if (trimmed) return structuredResult({ ...result.structuredContent, job: { ...job, result: trimmed } });
23499
+ }
23500
+ }
23501
+ return result;
23502
+ } catch (error2) {
23503
+ return errorResult(error2);
23504
+ }
23505
+ }
23506
+ );
22632
23507
  server.registerTool(
22633
23508
  "run_task_batch",
22634
23509
  {
22635
- description: "Use when: independent work. Do not use when: dependencies. Inputs: [{id, independent:true, task}]; maxParallelism caps dispatch. Returns: ordered receipts.",
22636
- inputSchema: {
23510
+ description: "Use when: independent:true work. Do not use when: dependencies. Returns: ordered receipts; maxParallelism caps dispatch.",
23511
+ inputSchema: external_exports.object({
22637
23512
  tasks: external_exports.array(batchItemParam).min(1).max(64),
22638
23513
  maxParallelism: external_exports.number().int().positive().max(64).optional(),
22639
23514
  endpoint: endpointParam
22640
- },
23515
+ }).strict(),
22641
23516
  outputSchema: batchResultSchema,
22642
23517
  annotations: { destructiveHint: false }
22643
23518
  },
@@ -22645,36 +23520,46 @@ server.registerTool(
22645
23520
  try {
22646
23521
  for (const item of tasks) {
22647
23522
  const task = item.task;
23523
+ if (task.scopeId === void 0 !== (task.scopeRevision === void 0)) {
23524
+ return errorResult(new Error(`batch item ${item.id}: scopeId and scopeRevision must be supplied together.`));
23525
+ }
22648
23526
  if (task.task === "embedding") {
22649
- if (task.input === void 0 || task.prompt !== void 0 || task.messages !== void 0 || task.tools !== void 0) {
23527
+ if (task.input === void 0 || task.prompt !== void 0 || task.systemPrompt !== void 0 || task.messages !== void 0 || task.tools !== void 0) {
22650
23528
  return errorResult(new Error(`batch item ${item.id}: embedding requires input and accepts no chat payload.`));
22651
23529
  }
22652
- } else if (task.input !== void 0 || task.prompt === void 0 && task.messages === void 0) {
22653
- return errorResult(new Error(`batch item ${item.id}: chat work requires prompt or messages and accepts no input.`));
23530
+ } else if (task.input !== void 0 || task.prompt === void 0 && task.systemPrompt === void 0 && task.messages === void 0) {
23531
+ return errorResult(new Error(`batch item ${item.id}: chat work requires prompt, systemPrompt, or messages and accepts no input.`));
23532
+ }
23533
+ if (task.images !== void 0 && (task.messages !== void 0 || task.prompt === void 0)) {
23534
+ return errorResult(new Error(`batch item ${item.id}: top-level images requires prompt without messages; put images inside messages instead.`));
22654
23535
  }
22655
23536
  if (task.images !== void 0 && task.model === void 0) {
22656
23537
  return errorResult(new Error(`batch item ${item.id}: images require an explicit tested vision model.`));
22657
23538
  }
22658
23539
  }
22659
- return withBatchTelemetry(parsedResult(await runTaskBatchRequest(endpoint ?? DEFAULT_SERVE_ENDPOINT, {
23540
+ return withBatchTelemetry(parsedResult(await runTaskBatchRequest2(endpoint ?? DEFAULT_SERVE_ENDPOINT, {
22660
23541
  tasks: tasks.map((item) => {
22661
23542
  const task = item.task;
22662
23543
  return {
22663
23544
  id: item.id,
22664
23545
  independent: item.independent,
22665
23546
  task: {
22666
- task: task.task === "code_review" ? "coding" : task.task,
22667
- objective: task.objective ?? "balanced",
23547
+ task: task.task === "code_review" ? "coding" : task.task ?? (task.scopeId === void 0 ? "completion" : void 0),
23548
+ objective: task.objective ?? (task.scopeId === void 0 ? "balanced" : void 0),
22668
23549
  model: task.model,
22669
23550
  session_id: task.sessionId,
23551
+ scope_id: task.scopeId,
23552
+ scope_revision: task.scopeRevision,
22670
23553
  context_tokens: task.contextTokens,
22671
- execution_preference: task.executionPreference ?? "auto",
22672
- min_placement_evidence: task.minPlacementEvidence ?? "configured",
22673
- required_capabilities: task.requiredCapabilities ?? [],
23554
+ execution_preference: task.executionPreference,
23555
+ min_placement_evidence: task.minPlacementEvidence,
23556
+ required_capabilities: task.requiredCapabilities,
22674
23557
  priority: task.priority ?? "normal",
23558
+ max_wait_seconds: task.maxWaitSeconds,
23559
+ timeout_seconds: task.timeoutSeconds,
22675
23560
  prompt: task.prompt,
22676
23561
  images: task.images,
22677
- messages: task.messages ?? [],
23562
+ messages: taskMessages(task),
22678
23563
  input: task.input,
22679
23564
  tools: task.tools,
22680
23565
  keep_alive: task.keepAlive,
@@ -22699,24 +23584,28 @@ server.registerTool(
22699
23584
  server.registerTool(
22700
23585
  "ollama_manage",
22701
23586
  {
22702
- description: "Use when: pulling an approved tag or stopping a loaded model. Do not use when: deleting/recommending. Inputs: action/model; timeoutSeconds is pull-only. Returns: lifecycle result. Next: inspect models.",
22703
- inputSchema: {
22704
- action: external_exports.enum(["pull", "stop"]).describe('"pull" = disk, "stop" = memory'),
23587
+ description: "Use when: approved pull/stop. Do not use when: delete/discovery. Returns: receipt; timeoutSeconds is pull-only.",
23588
+ inputSchema: external_exports.object({
23589
+ action: external_exports.enum(["pull", "stop"]),
22705
23590
  model: external_exports.string().min(1),
22706
23591
  ollamaEndpoint: ollamaEndpointParam,
22707
- timeoutSeconds: external_exports.number().int().positive().optional().describe(`"pull" only. Defaults to ${DEFAULT_PULL_TIMEOUT_SECONDS}s.`)
22708
- },
23592
+ timeoutSeconds: external_exports.number().int().positive().optional()
23593
+ }).strict(),
22709
23594
  outputSchema: manageResultSchema,
22710
23595
  annotations: { destructiveHint: false }
22711
23596
  },
22712
- async ({ action, model, ollamaEndpoint, timeoutSeconds }) => {
23597
+ async ({ action, model, ollamaEndpoint, timeoutSeconds }, extra) => {
22713
23598
  try {
22714
23599
  if (action === "stop" && timeoutSeconds !== void 0) {
22715
23600
  return errorResult(new Error('`timeoutSeconds` is valid only for action "pull".'));
22716
23601
  }
22717
- const data = action === "pull" ? await ollamaPull(ollamaEndpoint, model, timeoutSeconds) : await ollamaFetch(ollamaEndpoint, "/api/generate", {
23602
+ const data = action === "pull" ? await ollamaPull(ollamaEndpoint, model, timeoutSeconds, {
23603
+ signal: extra.signal,
23604
+ onProgress: pullProgressReporter(extra)
23605
+ }) : await ollamaFetch(ollamaEndpoint, "/api/generate", {
22718
23606
  method: "POST",
22719
- body: { model, keep_alive: 0 }
23607
+ body: { model, keep_alive: 0 },
23608
+ signal: extra.signal
22720
23609
  });
22721
23610
  const payload = data;
22722
23611
  if (typeof payload.error === "string" && payload.error) {
@@ -22731,11 +23620,11 @@ server.registerTool(
22731
23620
  server.registerTool(
22732
23621
  "ollama_delete",
22733
23622
  {
22734
- description: "DESTRUCTIVE AND IRREVERSIBLE. Use when: a human requests one exact tag. Do not use when: freeing memory, cleaning by age, or inferring a tag. Inputs: exact tag. Returns: deleted tag. Next: refresh models.",
22735
- inputSchema: {
23623
+ description: "DESTRUCTIVE AND IRREVERSIBLE. Use when: human names exact tag. Do not use when: inferred cleanup. Returns: deleted tag.",
23624
+ inputSchema: external_exports.object({
22736
23625
  model: external_exports.string().min(1),
22737
23626
  ollamaEndpoint: ollamaEndpointParam
22738
- },
23627
+ }).strict(),
22739
23628
  outputSchema: deleteResultSchema,
22740
23629
  annotations: { destructiveHint: true }
22741
23630
  },
@@ -22743,7 +23632,7 @@ server.registerTool(
22743
23632
  try {
22744
23633
  await ollamaFetch(ollamaEndpoint, "/api/delete", {
22745
23634
  method: "DELETE",
22746
- body: { name: model }
23635
+ body: { model }
22747
23636
  });
22748
23637
  return structuredResult({ deleted: model });
22749
23638
  } catch (error2) {
@@ -22751,57 +23640,54 @@ server.registerTool(
22751
23640
  }
22752
23641
  }
22753
23642
  );
23643
+ async function routedResearchModel(endpoint, executionPreference, minPlacementEvidence) {
23644
+ const decision = JSON.parse(
23645
+ await route2(endpoint, "coding", "balanced", void 0, void 0, void 0, [], void 0, executionPreference, minPlacementEvidence)
23646
+ );
23647
+ if (typeof decision.selected_model !== "string" || !decision.selected_model) {
23648
+ throw new Error(
23649
+ "No research model: pass `model`, set FREELLAMA_MCP_DEFAULT_MODEL, or install a coding model (models{view:'installed'} shows what routing can use)."
23650
+ );
23651
+ }
23652
+ return decision.selected_model;
23653
+ }
22754
23654
  server.registerTool(
22755
23655
  "delegate_research",
22756
23656
  {
22757
- description: "Use when: a narrow lookup needs an allowed workspace. Do not use when: mutation, broad judgment, or outside-workspace facts are needed. Pages tool evidence instead of flooding caller context. Returns: answer, citations, evidence, verdict. Next: verify or escalate yourself.",
22758
- inputSchema: {
22759
- question: external_exports.string().min(1).describe("narrow and self-contained, answerable by reading files"),
22760
- workspacePath: external_exports.string().min(1).describe("directory inside FREELLAMA_MCP_ALLOWED_ROOTS (absolute preferred; relative is resolved)"),
22761
- adapter: external_exports.enum(["bash", "octocode"]).optional().describe('"bash" (default) beat "octocode" on every model measured, and is faster'),
22762
- model: external_exports.string().min(1).optional().describe("omit to use FREELLAMA_MCP_DEFAULT_MODEL; research accuracy collapses below ~12B"),
23657
+ description: "Use when: workspace lookup. Do not use when: mutation/external facts. Returns: citations/answer; discard verification.recommendation=escalate, otherwise verify citations.",
23658
+ inputSchema: external_exports.object({
23659
+ question: external_exports.string().min(1),
23660
+ workspacePath: external_exports.string().min(1),
23661
+ adapter: external_exports.enum(["bash", "octocode"]).optional(),
23662
+ model: external_exports.string().min(1).optional(),
22763
23663
  endpoint: endpointParam,
22764
23664
  executionPreference: executionPreferenceParam,
22765
23665
  minPlacementEvidence: minPlacementEvidenceParam,
22766
- legacyText: external_exports.boolean().optional().describe("false default: compact text cue; true: legacy serialized JSON text"),
23666
+ legacyText: external_exports.boolean().optional(),
22767
23667
  agent: external_exports.object({
22768
23668
  maxTurns: external_exports.number().int().positive().optional(),
22769
- contextTokens: external_exports.number().int().positive().optional().describe("Total agent context; input reserves outputTokens and safety margin."),
22770
- outputTokens: external_exports.number().int().positive().optional().describe("Maximum generated tokens per model call; reserved from context."),
23669
+ contextTokens: external_exports.number().int().positive().optional(),
23670
+ outputTokens: external_exports.number().int().positive().optional(),
22771
23671
  temperature: external_exports.number().nonnegative().optional(),
22772
23672
  seed: external_exports.number().int().nonnegative().optional(),
22773
23673
  think: external_exports.boolean().optional(),
22774
- keepAlive: external_exports.string().min(1).optional().describe('Ollama duration such as "5m", "0", or "-1"'),
23674
+ keepAlive: external_exports.string().min(1).optional(),
22775
23675
  requestTimeoutSeconds: external_exports.number().positive().optional(),
22776
- toolTimeoutSeconds: external_exports.number().positive().optional(),
22777
- retryAttempts: external_exports.number().int().positive().optional(),
22778
- retryBackoffSeconds: external_exports.number().nonnegative().optional(),
22779
- maxParseRepairs: external_exports.number().int().nonnegative().optional(),
22780
- parseRepairEchoChars: external_exports.number().int().positive().optional(),
22781
- context: external_exports.object({
22782
- charsPerToken: external_exports.number().positive().optional(),
22783
- safetyMarginTokens: external_exports.number().int().nonnegative().optional(),
22784
- imageTokenEstimate: external_exports.number().int().nonnegative().optional(),
22785
- keepRecent: external_exports.number().int().nonnegative().optional(),
22786
- compactPreviewChars: external_exports.number().int().positive().optional(),
22787
- compactRetainRatio: external_exports.number().positive().lt(1).optional(),
22788
- clipHeadRatio: external_exports.number().positive().lt(1).optional(),
22789
- observationPageChars: external_exports.number().int().positive().optional(),
22790
- pinnedOverflow: external_exports.enum(["error", "clip"]).optional()
22791
- }).optional()
22792
- }).optional().describe("Per-call agent budget, recovery, and compaction controls.")
22793
- },
23676
+ toolTimeoutSeconds: external_exports.number().positive().optional()
23677
+ }).strict().optional()
23678
+ }).strict(),
22794
23679
  outputSchema: researchResultSchema,
22795
23680
  annotations: { destructiveHint: false }
22796
23681
  },
22797
- async ({ question, workspacePath, adapter, model, endpoint, executionPreference, minPlacementEvidence, legacyText, agent }) => {
23682
+ async ({ question, workspacePath, adapter, model, endpoint, executionPreference, minPlacementEvidence, legacyText, agent }, extra) => {
22798
23683
  const chosenAdapter = adapter ?? DEFAULT_RESEARCH_ADAPTER;
22799
- const chosenModel = model ?? DEFAULT_DELEGATE_MODEL;
22800
23684
  let resolvedWorkspace;
23685
+ let chosenModel;
22801
23686
  try {
22802
23687
  resolvedWorkspace = await assertAllowedWorkspace(workspacePath);
23688
+ chosenModel = model ?? DEFAULT_DELEGATE_MODEL ?? await routedResearchModel(endpoint, executionPreference, minPlacementEvidence);
22803
23689
  } catch (error2) {
22804
- return errorResult(error2);
23690
+ return researchErrorResult(error2);
22805
23691
  }
22806
23692
  const known = MODEL_EVIDENCE[chosenModel];
22807
23693
  if (known?.grade === "unusable") {
@@ -22816,23 +23702,24 @@ server.registerTool(
22816
23702
  summary: `Refused before running: ${chosenModel} is measured unusable for research here (${known.note}). Re-run with a ~27B model (see README), or answer it yourself.`
22817
23703
  });
22818
23704
  }
22819
- const dir = await mkdtemp(path4.join(tmpdir(), "freellama-delegate-"));
22820
- const promptFile = path4.join(dir, "prompt.md");
22821
- const resultFile = path4.join(dir, "result.json");
23705
+ const dir = await mkdtemp(path5.join(tmpdir(), "freellama-delegate-"));
23706
+ const promptFile = path5.join(dir, "prompt.md");
23707
+ const resultFile = path5.join(dir, "result.json");
22822
23708
  try {
22823
23709
  await writeFile(promptFile, `${question}
22824
23710
  `, "utf8");
22825
23711
  const adapter2 = RESEARCH_ADAPTERS[chosenAdapter];
22826
- if (!existsSync4(adapter2)) {
22827
- return errorResult(
23712
+ if (!existsSync5(adapter2)) {
23713
+ return researchErrorResult(
22828
23714
  new Error(
22829
23715
  `research adapter not found at ${adapter2}. In a published install it should be bundled under <package>/adapters; in-repo it comes from benchmark/local/scripts. Reinstall, or run \`npm run build\` from packages/mcp/ to re-copy it.`
22830
23716
  )
22831
23717
  );
22832
23718
  }
22833
23719
  const running = execFileAsync("python3", [adapter2], {
23720
+ signal: extra.signal,
22834
23721
  env: {
22835
- ...process.env,
23722
+ ...delegateEnvironment(),
22836
23723
  FREELLAMA_TARGET_MODEL: chosenModel,
22837
23724
  FREELLAMA_AGENT_MANAGED_ENDPOINT: endpoint ?? DEFAULT_SERVE_ENDPOINT,
22838
23725
  FREELLAMA_AGENT_EXECUTION_PREFERENCE: executionPreference ?? "auto",
@@ -22849,20 +23736,7 @@ server.registerTool(
22849
23736
  ...agent?.think !== void 0 ? { FREELLAMA_AGENT_THINK: String(agent.think) } : {},
22850
23737
  ...agent?.keepAlive !== void 0 ? { FREELLAMA_AGENT_KEEP_ALIVE: agent.keepAlive } : {},
22851
23738
  ...agent?.requestTimeoutSeconds !== void 0 ? { FREELLAMA_AGENT_REQUEST_TIMEOUT_SECONDS: String(agent.requestTimeoutSeconds) } : {},
22852
- ...agent?.toolTimeoutSeconds !== void 0 ? { FREELLAMA_AGENT_TOOL_TIMEOUT_SECONDS: String(agent.toolTimeoutSeconds) } : {},
22853
- ...agent?.retryAttempts !== void 0 ? { FREELLAMA_AGENT_RETRY_ATTEMPTS: String(agent.retryAttempts) } : {},
22854
- ...agent?.retryBackoffSeconds !== void 0 ? { FREELLAMA_AGENT_RETRY_BACKOFF_SECONDS: String(agent.retryBackoffSeconds) } : {},
22855
- ...agent?.maxParseRepairs !== void 0 ? { FREELLAMA_AGENT_MAX_PARSE_REPAIRS: String(agent.maxParseRepairs) } : {},
22856
- ...agent?.parseRepairEchoChars !== void 0 ? { FREELLAMA_AGENT_PARSE_REPAIR_ECHO_CHARS: String(agent.parseRepairEchoChars) } : {},
22857
- ...agent?.context?.charsPerToken !== void 0 ? { FREELLAMA_AGENT_CHARS_PER_TOKEN: String(agent.context.charsPerToken) } : {},
22858
- ...agent?.context?.safetyMarginTokens !== void 0 ? { FREELLAMA_AGENT_SAFETY_MARGIN_TOKENS: String(agent.context.safetyMarginTokens) } : {},
22859
- ...agent?.context?.imageTokenEstimate !== void 0 ? { FREELLAMA_AGENT_IMAGE_TOKEN_ESTIMATE: String(agent.context.imageTokenEstimate) } : {},
22860
- ...agent?.context?.keepRecent !== void 0 ? { FREELLAMA_AGENT_KEEP_RECENT: String(agent.context.keepRecent) } : {},
22861
- ...agent?.context?.compactPreviewChars !== void 0 ? { FREELLAMA_AGENT_COMPACT_PREVIEW_CHARS: String(agent.context.compactPreviewChars) } : {},
22862
- ...agent?.context?.compactRetainRatio !== void 0 ? { FREELLAMA_AGENT_COMPACT_RETAIN_RATIO: String(agent.context.compactRetainRatio) } : {},
22863
- ...agent?.context?.clipHeadRatio !== void 0 ? { FREELLAMA_AGENT_CLIP_HEAD_RATIO: String(agent.context.clipHeadRatio) } : {},
22864
- ...agent?.context?.observationPageChars !== void 0 ? { FREELLAMA_AGENT_OBSERVATION_PAGE_CHARS: String(agent.context.observationPageChars) } : {},
22865
- ...agent?.context?.pinnedOverflow !== void 0 ? { FREELLAMA_AGENT_PINNED_OVERFLOW: agent.context.pinnedOverflow } : {}
23739
+ ...agent?.toolTimeoutSeconds !== void 0 ? { FREELLAMA_AGENT_TOOL_TIMEOUT_SECONDS: String(agent.toolTimeoutSeconds) } : {}
22866
23740
  },
22867
23741
  timeout: DEFAULT_DELEGATE_TIMEOUT_SECONDS * 1e3,
22868
23742
  // The answer is read from `resultFile`, never from stdout — but execFile's default 1 MB
@@ -22888,7 +23762,7 @@ server.registerTool(
22888
23762
  try {
22889
23763
  result = parseAdapterResult(await readFile(resultFile, "utf8"));
22890
23764
  } catch {
22891
- return errorResult(
23765
+ return researchErrorResult(
22892
23766
  adapterError ?? new Error(
22893
23767
  `research adapter exited without writing a readable result file \u2014 it was killed before it could report. Check that the model is loadable and that ${DEFAULT_DELEGATE_TIMEOUT_SECONDS}s is enough for this question.`
22894
23768
  )
@@ -22905,7 +23779,7 @@ server.registerTool(
22905
23779
  // lets a reader tell a run that read three files from one that failed three commands —
22906
23780
  // indistinguishable in the trail before, and graded identically.
22907
23781
  status: call.status ?? "ok",
22908
- path: target ? path4.relative(resolvedWorkspace, target) : detail ? extractExistingWorkspacePath(detail, resolvedWorkspace) : null,
23782
+ path: target ? path5.relative(resolvedWorkspace, target) : detail ? extractExistingWorkspacePath(detail, resolvedWorkspace) : null,
22909
23783
  detail,
22910
23784
  detail_truncated: rawDetail !== detail
22911
23785
  };
@@ -22915,21 +23789,16 @@ server.registerTool(
22915
23789
  const evidenceText = evidence.map(
22916
23790
  (step) => ` ${step.step}. ${step.tool}${step.status === "ok" ? "" : ` [${step.status}]`}${step.path ? ` -> ${step.path}` : ""}${step.detail ? `: ${clipText(step.detail, 400)}` : ""}`
22917
23791
  ).join("\n");
22918
- const citations = succeeded.map((step) => ({
22919
- step: step.step,
22920
- tool: step.tool,
22921
- path: step.path,
22922
- command: step.detail,
22923
- command_truncated: step.detail_truncated
22924
- }));
23792
+ const citations = succeeded.map((step) => ({ step: step.step, tool: step.tool, path: step.path }));
22925
23793
  if (adapterError) {
22926
- return errorResult(
22927
- new Error(
22928
- `research adapter failed: ${result.final_answer}` + (evidenceText ? `
23794
+ const diagnostic = `research adapter failed: ${result.final_answer}` + (evidenceText ? `
22929
23795
  Evidence collected before the failure:
22930
- ${evidenceText}` : "")
22931
- )
22932
- );
23796
+ ${evidenceText}` : "");
23797
+ const refusal = external_exports.object({ receipt: external_exports.object({ error: external_exports.string() }).passthrough() }).safeParse(result.model_metadata?.transport_error);
23798
+ if (refusal.success) {
23799
+ return researchErrorResult(new Error(JSON.stringify(refusal.data.receipt)), diagnostic);
23800
+ }
23801
+ return researchErrorResult(new Error(diagnostic));
22933
23802
  }
22934
23803
  const verification = assessDelegatedAnswer(question, succeeded.length, chosenModel);
22935
23804
  const summary = `Delegated answer ready: ${result.tool_calls.length} tool call(s)` + (failed > 0 ? `, ${failed} of which did not succeed` : "") + `; ${result.usage.input_tokens ?? "?"} input / ${result.usage.output_tokens ?? "?"} output local tokens; verification=${verification.recommendation}. Read structuredContent.answer and citations.`;
@@ -22959,9 +23828,15 @@ ${evidenceText}` : "")
22959
23828
  evidence,
22960
23829
  summary
22961
23830
  };
22962
- return structuredResult(payload, { legacyJson: legacyText === true });
23831
+ const cited = [...new Set(citations.map((citation) => citation.path).filter(Boolean))];
23832
+ const answerText = `${clipText(result.final_answer, ANSWER_TEXT_MAX_CHARS)}
23833
+
23834
+ verification: ${verification.recommendation}` + (cited.length ? `
23835
+ cited: ${cited.slice(0, 20).join(", ")}` : "") + `
23836
+ (${chosenModel}, ${result.tool_calls.length} tool call(s))`;
23837
+ return structuredResult(payload, legacyText === true ? { legacyJson: true } : { text: answerText });
22963
23838
  } catch (error2) {
22964
- return errorResult(error2);
23839
+ return researchErrorResult(error2);
22965
23840
  } finally {
22966
23841
  await rm(dir, { recursive: true, force: true });
22967
23842
  }