@mgsoftwarebv/mcp-server-bridge 3.5.18 → 3.5.20

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
@@ -154,8 +154,8 @@ var init_util = __esm({
154
154
  "set"
155
155
  ]);
156
156
  getParsedType3 = (data) => {
157
- const t8 = typeof data;
158
- switch (t8) {
157
+ const t9 = typeof data;
158
+ switch (t9) {
159
159
  case "undefined":
160
160
  return ZodParsedType2.undefined;
161
161
  case "string":
@@ -5501,7 +5501,7 @@ var require_dataType = __commonJS({
5501
5501
  exports2.coerceAndCheckDataType = coerceAndCheckDataType;
5502
5502
  var COERCIBLE = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]);
5503
5503
  function coerceToTypes(types5, coerceTypes) {
5504
- return coerceTypes ? types5.filter((t8) => COERCIBLE.has(t8) || coerceTypes === "array" && t8 === "array") : [];
5504
+ return coerceTypes ? types5.filter((t9) => COERCIBLE.has(t9) || coerceTypes === "array" && t9 === "array") : [];
5505
5505
  }
5506
5506
  function coerceData(it, types5, coerceTo) {
5507
5507
  const { gen, data, opts } = it;
@@ -5511,9 +5511,9 @@ var require_dataType = __commonJS({
5511
5511
  gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types5, data, opts.strictNumbers), () => gen.assign(coerced, data)));
5512
5512
  }
5513
5513
  gen.if((0, codegen_1._)`${coerced} !== undefined`);
5514
- for (const t8 of coerceTo) {
5515
- if (COERCIBLE.has(t8) || t8 === "array" && opts.coerceTypes === "array") {
5516
- coerceSpecificType(t8);
5514
+ for (const t9 of coerceTo) {
5515
+ if (COERCIBLE.has(t9) || t9 === "array" && opts.coerceTypes === "array") {
5516
+ coerceSpecificType(t9);
5517
5517
  }
5518
5518
  }
5519
5519
  gen.else();
@@ -5523,8 +5523,8 @@ var require_dataType = __commonJS({
5523
5523
  gen.assign(data, coerced);
5524
5524
  assignParentData(it, coerced);
5525
5525
  });
5526
- function coerceSpecificType(t8) {
5527
- switch (t8) {
5526
+ function coerceSpecificType(t9) {
5527
+ switch (t9) {
5528
5528
  case "string":
5529
5529
  gen.elseIf((0, codegen_1._)`${dataType} == "number" || ${dataType} == "boolean"`).assign(coerced, (0, codegen_1._)`"" + ${data}`).elseIf((0, codegen_1._)`${data} === null`).assign(coerced, (0, codegen_1._)`""`);
5530
5530
  return;
@@ -5596,8 +5596,8 @@ var require_dataType = __commonJS({
5596
5596
  }
5597
5597
  if (types5.number)
5598
5598
  delete types5.integer;
5599
- for (const t8 in types5)
5600
- cond = (0, codegen_1.and)(cond, checkDataType(t8, data, strictNums, correct));
5599
+ for (const t9 in types5)
5600
+ cond = (0, codegen_1.and)(cond, checkDataType(t9, data, strictNums, correct));
5601
5601
  return cond;
5602
5602
  }
5603
5603
  exports2.checkDataTypes = checkDataTypes;
@@ -6504,9 +6504,9 @@ var require_validate = __commonJS({
6504
6504
  it.dataTypes = types5;
6505
6505
  return;
6506
6506
  }
6507
- types5.forEach((t8) => {
6508
- if (!includesType(it.dataTypes, t8)) {
6509
- strictTypesError(it, `type "${t8}" not allowed by context "${it.dataTypes.join(",")}"`);
6507
+ types5.forEach((t9) => {
6508
+ if (!includesType(it.dataTypes, t9)) {
6509
+ strictTypesError(it, `type "${t9}" not allowed by context "${it.dataTypes.join(",")}"`);
6510
6510
  }
6511
6511
  });
6512
6512
  narrowSchemaTypes(it, types5);
@@ -6522,7 +6522,7 @@ var require_validate = __commonJS({
6522
6522
  const rule = rules[keyword];
6523
6523
  if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it.schema, rule)) {
6524
6524
  const { type } = rule.definition;
6525
- if (type.length && !type.some((t8) => hasApplicableType(ts, t8))) {
6525
+ if (type.length && !type.some((t9) => hasApplicableType(ts, t9))) {
6526
6526
  strictTypesError(it, `missing type "${type.join(",")}" for keyword "${keyword}"`);
6527
6527
  }
6528
6528
  }
@@ -6531,15 +6531,15 @@ var require_validate = __commonJS({
6531
6531
  function hasApplicableType(schTs, kwdT) {
6532
6532
  return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer");
6533
6533
  }
6534
- function includesType(ts, t8) {
6535
- return ts.includes(t8) || t8 === "integer" && ts.includes("number");
6534
+ function includesType(ts, t9) {
6535
+ return ts.includes(t9) || t9 === "integer" && ts.includes("number");
6536
6536
  }
6537
6537
  function narrowSchemaTypes(it, withTypes) {
6538
6538
  const ts = [];
6539
- for (const t8 of it.dataTypes) {
6540
- if (includesType(withTypes, t8))
6541
- ts.push(t8);
6542
- else if (withTypes.includes("integer") && t8 === "number")
6539
+ for (const t9 of it.dataTypes) {
6540
+ if (includesType(withTypes, t9))
6541
+ ts.push(t9);
6542
+ else if (withTypes.includes("integer") && t9 === "number")
6543
6543
  ts.push("integer");
6544
6544
  }
6545
6545
  it.dataTypes = ts;
@@ -8141,7 +8141,7 @@ var require_core = __commonJS({
8141
8141
  type: (0, dataType_1.getJSONTypes)(def.type),
8142
8142
  schemaType: (0, dataType_1.getJSONTypes)(def.schemaType)
8143
8143
  };
8144
- (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k6) => addRule.call(this, k6, definition) : (k6) => definition.type.forEach((t8) => addRule.call(this, k6, definition, t8)));
8144
+ (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k6) => addRule.call(this, k6, definition) : (k6) => definition.type.forEach((t9) => addRule.call(this, k6, definition, t9)));
8145
8145
  return this;
8146
8146
  }
8147
8147
  getKeyword(keyword) {
@@ -8340,7 +8340,7 @@ var require_core = __commonJS({
8340
8340
  if (dataType && post)
8341
8341
  throw new Error('keyword with "post" flag cannot have "type"');
8342
8342
  const { RULES } = this;
8343
- let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t8 }) => t8 === dataType);
8343
+ let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t9 }) => t9 === dataType);
8344
8344
  if (!ruleGroup) {
8345
8345
  ruleGroup = { type: dataType, rules: [] };
8346
8346
  RULES.rules.push(ruleGroup);
@@ -8866,7 +8866,7 @@ var require_uniqueItems = __commonJS({
8866
8866
  gen.if((0, codegen_1._)`${i6} > 1`, () => (canOptimize() ? loopN : loopN2)(i6, j6));
8867
8867
  }
8868
8868
  function canOptimize() {
8869
- return itemTypes.length > 0 && !itemTypes.some((t8) => t8 === "object" || t8 === "array");
8869
+ return itemTypes.length > 0 && !itemTypes.some((t9) => t9 === "object" || t9 === "array");
8870
8870
  }
8871
8871
  function loopN(i6, j6) {
8872
8872
  const item = gen.name("item");
@@ -11643,8 +11643,8 @@ function getLengthableOrigin2(input) {
11643
11643
  return "unknown";
11644
11644
  }
11645
11645
  function parsedType2(data) {
11646
- const t8 = typeof data;
11647
- switch (t8) {
11646
+ const t9 = typeof data;
11647
+ switch (t9) {
11648
11648
  case "number": {
11649
11649
  return Number.isNaN(data) ? "nan" : "number";
11650
11650
  }
@@ -11661,7 +11661,7 @@ function parsedType2(data) {
11661
11661
  }
11662
11662
  }
11663
11663
  }
11664
- return t8;
11664
+ return t9;
11665
11665
  }
11666
11666
  function issue2(...args2) {
11667
11667
  const [iss, input, inst] = args2;
@@ -11736,8 +11736,8 @@ var init_util2 = __esm({
11736
11736
  }
11737
11737
  });
11738
11738
  getParsedType4 = (data) => {
11739
- const t8 = typeof data;
11740
- switch (t8) {
11739
+ const t9 = typeof data;
11740
+ switch (t9) {
11741
11741
  case "undefined":
11742
11742
  return "undefined";
11743
11743
  case "string":
@@ -11776,7 +11776,7 @@ var init_util2 = __esm({
11776
11776
  }
11777
11777
  return "object";
11778
11778
  default:
11779
- throw new Error(`Unknown data type: ${t8}`);
11779
+ throw new Error(`Unknown data type: ${t9}`);
11780
11780
  }
11781
11781
  };
11782
11782
  propertyKeyTypes2 = /* @__PURE__ */ new Set(["string", "number", "symbol"]);
@@ -12891,12 +12891,12 @@ function handleCatchall2(proms, input, payload, ctx, def, inst) {
12891
12891
  const unrecognized = [];
12892
12892
  const keySet = def.keySet;
12893
12893
  const _catchall = def.catchall._zod;
12894
- const t8 = _catchall.def.type;
12894
+ const t9 = _catchall.def.type;
12895
12895
  const isOptionalOut = _catchall.optout === "optional";
12896
12896
  for (const key in input) {
12897
12897
  if (keySet.has(key))
12898
12898
  continue;
12899
- if (t8 === "never") {
12899
+ if (t9 === "never") {
12900
12900
  unrecognized.push(key);
12901
12901
  continue;
12902
12902
  }
@@ -16657,16 +16657,16 @@ var init_he = __esm({
16657
16657
  number: { unit: "", shortLabel: "\u05E7\u05D8\u05DF", longLabel: "\u05D2\u05D3\u05D5\u05DC" }
16658
16658
  // no unit
16659
16659
  };
16660
- const typeEntry = (t8) => t8 ? TypeNames[t8] : void 0;
16661
- const typeLabel = (t8) => {
16662
- const e6 = typeEntry(t8);
16660
+ const typeEntry = (t9) => t9 ? TypeNames[t9] : void 0;
16661
+ const typeLabel = (t9) => {
16662
+ const e6 = typeEntry(t9);
16663
16663
  if (e6)
16664
16664
  return e6.label;
16665
- return t8 ?? TypeNames.unknown.label;
16665
+ return t9 ?? TypeNames.unknown.label;
16666
16666
  };
16667
- const withDefinite = (t8) => `\u05D4${typeLabel(t8)}`;
16668
- const verbFor = (t8) => {
16669
- const e6 = typeEntry(t8);
16667
+ const withDefinite = (t9) => `\u05D4${typeLabel(t9)}`;
16668
+ const verbFor = (t9) => {
16669
+ const e6 = typeEntry(t9);
16670
16670
  const gender = e6?.gender ?? "m";
16671
16671
  return gender === "f" ? "\u05E6\u05E8\u05D9\u05DB\u05D4 \u05DC\u05D4\u05D9\u05D5\u05EA" : "\u05E6\u05E8\u05D9\u05DA \u05DC\u05D4\u05D9\u05D5\u05EA";
16672
16672
  };
@@ -24658,8 +24658,8 @@ function convertBaseSchema(schema, ctx) {
24658
24658
  }
24659
24659
  const type = schema.type;
24660
24660
  if (Array.isArray(type)) {
24661
- const typeSchemas = type.map((t8) => {
24662
- const typeSchema = { ...schema, type: t8 };
24661
+ const typeSchemas = type.map((t9) => {
24662
+ const typeSchema = { ...schema, type: t9 };
24663
24663
  return convertBaseSchema(typeSchema, ctx);
24664
24664
  });
24665
24665
  if (typeSchemas.length === 0) {
@@ -53092,9 +53092,9 @@ var init_schemaDeserializationMiddleware = __esm({
53092
53092
  schemaDeserializationMiddleware = (config3) => (next, context2) => async (args2) => {
53093
53093
  const { response } = await next(args2);
53094
53094
  const { operationSchema } = getSmithyContext(context2);
53095
- const [, ns, n3, t8, i6, o3] = operationSchema ?? [];
53095
+ const [, ns, n3, t9, i6, o3] = operationSchema ?? [];
53096
53096
  try {
53097
- const parsed = await config3.protocol.deserializeResponse(operation(ns, n3, t8, i6, o3), {
53097
+ const parsed = await config3.protocol.deserializeResponse(operation(ns, n3, t9, i6, o3), {
53098
53098
  ...config3,
53099
53099
  ...context2
53100
53100
  }, response);
@@ -53159,9 +53159,9 @@ var init_schemaSerializationMiddleware = __esm({
53159
53159
  init_operation();
53160
53160
  schemaSerializationMiddleware = (config3) => (next, context2) => async (args2) => {
53161
53161
  const { operationSchema } = getSmithyContext(context2);
53162
- const [, ns, n3, t8, i6, o3] = operationSchema ?? [];
53162
+ const [, ns, n3, t9, i6, o3] = operationSchema ?? [];
53163
53163
  const endpoint2 = context2.endpointV2 ? async () => toEndpointV1(context2.endpointV2) : config3.endpoint;
53164
- const request3 = await config3.protocol.serializeRequest(operation(ns, n3, t8, i6, o3), args2.input, {
53164
+ const request3 = await config3.protocol.serializeRequest(operation(ns, n3, t9, i6, o3), args2.input, {
53165
53165
  ...config3,
53166
53166
  ...context2,
53167
53167
  endpoint: endpoint2
@@ -56495,9 +56495,9 @@ function __awaiter(thisArg, _arguments, P2, generator) {
56495
56495
  }
56496
56496
  function __generator(thisArg, body) {
56497
56497
  var _ = { label: 0, sent: function() {
56498
- if (t8[0] & 1) throw t8[1];
56499
- return t8[1];
56500
- }, trys: [], ops: [] }, f6, y2, t8, g6 = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
56498
+ if (t9[0] & 1) throw t9[1];
56499
+ return t9[1];
56500
+ }, trys: [], ops: [] }, f6, y2, t9, g6 = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
56501
56501
  return g6.next = verb(0), g6["throw"] = verb(1), g6["return"] = verb(2), typeof Symbol === "function" && (g6[Symbol.iterator] = function() {
56502
56502
  return this;
56503
56503
  }), g6;
@@ -56509,12 +56509,12 @@ function __generator(thisArg, body) {
56509
56509
  function step(op) {
56510
56510
  if (f6) throw new TypeError("Generator is already executing.");
56511
56511
  while (g6 && (g6 = 0, op[0] && (_ = 0)), _) try {
56512
- if (f6 = 1, y2 && (t8 = op[0] & 2 ? y2["return"] : op[0] ? y2["throw"] || ((t8 = y2["return"]) && t8.call(y2), 0) : y2.next) && !(t8 = t8.call(y2, op[1])).done) return t8;
56513
- if (y2 = 0, t8) op = [op[0] & 2, t8.value];
56512
+ if (f6 = 1, y2 && (t9 = op[0] & 2 ? y2["return"] : op[0] ? y2["throw"] || ((t9 = y2["return"]) && t9.call(y2), 0) : y2.next) && !(t9 = t9.call(y2, op[1])).done) return t9;
56513
+ if (y2 = 0, t9) op = [op[0] & 2, t9.value];
56514
56514
  switch (op[0]) {
56515
56515
  case 0:
56516
56516
  case 1:
56517
- t8 = op;
56517
+ t9 = op;
56518
56518
  break;
56519
56519
  case 4:
56520
56520
  _.label++;
@@ -56529,25 +56529,25 @@ function __generator(thisArg, body) {
56529
56529
  _.trys.pop();
56530
56530
  continue;
56531
56531
  default:
56532
- if (!(t8 = _.trys, t8 = t8.length > 0 && t8[t8.length - 1]) && (op[0] === 6 || op[0] === 2)) {
56532
+ if (!(t9 = _.trys, t9 = t9.length > 0 && t9[t9.length - 1]) && (op[0] === 6 || op[0] === 2)) {
56533
56533
  _ = 0;
56534
56534
  continue;
56535
56535
  }
56536
- if (op[0] === 3 && (!t8 || op[1] > t8[0] && op[1] < t8[3])) {
56536
+ if (op[0] === 3 && (!t9 || op[1] > t9[0] && op[1] < t9[3])) {
56537
56537
  _.label = op[1];
56538
56538
  break;
56539
56539
  }
56540
- if (op[0] === 6 && _.label < t8[1]) {
56541
- _.label = t8[1];
56542
- t8 = op;
56540
+ if (op[0] === 6 && _.label < t9[1]) {
56541
+ _.label = t9[1];
56542
+ t9 = op;
56543
56543
  break;
56544
56544
  }
56545
- if (t8 && _.label < t8[2]) {
56546
- _.label = t8[2];
56545
+ if (t9 && _.label < t9[2]) {
56546
+ _.label = t9[2];
56547
56547
  _.ops.push(op);
56548
56548
  break;
56549
56549
  }
56550
- if (t8[2]) _.ops.pop();
56550
+ if (t9[2]) _.ops.pop();
56551
56551
  _.trys.pop();
56552
56552
  continue;
56553
56553
  }
@@ -56556,7 +56556,7 @@ function __generator(thisArg, body) {
56556
56556
  op = [6, e6];
56557
56557
  y2 = 0;
56558
56558
  } finally {
56559
- f6 = t8 = 0;
56559
+ f6 = t9 = 0;
56560
56560
  }
56561
56561
  if (op[0] & 5) throw op[1];
56562
56562
  return { value: op[0] ? op[1] : void 0, done: true };
@@ -59404,8 +59404,8 @@ var init_DefaultRateLimiter = __esm({
59404
59404
  this.availableTokens = Math.min(this.availableTokens, this.maxCapacity);
59405
59405
  }
59406
59406
  updateMeasuredRate() {
59407
- const t8 = this.getCurrentTimeInSeconds();
59408
- const timeBucket = Math.floor(t8 * 2) / 2;
59407
+ const t9 = this.getCurrentTimeInSeconds();
59408
+ const timeBucket = Math.floor(t9 * 2) / 2;
59409
59409
  this.requestCount++;
59410
59410
  if (timeBucket > this.lastTxRateBucket) {
59411
59411
  const currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket);
@@ -61489,9 +61489,9 @@ var init_JsonShapeDeserializer = __esm({
61489
61489
  } else if (typeof record3.__type === "string") {
61490
61490
  for (const k6 in record3) {
61491
61491
  const v2 = record3[k6];
61492
- const t8 = jsonName ? nameMap[k6] ?? k6 : k6;
61493
- if (!(t8 in out)) {
61494
- out[t8] = v2;
61492
+ const t9 = jsonName ? nameMap[k6] ?? k6 : k6;
61493
+ if (!(t9 in out)) {
61494
+ out[t9] = v2;
61495
61495
  }
61496
61496
  }
61497
61497
  }
@@ -62293,7 +62293,7 @@ function validate2(xmlData, options) {
62293
62293
  } else if (tags2.length == 1) {
62294
62294
  return getErrorObject("InvalidTag", "Unclosed tag '" + tags2[0].tagName + "'.", getLineNumberForPosition(xmlData, tags2[0].tagStartPos));
62295
62295
  } else if (tags2.length > 0) {
62296
- return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags2.map((t8) => t8.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 });
62296
+ return getErrorObject("InvalidXml", "Invalid '" + JSON.stringify(tags2.map((t9) => t9.tagName), null, 4).replace(/\r?\n/g, "") + "' found.", { line: 1, col: 1 });
62297
62297
  }
62298
62298
  return true;
62299
62299
  }
@@ -75087,8 +75087,8 @@ function numKeys(data) {
75087
75087
  return keyCount;
75088
75088
  }
75089
75089
  var getParsedType = (data) => {
75090
- const t8 = typeof data;
75091
- switch (t8) {
75090
+ const t9 = typeof data;
75091
+ switch (t9) {
75092
75092
  case "undefined":
75093
75093
  return "undefined";
75094
75094
  case "string":
@@ -75127,7 +75127,7 @@ var getParsedType = (data) => {
75127
75127
  }
75128
75128
  return "object";
75129
75129
  default:
75130
- throw new Error(`Unknown data type: ${t8}`);
75130
+ throw new Error(`Unknown data type: ${t9}`);
75131
75131
  }
75132
75132
  };
75133
75133
  var propertyKeyTypes = /* @__PURE__ */ new Set(["string", "number", "symbol"]);
@@ -75434,8 +75434,8 @@ function getLengthableOrigin(input) {
75434
75434
  return "unknown";
75435
75435
  }
75436
75436
  function parsedType(data) {
75437
- const t8 = typeof data;
75438
- switch (t8) {
75437
+ const t9 = typeof data;
75438
+ switch (t9) {
75439
75439
  case "number": {
75440
75440
  return Number.isNaN(data) ? "nan" : "number";
75441
75441
  }
@@ -75452,7 +75452,7 @@ function parsedType(data) {
75452
75452
  }
75453
75453
  }
75454
75454
  }
75455
- return t8;
75455
+ return t9;
75456
75456
  }
75457
75457
  function issue(...args2) {
75458
75458
  const [iss, input, inst] = args2;
@@ -77084,12 +77084,12 @@ function handleCatchall(proms, input, payload, ctx, def, inst) {
77084
77084
  const unrecognized = [];
77085
77085
  const keySet = def.keySet;
77086
77086
  const _catchall = def.catchall._zod;
77087
- const t8 = _catchall.def.type;
77087
+ const t9 = _catchall.def.type;
77088
77088
  const isOptionalOut = _catchall.optout === "optional";
77089
77089
  for (const key in input) {
77090
77090
  if (keySet.has(key))
77091
77091
  continue;
77092
- if (t8 === "never") {
77092
+ if (t9 === "never") {
77093
77093
  unrecognized.push(key);
77094
77094
  continue;
77095
77095
  }
@@ -92511,6 +92511,7 @@ __export(schema_exports, {
92511
92511
  ticketComments: () => ticketComments,
92512
92512
  ticketCommentsRelations: () => ticketCommentsRelations,
92513
92513
  ticketEmbeddings: () => ticketEmbeddings,
92514
+ ticketFocusStateEnum: () => ticketFocusStateEnum,
92514
92515
  ticketOriginEnum: () => ticketOriginEnum,
92515
92516
  ticketPriorityEnum: () => ticketPriorityEnum,
92516
92517
  ticketRelations: () => ticketRelations,
@@ -92676,6 +92677,10 @@ var ticketPriorityEnum = pgEnum("ticket_priority", [
92676
92677
  "high",
92677
92678
  "critical"
92678
92679
  ]);
92680
+ var ticketFocusStateEnum = pgEnum("ticket_focus_state", [
92681
+ "today",
92682
+ "week"
92683
+ ]);
92679
92684
  var ticketTypeEnum = pgEnum("ticket_type", [
92680
92685
  "task",
92681
92686
  "bug",
@@ -93087,6 +93092,16 @@ var tickets = pgTable(
93087
93092
  type: ticketTypeEnum().default("task").notNull(),
93088
93093
  // Manual sort order within status columns (for kanban board)
93089
93094
  sequence: numeric({ mode: "number" }),
93095
+ // Cross-project personal work queue: manual rank within the current
93096
+ // assignee's queue (midpoint ordering, reset to null on reassignment).
93097
+ queueRank: doublePrecision("queue_rank"),
93098
+ // Teamlead "focus list" marker (today/week). null = not on a focus list.
93099
+ focusState: ticketFocusStateEnum("focus_state"),
93100
+ focusSetBy: uuid3("focus_set_by"),
93101
+ focusSetAt: timestamp("focus_set_at", {
93102
+ withTimezone: true,
93103
+ mode: "string"
93104
+ }),
93090
93105
  // Relationships
93091
93106
  teamId: uuid3("team_id").notNull(),
93092
93107
  projectId: uuid3("project_id"),
@@ -93190,6 +93205,11 @@ var tickets = pgTable(
93190
93205
  index("idx_tickets_priority").on(table.priority),
93191
93206
  index("idx_tickets_created_at").using("btree", table.createdAt.desc()),
93192
93207
  index("idx_tickets_due_date").on(table.teamId, table.dueDate),
93208
+ index("idx_tickets_assignee_queue_rank").on(
93209
+ table.assigneeId,
93210
+ table.queueRank
93211
+ ),
93212
+ index("idx_tickets_assignee_focus").on(table.assigneeId, table.focusState),
93193
93213
  index("idx_tickets_fts").using(
93194
93214
  "gin",
93195
93215
  table.fts.asc().nullsLast().op("tsvector_ops")
@@ -93237,6 +93257,11 @@ var tickets = pgTable(
93237
93257
  foreignColumns: [users.id],
93238
93258
  name: "tickets_updated_by_fkey"
93239
93259
  }).onDelete("set null"),
93260
+ foreignKey({
93261
+ columns: [table.focusSetBy],
93262
+ foreignColumns: [users.id],
93263
+ name: "tickets_focus_set_by_fkey"
93264
+ }).onDelete("set null"),
93240
93265
  foreignKey({
93241
93266
  columns: [table.versionTagId],
93242
93267
  foreignColumns: [versionTags.id],
@@ -105793,15 +105818,36 @@ var TOOLS = [
105793
105818
  "review",
105794
105819
  "resolved",
105795
105820
  "closed",
105796
- "backlog"
105821
+ "backlog",
105822
+ "blocked"
105797
105823
  ]
105798
105824
  },
105825
+ statuses: {
105826
+ type: "array",
105827
+ items: {
105828
+ type: "string",
105829
+ enum: [
105830
+ "open",
105831
+ "in_progress",
105832
+ "review",
105833
+ "resolved",
105834
+ "closed",
105835
+ "backlog",
105836
+ "blocked"
105837
+ ]
105838
+ },
105839
+ description: "Filter by multiple statuses (OR). Takes precedence over the single `status`."
105840
+ },
105799
105841
  priority: {
105800
105842
  type: "string",
105801
105843
  enum: ["low", "medium", "high", "critical"]
105802
105844
  },
105803
105845
  projectId: { type: "string" },
105804
105846
  customerId: { type: "string" },
105847
+ assigneeId: {
105848
+ type: "string",
105849
+ description: "Filter by assignee: a user UUID, or 'me' for the API key user."
105850
+ },
105805
105851
  q: {
105806
105852
  type: "string",
105807
105853
  description: "Search query for ticket number, title, or description"
@@ -105825,6 +105871,167 @@ var TOOLS = [
105825
105871
  required: []
105826
105872
  }
105827
105873
  },
105874
+ {
105875
+ name: "get-my-work-queue",
105876
+ description: "Get the API key user's personal cross-project work queue: every ticket assigned to them across all accessible projects, in 'what should I do now?' order. Each item has a derived workState (now = open/in_progress, waiting = review, later = backlog, blocked = blocked) plus ticketNumber, title, project, customer, status, priority, deadline (dueDate), queueRank, focusState and blockedReason. Ordered by focus list (today/week first), manual queueRank, priority, deadline, then last update. Use this to answer 'what's on my plate?'.",
105877
+ inputSchema: {
105878
+ type: "object",
105879
+ properties: {
105880
+ teamId: teamIdProp,
105881
+ projectId: { type: "string", description: "Filter to one project UUID." },
105882
+ priority: {
105883
+ type: "string",
105884
+ enum: ["low", "medium", "high", "critical"]
105885
+ },
105886
+ workStates: {
105887
+ type: "array",
105888
+ items: {
105889
+ type: "string",
105890
+ enum: ["now", "waiting", "later", "blocked", "done"]
105891
+ },
105892
+ description: "Filter by derived work state(s). Omit for all active work (excludes done)."
105893
+ },
105894
+ statuses: {
105895
+ type: "array",
105896
+ items: { type: "string" },
105897
+ description: "Explicit ticket status filter (wins over workStates)."
105898
+ },
105899
+ pageSize: { type: "number", default: 200, maximum: 500 }
105900
+ },
105901
+ required: []
105902
+ }
105903
+ },
105904
+ {
105905
+ name: "get-assignee-work-queue",
105906
+ description: "Get the cross-project work queue for a specific assignee, or for everyone. Teamlead-oriented: pass assigneeId as a user UUID, 'me' (default), or 'all' for the whole team. With assigneeId='all' and groupByAssignee=true the result is grouped per developer. Same item shape and ordering as get-my-work-queue. Use get-team-workload-overview first for high-level counts.",
105907
+ inputSchema: {
105908
+ type: "object",
105909
+ properties: {
105910
+ teamId: teamIdProp,
105911
+ assigneeId: {
105912
+ type: "string",
105913
+ description: "Whose queue: a user UUID, 'me' (default), or 'all' for every assignee."
105914
+ },
105915
+ groupByAssignee: {
105916
+ type: "boolean",
105917
+ description: "When assigneeId='all', group the items per assignee."
105918
+ },
105919
+ projectId: { type: "string" },
105920
+ priority: {
105921
+ type: "string",
105922
+ enum: ["low", "medium", "high", "critical"]
105923
+ },
105924
+ workStates: {
105925
+ type: "array",
105926
+ items: {
105927
+ type: "string",
105928
+ enum: ["now", "waiting", "later", "blocked", "done"]
105929
+ }
105930
+ },
105931
+ statuses: { type: "array", items: { type: "string" } },
105932
+ pageSize: { type: "number", default: 200, maximum: 500 }
105933
+ },
105934
+ required: []
105935
+ }
105936
+ },
105937
+ {
105938
+ name: "get-team-workload-overview",
105939
+ description: "Teamlead overview of open work per assignee across all accessible projects: for each developer (and optionally the unassigned bucket) the counts of total/now/waiting/later/blocked tickets, plus high-priority, stale (not updated within staleDays) and upcoming-deadline (within upcomingDays) counts. Use this to see who is overloaded or has stale/at-risk work, then drill in with get-assignee-work-queue.",
105940
+ inputSchema: {
105941
+ type: "object",
105942
+ properties: {
105943
+ teamId: teamIdProp,
105944
+ staleDays: {
105945
+ type: "number",
105946
+ default: 7,
105947
+ description: "Tickets not updated within this many days count as stale."
105948
+ },
105949
+ upcomingDays: {
105950
+ type: "number",
105951
+ default: 7,
105952
+ description: "Deadlines within this many days count as upcoming."
105953
+ },
105954
+ includeUnassigned: {
105955
+ type: "boolean",
105956
+ default: true,
105957
+ description: "Include the unassigned bucket in the overview."
105958
+ }
105959
+ },
105960
+ required: []
105961
+ }
105962
+ },
105963
+ {
105964
+ name: "set-assignee-queue-order",
105965
+ description: "Reorder a ticket within its assignee's personal cross-project work queue (manual prioritisation / drag-and-drop equivalent). Provide the ticketId and the target position (0 = top of the queue). Recomputes the ticket's queueRank via midpoint ordering. Permission: the API key user must be the ticket's assignee or an owner of the ticket's team. Alias: update-ticket-rank.",
105966
+ inputSchema: {
105967
+ type: "object",
105968
+ properties: {
105969
+ teamId: teamIdProp,
105970
+ ticketId: ticketIdentifierProp,
105971
+ position: {
105972
+ type: "number",
105973
+ description: "Target index in the assignee's ordered queue (0 = top)."
105974
+ }
105975
+ },
105976
+ required: ["ticketId", "position"]
105977
+ }
105978
+ },
105979
+ {
105980
+ name: "update-ticket-rank",
105981
+ description: "Alias of set-assignee-queue-order: reorder a ticket within its assignee's personal cross-project work queue by setting its target position (0 = top). Recomputes queueRank via midpoint ordering. Permission: assignee or team owner.",
105982
+ inputSchema: {
105983
+ type: "object",
105984
+ properties: {
105985
+ teamId: teamIdProp,
105986
+ ticketId: ticketIdentifierProp,
105987
+ position: {
105988
+ type: "number",
105989
+ description: "Target index in the assignee's ordered queue (0 = top)."
105990
+ }
105991
+ },
105992
+ required: ["ticketId", "position"]
105993
+ }
105994
+ },
105995
+ {
105996
+ name: "update-ticket-work-state",
105997
+ description: "Set a ticket's work state, which maps to its status: now -> in_progress, waiting -> review, later -> backlog, blocked -> blocked. When moving to blocked you may pass a blockedReason (stored on the ticket metadata). Permission: the API key user must be the ticket's assignee or an owner of the ticket's team.",
105998
+ inputSchema: {
105999
+ type: "object",
106000
+ properties: {
106001
+ teamId: teamIdProp,
106002
+ ticketId: ticketIdentifierProp,
106003
+ workState: {
106004
+ type: "string",
106005
+ enum: ["now", "waiting", "later", "blocked"]
106006
+ },
106007
+ blockedReason: {
106008
+ type: "string",
106009
+ description: "Reason stored when workState is 'blocked'."
106010
+ }
106011
+ },
106012
+ required: ["ticketId", "workState"]
106013
+ }
106014
+ },
106015
+ {
106016
+ name: "mark-ticket-blocked",
106017
+ description: "Mark a ticket as blocked / waiting on input (sets status=blocked and stores the reason on the ticket metadata), or clear it with unblock=true (sets status=in_progress). Permission: the API key user must be the ticket's assignee or an owner of the ticket's team.",
106018
+ inputSchema: {
106019
+ type: "object",
106020
+ properties: {
106021
+ teamId: teamIdProp,
106022
+ ticketId: ticketIdentifierProp,
106023
+ reason: {
106024
+ type: "string",
106025
+ description: "Why the ticket is blocked / what it is waiting on."
106026
+ },
106027
+ unblock: {
106028
+ type: "boolean",
106029
+ description: "When true, clears the blocked state back to in_progress."
106030
+ }
106031
+ },
106032
+ required: ["ticketId"]
106033
+ }
106034
+ },
105828
106035
  {
105829
106036
  name: "get-ticket-by-id",
105830
106037
  description: "Get a specific ticket by its ID or ticket number, including tags, comment text and a full attachment listing (with ids). Images from ticket and comment attachments are downloaded and returned inline as base64. For non-image attachments, call get-ticket-attachment with the listed id to get a download URL.",
@@ -106610,7 +106817,7 @@ var TOOLS = [
106610
106817
  },
106611
106818
  {
106612
106819
  name: "create-document",
106613
- description: 'Create a document (proposal, report, deliverables/opleverdocument, ...) from an array of content blocks. Use type \'deliverables\' for a document describing what was delivered for a customer; optionally pass `invoiceId` to link it to an invoice so it can be sent along as a PDF attachment. Each block is `{ id?, type, data }` (ids are auto-generated when omitted). Supported block types and their `data` shapes:\n- cover: { title, subtitle?, showLogo: bool, showDate: bool, clientName?, clientCompany?, date? }\n- toc: { title, maxDepth: 1-6 }\n- heading: { text, level: 1|2|3, numbered: bool }\n- text: { content: TipTapDoc | plain string (auto-converted; supports paragraphs and "- " bullet lists) }\n- callout: { variant: "info"|"warning"|"tip"|"important", title?, content: TipTapDoc | string }\n- table: { title?, headers: string[], rows: string[][] }\n- chart: { title?, chartType: "bar"|"line"|"pie", points: [{label, value}], seriesName?, series?: [{name, color?, points: [{label, value}]}], unit? ("\u20AC"|"%"|...), showValues: bool, color? }. Multi-series (bar/line only): extra series values are matched to the primary points by index; a legend is rendered automatically.\n- pricing: { title?, items: [{description, quantity?, unit?, price}], currency: "EUR", showTotal: bool, vatRate?, includeVat: bool }\n- timeline: { title?, phases: [{name, description?, duration, deliverables?: string[]}] }\n- two_column: { left: TipTapDoc|string, right: TipTapDoc|string, ratio?: "50_50"|"60_40"|"40_60"|"70_30"|"30_70" }\n- divider: { style: "page_break"|"line"|"space" }\n- signature: { title?, description?, signers: [{id, label, name?, company?, role?}] }\nAll text content runs through a humanizer that strips AI-sounding patterns (em-dashes, clich\xE9s, filler phrases); control it with `humanize`. The result includes ready-to-use links (internal dashboard editor URL + PDF status). Use get-document-link afterwards for a PDF download link or to publish a public customer share link.',
106820
+ description: 'Create a document (proposal, report, deliverables/opleverdocument, ...) from an array of content blocks. Use type \'deliverables\' for a document describing what was delivered for a customer; optionally pass `invoiceId` to link it to an invoice so it can be sent along as a PDF attachment. Each block is `{ id?, type, data }` (ids are auto-generated when omitted). Supported block types and their `data` shapes:\n- cover: { title, subtitle?, showLogo: bool, showDate: bool, clientName?, clientCompany?, date? }\n- toc: { title, maxDepth: 1-6 }\n- heading: { text, level: 1|2|3, numbered: bool }\n- text: { content: TipTapDoc | plain string (auto-converted; supports paragraphs and "- " bullet lists) }\n- callout: { variant: "info"|"warning"|"tip"|"important", title?, content: TipTapDoc | string }\n- table: { title?, headers: string[], rows: string[][] }\n- chart: { title?, chartType: "bar"|"line"|"pie", points: [{label, value}], seriesName?, series?: [{name, color?, points: [{label, value}]}], unit? ("\u20AC"|"%"|...), showValues: bool, color? }. Multi-series (bar/line only): extra series values are matched to the primary points by index; a legend is rendered automatically.\n- pricing: { title?, items: [{description, quantity?, unit?, price}], currency: "EUR", showTotal: bool, vatRate?, includeVat: bool }\n- timeline: { title?, phases: [{name, description?, duration, deliverables?: string[]}] }\n- image: { url, alt?, caption?, width? }. `url` is either a publicly reachable http(s) URL or an inline `data:image/png;base64,...` URI (png/jpeg/gif/svg) \u2014 the PDF compiler downloads/decodes and embeds it, so data URIs make the document fully self-contained (auth-gated URLs are skipped). `width` is a Typst length, e.g. "100%" (default), "80%" or "300pt".\n- team: { title?, members: [{name, role, bio?, imageUrl?}] }. `imageUrl` follows the same public-URL rule as image blocks.\n- two_column: { left: TipTapDoc|string, right: TipTapDoc|string, ratio?: "50_50"|"60_40"|"40_60"|"70_30"|"30_70" }\n- divider: { style: "page_break"|"line"|"space" }\n- signature: { title?, description?, signers: [{id, label, name?, company?, role?}] }\nAll text content runs through a humanizer that strips AI-sounding patterns (em-dashes, clich\xE9s, filler phrases); control it with `humanize`. The result includes ready-to-use links (internal dashboard editor URL + PDF status). Use get-document-link afterwards for a PDF download link or to publish a public customer share link.',
106614
106821
  inputSchema: {
106615
106822
  type: "object",
106616
106823
  properties: {
@@ -107921,7 +108128,7 @@ var RESOURCES = [
107921
108128
 
107922
108129
  // src/tools/team-resolution.ts
107923
108130
  function teamSelectionResponse(teams2) {
107924
- const list = teams2.map((t8) => `- ${t8.name ?? "(unnamed provider)"} (teamId: ${t8.id})`).join("\n");
108131
+ const list = teams2.map((t9) => `- ${t9.name ?? "(unnamed provider)"} (teamId: ${t9.id})`).join("\n");
107925
108132
  return {
107926
108133
  content: [
107927
108134
  {
@@ -108002,7 +108209,7 @@ function notFoundResponse(identifier) {
108002
108209
  };
108003
108210
  }
108004
108211
  function multipleMatchesResponse(identifier, matches) {
108005
- const list = matches.map((t8) => `- **${t8.ticketNumber}** (ID: ${t8.id}): ${t8.title}`).join("\n");
108212
+ const list = matches.map((t9) => `- **${t9.ticketNumber}** (ID: ${t9.id}): ${t9.title}`).join("\n");
108006
108213
  return {
108007
108214
  content: [
108008
108215
  {
@@ -110277,7 +110484,7 @@ function blockTypeSummary(blocks) {
110277
110484
  for (const block of blocks) {
110278
110485
  counts.set(block.type, (counts.get(block.type) ?? 0) + 1);
110279
110486
  }
110280
- return [...counts.entries()].map(([t8, n3]) => `${t8}\xD7${n3}`).join(", ");
110487
+ return [...counts.entries()].map(([t9, n3]) => `${t9}\xD7${n3}`).join(", ");
110281
110488
  }
110282
110489
  async function findAccessibleDocument(id, teamIds) {
110283
110490
  if (teamIds.length === 0) return null;
@@ -115845,17 +116052,17 @@ function computeInvoiceTotals(items, defaults, discount = 0) {
115845
116052
  };
115846
116053
  }
115847
116054
  function templateDefaultsFromInvoice(template, currency) {
115848
- const t8 = template ?? {};
116055
+ const t9 = template ?? {};
115849
116056
  return {
115850
- currency: t8.currency || currency || "EUR",
115851
- vatRate: t8.vatRate ?? 21,
115852
- taxRate: t8.taxRate ?? 0,
115853
- includeVat: t8.includeVat ?? true,
115854
- includeTax: t8.includeTax ?? false,
115855
- includeDiscount: t8.includeDiscount ?? false,
115856
- includeDecimals: t8.includeDecimals ?? true,
115857
- includeUnits: t8.includeUnits ?? true,
115858
- raw: t8
116057
+ currency: t9.currency || currency || "EUR",
116058
+ vatRate: t9.vatRate ?? 21,
116059
+ taxRate: t9.taxRate ?? 0,
116060
+ includeVat: t9.includeVat ?? true,
116061
+ includeTax: t9.includeTax ?? false,
116062
+ includeDiscount: t9.includeDiscount ?? false,
116063
+ includeDecimals: t9.includeDecimals ?? true,
116064
+ includeUnits: t9.includeUnits ?? true,
116065
+ raw: t9
115859
116066
  };
115860
116067
  }
115861
116068
  function plainTextFromLineItemName(name21) {
@@ -124108,7 +124315,7 @@ function quoteBlockId() {
124108
124315
  function paragraphsDoc(...texts) {
124109
124316
  return {
124110
124317
  type: "doc",
124111
- content: texts.filter((t8) => t8.trim().length > 0).map((text3) => ({
124318
+ content: texts.filter((t9) => t9.trim().length > 0).map((text3) => ({
124112
124319
  type: "paragraph",
124113
124320
  content: [{ type: "text", text: text3 }]
124114
124321
  }))
@@ -124223,21 +124430,21 @@ function applyLineItemsToBlocks(blocks, items, defaults, opts) {
124223
124430
  return next;
124224
124431
  }
124225
124432
  function templateDefaultsFromStored(template, currency) {
124226
- const t8 = template ?? {};
124433
+ const t9 = template ?? {};
124227
124434
  return {
124228
- currency: t8.currency || currency || "EUR",
124229
- vatRate: t8.vatRate ?? 21,
124230
- taxRate: t8.taxRate ?? 0,
124231
- includeVat: t8.includeVat ?? true,
124232
- includeTax: t8.includeTax ?? false,
124233
- includeDiscount: t8.includeDiscount ?? false,
124234
- includeDecimals: t8.includeDecimals ?? true,
124235
- includeUnits: t8.includeUnits ?? true,
124236
- includeQr: t8.includeQr ?? false,
124237
- size: t8.size || "a4",
124435
+ currency: t9.currency || currency || "EUR",
124436
+ vatRate: t9.vatRate ?? 21,
124437
+ taxRate: t9.taxRate ?? 0,
124438
+ includeVat: t9.includeVat ?? true,
124439
+ includeTax: t9.includeTax ?? false,
124440
+ includeDiscount: t9.includeDiscount ?? false,
124441
+ includeDecimals: t9.includeDecimals ?? true,
124442
+ includeUnits: t9.includeUnits ?? true,
124443
+ includeQr: t9.includeQr ?? false,
124444
+ size: t9.size || "a4",
124238
124445
  fromDetails: null,
124239
124446
  paymentDetails: null,
124240
- raw: t8
124447
+ raw: t9
124241
124448
  };
124242
124449
  }
124243
124450
 
@@ -124259,25 +124466,25 @@ function textResponse9(text3) {
124259
124466
  }
124260
124467
  async function loadTemplateDefaults(teamId) {
124261
124468
  const rows = await db.select().from(schema_exports.quotationTemplates).where(eq(schema_exports.quotationTemplates.teamId, teamId)).orderBy(desc(schema_exports.quotationTemplates.isDefault)).limit(1);
124262
- const t8 = rows[0];
124469
+ const t9 = rows[0];
124263
124470
  return {
124264
- currency: t8?.currency || "EUR",
124265
- vatRate: t8?.vatRate ?? 21,
124266
- taxRate: t8?.taxRate ?? 0,
124267
- includeVat: t8?.includeVat ?? true,
124268
- includeTax: t8?.includeTax ?? false,
124269
- includeDiscount: t8?.includeDiscount ?? false,
124270
- includeDecimals: t8?.includeDecimals ?? true,
124271
- includeUnits: t8?.includeUnits ?? true,
124272
- includeQr: t8?.includeQr ?? false,
124273
- size: t8?.size || "a4",
124274
- fromDetails: t8?.fromDetails ?? null,
124275
- paymentDetails: t8?.paymentDetails ?? null,
124276
- raw: t8 ?? {}
124471
+ currency: t9?.currency || "EUR",
124472
+ vatRate: t9?.vatRate ?? 21,
124473
+ taxRate: t9?.taxRate ?? 0,
124474
+ includeVat: t9?.includeVat ?? true,
124475
+ includeTax: t9?.includeTax ?? false,
124476
+ includeDiscount: t9?.includeDiscount ?? false,
124477
+ includeDecimals: t9?.includeDecimals ?? true,
124478
+ includeUnits: t9?.includeUnits ?? true,
124479
+ includeQr: t9?.includeQr ?? false,
124480
+ size: t9?.size || "a4",
124481
+ fromDetails: t9?.fromDetails ?? null,
124482
+ paymentDetails: t9?.paymentDetails ?? null,
124483
+ raw: t9 ?? {}
124277
124484
  };
124278
124485
  }
124279
124486
  function buildQuoteTemplate(defaults, titleOverride) {
124280
- const t8 = defaults.raw;
124487
+ const t9 = defaults.raw;
124281
124488
  return {
124282
124489
  currency: defaults.currency,
124283
124490
  includeVat: defaults.includeVat,
@@ -124289,27 +124496,27 @@ function buildQuoteTemplate(defaults, titleOverride) {
124289
124496
  vatRate: defaults.vatRate,
124290
124497
  taxRate: defaults.taxRate,
124291
124498
  size: defaults.size,
124292
- locale: t8.locale || "nl",
124499
+ locale: t9.locale || "nl",
124293
124500
  timezone: "Europe/Amsterdam",
124294
- customerLabel: t8.customerLabel || "Klant",
124295
- title: titleOverride || t8.title || "Offerte",
124296
- fromLabel: t8.fromLabel || "Van",
124297
- quotationNoLabel: t8.quotationNoLabel || "Offerte nr.",
124298
- issueDateLabel: t8.issueDateLabel || "Datum",
124299
- validUntilLabel: t8.validUntilLabel || "Geldig tot",
124300
- descriptionLabel: t8.descriptionLabel || "Omschrijving",
124301
- priceLabel: t8.priceLabel || "Prijs",
124302
- quantityLabel: t8.quantityLabel || "Aantal",
124303
- totalLabel: t8.totalLabel || "Totaal",
124304
- totalSummaryLabel: t8.totalSummaryLabel || "Totaal",
124305
- vatLabel: t8.vatLabel || "BTW",
124306
- subtotalLabel: t8.subtotalLabel || "Subtotaal",
124307
- taxLabel: t8.taxLabel || "Belasting",
124308
- discountLabel: t8.discountLabel || "Korting",
124309
- paymentLabel: t8.paymentLabel || "Betaling",
124310
- noteLabel: t8.noteLabel || "Notitie",
124311
- logoUrl: t8.logoUrl ?? null,
124312
- dateFormat: t8.dateFormat || "dd/MM/yyyy"
124501
+ customerLabel: t9.customerLabel || "Klant",
124502
+ title: titleOverride || t9.title || "Offerte",
124503
+ fromLabel: t9.fromLabel || "Van",
124504
+ quotationNoLabel: t9.quotationNoLabel || "Offerte nr.",
124505
+ issueDateLabel: t9.issueDateLabel || "Datum",
124506
+ validUntilLabel: t9.validUntilLabel || "Geldig tot",
124507
+ descriptionLabel: t9.descriptionLabel || "Omschrijving",
124508
+ priceLabel: t9.priceLabel || "Prijs",
124509
+ quantityLabel: t9.quantityLabel || "Aantal",
124510
+ totalLabel: t9.totalLabel || "Totaal",
124511
+ totalSummaryLabel: t9.totalSummaryLabel || "Totaal",
124512
+ vatLabel: t9.vatLabel || "BTW",
124513
+ subtotalLabel: t9.subtotalLabel || "Subtotaal",
124514
+ taxLabel: t9.taxLabel || "Belasting",
124515
+ discountLabel: t9.discountLabel || "Korting",
124516
+ paymentLabel: t9.paymentLabel || "Betaling",
124517
+ noteLabel: t9.noteLabel || "Notitie",
124518
+ logoUrl: t9.logoUrl ?? null,
124519
+ dateFormat: t9.dateFormat || "dd/MM/yyyy"
124313
124520
  };
124314
124521
  }
124315
124522
  async function nextQuotationNumber(teamId) {
@@ -124732,7 +124939,7 @@ async function handleGetTeams() {
124732
124939
  ]
124733
124940
  };
124734
124941
  }
124735
- const list = teams2.map((t8) => `- ${t8.name ?? "(unnamed provider)"} (teamId: ${t8.id})`).join("\n");
124942
+ const list = teams2.map((t9) => `- ${t9.name ?? "(unnamed provider)"} (teamId: ${t9.id})`).join("\n");
124736
124943
  return {
124737
124944
  content: [
124738
124945
  {
@@ -125354,7 +125561,7 @@ function normalizeTagName(name21) {
125354
125561
  }
125355
125562
  function formatTagList(tags2) {
125356
125563
  if (tags2.length === 0) return "";
125357
- return tags2.map((t8) => t8.name).join(", ");
125564
+ return tags2.map((t9) => t9.name).join(", ");
125358
125565
  }
125359
125566
  async function getTagsForTickets(ticketIds) {
125360
125567
  const result = /* @__PURE__ */ new Map();
@@ -125790,7 +125997,7 @@ async function resolveMergeTarget(teamId, input) {
125790
125997
  )
125791
125998
  );
125792
125999
  if (matches.length > 0) {
125793
- const general = matches.find((t8) => t8.projectId === null);
126000
+ const general = matches.find((t9) => t9.projectId === null);
125794
126001
  return { ok: true, tag: general ?? matches[0], created: false };
125795
126002
  }
125796
126003
  const [created] = await db.insert(schema_exports.tags).values({ teamId, name: input.targetName.trim(), projectId: null }).returning(TAG_COLUMNS);
@@ -125813,7 +126020,7 @@ async function handleMergeTags(input) {
125813
126020
  inArray(schema_exports.tags.teamId, accessibleTeamIds)
125814
126021
  )
125815
126022
  );
125816
- const foundIds = new Set(sourceTags.map((t8) => t8.id));
126023
+ const foundIds = new Set(sourceTags.map((t9) => t9.id));
125817
126024
  const missing = rawSourceIds.filter((id) => !foundIds.has(id));
125818
126025
  if (missing.length > 0) {
125819
126026
  return textResponse11(
@@ -125822,13 +126029,13 @@ async function handleMergeTags(input) {
125822
126029
  }
125823
126030
  const target = await resolveMergeTarget(resolved.teamId, input);
125824
126031
  if (!target.ok) return target.response;
125825
- const sourcesToMerge = sourceTags.filter((t8) => t8.id !== target.tag.id);
126032
+ const sourcesToMerge = sourceTags.filter((t9) => t9.id !== target.tag.id);
125826
126033
  if (sourcesToMerge.length === 0) {
125827
126034
  return textResponse11(
125828
126035
  "Error: nothing to merge \u2014 the only source tag is the same as the target tag."
125829
126036
  );
125830
126037
  }
125831
- const sourceIds = sourcesToMerge.map((t8) => t8.id);
126038
+ const sourceIds = sourcesToMerge.map((t9) => t9.id);
125832
126039
  const targetId = target.tag.id;
125833
126040
  const deleteSources = input.deleteSources ?? true;
125834
126041
  const results = await db.transaction(async (tx) => {
@@ -125924,7 +126131,7 @@ async function handleMergeTags(input) {
125924
126131
  return textResponse11(
125925
126132
  `\u2705 **Tags merged** into ${describeTag(target.tag)}${target.created ? " (newly created)" : ""}
125926
126133
 
125927
- Sources (${sourcesToMerge.length}): ${sourcesToMerge.map((t8) => `${t8.name} (${t8.id})`).join(", ")}
126134
+ Sources (${sourcesToMerge.length}): ${sourcesToMerge.map((t9) => `${t9.name} (${t9.id})`).join(", ")}
125928
126135
 
125929
126136
  Relations moved (duplicates skipped to keep a single tag per entity):
125930
126137
  ${line2("Tickets", results.tickets)}
@@ -125933,7 +126140,7 @@ ${line2("Projects", results.projects)}
125933
126140
  ${line2("Transactions", results.transactions)}
125934
126141
 
125935
126142
  Totals: ${movedTotal} relation(s) moved, ${skippedTotal} duplicate(s) skipped.
125936
- Source tags ${deleteSources ? "deleted" : "kept (now empty)"}: ${sourcesToMerge.map((t8) => t8.id).join(", ")}.`
126143
+ Source tags ${deleteSources ? "deleted" : "kept (now empty)"}: ${sourcesToMerge.map((t9) => t9.id).join(", ")}.`
125937
126144
  );
125938
126145
  }
125939
126146
  function planToResult(plan) {
@@ -126214,7 +126421,7 @@ async function handleUpdateTicket(input) {
126214
126421
  const { added, removed } = await syncTicketTags(
126215
126422
  ticket.id,
126216
126423
  ticket.teamId,
126217
- resolvedTags.tags.map((t8) => t8.id),
126424
+ resolvedTags.tags.map((t9) => t9.id),
126218
126425
  tagMode
126219
126426
  );
126220
126427
  if (added.length > 0) {
@@ -126321,37 +126528,37 @@ function toNumber3(value) {
126321
126528
  const parsed = Number.parseFloat(String(value));
126322
126529
  return Number.isFinite(parsed) ? parsed : 0;
126323
126530
  }
126324
- function formatTrip(t8) {
126531
+ function formatTrip(t9) {
126325
126532
  return {
126326
- id: t8.id,
126327
- date: t8.date,
126328
- startLocation: t8.startLocation,
126329
- endLocation: t8.endLocation,
126330
- tripType: t8.tripType,
126331
- distance: t8.distance != null ? toNumber3(t8.distance) : null,
126332
- odometerStart: t8.odometerStart != null ? toNumber3(t8.odometerStart) : null,
126333
- odometerEnd: t8.odometerEnd != null ? toNumber3(t8.odometerEnd) : null,
126334
- billingType: t8.billingType,
126335
- rate: t8.rate,
126336
- amount: t8.amount,
126337
- isInvoiced: t8.isInvoiced,
126338
- invoiceId: t8.invoiceId,
126339
- notes: t8.notes,
126340
- user: t8.user ? { id: t8.user.id, name: t8.user.fullName } : null,
126341
- project: t8.project ? { id: t8.project.id, name: t8.project.name } : null,
126342
- customer: t8.customer ? { id: t8.customer.id, name: t8.customer.name } : null,
126343
- invoice: t8.invoice ? {
126344
- id: t8.invoice.id,
126345
- invoiceNumber: t8.invoice.invoiceNumber,
126346
- status: t8.invoice.status
126533
+ id: t9.id,
126534
+ date: t9.date,
126535
+ startLocation: t9.startLocation,
126536
+ endLocation: t9.endLocation,
126537
+ tripType: t9.tripType,
126538
+ distance: t9.distance != null ? toNumber3(t9.distance) : null,
126539
+ odometerStart: t9.odometerStart != null ? toNumber3(t9.odometerStart) : null,
126540
+ odometerEnd: t9.odometerEnd != null ? toNumber3(t9.odometerEnd) : null,
126541
+ billingType: t9.billingType,
126542
+ rate: t9.rate,
126543
+ amount: t9.amount,
126544
+ isInvoiced: t9.isInvoiced,
126545
+ invoiceId: t9.invoiceId,
126546
+ notes: t9.notes,
126547
+ user: t9.user ? { id: t9.user.id, name: t9.user.fullName } : null,
126548
+ project: t9.project ? { id: t9.project.id, name: t9.project.name } : null,
126549
+ customer: t9.customer ? { id: t9.customer.id, name: t9.customer.name } : null,
126550
+ invoice: t9.invoice ? {
126551
+ id: t9.invoice.id,
126552
+ invoiceNumber: t9.invoice.invoiceNumber,
126553
+ status: t9.invoice.status
126347
126554
  } : null,
126348
- vehicle: t8.vehicle ? {
126349
- id: t8.vehicle.id,
126350
- name: t8.vehicle.name,
126351
- licensePlate: t8.vehicle.licensePlate
126555
+ vehicle: t9.vehicle ? {
126556
+ id: t9.vehicle.id,
126557
+ name: t9.vehicle.name,
126558
+ licensePlate: t9.vehicle.licensePlate
126352
126559
  } : null,
126353
- snapshotId: t8.snapshotId,
126354
- linkedTripId: t8.linkedTripId
126560
+ snapshotId: t9.snapshotId,
126561
+ linkedTripId: t9.linkedTripId
126355
126562
  };
126356
126563
  }
126357
126564
  var TRIP_RELATIONS = {
@@ -126406,10 +126613,10 @@ async function handleGetTrips(input) {
126406
126613
  limit: pageSize
126407
126614
  });
126408
126615
  const totals = rows.reduce(
126409
- (acc, t8) => {
126410
- const distance = toNumber3(t8.distance);
126411
- const amount = t8.amount ?? 0;
126412
- if (t8.tripType === "business") acc.businessKm += distance;
126616
+ (acc, t9) => {
126617
+ const distance = toNumber3(t9.distance);
126618
+ const amount = t9.amount ?? 0;
126619
+ if (t9.tripType === "business") acc.businessKm += distance;
126413
126620
  else acc.privateKm += distance;
126414
126621
  acc.totalKm += distance;
126415
126622
  acc.totalAmount += amount;
@@ -126731,10 +126938,10 @@ async function handleGetTripTemplates(input) {
126731
126938
  }).from(schema_exports.tripTemplates).where(and(...filters)).orderBy(asc(schema_exports.tripTemplates.name)).limit(Math.min(input.pageSize ?? 50, 200));
126732
126939
  return jsonResponse2({
126733
126940
  count: rows.length,
126734
- templates: rows.map((t8) => ({
126735
- ...t8,
126736
- distance: t8.distance != null ? toNumber3(t8.distance) : null,
126737
- returnDistance: t8.returnDistance != null ? toNumber3(t8.returnDistance) : null
126941
+ templates: rows.map((t9) => ({
126942
+ ...t9,
126943
+ distance: t9.distance != null ? toNumber3(t9.distance) : null,
126944
+ returnDistance: t9.returnDistance != null ? toNumber3(t9.returnDistance) : null
126738
126945
  }))
126739
126946
  });
126740
126947
  }
@@ -126827,9 +127034,11 @@ async function handleGetTickets(input) {
126827
127034
  const ctx = getAuthContext();
126828
127035
  const {
126829
127036
  status,
127037
+ statuses,
126830
127038
  priority,
126831
127039
  projectId,
126832
127040
  customerId,
127041
+ assigneeId,
126833
127042
  q: q3,
126834
127043
  tag: tag2,
126835
127044
  tags: tags2,
@@ -126848,10 +127057,18 @@ async function handleGetTickets(input) {
126848
127057
  customerIds
126849
127058
  );
126850
127059
  const filters = [accessPredicate, eq(schema_exports.tickets.isDeleted, false)];
126851
- if (status) filters.push(eq(schema_exports.tickets.status, status));
127060
+ if (statuses && statuses.length > 0) {
127061
+ filters.push(inArray(schema_exports.tickets.status, statuses));
127062
+ } else if (status) {
127063
+ filters.push(eq(schema_exports.tickets.status, status));
127064
+ }
126852
127065
  if (priority) filters.push(eq(schema_exports.tickets.priority, priority));
126853
127066
  if (projectId) filters.push(eq(schema_exports.tickets.projectId, projectId));
126854
127067
  if (customerId) filters.push(eq(schema_exports.tickets.customerId, customerId));
127068
+ if (assigneeId) {
127069
+ const resolvedAssignee = assigneeId === "me" ? ctx.userId : assigneeId;
127070
+ filters.push(eq(schema_exports.tickets.assigneeId, resolvedAssignee));
127071
+ }
126855
127072
  if (q3) {
126856
127073
  const pattern = `%${q3}%`;
126857
127074
  filters.push(
@@ -126894,30 +127111,35 @@ async function handleGetTickets(input) {
126894
127111
  priority: schema_exports.tickets.priority,
126895
127112
  type: schema_exports.tickets.type,
126896
127113
  createdAt: schema_exports.tickets.createdAt,
127114
+ dueDate: schema_exports.tickets.dueDate,
126897
127115
  projectId: schema_exports.tickets.projectId,
126898
127116
  customerId: schema_exports.tickets.customerId,
126899
127117
  projectName: schema_exports.projects.name,
126900
- customerName: schema_exports.customers.name
127118
+ customerName: schema_exports.customers.name,
127119
+ assigneeId: schema_exports.tickets.assigneeId,
127120
+ assigneeName: schema_exports.users.fullName
126901
127121
  }).from(schema_exports.tickets).leftJoin(schema_exports.projects, eq(schema_exports.projects.id, schema_exports.tickets.projectId)).leftJoin(
126902
127122
  schema_exports.customers,
126903
127123
  eq(schema_exports.customers.id, schema_exports.tickets.customerId)
126904
- ).where(and(...filters)).orderBy(desc(schema_exports.tickets.createdAt)).limit(Math.min(pageSize, 100));
126905
- const tagsByTicket = await getTagsForTickets(rows.map((t8) => t8.id));
127124
+ ).leftJoin(schema_exports.users, eq(schema_exports.users.id, schema_exports.tickets.assigneeId)).where(and(...filters)).orderBy(desc(schema_exports.tickets.createdAt)).limit(Math.min(pageSize, 100));
127125
+ const tagsByTicket = await getTagsForTickets(rows.map((t9) => t9.id));
126906
127126
  return {
126907
127127
  content: [
126908
127128
  {
126909
127129
  type: "text",
126910
127130
  text: `Found ${rows.length} tickets:
126911
127131
 
126912
- ${rows.map((t8) => {
126913
- const ticketTags2 = tagsByTicket.get(t8.id) ?? [];
126914
- return `**${t8.ticketNumber}**: ${t8.title}
126915
- ID: ${t8.id}
126916
- Status: ${t8.status} | Priority: ${t8.priority}
127132
+ ${rows.map((t9) => {
127133
+ const ticketTags2 = tagsByTicket.get(t9.id) ?? [];
127134
+ return `**${t9.ticketNumber}**: ${t9.title}
127135
+ ID: ${t9.id}
127136
+ Status: ${t9.status} | Priority: ${t9.priority}
126917
127137
  ${ticketTags2.length > 0 ? `Tags: ${formatTagList(ticketTags2)}
126918
- ` : ""}${t8.projectName ? `Project: ${t8.projectName}
126919
- ` : ""}${t8.customerName ? `Customer: ${t8.customerName}
126920
- ` : ""}Created: ${new Date(t8.createdAt).toLocaleDateString()}
127138
+ ` : ""}${t9.projectName ? `Project: ${t9.projectName}
127139
+ ` : ""}${t9.customerName ? `Customer: ${t9.customerName}
127140
+ ` : ""}${t9.assigneeName ? `Assignee: ${t9.assigneeName} [id: ${t9.assigneeId}]
127141
+ ` : ""}${t9.dueDate ? `Deadline: ${new Date(t9.dueDate).toLocaleDateString()}
127142
+ ` : ""}Created: ${new Date(t9.createdAt).toLocaleDateString()}
126921
127143
  `;
126922
127144
  }).join("\n") || "No tickets found."}`
126923
127145
  }
@@ -127193,7 +127415,7 @@ async function handleCreateTicket(input) {
127193
127415
  await syncTicketTags(
127194
127416
  created.id,
127195
127417
  resolvedTeamId,
127196
- resolvedTags.tags.map((t8) => t8.id),
127418
+ resolvedTags.tags.map((t9) => t9.id),
127197
127419
  "replace"
127198
127420
  );
127199
127421
  appliedTags = resolvedTags.tags;
@@ -127222,6 +127444,430 @@ ${tagErrors.map((e6) => ` \u2022 ${e6}`).join("\n")}
127222
127444
  };
127223
127445
  }
127224
127446
 
127447
+ // src/tools/work-queue.ts
127448
+ var t8 = schema_exports.tickets;
127449
+ var ACTIVE_STATUSES = [
127450
+ "open",
127451
+ "in_progress",
127452
+ "review",
127453
+ "blocked",
127454
+ "backlog"
127455
+ ];
127456
+ var RANK_BASE = 1e3;
127457
+ var RANK_MIN = 100;
127458
+ var RANK_APPEND_SPACING = 100;
127459
+ var workStateExpr = sql`
127460
+ CASE
127461
+ WHEN ${t8.status} IN ('open', 'in_progress') THEN 'now'
127462
+ WHEN ${t8.status} = 'review' THEN 'waiting'
127463
+ WHEN ${t8.status} = 'backlog' THEN 'later'
127464
+ WHEN ${t8.status} = 'blocked' THEN 'blocked'
127465
+ ELSE 'done'
127466
+ END
127467
+ `;
127468
+ function textResponse13(text3) {
127469
+ return { content: [{ type: "text", text: text3 }] };
127470
+ }
127471
+ function jsonResponse3(payload) {
127472
+ return {
127473
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
127474
+ };
127475
+ }
127476
+ function workStateToStatus(workState) {
127477
+ switch (workState) {
127478
+ case "now":
127479
+ return "in_progress";
127480
+ case "waiting":
127481
+ return "review";
127482
+ case "later":
127483
+ return "backlog";
127484
+ case "blocked":
127485
+ return "blocked";
127486
+ case "done":
127487
+ return "resolved";
127488
+ default:
127489
+ return null;
127490
+ }
127491
+ }
127492
+ function workStatesToStatuses(workStates) {
127493
+ const set3 = /* @__PURE__ */ new Set();
127494
+ for (const ws of workStates) {
127495
+ switch (ws) {
127496
+ case "now":
127497
+ set3.add("open");
127498
+ set3.add("in_progress");
127499
+ break;
127500
+ case "waiting":
127501
+ set3.add("review");
127502
+ break;
127503
+ case "later":
127504
+ set3.add("backlog");
127505
+ break;
127506
+ case "blocked":
127507
+ set3.add("blocked");
127508
+ break;
127509
+ case "done":
127510
+ set3.add("resolved");
127511
+ set3.add("closed");
127512
+ break;
127513
+ }
127514
+ }
127515
+ return [...set3];
127516
+ }
127517
+ async function loadWorkQueue(scope, assignee, filters) {
127518
+ const conditions = [
127519
+ buildTicketAccessPredicate(
127520
+ scope.teamIds,
127521
+ scope.projectIds,
127522
+ scope.customerIds
127523
+ ),
127524
+ eq(t8.isDeleted, false)
127525
+ ];
127526
+ if (assignee.mode === "one") {
127527
+ conditions.push(eq(t8.assigneeId, assignee.userId));
127528
+ } else {
127529
+ conditions.push(sql`${t8.assigneeId} IS NOT NULL`);
127530
+ }
127531
+ let statuses;
127532
+ if (filters.statuses && filters.statuses.length > 0) {
127533
+ statuses = filters.statuses;
127534
+ } else if (filters.workStates && filters.workStates.length > 0) {
127535
+ statuses = workStatesToStatuses(filters.workStates);
127536
+ } else {
127537
+ statuses = [...ACTIVE_STATUSES];
127538
+ }
127539
+ if (statuses.length > 0) {
127540
+ conditions.push(inArray(t8.status, statuses));
127541
+ }
127542
+ if (filters.priority) {
127543
+ conditions.push(eq(t8.priority, filters.priority));
127544
+ }
127545
+ if (filters.projectId) {
127546
+ conditions.push(eq(t8.projectId, filters.projectId));
127547
+ }
127548
+ const rows = await db.select({
127549
+ id: t8.id,
127550
+ ticketNumber: t8.ticketNumber,
127551
+ title: t8.title,
127552
+ status: t8.status,
127553
+ priority: t8.priority,
127554
+ type: t8.type,
127555
+ workState: workStateExpr,
127556
+ queueRank: t8.queueRank,
127557
+ focusState: t8.focusState,
127558
+ dueDate: t8.dueDate,
127559
+ updatedAt: t8.updatedAt,
127560
+ teamId: t8.teamId,
127561
+ assigneeId: t8.assigneeId,
127562
+ assigneeName: schema_exports.users.fullName,
127563
+ projectId: t8.projectId,
127564
+ projectName: schema_exports.projects.name,
127565
+ customerId: t8.customerId,
127566
+ customerName: schema_exports.customers.name,
127567
+ blockedReason: sql`(${t8.metadata} ->> 'blockedReason')`
127568
+ }).from(t8).leftJoin(schema_exports.projects, eq(schema_exports.projects.id, t8.projectId)).leftJoin(schema_exports.customers, eq(schema_exports.customers.id, t8.customerId)).leftJoin(schema_exports.users, eq(schema_exports.users.id, t8.assigneeId)).where(and(...conditions)).orderBy(
127569
+ sql`CASE ${t8.focusState} WHEN 'today' THEN 0 WHEN 'week' THEN 1 ELSE 2 END ASC`,
127570
+ sql`${t8.queueRank} ASC NULLS LAST`,
127571
+ desc(t8.priority),
127572
+ sql`${t8.dueDate} ASC NULLS LAST`,
127573
+ desc(t8.updatedAt)
127574
+ ).limit(Math.min(filters.pageSize ?? 200, 500));
127575
+ return rows;
127576
+ }
127577
+ function serializeItem(row) {
127578
+ return {
127579
+ id: row.id,
127580
+ ticketNumber: row.ticketNumber,
127581
+ title: row.title,
127582
+ status: row.status,
127583
+ workState: row.workState,
127584
+ priority: row.priority,
127585
+ type: row.type,
127586
+ dueDate: row.dueDate,
127587
+ queueRank: row.queueRank,
127588
+ focusState: row.focusState,
127589
+ project: row.projectId ? { id: row.projectId, name: row.projectName } : null,
127590
+ customer: row.customerId ? { id: row.customerId, name: row.customerName } : null,
127591
+ assignee: row.assigneeId ? { id: row.assigneeId, name: row.assigneeName } : null,
127592
+ blockedReason: row.blockedReason,
127593
+ updatedAt: row.updatedAt
127594
+ };
127595
+ }
127596
+ async function handleGetMyWorkQueue(input) {
127597
+ const ctx = getAuthContext();
127598
+ const scope = await resolveTeamScope(input.teamId);
127599
+ if (!scope.ok) return scope.response;
127600
+ const rows = await loadWorkQueue(
127601
+ scope,
127602
+ { mode: "one", userId: ctx.userId },
127603
+ input
127604
+ );
127605
+ const items = rows.map(serializeItem);
127606
+ return jsonResponse3({
127607
+ assigneeId: ctx.userId,
127608
+ teamScope: scope.teamIds,
127609
+ totals: {
127610
+ total: items.length,
127611
+ now: items.filter((i6) => i6.workState === "now").length,
127612
+ waiting: items.filter((i6) => i6.workState === "waiting").length,
127613
+ later: items.filter((i6) => i6.workState === "later").length,
127614
+ blocked: items.filter((i6) => i6.workState === "blocked").length
127615
+ },
127616
+ items
127617
+ });
127618
+ }
127619
+ async function handleGetAssigneeWorkQueue(input) {
127620
+ const ctx = getAuthContext();
127621
+ const scope = await resolveTeamScope(input.teamId);
127622
+ if (!scope.ok) return scope.response;
127623
+ const rawAssignee = input.assigneeId?.trim();
127624
+ const isAll = rawAssignee === "all";
127625
+ const targetUserId = !rawAssignee || rawAssignee === "me" ? ctx.userId : rawAssignee;
127626
+ const rows = await loadWorkQueue(
127627
+ scope,
127628
+ isAll ? { mode: "all" } : { mode: "one", userId: targetUserId },
127629
+ input
127630
+ );
127631
+ const items = rows.map(serializeItem);
127632
+ if (isAll && input.groupByAssignee) {
127633
+ const groups = /* @__PURE__ */ new Map();
127634
+ for (const item of items) {
127635
+ if (!item.assignee) continue;
127636
+ const existing = groups.get(item.assignee.id);
127637
+ if (existing) {
127638
+ existing.items.push(item);
127639
+ } else {
127640
+ groups.set(item.assignee.id, {
127641
+ assignee: item.assignee,
127642
+ items: [item]
127643
+ });
127644
+ }
127645
+ }
127646
+ return jsonResponse3({
127647
+ assigneeId: "all",
127648
+ teamScope: scope.teamIds,
127649
+ grouped: true,
127650
+ groups: [...groups.values()].map((g6) => ({
127651
+ assignee: g6.assignee,
127652
+ total: g6.items.length,
127653
+ items: g6.items
127654
+ }))
127655
+ });
127656
+ }
127657
+ return jsonResponse3({
127658
+ assigneeId: isAll ? "all" : targetUserId,
127659
+ teamScope: scope.teamIds,
127660
+ total: items.length,
127661
+ items
127662
+ });
127663
+ }
127664
+ async function handleGetTeamWorkloadOverview(input) {
127665
+ const scope = await resolveTeamScope(input.teamId);
127666
+ if (!scope.ok) return scope.response;
127667
+ const staleDays = input.staleDays ?? 7;
127668
+ const upcomingDays = input.upcomingDays ?? 7;
127669
+ const includeUnassigned = input.includeUnassigned ?? true;
127670
+ const conditions = [
127671
+ buildTicketAccessPredicate(
127672
+ scope.teamIds,
127673
+ scope.projectIds,
127674
+ scope.customerIds
127675
+ ),
127676
+ eq(t8.isDeleted, false),
127677
+ inArray(t8.status, [...ACTIVE_STATUSES])
127678
+ ];
127679
+ if (!includeUnassigned) {
127680
+ conditions.push(sql`${t8.assigneeId} IS NOT NULL`);
127681
+ }
127682
+ const rows = await db.select({
127683
+ assigneeId: t8.assigneeId,
127684
+ assigneeName: schema_exports.users.fullName,
127685
+ total: sql`count(*)::int`,
127686
+ now: sql`(count(*) filter (where ${t8.status} in ('open', 'in_progress')))::int`,
127687
+ waiting: sql`(count(*) filter (where ${t8.status} = 'review'))::int`,
127688
+ later: sql`(count(*) filter (where ${t8.status} = 'backlog'))::int`,
127689
+ blocked: sql`(count(*) filter (where ${t8.status} = 'blocked'))::int`,
127690
+ highPriority: sql`(count(*) filter (where ${t8.priority} in ('high', 'critical')))::int`,
127691
+ stale: sql`(count(*) filter (where ${t8.updatedAt} < now() - make_interval(days => ${staleDays})))::int`,
127692
+ upcoming: sql`(count(*) filter (where ${t8.dueDate} is not null and ${t8.dueDate} >= now() and ${t8.dueDate} <= now() + make_interval(days => ${upcomingDays})))::int`
127693
+ }).from(t8).leftJoin(schema_exports.users, eq(schema_exports.users.id, t8.assigneeId)).where(and(...conditions)).groupBy(t8.assigneeId, schema_exports.users.fullName).orderBy(desc(sql`count(*)`));
127694
+ return jsonResponse3({
127695
+ teamScope: scope.teamIds,
127696
+ config: { staleDays, upcomingDays, includeUnassigned },
127697
+ assignees: rows.map((r6) => ({
127698
+ assignee: r6.assigneeId ? { id: r6.assigneeId, name: r6.assigneeName } : null,
127699
+ total: r6.total,
127700
+ now: r6.now,
127701
+ waiting: r6.waiting,
127702
+ later: r6.later,
127703
+ blocked: r6.blocked,
127704
+ highPriority: r6.highPriority,
127705
+ stale: r6.stale,
127706
+ upcoming: r6.upcoming
127707
+ }))
127708
+ });
127709
+ }
127710
+ async function resolveManageableTicket(ticketId, scope) {
127711
+ const ctx = getAuthContext();
127712
+ const [ticket] = await db.select({
127713
+ id: t8.id,
127714
+ teamId: t8.teamId,
127715
+ status: t8.status,
127716
+ assigneeId: t8.assigneeId,
127717
+ projectId: t8.projectId,
127718
+ customerId: t8.customerId,
127719
+ metadata: t8.metadata
127720
+ }).from(t8).where(eq(t8.id, ticketId)).limit(1);
127721
+ if (!ticket) {
127722
+ return {
127723
+ ok: false,
127724
+ response: textResponse13(
127725
+ `Ticket not found: ${ticketId}. Call get-tickets first to find the correct ticket.`
127726
+ )
127727
+ };
127728
+ }
127729
+ let hasAccess = scope.teamIds.includes(ticket.teamId);
127730
+ if (!hasAccess && ticket.projectId)
127731
+ hasAccess = scope.projectIds.includes(ticket.projectId);
127732
+ if (!hasAccess && ticket.customerId)
127733
+ hasAccess = scope.customerIds.includes(ticket.customerId);
127734
+ if (!hasAccess) {
127735
+ return {
127736
+ ok: false,
127737
+ response: textResponse13(`No access to ticket: ${ticketId}.`)
127738
+ };
127739
+ }
127740
+ if (ticket.assigneeId !== ctx.userId) {
127741
+ const [membership] = await db.select({ role: schema_exports.usersOnTeam.role }).from(schema_exports.usersOnTeam).where(
127742
+ and(
127743
+ eq(schema_exports.usersOnTeam.userId, ctx.userId),
127744
+ eq(schema_exports.usersOnTeam.teamId, ticket.teamId)
127745
+ )
127746
+ ).limit(1);
127747
+ if (membership?.role !== "owner") {
127748
+ return {
127749
+ ok: false,
127750
+ response: textResponse13(
127751
+ "Permission denied: only the assignee or a team owner can manage this ticket's work queue."
127752
+ )
127753
+ };
127754
+ }
127755
+ }
127756
+ return { ok: true, ticket };
127757
+ }
127758
+ async function handleSetAssigneeQueueOrder(input) {
127759
+ if (!input.ticketId) return textResponse13("Error: `ticketId` is required.");
127760
+ if (typeof input.position !== "number" || input.position < 0) {
127761
+ return textResponse13("Error: `position` must be a number >= 0.");
127762
+ }
127763
+ const scope = await resolveTeamScope(input.teamId);
127764
+ if (!scope.ok) return scope.response;
127765
+ const resolved = await resolveManageableTicket(input.ticketId, scope);
127766
+ if (!resolved.ok) return resolved.response;
127767
+ const { ticket } = resolved;
127768
+ if (!ticket.assigneeId) {
127769
+ return textResponse13(
127770
+ "Ticket has no assignee; assign it first before ranking it in a queue."
127771
+ );
127772
+ }
127773
+ const queue = await db.select({ id: t8.id, queueRank: t8.queueRank }).from(t8).where(
127774
+ and(
127775
+ eq(t8.isDeleted, false),
127776
+ eq(t8.assigneeId, ticket.assigneeId),
127777
+ eq(t8.teamId, ticket.teamId),
127778
+ inArray(t8.status, [...ACTIVE_STATUSES]),
127779
+ ne(t8.id, ticket.id)
127780
+ )
127781
+ ).orderBy(
127782
+ sql`${t8.queueRank} ASC NULLS LAST`,
127783
+ desc(t8.priority),
127784
+ desc(t8.createdAt)
127785
+ );
127786
+ let newRank;
127787
+ if (queue.length === 0) {
127788
+ newRank = RANK_BASE;
127789
+ } else if (input.position <= 0) {
127790
+ const first = Number(queue[0]?.queueRank ?? RANK_BASE);
127791
+ newRank = Math.max(RANK_MIN, first - RANK_BASE / 2);
127792
+ } else if (input.position >= queue.length) {
127793
+ const last = Number(queue[queue.length - 1]?.queueRank ?? RANK_BASE);
127794
+ newRank = last + RANK_APPEND_SPACING;
127795
+ } else {
127796
+ const prev = Number(queue[input.position - 1]?.queueRank ?? RANK_BASE / 2);
127797
+ const next = Number(queue[input.position]?.queueRank ?? RANK_BASE * 1.5);
127798
+ newRank = (prev + next) / 2;
127799
+ }
127800
+ await db.update(t8).set({ queueRank: newRank }).where(eq(t8.id, ticket.id));
127801
+ return jsonResponse3({
127802
+ ticketId: ticket.id,
127803
+ assigneeId: ticket.assigneeId,
127804
+ position: input.position,
127805
+ queueRank: newRank
127806
+ });
127807
+ }
127808
+ async function handleUpdateTicketWorkState(input) {
127809
+ if (!input.ticketId) return textResponse13("Error: `ticketId` is required.");
127810
+ const newStatus = workStateToStatus(input.workState);
127811
+ if (!newStatus) {
127812
+ return textResponse13(
127813
+ `Error: invalid workState "${input.workState}". Allowed: now, waiting, later, blocked.`
127814
+ );
127815
+ }
127816
+ const scope = await resolveTeamScope(input.teamId);
127817
+ if (!scope.ok) return scope.response;
127818
+ const resolved = await resolveManageableTicket(input.ticketId, scope);
127819
+ if (!resolved.ok) return resolved.response;
127820
+ const { ticket } = resolved;
127821
+ const updateSet = {
127822
+ status: newStatus,
127823
+ updatedAt: sql`NOW()`
127824
+ };
127825
+ if (input.workState === "blocked") {
127826
+ const blockedMeta = JSON.stringify({
127827
+ blockedReason: input.blockedReason ?? null,
127828
+ blockedAt: (/* @__PURE__ */ new Date()).toISOString()
127829
+ });
127830
+ updateSet.metadata = sql`coalesce(${t8.metadata}, '{}'::jsonb) || ${blockedMeta}::jsonb`;
127831
+ }
127832
+ await db.update(t8).set(updateSet).where(eq(t8.id, ticket.id));
127833
+ return jsonResponse3({
127834
+ ticketId: ticket.id,
127835
+ workState: input.workState,
127836
+ status: newStatus,
127837
+ previousStatus: ticket.status
127838
+ });
127839
+ }
127840
+ async function handleMarkTicketBlocked(input) {
127841
+ if (!input.ticketId) return textResponse13("Error: `ticketId` is required.");
127842
+ const scope = await resolveTeamScope(input.teamId);
127843
+ if (!scope.ok) return scope.response;
127844
+ const resolved = await resolveManageableTicket(input.ticketId, scope);
127845
+ if (!resolved.ok) return resolved.response;
127846
+ const { ticket } = resolved;
127847
+ if (input.unblock) {
127848
+ await db.update(t8).set({ status: "in_progress", updatedAt: sql`NOW()` }).where(eq(t8.id, ticket.id));
127849
+ return jsonResponse3({
127850
+ ticketId: ticket.id,
127851
+ status: "in_progress",
127852
+ unblocked: true
127853
+ });
127854
+ }
127855
+ const blockedMeta = JSON.stringify({
127856
+ blockedReason: input.reason ?? null,
127857
+ blockedAt: (/* @__PURE__ */ new Date()).toISOString()
127858
+ });
127859
+ await db.update(t8).set({
127860
+ status: "blocked",
127861
+ updatedAt: sql`NOW()`,
127862
+ metadata: sql`coalesce(${t8.metadata}, '{}'::jsonb) || ${blockedMeta}::jsonb`
127863
+ }).where(eq(t8.id, ticket.id));
127864
+ return jsonResponse3({
127865
+ ticketId: ticket.id,
127866
+ status: "blocked",
127867
+ blockedReason: input.reason ?? null
127868
+ });
127869
+ }
127870
+
127225
127871
  // src/tools/user-activity-report.ts
127226
127872
  var DEFAULT_TIMEZONE = "Europe/Amsterdam";
127227
127873
  var DEFAULT_PAGE_SIZE = 200;
@@ -127392,10 +128038,10 @@ function formatIsoWithOffset(date10, timeZone) {
127392
128038
  const local = `${parts.year}-${parts.month}-${parts.day}T${hour2}:${parts.minute}:${parts.second}`;
127393
128039
  return `${local}${timeZoneOffset(date10, timeZone)}`;
127394
128040
  }
127395
- function textResponse13(text3) {
128041
+ function textResponse14(text3) {
127396
128042
  return { content: [{ type: "text", text: text3 }] };
127397
128043
  }
127398
- function jsonResponse3(payload) {
128044
+ function jsonResponse4(payload) {
127399
128045
  return {
127400
128046
  content: [{ type: "text", text: JSON.stringify(payload, null, 2) }]
127401
128047
  };
@@ -127420,7 +128066,7 @@ async function handleGetUserActivityReport(input) {
127420
128066
  limitations.push(`Ignored unknown include value "${value}".`);
127421
128067
  }
127422
128068
  if (includes.size === 0) {
127423
- return textResponse13(
128069
+ return textResponse14(
127424
128070
  `Error: \`include\` contained no valid categories. Allowed: ${ACTIVITY_INCLUDE_VALUES.join(", ")}.`
127425
128071
  );
127426
128072
  }
@@ -127432,12 +128078,12 @@ async function handleGetUserActivityReport(input) {
127432
128078
  timezone = DEFAULT_TIMEZONE;
127433
128079
  }
127434
128080
  if (input.dateFrom && !isValidIsoDate2(input.dateFrom)) {
127435
- return textResponse13(
128081
+ return textResponse14(
127436
128082
  `Error: invalid dateFrom "${input.dateFrom}". Use YYYY-MM-DD.`
127437
128083
  );
127438
128084
  }
127439
128085
  if (input.dateTo && !isValidIsoDate2(input.dateTo)) {
127440
- return textResponse13(
128086
+ return textResponse14(
127441
128087
  `Error: invalid dateTo "${input.dateTo}". Use YYYY-MM-DD.`
127442
128088
  );
127443
128089
  }
@@ -127447,17 +128093,17 @@ async function handleGetUserActivityReport(input) {
127447
128093
  timezone
127448
128094
  });
127449
128095
  if (window2.from > window2.to) {
127450
- return textResponse13(
128096
+ return textResponse14(
127451
128097
  `Error: dateFrom (${window2.from}) is after dateTo (${window2.to}).`
127452
128098
  );
127453
128099
  }
127454
128100
  const scope = await resolveTeamScope(input.teamId);
127455
128101
  if (!scope.ok) return scope.response;
127456
128102
  if (scope.teamIds.length === 0) {
127457
- return textResponse13("No accessible teams found.");
128103
+ return textResponse14("No accessible teams found.");
127458
128104
  }
127459
128105
  if (input.projectId && !scope.projectIds.includes(input.projectId)) {
127460
- return textResponse13(
128106
+ return textResponse14(
127461
128107
  `Project not found or no access: ${input.projectId}. Call get-projects first.`
127462
128108
  );
127463
128109
  }
@@ -127687,7 +128333,7 @@ async function handleGetUserActivityReport(input) {
127687
128333
  timeEntrySeconds += seconds;
127688
128334
  const hours = hoursFrom2(seconds);
127689
128335
  const linked = ticketsByEvent.get(r6.id) ?? [];
127690
- for (const t8 of linked) ticketsTouched.add(t8.id);
128336
+ for (const t9 of linked) ticketsTouched.add(t9.id);
127691
128337
  const primary = linked[0] ?? null;
127692
128338
  const projectLabel = r6.projectName ? ` on ${r6.projectName}` : "";
127693
128339
  const titleLabel = r6.title ? ` \u2014 ${r6.title}` : "";
@@ -127705,9 +128351,9 @@ async function handleGetUserActivityReport(input) {
127705
128351
  hours,
127706
128352
  eventType: r6.type,
127707
128353
  billingStatus: r6.billingStatus,
127708
- linkedTickets: linked.map((t8) => ({
127709
- id: t8.id,
127710
- ticketNumber: t8.ticketNumber
128354
+ linkedTickets: linked.map((t9) => ({
128355
+ id: t9.id,
128356
+ ticketNumber: t9.ticketNumber
127711
128357
  }))
127712
128358
  }
127713
128359
  });
@@ -127755,7 +128401,7 @@ async function handleGetUserActivityReport(input) {
127755
128401
  MAX_PAGE_SIZE
127756
128402
  );
127757
128403
  const pagedActivities = activities2.slice(0, pageSize);
127758
- return jsonResponse3({
128404
+ return jsonResponse4({
127759
128405
  user,
127760
128406
  period: { from: window2.from, to: window2.to, timezone },
127761
128407
  filters: {
@@ -127817,6 +128463,31 @@ function createMcpServer() {
127817
128463
  return await handleGetTeams();
127818
128464
  case "get-tickets":
127819
128465
  return await handleGetTickets(asToolArgs(toolArgs));
128466
+ case "get-my-work-queue":
128467
+ return await handleGetMyWorkQueue(
128468
+ asToolArgs(toolArgs)
128469
+ );
128470
+ case "get-assignee-work-queue":
128471
+ return await handleGetAssigneeWorkQueue(
128472
+ asToolArgs(toolArgs)
128473
+ );
128474
+ case "get-team-workload-overview":
128475
+ return await handleGetTeamWorkloadOverview(
128476
+ asToolArgs(toolArgs)
128477
+ );
128478
+ case "set-assignee-queue-order":
128479
+ case "update-ticket-rank":
128480
+ return await handleSetAssigneeQueueOrder(
128481
+ asToolArgs(toolArgs)
128482
+ );
128483
+ case "update-ticket-work-state":
128484
+ return await handleUpdateTicketWorkState(
128485
+ asToolArgs(toolArgs)
128486
+ );
128487
+ case "mark-ticket-blocked":
128488
+ return await handleMarkTicketBlocked(
128489
+ asToolArgs(toolArgs)
128490
+ );
127820
128491
  case "get-ticket-by-id":
127821
128492
  return await handleGetTicketById(asToolArgs(toolArgs));
127822
128493
  case "create-ticket":