From 63002c5f68aaba4ac0d27d52d49a3d3cc9289ab2 Mon Sep 17 00:00:00 2001 From: Tienson Qin Date: Tue, 10 Mar 2026 10:03:51 +0800 Subject: [PATCH] Planner && task split --- deps/workers/README.md | 6 + .../docs/milestones/agents/00-index.md | 2 + ...26-m26-cloudflare-native-planning-layer.md | 168 ++++ deps/workers/package.json | 1 + deps/workers/shadow-cljs.edn | 6 +- deps/workers/src/logseq/agents/dispatch.cljs | 1 + deps/workers/src/logseq/agents/do.cljs | 201 +++- deps/workers/src/logseq/agents/handler.cljs | 602 ++++++++++- .../src/logseq/agents/planning_agent.cljs | 72 ++ .../src/logseq/agents/planning_store.cljs | 219 ++++ .../src/logseq/agents/planning_workflow.cljs | 931 ++++++++++++++++++ deps/workers/src/logseq/agents/request.cljs | 84 ++ deps/workers/src/logseq/agents/routes.cljs | 10 + deps/workers/src/logseq/agents/worker.cljs | 109 +- .../workers/src/logseq/sync/malli_schema.cljs | 30 + .../src/logseq/sync/worker/dispatch.cljs | 1 + .../test/logseq/agents/handler_test.cljs | 60 ++ .../logseq/agents/planning_workflow_test.cljs | 122 +++ .../test/logseq/agents/request_test.cljs | 89 ++ .../test/logseq/agents/routes_test.cljs | 22 + .../agents/runtime_transport_test_runner.cljs | 15 + .../workers/test/logseq/sync/worker_test.cljs | 38 +- .../migrations/0007_add_planning_sessions.sql | 26 + deps/workers/worker/wrangler.agents.toml | 39 + deps/workers/yarn.lock | 862 +++++++++++++++- docs/agent-guide/003-agent-service.md | 51 + package.json | 3 +- src/main/frontend/components/agent_chat.cljs | 395 +++++++- src/main/frontend/config.cljs | 3 + src/main/frontend/handler/agent.cljs | 801 +++++++++++++-- .../handler/agent_chat_transport.cljs | 77 +- src/main/frontend/state.cljs | 1 + src/test/frontend/handler/agent_test.cljs | 110 +++ yarn.lock | 529 +++++++++- 34 files changed, 5520 insertions(+), 166 deletions(-) create mode 100644 deps/workers/docs/milestones/agents/26-m26-cloudflare-native-planning-layer.md create mode 100644 deps/workers/src/logseq/agents/planning_agent.cljs create mode 100644 deps/workers/src/logseq/agents/planning_store.cljs create mode 100644 deps/workers/src/logseq/agents/planning_workflow.cljs create mode 100644 deps/workers/test/logseq/agents/handler_test.cljs create mode 100644 deps/workers/test/logseq/agents/planning_workflow_test.cljs create mode 100644 deps/workers/worker/migrations/0007_add_planning_sessions.sql create mode 100644 src/test/frontend/handler/agent_test.cljs diff --git a/deps/workers/README.md b/deps/workers/README.md index 1dd2d743fd..805c81cb01 100644 --- a/deps/workers/README.md +++ b/deps/workers/README.md @@ -42,6 +42,12 @@ Local split note: - `worker/wrangler.agents.toml` sets `AGENT_RUNTIME_PROVIDER=e2b` by default. - On localhost, `/sessions*` forwarding retries during agents startup (up to ~30s) to avoid transient `503`. +Planning note: +- planning APIs live in the same `logseq-agents` service as execution APIs +- current planning entrypoint is `POST /planning/sessions` +- this keeps auth, managed ChatGPT token reuse, runtime dispatch, and future agent types inside one unified agent service +- `/sessions*`, `/planning*`, and future agent endpoints should continue to share the same worker unless there is a clear operational reason to split them + Production routing note: - If `api.logseq.com` is currently routed via AWS Route53/API Gateway, keep hostname routing in API Gateway. - Forward only `/sessions*` from API Gateway to the deployed agents worker URL (`*.workers.dev` or another worker-facing domain). diff --git a/deps/workers/docs/milestones/agents/00-index.md b/deps/workers/docs/milestones/agents/00-index.md index 3de62ece69..d8da40d17f 100644 --- a/deps/workers/docs/milestones/agents/00-index.md +++ b/deps/workers/docs/milestones/agents/00-index.md @@ -24,3 +24,5 @@ Milestones are tracked as separate files in this folder: - `21-m21-store-snapshots-metadata-in-d1.md` - `23-m23-local-runner-via-tunnel.md` - `24-m24-e2b-sandbox-runtime-default.md` +- `25-m25-chatgpt-login-token-auth.md` +- `26-m26-cloudflare-native-planning-layer.md` diff --git a/deps/workers/docs/milestones/agents/26-m26-cloudflare-native-planning-layer.md b/deps/workers/docs/milestones/agents/26-m26-cloudflare-native-planning-layer.md new file mode 100644 index 0000000000..10ae9c081d --- /dev/null +++ b/deps/workers/docs/milestones/agents/26-m26-cloudflare-native-planning-layer.md @@ -0,0 +1,168 @@ +# M26: Cloudflare-Native Planning Layer + +Status: Proposed +Target: Add a planning control plane on top of the existing `/sessions` +agents service by using Cloudflare Agents for interactive planning chat +and state, and Cloudflare Workflows for durable planning, +approval, execution dispatch, and replanning flows. + +## Goal + +Introduce a planning layer above the existing agents execution service +while reducing home-made control-plane and planning-chat transport code +as much as possible. + +The planning layer should reuse the existing execution substrate rather +than replace it. + +The milestone also covers turning planner output into real Logseq +`#Task` blocks and keeping those tasks synchronized with execution +status as sessions run. + +## Why M26 + +- The current agents service already provides the execution substrate for sandbox work and should be reused. +- The missing system layer is durable planning and orchestration, not another execution backend. +- Cloudflare Agents can reduce custom realtime planning chat and planning-session state code. +- Cloudflare Workflows can reduce custom orchestration, retry, wait-state, and replanning logic. +- Planning state and execution-operational state need clearer separation. + +## Architectural Decision + +- Keep the existing `/sessions` APIs and execution semantics. +- Do not replace E2B, local-runner, runtime-provider, source-control, or checkpoint logic in this milestone. +- Use Cloudflare Agent instances as the canonical planning-session model. +- Use Workflows for direct planning, repo-aware planning, approval waits, execution fan-out, and replanning. +- Keep the Logseq graph as the product-visible system of record for goals, plans, tasks, and execution summaries. +- Accept that operational state remains split across Logseq graph, Cloudflare Agent/Workflow state, and the existing agents service. + +## Scope + +1. Define a planning-session model backed by Cloudflare Agents. +2. Define a workflow-driven planning pipeline: +- goal intake +- clarification +- direct planning +- repo-aware research +- approval gate +- execution dispatch +- replanning +3. Define planner-created Logseq task persistence: +- create real `#Task` blocks under the goal block by default +- initialize new planner-created tasks as `Todo` +- inherit `project` and `agent` from goal or plan context when available +- use created task `:block/uuid` as the canonical task identity for later reconciliation +4. Define the handoff from planned task to existing `sessions/create`. +5. Define the frontend direction to replace custom planning-chat transport with Cloudflare-native client primitives where possible. +6. Define persistence boundaries across Logseq graph, Agent/Workflow state, and existing execution-operational state. +7. Define an initial rollout strategy that keeps the execution service unchanged. + +## Out of Scope + +- Replacing `/sessions` execution APIs. +- Replacing sandbox provisioning or runtime-provider logic. +- Replacing checkpoint persistence. +- Replacing managed auth and GitHub token logic. +- Migrating all existing execution chat/session flows to Cloudflare Agents immediately. +- Full frontend rewrite. + +## Workstreams + +### WS1: Planning Architecture and Contracts + +- Define planning entities and lifecycle. +- Define mapping from Goal, Plan, and Task into Logseq task blocks and execution-session payloads. +- Define the planner task contract: + - goal block parent + - task title and content + - `#Task` marker + - initial `Todo` status + - inherited `project` + - inherited `agent` + - optional dependency metadata +- Define approval triggers, follow-up task creation, and replanning entrypoints. +- Define reconciliation rules for replanning: + - match by stored task `:block/uuid` when available + - fall back to best-effort matching against existing non-started child tasks when necessary + - update planning-owned fields before execution starts + - avoid duplicate task creation + - preserve execution-owned fields once a session exists + +### WS2: Cloudflare Agent Planning Session + +- Define a planning Agent instance as the canonical interactive planning session. +- Use Cloudflare-native chat, state, and client primitives where possible. +- Minimize or eliminate custom planning transport code. + +### WS3: Workflow Orchestration + +- Define Workflow steps for decomposition, repo-aware research, approval wait, execution dispatch, and replanning. +- Define retry behavior, failure handling, and pause/resume semantics. +- Define how Workflow state links back to Logseq graph records and planning sessions. + +### WS4: Logseq Task Persistence + +- Create planner-generated tasks as child blocks under the goal block. +- Make planner-created tasks immediately runnable when inherited `project` and `agent` are present. +- Reuse the existing runnable task shape already consumed by the agent session flow. +- Preserve execution-owned fields such as session id, PR URL, checkpoint metadata, and terminal execution status after a task has started. + +### WS5: Execution Handoff + +- Define exactly how a planned task becomes a `POST /sessions` request. +- Include project metadata, agent metadata, runtime-provider selection, optional runner pinning, checkpoint reuse, and capability flags. +- Reuse the existing execution API instead of inventing a second runtime control surface. +- Reuse the existing session-to-task-status mapping for planner-created tasks: + - `created` / `running` -> `Doing` + - `paused` -> `Todo` + - `completed` -> `Done` + - `pr-created` -> `In Review` + - `failed` / `canceled` -> `Canceled` +- Apply the same execution status sync to planner-created and manually created runnable tasks. + +### WS6: Frontend Integration Strategy + +- Reuse existing chat UI components where practical. +- Replace custom planning-chat transport with Cloudflare Agent client APIs. +- Keep execution chat/session UI on the existing service unless a later milestone migrates it. + +### WS7: Validation and Rollout + +- Validate that the planning layer can drive the existing execution backend without adding new custom orchestration endpoints. +- Roll out behind a planning-specific feature flag or internal-only entrypoint. +- Define degraded behavior if Cloudflare Agent or Workflow state is temporarily unavailable. + +## Exit Criteria + +1. A planning session can be represented as a Cloudflare Agent instance. +2. A planning Workflow can orchestrate decomposition, approval, and execution dispatch durably. +3. Planner-generated tasks appear in Logseq as real `#Task` blocks under the goal block. +4. Planner-created tasks default to `Todo` and inherit `project` and `agent` when available. +5. Replanning updates existing planner-created tasks without creating duplicates. +6. Planned tasks have a documented translation into the existing `sessions/create` contract. +7. The design clearly separates planning state from execution-operational state. +8. The milestone reduces planned custom code by preferring Cloudflare-native planning primitives. +9. Existing `/sessions` execution behavior remains unchanged. + +## Validation + +- Update the planning architecture document to reflect the new layering and persistence model. +- Review the milestone against the current agents worker contracts before implementation starts. +- Walk through the task-to-`sessions/create` mapping and confirm every required execution field is accounted for. +- Validate planner-created task behavior: + - tasks are created under the goal block + - tasks are marked as `#Task` + - tasks default to `Todo` + - tasks inherit `project` and `agent` from goal or plan context when present + - planner reruns reconcile by stored task `:block/uuid`, with title-based fallback before execution starts + - execution updates status through the existing session-status mapping + - replanning does not overwrite execution-owned fields after a task has started +- Future implementation should add targeted tests for planning-to-execution contract handling and Workflow integration. + +## Defaults Chosen + +- Planner-created tasks live under the goal block by default. +- Initial planner-created status is `Todo`. +- `project` and `agent` are inherited from goal or plan context when present. +- Planner-created and manual runnable tasks share the same execution status-sync path. +- Created task `:block/uuid` is the canonical task identity after creation. diff --git a/deps/workers/package.json b/deps/workers/package.json index 182bcbcaf2..a145ccbfa1 100644 --- a/deps/workers/package.json +++ b/deps/workers/package.json @@ -27,6 +27,7 @@ "dependencies": { "@sentry/cloudflare": "^10.38.0", "@sentry/node": "^10.38.0", + "agents": "^0.7.5", "better-sqlite3": "^12.6.2", "e2b": "2.14.0", "shadow-cljs": "^3.3.4", diff --git a/deps/workers/shadow-cljs.edn b/deps/workers/shadow-cljs.edn index 91ba2c70f0..f7c44fda98 100644 --- a/deps/workers/shadow-cljs.edn +++ b/deps/workers/shadow-cljs.edn @@ -20,7 +20,9 @@ :warnings {:fn-deprecated false :redef false}} :modules {:main {:exports {default logseq.agents.worker/worker - AgentSessionDO logseq.agents.worker/AgentSessionDO}}} + AgentSessionDO logseq.agents.worker/AgentSessionDO + PlanningSessionAgent logseq.agents.worker/PlanningSessionAgent + PlanningWorkflow logseq.agents.worker/PlanningWorkflow}}} :js-options {:js-provider :import} :closure-defines {shadow.cljs.devtools.client.env/enabled false goog.debug.LOGGING_ENABLED true} @@ -50,7 +52,7 @@ :main logseq.agents.e2b-runtime-provider-test-runner/main} :agents-runtime-transport-test {:target :node-test :output-to "worker/dist/agents-runtime-transport-test.js" - :ns-regexp "logseq\\.agents\\.(sandbox-test|runtime-provider-test)$" + :ns-regexp "logseq\\.agents\\.(sandbox-test|runtime-provider-test|routes-test|request-test|planning-workflow-test|handler-test)$" :devtools {:enabled false} :main logseq.agents.runtime-transport-test-runner/main} :agents-m25-managed-auth-test {:target :node-test diff --git a/deps/workers/src/logseq/agents/dispatch.cljs b/deps/workers/src/logseq/agents/dispatch.cljs index 7e167142dd..84ea61ded0 100644 --- a/deps/workers/src/logseq/agents/dispatch.cljs +++ b/deps/workers/src/logseq/agents/dispatch.cljs @@ -17,6 +17,7 @@ (http/json-response :worker/health {:ok true}) (or (string/starts-with? path "/auth") + (string/starts-with? path "/planning") (string/starts-with? path "/sessions") (string/starts-with? path "/runners")) (agent-handler/handle-fetch #js {:env env} request) diff --git a/deps/workers/src/logseq/agents/do.cljs b/deps/workers/src/logseq/agents/do.cljs index 6ec71d8f7d..e2df3fd7b0 100644 --- a/deps/workers/src/logseq/agents/do.cljs +++ b/deps/workers/src/logseq/agents/do.cljs @@ -2,6 +2,7 @@ (:require [clojure.string :as string] [lambdaisland.glogi :as log] [logseq.agents.checkpoint-store :as checkpoint-store] + [logseq.agents.planning-store :as planning-store] [logseq.agents.runner-store :as runner-store] [logseq.agents.runtime-provider :as runtime-provider] [logseq.agents.sandbox :as sandbox] @@ -94,6 +95,7 @@ session)) (declare (get-in session [:runtime :provider]) str string/lower-case)) +(def ^:private session-status->planning-task-status + {"created" "Doing" + "running" "Doing" + "paused" "Todo" + "completed" "Done" + "pr-created" "In Review" + "failed" "Canceled" + "canceled" "Canceled"}) + +(def ^:private terminal-planning-task-statuses + #{"Done" "In Review" "Canceled"}) + +(defn- session-planning-session-id + [session] + (some-> (get-in (session-task session) [:source :node-id]) non-empty-str)) + +(defn- session-planning-task-uuid + [session] + (some-> (get-in (session-task session) [:task-uuid]) non-empty-str)) + +(defn- task-message-content + [task] + (or (some-> (get-in task [:intent :content]) non-empty-str) + (some-> (:content task) non-empty-str) + (some-> (:description task) non-empty-str) + (some-> (:title task) non-empty-str))) + +(defn- planning-task-terminal? + [task] + (contains? terminal-planning-task-statuses + (some-> (:status task) non-empty-str))) + +(defn- planning-task-active? + [task] + (or (string? (some-> (:session-id task) non-empty-str)) + (contains? #{"Doing" "Done" "In Review" "Canceled"} + (some-> (:status task) non-empty-str)))) + +(defn- planning-dependencies-satisfied? + [task task-by-uuid] + (let [deps (if (sequential? (:dependencies task)) + (keep non-empty-str (:dependencies task)) + [])] + (every? (fn [task-uuid] + (planning-task-terminal? (get task-by-uuid task-uuid))) + deps))) + +(defn- next-sequenced-task + [tasks current-task-uuid] + (let [tasks (vec (or tasks [])) + task-by-uuid (into {} + (keep (fn [task] + (when-let [task-uuid (some-> (:task-uuid task) non-empty-str)] + [task-uuid task]))) + tasks) + current-index (or (some (fn [[idx task]] + (when (= current-task-uuid (some-> (:task-uuid task) non-empty-str)) + idx)) + (map-indexed vector tasks)) + -1)] + (some (fn [task] + (when (and (not (planning-task-active? task)) + (planning-dependencies-satisfied? task task-by-uuid)) + task)) + (drop (inc current-index) tasks)))) + +(defn- update-planning-tasks-for-terminal-event + [tasks current-task-uuid session-id session-status] + (mapv (fn [task] + (if (= current-task-uuid (some-> (:task-uuid task) non-empty-str)) + (cond-> (assoc task + :session-id session-id + :status (or (get session-status->planning-task-status session-status) + (:status task))) + (string? (task-message-content task)) (assoc :content (task-message-content task))) + task)) + (or tasks []))) + +(defn- mark-next-task-doing + [tasks next-task-uuid session-id] + (mapv (fn [task] + (if (= next-task-uuid (some-> (:task-uuid task) non-empty-str)) + (assoc task :session-id session-id :status "Doing") + task)) + (or tasks []))) + +(defn- append-dispatch-session-record + [dispatch-sessions session-id task planning-session] + (let [task-uuid (some-> (:task-uuid task) non-empty-str)] + (if-not (and (string? session-id) (string? task-uuid)) + (vec (or dispatch-sessions [])) + (let [existing (some #(when (= task-uuid (some-> (:task-uuid %) non-empty-str)) %) dispatch-sessions) + record (or existing + {:id session-id + :task-uuid task-uuid + :source {:node-id (:planning-session-id planning-session) + :node-title (or (some-> planning-session :goal :title non-empty-str) + (some-> planning-session :goal :node-title non-empty-str) + "Planning Task")} + :intent {:content (task-message-content task)} + :project (:project planning-session) + :agent (:agent planning-session)})] + (conj (vec (remove #(= task-uuid (some-> (:task-uuid %) non-empty-str)) + (or dispatch-sessions []))) + record))))) + +(defn- current-session session-planning-session-id) + current-task-uuid (some-> current-session session-planning-task-uuid)] + (when (and (map? current-session) + (= session-id (:id current-session)) + (string? planning-session-id) + (string? current-task-uuid) + (contains? #{"completed" "failed" "canceled"} session-status)) + (p/let [planning-session (planning-store/ next-task :task-uuid non-empty-str) + tasks (if (string? next-task-uuid) + (mark-next-task-doing tasks next-task-uuid session-id) + tasks) + updated-session (assoc planning-session + :status (if (string? next-task-uuid) "dispatching" (:status planning-session)) + :plan (assoc (:plan planning-session) :tasks tasks) + :dispatch-sessions (if (map? next-task) + (append-dispatch-session-record (:dispatch-sessions planning-session) + session-id + next-task + planning-session) + (:dispatch-sessions planning-session)))] + (p/let [_ (planning-store/promise (.write writer (sse-bytes event)))] - nil))))) + (-> (p/let [events (promise (.write writer (sse-bytes event)))] + nil)))) + (p/catch (fn [error] + (when-not @closed? + (log/error :agent/session-stream-backlog-failed + {:error error + :since-ts since-ts}) + (cleanup))))))) (js/Response. (.-readable stream) diff --git a/deps/workers/src/logseq/agents/handler.cljs b/deps/workers/src/logseq/agents/handler.cljs index 59a56d8e95..a57bf9b183 100644 --- a/deps/workers/src/logseq/agents/handler.cljs +++ b/deps/workers/src/logseq/agents/handler.cljs @@ -2,6 +2,8 @@ (:require [clojure.string :as string] [lambdaisland.glogi :as log] [logseq.agents.managed-auth :as managed-auth] + [logseq.agents.planning-store :as planning-store] + [logseq.agents.planning-workflow :as planning-workflow] [logseq.agents.request :as agent-request] [logseq.agents.routes :as routes] [logseq.agents.runner-store :as runner-store] @@ -43,6 +45,12 @@ [claims] (aget claims "sub")) +(defn- planning-session-id-from-route + [route] + (some-> (get-in route [:path-params :planning-session-id]) str string/trim not-empty)) + +(declare planning-agent-binding planning-workflow-binding) + (defn- codex-agent? [agent] (cond @@ -120,7 +128,8 @@ :headers headers})] (.fetch stub forwarded-request)))) -(defn- handle-create [{:keys [env request url claims]}] +(defn- handle-session-create + [{:keys [env request url claims]} normalize-request] (p/let [result (common/read-json request)] (if (nil? result) (http/bad-request "missing body") @@ -148,12 +157,593 @@ (if-let [^js stub (session-stub env session-id)] (let [headers (base-headers request claims) _ (.set headers "x-stream-base" (.-origin url)) - task (agent-request/normalize-session-create body') + task (normalize-request body') body-json (js/JSON.stringify (clj->js task)) do-url (str (.-origin url) "/__session__/init")] (forward-request stub do-url "POST" headers body-json)) (http/error-response "server error" 500))))))))) +(defn- handle-create [{:keys [env request url claims]}] + (handle-session-create {:env env + :request request + :url url + :claims claims} + agent-request/normalize-session-create)) + +(defn- normalize-planning-session-create + [body claims] + (let [planning-session-id (or (some-> (:planning-session-id body) str string/trim not-empty) + (some-> (:session-id body) str string/trim not-empty) + (str (random-uuid))) + goal (if (map? (:goal body)) + (:goal body) + (cond-> {} + (some-> (:node-id body) str string/trim not-empty) (assoc :node-id (some-> (:node-id body) str string/trim)) + (some-> (:node-title body) str string/trim not-empty) (assoc :title (some-> (:node-title body) str string/trim)) + (some-> (:content body) str string/trim not-empty) (assoc :description (some-> (:content body) str string/trim)))) + require-approval (true? (:require-approval body)) + approval-status (if require-approval "pending" "approved")] + {:planning-session-id planning-session-id + :user-id (claims-user-id claims) + :status "active" + :goal goal + :project (when (map? (:project body)) (:project body)) + :agent (:agent body) + :approval-status approval-status + :require-approval require-approval + :auto-dispatch (if (boolean? (:auto-dispatch body)) + (:auto-dispatch body) + false) + :auto-replan (true? (:auto-replan body)) + :replan-delay-sec (if (number? (:replan-delay-sec body)) + (max 0 (:replan-delay-sec body)) + 0)})) + +(defn- handle-planning-create [{:keys [env request claims]}] + (cond + (not (planning-agent-binding env)) + (http/error-response "planning chat transport unavailable" 503) + + (not (planning-store/available? env)) + (http/error-response "planning state unavailable" 503) + + :else + (p/let [result (common/read-json request)] + (if (nil? result) + (http/bad-request "missing body") + (p/let [body' (js->clj result :keywordize-keys true) + session (normalize-planning-session-create body' claims) + planning-session-id (:planning-session-id session) + user-id (:user-id session) + params (agent-request/normalize-planning-workflow-create + (merge body' + {:planning-session-id planning-session-id + :user-id user-id + :goal (:goal session) + :project (:project session) + :agent (:agent session) + :require-approval (:require-approval session) + :auto-dispatch (:auto-dispatch session) + :auto-replan (:auto-replan session) + :replan-delay-sec (:replan-delay-sec session)})) + params (when (map? params) + (planning-workflow/enrich-params-with-model-plan env + params + {:timeout-ms 2500})) + orchestrated (when (map? params) + (planning-workflow/orchestrate-response params))] + (if-not (and (string? planning-session-id) + (string? user-id) + (map? params) + (map? orchestrated)) + (http/bad-request "invalid body") + (-> (planning-store/clj result :keywordize-keys true) + params (agent-request/normalize-planning-workflow-create body') + planning-session-id (or (:planning-session-id params) + (str (random-uuid))) + params (cond-> params + (string? planning-session-id) (assoc :planning-session-id planning-session-id) + (string? user-id) (assoc :user-id user-id)) + params (when (map? params) + (planning-workflow/enrich-params-with-model-plan env params)) + workflow-id (or (:workflow-id params) + (str planning-session-id "-workflow")) + workflow-binding (planning-workflow-binding env)] + (if-not (and (map? params) + (string? user-id) + (string? planning-session-id)) + (http/bad-request "invalid body") + (-> (.create workflow-binding + (clj->js {:id workflow-id + :params (dissoc params :workflow-id)})) + (p/then (fn [instance] + (let [workflow-id (or (some-> instance .-id) + workflow-id)] + (-> (planning-store/ (.get workflow-binding workflow-id) + (p/then (fn [instance] + (if-not instance + (http/not-found) + (-> (.status instance) + (p/then (fn [status] + (http/json-response :planning.workflows/get + {:workflow-id workflow-id + :status status}))))))) + (p/catch (fn [error] + (log/error :agent/planning-workflow-get-failed error) + (http/error-response "failed to fetch planning workflow" 500))))))) + +(defn- (.fetch stub (planning-agent-request request planning-session-id)) + (p/then (fn [response] + (if-not (.-ok response) + nil + (-> (.json response) + (p/then (fn [payload] + (let [state (js->clj payload :keywordize-keys true)] + (when (map? state) + state)))))))) + (p/catch (fn [_] + nil)))) + (p/resolved nil))) + +(defn- workflow-event-payload + [event-type approval-status] + (let [approved? (= "approved" approval-status)] + {:type event-type + :payload {:approved approved? + :decision approval-status}})) + +(defn- (.get workflow-binding workflow-id) + (p/then (fn [instance] + (if-not instance + (p/rejected (ex-info "workflow not found" {:workflow-id workflow-id})) + (let [send-event (aget instance "sendEvent")] + (if (fn? send-event) + (.call send-event instance (clj->js event)) + (p/rejected (ex-info "workflow event transport unavailable" + {:workflow-id workflow-id})))))))))) + +(defn- planning-session-status-for-approval + [approval-status] + (case approval-status + "approved" "queued" + "rejected" "rejected" + "pending" "waiting-approval" + "waiting-approval")) + +(defn- normalize-task-binding + [binding] + (when (map? binding) + (let [task-uuid (some-> (:task-uuid binding) str string/trim not-empty) + block-uuid (some-> (:block-uuid binding) str string/trim not-empty) + session-id (some-> (:session-id binding) str string/trim not-empty)] + (when (and task-uuid block-uuid) + (cond-> {:task-uuid task-uuid + :block-uuid block-uuid} + (string? session-id) (assoc :session-id session-id)))))) + +(defn- merge-task-bindings + [tasks bindings] + (let [binding-by-task-uuid (->> bindings + (keep normalize-task-binding) + (reduce (fn [acc binding] + (assoc acc (:task-uuid binding) binding)) + {}))] + (mapv (fn [task] + (if-let [binding (get binding-by-task-uuid + (some-> (:task-uuid task) str string/trim not-empty))] + (cond-> (assoc task :block-uuid (:block-uuid binding)) + (string? (:session-id binding)) (assoc :session-id (:session-id binding) + :status (or (:status task) "Doing"))) + task)) + (or tasks [])))) + +(defn- handle-planning-session-get [{:keys [env claims route]}] + (let [user-id (claims-user-id claims) + planning-session-id (planning-session-id-from-route route)] + (cond + (not (planning-store/available? env)) + (http/error-response "planning state unavailable" 503) + + (not (string? user-id)) + (http/unauthorized) + + (not (string? planning-session-id)) + (http/bad-request "invalid planning session id") + + :else + (p/let [planning-session ( (get-in body [:approval :decision]) str string/trim string/lower-case not-empty) + (some-> (:decision body) str string/trim string/lower-case not-empty))) + +(defn- handle-planning-session-approval [{:keys [env request claims route]}] + (let [user-id (claims-user-id claims) + planning-session-id (planning-session-id-from-route route) + workflow-binding (planning-workflow-binding env)] + (cond + (not (planning-store/available? env)) + (http/error-response "planning state unavailable" 503) + + (not (string? user-id)) + (http/unauthorized) + + (not (string? planning-session-id)) + (http/bad-request "invalid planning session id") + + :else + (p/let [result (common/read-json request) + body (if (nil? result) {} (js->clj result :keywordize-keys true)) + planning-state ( (:workflow-id existing) str string/trim not-empty)] + (cond + (and workflow-binding (string? workflow-id)) + (-> ( (get-in body [:approval :comment]) + str + string/trim + not-empty)} + :auto-dispatch (if (boolean? (:auto-dispatch existing)) + (:auto-dispatch existing) + true) + :auto-replan (true? (:auto-replan existing)) + :replan-delay-sec (:replan-delay-sec existing)}) + workflow-id (or workflow-id + (str planning-session-id "-workflow"))] + (-> (.create workflow-binding + (clj->js {:id workflow-id + :params params})) + (p/then (fn [instance] + (let [workflow-id (or (some-> instance .-id) + workflow-id)] + (planning-store/clj result :keywordize-keys true)) + bindings (if (sequential? (:tasks body)) + (vec (:tasks body)) + []) + merged-tasks (merge-task-bindings (get-in existing [:plan :tasks]) bindings) + updated-session (assoc existing :plan (assoc (:plan existing) :tasks merged-tasks))] + (p/let [updated (planning-store/clj result :keywordize-keys true)) + params (agent-request/normalize-planning-workflow-create + (merge {:planning-session-id planning-session-id + :user-id user-id + :goal (:goal existing) + :project (:project existing) + :agent (:agent existing) + :planning-messages (:messages planning-state) + :require-approval (:require-approval existing) + :auto-dispatch (:auto-dispatch existing) + :auto-replan (:auto-replan existing) + :replan-delay-sec (:replan-delay-sec existing)} + body)) + params (when (map? params) + (planning-workflow/enrich-params-with-model-plan env params)) + existing-workflow-id (some-> (:workflow-id existing) str string/trim not-empty) + workflow-id (or (:workflow-id params) + existing-workflow-id + (str planning-session-id "-replan-" (random-uuid))) + plan-tasks (or (:tasks params) + (get-in existing [:plan :tasks])) + update-data {:workflow-id workflow-id + :status (if (true? (:require-approval params)) + "waiting-approval" + "queued") + :plan {:tasks plan-tasks} + :approval-status (if (true? (:require-approval params)) + "pending" + "approved") + :require-approval (true? (:require-approval params)) + :auto-dispatch (if (boolean? (:auto-dispatch params)) + (:auto-dispatch params) + true) + :auto-replan (true? (:auto-replan params)) + :replan-delay-sec (:replan-delay-sec params)}] + (if (string? existing-workflow-id) + (-> ( (.create workflow-binding + (clj->js {:id workflow-id + :params (assoc (dissoc params :workflow-id) + :workflow-id workflow-id)})) + (p/then (fn [instance] + (let [workflow-id (or (some-> instance .-id) + workflow-id)] + (planning-store/ (.fetch stub (planning-agent-request request planning-session-id)) + (p/catch (fn [error] + (log/error :agent/planning-chat-forward-failed error) + (http/error-response "planning chat transport failed" 500)))) + (http/error-response "planning chat transport unavailable" 503))))))) + (defn- handle-auth-status [{:keys [env claims]}] (let [user-id (claims-user-id claims)] (if-not (string? user-id) @@ -385,6 +975,14 @@ (case (:handler route) :auth.chatgpt/import (handle-auth-import ctx) :auth.chatgpt/status (handle-auth-status ctx) + :planning.sessions/create (handle-planning-create ctx) + :planning.sessions/get (handle-planning-session-get ctx) + :planning.sessions/approval (handle-planning-session-approval ctx) + :planning.sessions/tasks.sync (handle-planning-session-task-sync ctx) + :planning.sessions/replan (handle-planning-session-replan ctx) + :planning.chat/transport (handle-planning-chat-transport ctx) + :planning.workflows/create (handle-planning-workflow-create ctx) + :planning.workflows/get (handle-planning-workflow-get ctx) :sessions/create (handle-create ctx) :sessions/get (handle-get ctx) :sessions/messages (handle-messages ctx) diff --git a/deps/workers/src/logseq/agents/planning_agent.cljs b/deps/workers/src/logseq/agents/planning_agent.cljs new file mode 100644 index 0000000000..2ec4ad1c52 --- /dev/null +++ b/deps/workers/src/logseq/agents/planning_agent.cljs @@ -0,0 +1,72 @@ +(ns logseq.agents.planning-agent + (:require [clojure.string :as string] + [logseq.sync.common :as common] + [logseq.sync.platform.core :as platform])) + +(defn- non-empty-str + [value] + (when (string? value) + (let [trimmed (string/trim value)] + (when-not (string/blank? trimmed) + trimmed)))) + +(defn default-state + [planning-session-id] + {:planning-session-id planning-session-id + :messages [] + :updated-at (common/now-ms)}) + +(defn- ensure-state-shape + [planning-session-id state] + (let [messages (if (sequential? (:messages state)) + (vec (:messages state)) + [])] + (cond-> {:planning-session-id planning-session-id + :messages messages + :updated-at (common/now-ms)} + (map? state) (merge (dissoc state :messages :updated-at :planning-session-id))))) + +(defn append-message + [planning-session-id state role content] + (let [content (non-empty-str content) + state (ensure-state-shape planning-session-id state)] + (if-not (string? content) + state + (update (assoc state :updated-at (common/now-ms)) + :messages + (fnil conj []) + {:id (str (random-uuid)) + :role role + :content content + :ts (common/now-ms)})))) + +(defn normalize-state + [planning-session-id state] + (ensure-state-shape planning-session-id state)) + +(defn parse-message-content + [payload] + (cond + (string? payload) + (or (try + (some-> (js/JSON.parse payload) + (js->clj :keywordize-keys true) + :content + non-empty-str) + (catch :default _ + nil)) + (non-empty-str payload)) + + (map? payload) + (or (some-> (:content payload) non-empty-str) + (some-> (:message payload) non-empty-str)) + + :else + nil)) + +(defn json-response + [data status] + (platform/response + (js/JSON.stringify (clj->js data)) + #js {:status status + :headers #js {"content-type" "application/json"}})) diff --git a/deps/workers/src/logseq/agents/planning_store.cljs b/deps/workers/src/logseq/agents/planning_store.cljs new file mode 100644 index 0000000000..3ff4b92439 --- /dev/null +++ b/deps/workers/src/logseq/agents/planning_store.cljs @@ -0,0 +1,219 @@ +(ns logseq.agents.planning-store + (:require [clojure.string :as string] + [logseq.sync.common :as common] + [promesa.core :as p])) + +(defn- non-empty-str + [value] + (when (string? value) + (let [trimmed (string/trim value)] + (when-not (string/blank? trimmed) + trimmed)))) + +(defn- normalize-int + [value default-value] + (let [parsed (if (number? value) + value + (some-> value str js/parseInt))] + (if (and (number? parsed) + (not (js/isNaN parsed))) + parsed + default-value))) + +(defn- bool->int + [value] + (if (true? value) 1 0)) + +(defn- int->bool + [value] + (pos? (normalize-int value 0))) + +(defn- parse-json + [value default-value] + (if-let [raw (non-empty-str value)] + (try + (js->clj (js/JSON.parse raw) :keywordize-keys true) + (catch :default _ + default-value)) + default-value)) + +(defn- serialize-json + [value] + (when (some? value) + (js/JSON.stringify (clj->js value)))) + +(defn- db-binding + [^js env] + (aget env "AGENTS_DB")) + +(defn available? + [^js env] + (boolean (db-binding env))) + +(declare planning-session + [row] + (when row + (let [planning-session-id (aget row "planning_session_id") + user-id (aget row "user_id") + workflow-id (non-empty-str (aget row "workflow_id")) + status (or (non-empty-str (aget row "status")) "queued") + approval-status (or (non-empty-str (aget row "approval_status")) "pending") + require-approval (int->bool (aget row "require_approval")) + auto-dispatch (int->bool (aget row "auto_dispatch")) + auto-replan (int->bool (aget row "auto_replan")) + replan-delay-sec (normalize-int (aget row "replan_delay_sec") 0) + created-at (normalize-int (aget row "created_at") 0) + updated-at (normalize-int (aget row "updated_at") 0) + last-error (non-empty-str (aget row "last_error")) + goal (parse-json (aget row "goal_json") nil) + plan (parse-json (aget row "plan_json") nil) + project (parse-json (aget row "project_json") nil) + agent (parse-json (aget row "agent_json") nil) + scheduled-actions (parse-json (aget row "scheduled_actions_json") []) + dispatch-sessions (parse-json (aget row "dispatch_sessions_json") [])] + (when (and (string? planning-session-id) + (string? user-id)) + (cond-> {:planning-session-id planning-session-id + :user-id user-id + :status status + :approval-status approval-status + :require-approval require-approval + :auto-dispatch auto-dispatch + :auto-replan auto-replan + :replan-delay-sec replan-delay-sec + :scheduled-actions (if (sequential? scheduled-actions) + (vec scheduled-actions) + []) + :dispatch-sessions (if (sequential? dispatch-sessions) + (vec dispatch-sessions) + []) + :created-at created-at + :updated-at updated-at} + (string? workflow-id) (assoc :workflow-id workflow-id) + (map? goal) (assoc :goal goal) + (map? plan) (assoc :plan plan) + (map? project) (assoc :project project) + (some? agent) (assoc :agent agent) + (string? last-error) (assoc :last-error last-error)))))) + +(defn (:planning-session-id planning-session) non-empty-str) + user-id (some-> (:user-id planning-session) non-empty-str) + workflow-id (some-> (:workflow-id planning-session) non-empty-str) + status (or (some-> (:status planning-session) non-empty-str) "queued") + approval-status (or (some-> (:approval-status planning-session) non-empty-str) + (if (true? (:require-approval planning-session)) "pending" "approved")) + require-approval (bool->int (true? (:require-approval planning-session))) + auto-dispatch (bool->int (if (boolean? (:auto-dispatch planning-session)) + (:auto-dispatch planning-session) + true)) + auto-replan (bool->int (true? (:auto-replan planning-session))) + replan-delay-sec (max 0 (normalize-int (:replan-delay-sec planning-session) 0)) + now (common/now-ms)] + (if (and (string? planning-session-id) + (string? user-id)) + (p/let [_ (common/ (:last-error planning-session) non-empty-str) + now + now) + stored ( planning-session-id non-empty-str)] + (p/let [result (common/planning-session (first rows))) + (p/resolved nil)) + (p/resolved nil))) + +(defn user-id non-empty-str)] + (if-let [planning-session-id (some-> planning-session-id non-empty-str)] + (p/let [result (common/planning-session (first rows))) + (p/resolved nil)) + (p/resolved nil)) + (p/resolved nil))) + +(defn approval-status non-empty-str) + "pending")})) diff --git a/deps/workers/src/logseq/agents/planning_workflow.cljs b/deps/workers/src/logseq/agents/planning_workflow.cljs new file mode 100644 index 0000000000..c30c6dbf53 --- /dev/null +++ b/deps/workers/src/logseq/agents/planning_workflow.cljs @@ -0,0 +1,931 @@ +(ns logseq.agents.planning-workflow + (:require [clojure.string :as string] + [lambdaisland.glogi :as log] + [logseq.agents.planning-store :as planning-store] + [logseq.sync.platform.core :as platform])) + +(defn- non-empty-str + [value] + (when (string? value) + (let [trimmed (string/trim value)] + (when-not (string/blank? trimmed) + trimmed)))) + +(defn- task-content + [task] + (or (some-> (:content task) non-empty-str) + (let [title (some-> (:title task) non-empty-str) + description (some-> (:description task) non-empty-str)] + (cond + (and title description) (str title "\n" description) + title title + description description + :else nil)))) + +(defn- maybe-task-uuid + [idx task] + (or (some-> (:task-uuid task) non-empty-str) + (some-> (:block-uuid task) non-empty-str) + (str "task-" (inc idx)))) + +(declare native-promise) + +(defn normalize-task + [idx task] + (when (map? task) + (when-let [content (task-content task)] + (cond-> {:title (or (some-> (:title task) non-empty-str) + (str "Task " (inc idx))) + :content content} + (some-> (:description task) non-empty-str) (assoc :description (non-empty-str (:description task))) + (sequential? (:dependencies task)) (assoc :dependencies (vec (:dependencies task))) + (sequential? (:acceptance-criteria task)) (assoc :acceptance-criteria (vec (:acceptance-criteria task))))))) + +(defn- normalize-planner-task + [idx task] + (when-let [normalized (normalize-task idx task)] + (cond-> (assoc normalized + :task-uuid (maybe-task-uuid idx task) + :status "Todo" + :marker "#Task") + (some-> (:block-uuid task) non-empty-str) (assoc :block-uuid (non-empty-str (:block-uuid task)))))) + +(defn normalize-tasks + [tasks] + (->> tasks + (map-indexed normalize-task) + (remove nil?) + vec)) + +(defn- normalize-planner-tasks + [tasks] + (->> tasks + (map-indexed normalize-planner-task) + (remove nil?) + vec)) + +(defn- fallback-task-title + [goal fallback] + (or (some-> goal :title non-empty-str) + (some-> goal :node-title non-empty-str) + fallback)) + +(defn- planning-message-text + [message] + (cond + (string? message) + (non-empty-str message) + + (map? message) + (or (some-> (:content message) non-empty-str) + (some-> (:message message) non-empty-str) + (some-> (:text message) non-empty-str)) + + :else + nil)) + +(defn- planning-message-texts + [params] + (->> (:planning-messages params) + (keep planning-message-text) + vec)) + +(defn- repo-note + [params] + (let [repo-url (some-> (get-in params [:project :repo-url]) non-empty-str) + base-branch (some-> (get-in params [:project :base-branch]) non-empty-str)] + (when (string? repo-url) + (str "Repository: " repo-url + (when (string? base-branch) + (str " (base branch: " base-branch ")")))))) + +(defn- planning-context-texts + [params] + (let [goal (:goal params)] + (->> [(some-> goal :title non-empty-str) + (some-> goal :description non-empty-str) + (some-> goal :node-title non-empty-str) + (some-> (:replan-note params) non-empty-str) + (some-> (get-in params [:approval :comment]) non-empty-str) + (repo-note params)] + (concat (planning-message-texts params)) + (keep non-empty-str) + vec))) + +(defn- parse-json-safe + [value] + (if (string? value) + (try + (js->clj (js/JSON.parse value) :keywordize-keys true) + (catch :default _ + nil)) + nil)) + +(defn- extract-file-references + [params] + (let [seen (volatile! #{})] + (->> (planning-context-texts params) + (mapcat #(re-seq #"[A-Za-z0-9_./-]+\.(?:clj|cljs|cljc|edn|sql|ts|tsx|js|jsx)" %)) + (keep (fn [file-path] + (let [normalized (non-empty-str file-path)] + (when (and (string? normalized) + (not (contains? @seen normalized))) + (vswap! seen conj normalized) + normalized)))) + vec))) + +(defn- planner-api-token + [^js env params] + (or (some-> (get-in params [:agent :api-token]) non-empty-str) + (some-> (aget env "OPENAI_API_KEY") non-empty-str))) + +(defn- planner-base-url + [^js env params] + (let [base-url (or (some-> (get-in params [:agent :base-url]) non-empty-str) + (some-> (aget env "OPENAI_BASE_URL") non-empty-str) + "https://api.openai.com/v1")] + (string/replace base-url #"/+$" ""))) + +(defn- planner-model + [params] + (or (some-> (get-in params [:agent :model]) non-empty-str) + "gpt-4.1-mini")) + +(defn- generic-clarifications + [params] + (let [goal (:goal params) + goal-description (or (some-> goal :description non-empty-str) + (some-> goal :title non-empty-str) + (some-> goal :node-title non-empty-str)) + repo-url (some-> (get-in params [:project :repo-url]) non-empty-str) + replan-note (some-> (:replan-note params) non-empty-str)] + (vec + (keep identity + [(when-not (string? repo-url) + "Confirm the target repository and branch before final implementation work begins.") + (when (and (string? goal-description) + (< (count goal-description) 48)) + "Clarify the expected user-visible outcome so the plan can stay focused.") + (when (string? replan-note) + (str "Address the replanning feedback: " replan-note))])))) + +(defn- generic-workstreams + [params tasks] + (let [file-references (extract-file-references params)] + (cond + (seq tasks) + (->> tasks + (take 4) + (mapv (fn [task] + {:title (or (some-> (:title task) non-empty-str) + "Implementation task") + :description (or (some-> (:description task) non-empty-str) + (task-content task) + "Complete the planned workstream.")}))) + + (seq file-references) + [{:title "Codebase review" + :description (str "Review the referenced files and current implementation constraints: " + (string/join ", " file-references) + ".")} + {:title "Implementation" + :description "Translate the requested change into concrete code and data-flow updates."} + {:title "Validation" + :description "Check the result end-to-end and summarize any follow-up work."}] + + :else + [{:title "Investigation" + :description "Review the current state, surrounding constraints, and open questions."} + {:title "Implementation" + :description "Translate the request into concrete implementation tasks."} + {:title "Validation" + :description "Verify the outcome and capture remaining risks."}]))) + +(defn- generic-milestones + [workstreams] + (mapv (fn [workstream] + {:title (str (:title workstream) " completed") + :description (:description workstream)}) + workstreams)) + +(defn- generic-dependencies + [params] + (vec + (keep identity + [(when-let [repo (repo-note params)] + (str "Use " repo " as the source of truth for planning and validation.")) + (when (true? (:require-approval params)) + "Execution should remain blocked until the approval state is explicitly updated.")]))) + +(defn- generic-risks + [params] + (vec + (keep identity + [(when (true? (:require-approval params)) + "Approval and execution state can drift if the planning UI is not refreshed after control actions.") + (when-not (string? (some-> (get-in params [:project :repo-url]) non-empty-str)) + "Repository-aware planning will stay shallow until repo metadata is available.")]))) + +(defn- generic-planner-tasks + [params] + (let [goal (:goal params) + goal-description (or (some-> goal :description non-empty-str) + (some-> goal :title non-empty-str) + (some-> goal :node-title non-empty-str) + "Complete the requested work.") + file-references (extract-file-references params)] + [{:title "Inspect current state" + :description (str "Review the goal, current codebase, and constraints. " + goal-description + (when-let [repo (repo-note params)] + (str "\n" repo)) + (when (seq file-references) + (str "\nReferenced files: " (string/join ", " file-references))))} + {:title (str "Implement " (fallback-task-title goal "the requested change")) + :description goal-description} + {:title "Validate and summarize" + :description "Check the result, note risks, and summarize follow-up work."}])) + +(def ^:private planner-json-schema + {:type "object" + :additionalProperties false + :required ["goal_understanding" "clarifications" "workstreams" "milestones" "tasks" "dependencies" "risks"] + :properties {"goal_understanding" {:type "string"} + "clarifications" {:type "array" + :items {:type "string"}} + "workstreams" {:type "array" + :items {:type "object" + :additionalProperties false + :required ["title" "description"] + :properties {"title" {:type "string"} + "description" {:type "string"}}}} + "milestones" {:type "array" + :items {:type "object" + :additionalProperties false + :required ["title" "description"] + :properties {"title" {:type "string"} + "description" {:type "string"}}}} + "tasks" {:type "array" + :items {:type "object" + :additionalProperties false + :required ["title" "description"] + :properties {"title" {:type "string"} + "description" {:type "string"} + "content" {:type "string"} + "task_uuid" {:type "string"} + "block_uuid" {:type "string"} + "dependencies" {:type "array" + :items {:type "string"}} + "acceptance_criteria" {:type "array" + :items {:type "string"}}}}} + "dependencies" {:type "array" + :items {:type "string"}} + "risks" {:type "array" + :items {:type "string"}}}}) + +(defn- planner-prompt + [params] + (let [goal (:goal params) + existing-tasks (or (:tasks params) + (:planned-tasks params) + (:existing-tasks params) + []) + context {:goal {:title (some-> goal :title non-empty-str) + :description (or (some-> goal :description non-empty-str) + (some-> goal :node-title non-empty-str)) + :node-title (some-> goal :node-title non-empty-str)} + :project {:repo-url (some-> (get-in params [:project :repo-url]) non-empty-str) + :base-branch (some-> (get-in params [:project :base-branch]) non-empty-str)} + :planning-messages (planning-message-texts params) + :existing-tasks (->> existing-tasks + (keep #(select-keys % [:task-uuid :block-uuid :title :description :content :status :session-id])) + vec) + :require-approval (true? (:require-approval params)) + :auto-dispatch (if (boolean? (:auto-dispatch params)) + (:auto-dispatch params) + true) + :replan-note (some-> (:replan-note params) non-empty-str)}] + (str "You are a software planning model. Produce a concise implementation plan as strict JSON.\n" + "Do not anchor on technology or file names unless they are present in the provided context.\n" + "Use repo metadata and planning messages only as supporting context.\n" + "Return implementation-oriented workstreams, milestones, tasks, dependencies, and risks.\n\n" + "Planning context JSON:\n" + (js/JSON.stringify (clj->js context) nil 2)))) + +(defn- normalize-model-plan + [payload params] + (when (map? payload) + (let [tasks (normalize-tasks (:tasks payload)) + workstreams (if (sequential? (:workstreams payload)) + (->> (:workstreams payload) + (keep #(when (map? %) + (select-keys % [:title :description]))) + vec) + []) + milestones (if (sequential? (:milestones payload)) + (->> (:milestones payload) + (keep #(when (map? %) + (select-keys % [:title :description]))) + vec) + [])] + (when (seq tasks) + {:goal-understanding (or (some-> (:goal_understanding payload) non-empty-str) + (some-> (:goal-understanding payload) non-empty-str) + (some-> (:goal params) :description non-empty-str) + (some-> (:goal params) :title non-empty-str) + "") + :clarifications (if (sequential? (:clarifications payload)) + (vec (keep non-empty-str (:clarifications payload))) + []) + :workstreams workstreams + :milestones milestones + :tasks tasks + :dependencies (if (sequential? (:dependencies payload)) + (vec (keep non-empty-str (:dependencies payload))) + []) + :risks (if (sequential? (:risks payload)) + (vec (keep non-empty-str (:risks payload))) + [])})))) + +(defn- (js/fetch url + #js {:method "POST" + :headers headers + :body (js/JSON.stringify (clj->js request-body))}) + (.then (fn [response] + (if-not (.-ok response) + nil + (-> (.json response) + (.then (fn [payload] + (let [payload (js->clj payload :keywordize-keys true) + content (some-> payload :choices first :message :content non-empty-str) + plan-payload (or (parse-json-safe content) + (some-> payload :choices first :message :parsed))] + (normalize-model-plan plan-payload params)))))))) + (.catch (fn [error] + (log/warn :agent/planning-model-plan-failed error) + nil)))) + (native-promise nil))) + +(defn- ( params :plan :tasks))] + (if (seq tasks) + tasks + (generic-planner-tasks params)))) + +(defn build-plan + [params] + (let [goal (:goal params) + tasks (normalize-tasks (planner-source-tasks params)) + existing-plan (when (map? (:plan params)) (:plan params)) + workstreams (or (some-> existing-plan :workstreams seq vec) + (generic-workstreams params tasks))] + {:goal-understanding (or (some-> existing-plan :goal-understanding non-empty-str) + (some-> goal :description non-empty-str) + (some-> goal :title non-empty-str) + (some-> goal :node-title non-empty-str) + "") + :clarifications (or (some-> existing-plan :clarifications seq vec) + (generic-clarifications params)) + :workstreams workstreams + :milestones (or (some-> existing-plan :milestones seq vec) + (generic-milestones workstreams)) + :tasks tasks + :dependencies (or (some-> existing-plan :dependencies seq vec) + (generic-dependencies params)) + :risks (or (some-> existing-plan :risks seq vec) + (generic-risks params))})) + +(defn- approval-decision + [params] + (let [decision (some-> (get-in params [:approval :decision]) + non-empty-str + string/lower-case)] + (cond + (contains? #{"approved" "pending" "rejected"} decision) + decision + + (true? (:require-approval params)) + "pending" + + :else + "approved"))) + +(defn- started-task? + [task] + (or (string? (some-> (:session-id task) non-empty-str)) + (contains? #{"Doing" "Done" "In Review" "Canceled"} + (some-> (:status task) non-empty-str)))) + +(def ^:private execution-owned-fields + #{:session-id + :status + :runtime-provider + :runner-id + :pr-url + :runtime + :sandbox-checkpoint + :checkpoint-metadata}) + +(defn- preserve-execution-owned-fields + [existing planned] + (merge planned + (select-keys existing execution-owned-fields) + (select-keys existing [:title :content :description :block-uuid]))) + +(defn- task-match + [existing task] + (let [task-uuid (some-> (:task-uuid task) non-empty-str)] + (some (fn [candidate] + (when (= task-uuid (some-> (:task-uuid candidate) non-empty-str)) + candidate)) + existing))) + +(defn- reconcile-planned-tasks + [existing-tasks planned-tasks] + (let [existing (vec (filter map? existing-tasks)) + matched-task-uuids (volatile! #{}) + reconciled (mapv (fn [task] + (if-let [matched (task-match existing task)] + (do + (when-let [task-uuid (some-> (:task-uuid matched) non-empty-str)] + (vswap! matched-task-uuids conj task-uuid)) + (if (started-task? matched) + (preserve-execution-owned-fields matched task) + (merge matched task))) + task)) + planned-tasks) + untouched-existing (->> existing + (remove (fn [task] + (contains? @matched-task-uuids + (or (some-> (:task-uuid task) non-empty-str) + "")))) + (remove nil?) + vec)] + (vec (concat reconciled untouched-existing)))) + +(defn- planning-status + [approval-status dispatch-sessions auto-dispatch?] + (cond + (= "pending" approval-status) "waiting-approval" + (= "rejected" approval-status) "rejected" + (and auto-dispatch? (seq dispatch-sessions)) "dispatching" + :else "planned")) + +(defn- dispatch-session-id + [planning-session-id task] + (or (some-> (:session-id task) non-empty-str) + (some-> (:goal task) :node-id non-empty-str) + (some-> (:goal task) :block-uuid non-empty-str) + (some-> (:goal task) :id non-empty-str) + (some-> task :parent-session-id non-empty-str) + (some-> planning-session-id non-empty-str))) + +(defn- dispatch-session + [planning-session-id params task] + (let [runtime-provider (some-> (:runtime-provider params) non-empty-str string/lower-case) + runner-id (some-> (:runner-id params) non-empty-str) + parent-session-id (or (some-> (:execution-session-id params) non-empty-str) + (some-> (:goal params) :node-id non-empty-str) + (some-> (:goal params) :block-uuid non-empty-str)) + goal-title (or (some-> (:goal params) :title non-empty-str) + (some-> (:goal params) :node-title non-empty-str) + "Planning Task")] + (cond-> {:id (dispatch-session-id planning-session-id + (assoc task :parent-session-id parent-session-id)) + :source {:node-id planning-session-id + :node-title goal-title} + :intent {:content (:content task)} + :project (:project params) + :agent (:agent params) + :task-uuid (:task-uuid task) + :capabilities {:push-enabled true + :pr-enabled true}} + (string? runtime-provider) (assoc :runtime-provider runtime-provider) + (string? runner-id) (assoc :runner-id runner-id)))) + +(defn- ready-dispatch-task? + [task] + (not (started-task? task))) + +(defn- next-dispatchable-tasks + [planning-session-id params tasks] + (if-let [task (some ready-dispatch-task? tasks)] + [(dispatch-session planning-session-id params task)] + [])) + +(defn- repo-aware-state + [params] + {:enabled (boolean (some-> (get-in params [:project :repo-url]) non-empty-str)) + :repo-url (some-> (get-in params [:project :repo-url]) non-empty-str) + :base-branch (some-> (get-in params [:project :base-branch]) non-empty-str) + :agent-provider (or (some-> (:agent params) :provider non-empty-str) + (some-> (:agent params) non-empty-str))}) + +(defn- scheduled-actions + [dispatch-ready? dispatch-sessions auto-replan? replan-delay-sec] + (if (and dispatch-ready? + (seq dispatch-sessions) + auto-replan? + (pos-int? replan-delay-sec)) + [{:type "replan" + :delay-sec replan-delay-sec}] + [])) + +(defn- session-stub + [^js env session-id] + (when-let [^js namespace (aget env "LOGSEQ_AGENT_SESSION_DO")] + (let [do-id (.idFromName namespace session-id)] + (.get namespace do-id)))) + +(defn- js session-task))})] + (-> (.fetch stub request) + (.then (fn [response] + {:session-id (:id session-task) + :ok (.-ok response) + :status (.-status response)})) + (.catch (fn [error] + {:session-id (:id session-task) + :ok false + :status 500 + :error (str error)}))))) + (native-promise {:session-id (:id session-task) + :ok false + :status 503 + :error "session durable object unavailable"}))) + +(defn- js + (mapv (fn [session-task] + ( (:planning-session-id params) non-empty-str) + (str (random-uuid))) + planned-tasks (normalize-planner-tasks (planner-source-tasks params)) + existing-tasks (if (sequential? (:existing-tasks params)) + (vec (:existing-tasks params)) + []) + reconciled-tasks (reconcile-planned-tasks existing-tasks planned-tasks) + approval-status (approval-decision params) + auto-dispatch? (if (boolean? (:auto-dispatch params)) + (:auto-dispatch params) + true) + auto-replan? (true? (:auto-replan params)) + replan-delay-sec (let [delay (if (number? (:replan-delay-sec params)) + (:replan-delay-sec params) + (some-> (:replan-delay-sec params) str js/parseInt))] + (if (and (number? delay) (not (js/isNaN delay))) + (max 0 delay) + 0)) + dispatch-ready? (and auto-dispatch? + (= "approved" approval-status)) + dispatch-sessions (if dispatch-ready? + (next-dispatchable-tasks planning-session-id params reconciled-tasks) + []) + status (planning-status approval-status dispatch-sessions auto-dispatch?) + plan (build-plan params)] + {:ok true + :status status + :planning-session-id planning-session-id + :plan plan + :reconciled-tasks reconciled-tasks + :dispatch-sessions dispatch-sessions + :scheduled-actions (scheduled-actions dispatch-ready? dispatch-sessions auto-replan? replan-delay-sec) + :planning-state {:planning-session-id planning-session-id + :phase (if (= "waiting-approval" status) "approval" "planning") + :approval-status approval-status + :requires-approval (true? (:require-approval params)) + :auto-dispatch auto-dispatch? + :auto-replan auto-replan? + :replan-delay-sec replan-delay-sec + :repo-aware (repo-aware-state params)} + :goal (:goal params) + :require-approval (true? (:require-approval params))})) + +(defn workflow-response + [instance details] + {:workflow-id (or (some-> instance .-id) + (:id details)) + :status (or (:status details) + "queued") + :details details}) + +(defn orchestrate-response + [params] + (orchestrate params)) + +(defn- native-promise + [value] + (js/Promise.resolve value)) + +(defn- > dispatch-sessions + (reduce (fn [acc session] + (if-let [task-uuid (some-> (:task-uuid session) non-empty-str)] + (assoc acc task-uuid (:id session)) + acc)) + {}))] + (if (empty? session-id-by-task) + orchestrated + (update orchestrated + :reconciled-tasks + (fn [tasks] + (mapv (fn [task] + (if-let [session-id (get session-id-by-task + (some-> (:task-uuid task) non-empty-str))] + (cond-> (assoc task :session-id session-id) + (not (started-task? task)) (assoc :status "Doing")) + task)) + (or tasks []))))))) + +(defn- workflow-event-payload + [event] + (let [event (if (map? event) + event + (js->clj event :keywordize-keys true))] + (when (map? event) + (if (contains? event :payload) + (:payload event) + event)))) + +(defn- approval-decision-from-event + [event] + (let [payload (workflow-event-payload event) + decision (some-> (:decision payload) non-empty-str string/lower-case)] + (cond + (contains? #{"approved" "pending" "rejected"} decision) + decision + + (true? (:approved payload)) + "approved" + + (false? (:approved payload)) + "rejected" + + :else + "pending"))) + +(defn- (.call wait-for-event + step + "wait-for-approval" + #js {:type "approval" + :timeout "30 days"}) + (.then approval-decision-from-event) + (.catch (fn [_] + "pending"))) + (js/Promise.resolve "pending")))) + +(defn- replan-action + [orchestrated] + (->> (:scheduled-actions orchestrated) + (filter map?) + (some (fn [action] + (when (= "replan" (some-> (:type action) non-empty-str string/lower-case)) + action))))) + +(defn- (:user-id params) non-empty-str) + planning-session-id (some-> (:planning-session-id orchestrated) non-empty-str) + workflow-id (some-> (:workflow-id params) non-empty-str) + persisted-plan (assoc (:plan orchestrated) + :tasks (:reconciled-tasks orchestrated))] + (if (and env + (planning-store/available? env) + (string? user-id) + (string? planning-session-id)) + (native-promise + (planning-store/ (.call wait-for-event + step + "wait-for-replan" + #js {:type "replan" + :timeout "30 days"}) + (.then replan-event-params) + (.catch (fn [_] + nil))) + (js/Promise.resolve nil)))) + +(defn- ( orchestrated + (some? dispatch-results) (assoc :dispatch-results dispatch-results)) + orchestrated (with-dispatched-task-sessions orchestrated)] + (-> ( ( ( ( this .-env) + user-id (some-> (:user-id params) non-empty-str) + orchestrated (orchestrate params)] + (-> (if (= "waiting-approval" (:status orchestrated)) + (-> ( ( ( (:decision value) + non-empty-str + string/lower-case) + decision (if (contains? #{"pending" "approved" "rejected"} decision) + decision + "pending") + approval-comment (some-> (:comment value) non-empty-str)] + (cond-> {:decision decision} + (string? approval-comment) (assoc :comment approval-comment))))) + (defn normalize-session-create [body] (when (map? body) @@ -48,6 +71,67 @@ (string? runtime-provider) (assoc :runtime-provider runtime-provider) (string? runner-id) (assoc :runner-id runner-id))))) +(defn normalize-planning-create + [body] + (when-let [task (normalize-session-create body)] + (let [agent (:agent task)] + (assoc task + :agent + (cond + (string? agent) + {:provider agent + :permission-mode "read-only"} + + (map? agent) + (cond-> agent + (and (= "codex" (some-> (:provider agent) non-empty-str string/lower-case)) + (nil? (:permission-mode agent)) + (nil? (:permissionMode agent))) + (assoc :permission-mode "read-only")) + + :else agent))))) + +(defn normalize-planning-workflow-create + [body] + (when (map? body) + (let [workflow-id (some-> (:workflow-id body) non-empty-str) + planning-session-id (some-> (:planning-session-id body) non-empty-str) + user-id (some-> (:user-id body) non-empty-str) + goal (:goal body) + tasks (or (:tasks body) (:planned-tasks body)) + tasks (when (sequential? tasks) (vec tasks)) + planning-messages (when (sequential? (:planning-messages body)) + (vec (:planning-messages body))) + require-approval? (true? (:require-approval body)) + auto-dispatch? (normalize-bool (:auto-dispatch body) true) + auto-replan? (normalize-bool (:auto-replan body) false) + replan-delay-sec (non-negative-int (:replan-delay-sec body) 0) + replan-note (or (some-> (:replan-note body) non-empty-str) + (some-> (:comment body) non-empty-str)) + approval (normalize-approval (:approval body)) + project (when (map? (:project body)) (:project body)) + agent (:agent body) + runtime-provider (some-> (:runtime-provider body) + non-empty-str + string/lower-case) + runner-id (some-> (:runner-id body) non-empty-str)] + (cond-> {:require-approval require-approval? + :auto-dispatch auto-dispatch? + :auto-replan auto-replan? + :replan-delay-sec replan-delay-sec} + (string? workflow-id) (assoc :workflow-id workflow-id) + (string? planning-session-id) (assoc :planning-session-id planning-session-id) + (string? user-id) (assoc :user-id user-id) + (map? goal) (assoc :goal goal) + (sequential? tasks) (assoc :tasks tasks) + (sequential? planning-messages) (assoc :planning-messages planning-messages) + (map? project) (assoc :project project) + (some? agent) (assoc :agent agent) + (string? runtime-provider) (assoc :runtime-provider runtime-provider) + (string? runner-id) (assoc :runner-id runner-id) + (string? replan-note) (assoc :replan-note replan-note) + (map? approval) (assoc :approval approval))))) + (defn normalize-runner-register [body user-id] (when (map? body) diff --git a/deps/workers/src/logseq/agents/routes.cljs b/deps/workers/src/logseq/agents/routes.cljs index ab243831e4..8bed4b397b 100644 --- a/deps/workers/src/logseq/agents/routes.cljs +++ b/deps/workers/src/logseq/agents/routes.cljs @@ -5,6 +5,16 @@ [["/auth" ["/chatgpt/import" {:methods {"POST" :auth.chatgpt/import}}] ["/chatgpt/status" {:methods {"GET" :auth.chatgpt/status}}]] + ["/planning" + ["/sessions" {:methods {"POST" :planning.sessions/create}}] + ["/sessions/:planning-session-id" {:methods {"GET" :planning.sessions/get}}] + ["/sessions/:planning-session-id/approval" {:methods {"POST" :planning.sessions/approval}}] + ["/sessions/:planning-session-id/tasks/sync" {:methods {"POST" :planning.sessions/tasks.sync}}] + ["/sessions/:planning-session-id/replan" {:methods {"POST" :planning.sessions/replan}}] + ["/chat/:planning-session-id" {:methods {"GET" :planning.chat/transport + "POST" :planning.chat/transport}}] + ["/workflows" {:methods {"POST" :planning.workflows/create}}] + ["/workflows/:workflow-id" {:methods {"GET" :planning.workflows/get}}]] ["/sessions" ["" {:methods {"POST" :sessions/create}}] ["/:session-id" diff --git a/deps/workers/src/logseq/agents/worker.cljs b/deps/workers/src/logseq/agents/worker.cljs index 2cbdcf7f8e..3bd0d9c7d8 100644 --- a/deps/workers/src/logseq/agents/worker.cljs +++ b/deps/workers/src/logseq/agents/worker.cljs @@ -1,11 +1,15 @@ (ns logseq.agents.worker ;; Turn off false defclass errors {:clj-kondo/config {:linters {:unresolved-symbol {:level :off}}}} - (:require ["cloudflare:workers" :refer [DurableObject]] + (:require ["agents" :refer [Agent]] + ["cloudflare:workers" :refer [DurableObject WorkflowEntrypoint]] [logseq.agents.dispatch :as dispatch] [logseq.agents.do :as agent-do] + [logseq.agents.planning-agent :as planning-agent] + [logseq.agents.planning-workflow :as planning-workflow] [logseq.sync.logging :as logging] [logseq.sync.sentry.worker :as sentry] + [promesa.core :as p] [shadow.cljs.modern :refer (defclass)])) (logging/install!) @@ -28,3 +32,106 @@ Object (fetch [this request] (agent-do/handle-fetch this request))) + +(defclass PlanningWorkflow + (extends WorkflowEntrypoint) + + (constructor [this ctx env] + (super ctx env) + (set! (.-ctx this) ctx) + (set! (.-env this) env)) + + Object + (run [this event step] + (planning-workflow/run this event step))) + +(defn- planning-session-id + [this props] + (or (some-> props :planning-session-id str) + (some-> this .-name str) + "default")) + +(defn- get-planning-state + [this props] + (let [planning-session-id (planning-session-id this props) + current-state (some-> this .-state (js->clj :keywordize-keys true))] + (planning-agent/normalize-state planning-session-id current-state))) + +(defn- set-planning-state! + [^js this state] + (let [state' (clj->js state) + set-state (aget this "setState") + set-state-internal (aget this "_setStateInternal")] + (cond + (fn? set-state) + (.call set-state this state') + + (fn? set-state-internal) + (.call set-state-internal this state' "server") + + :else + (set! (.-_state this) state'))) + state) + +(defclass PlanningSessionAgent + (extends Agent) + + (constructor [this ctx env] + (super ctx env) + (set! (.-ctx this) ctx) + (set! (.-env this) env)) + + Object + (onStart [this props] + (let [props (when (some? props) + (js->clj props :keywordize-keys true))] + (set-planning-state! this (get-planning-state this props)))) + + (onRequest [this request] + (let [method (.-method request)] + (cond + (= method "GET") + (planning-agent/json-response (get-planning-state this nil) 200) + + (= method "POST") + (-> (.text request) + (p/then (fn [raw] + (if-let [content (planning-agent/parse-message-content raw)] + (let [session-id (planning-session-id this nil) + next-state (-> (get-planning-state this nil) + (planning-agent/append-message session-id "user" content) + (set-planning-state! this))] + (planning-agent/json-response {:ok true + :state next-state} + 200)) + (planning-agent/json-response {:error "invalid message"} 400)))) + (p/catch (fn [_error] + (planning-agent/json-response {:error "invalid message"} 400)))) + + :else + (planning-agent/json-response {:error "method not allowed"} 405)))) + + (onConnect [this connection _ctx] + (let [state (get-planning-state this nil)] + (.send connection + (js/JSON.stringify + (clj->js {:type "planning.state" + :state state}))))) + + (onMessage [this connection message] + (let [content (planning-agent/parse-message-content message)] + (if-not (string? content) + (.send connection + (js/JSON.stringify + #js {:type "planning.error" + :message "invalid message"})) + (let [session-id (planning-session-id this nil) + next-state (-> (get-planning-state this nil) + (planning-agent/append-message session-id "user" content) + (set-planning-state! this)) + latest-message (last (:messages next-state))] + (.broadcast this + (js/JSON.stringify + (clj->js {:type "planning.message" + :message latest-message + :state next-state})))))))) diff --git a/deps/workers/src/logseq/sync/malli_schema.cljs b/deps/workers/src/logseq/sync/malli_schema.cljs index 72925aed7e..c384a95797 100644 --- a/deps/workers/src/logseq/sync/malli_schema.cljs +++ b/deps/workers/src/logseq/sync/malli_schema.cljs @@ -373,6 +373,32 @@ [:snapshot-id {:optional true} [:maybe :string]] [:message {:optional true} [:maybe :string]]]) +(def planning-session-response-schema + [:map + [:planning-session-id :string] + [:status :string] + [:workflow-id {:optional true} [:maybe :string]] + [:chat-path {:optional true} :string] + [:goal {:optional true} :map] + [:project {:optional true} :map] + [:agent {:optional true} :any] + [:approval-status {:optional true} :string] + [:require-approval {:optional true} :boolean] + [:auto-dispatch {:optional true} :boolean] + [:auto-replan {:optional true} :boolean] + [:replan-delay-sec {:optional true} :int] + [:plan {:optional true} :map] + [:scheduled-actions {:optional true} [:sequential :map]] + [:dispatch-sessions {:optional true} [:sequential :map]] + [:created-at {:optional true} :int] + [:updated-at {:optional true} :int]]) + +(def planning-workflow-response-schema + [:map + [:workflow-id :string] + [:status :string] + [:planning-session-id {:optional true} :string]]) + (def runner-response-schema [:map [:runner-id :string] @@ -451,6 +477,10 @@ :sessions/snapshot sessions-snapshot-response-schema :sessions/events sessions-events-response-schema :sessions/branches sessions-branches-response-schema + :planning.sessions/create planning-session-response-schema + :planning.sessions/get planning-session-response-schema + :planning.workflows/create planning-workflow-response-schema + :planning.workflows/get planning-workflow-response-schema :auth.chatgpt/import auth-chatgpt-status-response-schema :auth.chatgpt/status auth-chatgpt-status-response-schema :runners/register runners-register-response-schema diff --git a/deps/workers/src/logseq/sync/worker/dispatch.cljs b/deps/workers/src/logseq/sync/worker/dispatch.cljs index 30920c3c9a..885b8b95a5 100644 --- a/deps/workers/src/logseq/sync/worker/dispatch.cljs +++ b/deps/workers/src/logseq/sync/worker/dispatch.cljs @@ -51,6 +51,7 @@ (index-handler/handle-fetch #js {:env env :d1 (aget env "DB")} request) (or (string/starts-with? path "/auth") + (string/starts-with? path "/planning") (string/starts-with? path "/sessions")) (if-let [^js agents-service (aget env "AGENTS_SERVICE")] (if (local-dev-host? request) diff --git a/deps/workers/test/logseq/agents/handler_test.cljs b/deps/workers/test/logseq/agents/handler_test.cljs new file mode 100644 index 0000000000..98bc7b9665 --- /dev/null +++ b/deps/workers/test/logseq/agents/handler_test.cljs @@ -0,0 +1,60 @@ +(ns logseq.agents.handler-test + (:require [cljs.test :refer [async deftest is]] + [logseq.agents.handler :as handler] + [logseq.sync.platform.core :as platform])) + +(deftest planning-session-get-requires-planning-store-test + (async done + (let [request (platform/request "http://example.com/planning/sessions/plan-1" + #js {:method "GET"})] + (-> (js/Promise.resolve + (handler/handle {:env #js {} + :request request + :url (platform/request-url request) + :claims #js {"sub" "user-1"} + :route {:handler :planning.sessions/get + :path-params {:planning-session-id "plan-1"}}})) + (.then (fn [response] + (is (= 503 (.-status response))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done))))))) + +(deftest planning-chat-transport-requires-agent-binding-test + (async done + (let [request (platform/request "http://example.com/planning/chat/plan-1" + #js {:method "GET"})] + (-> (js/Promise.resolve + (handler/handle {:env #js {} + :request request + :url (platform/request-url request) + :claims #js {"sub" "user-1"} + :route {:handler :planning.chat/transport + :path-params {:planning-session-id "plan-1"}}})) + (.then (fn [response] + (is (= 503 (.-status response))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done))))))) + +(deftest planning-session-replan-requires-workflow-binding-test + (async done + (let [request (platform/request "http://example.com/planning/sessions/plan-1/replan" + #js {:method "POST" + :headers #js {"content-type" "application/json"} + :body (js/JSON.stringify #js {:tasks #js []})})] + (-> (js/Promise.resolve + (handler/handle {:env #js {} + :request request + :url (platform/request-url request) + :claims #js {"sub" "user-1"} + :route {:handler :planning.sessions/replan + :path-params {:planning-session-id "plan-1"}}})) + (.then (fn [response] + (is (= 503 (.-status response))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done))))))) diff --git a/deps/workers/test/logseq/agents/planning_workflow_test.cljs b/deps/workers/test/logseq/agents/planning_workflow_test.cljs new file mode 100644 index 0000000000..faebba1136 --- /dev/null +++ b/deps/workers/test/logseq/agents/planning_workflow_test.cljs @@ -0,0 +1,122 @@ +(ns logseq.agents.planning-workflow-test + (:require [cljs.test :refer [async deftest is]] + [logseq.agents.planning-workflow :as planning-workflow])) + +(deftest build-plan-test + (let [plan (planning-workflow/build-plan + {:goal {:title "Plan Goal" + :description "Build a planner"} + :tasks [{:title "Task A" + :description "Desc A"} + {:content "Explicit content"}]})] + (is (= "Build a planner" (:goal-understanding plan))) + (is (= [{:title "Task A" + :content "Task A\nDesc A" + :description "Desc A"} + {:title "Task 2" + :content "Explicit content"}] + (:tasks plan))))) + +(deftest run-awaits-approval-before-dispatch-test + (async done + (-> (planning-workflow/run nil + {:planning-session-id "plan-1" + :goal {:title "Plan Goal" + :description "Build planning workflow"} + :tasks [{:title "Task A" + :description "Desc A"}] + :project {:id "project-1" + :repo-url "https://github.com/example/repo" + :base-branch "main"} + :agent {:provider "codex"} + :runtime-provider "local-runner" + :runner-id "runner-1" + :require-approval true + :approval {:decision "pending"} + :auto-dispatch true + :auto-replan true + :replan-delay-sec 300} + nil) + (.then (fn [result] + (is (= "waiting-approval" (:status result))) + (is (= [] (:dispatch-sessions result))) + (is (= "pending" (get-in result [:planning-state :approval-status]))) + (is (= "https://github.com/example/repo" + (get-in result [:planning-state :repo-aware :repo-url]))) + (is (= [] (:scheduled-actions result))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done)))))) + +(deftest run-dispatches-approved-repo-aware-tasks-and-schedules-replan-test + (async done + (-> (planning-workflow/run nil + {:planning-session-id "plan-2" + :goal {:title "Plan Goal" + :description "Build planning workflow"} + :tasks [{:title "Task A" + :description "Desc A"}] + :project {:id "project-1" + :repo-url "https://github.com/example/repo" + :base-branch "main"} + :agent {:provider "codex" + :mode "gpt-5-codex"} + :runtime-provider "local-runner" + :runner-id "runner-1" + :require-approval true + :approval {:decision "approved"} + :auto-dispatch true + :auto-replan true + :replan-delay-sec 120} + nil) + (.then (fn [result] + (is (= "dispatching" (:status result))) + (is (= 1 (count (:dispatch-sessions result)))) + (is (= "https://github.com/example/repo" + (get-in result [:dispatch-sessions 0 :project :repo-url]))) + (is (= "codex" + (get-in result [:dispatch-sessions 0 :agent :provider]))) + (is (= "local-runner" (get-in result [:dispatch-sessions 0 :runtime-provider]))) + (is (= "runner-1" (get-in result [:dispatch-sessions 0 :runner-id]))) + (is (= [{:type "replan" + :delay-sec 120}] + (:scheduled-actions result))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done)))))) + +(deftest run-replanning-preserves-execution-owned-fields-test + (async done + (-> (planning-workflow/run nil + {:planning-session-id "plan-3" + :goal {:title "Plan Goal"} + :tasks [{:task-uuid "task-1" + :title "Task A (replanned)" + :description "Desc A"} + {:task-uuid "task-2" + :title "Task B" + :description "Desc B"}] + :existing-tasks [{:task-uuid "task-1" + :title "Task A" + :status "Doing" + :session-id "sess-1" + :pr-url "https://github.com/example/repo/pull/1"}] + :project {:id "project-1" + :repo-url "https://github.com/example/repo"} + :agent {:provider "codex"} + :approval {:decision "approved"} + :auto-dispatch false + :auto-replan false} + nil) + (.then (fn [result] + (is (= "Task A" (get-in result [:reconciled-tasks 0 :title]))) + (is (= "sess-1" (get-in result [:reconciled-tasks 0 :session-id]))) + (is (= "https://github.com/example/repo/pull/1" + (get-in result [:reconciled-tasks 0 :pr-url]))) + (is (= 2 (count (:reconciled-tasks result)))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done)))))) diff --git a/deps/workers/test/logseq/agents/request_test.cljs b/deps/workers/test/logseq/agents/request_test.cljs index cfcd56494f..c370ed3cb8 100644 --- a/deps/workers/test/logseq/agents/request_test.cljs +++ b/deps/workers/test/logseq/agents/request_test.cljs @@ -63,6 +63,95 @@ :pr-enabled true}} normalized))))) +(deftest normalize-planning-create-test + (testing "normalizes planning request into agent task with codex read-only default" + (let [body {:session-id "plan-1" + :node-id "goal-1" + :node-title "Plan Goal" + :content "Plan this task graph" + :project {:id "project-1" + :title "Demo Project" + :repo-url "https://github.com/example/repo"} + :agent "codex"} + normalized (request/normalize-planning-create body)] + (is (= {:id "plan-1" + :source {:node-id "goal-1" + :node-title "Plan Goal"} + :intent {:content "Plan this task graph"} + :project {:id "project-1" + :title "Demo Project" + :repo-url "https://github.com/example/repo"} + :agent {:provider "codex" + :permission-mode "read-only"} + :capabilities {:push-enabled true + :pr-enabled true}} + normalized)))) + + (testing "keeps explicit agent permission mode" + (let [body {:session-id "plan-2" + :node-id "goal-2" + :node-title "Plan Goal" + :content "Plan this task graph" + :project {:id "project-1" + :title "Demo Project" + :repo-url "https://github.com/example/repo"} + :agent {:provider "codex" + :permission-mode "full-access"}} + normalized (request/normalize-planning-create body)] + (is (= "full-access" (get-in normalized [:agent :permission-mode])))))) + +(deftest normalize-planning-workflow-create-test + (let [body {:workflow-id "wf-1" + :planning-session-id "plan-1" + :user-id "user-1" + :goal {:title "Plan Goal" + :description "Plan this feature"} + :tasks [{:title "Task A" + :description "Desc A"}] + :project {:id "project-1" + :repo-url "https://github.com/example/repo" + :base-branch "main"} + :agent {:provider "codex"} + :runtime-provider "local-runner" + :runner-id "runner-1" + :approval {:decision "approved" + :comment "looks good"} + :auto-dispatch true + :auto-replan true + :replan-delay-sec 300 + :require-approval true} + normalized (request/normalize-planning-workflow-create body)] + (is (= {:workflow-id "wf-1" + :planning-session-id "plan-1" + :user-id "user-1" + :goal {:title "Plan Goal" + :description "Plan this feature"} + :tasks [{:title "Task A" + :description "Desc A"}] + :project {:id "project-1" + :repo-url "https://github.com/example/repo" + :base-branch "main"} + :agent {:provider "codex"} + :runtime-provider "local-runner" + :runner-id "runner-1" + :approval {:decision "approved" + :comment "looks good"} + :auto-dispatch true + :auto-replan true + :replan-delay-sec 300 + :require-approval true} + normalized)))) + +(deftest normalize-planning-workflow-create-defaults-test + (let [body {:goal {:title "Plan Goal"}} + normalized (request/normalize-planning-workflow-create body)] + (is (= {:goal {:title "Plan Goal"} + :require-approval false + :auto-dispatch true + :auto-replan false + :replan-delay-sec 0} + normalized)))) + (deftest sessions-pr-coerce-test (testing "accepts sessions/pr request payload" (let [body {:title "feat: add m14 publish" diff --git a/deps/workers/test/logseq/agents/routes_test.cljs b/deps/workers/test/logseq/agents/routes_test.cljs index 38783673c3..0ebf99c2e3 100644 --- a/deps/workers/test/logseq/agents/routes_test.cljs +++ b/deps/workers/test/logseq/agents/routes_test.cljs @@ -68,3 +68,25 @@ (is (= :auth.chatgpt/import (:handler match)))) (let [match (routes/match-route "GET" "/auth/chatgpt/status")] (is (= :auth.chatgpt/status (:handler match)))))) + +(deftest match-route-planning-test + (testing "planning routes" + (let [match (routes/match-route "POST" "/planning/sessions")] + (is (= :planning.sessions/create (:handler match)))) + (let [match (routes/match-route "GET" "/planning/sessions/plan-1")] + (is (= :planning.sessions/get (:handler match))) + (is (= "plan-1" (get-in match [:path-params :planning-session-id])))) + (let [match (routes/match-route "POST" "/planning/sessions/plan-1/approval")] + (is (= :planning.sessions/approval (:handler match))) + (is (= "plan-1" (get-in match [:path-params :planning-session-id])))) + (let [match (routes/match-route "POST" "/planning/sessions/plan-1/replan")] + (is (= :planning.sessions/replan (:handler match))) + (is (= "plan-1" (get-in match [:path-params :planning-session-id])))) + (let [match (routes/match-route "GET" "/planning/chat/plan-1")] + (is (= :planning.chat/transport (:handler match))) + (is (= "plan-1" (get-in match [:path-params :planning-session-id])))) + (let [match (routes/match-route "POST" "/planning/workflows")] + (is (= :planning.workflows/create (:handler match)))) + (let [match (routes/match-route "GET" "/planning/workflows/workflow-1")] + (is (= :planning.workflows/get (:handler match))) + (is (= "workflow-1" (get-in match [:path-params :workflow-id])))))) diff --git a/deps/workers/test/logseq/agents/runtime_transport_test_runner.cljs b/deps/workers/test/logseq/agents/runtime_transport_test_runner.cljs index 46e262160b..d8f2b3c4f8 100644 --- a/deps/workers/test/logseq/agents/runtime_transport_test_runner.cljs +++ b/deps/workers/test/logseq/agents/runtime_transport_test_runner.cljs @@ -1,5 +1,9 @@ (ns logseq.agents.runtime-transport-test-runner (:require [cljs.test :as ct] + [logseq.agents.handler-test] + [logseq.agents.planning-workflow-test] + [logseq.agents.request-test] + [logseq.agents.routes-test] [logseq.agents.runtime-provider-test] [logseq.agents.sandbox-test] [shadow.test :as st] @@ -19,6 +23,17 @@ (defn main [& _args] (reset-test-data!) (ct/test-vars [#'logseq.agents.runtime-provider-test/local-runner-provider-provision-test + #'logseq.agents.planning-workflow-test/build-plan-test + #'logseq.agents.planning-workflow-test/run-awaits-approval-before-dispatch-test + #'logseq.agents.planning-workflow-test/run-dispatches-approved-repo-aware-tasks-and-schedules-replan-test + #'logseq.agents.planning-workflow-test/run-replanning-preserves-execution-owned-fields-test + #'logseq.agents.routes-test/match-route-planning-test + #'logseq.agents.request-test/normalize-planning-create-test + #'logseq.agents.request-test/normalize-planning-workflow-create-test + #'logseq.agents.request-test/normalize-planning-workflow-create-defaults-test + #'logseq.agents.handler-test/planning-session-get-requires-planning-store-test + #'logseq.agents.handler-test/planning-chat-transport-requires-agent-binding-test + #'logseq.agents.handler-test/planning-session-replan-requires-workflow-binding-test #'logseq.agents.sandbox-test/session-endpoint-test #'logseq.agents.sandbox-test/create-session-payload-test #'logseq.agents.sandbox-test/send-message-uses-acp-prompt-test diff --git a/deps/workers/test/logseq/sync/worker_test.cljs b/deps/workers/test/logseq/sync/worker_test.cljs index 28c0471a8e..908f26c296 100644 --- a/deps/workers/test/logseq/sync/worker_test.cljs +++ b/deps/workers/test/logseq/sync/worker_test.cljs @@ -1,11 +1,11 @@ (ns logseq.sync.worker-test (:require [cljs.test :refer [async deftest is]] [datascript.core :as d] + [logseq.db.common.order :as db-order] + [logseq.db.frontend.schema :as db-schema] [logseq.sync.order :as sync-order] [logseq.sync.platform.core :as platform] - [logseq.sync.worker.dispatch :as dispatch] - [logseq.db.common.order :as db-order] - [logseq.db.frontend.schema :as db-schema])) + [logseq.sync.worker.dispatch :as dispatch])) (defn- new-conn [] (d/create-conn db-schema/schema)) @@ -55,6 +55,17 @@ (is false (str "unexpected error: " error)) (done))))))) +(deftest dispatch-worker-fetch-planning-service-unavailable-test + (async done + (let [request (platform/request "http://example.com/planning/sessions" #js {:method "POST"}) + resp (dispatch/handle-worker-fetch request #js {})] + (-> (.then resp (fn [resolved] + (is (= 503 (.-status resolved))) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done))))))) + (deftest dispatch-worker-fetch-sessions-forward-to-agents-service-test (async done (let [request (platform/request "http://example.com/sessions/session-2/events?since=1" #js {:method "GET"}) @@ -74,6 +85,27 @@ (is false (str "unexpected error: " error)) (done))))))) +(deftest dispatch-worker-fetch-planning-forward-to-agents-service-test + (async done + (let [request (platform/request "http://example.com/planning/sessions" #js {:method "POST" + :headers #js {"content-type" "application/json"} + :body (js/JSON.stringify #js {:session-id "plan-1"})}) + captured-url (atom nil) + env #js {:AGENTS_SERVICE #js {:fetch (fn [forwarded] + (reset! captured-url (.-url forwarded)) + (js/Promise.resolve + (js/Response. (js/JSON.stringify #js {:ok true}) + #js {:status 202 + :headers #js {"content-type" "application/json"}})))}}] + (-> (dispatch/handle-worker-fetch request env) + (.then (fn [resolved] + (is (= 202 (.-status resolved))) + (is (= "http://example.com/planning/sessions" @captured-url)) + (done))) + (.catch (fn [error] + (is false (str "unexpected error: " error)) + (done))))))) + (deftest dispatch-worker-fetch-sessions-local-retry-test (async done (let [request (platform/request "http://127.0.0.1:8787/sessions/session-3" diff --git a/deps/workers/worker/migrations/0007_add_planning_sessions.sql b/deps/workers/worker/migrations/0007_add_planning_sessions.sql new file mode 100644 index 0000000000..ddfca036ec --- /dev/null +++ b/deps/workers/worker/migrations/0007_add_planning_sessions.sql @@ -0,0 +1,26 @@ +create table if not exists planning_sessions ( + planning_session_id text primary key, + user_id text not null, + workflow_id text, + status text not null default 'queued', + goal_json text, + plan_json text, + project_json text, + agent_json text, + approval_status text not null default 'pending', + require_approval integer not null default 0, + auto_dispatch integer not null default 1, + auto_replan integer not null default 0, + replan_delay_sec integer not null default 0, + scheduled_actions_json text, + dispatch_sessions_json text, + last_error text, + created_at integer not null, + updated_at integer not null +); + +create index if not exists idx_planning_sessions_user_updated_at +on planning_sessions (user_id, updated_at desc); + +create index if not exists idx_planning_sessions_workflow_id +on planning_sessions (workflow_id); diff --git a/deps/workers/worker/wrangler.agents.toml b/deps/workers/worker/wrangler.agents.toml index 2a467be350..3259394a8f 100644 --- a/deps/workers/worker/wrangler.agents.toml +++ b/deps/workers/worker/wrangler.agents.toml @@ -16,6 +16,15 @@ invocation_logs = false name = "LOGSEQ_AGENT_SESSION_DO" class_name = "AgentSessionDO" +[[durable_objects.bindings]] +name = "PLANNING_AGENT" +class_name = "PlanningSessionAgent" + +[[workflows]] +binding = "PLANNING_WORKFLOW" +name = "logseq-agents-planning-dev" +class_name = "PlanningWorkflow" + [[r2_buckets]] binding = "BACKUP_BUCKET" bucket_name = "logseq-sync-assets-dev" @@ -30,6 +39,10 @@ database_id = "00325aa2-c805-4693-b599-900a25dcde42" tag = "v1" new_sqlite_classes = [ "AgentSessionDO" ] +[[migrations]] +tag = "v2" +new_sqlite_classes = [ "PlanningSessionAgent" ] + [vars] COGNITO_JWKS_URL = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8/.well-known/jwks.json" COGNITO_ISSUER = "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_dtagLnju8" @@ -56,6 +69,15 @@ SENTRY_TRACES_SAMPLE_RATE = "0.1" name = "LOGSEQ_AGENT_SESSION_DO" class_name = "AgentSessionDO" +[[env.staging.durable_objects.bindings]] +name = "PLANNING_AGENT" +class_name = "PlanningSessionAgent" + +[[env.staging.workflows]] +binding = "PLANNING_WORKFLOW" +name = "logseq-agents-planning-staging" +class_name = "PlanningWorkflow" + [[env.staging.r2_buckets]] binding = "BACKUP_BUCKET" bucket_name = "logseq-sync-assets-dev" @@ -70,6 +92,10 @@ database_id = "00325aa2-c805-4693-b599-900a25dcde42" tag = "v1" new_sqlite_classes = [ "AgentSessionDO" ] +[[env.staging.migrations]] +tag = "v2" +new_sqlite_classes = [ "PlanningSessionAgent" ] + [env.staging.version_metadata] binding = "CF_VERSION_METADATA" @@ -91,6 +117,15 @@ SENTRY_TRACES_SAMPLE_RATE = "0.1" name = "LOGSEQ_AGENT_SESSION_DO" class_name = "AgentSessionDO" +[[env.prod.durable_objects.bindings]] +name = "PLANNING_AGENT" +class_name = "PlanningSessionAgent" + +[[env.prod.workflows]] +binding = "PLANNING_WORKFLOW" +name = "logseq-agents-planning-prod" +class_name = "PlanningWorkflow" + [[env.prod.r2_buckets]] binding = "BACKUP_BUCKET" bucket_name = "logseq-sync-assets-prod" @@ -105,5 +140,9 @@ database_id = "4c80e058-69b5-4985-88d1-f53711d817ba" tag = "v1" new_sqlite_classes = [ "AgentSessionDO" ] +[[env.prod.migrations]] +tag = "v2" +new_sqlite_classes = [ "PlanningSessionAgent" ] + [env.prod.version_metadata] binding = "CF_VERSION_METADATA" diff --git a/deps/workers/yarn.lock b/deps/workers/yarn.lock index 3f44f5d71e..c004366f40 100644 --- a/deps/workers/yarn.lock +++ b/deps/workers/yarn.lock @@ -2,6 +2,15 @@ # yarn lockfile v1 +"@apidevtools/json-schema-ref-parser@^11.5.5": + version "11.9.3" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz#0e0c9061fc41cf03737d499a4e6a8299fdd2bfa7" + integrity sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.15" + js-yaml "^4.1.0" + "@apm-js-collab/code-transformer@^0.8.0": version "0.8.2" resolved "https://registry.yarnpkg.com/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz#a3160f16d1c4df9cb81303527287ad18d00994d1" @@ -16,11 +25,28 @@ debug "^4.4.1" module-details-from-path "^1.0.4" +"@babel/runtime-corejs3@^7.26.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz#56cd28ec515364482afeb880b476936a702461b9" + integrity sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA== + dependencies: + core-js-pure "^3.48.0" + +"@babel/runtime@^7.26.0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== + "@bufbuild/protobuf@^2.6.2": version "2.11.0" resolved "https://registry.yarnpkg.com/@bufbuild/protobuf/-/protobuf-2.11.0.tgz#3ec3985c9074b23aea337957225fe15a0e845f8e" integrity sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ== +"@cfworker/json-schema@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6" + integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== + "@connectrpc/connect-web@2.0.0-rc.3": version "2.0.0-rc.3" resolved "https://registry.yarnpkg.com/@connectrpc/connect-web/-/connect-web-2.0.0-rc.3.tgz#4dd9c18b8105cb7395077808c4967a9eafdffe76" @@ -31,6 +57,11 @@ resolved "https://registry.yarnpkg.com/@connectrpc/connect/-/connect-2.0.0-rc.3.tgz#f75cd1bd132dd983ff4f16432aaf5309ee673662" integrity sha512-ARBt64yEyKbanyRETTjcjJuHr2YXorzQo0etyS5+P6oSeW8xEuzajA9g+zDnMcj1hlX2dQE93foIWQGfpru7gQ== +"@hono/node-server@^1.19.9": + version "1.19.11" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.11.tgz#dc419f0826dd2504e9fc86ad289d5636a0444e2f" + integrity sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g== + "@isaacs/cliui@^9.0.0": version "9.0.0" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-9.0.0.tgz#4d0a3f127058043bf2e7ee169eaf30ed901302f3" @@ -43,6 +74,34 @@ dependencies: minipass "^7.0.4" +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + +"@modelcontextprotocol/sdk@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz#5b35d73062125f126cc70b0be83cbab53bcdde74" + integrity sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.2.1" + express-rate-limit "^8.2.1" + hono "^4.11.4" + jose "^6.1.3" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.1" + "@opentelemetry/api-logs@0.207.0": version "0.207.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz#ae991c51eedda55af037a3e6fc1ebdb12b289f49" @@ -461,6 +520,16 @@ dependencies: "@types/node" "*" +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/lodash@^4.17.7": + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" + integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== + "@types/mysql@2.15.27": version "2.15.27" resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.27.tgz#fb13b0e8614d39d42f40f381217ec3215915f1e9" @@ -507,6 +576,14 @@ dependencies: "@types/node" "*" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-import-attributes@^1.9.5: version "1.9.5" resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" @@ -517,6 +594,54 @@ acorn@^8.15.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +agents@^0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/agents/-/agents-0.7.5.tgz#c518376c4bc96db9e133b9d59a24678b9b1585e0" + integrity sha512-sB37uMLt4aenYJwhbCSnIaYZuepoJwXYTX/8WXJGFj/FuWoScHrY+o2LrxKgzSmDA4pMMn4JIXaq/wMHs8OAJg== + dependencies: + "@cfworker/json-schema" "^4.1.1" + "@modelcontextprotocol/sdk" "1.26.0" + cron-schedule "^6.0.0" + json-schema "^0.4.0" + json-schema-to-typescript "^15.0.4" + mimetext "^3.0.28" + nanoid "^5.1.6" + partyserver "^0.3.3" + partysocket "1.1.16" + yargs "^18.0.0" + +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + +ajv@^8.0.0, ajv@^8.17.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + +ansi-styles@^6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -556,6 +681,21 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.2.tgz#1a32cdb966beaf68de50a9dfbe5b58f83cb8890c" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + brace-expansion@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" @@ -591,6 +731,27 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +bytes@^3.1.2, bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + chalk@^5.3.0: version "5.6.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" @@ -611,12 +772,59 @@ cjs-module-lexer@^2.2.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== +cliui@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-9.0.1.tgz#6f7890f386f6f1f79953adc1f78dec46fcc2d291" + integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w== + dependencies: + string-width "^7.2.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + compare-versions@^6.1.0: version "6.1.1" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-6.1.1.tgz#7af3cc1099ba37d244b3145a9af5201b629148a9" integrity sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg== -cross-spawn@^7.0.6: +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.1.tgz#a8b7bbeb2904befdfb6787e5c0c086959f605f9b" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + +content-type@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +core-js-pure@^3.48.0: + version "3.48.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.48.0.tgz#7d5a3fe1ec3631b9aa76a81c843ac2ce918e5023" + integrity sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw== + +cors@^2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + +cron-schedule@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cron-schedule/-/cron-schedule-6.0.0.tgz#62f4fa6a980c2c14f322deaaaeee4c27f9f0c84f" + integrity sha512-BoZaseYGXOo5j5HUwTaegIog3JJbuH4BbrY9A1ArLjXpy+RWb3mV28F/9Gv1dDA7E2L8kngWva4NWisnLTyfgQ== + +cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -625,7 +833,7 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.3.5, debug@^4.4.1: +debug@^4.3.5, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -644,6 +852,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +depd@^2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + detect-libc@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" @@ -657,6 +870,15 @@ dockerfile-ast@^0.7.1: vscode-languageserver-textdocument "^1.0.8" vscode-languageserver-types "^3.17.3" +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + e2b@2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/e2b/-/e2b-2.14.0.tgz#40f163315fa73d2e6fc11d23c09f2dfa902041e3" @@ -673,6 +895,21 @@ e2b@2.14.0: platform "^1.3.6" tar "^7.5.9" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +emoji-regex@^10.3.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + +encodeurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.5" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" @@ -680,16 +917,133 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +escalade@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +etag@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-target-polyfill@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/event-target-polyfill/-/event-target-polyfill-0.0.4.tgz#060ee66e85aaedc76b6fa66079782dcc11cba496" + integrity sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ== + +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1: + version "3.0.6" + resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" + integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== + +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + expand-template@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== +express-rate-limit@^8.2.1: + version "8.3.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.3.1.tgz#0aaba098eadd40f6737f30a98e6b16fa1a29edfb" + integrity sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw== + dependencies: + ip-address "10.1.0" + +express@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + foreground-child@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" @@ -703,11 +1057,60 @@ forwarded-parse@2.1.2: resolved "https://registry.yarnpkg.com/forwarded-parse/-/forwarded-parse-2.1.2.tgz#08511eddaaa2ddfd56ba11138eee7df117a09325" integrity sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw== +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-east-asian-width@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" + integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -725,6 +1128,46 @@ glob@^11.1.0: package-json-from-dist "^1.0.0" path-scurry "^2.0.0" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +hono@^4.11.4: + version "4.12.5" + resolved "https://registry.yarnpkg.com/hono/-/hono-4.12.5.tgz#8c16209b35040025d3f110d18f3b821de6cab00f" + integrity sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg== + +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -740,7 +1183,7 @@ import-in-the-middle@^2.0.0, import-in-the-middle@^2.0.6: cjs-module-lexer "^2.2.0" module-details-from-path "^1.0.4" -inherits@^2.0.3, inherits@^2.0.4: +inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -750,6 +1193,33 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ip-address@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4" + integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -767,11 +1237,112 @@ jackspeak@^4.1.1: dependencies: "@isaacs/cliui" "^9.0.0" +jose@^6.1.3: + version "6.2.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.1.tgz#7a6b1de83816deaee9055a558e1278a7b2b9ea1b" + integrity sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw== + +js-base64@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.8.tgz#af44496bc09fa178ed9c4adf67eb2b46f5c6d2a4" + integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow== + +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + dependencies: + argparse "^2.0.1" + +json-schema-to-typescript@^15.0.4: + version "15.0.4" + resolved "https://registry.yarnpkg.com/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz#a530c7f17312503b262ae12233749732171840f3" + integrity sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ== + dependencies: + "@apidevtools/json-schema-ref-parser" "^11.5.5" + "@types/json-schema" "^7.0.15" + "@types/lodash" "^4.17.7" + is-glob "^4.0.3" + js-yaml "^4.1.0" + lodash "^4.17.21" + minimist "^1.2.8" + prettier "^3.2.5" + tinyglobby "^0.2.9" + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +lodash@^4.17.21: + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== + lru-cache@^11.0.0: version "11.2.6" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.2.6.tgz#356bf8a29e88a7a2945507b31f6429a65a192c58" integrity sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.35: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + +mimetext@^3.0.28: + version "3.0.28" + resolved "https://registry.yarnpkg.com/mimetext/-/mimetext-3.0.28.tgz#ffa6292cd13c0913b0783161cd93018203debc09" + integrity sha512-eQXpbNrtxLCjUtiVbR/qR09dbPgZ2o+KR1uA7QKqGhbn8QV7HIL16mXXsobBL4/8TqoYh1us31kfz+dNfCev9g== + dependencies: + "@babel/runtime" "^7.26.0" + "@babel/runtime-corejs3" "^7.26.0" + js-base64 "^3.7.7" + mime-types "^2.1.35" + mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" @@ -791,7 +1362,7 @@ minimatch@^9.0.0: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.3: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -823,11 +1394,21 @@ ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.6.tgz#30363f664797e7d40429f6c16946d6bd7a3f26c9" + integrity sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg== + napi-build-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz#13c22c0187fcfccce1461844136372a47ddc027e" integrity sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + node-abi@^3.3.0: version "3.87.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.87.0.tgz#423e28fea5c2f195fddd98acded9938c001ae6dd" @@ -835,6 +1416,23 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +on-finished@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -859,6 +1457,25 @@ package-json-from-dist@^1.0.0: resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== +parseurl@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +partyserver@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/partyserver/-/partyserver-0.3.3.tgz#89d05eb9d25456d2a1f4454ac7f5d67636887762" + integrity sha512-jSGWNZ5lJj9czq+97y9N02HyZZtQH8wmgJRkTQahfGnzgL5+R12dMX3E+p7BscB2cVj6LZLv1uHpKL057rjJmw== + dependencies: + nanoid "^5.1.6" + +partysocket@1.1.16: + version "1.1.16" + resolved "https://registry.yarnpkg.com/partysocket/-/partysocket-1.1.16.tgz#0c8894b8a1f370c5975bce26a1c7ceff36b0aec0" + integrity sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w== + dependencies: + event-target-polyfill "^0.0.4" + path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -872,6 +1489,11 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" @@ -893,6 +1515,16 @@ pg-types@^2.2.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + platform@^1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" @@ -938,6 +1570,11 @@ prebuild-install@^7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prettier@^3.2.5: + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" @@ -948,6 +1585,14 @@ progress@^2.0.3: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +proxy-addr@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -961,6 +1606,28 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +qs@^6.14.0, qs@^6.14.1: + version "6.15.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.0.tgz#db8fd5d1b1d2d6b5b33adaf87429805f1909e7b3" + integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== + dependencies: + side-channel "^1.1.0" + +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -985,6 +1652,11 @@ readline-sync@^1.4.10: resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-in-the-middle@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz#dbde2587f669398626d56b20c868ab87bf01cce4" @@ -993,16 +1665,64 @@ require-in-the-middle@^8.0.0: debug "^4.3.5" module-details-from-path "^1.0.3" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + semver@^7.3.5: version "7.7.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + shadow-cljs-jar@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/shadow-cljs-jar/-/shadow-cljs-jar-1.3.4.tgz#0939d91c468b4bc5eab5a958f79e7ef5696fdf62" @@ -1033,6 +1753,46 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" @@ -1065,6 +1825,20 @@ source-map@^0.6.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -1072,6 +1846,13 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +strip-ansi@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -1109,6 +1890,19 @@ tar@^7.5.9: minizlib "^3.1.0" yallist "^5.0.0" +tinyglobby@^0.2.9: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -1116,6 +1910,15 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + undici-types@~7.16.0: version "7.16.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" @@ -1126,11 +1929,21 @@ undici@^6.22.0: resolved "https://registry.yarnpkg.com/undici/-/undici-6.23.0.tgz#7953087744d9095a96f115de3140ca3828aff3a4" integrity sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g== +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +vary@^1, vary@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + vscode-languageserver-textdocument@^1.0.8: version "1.0.12" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz#457ee04271ab38998a093c68c2342f53f6e4a631" @@ -1155,6 +1968,15 @@ which@^5.0.0: dependencies: isexe "^3.1.1" +wrap-ansi@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -1170,7 +1992,39 @@ xtend@^4.0.0: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-5.0.0.tgz#00e2de443639ed0d78fd87de0d27469fbcffb533" integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== + +yargs-parser@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8" + integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== + +yargs@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-18.0.0.tgz#6c84259806273a746b09f579087b68a3c2d25bd1" + integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg== + dependencies: + cliui "^9.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + string-width "^7.2.0" + y18n "^5.0.5" + yargs-parser "^22.0.0" + +zod-to-json-schema@^3.25.1: + version "3.25.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba" + integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + +"zod@^3.25 || ^4.0": + version "4.3.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" + integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== diff --git a/docs/agent-guide/003-agent-service.md b/docs/agent-guide/003-agent-service.md index f9424ebee4..3b2b314d6c 100644 --- a/docs/agent-guide/003-agent-service.md +++ b/docs/agent-guide/003-agent-service.md @@ -149,6 +149,57 @@ Errors and idempotency: - GET /sandbox/sessions/:id/stream - server-sent events of normalized agent events +## Planning Layer +- Planning sessions are separate from execution sessions. +- Planning sessions are backed by a Cloudflare Agent instance and addressed by + `planning-session-id`. +- Planning workflows are backed by Cloudflare Workflows and persist their + orchestration state into `planning_sessions`. +- Planning state is product-visible through: + - `GET /planning/sessions/:planning-session-id` + - `POST /planning/sessions` + - `POST /planning/sessions/:planning-session-id/approval` + - `POST /planning/sessions/:planning-session-id/replan` + - `GET|POST /planning/chat/:planning-session-id` + +### Planning Session Shape +- `planning-session-id` +- `workflow-id` +- `status` +- `goal` +- `project` +- `agent` +- `plan` +- `approval-status` +- `require-approval` +- `auto-dispatch` +- `auto-replan` +- `replan-delay-sec` +- `scheduled-actions` +- `dispatch-sessions` + +### Planning To Execution Mapping +- A planning task becomes a normal execution `POST /sessions` payload. +- The mapping is: + - `dispatch-session.id` -> `sessions/create.session-id` + - `dispatch-session.source.node-id` -> `source.node-id` + - `dispatch-session.source.node-title` -> `source.node-title` + - `dispatch-session.intent.content` -> `intent.content` + - `dispatch-session.project` -> `project` + - `dispatch-session.agent` -> `agent` + - `dispatch-session.runtime-provider` -> `runtime-provider` + - `dispatch-session.runner-id` -> `runner-id` + - `dispatch-session.capabilities` -> `capabilities` +- Execution remains on the existing `/sessions` substrate; planning never + provisions a second execution backend. + +### Planning Ownership Rules +- Planning session reads/writes are always resolved against the authenticated + `claims.sub`. +- Planning chat transport is not forwarded by `planning-session-id` alone. +- Approval and replan act on the stored planning session and its tracked + workflow, not on arbitrary workflow ids supplied by the client. + ## Example: Session Creation Payload ```json { diff --git a/package.json b/package.json index 2d46949173..120580e3bb 100644 --- a/package.json +++ b/package.json @@ -154,8 +154,8 @@ "@tabler/icons-react": "^2.47.0", "@tabler/icons-webfont": "^2.47.0", "@tippyjs/react": "4.2.5", + "agents": "^0.7.5", "aws-amplify": "^6.15.6", - "ghostty-web": "^0.4.0", "bignumber.js": "^9.0.2", "chokidar": "3.5.1", "chrono-node": "2.2.4", @@ -167,6 +167,7 @@ "fs": "0.0.1-security", "fs-extra": "9.1.0", "fuse.js": "6.4.6", + "ghostty-web": "^0.4.0", "grapheme-splitter": "1.0.4", "graphology": "0.20.0", "hnswlib-wasm": "^0.8.2", diff --git a/src/main/frontend/components/agent_chat.cljs b/src/main/frontend/components/agent_chat.cljs index 5d11b7ebc3..3c6d56d40c 100644 --- a/src/main/frontend/components/agent_chat.cljs +++ b/src/main/frontend/components/agent_chat.cljs @@ -1,12 +1,15 @@ (ns frontend.components.agent-chat (:require ["@ai-sdk/react" :refer [useChat]] + ["agents/client" :refer [AgentClient]] ["ghostty-web" :refer [FitAddon Terminal init]] [cljs-bean.core :as bean] + [clojure.set :as set] [clojure.string :as string] [frontend.components.select :as select] [frontend.handler.agent :as agent-handler] [frontend.handler.agent-chat-transport :as chat-transport] [frontend.handler.db-based.sync :as db-sync] + [frontend.handler.notification :as notification] [frontend.modules.agent-chat.event :as chat-event] [frontend.state :as state] [logseq.shui.hooks :as hooks] @@ -99,7 +102,9 @@ (defn- ^:large-vars/cleanup-todo session->messages [session block] - (let [events (:events session) + (let [planning-messages (when (sequential? (:planning-messages session)) + (vec (:planning-messages session))) + events (:events session) base (let [acc (atom {:items {} :order [] :item-kind-by-id {} @@ -366,16 +371,18 @@ (append-text-part! item-id role delta))))))))] (doseq [event events] (process-event! event)) - (->> (:order @acc) - (keep (fn [item-id] - (let [entry (get-in @acc [:items item-id]) - parts (vec (keep normalize-message-part (:parts entry))) - role (chat-role (:role entry))] - (when (seq parts) - {:id item-id - :role role - :parts parts})))) - vec))) + (if (seq planning-messages) + planning-messages + (->> (:order @acc) + (keep (fn [item-id] + (let [entry (get-in @acc [:items item-id]) + parts (vec (keep normalize-message-part (:parts entry))) + role (chat-role (:role entry))] + (when (seq parts) + {:id item-id + :role role + :parts parts})))) + vec)))) task-text (some-> (or (:block/raw-title block) (:block/title block)) string/trim) user-message (when-not (string/blank? task-text) @@ -427,6 +434,48 @@ (let [single-line (string/replace summary #"\s+" " ")] (subs single-line 0 (min commit-message-max-len (count single-line)))))) +(def ^:private planner-started-statuses + #{"Doing" "Done" "In Review" "Canceled"}) + +(defn- planning-session-tasks + [session session-chat-messages] + (or (some-> session :plan :tasks seq vec) + (some-> session-chat-messages + latest-assistant-summary + agent-handler/planner-tasks-from-text))) + +(defn- planning-task-started? + [task] + (or (string? (normalized-text (:session-id task))) + (contains? planner-started-statuses + (some-> (:status task) normalized-text)))) + +(defn- executable-planner-tasks + [tasks] + (->> tasks + (filter map?) + (remove planning-task-started?) + vec)) + +(defn- planning-task-title + [task] + (or (some-> (:title task) normalized-text) + (some-> (:content task) + normalized-text + (string/split #"\n") + first + normalized-text) + "Planned task")) + +(defn- planning-task-status-label + [task] + (or (some-> (:status task) normalized-text) + (when (string? (normalized-text (:session-id task))) + "Doing") + (when (string? (normalized-text (:block-uuid task))) + "Todo") + "Draft")) + (defn- session-messages-need-sync? [session-messages ui-messages] (let [ui-by-id (into {} (map (juxt :id identity) ui-messages))] @@ -569,7 +618,9 @@ [block] (let [block-uuid (:block/uuid block) [sessions] (hooks/use-atom (:agent/sessions @state/state)) - session (get sessions (str block-uuid)) + [planning-sessions] (hooks/use-atom (:agent/planning-sessions @state/state)) + session (or (get planning-sessions (str block-uuid)) + (get sessions (str block-uuid))) session-id (or (:session-id session) (agent-handler/task-session-id block) (some-> block-uuid str)) @@ -579,12 +630,25 @@ pr-created? (string? task-pr-url) agent-value (:logseq.property/agent block) agent-label (agent-title agent-value) + session-kind (some-> (:session-kind session) normalized-text) + planning-chat-path (some-> (:planning-chat-path session) normalized-text) + planning-session? (or (= "planning" session-kind) + (string? planning-chat-path)) + planning-session-id (some-> (:planning-session-id session) normalized-text) + execution-session? (not planning-session?) + start-strategy (agent-handler/session-start-strategy block) + message-endpoint (when planning-session? + (or planning-chat-path + (when (string? session-id) + (str "/planning/chat/" session-id)))) session-messages (session->messages session block) transport (hooks/use-memo #(chat-transport/make-transport {:base base :session-id session-id - :open-stream? false}) - [base session-id]) + :open-stream? false + :message-endpoint message-endpoint + :stream-endpoint nil}) + [base session-id message-endpoint]) chat (useChat #js {:id session-id :transport transport :messages (clj->js session-messages)}) @@ -614,10 +678,15 @@ [loading-branches? set-loading-branches?!] (rum/use-state false) [starting-session? set-starting-session?!] (rum/use-state false) [publish-mode set-publish-mode!] (rum/use-state nil) + [creating-tasks? set-creating-tasks?!] (rum/use-state false) + [planning-action-mode set-planning-action-mode!] (rum/use-state nil) + [planning-note set-planning-note!] (rum/use-state "") + [selected-task-uuids set-selected-task-uuids!] (rum/use-state #{}) [terminal-visible? set-terminal-visible!] (rum/use-state false) [terminal-status set-terminal-status!] (rum/use-state :idle) [terminal-error set-terminal-error!] (rum/use-state nil) [terminal-connection-key set-terminal-connection-key!] (rum/use-state 0) + planning-client-ref (hooks/use-ref nil) terminal-container-ref (hooks/use-ref nil) auth-token (state/get-auth-id-token) terminal-url (agent-handler/terminal-websocket-url base @@ -626,6 +695,16 @@ terminal-open-disabled? (or (not session-started?) (not (string? terminal-url))) trimmed-draft (string/trim (or draft "")) + trimmed-planning-note (string/trim (or planning-note "")) + planner-tasks (planning-session-tasks session session-chat-messages) + executable-planner-task-list (executable-planner-tasks planner-tasks) + selectable-task-uuids (->> executable-planner-task-list + (keep (fn [task] + (some-> (:task-uuid task) normalized-text))) + set) + selected-planner-task-uuids (if (empty? selectable-task-uuids) + #{} + (set/intersection selected-task-uuids selectable-task-uuids)) selected-start-branch (normalized-text start-branch) branch-select-items (vec (map (fn [branch] {:value branch @@ -639,6 +718,20 @@ (not (string? selected-start-branch)) (not (agent-handler/task-ready? block))) publish-disabled? (or input-disabled? busy? publish-busy?) + create-tasks-disabled? (or creating-tasks? busy? (empty? planner-tasks)) + planning-action-busy? (some? planning-action-mode) + approve-disabled? (or planning-action-busy? + (not planning-session?) + (= "approved" (:approval-status session))) + reject-disabled? (or planning-action-busy? + (not planning-session?) + (= "rejected" (:approval-status session))) + replan-disabled? (or planning-action-busy? + (not planning-session?)) + execute-selected-disabled? (or planning-action-busy? + creating-tasks? + (empty? planner-tasks) + (empty? selected-planner-task-uuids)) can-send? (and (not input-disabled?) (not (string/blank? trimmed-draft)) (not busy?)) @@ -646,13 +739,20 @@ send-message! (fn [] (when (and can-send? base session-id) (set-draft! "") - (-> (.sendMessage chat #js {:text trimmed-draft}) - (.catch (fn [_] nil))))) + (if planning-session? + (do + (agent-handler/append-planning-message! block-uuid "user" trimmed-draft) + (when-let [client (hooks/deref planning-client-ref)] + (.send client + (js/JSON.stringify + (clj->js {:content trimmed-draft}))))) + (-> (.sendMessage chat #js {:text trimmed-draft}) + (.catch (fn [_] nil)))))) start-session! (fn [] (when (and base session-id (not start-session-disabled?)) (set-starting-session?! true) (let [opts {:base-branch selected-start-branch}] - (-> (agent-handler/ (agent-handler/ (agent-handler/ (agent-handler/ {} + (string? planning-session-id) + (assoc :planning-session-id planning-session-id))) + (p/then (fn [results] + (notification/show! (str "Synced " (count results) " task" + (when (not= 1 (count results)) "s") + ".") + :success false))) + (p/catch (fn [error] + (notification/show! (or (some-> error ex-message) + "Failed to create tasks.") + :error false) + nil)) + (p/finally (fn [] (set-creating-tasks?! false)))))) + planning-action! (fn [mode f success-message error-message] + (set-planning-action-mode! mode) + (-> (f) + (p/then (fn [result] + (when (string? success-message) + (notification/show! success-message :success false)) + result)) + (p/catch (fn [error] + (notification/show! (or (some-> error ex-message) + error-message) + :error false) + nil)) + (p/finally (fn [] (set-planning-action-mode! nil))))) + maybe-send-planning-note! (fn [] + (when (and planning-session? + (not (string/blank? trimmed-planning-note))) + (agent-handler/append-planning-message! block-uuid "user" trimmed-planning-note) + (when-let [client (hooks/deref planning-client-ref)] + (.send client + (js/JSON.stringify + (clj->js {:content trimmed-planning-note})))))) + approve-plan! (fn [] + (planning-action! :approve + (fn [] + (agent-handler/js {:agent "PlanningSessionAgent" + :basePath (str "planning/chat/" planning-session-id) + :host base + :query {:token auth-token} + :onMessage (fn [event] + (when-let [raw (some-> event .-data)] + (when-let [message (parse-json-safe raw)] + (case (:type message) + "planning.state" + (agent-handler/replace-planning-messages! block-uuid + (:state message)) + + "planning.message" + (agent-handler/replace-planning-messages! block-uuid + (:state message)) + + nil))))}))] + (hooks/set-ref! planning-client-ref client))) + (fn [] + (when-let [client (hooks/deref planning-client-ref)] + (.close client)) + (hooks/set-ref! planning-client-ref nil))) + [planning-session? planning-session-id auth-token base block-uuid]) + (hooks/use-effect! + (fn [] + (when planning-session? + (set-selected-task-uuids! selectable-task-uuids)) + nil) + [planning-session? + (pr-str (mapv (fn [task] + (select-keys task [:task-uuid :block-uuid :session-id :status])) + planner-tasks))]) (hooks/use-effect! (fn [] (let [alive? (atom true) @@ -760,10 +978,10 @@ [session-id (pr-str session-chat-messages) (pr-str chat-messages)]) (hooks/use-effect! (fn [] - (when (and base session-id session-started?) + (when (and base session-id session-started? (not planning-session?)) (agent-handler/ [:div.flex.flex-wrap.items-center.gap-2 [:div {:class "inline-flex w-fit items-center gap-1 rounded-lg border border-border bg-muted/40 p-1"} @@ -988,13 +1209,67 @@ {:size :sm :variant :outline :class "h-7 px-2 text-xs" - :disabled publish-disabled? + :disabled create-tasks-disabled? :on-click (fn [_] - (publish! false))} - (if (= publish-mode :push) - "Pushing..." - "Push")) - (when-not pr-created? + (create-tasks!))} + (if creating-tasks? + "Syncing tasks..." + (if planning-session? "Sync tasks" "Create tasks"))) + (when planning-session? + (shui/button + {:size :sm + :variant :outline + :class "h-7 px-2 text-xs" + :disabled execute-selected-disabled? + :on-click (fn [_] + (execute-selected!))} + (if (= planning-action-mode :execute) + "Starting..." + "Execute selected"))) + (when planning-session? + (shui/button + {:size :sm + :variant :outline + :class "h-7 px-2 text-xs" + :disabled reject-disabled? + :on-click (fn [_] + (reject-plan!))} + (if (= planning-action-mode :reject) + "Rejecting..." + "Reject"))) + (when planning-session? + (shui/button + {:size :sm + :variant :outline + :class "h-7 px-2 text-xs" + :disabled replan-disabled? + :on-click (fn [_] + (replan!))} + (if (= planning-action-mode :replan) + "Replanning..." + "Replan"))) + (when planning-session? + (shui/button + {:size :sm + :class "h-7 px-2 text-xs" + :disabled approve-disabled? + :on-click (fn [_] + (approve-plan!))} + (if (= planning-action-mode :approve) + "Approving..." + "Approve"))) + (when execution-session? + (shui/button + {:size :sm + :variant :outline + :class "h-7 px-2 text-xs" + :disabled publish-disabled? + :on-click (fn [_] + (publish! false))} + (if (= publish-mode :push) + "Pushing..." + "Push"))) + (when (and execution-session? (not pr-created?)) (shui/button {:size :sm :class "h-7 px-2 text-xs" @@ -1004,6 +1279,72 @@ (if (= publish-mode :pr) "Creating PR..." "Push + PR")))] + (when planning-session? + [:div {:class "rounded-xl border border-border/70 bg-muted/20 p-3"} + [:div.flex.flex-col.gap-3 + [:div.flex.flex-wrap.items-center.justify-between.gap-2 + [:div.text-xs.opacity-70 + (str "Approval: " (or (:approval-status session) "pending") + " | Tasks: " (count planner-tasks) + " | Selected: " (count selected-planner-task-uuids))] + (when (seq selectable-task-uuids) + [:div.flex.items-center.gap-2.text-xs + (shui/button + {:size :sm + :variant :ghost + :class "h-7 px-2 text-xs" + :on-click (fn [_] + (set-selected-task-uuids! selectable-task-uuids))} + "Select all") + (shui/button + {:size :sm + :variant :ghost + :class "h-7 px-2 text-xs" + :on-click (fn [_] + (set-selected-task-uuids! #{}))} + "Clear")])] + [:textarea.w-full.rounded-lg.border.border-border.bg-background.p-2.text-sm.outline-none + {:rows 3 + :value planning-note + :placeholder "Add approval notes or replanning feedback..." + :on-change (fn [e] + (set-planning-note! (.. e -target -value)))}] + (if (seq planner-tasks) + [:div.flex.flex-col.gap-2 + (for [task planner-tasks + :let [task-uuid (some-> (:task-uuid task) normalized-text) + selectable? (and (string? task-uuid) + (contains? selectable-task-uuids task-uuid)) + checked? (and selectable? + (contains? selected-planner-task-uuids task-uuid))]] + [:label {:key (or task-uuid (str "planned-task-" (random-uuid))) + :class "flex items-start gap-3 rounded-lg border border-border/60 bg-background/70 px-3 py-2"} + [:input.mt-1 + {:type "checkbox" + :disabled (not selectable?) + :checked (boolean checked?) + :on-change (fn [e] + (let [checked (.. e -target -checked)] + (set-selected-task-uuids! + (if checked + (conj selected-planner-task-uuids task-uuid) + (disj selected-planner-task-uuids task-uuid)))))}] + [:div.min-w-0.flex-1 + [:div.flex.flex-wrap.items-center.gap-2 + [:div.font-medium (planning-task-title task)] + [:div {:class "rounded-full bg-muted px-2 py-0.5 text-[10px] uppercase tracking-wide opacity-70"} + (planning-task-status-label task)]] + (when-let [description (some-> (:description task) normalized-text)] + [:div.mt-1.text-xs.opacity-80 description]) + [:div {:class "mt-1 flex flex-wrap items-center gap-2 text-[10px] opacity-60"} + (when-let [task-uuid task-uuid] + [:span (str "task " task-uuid)]) + (when-let [block-id (some-> (:block-uuid task) str normalized-text)] + [:span (str "block " block-id)]) + (when-let [task-session-id (some-> (:session-id task) normalized-text)] + [:span (str "session " task-session-id)])]]])] + [:div.text-xs.opacity-70 + "Planning tasks will appear here once the workflow produces a persisted plan."])]]) (when (string? error) [:div {:class "mt-0.5 rounded-lg border border-red-300/40 bg-red-500/5 px-3 py-1.5 text-xs text-red-500"} error]) diff --git a/src/main/frontend/config.cljs b/src/main/frontend/config.cljs index 52142340c4..ac32d201c2 100644 --- a/src/main/frontend/config.cljs +++ b/src/main/frontend/config.cljs @@ -71,6 +71,9 @@ (goog-define ENABLE-PLUGINS true) (defonce feature-plugin-system-on? ENABLE-PLUGINS) +(goog-define ENABLE-AGENT-PLANNING false) +(defonce feature-agent-planning-on? ENABLE-AGENT-PLANNING) + ;; Desktop only as other platforms requires better understanding of their ;; multi-graph workflows and optimal place for a "global" dir (def global-config-enabled? util/electron?) diff --git a/src/main/frontend/handler/agent.cljs b/src/main/frontend/handler/agent.cljs index c9911bff36..da4deefad5 100644 --- a/src/main/frontend/handler/agent.cljs +++ b/src/main/frontend/handler/agent.cljs @@ -2,6 +2,7 @@ "Agent sessions for tasks." (:require [clojure.string :as string] [electron.ipc :as electron-ipc] + [frontend.config :as config] [frontend.db :as db] [frontend.handler.agent-cancel :as agent-cancel] [frontend.handler.db-based.sync :as db-sync] @@ -39,7 +40,25 @@ (let [value (string/trim value)] (when-not (string/blank? value) value)))) -(declare nil (:block/title agent-page))] (cond-> {} (string? provider) (assoc :provider provider)))) @@ -138,7 +157,10 @@ (task-sandbox-checkpoint project-page))) agent-page (:logseq.property/agent block) project (when project-page (project-config project-page opts)) - agent (when agent-page (agent-config agent-page))] + agent (when agent-page + (cond-> (agent-config agent-page opts) + (string? (blank->nil (:agent/mode opts))) (assoc :mode (blank->nil (:agent/mode opts))) + (string? (blank->nil (:agent/permission-mode opts))) (assoc :permission-mode (blank->nil (:agent/permission-mode opts)))))] {:block-uuid block-uuid :node-id node-id :node-title node-title @@ -168,6 +190,50 @@ (and (map? agent) (codex-agent? agent)))) +(defn planning-enabled? + [] + (or config/dev? + config/feature-agent-planning-on? + (user-handler/alpha-or-beta-user?))) + +(def ^:private planning-intent-pattern + #"(?i)\b(plan|planning|roadmap|break down|decompose|architecture|research|investigate|clarify|phases?|milestones?|workstreams?)\b") + +(def ^:private execution-intent-pattern + #"(?i)\b(implement|build|create|fix|refactor|update|change|add|remove|debug|repair|ship)\b") + +(defn session-start-strategy + [block] + (let [{:keys [content]} (task-context block) + content (blank->nil content) + lines (if (string? content) + (count (string/split-lines content)) + 0) + content-length (count (or content "")) + planning-signals (cond-> 0 + (and (string? content) + (re-find planning-intent-pattern content)) inc + (> lines 6) inc + (> content-length 260) inc + (and (string? content) + (>= (count (re-seq #"\band\b|," content)) 3)) inc) + _ (prn :debug :planning-signals planning-signals + :content content) + execution-signal? (and (string? content) + (re-find execution-intent-pattern content)) + planning? (and (planning-enabled?) + (or (>= planning-signals 2) + (and (>= planning-signals 1) + (not execution-signal?))))] + {:mode (if planning? :planning :execution) + :planning? planning? + :reason (cond + planning? + "Task looks broad enough to benefit from planning first." + + :else + "Task looks concrete enough to execute directly.")})) + (defn- login-required-error? [error] (let [status (:status (ex-data error)) @@ -316,7 +382,8 @@ (build-session-body block nil)) ([block opts] (let [{:keys [block-uuid node-id node-title content attachments sandbox-checkpoint project agent]} (task-context block opts) - session-id (some-> block-uuid str)] + session-id (or (some-> (:session-id opts) blank->nil) + (some-> block-uuid str))] (when (and session-id node-id (string? node-title) (string? content) (map? project) (map? agent)) (cond-> {:session-id session-id :node-id node-id @@ -329,6 +396,335 @@ :pr-enabled true}} (map? sandbox-checkpoint) (assoc :sandbox-checkpoint sandbox-checkpoint)))))) +(def ^:private planner-default-status :logseq.property/status.todo) + +(defn- parse-block-uuid + [value] + (cond + (uuid? value) value + (string? value) + (try + (uuid value) + (catch :default _ nil)) + :else nil)) + +(defn- planner-task-title + [{:keys [title content description]}] + (or (blank->nil title) + (some-> (or (blank->nil content) + (blank->nil description)) + (string/split #"\n") + first + blank->nil))) + +(defn- planner-task-content + [{:keys [content title description]}] + (or (blank->nil content) + (let [title (blank->nil title) + description (blank->nil description)] + (cond + (and title description) (str title "\n" description) + title title + description description + :else nil)))) + +(defn- unwrap-json-code-fence + [text] + (when-let [text (blank->nil text)] + (or (some->> text + (re-find #"(?is)```(?:json)?\s*\n(.*?)\n```") + second + blank->nil) + text))) + +(defn- parse-json-safe + [text] + (when-let [text (unwrap-json-code-fence text)] + (try + (js->clj (js/JSON.parse text) :keywordize-keys true) + (catch :default _ nil)))) + +(defn planner-tasks-from-text + [text] + (let [parsed (parse-json-safe text) + tasks (cond + (vector? parsed) parsed + (sequential? (:tasks parsed)) (vec (:tasks parsed)) + :else nil)] + (when (seq tasks) + (->> tasks + (keep (fn [task] + (when (map? task) + (let [task' (cond-> {} + (parse-block-uuid (:block-uuid task)) (assoc :block-uuid (parse-block-uuid (:block-uuid task))) + (string? (blank->nil (:title task))) (assoc :title (blank->nil (:title task))) + (string? (blank->nil (:description task))) (assoc :description (blank->nil (:description task))) + (string? (blank->nil (:content task))) (assoc :content (blank->nil (:content task))))] + (when (planner-task-content task') + task'))))) + vec + seq + vec)))) + +(defn- planning-summary-task + [task] + (when (map? task) + (let [title (blank->nil (:title task)) + description (blank->nil (:description task)) + content (blank->nil (:content task)) + task-uuid (blank->nil (:task-uuid task)) + block-uuid (blank->nil (:block-uuid task)) + session-id (blank->nil (:session-id task))] + (cond-> {} + (string? title) (assoc :title title) + (string? description) (assoc :description description) + (string? content) (assoc :content content) + (string? task-uuid) (assoc :task-uuid task-uuid) + (string? block-uuid) (assoc :block-uuid block-uuid) + (string? session-id) (assoc :session-id session-id))))) + +(defn- planning-session-summary + [planning-session] + (let [tasks (or (some-> planning-session :plan :tasks) + []) + tasks (->> tasks + (keep planning-summary-task) + vec) + status (blank->nil (:status planning-session)) + approval-status (blank->nil (:approval-status planning-session)) + workflow-id (blank->nil (:workflow-id planning-session)) + scheduled-actions (or (:scheduled-actions planning-session) []) + header-lines (cond-> [] + (string? status) (conj (str "Planning status: " status ".")) + (string? approval-status) (conj (str "Approval status: " approval-status ".")) + (string? workflow-id) (conj (str "Workflow id: " workflow-id ".")) + (seq scheduled-actions) (conj (str "Scheduled actions: " + (pr-str scheduled-actions) + ".")))] + (when (or (seq tasks) (seq header-lines)) + (str (string/join "\n" header-lines) + (when (seq header-lines) "\n\n") + "```json\n" + (js/JSON.stringify (clj->js {:tasks tasks}) nil 2) + "\n```")))) + +(defn- planning-summary-messages + [planning-session] + (when-let [summary (planning-session-summary planning-session)] + [{:id (str "planning-summary-" (or (:planning-session-id planning-session) + (random-uuid))) + :role "assistant" + :parts [{:type "text" + :text summary}]}])) + +(defn- planning-chat-message + [message] + (let [content (blank->nil (:content message)) + role (blank->nil (:role message)) + message-id (blank->nil (:id message))] + (when (and (string? content) + (string? role)) + {:id (or message-id (str "planning-message-" (random-uuid))) + :role role + :parts [{:type "text" + :text content}]}))) + +(defn- planning-chat-messages + [planning-state] + (->> (:messages planning-state) + (keep planning-chat-message) + vec)) + +(defn- planning-summary-message? + [message] + (string/starts-with? (or (:id message) "") "planning-summary-")) + +(defn- merge-planning-summary-messages + [block-uuid planning-session] + (let [existing-messages (or (get-in (session-state block-uuid) [:planning-messages]) []) + non-summary (remove planning-summary-message? existing-messages)] + (into (or (planning-summary-messages planning-session) []) + non-summary))) + +(defn- planning-session-state-update + [block-uuid planning-session] + (let [planning-session-id (or (:planning-session-id planning-session) + (:session-id planning-session)) + session-id (blank->nil planning-session-id)] + {:session-id session-id + :session-kind "planning" + :planning-session-id session-id + :planning-chat-path (or (blank->nil (:chat-path planning-session)) + (when (string? session-id) + (str "/planning/chat/" session-id))) + :workflow-id (:workflow-id planning-session) + :status (:status planning-session) + :plan (:plan planning-session) + :dispatch-sessions (:dispatch-sessions planning-session) + :scheduled-actions (:scheduled-actions planning-session) + :approval-status (:approval-status planning-session) + :require-approval (true? (:require-approval planning-session)) + :auto-dispatch (if (boolean? (:auto-dispatch planning-session)) + (:auto-dispatch planning-session) + true) + :auto-replan (true? (:auto-replan planning-session)) + :replan-delay-sec (:replan-delay-sec planning-session) + :planning-messages (merge-planning-summary-messages block-uuid planning-session) + :planning-loaded? true + :planning-loading? false})) + +(defn- enrich-planning-task-state + [task] + (if-let [block (some-> (:block-uuid task) parse-uuid-safe (vector :block/uuid) db/entity)] + (cond-> task + true (assoc :status (or (pu/get-block-property-value block :logseq.property/status) + (:status task))) + (string? (task-session-id block)) (assoc :session-id (task-session-id block)) + (string? (task-pr-url block)) (assoc :pr-url (task-pr-url block))) + task)) + +(defn- apply-planning-session! + [block-uuid planning-session] + (when (map? planning-session) + (let [planning-session (update-in planning-session [:plan :tasks] + (fn [tasks] + (if (sequential? tasks) + (mapv enrich-planning-task-state tasks) + tasks)))] + (doseq [task (get-in planning-session [:plan :tasks])] + (when (and (:block-uuid task) (:session-id task)) + (maybe-store-task-session-id! (:block-uuid task) (:session-id task)))) + (update-session-state! block-uuid + (planning-session-state-update block-uuid planning-session))) + planning-session)) + +(defn replace-planning-messages! + [block-uuid planning-state] + (let [existing-summary (->> (get-in (session-state block-uuid) [:planning-messages]) + (filter #(string/starts-with? (or (:id %) "") "planning-summary-")) + vec) + messages (into existing-summary (planning-chat-messages planning-state))] + (update-session-state! block-uuid {:planning-messages messages + :planning-agent-state planning-state}))) + +(defn append-planning-message! + [block-uuid role content] + (when-let [content (blank->nil content)] + (update-session! block-uuid + (fn [session] + (let [messages (vec (or (:planning-messages session) []))] + (assoc session + :planning-messages + (conj messages {:id (str "planning-local-" (random-uuid)) + :role role + :parts [{:type "text" + :text content}]})))) + :agent/planning-sessions))) + +(defn- planner-goal-context + [goal-block {:keys [project agent] :as _opts}] + {:project (or project (:logseq.property/project goal-block)) + :agent (or agent (:logseq.property/agent goal-block))}) + +(defn- task-tagged? + [block class-ident] + (boolean + (some #(= class-ident (:db/ident %)) + (:block/tags block)))) + +(defn- maybe-set-planner-task-defaults! + [block-uuid {:keys [project agent]}] + (when-let [block (db/entity [:block/uuid block-uuid])] + (when-not (task-tagged? block :logseq.class/Task) + (property-handler/set-block-property! block-uuid :block/tags :logseq.class/Task)) + (when-not (pu/get-block-property-value block :logseq.property/status) + (property-handler/set-block-property! block-uuid :logseq.property/status planner-default-status)) + (when (and project + (not= (:db/id (:logseq.property/project block)) + (:db/id project))) + (property-handler/set-block-property! block-uuid :logseq.property/project (:block/title project))) + (when (and agent + (not= (:db/id (:logseq.property/agent block)) + (:db/id agent))) + (property-handler/set-block-property! block-uuid :logseq.property/agent (:block/title agent))))) + +(defn- maybe-update-planner-task-content! + [block task] + (when-let [content (planner-task-content task)] + (when (not= (:block/title block) content) + (editor-handler/save-block! (state/get-current-repo) (:block/uuid block) content)))) + +(defn- result :blocks first :block/uuid)] + (when block-uuid + (maybe-set-planner-task-defaults! block-uuid context) + {:block-uuid block-uuid}))))) + +(defn- > tasks + (keep (fn [task] + (let [task-uuid (blank->nil (:task-uuid task)) + block-uuid (some-> (:block-uuid task) str blank->nil) + session-id (blank->nil (:session-id task))] + (when (and (string? task-uuid) + (string? block-uuid)) + (cond-> {:task-uuid task-uuid + :block-uuid block-uuid} + (string? session-id) (assoc :session-id session-id)))))) + vec)] + (if-not (and (string? base) + (string? (blank->nil planning-session-id)) + (seq bindings)) + (p/resolved nil) + (db-sync/fetch-json (str base "/planning/sessions/" planning-session-id "/tasks/sync") + {:method "POST" + :headers {"content-type" "application/json"} + :body (js/JSON.stringify (clj->js {:tasks bindings}))} + {:response-schema :planning.sessions/get})))) + +(defn nil (:planning-session-id opts))] + (p/loop [remaining (seq tasks) + acc [] + anchor-block-uuid nil] + (if-let [task (first remaining)] + (p/let [result ( acc result (conj (merge task result))) + (or (:block-uuid result) + anchor-block-uuid))) + (p/let [_ (nil (:session-id task))) + (contains? planning-started-task-statuses + (some-> (:status task) str string/trim not-empty)))) + +(defn nil planning-session-id)] + (if-not (and (string? base) (string? planning-session-id)) + (p/resolved nil) + (p/let [_ (js/Promise. user-handler/task--ensure-id&access-token) + resp (db-sync/fetch-json (str base "/planning/sessions/" planning-session-id) + {:method "GET"} + {:response-schema :planning.sessions/get})] + (apply-planning-session! block-uuid resp))))) + +(defn (session-state block-uuid) :planning-session-id blank->nil) + (some-> (session-state block-uuid) :session-id blank->nil)) + approval-status (some-> approval-status str string/trim string/lower-case not-empty) + approval-comment (some-> (:comment opts) blank->nil)] + (if-not (and (string? base) + (string? planning-session-id) + (contains? #{"pending" "approved" "rejected"} approval-status)) + (p/resolved nil) + (p/let [_ (js/Promise. user-handler/task--ensure-id&access-token) + resp (db-sync/fetch-json (str base "/planning/sessions/" planning-session-id "/approval") + {:method "POST" + :headers {"content-type" "application/json"} + :body (js/JSON.stringify + (clj->js (cond-> {:approval {:decision approval-status}} + (string? approval-comment) (assoc-in [:approval :comment] approval-comment))))} + {:response-schema :planning.sessions/get})] + (apply-planning-session! block-uuid resp)))))) + +(defn (session-state block-uuid) :planning-session-id blank->nil) + (some-> (session-state block-uuid) :session-id blank->nil)) + replan-note (some-> (:replan-note opts) blank->nil)] + (if-not (and (string? base) + (string? planning-session-id)) + (p/resolved nil) + (p/let [_ (js/Promise. user-handler/task--ensure-id&access-token) + resp (db-sync/fetch-json (str base "/planning/sessions/" planning-session-id "/replan") + {:method "POST" + :headers {"content-type" "application/json"} + :body (js/JSON.stringify + (clj->js (cond-> {} + (string? replan-note) (assoc :replan-note replan-note))))} + {:response-schema :planning.sessions/get})] + (apply-planning-session! block-uuid resp)))))) + +(defn- goal-block task-session-id blank->nil) + (some-> goal-block-uuid str blank->nil)) + block-uuid (:block-uuid task) + block (when block-uuid + (db/entity [:block/uuid block-uuid])) + existing-session-id (or (blank->nil (:session-id task)) + (some-> goal-block task-session-id blank->nil) + (some-> block task-session-id blank->nil))] + (cond + (not block) + (p/resolved nil) + + (or existing-session-id + (task-session-created? block) + (planning-task-started? task)) + (let [session-id (or existing-session-id + (task-session-id block))] + (when (string? session-id) + (when goal-block-uuid + (maybe-store-task-session-id! goal-block-uuid session-id)) + (maybe-store-task-session-id! block-uuid session-id)) + (p/resolved (cond-> (select-keys task [:task-uuid :block-uuid]) + (string? session-id) (assoc :session-id session-id)))) + + :else + (p/let [resp ( resp :session-id blank->nil) + (some-> (db/entity [:block/uuid goal-block-uuid]) + task-session-id + blank->nil) + (some-> (db/entity [:block/uuid block-uuid]) + task-session-id + blank->nil))] + (when (string? session-id) + (when goal-block-uuid + (maybe-store-task-session-id! goal-block-uuid session-id)) + (maybe-store-task-session-id! block-uuid session-id)) + (cond-> (select-keys task [:task-uuid :block-uuid]) + (string? session-id) (assoc :session-id session-id)))))) + +(defn nil (:planning-session-id opts)) + selected-task-uuids (set (keep blank->nil (:selected-task-uuids opts)))] + (p/let [persisted-tasks (nil (:task-uuid task))] + [task-uuid task]))) + persisted-tasks) + selected-tasks (->> tasks + (map (fn [task] + (merge task + (get persisted-by-task-uuid + (blank->nil (:task-uuid task)))))) + (filter (fn [task] + (or (empty? selected-task-uuids) + (contains? selected-task-uuids + (blank->nil (:task-uuid task)))))) + vec) + results (p/loop [remaining selected-tasks + acc []] + (if-let [task (first remaining)] + (p/let [result ( acc result (conj result)))) + acc)) + sync-response (property-value checkpoint)))))))) (defn- update-session! - [block-uuid f] - (state/update-state! :agent/sessions - (fn [sessions] - (let [key (session-key block-uuid) - session (get sessions key {})] - (assoc sessions key (f session)))))) + ([block-uuid f] + (update-session! block-uuid f nil)) + ([block-uuid f bucket-key] + (let [bucket-key (or bucket-key :agent/sessions)] + (state/update-state! bucket-key + (fn [sessions] + (let [key (session-key block-uuid) + session (get sessions key {})] + (assoc sessions key (f session)))))))) + +(defn- session-bucket-key + [block-uuid data] + (let [session-kind (some-> (:session-kind data) + blank->nil + string/lower-case) + planning-sessions (state/sub :agent/planning-sessions) + key (session-key block-uuid)] + (cond + (= "planning" session-kind) :agent/planning-sessions + (= "session" session-kind) :agent/sessions + (contains? planning-sessions key) :agent/planning-sessions + :else :agent/sessions))) (defn- update-session-state! [block-uuid data] - (update-session! block-uuid #(merge % data))) + (update-session! block-uuid + #(merge % data) + (session-bucket-key block-uuid data))) (defn- event->status [event] (case (:type event) @@ -556,7 +1114,9 @@ :kind "user"})) (defn- session-state [block-uuid] - (get (state/sub :agent/sessions) (session-key block-uuid))) + (let [key (session-key block-uuid)] + (or (get (state/sub :agent/planning-sessions) key) + (get (state/sub :agent/sessions) key)))) (defn (:session-kind session) + str + string/trim + string/lower-case)] + (or (= "planning" session-kind) + (string? (blank->nil (:planning-chat-path session)))))) + (defn block-uuid str))] @@ -732,47 +1302,81 @@ (p/resolved nil) (:session-id session) - (do - (maybe-store-task-session-id! block-uuid (:session-id session)) - (when-not (:streaming? session) - (-> ( (db-sync/fetch-json (str base "/planning/sessions/" session-id) + {:method "GET"} + {:response-schema :planning.sessions/get}) + (p/then (fn [resp] + (apply-planning-session! block-uuid resp))) + (p/catch (fn [error] + (update-session-state! block-uuid {:planning-loading? false}) + (let [status (:status (ex-data error))] + (when-not (= status 404) + (log/error :agent/ensure-planning-session-failed error))) + nil)))) + (p/resolved session)) + (do + (maybe-store-task-session-id! block-uuid (:session-id session)) + (when-not (:streaming? session) + (-> ( (p/let [resp (db-sync/fetch-json (str base "/sessions/" session-id) - {:method "GET"} - {:response-schema :sessions/get}) - session-id' (or (:session-id resp) session-id) - stream-url (session-stream-url base session-id')] - (update-session-state! block-uuid {:session-id session-id' - :status (:status resp) - :runtime-provider (:runtime-provider resp) - :terminal-enabled (true? (:terminal-enabled resp)) - :stream-url stream-url - :loading? false}) - (maybe-store-task-session-id! block-uuid session-id') - (maybe-update-task-status! block-uuid (:status resp)) - ( (p/let [resp (db-sync/fetch-json (str base "/sessions/" session-id) + {:method "GET"} + {:response-schema :sessions/get}) + session-id' (or (:session-id resp) session-id) + stream-url (session-stream-url base session-id')] + (update-session-state! block-uuid {:session-id session-id' + :status (:status resp) + :runtime-provider (:runtime-provider resp) + :terminal-enabled (true? (:terminal-enabled resp)) + :stream-url stream-url + :loading? false}) + (maybe-store-task-session-id! block-uuid session-id') + (maybe-update-task-status! block-uuid (:status resp)) + ( create-path str string/trim))) (defn (p/let [resp (db-sync/fetch-json (str base "/sessions") + (-> (p/let [resp (db-sync/fetch-json (str base create-path) {:method "POST" :headers {"content-type" "application/json"} :body (js/JSON.stringify (clj->js body))} - {:response-schema :sessions/create}) - session-id (:session-id resp) + {:response-schema response-schema}) + session-id (or (:session-id resp) + (:planning-session-id resp)) status (:status resp) - stream-url (:stream-url resp) + stream-url (when-not planning? + (:stream-url resp)) + planning-chat-path (when planning? + (or (blank->nil (:chat-path resp)) + (when (string? session-id) + (str "/planning/chat/" session-id)))) block-uuid (:block/uuid block) - _ (when-let [raw-message (message-body (:content raw-body))] - (let [coerced (coerce-http-request :sessions/message raw-message) - msg-body (if (map? coerced) coerced raw-message)] - (db-sync/fetch-json (str base "/sessions/" session-id "/messages") - {:method "POST" - :headers {"content-type" "application/json"} - :body (js/JSON.stringify (clj->js msg-body))} - {:response-schema :sessions/message})))] + _ (when (and (not planning?) + (string? session-id)) + (when-let [raw-message (message-body (:content raw-body))] + (let [coerced (coerce-http-request :sessions/message raw-message) + msg-body (if (map? coerced) coerced raw-message)] + (db-sync/fetch-json (str base "/sessions/" session-id "/messages") + {:method "POST" + :headers {"content-type" "application/json"} + :body (js/JSON.stringify (clj->js msg-body))} + {:response-schema :sessions/message}))))] (notification/clear! github-install-required-notification-uid) - (update-session-state! block-uuid {:session-id session-id - :status status - :runtime-provider (:runtime-provider resp) - :terminal-enabled (true? (:terminal-enabled resp)) - :stream-url stream-url - :started-at (util/time-ms)}) - (maybe-store-task-session-id! block-uuid session-id) - ( ( ( (or opts {}) + (nil? (:agent/permission-mode opts)) + (assoc :agent/permission-mode "read-only"))] + ( {:create-pr (if (nil? create-pr?) true (true? create-pr?)) diff --git a/src/main/frontend/handler/agent_chat_transport.cljs b/src/main/frontend/handler/agent_chat_transport.cljs index f3147ea1f9..5caae5ce4d 100644 --- a/src/main/frontend/handler/agent_chat_transport.cljs +++ b/src/main/frontend/handler/agent_chat_transport.cljs @@ -126,6 +126,21 @@ [base session-id] (str base "/sessions/" session-id "/messages")) +(defn- endpoint-url + [base endpoint] + (let [endpoint (non-empty-str endpoint)] + (cond + (and (string? endpoint) + (or (string/starts-with? endpoint "http://") + (string/starts-with? endpoint "https://"))) + endpoint + + (and (string? base) (string? endpoint)) + (str base endpoint) + + :else + nil))) + (defn- runtime-error-message [event] (let [data (if (map? (:data event)) (:data event) {})] @@ -573,7 +588,14 @@ (.finally (fn [] nil)))))) (defn- send-messages! - [{:keys [base session-id fetch-fn now-fn idle-timeout-ms open-stream?]} opts] + [{:keys [base + session-id + fetch-fn + now-fn + idle-timeout-ms + open-stream? + message-endpoint + stream-endpoint]} opts] (let [message (last-user-message-text (aget opts "messages"))] (if-not (and (string? base) (string? session-id)) (js/Promise.reject @@ -585,8 +607,10 @@ start-ts (now-fn) abort-signal (aget opts "abortSignal") headers (auth-headers) - post-url (messages-url base session-id) - stream-endpoint (stream-url base session-id)] + post-url (or (endpoint-url base message-endpoint) + (messages-url base session-id)) + stream-url' (or (endpoint-url base stream-endpoint) + (stream-url base session-id))] (-> (p/let [post-resp (fetch-fn post-url #js {:method "POST" :headers headers @@ -598,34 +622,45 @@ (throw (js/Error. (str "send message failed: " (.-status post-resp)))))] (if (false? open-stream?) (chunks->readable-stream [{:type "finish"}]) - (p/let [stream-resp (fetch-fn stream-endpoint - #js {:method "GET" - :headers headers - :signal abort-signal}) - _ (when-not (.-ok stream-resp) - (throw (js/Error. (str "open stream failed: " (.-status stream-resp))))) - _ (when-not (.-body stream-resp) - (throw (js/Error. "stream response has no body")))] - (let [ts (js/TransformStream.) - writer (.getWriter (.-writable ts))] - (start-stream-consumer! {:response stream-resp - :writer writer - :start-ts start-ts - :idle-timeout-ms (or idle-timeout-ms default-idle-timeout-ms) - :abort-signal abort-signal}) - (.-readable ts))))) + (if-not (string? stream-url') + (chunks->readable-stream [{:type "finish"}]) + (p/let [stream-resp (fetch-fn stream-url' + #js {:method "GET" + :headers headers + :signal abort-signal}) + _ (when-not (.-ok stream-resp) + (throw (js/Error. (str "open stream failed: " (.-status stream-resp))))) + _ (when-not (.-body stream-resp) + (throw (js/Error. "stream response has no body")))] + (let [ts (js/TransformStream.) + writer (.getWriter (.-writable ts))] + (start-stream-consumer! {:response stream-resp + :writer writer + :start-ts start-ts + :idle-timeout-ms (or idle-timeout-ms default-idle-timeout-ms) + :abort-signal abort-signal}) + (.-readable ts)))))) (.catch (fn [error] (js/Promise.reject error))))))))) (defn make-transport - [{:keys [base session-id fetch-fn now-fn idle-timeout-ms open-stream?]}] + [{:keys [base + session-id + fetch-fn + now-fn + idle-timeout-ms + open-stream? + message-endpoint + stream-endpoint]}] #js {:sendMessages (fn [opts] (send-messages! {:base base :session-id session-id :fetch-fn fetch-fn :now-fn now-fn :idle-timeout-ms idle-timeout-ms - :open-stream? open-stream?} + :open-stream? open-stream? + :message-endpoint message-endpoint + :stream-endpoint stream-endpoint} opts)) :reconnectToStream (fn [_opts] (js/Promise.resolve nil))}) diff --git a/src/main/frontend/state.cljs b/src/main/frontend/state.cljs index 566a3ed83d..fa61db8b8b 100644 --- a/src/main/frontend/state.cljs +++ b/src/main/frontend/state.cljs @@ -293,6 +293,7 @@ :rtc/asset-upload-download-progress (atom {}) :rtc/users-info (atom {}) :agent/sessions (atom {}) + :agent/planning-sessions (atom {}) :user/info {:UserGroups (storage/get :user-groups)} :encryption/graph-parsing? false diff --git a/src/test/frontend/handler/agent_test.cljs b/src/test/frontend/handler/agent_test.cljs new file mode 100644 index 0000000000..2b7a2854c6 --- /dev/null +++ b/src/test/frontend/handler/agent_test.cljs @@ -0,0 +1,110 @@ +(ns frontend.handler.agent-test + (:require [clojure.string :as string] + [clojure.test :refer [deftest is testing use-fixtures]] + [frontend.db :as db] + [frontend.handler.agent :as agent-handler] + [frontend.handler.db-based.property :as db-property-handler] + [frontend.handler.property.util :as pu] + [frontend.test.helper :as test-helper :include-macros true :refer [deftest-async]] + [promesa.core :as p])) + +(use-fixtures :each {:before test-helper/start-test-db! + :after (fn [] (test-helper/destroy-test-db!))}) + +(defn- setup-goal-context! + [] + (test-helper/load-test-files [{:page {:block/title "Goal Page"} + :blocks [{:block/title "Goal"}]} + {:page {:block/title "Project Alpha"}} + {:page {:block/title "Codex"}}]) + (let [goal (test-helper/find-block-by-content "Goal") + project-page (db/get-page "Project Alpha") + agent-page (db/get-page "Codex")] + (db-property-handler/set-block-property! (:db/id project-page) :block/tags :logseq.class/Project) + (db-property-handler/set-block-property! (:db/id project-page) :logseq.property/git-repo "https://github.com/logseq/logseq") + (db-property-handler/set-block-property! (:db/id agent-page) :block/tags :logseq.class/Agent) + (db-property-handler/set-block-property! (:db/id goal) :logseq.property/project "Project Alpha") + (db-property-handler/set-block-property! (:db/id goal) :logseq.property/agent "Codex") + {:goal goal + :project-page project-page + :agent-page agent-page})) + +(deftest-async persist-planner-tasks-creates-runnable-task-blocks-test + (p/let [{:keys [goal]} (setup-goal-context!) + tasks (agent-handler/ (:logseq.property/project task-block) :block/title))) + (is (= "Codex" (some-> (:logseq.property/agent task-block) :block/title))) + (is (some? (agent-handler/build-session-body task-block))))) + +(deftest-async upsert-planner-tasks-reconciles-existing-task-by-block-uuid-test + (p/let [{:keys [goal]} (setup-goal-context!) + created (agent-handler/ body :agent :provider string/lower-case))) + (is (= "read-only" (get-in body [:agent :permission-mode]))))) + +(deftest planner-tasks-from-text-parses-json-object-and-code-fence-test + (testing "json object with tasks" + (is (= [{:title "Task A" + :description "Desc A"} + {:title "Task B" + :content "Explicit content"}] + (agent-handler/planner-tasks-from-text + "{\"tasks\":[{\"title\":\"Task A\",\"description\":\"Desc A\"},{\"title\":\"Task B\",\"content\":\"Explicit content\"}]}")))) + (testing "fenced json array" + (is (= [{:title "Task C" + :description "Desc C"}] + (agent-handler/planner-tasks-from-text + "```json\n[{\"title\":\"Task C\",\"description\":\"Desc C\"}]\n```")))) + (testing "prose with fenced json block" + (is (= [{:title "Task D" + :description "Desc D"}] + (agent-handler/planner-tasks-from-text + "Here is the plan.\n\n```json\n{\"tasks\":[{\"title\":\"Task D\",\"description\":\"Desc D\"}]}\n```\n\nReview it.")))) + (testing "invalid payload returns nil" + (is (nil? (agent-handler/planner-tasks-from-text "not planner json"))))) diff --git a/yarn.lock b/yarn.lock index 76a0596727..765e468cf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -60,6 +60,15 @@ "@capacitor/core" "^7.4.4" "@capacitor/ios" "^7.4.4" +"@apidevtools/json-schema-ref-parser@^11.5.5": + version "11.9.3" + resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz#0e0c9061fc41cf03737d499a4e6a8299fdd2bfa7" + integrity sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ== + dependencies: + "@jsdevtools/ono" "^7.1.3" + "@types/json-schema" "^7.0.15" + js-yaml "^4.1.0" + "@aw-web-design/x-default-browser@1.4.126": version "1.4.126" resolved "https://registry.yarnpkg.com/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz#43e4bd8f0314ed907a8718d7e862a203af79bc16" @@ -3580,12 +3589,19 @@ pirates "^4.0.6" source-map-support "^0.5.16" +"@babel/runtime-corejs3@^7.26.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.29.0.tgz#56cd28ec515364482afeb880b476936a702461b9" + integrity sha512-TgUkdp71C9pIbBcHudc+gXZnihEDOjUAmXO1VO4HHGES7QLZcShR0stfKIxLSNIYx2fqhmJChOjm/wkF8wv4gA== + dependencies: + core-js-pure "^3.48.0" + "@babel/runtime@^7.10.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.28.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.2.tgz#2ae5a9d51cc583bd1f5673b3bb70d6d819682473" integrity sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA== -"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.17.8", "@babel/runtime@^7.21.0": +"@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.17.8", "@babel/runtime@^7.21.0", "@babel/runtime@^7.26.0": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== @@ -3856,6 +3872,11 @@ resolved "https://registry.yarnpkg.com/@capgo/capacitor-navigation-bar/-/capacitor-navigation-bar-7.1.32.tgz#937902665c1602bc653e9344538cfc3abc525062" integrity sha512-bigqO8GD1qiyoGdMPCDXOmthhjEAokW0P4Aq+1ejWAKzy4tge44r2vThuTV+W75sPWFiNXdt8tB+iSQImN1row== +"@cfworker/json-schema@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6" + integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -4243,6 +4264,11 @@ resolved "https://registry.yarnpkg.com/@highlightjs/cdn-assets/-/cdn-assets-10.4.1.tgz#261bc7a391cd22e86e19ba809a9b997ca37f1514" integrity sha512-dJ6ZNFZ0TIatflV7h+5S6swUrc+hfivRgylKCcUk4+sb2Gi7uj5rSSv3x0p/fwIpyYlrKhxA2LqtdVBZ1Evk5Q== +"@hono/node-server@^1.19.9": + version "1.19.11" + resolved "https://registry.yarnpkg.com/@hono/node-server/-/node-server-1.19.11.tgz#dc419f0826dd2504e9fc86ad289d5636a0444e2f" + integrity sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g== + "@hookform/resolvers@^3.3.2": version "3.10.0" resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.10.0.tgz#7bfd18113daca4e57e27e1205b7d5a2d371aa59a" @@ -4732,6 +4758,11 @@ resolved "https://registry.yarnpkg.com/@js-joda/timezone/-/timezone-2.5.0.tgz#b422ff400c25ae311384239c62724eecee2e442b" integrity sha512-HHFVhGUKIOtITiT+sbQRdYuO5Q+a8FDj/vQSGUSxe6+5w2in5JsavfRsAN2tU/NCdBeFx/6q8evHMtOrXfdn2g== +"@jsdevtools/ono@^7.1.3": + version "7.1.3" + resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" + integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== + "@juggle/resize-observer@^3.3.1": version "3.4.0" resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" @@ -4962,6 +4993,29 @@ "@lezer/lr" "^1.0.0" json5 "^2.2.1" +"@modelcontextprotocol/sdk@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz#5b35d73062125f126cc70b0be83cbab53bcdde74" + integrity sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg== + dependencies: + "@hono/node-server" "^1.19.9" + ajv "^8.17.1" + ajv-formats "^3.0.1" + content-type "^1.0.5" + cors "^2.8.5" + cross-spawn "^7.0.5" + eventsource "^3.0.2" + eventsource-parser "^3.0.0" + express "^5.2.1" + express-rate-limit "^8.2.1" + hono "^4.11.4" + jose "^6.1.3" + json-schema-typed "^8.0.2" + pkce-challenge "^5.0.0" + raw-body "^3.0.0" + zod "^3.25 || ^4.0" + zod-to-json-schema "^3.25.1" + "@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11" @@ -9162,6 +9216,11 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.23.tgz#c1bb06db218acc8fc232da0447473fc2fb9d9841" integrity sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA== +"@types/lodash@^4.17.7": + version "4.17.24" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" + integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== + "@types/mapbox-gl@^2.6.0": version "2.7.21" resolved "https://registry.yarnpkg.com/@types/mapbox-gl/-/mapbox-gl-2.7.21.tgz#b0cad1e4c3d1bf1592444de36a4f27e890310416" @@ -9654,6 +9713,14 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + accepts@~1.3.4, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -9716,6 +9783,22 @@ agent-base@6: dependencies: debug "4" +agents@^0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/agents/-/agents-0.7.5.tgz#c518376c4bc96db9e133b9d59a24678b9b1585e0" + integrity sha512-sB37uMLt4aenYJwhbCSnIaYZuepoJwXYTX/8WXJGFj/FuWoScHrY+o2LrxKgzSmDA4pMMn4JIXaq/wMHs8OAJg== + dependencies: + "@cfworker/json-schema" "^4.1.1" + "@modelcontextprotocol/sdk" "1.26.0" + cron-schedule "^6.0.0" + json-schema "^0.4.0" + json-schema-to-typescript "^15.0.4" + mimetext "^3.0.28" + nanoid "^5.1.6" + partyserver "^0.3.3" + partysocket "1.1.16" + yargs "^18.0.0" + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -9741,6 +9824,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -9773,6 +9863,16 @@ ajv@^8.0.0, ajv@^8.0.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +ajv@^8.17.1: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + amazon-cognito-identity-js@6.3.16: version "6.3.16" resolved "https://registry.yarnpkg.com/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.16.tgz#4a6aceb64c80406b9ee4344980794ff9e506017a" @@ -9828,6 +9928,11 @@ ansi-regex@^6.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== +ansi-regex@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -9852,6 +9957,11 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== +ansi-styles@^6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" @@ -10502,6 +10612,21 @@ body-parser@^1.19.0: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.2.tgz#1a32cdb966beaf68de50a9dfbe5b58f83cb8890c" + integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.3" + http-errors "^2.0.0" + iconv-lite "^0.7.0" + on-finished "^2.4.1" + qs "^6.14.1" + raw-body "^3.0.1" + type-is "^2.0.1" + body-parser@~1.20.3: version "1.20.4" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f" @@ -10751,7 +10876,7 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== -bytes@3.1.2, bytes@~3.1.2: +bytes@3.1.2, bytes@^3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -11165,6 +11290,15 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +cliui@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-9.0.1.tgz#6f7890f386f6f1f79953adc1f78dec46fcc2d291" + integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w== + dependencies: + string-width "^7.2.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -11478,6 +11612,11 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== +content-disposition@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.1.tgz#a8b7bbeb2904befdfb6787e5c0c086959f605f9b" + integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== + content-disposition@~0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -11485,7 +11624,7 @@ content-disposition@~0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4, content-type@~1.0.5: +content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -11644,12 +11783,17 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie-signature@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454" integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA== -cookie@^0.7.2, cookie@~0.7.1, cookie@~0.7.2: +cookie@^0.7.1, cookie@^0.7.2, cookie@~0.7.1, cookie@~0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== @@ -11674,7 +11818,7 @@ core-js-compat@^3.48.0: dependencies: browserslist "^4.28.1" -core-js-pure@^3.23.3: +core-js-pure@^3.23.3, core-js-pure@^3.48.0: version "3.48.0" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.48.0.tgz#7d5a3fe1ec3631b9aa76a81c843ac2ce918e5023" integrity sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw== @@ -11684,6 +11828,14 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== +cors@^2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -11764,6 +11916,11 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +cron-schedule@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cron-schedule/-/cron-schedule-6.0.0.tgz#62f4fa6a980c2c14f322deaaaeee4c27f9f0c84f" + integrity sha512-BoZaseYGXOo5j5HUwTaegIog3JJbuH4BbrY9A1ArLjXpy+RWb3mV28F/9Gv1dDA7E2L8kngWva4NWisnLTyfgQ== + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -11782,7 +11939,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1, cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.1, cross-spawn@^7.0.3, cross-spawn@^7.0.5, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -12255,7 +12412,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@2.0.0, depd@~2.0.0: +depd@2.0.0, depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -12615,6 +12772,11 @@ emoji-mart@^5.5.2: resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.6.0.tgz#71b3ed0091d3e8c68487b240d9d6d9a73c27f023" integrity sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow== +emoji-regex@^10.3.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -12635,16 +12797,16 @@ encode-utf8@^1.0.3: resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== +encodeurl@^2.0.0, encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.5" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c" @@ -12996,7 +13158,7 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== @@ -13082,7 +13244,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: +etag@^1.8.1, etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== @@ -13095,6 +13257,11 @@ event-emitter@^0.3.5: d "1" es5-ext "~0.10.14" +event-target-polyfill@^0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/event-target-polyfill/-/event-target-polyfill-0.0.4.tgz#060ee66e85aaedc76b6fa66079782dcc11cba496" + integrity sha512-Gs6RLjzlLRdT8X9ZipJdIZI/Y6/HhRLyq9RdDlCsnpxr/+Nn6bU2EFGuC94GjxqhM+Nmij2Vcq98yoHrU8uNFQ== + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -13110,11 +13277,18 @@ events@^3.0.0, events@^3.1.0, events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -eventsource-parser@^3.0.6: +eventsource-parser@^3.0.0, eventsource-parser@^3.0.1, eventsource-parser@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/eventsource-parser/-/eventsource-parser-3.0.6.tgz#292e165e34cacbc936c3c92719ef326d4aeb4e90" integrity sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg== +eventsource@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-3.0.7.tgz#1157622e2f5377bb6aef2114372728ba0c156989" + integrity sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA== + dependencies: + eventsource-parser "^3.0.1" + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" @@ -13183,6 +13357,13 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" +express-rate-limit@^8.2.1: + version "8.3.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-8.3.1.tgz#0aaba098eadd40f6737f30a98e6b16fa1a29edfb" + integrity sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw== + dependencies: + ip-address "10.1.0" + express@^4.17.3: version "4.22.1" resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069" @@ -13220,6 +13401,40 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" +express@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/express/-/express-5.2.1.tgz#8f21d15b6d327f92b4794ecf8cb08a72f956ac04" + integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.1" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + depd "^2.0.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + ext@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -13384,6 +13599,11 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + fetch-retry@^5.0.2: version "5.0.6" resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.6.tgz#17d0bc90423405b7a88b74355bf364acd2a7fa56" @@ -13456,6 +13676,18 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.1.tgz#a2c517a6559852bcdb06d1f8bd7f51b68fad8099" + integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + finalhandler@~1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88" @@ -13712,6 +13944,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fresh@~0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -13908,6 +14145,11 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-east-asian-width@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" + integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + get-intrinsic@^1.1.3, get-intrinsic@^1.2.2, get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" @@ -14698,6 +14940,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +hono@^4.11.4: + version "4.12.6" + resolved "https://registry.yarnpkg.com/hono/-/hono-4.12.6.tgz#ce8c4eae2f73a2c96d2148bee44e49227eb0a666" + integrity sha512-KljEp+MeEEEIOT75qBo1UjqqB29fRMtlDEwCxcexOzdkUq6LR/vRvHk5pdROcxyOYyW1niq7Gb5pFVGy5R1eBw== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -14795,7 +15042,7 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~2.0.0, http-errors@~2.0.1: +http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.0, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== @@ -14855,6 +15102,13 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.7.0, iconv-lite@~0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -15000,6 +15254,11 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip-address@10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4" + integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q== + ip@1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" @@ -15385,6 +15644,11 @@ is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-regex@^1.1.4, is-regex@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" @@ -15688,6 +15952,16 @@ jiti@^1.19.1, jiti@^1.20.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== +jose@^6.1.3: + version "6.2.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.1.tgz#7a6b1de83816deaee9055a558e1278a7b2b9ea1b" + integrity sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw== + +js-base64@^3.7.7: + version "3.7.8" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.8.tgz#af44496bc09fa178ed9c4adf67eb2b46f5c6d2a4" + integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow== + js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" @@ -15764,6 +16038,21 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-schema-to-typescript@^15.0.4: + version "15.0.4" + resolved "https://registry.yarnpkg.com/json-schema-to-typescript/-/json-schema-to-typescript-15.0.4.tgz#a530c7f17312503b262ae12233749732171840f3" + integrity sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ== + dependencies: + "@apidevtools/json-schema-ref-parser" "^11.5.5" + "@types/json-schema" "^7.0.15" + "@types/lodash" "^4.17.7" + is-glob "^4.0.3" + js-yaml "^4.1.0" + lodash "^4.17.21" + minimist "^1.2.8" + prettier "^3.2.5" + tinyglobby "^0.2.9" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -15774,6 +16063,11 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== +json-schema-typed@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-8.0.2.tgz#e98ee7b1899ff4a184534d1f167c288c66bbeff4" + integrity sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA== + json-schema@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -16784,6 +17078,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + mem@^4.0.0: version "4.3.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" @@ -16852,6 +17151,11 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -17197,7 +17501,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -"mime-db@>= 1.43.0 < 2": +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: version "1.54.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== @@ -17209,6 +17513,13 @@ mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.27, mime-types@^2.1.31, dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.2.tgz#39002d4182575d5af036ffa118100f2524b2e2ab" + integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -17219,6 +17530,16 @@ mime@^2.0.3, mime@^2.5.2: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mimetext@^3.0.28: + version "3.0.28" + resolved "https://registry.yarnpkg.com/mimetext/-/mimetext-3.0.28.tgz#ffa6292cd13c0913b0783161cd93018203debc09" + integrity sha512-eQXpbNrtxLCjUtiVbR/qR09dbPgZ2o+KR1uA7QKqGhbn8QV7HIL16mXXsobBL4/8TqoYh1us31kfz+dNfCev9g== + dependencies: + "@babel/runtime" "^7.26.0" + "@babel/runtime-corejs3" "^7.26.0" + js-base64 "^3.7.7" + mime-types "^2.1.35" + mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -17312,7 +17633,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -17471,7 +17792,7 @@ nanoid@^3.3.11: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== -nanoid@^5.0.7, nanoid@^5.0.9: +nanoid@^5.0.7, nanoid@^5.0.9, nanoid@^5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.6.tgz#30363f664797e7d40429f6c16946d6bd7a3f26c9" integrity sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg== @@ -17520,6 +17841,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + negotiator@~0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" @@ -17893,7 +18219,7 @@ observable-fns@^0.6.1: resolved "https://registry.yarnpkg.com/observable-fns/-/observable-fns-0.6.1.tgz#636eae4fdd1132e88c0faf38d33658cc79d87e37" integrity sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg== -on-finished@2.4.1, on-finished@~2.4.1: +on-finished@2.4.1, on-finished@^2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -18262,11 +18588,25 @@ parse5@^7.0.0: dependencies: entities "^6.0.0" -parseurl@~1.3.3: +parseurl@^1.3.3, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +partyserver@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/partyserver/-/partyserver-0.3.3.tgz#89d05eb9d25456d2a1f4454ac7f5d67636887762" + integrity sha512-jSGWNZ5lJj9czq+97y9N02HyZZtQH8wmgJRkTQahfGnzgL5+R12dMX3E+p7BscB2cVj6LZLv1uHpKL057rjJmw== + dependencies: + nanoid "^5.1.6" + +partysocket@1.1.16: + version "1.1.16" + resolved "https://registry.yarnpkg.com/partysocket/-/partysocket-1.1.16.tgz#0c8894b8a1f370c5975bce26a1c7ceff36b0aec0" + integrity sha512-d7xFv+ZC7x0p/DAHWJ5FhxQhimIx+ucyZY+kxL0cKddLBmK9c4p2tEA/L+dOOrWm6EYrRwrBjKQV0uSzOY9x1w== + dependencies: + event-target-polyfill "^0.0.4" + pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" @@ -18378,6 +18718,11 @@ path-scurry@^2.0.0: lru-cache "^11.0.0" minipass "^7.1.2" +path-to-regexp@^8.0.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz#aa818a6981f99321003a08987d3cec9c3474cd1f" + integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== + path-to-regexp@~0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" @@ -18599,6 +18944,11 @@ pixi.js@6.2.0: "@pixi/ticker" "6.2.0" "@pixi/utils" "6.2.0" +pkce-challenge@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.1.tgz#3b4446865b17b1745e9ace2016a31f48ddf6230d" + integrity sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ== + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -19184,6 +19534,11 @@ prettier@^2.7.1, prettier@^2.8.0: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.2.5: + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -19276,7 +19631,7 @@ protocol-buffers-schema@^3.3.1: resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -19421,6 +19776,13 @@ qs@^6.12.3: dependencies: side-channel "^1.1.0" +qs@^6.14.0, qs@^6.14.1: + version "6.15.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.0.tgz#db8fd5d1b1d2d6b5b33adaf87429805f1909e7b3" + integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== + dependencies: + side-channel "^1.1.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -19481,6 +19843,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0, raw-body@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.2.tgz#3e3ada5ae5568f9095d84376fd3a49b8fb000a51" + integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.7.0" + unpipe "~1.0.0" + raw-body@~2.5.3: version "2.5.3" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" @@ -20340,6 +20712,17 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -20513,6 +20896,23 @@ send-intent@^7.0.0: dependencies: "@capacitor/cli" "^7.0.0" +send@^1.1.0, send@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.1.tgz#9eab743b874f3550f40a26867bf286ad60d3f3ed" + integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== + dependencies: + debug "^4.4.3" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.1" + mime-types "^3.0.2" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.2" + send@~0.19.0, send@~0.19.1: version "0.19.2" resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29" @@ -20555,6 +20955,16 @@ serialize-javascript@^6.0.2: dependencies: randombytes "^2.1.0" +serve-static@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.1.tgz#7f186a4a4e5f5b663ad7a4294ff1bf37cf0e98a9" + integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + serve-static@~1.16.2: version "1.16.3" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9" @@ -21097,16 +21507,16 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== +statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.1, statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== -statuses@~2.0.1, statuses@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" - integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== - stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" @@ -21253,6 +21663,15 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^7.0.0, string-width@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + string.prototype.padend@^3.0.0: version "3.1.6" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz#ba79cf8992609a91c872daa47c6bb144ee7f62a5" @@ -21352,6 +21771,13 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" +strip-ansi@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -21997,6 +22423,14 @@ tinyexec@^0.3.2: resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.2.tgz#941794e657a85e496577995c6eef66f53f42b3d2" integrity sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA== +tinyglobby@^0.2.9: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + tinyqueue@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" @@ -22242,6 +22676,15 @@ type-fest@^2.19.0, type-fest@~2.19: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== +type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -22832,7 +23275,7 @@ value-or-function@^3.0.0: resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg== -vary@^1, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -23301,6 +23744,15 @@ wrap-ansi@^8.1.0: string-width "^5.0.1" strip-ansi "^7.0.1" +wrap-ansi@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -23461,7 +23913,7 @@ yargs-parser@20.2.4: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== -yargs-parser@>=5.0.0-security.0: +yargs-parser@>=5.0.0-security.0, yargs-parser@^22.0.0: version "22.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8" integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== @@ -23561,6 +24013,18 @@ yargs@^16.1.1, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" +yargs@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-18.0.0.tgz#6c84259806273a746b09f579087b68a3c2d25bd1" + integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg== + dependencies: + cliui "^9.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + string-width "^7.2.0" + y18n "^5.0.5" + yargs-parser "^22.0.0" + yargs@^7.1.0: version "7.1.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.2.tgz#63a0a5d42143879fdbb30370741374e0641d55db" @@ -23638,12 +24102,17 @@ zen-push@0.2.1: dependencies: zen-observable "^0.7.0" +zod-to-json-schema@^3.25.1: + version "3.25.1" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz#7f24962101a439ddade2bf1aeab3c3bfec7d84ba" + integrity sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA== + zod@^3.22.4: version "3.25.76" resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ== -zod@^4.3.6: +"zod@^3.25 || ^4.0", zod@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==