#pragma once #include "tool.hpp" #include #include #include #include class QNetworkReply; class QTimer; namespace ZShell::llm { // Fetches an http(s) URL and returns its content as plain text or raw // HTML. Read-only. Mirrors opencode's webfetch tool, without markdown // conversion and the permission prompt. Concurrent fetches are // supported; results are always delivered on a later event loop // iteration, never synchronously from execute(). class WebFetchTool : public LlmTool { Q_OBJECT public: static constexpr int MaxResponseBytes = 5 * 1024 * 1024; static constexpr int DefaultTimeoutSeconds = 30; static constexpr int MaxTimeoutSeconds = 120; // Caps the characters handed to the model so a large page cannot // blow out the context. static constexpr int MaxOutputChars = 64 * 1024; explicit WebFetchTool(QObject* parent = nullptr); ~WebFetchTool() override; QString name() const override; QString description() const override; QJsonObject parameters() const override; void execute( const QJsonObject& args, std::function done) override; void cancel() override; // Strips tags (skipping script/style/noscript/iframe/object/embed/ // head) and decodes common entities. static QString extractTextFromHtml(const QString& html); static QString decodeEntities(const QString& text); private: struct Job { QNetworkReply* reply = nullptr; QTimer* timer = nullptr; QByteArray body; bool tooLarge = false; QString format; std::function done; }; void completeJob(Job* job, QJsonObject result); QNetworkAccessManager m_manager; QList m_jobs; }; } // namespace ZShell::llm