@impractical-ai/motion 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,23 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- openBrowser
4
- } from "./chunk-ZCTJTQB7.js";
3
+ DEVICE_START_BODY,
4
+ fetchWorkspaces,
5
+ grantScope,
6
+ openBrowser,
7
+ workspaceLabel
8
+ } from "./chunk-L5ERED5A.js";
5
9
  import {
6
10
  ApiError,
7
11
  admitMcpCall,
8
12
  invoke,
9
13
  invokeAt,
10
14
  loadConfig,
15
+ requireConfig,
11
16
  saveConfig,
17
+ studioQuery,
12
18
  validateApiResponse,
13
19
  withAdmissionReceipt
14
- } from "./chunk-GAHKKLF3.js";
20
+ } from "./chunk-O3BUN66P.js";
15
21
  import {
16
22
  BUILD_COMMIT,
17
23
  PACKAGE_VERSION,
18
24
  PRODUCTION_APP_URL,
19
25
  PRODUCTION_SUPABASE_URL
20
- } from "./chunk-PTYSKJ2Y.js";
26
+ } from "./chunk-7SNCVSTA.js";
21
27
  import {
22
28
  __commonJS,
23
29
  __export,
@@ -3100,9 +3106,28 @@ var require_utils = __commonJS({
3100
3106
  "use strict";
3101
3107
  var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
3102
3108
  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);
3109
+ var isPort = RegExp.prototype.test.bind(/^\d*$/u);
3103
3110
  var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
3104
3111
  var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
3105
- var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
3112
+ var isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
3113
+ var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
3114
+ var isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
3115
+ var BYTE_HEX = new Array(256);
3116
+ {
3117
+ const HEX_DIGITS = "0123456789ABCDEF";
3118
+ for (let i = 0; i < 256; i++) {
3119
+ BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
3120
+ }
3121
+ }
3122
+ function percentEncodeNonAscii(cp) {
3123
+ if (cp < 2048) {
3124
+ return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
3125
+ }
3126
+ if (cp < 65536) {
3127
+ return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3128
+ }
3129
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
3130
+ }
3106
3131
  function stringArrayToHexStripped(input) {
3107
3132
  let acc = "";
3108
3133
  let code = 0;
@@ -3127,91 +3152,105 @@ var require_utils = __commonJS({
3127
3152
  }
3128
3153
  return acc;
3129
3154
  }
3155
+ var isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
3156
+ var isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
3157
+ var isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
3130
3158
  var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
3131
- function consumeIsZone(buffer) {
3132
- buffer.length = 0;
3133
- return true;
3134
- }
3135
- function consumeHextets(buffer, address, output) {
3136
- if (buffer.length) {
3137
- const hex3 = stringArrayToHexStripped(buffer);
3138
- if (hex3 !== "") {
3139
- address.push(hex3);
3140
- } else {
3141
- output.error = true;
3142
- return false;
3159
+ function isZoneIdentifier(zone) {
3160
+ if (zone.length === 0) return false;
3161
+ for (let i = 0; i < zone.length; i++) {
3162
+ if (isZoneCharacter(zone[i])) continue;
3163
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
3164
+ i += 2;
3165
+ continue;
3143
3166
  }
3144
- buffer.length = 0;
3167
+ return false;
3145
3168
  }
3146
3169
  return true;
3147
3170
  }
3148
- function getIPV6(input) {
3149
- let tokenCount = 0;
3150
- const output = { error: false, address: "", zone: "" };
3151
- const address = [];
3152
- const buffer = [];
3153
- let endipv6Encountered = false;
3154
- let endIpv6 = false;
3155
- let consume = consumeHextets;
3156
- for (let i = 0; i < input.length; i++) {
3157
- const cursor = input[i];
3158
- if (cursor === "[" || cursor === "]") {
3159
- continue;
3160
- }
3161
- if (cursor === ":") {
3162
- if (endipv6Encountered === true) {
3163
- endIpv6 = true;
3164
- }
3165
- if (!consume(buffer, address, output)) {
3166
- break;
3167
- }
3168
- if (++tokenCount > 7) {
3169
- output.error = true;
3170
- break;
3171
- }
3172
- if (i > 0 && input[i - 1] === ":") {
3173
- endipv6Encountered = true;
3174
- }
3175
- address.push(":");
3176
- continue;
3177
- } else if (cursor === "%") {
3178
- if (!consume(buffer, address, output)) {
3179
- break;
3171
+ function compressIPv6ZeroRun(hextets) {
3172
+ let bestStart = -1;
3173
+ let bestLength = 0;
3174
+ let runStart = -1;
3175
+ let runLength = 0;
3176
+ for (let i = 0; i < hextets.length; i++) {
3177
+ if (hextets[i] === "0") {
3178
+ if (runStart === -1) runStart = i;
3179
+ runLength++;
3180
+ if (runLength > bestLength) {
3181
+ bestLength = runLength;
3182
+ bestStart = runStart;
3180
3183
  }
3181
- consume = consumeIsZone;
3182
3184
  } else {
3183
- buffer.push(cursor);
3185
+ runStart = -1;
3186
+ runLength = 0;
3187
+ }
3188
+ }
3189
+ if (bestLength < 2) return hextets.join(":");
3190
+ const head = hextets.slice(0, bestStart).join(":");
3191
+ const tail = hextets.slice(bestStart + bestLength).join(":");
3192
+ return head + "::" + tail;
3193
+ }
3194
+ function normalizeIPv6Address(input) {
3195
+ const compression = input.indexOf("::");
3196
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
3197
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
3198
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
3199
+ if (compression !== -1) {
3200
+ if (left.length === 1 && left[0] === "") left.length = 0;
3201
+ if (right.length === 1 && right[0] === "") right.length = 0;
3202
+ }
3203
+ const parts = left.concat(right);
3204
+ let hextetCount = 0;
3205
+ for (let i = 0; i < parts.length; i++) {
3206
+ const part = parts[i];
3207
+ if (part === "") return void 0;
3208
+ if (part.indexOf(".") !== -1) {
3209
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
3210
+ hextetCount += 2;
3184
3211
  continue;
3185
3212
  }
3213
+ if (!isHextet(part)) return void 0;
3214
+ parts[i] = parseInt(part, 16).toString(16);
3215
+ hextetCount++;
3186
3216
  }
3187
- if (buffer.length) {
3188
- if (consume === consumeIsZone) {
3189
- output.zone = buffer.join("");
3190
- } else if (endIpv6) {
3191
- address.push(buffer.join(""));
3192
- } else {
3193
- address.push(stringArrayToHexStripped(buffer));
3194
- }
3217
+ if (compression === -1) {
3218
+ if (hextetCount !== 8) return void 0;
3219
+ return compressIPv6ZeroRun(parts);
3195
3220
  }
3196
- output.address = address.join("");
3197
- return output;
3221
+ if (hextetCount >= 8) return void 0;
3222
+ const expanded = parts.slice(0, left.length);
3223
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
3224
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
3225
+ return compressIPv6ZeroRun(expanded);
3198
3226
  }
3199
3227
  function normalizeIPv6(host) {
3200
- if (findToken(host, ":") < 2) {
3201
- return { host, isIPV6: false };
3202
- }
3203
- const ipv63 = getIPV6(host);
3204
- if (!ipv63.error) {
3205
- let newHost = ipv63.address;
3206
- let escapedHost = ipv63.address;
3207
- if (ipv63.zone) {
3208
- newHost += "%" + ipv63.zone;
3209
- escapedHost += "%25" + ipv63.zone;
3210
- }
3211
- return { host: newHost, isIPV6: true, escapedHost };
3212
- } else {
3213
- return { host, isIPV6: false };
3214
- }
3228
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
3229
+ const hasBracket = host[0] === "[" || host[host.length - 1] === "]";
3230
+ if (hasBracket && !bracketed) return { host, isIPV6: false, error: true };
3231
+ let input = bracketed ? host.slice(1, -1) : host;
3232
+ if (bracketed && isIPvFuture(input)) {
3233
+ input = input.toLowerCase();
3234
+ return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true };
3235
+ }
3236
+ if (findToken(input, ":") < 2) {
3237
+ return { host, isIPV6: false, error: bracketed };
3238
+ }
3239
+ let zoneIdentifier = "";
3240
+ const zoneSeparator = input.indexOf("%");
3241
+ if (zoneSeparator !== -1) {
3242
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
3243
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
3244
+ if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true };
3245
+ input = input.slice(0, zoneSeparator);
3246
+ }
3247
+ const address = normalizeIPv6Address(input);
3248
+ if (address === void 0) return { host, isIPV6: false, error: true };
3249
+ return {
3250
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
3251
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
3252
+ isIPV6: true
3253
+ };
3215
3254
  }
3216
3255
  function findToken(str, token) {
3217
3256
  let ind = 0;
@@ -3330,7 +3369,8 @@ var require_utils = __commonJS({
3330
3369
  function normalizePathEncoding(input) {
3331
3370
  let output = "";
3332
3371
  for (let i = 0; i < input.length; i++) {
3333
- if (input[i] === "%" && i + 2 < input.length) {
3372
+ const ch = input[i];
3373
+ if (ch === "%" && i + 2 < input.length) {
3334
3374
  const hex3 = input.slice(i + 1, i + 3);
3335
3375
  if (isHexPair(hex3)) {
3336
3376
  const normalizedHex = hex3.toUpperCase();
@@ -3344,10 +3384,152 @@ var require_utils = __commonJS({
3344
3384
  continue;
3345
3385
  }
3346
3386
  }
3347
- if (isPathCharacter(input[i])) {
3348
- output += input[i];
3387
+ if (isPathCharacter(ch)) {
3388
+ output += ch;
3389
+ } else {
3390
+ const code = input.charCodeAt(i);
3391
+ if (code < 128) {
3392
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3393
+ } else if (code < 55296 || code > 57343) {
3394
+ output += percentEncodeNonAscii(code);
3395
+ } else if (code <= 56319 && i + 1 < input.length) {
3396
+ const low = input.charCodeAt(i + 1);
3397
+ if (low >= 56320 && low <= 57343) {
3398
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3399
+ i++;
3400
+ } else {
3401
+ output += percentEncodeNonAscii(65533);
3402
+ }
3403
+ } else {
3404
+ output += percentEncodeNonAscii(65533);
3405
+ }
3406
+ }
3407
+ }
3408
+ return output;
3409
+ }
3410
+ function serializePathEncoding(input, pathNoScheme = false) {
3411
+ let output = "";
3412
+ let firstSegment = pathNoScheme && input[0] !== "/";
3413
+ for (let i = 0; i < input.length; i++) {
3414
+ const ch = input[i];
3415
+ if (ch === "%" && i + 2 < input.length) {
3416
+ const hex3 = input.slice(i + 1, i + 3);
3417
+ if (isHexPair(hex3)) {
3418
+ output += "%" + hex3.toUpperCase();
3419
+ i += 2;
3420
+ continue;
3421
+ }
3422
+ }
3423
+ if (ch === "/") {
3424
+ firstSegment = false;
3425
+ }
3426
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) {
3427
+ output += ch;
3428
+ } else {
3429
+ const code = input.charCodeAt(i);
3430
+ if (code < 128) {
3431
+ output += BYTE_HEX[code];
3432
+ } else if (code < 55296 || code > 57343) {
3433
+ output += percentEncodeNonAscii(code);
3434
+ } else if (code <= 56319 && i + 1 < input.length) {
3435
+ const low = input.charCodeAt(i + 1);
3436
+ if (low >= 56320 && low <= 57343) {
3437
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3438
+ i++;
3439
+ } else {
3440
+ output += percentEncodeNonAscii(65533);
3441
+ }
3442
+ } else {
3443
+ output += percentEncodeNonAscii(65533);
3444
+ }
3445
+ }
3446
+ }
3447
+ return output;
3448
+ }
3449
+ function encodeComponent(input, isAllowed) {
3450
+ let output = "";
3451
+ for (let i = 0; i < input.length; i++) {
3452
+ const ch = input[i];
3453
+ if (ch === "%" && i + 2 < input.length) {
3454
+ const hex3 = input.slice(i + 1, i + 3);
3455
+ if (isHexPair(hex3)) {
3456
+ output += "%" + hex3.toUpperCase();
3457
+ i += 2;
3458
+ continue;
3459
+ }
3460
+ }
3461
+ if (isAllowed(ch)) {
3462
+ output += ch;
3463
+ } else {
3464
+ const code = input.charCodeAt(i);
3465
+ if (code < 128) {
3466
+ output += BYTE_HEX[code];
3467
+ } else if (code < 55296 || code > 57343) {
3468
+ output += percentEncodeNonAscii(code);
3469
+ } else if (code <= 56319 && i + 1 < input.length) {
3470
+ const low = input.charCodeAt(i + 1);
3471
+ if (low >= 56320 && low <= 57343) {
3472
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3473
+ i++;
3474
+ } else {
3475
+ output += percentEncodeNonAscii(65533);
3476
+ }
3477
+ } else {
3478
+ output += percentEncodeNonAscii(65533);
3479
+ }
3480
+ }
3481
+ }
3482
+ return output;
3483
+ }
3484
+ function encodeUserinfo(input) {
3485
+ return encodeComponent(input, isUserinfoCharacter);
3486
+ }
3487
+ function encodeQuery(input) {
3488
+ return encodeComponent(input, isQueryFragmentCharacter);
3489
+ }
3490
+ function encodeFragment(input) {
3491
+ return encodeComponent(input, isQueryFragmentCharacter);
3492
+ }
3493
+ function isEscapeSafe(cp) {
3494
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
3495
+ }
3496
+ function normalizeQueryFragmentEncoding(input) {
3497
+ let output = "";
3498
+ for (let i = 0; i < input.length; i++) {
3499
+ const ch = input[i];
3500
+ if (ch === "%" && i + 2 < input.length) {
3501
+ const hex3 = input.slice(i + 1, i + 3);
3502
+ if (isHexPair(hex3)) {
3503
+ const normalizedHex = hex3.toUpperCase();
3504
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
3505
+ if (isUnreserved(decoded)) {
3506
+ output += decoded;
3507
+ } else {
3508
+ output += "%" + normalizedHex;
3509
+ }
3510
+ i += 2;
3511
+ continue;
3512
+ }
3513
+ }
3514
+ if (isQueryFragmentCharacter(ch)) {
3515
+ output += ch;
3349
3516
  } else {
3350
- output += escape(input[i]);
3517
+ const code = input.charCodeAt(i);
3518
+ if (code < 128) {
3519
+ output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
3520
+ } else if (code < 55296 || code > 57343) {
3521
+ output += percentEncodeNonAscii(code);
3522
+ } else if (code <= 56319 && i + 1 < input.length) {
3523
+ const low = input.charCodeAt(i + 1);
3524
+ if (low >= 56320 && low <= 57343) {
3525
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
3526
+ i++;
3527
+ } else {
3528
+ output += percentEncodeNonAscii(65533);
3529
+ }
3530
+ } else {
3531
+ output += percentEncodeNonAscii(65533);
3532
+ }
3351
3533
  }
3352
3534
  }
3353
3535
  return output;
@@ -3370,14 +3552,18 @@ var require_utils = __commonJS({
3370
3552
  function recomposeAuthority(component) {
3371
3553
  const uriTokens = [];
3372
3554
  if (component.userinfo !== void 0) {
3373
- uriTokens.push(component.userinfo);
3555
+ uriTokens.push(encodeUserinfo(component.userinfo));
3374
3556
  uriTokens.push("@");
3375
3557
  }
3376
3558
  if (component.host !== void 0) {
3377
- let host = unescape(component.host);
3559
+ let host = component.host;
3378
3560
  if (!isIPv4(host)) {
3379
- const ipV6res = normalizeIPv6(host);
3380
- if (ipV6res.isIPV6 === true) {
3561
+ let ipV6res = normalizeIPv6(host);
3562
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
3563
+ host = normalizePercentEncoding(host, true);
3564
+ ipV6res = normalizeIPv6(host);
3565
+ }
3566
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
3381
3567
  host = `[${ipV6res.escapedHost}]`;
3382
3568
  } else {
3383
3569
  host = reescapeHostDelimiters(host, false);
@@ -3386,8 +3572,12 @@ var require_utils = __commonJS({
3386
3572
  uriTokens.push(host);
3387
3573
  }
3388
3574
  if (typeof component.port === "number" || typeof component.port === "string") {
3575
+ const port = String(component.port);
3576
+ if (!isPort(port)) {
3577
+ throw new TypeError("URI port is malformed.");
3578
+ }
3389
3579
  uriTokens.push(":");
3390
- uriTokens.push(String(component.port));
3580
+ uriTokens.push(port);
3391
3581
  }
3392
3582
  return uriTokens.length ? uriTokens.join("") : void 0;
3393
3583
  }
@@ -3397,6 +3587,11 @@ var require_utils = __commonJS({
3397
3587
  reescapeHostDelimiters,
3398
3588
  normalizePercentEncoding,
3399
3589
  normalizePathEncoding,
3590
+ serializePathEncoding,
3591
+ normalizeQueryFragmentEncoding,
3592
+ encodeUserinfo,
3593
+ encodeQuery,
3594
+ encodeFragment,
3400
3595
  escapePreservingEscapes,
3401
3596
  removeDotSegments,
3402
3597
  isIPv4,
@@ -3412,7 +3607,7 @@ var require_schemes = __commonJS({
3412
3607
  "node_modules/fast-uri/lib/schemes.js"(exports, module) {
3413
3608
  "use strict";
3414
3609
  var { isUUID } = require_utils();
3415
- var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
3610
+ var URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
3416
3611
  var supportedSchemeNames = (
3417
3612
  /** @type {const} */
3418
3613
  [
@@ -3473,9 +3668,10 @@ var require_schemes = __commonJS({
3473
3668
  wsComponent.secure = void 0;
3474
3669
  }
3475
3670
  if (wsComponent.resourceName) {
3476
- const [path7, query] = wsComponent.resourceName.split("?");
3671
+ const queryIndex = wsComponent.resourceName.indexOf("?");
3672
+ const path7 = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
3477
3673
  wsComponent.path = path7 && path7 !== "/" ? path7 : void 0;
3478
- wsComponent.query = query;
3674
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
3479
3675
  wsComponent.resourceName = void 0;
3480
3676
  }
3481
3677
  wsComponent.fragment = void 0;
@@ -3487,7 +3683,7 @@ var require_schemes = __commonJS({
3487
3683
  return urnComponent;
3488
3684
  }
3489
3685
  const matches = urnComponent.path.match(URN_REG);
3490
- if (matches) {
3686
+ if (matches && matches[0] === urnComponent.path) {
3491
3687
  const scheme = options.scheme || urnComponent.scheme || "urn";
3492
3688
  urnComponent.nid = matches[1].toLowerCase();
3493
3689
  urnComponent.nss = matches[2];
@@ -3621,8 +3817,17 @@ var require_schemes = __commonJS({
3621
3817
  var require_fast_uri = __commonJS({
3622
3818
  "node_modules/fast-uri/index.js"(exports, module) {
3623
3819
  "use strict";
3624
- var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3820
+ var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
3625
3821
  var { SCHEMES, getSchemeHandler } = require_schemes();
3822
+ var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
3823
+ var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
3824
+ function decodeValidScheme(scheme) {
3825
+ const decodedScheme = unescape(String(scheme));
3826
+ if (!VALID_SCHEME.test(decodedScheme)) {
3827
+ throw new TypeError(MALFORMED_SCHEME_ERROR);
3828
+ }
3829
+ return decodedScheme;
3830
+ }
3626
3831
  function normalize2(uri, options) {
3627
3832
  if (typeof uri === "string") {
3628
3833
  uri = /** @type {T} */
@@ -3635,12 +3840,34 @@ var require_fast_uri = __commonJS({
3635
3840
  }
3636
3841
  function resolve(baseURI, relativeURI, options) {
3637
3842
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3638
- const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
3639
- const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
3640
- if (baseMalformed || relativeMalformed) {
3843
+ const {
3844
+ parsed: baseParsed,
3845
+ malformedAuthorityOrPort: baseMalformed,
3846
+ malformedPercentEncoding: baseMalformedPercentEncoding,
3847
+ malformedSchemeSpecific: baseMalformedSchemeSpecific,
3848
+ malformedHost: baseMalformedHost,
3849
+ malformedScheme: baseMalformedScheme
3850
+ } = parseWithStatus(baseURI, schemelessOptions);
3851
+ const {
3852
+ parsed: relativeParsed,
3853
+ malformedAuthorityOrPort: relativeMalformed,
3854
+ malformedPercentEncoding: relativeMalformedPercentEncoding,
3855
+ malformedSchemeSpecific: relativeMalformedSchemeSpecific,
3856
+ malformedHost: relativeMalformedHost,
3857
+ malformedScheme: relativeMalformedScheme
3858
+ } = parseWithStatus(relativeURI, schemelessOptions);
3859
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) {
3641
3860
  throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
3642
3861
  }
3643
3862
  const resolved2 = resolveComponent2(baseParsed, relativeParsed, schemelessOptions, true);
3863
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved2.scheme);
3864
+ const resolvedHost = resolved2.host;
3865
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
3866
+ canonicalizeHost(resolved2, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
3867
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !new RegExp("\\P{ASCII}", "u").test(resolvedHost);
3868
+ if (resolved2.error && !encodedASCIIHost) {
3869
+ throw new Error(resolved2.error);
3870
+ }
3644
3871
  schemelessOptions.skipEscape = true;
3645
3872
  return serialize(resolved2, schemelessOptions);
3646
3873
  }
@@ -3700,7 +3927,7 @@ var require_fast_uri = __commonJS({
3700
3927
  function equal(uriA, uriB, options) {
3701
3928
  const normalizedA = normalizeComparableURI(uriA, options);
3702
3929
  const normalizedB = normalizeComparableURI(uriB, options);
3703
- return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase();
3930
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
3704
3931
  }
3705
3932
  function serialize(cmpts, opts) {
3706
3933
  const component = {
@@ -3721,19 +3948,22 @@ var require_fast_uri = __commonJS({
3721
3948
  };
3722
3949
  const options = Object.assign({}, opts);
3723
3950
  const uriTokens = [];
3951
+ if (component.scheme) {
3952
+ component.scheme = decodeValidScheme(component.scheme);
3953
+ }
3724
3954
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
3725
3955
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
3956
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
3957
+ const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
3726
3958
  if (component.path !== void 0) {
3727
3959
  if (!options.skipEscape) {
3728
- component.path = escapePreservingEscapes(component.path);
3729
- if (component.scheme !== void 0) {
3730
- component.path = component.path.split("%3A").join(":");
3731
- }
3960
+ component.path = serializePathEncoding(component.path, pathNoScheme);
3732
3961
  } else {
3733
3962
  component.path = normalizePercentEncoding(component.path);
3734
3963
  }
3735
3964
  }
3736
3965
  if (options.reference !== "suffix" && component.scheme) {
3966
+ component.scheme = decodeValidScheme(component.scheme);
3737
3967
  uriTokens.push(component.scheme, ":");
3738
3968
  }
3739
3969
  const authority = recomposeAuthority(component);
@@ -3751,16 +3981,19 @@ var require_fast_uri = __commonJS({
3751
3981
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
3752
3982
  s = removeDotSegments(s);
3753
3983
  }
3984
+ if (pathNoScheme) {
3985
+ s = serializePathEncoding(s, true);
3986
+ }
3754
3987
  if (authority === void 0 && s[0] === "/" && s[1] === "/") {
3755
3988
  s = "/%2F" + s.slice(2);
3756
3989
  }
3757
3990
  uriTokens.push(s);
3758
3991
  }
3759
3992
  if (component.query !== void 0) {
3760
- uriTokens.push("?", component.query);
3993
+ uriTokens.push("?", encodeQuery(component.query));
3761
3994
  }
3762
3995
  if (component.fragment !== void 0) {
3763
- uriTokens.push("#", component.fragment);
3996
+ uriTokens.push("#", encodeFragment(component.fragment));
3764
3997
  }
3765
3998
  return uriTokens.join("");
3766
3999
  }
@@ -3776,6 +4009,35 @@ var require_fast_uri = __commonJS({
3776
4009
  }
3777
4010
  return void 0;
3778
4011
  }
4012
+ function hasMalformedPercentEncoding(component) {
4013
+ if (component === void 0) return false;
4014
+ let percent = component.indexOf("%");
4015
+ while (percent !== -1) {
4016
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
4017
+ return true;
4018
+ }
4019
+ percent = component.indexOf("%", percent + 3);
4020
+ }
4021
+ return false;
4022
+ }
4023
+ function isIPLiteral(host) {
4024
+ return host[0] === "[" && host[host.length - 1] === "]";
4025
+ }
4026
+ function hasMalformedComponentPercentEncoding(matches) {
4027
+ const host = matches[4];
4028
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4029
+ }
4030
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4031
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4032
+ try {
4033
+ parsed.host = new URL("http://" + parsed.host).hostname;
4034
+ } catch (e) {
4035
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
4036
+ return true;
4037
+ }
4038
+ }
4039
+ return false;
4040
+ }
3779
4041
  function parseWithStatus(uri, opts) {
3780
4042
  const options = Object.assign({}, opts);
3781
4043
  const parsed = {
@@ -3788,6 +4050,11 @@ var require_fast_uri = __commonJS({
3788
4050
  fragment: void 0
3789
4051
  };
3790
4052
  let malformedAuthorityOrPort = false;
4053
+ let malformedPercentEncoding = false;
4054
+ let malformedSchemeSpecific = false;
4055
+ let malformedHost = false;
4056
+ let malformedIPLiteral = false;
4057
+ let malformedScheme = false;
3791
4058
  let isIP = false;
3792
4059
  if (options.reference === "suffix") {
3793
4060
  if (options.scheme) {
@@ -3824,6 +4091,19 @@ var require_fast_uri = __commonJS({
3824
4091
  parsed.path = matches[6] || "";
3825
4092
  parsed.query = matches[7];
3826
4093
  parsed.fragment = matches[8];
4094
+ if (parsed.scheme !== void 0) {
4095
+ const decodedScheme = unescape(parsed.scheme);
4096
+ if (VALID_SCHEME.test(decodedScheme)) {
4097
+ parsed.scheme = decodedScheme.toLowerCase();
4098
+ } else {
4099
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
4100
+ malformedScheme = true;
4101
+ }
4102
+ }
4103
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
4104
+ if (malformedPercentEncoding) {
4105
+ parsed.error = parsed.error || "URI contains malformed percent-encoding.";
4106
+ }
3827
4107
  if (isNaN(parsed.port)) {
3828
4108
  parsed.port = matches[5];
3829
4109
  }
@@ -3835,9 +4115,16 @@ var require_fast_uri = __commonJS({
3835
4115
  if (parsed.host) {
3836
4116
  const ipv4result = isIPv4(parsed.host);
3837
4117
  if (ipv4result === false) {
4118
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
4119
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
3838
4120
  const ipv6result = normalizeIPv6(parsed.host);
3839
- parsed.host = ipv6result.host.toLowerCase();
3840
- isIP = ipv6result.isIPV6;
4121
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4122
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4123
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4124
+ if (malformedIPLiteral) {
4125
+ parsed.error = parsed.error || "URI host is malformed.";
4126
+ malformedAuthorityOrPort = true;
4127
+ }
3841
4128
  } else {
3842
4129
  isIP = true;
3843
4130
  }
@@ -3855,42 +4142,37 @@ var require_fast_uri = __commonJS({
3855
4142
  parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
3856
4143
  }
3857
4144
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
3858
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
3859
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
3860
- try {
3861
- parsed.host = new URL("http://" + parsed.host).hostname;
3862
- } catch (e) {
3863
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
3864
- }
4145
+ if (!malformedIPLiteral) {
4146
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4147
+ }
4148
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4149
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4150
+ if (!isIP) {
4151
+ host = normalizePercentEncoding(host.toLowerCase());
3865
4152
  }
4153
+ parsed.host = reescapeHostDelimiters(host, isIP);
3866
4154
  }
3867
4155
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
3868
- if (uri.indexOf("%") !== -1) {
3869
- if (parsed.scheme !== void 0) {
3870
- parsed.scheme = unescape(parsed.scheme);
3871
- }
3872
- if (parsed.host !== void 0) {
3873
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
3874
- }
3875
- }
3876
4156
  if (parsed.path) {
3877
4157
  parsed.path = normalizePathEncoding(parsed.path);
3878
4158
  }
4159
+ if (parsed.query) {
4160
+ parsed.query = normalizeQueryFragmentEncoding(parsed.query);
4161
+ }
3879
4162
  if (parsed.fragment) {
3880
- try {
3881
- parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
3882
- } catch {
3883
- parsed.error = parsed.error || "URI malformed";
3884
- }
4163
+ parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
3885
4164
  }
3886
4165
  }
3887
4166
  if (schemeHandler && schemeHandler.parse) {
3888
4167
  schemeHandler.parse(parsed, options);
4168
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) {
4169
+ malformedSchemeSpecific = true;
4170
+ }
3889
4171
  }
3890
4172
  } else {
3891
4173
  parsed.error = parsed.error || "URI can not be parsed.";
3892
4174
  }
3893
- return { parsed, malformedAuthorityOrPort };
4175
+ return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
3894
4176
  }
3895
4177
  function parse4(uri, opts) {
3896
4178
  return parseWithStatus(uri, opts).parsed;
@@ -3899,20 +4181,28 @@ var require_fast_uri = __commonJS({
3899
4181
  return normalizeStringWithStatus(uri, opts).normalized;
3900
4182
  }
3901
4183
  function normalizeStringWithStatus(uri, opts) {
3902
- const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
4184
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
3903
4185
  return {
3904
- normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
3905
- malformedAuthorityOrPort
4186
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
4187
+ malformedAuthorityOrPort,
4188
+ malformedPercentEncoding,
4189
+ malformedSchemeSpecific,
4190
+ malformedHost,
4191
+ malformedScheme
3906
4192
  };
3907
4193
  }
3908
4194
  function normalizeComparableURI(uri, opts) {
3909
- if (typeof uri === "string") {
3910
- const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts);
3911
- return malformedAuthorityOrPort ? void 0 : normalized;
4195
+ if (typeof uri !== "string" && typeof uri !== "object") {
4196
+ return void 0;
3912
4197
  }
3913
- if (typeof uri === "object") {
3914
- return serialize(uri, opts);
4198
+ let value;
4199
+ try {
4200
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
4201
+ } catch {
4202
+ return void 0;
3915
4203
  }
4204
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
4205
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
3916
4206
  }
3917
4207
  var fastUri = {
3918
4208
  SCHEMES,
@@ -4332,7 +4622,7 @@ var require_core = __commonJS({
4332
4622
  errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4333
4623
  if (!errors || errors.length === 0)
4334
4624
  return "No errors";
4335
- return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
4625
+ return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text2, msg) => text2 + separator + msg);
4336
4626
  }
4337
4627
  $dataMetaSchema(metaSchema, keywordsJsonPointers) {
4338
4628
  const rules = this.RULES.all;
@@ -46823,8 +47113,8 @@ function ko_default() {
46823
47113
  }
46824
47114
 
46825
47115
  // node_modules/zod/v4/locales/lt.js
46826
- var capitalizeFirstCharacter = (text) => {
46827
- return text.charAt(0).toUpperCase() + text.slice(1);
47116
+ var capitalizeFirstCharacter = (text2) => {
47117
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
46828
47118
  };
46829
47119
  function getUnitTypeFromNumber(number4) {
46830
47120
  const abs = Math.abs(number4);
@@ -57051,6 +57341,9 @@ var Protocol = class {
57051
57341
  this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => {
57052
57342
  const handleTaskResult = async () => {
57053
57343
  const taskId = request.params.taskId;
57344
+ if (!await this._taskStore.getTask(taskId, extra.sessionId)) {
57345
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
57346
+ }
57054
57347
  if (this._taskMessageQueue) {
57055
57348
  let queuedMessage;
57056
57349
  while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) {
@@ -57081,12 +57374,12 @@ var Protocol = class {
57081
57374
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
57082
57375
  }
57083
57376
  if (!isTerminal(task.status)) {
57084
- await this._waitForTaskUpdate(taskId, extra.signal);
57377
+ await this._waitForTaskUpdate(taskId, extra.signal, extra.sessionId);
57085
57378
  return await handleTaskResult();
57086
57379
  }
57087
57380
  if (isTerminal(task.status)) {
57088
57381
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
57089
- this._clearTaskQueue(taskId);
57382
+ this._clearTaskQueue(taskId, extra.sessionId);
57090
57383
  return {
57091
57384
  ...result,
57092
57385
  _meta: {
@@ -57123,7 +57416,7 @@ var Protocol = class {
57123
57416
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
57124
57417
  }
57125
57418
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
57126
- this._clearTaskQueue(request.params.taskId);
57419
+ this._clearTaskQueue(request.params.taskId, extra.sessionId);
57127
57420
  const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId);
57128
57421
  if (!cancelledTask) {
57129
57422
  throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`);
@@ -57251,6 +57544,19 @@ var Protocol = class {
57251
57544
  const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler;
57252
57545
  const capturedTransport = this._transport;
57253
57546
  const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId;
57547
+ const sessionId = capturedTransport?.sessionId;
57548
+ const store = this._taskStore;
57549
+ let relatedTaskFound = true;
57550
+ let relatedTaskLookup;
57551
+ if (relatedTaskId && store && this._taskMessageQueue && sessionId !== void 0) {
57552
+ relatedTaskFound = false;
57553
+ relatedTaskLookup = (async () => {
57554
+ if (!await store.getTask(relatedTaskId, sessionId)) {
57555
+ throw new McpError(ErrorCode.InvalidParams, `Task not found: ${relatedTaskId}`);
57556
+ }
57557
+ relatedTaskFound = true;
57558
+ })();
57559
+ }
57254
57560
  if (handler === void 0) {
57255
57561
  const errorResponse = {
57256
57562
  jsonrpc: "2.0",
@@ -57260,7 +57566,10 @@ var Protocol = class {
57260
57566
  message: "Method not found"
57261
57567
  }
57262
57568
  };
57263
- if (relatedTaskId && this._taskMessageQueue) {
57569
+ if (relatedTaskId && relatedTaskLookup) {
57570
+ const queuedError = { type: "error", message: errorResponse, timestamp: Date.now() };
57571
+ relatedTaskLookup.then(() => this._enqueueTaskMessage(relatedTaskId, queuedError, sessionId), () => capturedTransport?.send(errorResponse)).catch((error51) => this._onerror(new Error(`Failed to send an error response: ${error51}`)));
57572
+ } else if (relatedTaskId && this._taskMessageQueue) {
57264
57573
  this._enqueueTaskMessage(relatedTaskId, {
57265
57574
  type: "error",
57266
57575
  message: errorResponse,
@@ -57311,7 +57620,10 @@ var Protocol = class {
57311
57620
  closeSSEStream: extra?.closeSSEStream,
57312
57621
  closeStandaloneSSEStream: extra?.closeStandaloneSSEStream
57313
57622
  };
57314
- Promise.resolve().then(() => {
57623
+ (relatedTaskLookup ?? Promise.resolve()).then(() => {
57624
+ if (relatedTaskLookup && abortController.signal.aborted) {
57625
+ throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled");
57626
+ }
57315
57627
  if (taskCreationParams) {
57316
57628
  this.assertTaskHandlerCapability(request.method);
57317
57629
  }
@@ -57346,7 +57658,7 @@ var Protocol = class {
57346
57658
  ...error51["data"] !== void 0 && { data: error51["data"] }
57347
57659
  }
57348
57660
  };
57349
- if (relatedTaskId && this._taskMessageQueue) {
57661
+ if (relatedTaskId && this._taskMessageQueue && relatedTaskFound) {
57350
57662
  await this._enqueueTaskMessage(relatedTaskId, {
57351
57663
  type: "error",
57352
57664
  message: errorResponse,
@@ -57826,7 +58138,7 @@ var Protocol = class {
57826
58138
  throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
57827
58139
  }
57828
58140
  const maxQueueSize = this._options?.maxTaskQueueSize;
57829
- await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize);
58141
+ await this._taskMessageQueue.enqueue(taskId, message, sessionId ?? this._transport?.sessionId, maxQueueSize);
57830
58142
  }
57831
58143
  /**
57832
58144
  * Clears the message queue for a task and rejects any pending request resolvers.
@@ -57855,12 +58167,13 @@ var Protocol = class {
57855
58167
  * Uses polling to check for updates at the task's configured poll interval.
57856
58168
  * @param taskId The task ID to wait for
57857
58169
  * @param signal Abort signal to cancel the wait
58170
+ * @param sessionId Session of the request that waits, passed to the task store
57858
58171
  * @returns Promise that resolves when an update occurs or rejects if aborted
57859
58172
  */
57860
- async _waitForTaskUpdate(taskId, signal) {
58173
+ async _waitForTaskUpdate(taskId, signal, sessionId) {
57861
58174
  let interval = this._options?.defaultTaskPollInterval ?? 1e3;
57862
58175
  try {
57863
- const task = await this._taskStore?.getTask(taskId);
58176
+ const task = await this._taskStore?.getTask(taskId, sessionId);
57864
58177
  if (task?.pollInterval) {
57865
58178
  interval = task.pollInterval;
57866
58179
  }
@@ -58739,6 +59052,42 @@ var ExperimentalMcpServerTasks = class {
58739
59052
  };
58740
59053
 
58741
59054
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
59055
+ function toolInputElementCount(value, max) {
59056
+ let count = 0;
59057
+ const stack = [value];
59058
+ while (stack.length > 0) {
59059
+ const node = stack.pop();
59060
+ if (node === null || typeof node !== "object")
59061
+ continue;
59062
+ if (Array.isArray(node)) {
59063
+ for (const child of node) {
59064
+ if (++count > max)
59065
+ return count;
59066
+ if (child !== null && typeof child === "object")
59067
+ stack.push(child);
59068
+ }
59069
+ } else {
59070
+ for (const key in node) {
59071
+ if (!Object.prototype.hasOwnProperty.call(node, key))
59072
+ continue;
59073
+ if (++count > max)
59074
+ return count;
59075
+ const child = node[key];
59076
+ if (child !== null && typeof child === "object")
59077
+ stack.push(child);
59078
+ }
59079
+ }
59080
+ }
59081
+ return count;
59082
+ }
59083
+ function resolveMaxToolInputElements(value) {
59084
+ if (value === void 0 || value === Infinity)
59085
+ return void 0;
59086
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) {
59087
+ throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
59088
+ }
59089
+ return value;
59090
+ }
58742
59091
  var McpServer = class {
58743
59092
  constructor(serverInfo, options) {
58744
59093
  this._registeredResources = {};
@@ -58750,6 +59099,7 @@ var McpServer = class {
58750
59099
  this._resourceHandlersInitialized = false;
58751
59100
  this._promptHandlersInitialized = false;
58752
59101
  this.server = new Server(serverInfo, options);
59102
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
58753
59103
  }
58754
59104
  /**
58755
59105
  * Access experimental features.
@@ -58880,12 +59230,15 @@ var McpServer = class {
58880
59230
  * Validates tool input arguments against the tool's input schema.
58881
59231
  */
58882
59232
  async validateToolInput(tool, args, toolName) {
59233
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) {
59234
+ throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
59235
+ }
58883
59236
  if (!tool.inputSchema) {
58884
59237
  return void 0;
58885
59238
  }
58886
59239
  const inputObj = normalizeObjectSchema(tool.inputSchema);
58887
59240
  const schemaToParse = inputObj ?? tool.inputSchema;
58888
- const parseResult = await safeParseAsync2(schemaToParse, args);
59241
+ const parseResult = await safeParseAsync2(schemaToParse, args ?? {});
58889
59242
  if (!parseResult.success) {
58890
59243
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
58891
59244
  const errorMessage = getParseErrorMessage(error51);
@@ -59123,7 +59476,7 @@ var McpServer = class {
59123
59476
  }
59124
59477
  if (prompt.argsSchema) {
59125
59478
  const argsObj = normalizeObjectSchema(prompt.argsSchema);
59126
- const parseResult = await safeParseAsync2(argsObj, request.params.arguments);
59479
+ const parseResult = await safeParseAsync2(argsObj, request.params.arguments ?? {});
59127
59480
  if (!parseResult.success) {
59128
59481
  const error51 = "error" in parseResult ? parseResult.error : "Unknown error";
59129
59482
  const errorMessage = getParseErrorMessage(error51);
@@ -59637,6 +59990,79 @@ var StdioServerTransport = class {
59637
59990
  }
59638
59991
  };
59639
59992
 
59993
+ // src/mcp/workspaces.ts
59994
+ var fail = (text2) => ({ text: text2, isError: true });
59995
+ var LEGACY_KEY_NOTE = "This connection uses an older key bound to a single workspace. Call `connect` once (the user approves in the browser) to replace it with one key that reaches every workspace they belong to.";
59996
+ var TARGETED_TOOLS = /* @__PURE__ */ new Set(["create_video", "brand_extract", "brand_get", "motion_profile"]);
59997
+ var WORKSPACE_ACTING_TOOLS = /* @__PURE__ */ new Set([
59998
+ "create_video",
59999
+ "write_draft_files",
60000
+ "edit_draft_file",
60001
+ "export_video",
60002
+ "brand_extract",
60003
+ "install_kits",
60004
+ "install_font",
60005
+ "install_components",
60006
+ "motion_profile"
60007
+ ]);
60008
+ var CREATING_TOOLS = /* @__PURE__ */ new Set(["create_video", "brand_extract"]);
60009
+ var text = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
60010
+ function admissionTarget(toolName, args, envWorkspace) {
60011
+ const projectId = text(args.projectId);
60012
+ const draftId = text(args.draftId);
60013
+ const jobIds = Array.isArray(args.jobIds) ? args.jobIds.filter((id) => typeof id === "string").slice(0, 50) : void 0;
60014
+ const addressed = Boolean(projectId || draftId || jobIds?.length);
60015
+ const workspace = text(args.workspace) ?? (addressed ? void 0 : envWorkspace);
60016
+ const targeted = TARGETED_TOOLS.has(toolName) || Boolean(text(args.brandSlug));
60017
+ return {
60018
+ ...workspace ? { workspace } : {},
60019
+ ...projectId ? { projectId } : {},
60020
+ ...draftId ? { draftId } : {},
60021
+ ...jobIds?.length ? { jobIds } : {},
60022
+ ...!workspace && !addressed && !targeted ? { all: true } : {}
60023
+ };
60024
+ }
60025
+ function workspaceNote(toolName, workspace) {
60026
+ const label = workspaceLabel(workspace);
60027
+ const why = workspace.because ? ` (${workspace.because})` : "";
60028
+ return CREATING_TOOLS.has(toolName) ? `Created in ${label}${why}.` : `Workspace: ${label}${why}`;
60029
+ }
60030
+ function describeRow(workspace, lastOpen) {
60031
+ const usage = workspace.usage;
60032
+ return `- ${workspaceLabel(workspace)}${workspace.id === lastOpen ? " [open in the web app]" : ""} \u2014 role: ${workspace.role || "unknown"}` + (workspace.plan ? `, plan: ${workspace.plan}` : "") + (usage?.summary ? `, ${usage.summary}` : "");
60033
+ }
60034
+ var listWorkspacesTool = {
60035
+ name: "list_workspaces",
60036
+ title: "List the workspaces this connection can use",
60037
+ description: "List every Impractical workspace the connected user belongs to (id, name, slug, role, plan) and which one is open in the web app. Pass a workspace id, slug or name as `workspace` to create_video / brand_extract to choose where new work goes. Does not use quota.",
60038
+ inputSchema: {},
60039
+ local: true,
60040
+ handler: async (_args, ctx) => {
60041
+ const listing = await fetchWorkspaces(ctx.invoke);
60042
+ if (!listing.supported) {
60043
+ return {
60044
+ text: "This Impractical server does not support workspace listing yet; this connection keeps using its one workspace. account_status shows which."
60045
+ };
60046
+ }
60047
+ if (listing.keyScope !== "user") {
60048
+ const only = listing.workspaces[0];
60049
+ return {
60050
+ text: (only ? `Connected workspace: ${describeRow(only, null).slice(2)}
60051
+
60052
+ ` : "") + LEGACY_KEY_NOTE
60053
+ };
60054
+ }
60055
+ if (!listing.workspaces.length) {
60056
+ return fail("The connected user is not an active member of any workspace.");
60057
+ }
60058
+ const open = listing.workspaces.find((workspace) => workspace.id === listing.lastOpenWorkspaceId);
60059
+ return {
60060
+ text: "Workspaces:\n" + listing.workspaces.map((workspace) => describeRow(workspace, listing.lastOpenWorkspaceId)).join("\n") + "\n\nLists span all of these. Tools given a project/draft id act in its workspace. New work goes to `workspace` if passed, else " + (open ? `${workspaceLabel(open)}, the one open in the web app.` : listing.workspaces.length === 1 ? "the only workspace." : "the workspace open in the web app \u2014 none is recorded, so pass `workspace` explicitly.")
60061
+ };
60062
+ }
60063
+ };
60064
+ var WORKSPACE_TOOLS = [listWorkspacesTool];
60065
+
59640
60066
  // src/mcp/contract-mode.ts
59641
60067
  var CONTRACT_ENV = "IMPRACTICAL_CONTRACT";
59642
60068
  function b20Semantics(env = process.env) {
@@ -60511,8 +60937,8 @@ var collectFocusProbe = (input) => {
60511
60937
  if (!parent) break;
60512
60938
  for (const sibling of Array.from(parent.children)) {
60513
60939
  if (sibling === level) continue;
60514
- const text = textOf(sibling);
60515
- if (text && siblingTexts.length < 16) siblingTexts.push(text);
60940
+ const text2 = textOf(sibling);
60941
+ if (text2 && siblingTexts.length < 16) siblingTexts.push(text2);
60516
60942
  }
60517
60943
  level = parent;
60518
60944
  }
@@ -60539,11 +60965,11 @@ var NARRATIVE_ENTITY_STOPLIST = new Set(
60539
60965
  "the a an and or but nor to for with without your our their his her its my you we they it this that these those here there now new all one every each more most best better first last next meet introducing get set go from in on at by of no not just say hello welcome today tomorrow how what when where why who which try free start build ship launch power fast easy simple ai app team plan pro data live real time work works make made let us see watch look".split(/\s+/)
60540
60966
  );
60541
60967
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
60542
- var containsEntity = (text, entity) => new RegExp(`(?:^|[^A-Za-z0-9])${escapeRegExp(entity)}(?:[^A-Za-z0-9]|$)`, "i").test(text);
60968
+ var containsEntity = (text2, entity) => new RegExp(`(?:^|[^A-Za-z0-9])${escapeRegExp(entity)}(?:[^A-Za-z0-9]|$)`, "i").test(text2);
60543
60969
  function extractEntityTokens(copy) {
60544
60970
  const out = [];
60545
- for (const text of copy) {
60546
- for (const match of text.matchAll(/\b[A-Z][a-zA-Z0-9'&-]*(?:\s+[A-Z][a-zA-Z0-9'&-]*){0,2}\b/g)) {
60971
+ for (const text2 of copy) {
60972
+ for (const match of text2.matchAll(/\b[A-Z][a-zA-Z0-9'&-]*(?:\s+[A-Z][a-zA-Z0-9'&-]*){0,2}\b/g)) {
60547
60973
  const words = match[0].trim().split(/\s+/);
60548
60974
  while (words.length && NARRATIVE_ENTITY_STOPLIST.has(words[0].toLowerCase())) words.shift();
60549
60975
  while (words.length && NARRATIVE_ENTITY_STOPLIST.has(words[words.length - 1].toLowerCase()))
@@ -60565,7 +60991,7 @@ function narrativeCameraFindings(probes) {
60565
60991
  const key = entity.toLowerCase();
60566
60992
  if (flagged.has(key)) continue;
60567
60993
  if (containsEntity(probe.focusedText, entity)) continue;
60568
- const sibling = probe.siblingTexts.find((text) => containsEntity(text, entity));
60994
+ const sibling = probe.siblingTexts.find((text2) => containsEntity(text2, entity));
60569
60995
  if (!sibling) continue;
60570
60996
  flagged.add(key);
60571
60997
  out.push(
@@ -60803,8 +61229,8 @@ var collectPoseGeometry = (input) => {
60803
61229
  for (const node of Array.from(el2.childNodes)) {
60804
61230
  if (node.nodeType === Node.TEXT_NODE) direct += node.textContent ?? "";
60805
61231
  }
60806
- const text = direct.trim();
60807
- if (text.length < 3) continue;
61232
+ const text2 = direct.trim();
61233
+ if (text2.length < 3) continue;
60808
61234
  const r = el2.getBoundingClientRect();
60809
61235
  if (r.width < 2 || r.height < 8) continue;
60810
61236
  const frac = clippedVisibleFraction(r, el2);
@@ -60814,7 +61240,7 @@ var collectPoseGeometry = (input) => {
60814
61240
  edgeTexts.push({
60815
61241
  textId: tagOf(el2, "data-motion-textid"),
60816
61242
  selector: describe3(el2),
60817
- text: text.slice(0, 60),
61243
+ text: text2.slice(0, 60),
60818
61244
  opacity,
60819
61245
  visibleFraction: frac,
60820
61246
  screenHeight: r.height
@@ -61204,8 +61630,8 @@ var PLATE_EXPOSED_MIN = 0.25;
61204
61630
  var PLATE_PERSISTENCE_MIN = 0.75;
61205
61631
  function parseShotBeats(raw) {
61206
61632
  if (!raw) return [];
61207
- const text = raw.replace(/^\[shotlist\.json[^\n]*\]\s*\n/, "");
61208
- const parsed = JSON.parse(text);
61633
+ const text2 = raw.replace(/^\[shotlist\.json[^\n]*\]\s*\n/, "");
61634
+ const parsed = JSON.parse(text2);
61209
61635
  return (parsed.beats ?? []).map((b, i) => ({
61210
61636
  index: i + 1,
61211
61637
  start: Number(b.start ?? 0),
@@ -62333,8 +62759,8 @@ function collectCallerValues(ast) {
62333
62759
  const inner = t3.isJSXExpressionContainer(v) ? v.expression : v;
62334
62760
  if (t3.isJSXEmptyExpression(inner) || t3.isBooleanLiteral(inner) || t3.isNullLiteral(inner)) continue;
62335
62761
  if (t3.isIdentifier(inner) && inner.name === "undefined") continue;
62336
- const text = t3.isStringLiteral(inner) ? inner.value : t3.isTemplateLiteral(inner) ? inner.quasis.map((q) => q.value.cooked ?? "").join("\u2026") : "";
62337
- props.set(attr.name.name, [...props.get(attr.name.name) ?? [], text]);
62762
+ const text2 = t3.isStringLiteral(inner) ? inner.value : t3.isTemplateLiteral(inner) ? inner.quasis.map((q) => q.value.cooked ?? "").join("\u2026") : "";
62763
+ props.set(attr.name.name, [...props.get(attr.name.name) ?? [], text2]);
62338
62764
  }
62339
62765
  byComponent.set(el2.name.name, props);
62340
62766
  });
@@ -63008,8 +63434,8 @@ function serializeMarkSet(set2) {
63008
63434
  if (w.overflow) notes.push(`content overflows its box (${w.overflow})`);
63009
63435
  if (!w.visible) notes.push("not visible");
63010
63436
  if (w.src) notes.push(`src ${w.src}`);
63011
- const text = w.text ? JSON.stringify(w.text) : "\u2014";
63012
- lines.push(`${w.mark} | ${w.type} | ${text} | ${Math.round(w.rect.x)},${Math.round(w.rect.y)},${Math.round(w.rect.w)},${Math.round(w.rect.h)} | ${w.click ? "yes" : "no"} | ${notes.join("; ") || "\u2014"}`);
63437
+ const text2 = w.text ? JSON.stringify(w.text) : "\u2014";
63438
+ lines.push(`${w.mark} | ${w.type} | ${text2} | ${Math.round(w.rect.x)},${Math.round(w.rect.y)},${Math.round(w.rect.w)},${Math.round(w.rect.h)} | ${w.click ? "yes" : "no"} | ${notes.join("; ") || "\u2014"}`);
63013
63439
  }
63014
63440
  return lines.join("\n");
63015
63441
  }
@@ -64290,7 +64716,7 @@ async function runReviewModules(input) {
64290
64716
  // frame-sized, so geometry cannot tell the target from its wrapper or its children.
64291
64717
  // The author wrote the intent down — world.camera.zoomTo(detailCard, …).
64292
64718
  subjectAt: async (t4) => describeShot(shotAt(shots, t4)),
64293
- log: (name2, text) => writeFileSync5(path4.join(frameDir, name2), text),
64719
+ log: (name2, text2) => writeFileSync5(path4.join(frameDir, name2), text2),
64294
64720
  // One red box around the element the action names — Set-of-Mark scoped to a single
64295
64721
  // element, so the eye is pointed without the 26-mark noise that hurt the earlier test.
64296
64722
  annotate: async (jpg, action) => {
@@ -64384,7 +64810,7 @@ async function runReviewModules(input) {
64384
64810
  duration: duration3,
64385
64811
  ask: (prompt, images2) => runner(family2)(prompt, images2, { timeoutMs: 3e5 }),
64386
64812
  variant: reviewWalkVariant(),
64387
- log: (name2, text) => writeFileSync5(path4.join(frameDir, name2), text)
64813
+ log: (name2, text2) => writeFileSync5(path4.join(frameDir, name2), text2)
64388
64814
  });
64389
64815
  for (const f of res.findings) failures.push(`ACTION ${f.index} ${f.start.toFixed(2)}\u2013${f.end.toFixed(2)}s: ${f.label} \u2014 ${f.claim.slice(0, 90)} \u2014 ${f.evidence.slice(0, 160)}`);
64390
64816
  }
@@ -64493,10 +64919,10 @@ async function runReviewModules(input) {
64493
64919
  );
64494
64920
  }
64495
64921
  if (ledger) {
64496
- const text = describeLedger(ledger);
64497
- if (text.trim()) {
64922
+ const text2 = describeLedger(ledger);
64923
+ if (text2.trim()) {
64498
64924
  lines.push(" ISSUE LEDGER \u2014 the same defect seen at several actions is ONE issue; support counts how many saw it:");
64499
- for (const l of text.split("\n")) lines.push(" " + l);
64925
+ for (const l of text2.split("\n")) lines.push(" " + l);
64500
64926
  }
64501
64927
  }
64502
64928
  if (!failures.length) lines.push(" all beats clean \u2014 every module ran and every action was checked; export is not held by the review modules");
@@ -65014,8 +65440,8 @@ function pushAdvisoryEnabled(env = process.env) {
65014
65440
  }
65015
65441
  var lineOf = (content, index) => content.slice(0, index).split("\n").length;
65016
65442
  var scannable = (path7) => !path7.startsWith("kit/") && !path7.endsWith(".json") && !path7.endsWith(".md") && /\.(tsx?|jsx?|css|html)$/.test(path7);
65017
- var clip = (text, max = 90) => {
65018
- const flat = text.replace(/\s+/g, " ").trim();
65443
+ var clip = (text2, max = 90) => {
65444
+ const flat = text2.replace(/\s+/g, " ").trim();
65019
65445
  return flat.length > max ? `${flat.slice(0, max - 1)}\u2026` : flat;
65020
65446
  };
65021
65447
  var CLASSNAME_RE = /className\s*=\s*(?:"([^"]*)"|'([^']*)'|\{`([^`]*)`\})/g;
@@ -65910,7 +66336,7 @@ async function sampleTextBlocks(page, minFontPx) {
65910
66336
  const viewportHeight = window.innerHeight;
65911
66337
  const margin = 0.02 * Math.min(viewportWidth, viewportHeight);
65912
66338
  const shown = (style) => style.visibility !== "hidden" && style.display !== "none" && !(parseFloat(style.opacity) < 0.05);
65913
- const describe3 = (el2, text) => {
66339
+ const describe3 = (el2, text2) => {
65914
66340
  const style = getComputedStyle(el2);
65915
66341
  const fontSize = parseFloat(style.fontSize);
65916
66342
  if (!Number.isFinite(fontSize)) return null;
@@ -65929,7 +66355,7 @@ async function sampleTextBlocks(page, minFontPx) {
65929
66355
  }
65930
66356
  }
65931
66357
  return {
65932
- text: text.slice(0, 40),
66358
+ text: text2.slice(0, 40),
65933
66359
  fontSize,
65934
66360
  x: rect.x,
65935
66361
  y: rect.y,
@@ -65961,8 +66387,8 @@ async function sampleTextBlocks(page, minFontPx) {
65961
66387
  }
65962
66388
  for (const [root, slot] of slotOf) {
65963
66389
  if (!liveRoots.has(root)) continue;
65964
- const text = (root.textContent ?? "").replace(/\s+/g, " ").trim();
65965
- out[slot] = describe3(root, text);
66390
+ const text2 = (root.textContent ?? "").replace(/\s+/g, " ").trim();
66391
+ out[slot] = describe3(root, text2);
65966
66392
  }
65967
66393
  return out.filter((block) => block !== null);
65968
66394
  },
@@ -65982,7 +66408,7 @@ var WITNESS_SCALE_FRACTION = 0.04;
65982
66408
  var MIN_WITNESS_SECONDS = 0.9;
65983
66409
  var STATEMENT_HOLD_SECONDS = 1.2;
65984
66410
  var RIDING_DIFF_THRESHOLD = 0.05;
65985
- var normalize = (text) => text.trim().replace(/\s+/g, " ");
66411
+ var normalize = (text2) => text2.trim().replace(/\s+/g, " ");
65986
66412
  var settledBetween = (a, b, frameDiagonal) => Math.hypot(a.x + a.width / 2 - (b.x + b.width / 2), a.y + a.height / 2 - (b.y + b.height / 2)) < frameDiagonal * 0.01;
65987
66413
  var ICON_GLYPHS = /^[\s\u2190-\u21FF\u27F0-\u27FF\u2794-\u27BF\u2713\u2714\u2705\u2717\u2718\u2716\u274C\u25B6\u25C0\u25B2\u25BC\u2605\u2606\u2B50]+$|^\s*(->|=>|<-|<=|\[x\]|\[ \])\s*$/;
65988
66414
  function analyzeTextContract(input) {
@@ -66061,12 +66487,12 @@ function analyzeTextContract(input) {
66061
66487
  );
66062
66488
  const firstLocked = lockedLines?.length ? normalize(lockedLines[0]) : null;
66063
66489
  for (const [index, block] of openers.entries()) {
66064
- const text = normalize(block.text);
66065
- const isDeclaredOpening = firstLocked ? firstLocked.startsWith(text) || text.startsWith(firstLocked) : index === 0 && openers.length === 1;
66490
+ const text2 = normalize(block.text);
66491
+ const isDeclaredOpening = firstLocked ? firstLocked.startsWith(text2) || text2.startsWith(firstLocked) : index === 0 && openers.length === 1;
66066
66492
  if (!isDeclaredOpening && (firstLocked || openers.length > 1)) {
66067
66493
  findings.push({
66068
66494
  kind: "frame0-leak",
66069
- detail: `"${text.slice(0, 30)}" is visible at frame 0 but is not the film's opening \u2014 a later element is leaking its initial state. Hide it statically (App.tsx or gsap.set in setup); a from() tween does not hide anything on a cold seek.`
66495
+ detail: `"${text2.slice(0, 30)}" is visible at frame 0 but is not the film's opening \u2014 a later element is leaking its initial state. Hide it statically (App.tsx or gsap.set in setup); a from() tween does not hide anything on a cold seek.`
66070
66496
  });
66071
66497
  }
66072
66498
  }
@@ -66104,13 +66530,13 @@ function analyzeTextContract(input) {
66104
66530
  const glyphSeen = /* @__PURE__ */ new Set();
66105
66531
  for (const sample of samples) {
66106
66532
  for (const block of sample.blocks) {
66107
- const text = block.text.trim();
66108
- if (!text || block.fontSize < CANDIDATE_MIN_PX) continue;
66109
- if (!ICON_GLYPHS.test(text) || glyphSeen.has(text)) continue;
66110
- glyphSeen.add(text);
66533
+ const text2 = block.text.trim();
66534
+ if (!text2 || block.fontSize < CANDIDATE_MIN_PX) continue;
66535
+ if (!ICON_GLYPHS.test(text2) || glyphSeen.has(text2)) continue;
66536
+ glyphSeen.add(text2);
66111
66537
  findings.push({
66112
66538
  kind: "glyph-icon",
66113
- detail: `"${text}" is a text glyph doing an icon's job. Use the real icon from lucide-react (import { ArrowRight, Check } from 'lucide-react') \u2014 a glyph inherits the text font, so it sits on the baseline at the wrong weight and optical size, and it cannot take a stroke width or animate as its own element.`
66539
+ detail: `"${text2}" is a text glyph doing an icon's job. Use the real icon from lucide-react (import { ArrowRight, Check } from 'lucide-react') \u2014 a glyph inherits the text font, so it sits on the baseline at the wrong weight and optical size, and it cannot take a stroke width or animate as its own element.`
66114
66540
  });
66115
66541
  }
66116
66542
  }
@@ -70158,11 +70584,11 @@ async function openPreview(ctx, draftId) {
70158
70584
  const consoleTail = [];
70159
70585
  page.on("pageerror", (err) => pageErrors.push((err.stack || err.message || String(err)).slice(0, 900)));
70160
70586
  page.on("console", (msg) => {
70161
- const text = msg.text();
70162
- if (/music\.json|connect-src/.test(text)) return;
70163
- consoleTail.push(`console.${msg.type()}: ${text.slice(0, 300)}`);
70587
+ const text2 = msg.text();
70588
+ if (/music\.json|connect-src/.test(text2)) return;
70589
+ consoleTail.push(`console.${msg.type()}: ${text2.slice(0, 300)}`);
70164
70590
  if (consoleTail.length > 30) consoleTail.shift();
70165
- if (msg.type() === "error") pageErrors.push("console.error: " + text.slice(0, 600));
70591
+ if (msg.type() === "error") pageErrors.push("console.error: " + text2.slice(0, 600));
70166
70592
  });
70167
70593
  await page.goto(PREVIEW_URL, { waitUntil: "load", timeout: 6e4 });
70168
70594
  try {
@@ -70477,6 +70903,9 @@ var motionContextTool = {
70477
70903
  title: "Study the motion library",
70478
70904
  description: `Retrieve the current versioned house skills, relevant templates, selected production reference sources, the catalog of house kits this draft can install, and rendered style frames for this brief. CALL THIS after engine_types and before create_video. The production library is fetched live; local development may explicitly override its guidance with ${LOCAL_MOTION_LIBRARY_ENV}.`,
70479
70905
  inputSchema: {
70906
+ workspace: external_exports.string().min(1).max(200).optional().describe(
70907
+ "Workspace holding brandSlug (id, slug or name). Default: the one open in the web app, or the only one."
70908
+ ),
70480
70909
  brief: external_exports.string().min(1).describe("What the video should communicate and how it should feel."),
70481
70910
  styleId: external_exports.string().optional().describe("Optional style id from a previous motion_context result."),
70482
70911
  templateId: external_exports.string().optional().describe("Optional template id to study in detail."),
@@ -70721,37 +71150,37 @@ function cameraLint(files) {
70721
71150
  lines.forEach((raw, i) => {
70722
71151
  if (ALLOW.test(raw)) return;
70723
71152
  const line = i + 1;
70724
- const text = raw.trim().slice(0, 140);
71153
+ const text2 = raw.trim().slice(0, 140);
70725
71154
  if (path7.endsWith("App.tsx") || path7.endsWith(".tsx")) {
70726
71155
  if (APP_STAGE_TRANSFORM.test(raw))
70727
- out.push({ path: path7, line, text, severity: "note", why: "the stage element carries its own transform \u2014 that is a hand-rolled camera", fix: "remove it; the World Camera moves the stage" });
71156
+ out.push({ path: path7, line, text: text2, severity: "note", why: "the stage element carries its own transform \u2014 that is a hand-rolled camera", fix: "remove it; the World Camera moves the stage" });
70728
71157
  return;
70729
71158
  }
70730
71159
  let m;
70731
71160
  if (RETURN_TIMELINE.test(raw)) {
70732
- out.push({ path: path7, line, text, severity: "note", why: "the direction returns the timeline \u2014 the stage awaits that value, and a GSAP timeline is a thenable that resolves only when the film COMPLETES; a paused film never does, so the preview never becomes ready", fix: "return nothing from direction (delete this line); the engine already owns ctx.tl" });
71161
+ out.push({ path: path7, line, text: text2, severity: "note", why: "the direction returns the timeline \u2014 the stage awaits that value, and a GSAP timeline is a thenable that resolves only when the film COMPLETES; a paused film never does, so the preview never becomes ready", fix: "return nothing from direction (delete this line); the engine already owns ctx.tl" });
70733
71162
  return;
70734
71163
  }
70735
71164
  if (m = LEGACY.exec(raw)) {
70736
71165
  const verb = m[1];
70737
71166
  const fix = verb === "set" || verb === "full" ? "world.camera.cutTo(subject) (or world.camera.moveTo for a glide)" : verb === "move" ? "film.at(t).do(world.camera.moveTo(subject)) \u2014 name the subject, the verb centres and frames it" : verb === "crop" || verb === "focus" ? "world.camera.zoomTo(subject) (or focusOn)" : "world.camera.moveTo / zoomTo / cutTo / whipTo";
70738
- out.push({ path: path7, line, text, severity: "note", why: `legacy camera API (camera.${verb})`, fix });
71167
+ out.push({ path: path7, line, text: text2, severity: "note", why: `legacy camera API (camera.${verb})`, fix });
70739
71168
  return;
70740
71169
  }
70741
71170
  if (!b20Semantics() && HAND_TIMED_BOUNDARY.test(raw)) {
70742
- out.push({ path: path7, line, text, severity: "note", why: "a scene boundary scheduled by hand (film.at(<number>) with an exit/strike/transition)", fix: "film.cut(previous.exit(...), next.enter(...), world.camera.moveTo(...)) \u2014 the sequencer advances on the previous plan's end horizon; typed times are for in-scene cues only" });
71171
+ out.push({ path: path7, line, text: text2, severity: "note", why: "a scene boundary scheduled by hand (film.at(<number>) with an exit/strike/transition)", fix: "film.cut(previous.exit(...), next.enter(...), world.camera.moveTo(...)) \u2014 the sequencer advances on the previous plan's end horizon; typed times are for in-scene cues only" });
70743
71172
  return;
70744
71173
  }
70745
71174
  if (STAGE_TWEEN.test(raw)) {
70746
- out.push({ path: path7, line, text, severity: "note", why: "a tween on the stage element is a hand-rolled camera move", fix: "world.camera.moveTo / zoomTo on the subject instead" });
71175
+ out.push({ path: path7, line, text: text2, severity: "note", why: "a tween on the stage element is a hand-rolled camera move", fix: "world.camera.moveTo / zoomTo on the subject instead" });
70747
71176
  return;
70748
71177
  }
70749
71178
  if (m = SCENE_ROOT.exec(raw)) {
70750
71179
  const vars = m[1];
70751
71180
  if (SCALE_IN.test(vars))
70752
- out.push({ path: path7, line, text, severity: "note", why: "scaling a scene root is a zoom in disguise", fix: "world.camera.zoomTo(the component the story is about)" });
71181
+ out.push({ path: path7, line, text: text2, severity: "note", why: "scaling a scene root is a zoom in disguise", fix: "world.camera.zoomTo(the component the story is about)" });
70753
71182
  else if (TRANSLATE_IN.test(vars))
70754
- out.push({ path: path7, line, text, severity: "note", why: "translating a scene root by hand", fix: "use the engine's exit families or world.strike / world.transition for a scene change" });
71183
+ out.push({ path: path7, line, text: text2, severity: "note", why: "translating a scene root by hand", fix: "use the engine's exit families or world.strike / world.transition for a scene change" });
70755
71184
  }
70756
71185
  });
70757
71186
  }
@@ -70773,6 +71202,8 @@ var UNMETERED_MCP_TOOLS = /* @__PURE__ */ new Set([
70773
71202
  "whoami",
70774
71203
  "upgrade",
70775
71204
  "studio_link",
71205
+ // Lists the user's workspaces (multi-workspace keys); read-only.
71206
+ "list_workspaces",
70776
71207
  // This tool has a packaged local fallback and is explicitly required before
70777
71208
  // authentication-dependent work. Admission must not block that fallback.
70778
71209
  "engine_types"
@@ -70789,8 +71220,9 @@ var createSessionState = () => ({
70789
71220
  shotListByDraft: /* @__PURE__ */ new Map(),
70790
71221
  measuredByDraft: /* @__PURE__ */ new Map()
70791
71222
  });
71223
+ var WORKSPACE_ARG = external_exports.string().min(1).max(200).optional();
70792
71224
  var json2 = (value) => ({ text: JSON.stringify(value, null, 2) });
70793
- var fail = (text) => ({ text, isError: true });
71225
+ var fail2 = (text2) => ({ text: text2, isError: true });
70794
71226
  var nap = (ctx, ms) => ctx.sleep ? ctx.sleep(ms) : new Promise((resolve) => setTimeout(resolve, ms));
70795
71227
  var TERMINAL = /* @__PURE__ */ new Set(["completed", "failed", "canceled"]);
70796
71228
  var POLL_ERROR_LIMIT = 5;
@@ -70888,16 +71320,20 @@ async function measuredBlock(ctx, rows) {
70888
71320
  return parts.length ? "\n\n" + parts.join("\n") : "";
70889
71321
  }
70890
71322
  var getDraft = (ctx, draftId) => ctx.invoke("get-draft", { draftId });
70891
- var studioLink = (ctx, projectId, slug) => {
71323
+ var studioLink = (ctx, projectId, slug, workspace = ctx.callWorkspaceId) => {
70892
71324
  const base = (ctx.appUrl ?? "https://motion.impractical.ai").replace(/\/$/, "");
70893
- return `${base}/projects/${projectId}/studio${slug ? `?scene=${slug}` : ""}`;
71325
+ return `${base}/projects/${projectId}/studio${studioQuery(slug, workspace)}`;
71326
+ };
71327
+ var resolvedStudioLink = async (ctx, projectId, slug) => {
71328
+ const workspace = ctx.callWorkspaceId ?? await ctx.projectWorkspace?.(projectId).catch(() => null) ?? null;
71329
+ return studioLink(ctx, projectId, slug, workspace);
70894
71330
  };
70895
71331
  async function saveDraft(ctx, draftId, apply) {
70896
71332
  let conflicts = 0;
70897
71333
  while (true) {
70898
71334
  const draft = await getDraft(ctx, draftId);
70899
71335
  if (draft.lockedByJobId) {
70900
- return fail(
71336
+ return fail2(
70901
71337
  `Draft ${draftId} is locked by job ${draft.lockedByJobId}. Wait for it with job_status({ jobIds: ["${draft.lockedByJobId}"], wait: true }), then retry.`
70902
71338
  );
70903
71339
  }
@@ -70916,7 +71352,7 @@ async function saveDraft(ctx, draftId, apply) {
70916
71352
  const atelierFindings = atelierEnabled() ? await atelierSourceFindings(String(merged["direction.ts"] ?? ""), String(merged["App.tsx"] ?? ""), ctx) : [];
70917
71353
  const preflightError = await preflightDraft(merged);
70918
71354
  if (preflightError) {
70919
- return fail(
71355
+ return fail2(
70920
71356
  "Local build preflight failed \u2014 nothing was sent to the cloud, fix and save again:\n" + preflightError
70921
71357
  );
70922
71358
  }
@@ -70967,7 +71403,7 @@ async function saveDraft(ctx, draftId, apply) {
70967
71403
  continue;
70968
71404
  }
70969
71405
  if (status === 409) {
70970
- return fail(
71406
+ return fail2(
70971
71407
  `Save rejected (409): ${message}. The draft changed underneath this edit \u2014 re-read the file and re-apply your change on top of the new content.`
70972
71408
  );
70973
71409
  }
@@ -71037,9 +71473,24 @@ function reviewModulesFailure(capture, session, draftId) {
71037
71473
  var accountStatus = {
71038
71474
  name: "account_status",
71039
71475
  title: "Show account, plan, and MCP usage",
71040
- description: "Show the connected workspace, current role, plan, weekly MCP usage, reset time, seats, and billing state. Run this once at the start of a session to confirm the connection works.",
71476
+ description: "Show the connected workspace(s) with role, plan, weekly MCP usage, reset time, seats, and billing state. Run this once at the start of a session to confirm the connection works.",
71041
71477
  inputSchema: {},
71042
- handler: async (_args, ctx) => json2(await ctx.invoke("workspace-bootstrap"))
71478
+ handler: async (_args, ctx) => {
71479
+ const listing = await fetchWorkspaces(ctx.invoke);
71480
+ if (listing.supported && listing.keyScope === "user") {
71481
+ const open = listing.workspaces.find((w) => w.id === listing.lastOpenWorkspaceId);
71482
+ return json2({
71483
+ keyScope: "user",
71484
+ workspaces: listing.workspaces,
71485
+ openInWebApp: open ? workspaceLabel(open) : null,
71486
+ howWorkspacesWork: "This connection reaches every workspace listed. Lists span all of them; tools given a project/draft id act in its workspace; new work goes to `workspace` if passed, else the workspace open in the web app, else the only one."
71487
+ });
71488
+ }
71489
+ const snapshot = await ctx.invoke("workspace-bootstrap");
71490
+ return json2(
71491
+ listing.supported ? { ...snapshot, workspaceAccess: LEGACY_KEY_NOTE } : snapshot
71492
+ );
71493
+ }
71043
71494
  };
71044
71495
  var whoami = {
71045
71496
  ...accountStatus,
@@ -71088,8 +71539,11 @@ var upgradeTool = {
71088
71539
  var listProjects = {
71089
71540
  name: "list_projects",
71090
71541
  title: "List video projects",
71091
- description: "List video projects in the connected workspace, newest first. Large workspaces: pass limit/offset to page instead of relying on a truncated dump, and nameFilter to search. The tool pages through the server list internally, so totals describe the whole workspace (up to 2,000 projects).",
71542
+ description: "List video projects, newest first \u2014 across every workspace the user belongs to (each project tagged with its workspace), or one workspace with `workspace`. Large lists: pass limit/offset to page instead of relying on a truncated dump, and nameFilter to search. The tool pages through the server list internally, so totals describe the whole listing (up to 2,000 projects in total).",
71092
71543
  inputSchema: {
71544
+ workspace: WORKSPACE_ARG.describe(
71545
+ "Only this workspace (id, slug or name). Default: every workspace you belong to."
71546
+ ),
71093
71547
  limit: external_exports.number().int().min(1).max(200).optional().describe("Return at most this many projects (default: all)."),
71094
71548
  offset: external_exports.number().int().min(0).optional().describe("Skip this many projects, for paging."),
71095
71549
  nameFilter: external_exports.string().optional().describe("Case-insensitive substring match on the project name/slug.")
@@ -71100,7 +71554,9 @@ var listProjects = {
71100
71554
  for (let pageCount = 0; serverOffset !== null && pageCount < 20; pageCount++) {
71101
71555
  const result = await ctx.invoke("list-projects", {
71102
71556
  offset: serverOffset,
71103
- limit: 100
71557
+ limit: 100,
71558
+ // Spans every workspace for a multi-workspace key; ignored otherwise.
71559
+ ...args.workspace ? {} : { allWorkspaces: true }
71104
71560
  });
71105
71561
  const rows = result.projects ?? [];
71106
71562
  fetched.push(...rows);
@@ -71125,7 +71581,7 @@ var listProjects = {
71125
71581
  offset,
71126
71582
  returned: projects.length,
71127
71583
  hasMore: offset + projects.length < matched,
71128
- ...truncated ? { note: "Workspace exceeds 2,000 projects; totals reflect the first 2,000." } : {},
71584
+ ...truncated ? { note: "More than 2,000 projects; totals reflect the newest 2,000." } : {},
71129
71585
  projects
71130
71586
  });
71131
71587
  }
@@ -71142,6 +71598,9 @@ var createVideo = {
71142
71598
  title: "Create a video project",
71143
71599
  description: "Create a new video project and an empty draft for you to direct. This does NOT run Impractical's own director \u2014 it scaffolds App.tsx, direction.ts and brand.json and hands them to you. Read the `impractical://engine.d.ts` resource, then write the scene yourself with edit_draft_file / write_draft_files.",
71144
71600
  inputSchema: {
71601
+ workspace: WORKSPACE_ARG.describe(
71602
+ 'Workspace to create the video in (id, slug or name \u2014 e.g. when the user says "a video for Rox"). Default: the one open in the web app, else the only one; with several and none open, the call fails listing them. The result says where it landed and why.'
71603
+ ),
71145
71604
  valueProp: external_exports.string().min(1).describe("One or two sentences on what ships and why it matters."),
71146
71605
  name: external_exports.string().optional().describe("Human-readable project name. Defaults to the slug."),
71147
71606
  slug: external_exports.string().optional().describe(
@@ -71167,24 +71626,24 @@ var createVideo = {
71167
71626
  },
71168
71627
  handler: async (args, ctx) => {
71169
71628
  if (ctx.qualityLoop && !args.creativeContract) {
71170
- return fail(
71629
+ return fail2(
71171
71630
  "The local quality loop requires creativeContract before scaffolding. Name the audience, singleIdea, visualProof, referenceStrategy, productSource, opening, proof, and payoff. This is a planning gate only; it does not change the scene schema."
71172
71631
  );
71173
71632
  }
71174
71633
  if (ctx.phasedFlow) {
71175
71634
  const gate = phasedGateFailure({ concept: args.concept, content: args.content });
71176
- if (gate) return fail(gate);
71635
+ if (gate) return fail2(gate);
71177
71636
  if (shotListEnabled()) {
71178
71637
  const shotGap = shotListGateFailure(args.concept);
71179
- if (shotGap) return fail(shotGap);
71638
+ if (shotGap) return fail2(shotGap);
71180
71639
  }
71181
71640
  const refDir = refInjectDir();
71182
71641
  if (refDir) {
71183
71642
  const refGap = refInjectGateFailure(args.concept, refDir);
71184
- if (refGap) return fail(refGap);
71643
+ if (refGap) return fail2(refGap);
71185
71644
  }
71186
71645
  if (!ctx.qualityLoopState?.referenceLedger) {
71187
- return fail(
71646
+ return fail2(
71188
71647
  "The phased flow requires gather_references BEFORE create_video: record the real references this film is designed against (or an honest noneFound after exhausting the ladder: local code -> product site -> screenshots -> component-class images)."
71189
71648
  );
71190
71649
  }
@@ -71290,7 +71749,7 @@ var readDraftFile = {
71290
71749
  const path7 = String(args.path);
71291
71750
  const content = draft.files[path7];
71292
71751
  if (content === void 0) {
71293
- return fail(
71752
+ return fail2(
71294
71753
  `No file "${path7}" in draft ${args.draftId}. Available: ${Object.keys(draft.files).sort().join(", ")}`
71295
71754
  );
71296
71755
  }
@@ -71365,7 +71824,7 @@ var editDraftFile = {
71365
71824
  return { files: { [path7]: next }, deletedPaths: [] };
71366
71825
  }
71367
71826
  );
71368
- return problem ? fail(problem) : output;
71827
+ return problem ? fail2(problem) : output;
71369
71828
  }
71370
71829
  };
71371
71830
  var jobStatus = {
@@ -71474,7 +71933,7 @@ var exportVideo = {
71474
71933
  gateReform,
71475
71934
  capture: ctx.qualityLoopState?.finalMotionReviewByDraft.get(String(args.draftId))
71476
71935
  }) ?? driftMotionFailure(ctx.qualityLoopState?.finalMotionReviewByDraft.get(String(args.draftId)));
71477
- if (failure) return fail(`Quality gate blocked export: ${failure}`);
71936
+ if (failure) return fail2(`Quality gate blocked export: ${failure}`);
71478
71937
  }
71479
71938
  const started = await ctx.invoke("start-export", { draftId: args.draftId });
71480
71939
  return json2({
@@ -71490,15 +71949,25 @@ var exportVideo = {
71490
71949
  var brandList = {
71491
71950
  name: "brand_list",
71492
71951
  title: "List brand kits",
71493
- description: "List the brand kits saved in this workspace.",
71494
- inputSchema: {},
71495
- handler: async (_args, ctx) => json2(await ctx.invoke("brand-kits", { action: "list" }))
71952
+ description: "List saved brand kits \u2014 across every workspace the user belongs to (each tagged with its workspace), or one workspace with `workspace`.",
71953
+ inputSchema: {
71954
+ workspace: WORKSPACE_ARG.describe("Only this workspace. Default: all of them.")
71955
+ },
71956
+ handler: async (args, ctx) => json2(await ctx.invoke("brand-kits", {
71957
+ action: "list",
71958
+ ...args.workspace ? {} : { allWorkspaces: true }
71959
+ }))
71496
71960
  };
71497
71961
  var brandGet = {
71498
71962
  name: "brand_get",
71499
71963
  title: "Get a brand kit",
71500
71964
  description: "Get one brand kit \u2014 its colors, fonts and logo. Treat these as law when directing.",
71501
- inputSchema: { slug: external_exports.string().describe("Brand kit slug.") },
71965
+ inputSchema: {
71966
+ slug: external_exports.string().describe("Brand kit slug."),
71967
+ workspace: WORKSPACE_ARG.describe(
71968
+ "Workspace holding the kit (brand_list shows it). Default: the one open in the web app, or the only one."
71969
+ )
71970
+ },
71502
71971
  handler: async (args, ctx) => json2(await ctx.invoke("brand-kits", { action: "get", slug: args.slug }))
71503
71972
  };
71504
71973
  var brandExtract = {
@@ -71506,6 +71975,9 @@ var brandExtract = {
71506
71975
  title: "Extract a brand kit from a URL",
71507
71976
  description: "Start extracting a brand kit (colors, fonts, logo) from a website and return the run id immediately. To observe an existing run, call this tool with jobId instead of url; that status mode never starts another extraction.",
71508
71977
  inputSchema: {
71978
+ workspace: WORKSPACE_ARG.describe(
71979
+ "Workspace to save the kit in (id, slug or name). Default: the one open in the web app, else the only one; with several and none open, the call fails listing them. Pass the same value when continuing by jobId."
71980
+ ),
71509
71981
  url: external_exports.string().optional().describe("The brand website URL. Omit when continuing by jobId."),
71510
71982
  jobId: external_exports.string().optional().describe("Run id returned by a previous brand_extract call."),
71511
71983
  reextract: external_exports.boolean().optional().describe("Re-extract even if a kit already exists."),
@@ -71517,8 +71989,8 @@ var brandExtract = {
71517
71989
  handler: async (args, ctx, progress) => {
71518
71990
  const url2 = typeof args.url === "string" ? args.url : "";
71519
71991
  const jobId = typeof args.jobId === "string" ? args.jobId : "";
71520
- if (url2 && jobId) return fail("Pass either url to start or jobId to continue, not both.");
71521
- if (!url2 && !jobId) return fail("Pass url to start an extraction, or jobId to continue one.");
71992
+ if (url2 && jobId) return fail2("Pass either url to start or jobId to continue, not both.");
71993
+ if (!url2 && !jobId) return fail2("Pass url to start an extraction, or jobId to continue one.");
71522
71994
  if (url2) {
71523
71995
  const started = await ctx.invoke("brand-extraction", {
71524
71996
  action: "start",
@@ -71538,7 +72010,7 @@ var brandExtract = {
71538
72010
  if (!args.wait) {
71539
72011
  const status = await readStatus();
71540
72012
  if (status.status === "error" || status.status === "failed" || status.error) {
71541
- return fail(`Brand extraction failed${status.error ? `: ${status.error}` : ""}`);
72013
+ return fail2(`Brand extraction failed${status.error ? `: ${status.error}` : ""}`);
71542
72014
  }
71543
72015
  return json2({ jobId, ...status });
71544
72016
  }
@@ -71560,7 +72032,7 @@ var brandExtract = {
71560
72032
  return json2({ jobId, ...status });
71561
72033
  }
71562
72034
  if (status.status === "error" || status.status === "failed" || status.error) {
71563
- return fail(`Brand extraction failed${status.error ? `: ${status.error}` : ""}`);
72035
+ return fail2(`Brand extraction failed${status.error ? `: ${status.error}` : ""}`);
71564
72036
  }
71565
72037
  } catch (error51) {
71566
72038
  lastError = error51 instanceof Error ? error51.message : String(error51);
@@ -71588,6 +72060,9 @@ var motionProfile = {
71588
72060
  title: "Manage the personal motion profile",
71589
72061
  description: "Inspect and manage the user's learned motion preferences. Call with action 'get' to see the effective profile before directing. Use 'remember' ONLY when the user explicitly asks to remember a preference; use 'propose' to submit an inferred preference as a pending proposal for the user to confirm \u2014 never confirm an inferred preference yourself. 'confirm'/'reject' decide a pending proposal after asking the user; 'forget', 'pause', 'resume' and 'reset' manage stored preferences on the user's behalf.",
71590
72062
  inputSchema: {
72063
+ workspace: WORKSPACE_ARG.describe(
72064
+ "Workspace whose profile layers to use. Default: the draft's workspace when draftId is given, else the one open in the web app, else the only one."
72065
+ ),
71591
72066
  action: external_exports.enum(["get", "remember", "propose", "confirm", "reject", "forget", "pause", "resume", "reset"]).describe("The profile command to run."),
71592
72067
  rule: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe(
71593
72068
  "Structured preference for remember/propose, e.g. {kind:'trait',trait:'pacing',value:'fast'}, {kind:'library',itemType:'style',itemId:'optical',stance:'prefer'}, {kind:'freeform',text:'...'}, {kind:'duration',max:15}, or {kind:'example',draftId:'...'}."
@@ -71599,7 +72074,10 @@ var motionProfile = {
71599
72074
  contentType: external_exports.string().optional().describe("Content type this preference applies to."),
71600
72075
  draftId: external_exports.string().optional().describe("Draft that motivated a proposal.")
71601
72076
  },
71602
- handler: async (args, ctx) => json2(await ctx.invoke("motion-profile", args))
72077
+ handler: async (args, ctx) => {
72078
+ const { workspace: _workspace, ...body } = args;
72079
+ return json2(await ctx.invoke("motion-profile", body));
72080
+ }
71603
72081
  };
71604
72082
  var studioLinkTool = {
71605
72083
  name: "studio_link",
@@ -71610,7 +72088,7 @@ var studioLinkTool = {
71610
72088
  slug: external_exports.string().optional().describe("Draft slug to open directly.")
71611
72089
  },
71612
72090
  handler: async (args, ctx) => ({
71613
- text: studioLink(ctx, String(args.projectId), args.slug)
72091
+ text: await resolvedStudioLink(ctx, String(args.projectId), args.slug)
71614
72092
  })
71615
72093
  };
71616
72094
  var REMOTE_SAFE_TOOLS = [
@@ -71759,10 +72237,10 @@ function kitCatalog(sources, {
71759
72237
  if (header.length) break;
71760
72238
  continue;
71761
72239
  }
71762
- const text = line.replace(/^\/\/\s*/, "").replace(/[─]+/g, "").trim();
71763
- if (!text) continue;
71764
- if (!header.length) standsAlone = KIT_TITLE_BAR.test(raw) || /[.!?]$/.test(text);
71765
- header.push(text);
72240
+ const text2 = line.replace(/^\/\/\s*/, "").replace(/[─]+/g, "").trim();
72241
+ if (!text2) continue;
72242
+ if (!header.length) standsAlone = KIT_TITLE_BAR.test(raw) || /[.!?]$/.test(text2);
72243
+ header.push(text2);
71766
72244
  if (header.length >= (headerMode === "title" && standsAlone ? 1 : 2)) break;
71767
72245
  }
71768
72246
  const exports = [...file2.content.matchAll(/^export (?:const|function) (\w+)/gm)].map((m) => m[1]).filter((n) => !n.startsWith("_")).filter(
@@ -72298,7 +72776,7 @@ var openStudioTool = {
72298
72776
  slug: external_exports.string().optional().describe("Draft slug to open directly.")
72299
72777
  },
72300
72778
  handler: async (args, ctx) => {
72301
- const url2 = studioLink(ctx, String(args.projectId), args.slug);
72779
+ const url2 = await resolvedStudioLink(ctx, String(args.projectId), args.slug);
72302
72780
  const needle = `/projects/${String(args.projectId)}`;
72303
72781
  try {
72304
72782
  const existing = await findOpenStudioTab(needle);
@@ -72320,9 +72798,11 @@ var openStudioTool = {
72320
72798
  // src/mcp/connect.ts
72321
72799
  var CONNECT_WAIT_SECONDS = 300;
72322
72800
  async function connectWorkspace(input, deps) {
72323
- const grant = await deps.request(deps.supabaseUrl, "cli-device-auth", {
72324
- action: "start"
72325
- });
72801
+ const grant = await deps.request(
72802
+ deps.supabaseUrl,
72803
+ "cli-device-auth",
72804
+ DEVICE_START_BODY
72805
+ );
72326
72806
  const browserOpened = deps.open(grant.verificationUri);
72327
72807
  const expiry = Date.parse(grant.expiresAt);
72328
72808
  const deadline = Math.min(
@@ -72347,7 +72827,12 @@ async function connectWorkspace(input, deps) {
72347
72827
  supabaseUrl: deps.supabaseUrl,
72348
72828
  appUrl: deps.appUrl
72349
72829
  });
72350
- return { status: "connected", userCode: grant.userCode, browserOpened };
72830
+ return {
72831
+ status: "connected",
72832
+ userCode: grant.userCode,
72833
+ browserOpened,
72834
+ keyScope: grantScope(poll)
72835
+ };
72351
72836
  }
72352
72837
  await deps.sleep(intervalMs);
72353
72838
  }
@@ -72361,7 +72846,7 @@ async function connectWorkspace(input, deps) {
72361
72846
  var connectTool = {
72362
72847
  name: "connect",
72363
72848
  title: "Connect this workspace",
72364
- description: "Authenticate Impractical Motion for this machine. Opens the user's browser to approve the connection, waits for them to confirm, and stores the credential locally. TELL THE USER YOU ARE ABOUT TO OPEN THEIR BROWSER BEFORE CALLING THIS \u2014 the call blocks until they approve. Use it whenever a tool reports that the workspace is not connected.",
72849
+ description: "Authenticate Impractical Motion for this machine. Opens the user's browser to approve the connection, waits for them to confirm, and stores the credential locally. TELL THE USER YOU ARE ABOUT TO OPEN THEIR BROWSER BEFORE CALLING THIS \u2014 the call blocks until they approve. Use it whenever a tool reports that the workspace is not connected. The key it stores reaches every workspace the user belongs to; calling it again also upgrades an older single-workspace connection.",
72365
72850
  inputSchema: {
72366
72851
  waitSeconds: external_exports.number().optional().describe(`How long to wait for browser approval. Default ${CONNECT_WAIT_SECONDS}.`)
72367
72852
  },
@@ -72392,6 +72877,13 @@ var connectTool = {
72392
72877
  };
72393
72878
  }
72394
72879
  try {
72880
+ if (result.keyScope === "user") {
72881
+ const listing = await fetchWorkspaces(ctx.invoke);
72882
+ const open = listing.workspaces.find((w) => w.id === listing.lastOpenWorkspaceId);
72883
+ return {
72884
+ text: `Connected. This key reaches all ${listing.workspaces.length} of the user's workspaces: ` + listing.workspaces.map(workspaceLabel).join(", ") + ". Lists span all of them; tools that address a project or draft act in its workspace; new videos go to the workspace named with `workspace`, else " + (open ? `the one open in the web app (now ${workspaceLabel(open)})` : "the one open in the web app") + (listing.workspaces.length === 1 ? " \u2014 here the only one." : ".")
72885
+ };
72886
+ }
72395
72887
  const snapshot = await ctx.invoke(
72396
72888
  "workspace-bootstrap"
72397
72889
  );
@@ -72448,6 +72940,7 @@ var engineTypesTool = {
72448
72940
  };
72449
72941
  var TOOLS = [
72450
72942
  connectTool,
72943
+ ...WORKSPACE_TOOLS,
72451
72944
  upgradeTool,
72452
72945
  ...REMOTE_SAFE_TOOLS,
72453
72946
  getFramesTool,
@@ -72565,12 +73058,23 @@ function register(server, tool, ctx) {
72565
73058
  async (args, extra) => {
72566
73059
  try {
72567
73060
  const admissionName = tool.admitAs ?? tool.name;
72568
- const admission = ctx.admit && !UNMETERED_MCP_TOOLS.has(admissionName) ? await ctx.admit(admissionName) : null;
72569
- const execute = () => tool.handler(args ?? {}, ctx, toolProgress(extra));
72570
- const output = admission?.receipt && ctx.withAdmission ? await ctx.withAdmission(admission.receipt, execute) : await execute();
73061
+ const admission = ctx.admit && !UNMETERED_MCP_TOOLS.has(admissionName) ? await ctx.admit(
73062
+ admissionName,
73063
+ admissionTarget(tool.name, args ?? {}, ctx.defaultWorkspace)
73064
+ ) : null;
73065
+ const admitted = admission?.keyScope === "user" ? admission.workspace ?? null : null;
73066
+ const linkWorkspace = admission?.workspace?.id && admission.workspace.reason !== "any" ? admission.workspace.id : void 0;
73067
+ const callCtx = linkWorkspace ? { ...ctx, callWorkspaceId: linkWorkspace } : ctx;
73068
+ const execute = () => tool.handler(args ?? {}, callCtx, toolProgress(extra));
73069
+ const output = admission?.receipt && ctx.withAdmission ? await ctx.withAdmission(admission.receipt, execute, admitted?.id) : await execute();
72571
73070
  if (admission?.warning) output.text += `
72572
73071
 
72573
73072
  ${formatUsageNotice(admission.warning)}`;
73073
+ if (admitted && !output.isError && WORKSPACE_ACTING_TOOLS.has(tool.name)) {
73074
+ output.text += `
73075
+
73076
+ ${workspaceNote(tool.name, admitted)}`;
73077
+ }
72574
73078
  return toContent(output);
72575
73079
  } catch (error51) {
72576
73080
  const status = error51.status;
@@ -72588,7 +73092,11 @@ ${formatUsageNotice(admission.warning)}`;
72588
73092
  isError: true
72589
73093
  });
72590
73094
  }
72591
- const hint = status === 401 || isNotConnected(message) ? "\n\nCall the `connect` tool to authenticate this workspace." : status === 402 ? "\n\nThe workspace needs an active subscription or trial." : "";
73095
+ const hint = status === 401 || isNotConnected(message) ? "\n\nCall the `connect` tool to authenticate this workspace." : status === 409 && details?.error === "workspace_selection_required" ? `
73096
+
73097
+ The user belongs to several workspaces${workspaceChoices(details)} and none was named or open in the web app. Ask which one, then retry with \`workspace\`.` : status === 403 && details?.error === "workspace_access_denied" ? `
73098
+
73099
+ That workspace is not available to this user${workspaceChoices(details)}. Call list_workspaces and retry with one of them.` : status === 402 ? "\n\nThe workspace needs an active subscription or trial." : "";
72592
73100
  return toContent({ text: `${tool.name} failed: ${message}${hint}`, isError: true });
72593
73101
  }
72594
73102
  }
@@ -72678,6 +73186,31 @@ function createServer(ctx) {
72678
73186
  registerEngineResource(server);
72679
73187
  return server;
72680
73188
  }
73189
+ function workspaceChoices(details) {
73190
+ const list = Array.isArray(details?.workspaces) ? details.workspaces : [];
73191
+ const names = list.filter((row) => typeof row === "object" && row !== null && typeof row.id === "string" && /^[A-Za-z0-9_-]{1,100}$/.test(row.id)).slice(0, 20).map(
73192
+ (row) => typeof row.name === "string" && row.name.length <= 100 ? `${row.name.replace(/[\u0000-\u001f]/g, "")} \u2014 ${row.id}` : row.id
73193
+ );
73194
+ return names.length ? ` (${names.join(", ")})` : "";
73195
+ }
73196
+ function createProjectWorkspaceLookup(request = (body) => {
73197
+ const { apiKey, supabaseUrl } = requireConfig();
73198
+ return invokeAt(supabaseUrl, "workspace-bootstrap", body, apiKey);
73199
+ }) {
73200
+ const cache2 = /* @__PURE__ */ new Map();
73201
+ return async (projectId) => {
73202
+ if (cache2.has(projectId)) return cache2.get(projectId) ?? null;
73203
+ let id = null;
73204
+ try {
73205
+ const result = await request({ action: "project_workspace", projectId });
73206
+ id = typeof result?.workspace?.id === "string" ? result.workspace.id : null;
73207
+ } catch {
73208
+ id = null;
73209
+ }
73210
+ cache2.set(projectId, id);
73211
+ return id;
73212
+ };
73213
+ }
72681
73214
  async function runStdioServer() {
72682
73215
  const config2 = loadConfig();
72683
73216
  if (!config2.apiKey) {
@@ -72699,7 +73232,9 @@ async function runStdioServer() {
72699
73232
  phasedFlow: phasedFlowEnabled(),
72700
73233
  // stdio runs on the user's machine, so completed preview builds can be
72701
73234
  // measured in the local browser and answered with what the frame became.
72702
- measurePreview: measureStage
73235
+ measurePreview: measureStage,
73236
+ defaultWorkspace: config2.workspace,
73237
+ projectWorkspace: createProjectWorkspaceLookup()
72703
73238
  });
72704
73239
  await server.connect(new StdioServerTransport());
72705
73240
  const served = TOOLS.length + 2 + createComponentTools().length + (motionLabEnabled() ? 1 : 0) + (phasedFlowEnabled() ? 1 : 0);
@@ -72709,6 +73244,7 @@ async function runStdioServer() {
72709
73244
  export {
72710
73245
  ENGINE_DTS_URI,
72711
73246
  buildServerInstructions,
73247
+ createProjectWorkspaceLookup,
72712
73248
  createServer,
72713
73249
  runStdioServer
72714
73250
  };