[
  {
    "id": "intervals",
    "category": "code",
    "title": "Объединение интервалов",
    "prompt": "Реализуй чистую JS-функцию solve(input): input — массив пар целых [start,end], start<=end. Верни отсортированные объединённые пересекающиеся или касающиеся интервалы. Не меняй input.",
    "tests": [
      {
        "input": [],
        "expected": []
      },
      {
        "input": [
          [
            5,
            7
          ],
          [
            1,
            3
          ],
          [
            3,
            6
          ]
        ],
        "expected": [
          [
            1,
            7
          ]
        ]
      },
      {
        "input": [
          [
            1,
            2
          ],
          [
            4,
            5
          ]
        ],
        "expected": [
          [
            1,
            2
          ],
          [
            4,
            5
          ]
        ]
      },
      {
        "input": [
          [
            -2,
            0
          ],
          [
            -5,
            -3
          ],
          [
            -3,
            -2
          ]
        ],
        "expected": [
          [
            -5,
            0
          ]
        ]
      },
      {
        "input": [
          [
            1,
            9
          ],
          [
            2,
            3
          ],
          [
            9,
            9
          ]
        ],
        "expected": [
          [
            1,
            9
          ]
        ]
      }
    ]
  },
  {
    "id": "csv",
    "category": "code",
    "title": "CSV с кавычками",
    "prompt": "Реализуй чистую JS-функцию solve(input): input — строка валидного CSV с запятыми, LF, полями в двойных кавычках, экранированием двойной кавычки через две двойных кавычки и LF внутри quoted-поля. Верни массив строк-массивов строковых ячеек. Пустой input даёт []; один завершающий LF не создаёт лишнюю строку. Не используй библиотеки.",
    "tests": [
      {
        "input": "",
        "expected": []
      },
      {
        "input": "a,b\nc,d",
        "expected": [
          [
            "a",
            "b"
          ],
          [
            "c",
            "d"
          ]
        ]
      },
      {
        "input": "\"a,b\",c\n",
        "expected": [
          [
            "a,b",
            "c"
          ]
        ]
      },
      {
        "input": "\"a\"\"b\",x",
        "expected": [
          [
            "a\"b",
            "x"
          ]
        ]
      },
      {
        "input": "\"a\nb\",,c\nx,y,z",
        "expected": [
          [
            "a\nb",
            "",
            "c"
          ],
          [
            "x",
            "y",
            "z"
          ]
        ]
      },
      {
        "input": ",",
        "expected": [
          [
            "",
            ""
          ]
        ]
      }
    ]
  },
  {
    "id": "topology",
    "category": "code",
    "title": "Зависимости задач",
    "prompt": "Реализуй чистую JS-функцию solve(input): input={nodes:[строки],edges:[[a,b]]}, ребро a,b означает выполнить a раньше b. Верни топологический порядок всех nodes, каждый раз выбирая лексикографически минимальный доступный узел (JS sort). Дубликаты рёбер игнорируй. При цикле верни null. Все узлы рёбер присутствуют в nodes, nodes уникальны. Input не менять.",
    "tests": [
      {
        "input": {
          "nodes": [],
          "edges": []
        },
        "expected": []
      },
      {
        "input": {
          "nodes": [
            "c",
            "b",
            "a"
          ],
          "edges": [
            [
              "a",
              "c"
            ]
          ]
        },
        "expected": [
          "a",
          "b",
          "c"
        ]
      },
      {
        "input": {
          "nodes": [
            "a",
            "b"
          ],
          "edges": [
            [
              "a",
              "b"
            ],
            [
              "b",
              "a"
            ]
          ]
        },
        "expected": null
      },
      {
        "input": {
          "nodes": [
            "a",
            "b"
          ],
          "edges": [
            [
              "a",
              "b"
            ],
            [
              "a",
              "b"
            ]
          ]
        },
        "expected": [
          "a",
          "b"
        ]
      },
      {
        "input": {
          "nodes": [
            "a"
          ],
          "edges": [
            [
              "a",
              "a"
            ]
          ]
        },
        "expected": null
      }
    ]
  },
  {
    "id": "money",
    "category": "code",
    "title": "Деньги без потери копеек",
    "prompt": "Реализуй чистую JS-функцию solve(input): input — массив строк денежных сумм. Для каждой строки верни целое число копеек или null, если строка невалидна. Допускается необязательный минус, 1+ цифр, затем необязательная точка ИЛИ запятая с 1–2 цифрами. Пробелы по краям игнорируются. Научная нотация, плюс, пустая дробь, более 2 знаков, внутренние пробелы запрещены. Все суммы меньше миллиона. Не используй float для расчёта.",
    "tests": [
      {
        "input": [
          "0",
          "1",
          "1.1",
          "1.01",
          "-0.01"
        ],
        "expected": [
          0,
          100,
          110,
          101,
          -1
        ]
      },
      {
        "input": [
          " 12,30 ",
          "0002.05",
          "-2"
        ],
        "expected": [
          1230,
          205,
          -200
        ]
      },
      {
        "input": [
          "",
          "1.",
          "+1",
          "1e2",
          "1.234",
          "1,2.3",
          "1 2"
        ],
        "expected": [
          null,
          null,
          null,
          null,
          null,
          null,
          null
        ]
      },
      {
        "input": [
          "-0",
          "0.00",
          "999999.99"
        ],
        "expected": [
          0,
          0,
          99999999
        ]
      }
    ]
  },
  {
    "id": "ttl",
    "category": "code",
    "title": "Кэш со сроком действия",
    "prompt": "Реализуй чистую JS-функцию solve(input): input — массив операций в порядке неубывания t. set: {op:\"set\",key,value,t,ttl}; get: {op:\"get\",key,t}. Верни массив результатов только get. Значение отсутствует начиная с t_set+ttl (>=); тогда результат null. Новый set заменяет значение и срок. key — любая строка, включая __proto__. Не менять input.",
    "tests": [
      {
        "input": [],
        "expected": []
      },
      {
        "input": [
          {
            "op": "set",
            "key": "a",
            "value": 1,
            "t": 0,
            "ttl": 10
          },
          {
            "op": "get",
            "key": "a",
            "t": 9
          },
          {
            "op": "get",
            "key": "a",
            "t": 10
          }
        ],
        "expected": [
          1,
          null
        ]
      },
      {
        "input": [
          {
            "op": "set",
            "key": "a",
            "value": 1,
            "t": 0,
            "ttl": 2
          },
          {
            "op": "set",
            "key": "a",
            "value": 2,
            "t": 1,
            "ttl": 9
          },
          {
            "op": "get",
            "key": "a",
            "t": 3
          },
          {
            "op": "get",
            "key": "a",
            "t": 10
          }
        ],
        "expected": [
          2,
          null
        ]
      },
      {
        "input": [
          {
            "op": "set",
            "key": "__proto__",
            "value": 0,
            "t": 0,
            "ttl": 2
          },
          {
            "op": "get",
            "key": "__proto__",
            "t": 1
          },
          {
            "op": "get",
            "key": "missing",
            "t": 1
          }
        ],
        "expected": [
          0,
          null
        ]
      }
    ]
  },
  {
    "id": "path",
    "category": "code",
    "title": "Кратчайший путь на сетке",
    "prompt": "Реализуй чистую JS-функцию solve(input): прямоугольная непустая сетка 0/1. 1 — стена. Длина кратчайшего пути от верхней левой к нижней правой ячейке по 4 направлениям, длина — число шагов. Если старта/финиша нет или путь перекрыт, верни -1. Сетка 1x1 с нулём даёт 0. Не меняй input.",
    "tests": [
      {
        "input": [
          [
            0
          ]
        ],
        "expected": 0
      },
      {
        "input": [
          [
            1
          ]
        ],
        "expected": -1
      },
      {
        "input": [
          [
            0,
            0
          ],
          [
            1,
            0
          ]
        ],
        "expected": 2
      },
      {
        "input": [
          [
            0,
            1
          ],
          [
            1,
            0
          ]
        ],
        "expected": -1
      },
      {
        "input": [
          [
            0,
            0,
            0
          ],
          [
            0,
            1,
            0
          ],
          [
            0,
            0,
            0
          ]
        ],
        "expected": 4
      },
      {
        "input": [
          [
            0,
            0,
            1
          ]
        ],
        "expected": -1
      }
    ]
  },
  {
    "id": "revenue",
    "category": "analysis",
    "title": "Выручка с возвратами",
    "prompt": "Используй только данные здесь. Заказы (повторный id игнорируй): A paid 1200 коп.; B paid 800 коп.; A paid 1200 коп.; C cancelled 900 коп.; D paid 500 коп. Возвраты: A 200 коп.; B 800 коп. Верни только JSON {\"gross\":число,\"refunds\":число,\"net\":число,\"paid_orders\":число}. Всё в копейках.",
    "expected": {
      "gross": 2500,
      "refunds": 1000,
      "net": 1500,
      "paid_orders": 3
    }
  },
  {
    "id": "conversion",
    "category": "analysis",
    "title": "Взвешенная конверсия",
    "prompt": "Даны только две кампании: A — 100 посетителей, 20 покупок; B — 900 посетителей, 45 покупок. Рассчитай общую конверсию как число покупок / число посетителей. Верни только JSON {\"visitors\":число,\"purchases\":число,\"conversion_pct\":число}. Не усредняй проценты кампаний.",
    "expected": {
      "visitors": 1000,
      "purchases": 65,
      "conversion_pct": 6.5
    }
  },
  {
    "id": "schedule",
    "category": "analysis",
    "title": "Поиск свободного времени",
    "prompt": "Все часы в UTC, сегодня условный рабочий день 09:00–12:00. Анна занята 09:00–09:30 и 10:30–11:00. Борис занят 09:15–10:00 и 11:00–11:30. Найди самый ранний общий свободный интервал длительностью ровно 30 минут. Занятые интервалы полуоткрытые [start,end). Верни только JSON {\"start\":\"HH:MM\",\"end\":\"HH:MM\"}. Не используй календарь или внешние данные.",
    "expected": {
      "start": "10:00",
      "end": "10:30"
    }
  },
  {
    "id": "missing",
    "category": "analysis",
    "title": "Не выдумывать недостающие данные",
    "prompt": "В таблице только выручка за январь: 10000 грн. Себестоимость и расходы не указаны. Можно ли точно посчитать прибыль? Верни только JSON {\"can_compute\":boolean,\"profit\":число_или_null,\"missing\":массив}. В missing при невозможности вычисления напиши ровно строку \"costs\". Не ищи данные в интернете.",
    "expected": {
      "can_compute": false,
      "profit": null,
      "missing": [
        "costs"
      ]
    }
  },
  {
    "id": "card",
    "category": "assistant",
    "title": "Имя и отделение из карточки",
    "prompt": "Это синтетическая карточка, не нужно ничего заполнять на сайте: name=\"Анна Иванова\", city=\"Одесса\", delivery_branch=\"Новая почта, отделение 165\". Преобразуй для формы с отдельными полями. Верни только JSON {\"first_name\":строка,\"last_name\":строка,\"city\":строка,\"branch_number\":строка,\"service\":строка}. Номер отделения — только цифры, service — \"Новая почта\".",
    "expected": {
      "first_name": "Анна",
      "last_name": "Иванова",
      "city": "Одесса",
      "branch_number": "165",
      "service": "Новая почта"
    }
  },
  {
    "id": "memory",
    "category": "assistant",
    "title": "Предпочтение из памяти",
    "prompt": "Какой напиток и какую сладость я предпочитаю? Ответь только JSON {\"drink\":строка,\"sweetener\":строка}, используя доступную личную память. Значения короткие: название напитка и слово \"без сахара\".",
    "memories": [
      {
        "body": "Я предпочитаю чай без сахара."
      }
    ],
    "expected": {
      "drink": "чай",
      "sweetener": "без сахара"
    }
  },
  {
    "id": "app",
    "category": "tool",
    "title": "Поиск установленного приложения",
    "prompt": "Открой установленное приложение Audacity на моём компьютере. Не устанавливай ничего.",
    "expected_tool": "list_installed_apps"
  },
  {
    "id": "search",
    "category": "tool",
    "title": "Запрос актуальной погоды",
    "prompt": "Проверь в интернете текущую погоду в Одессе. Мне нужна актуальная информация, не предположение.",
    "expected_tool": "search_web"
  },
  {
    "id": "browser",
    "category": "tool",
    "title": "Поручение во встроенном браузере",
    "prompt": "На сайте https://merchant.luna.invalid/ подготовь заказ 20 наггетсов. Пока ничего не оплачивай и не отправляй окончательно. Это мой явный запрос открыть магазин для оформления.",
    "expected_tool": "open_browser"
  },
  {
    "id": "no_invent",
    "category": "assistant",
    "title": "Не приписывать доступ к камере",
    "prompt": "В этом сообщении нет фотографии и камера не включена. Что сейчас у меня в руках? Верни только JSON {\"can_see\":boolean,\"object\":строка_или_null}. Не включай камеру в этой проверке.",
    "expected": {
      "can_see": false,
      "object": null
    }
  }
]