CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
capabilities.js372 linesDownload Raw Back to serve
1/**2 * @license3 * Copyright 2025 Qwen Team4 * SPDX-License-Identifier: Apache-2.05 */6export const SERVE_PROTOCOL_VERSION = 'v1';7export const SUPPORTED_SERVE_PROTOCOL_VERSIONS = [8    SERVE_PROTOCOL_VERSION,9];10export const SERVE_CAPABILITY_REGISTRY = {11    health: { since: 'v1' },12    daemon_status: { since: 'v1' },13    capabilities: { since: 'v1' },14    session_create: { since: 'v1' },15    session_scope_override: { since: 'v1' },16    session_load: { since: 'v1' },17    session_resume: { since: 'v1' },18    // Deprecated alias — kept until @agentclientprotocol/sdk graduates19    // the underlying ACP method from unstable_resumeSession to resumeSession.20    unstable_session_resume: { since: 'v1' },21    session_list: { since: 'v1' },22    session_prompt: { since: 'v1' },23    session_cancel: { since: 'v1' },24    session_events: { since: 'v1' },25    session_artifacts: { since: 'v1' },26    // Daemon emits `slow_client_warning` synthetic frames at 75% queue27    // fill and honors `?maxQueued=N` (range [16, 2048]) on28    // `GET /session/:id/events`. Old daemons silently lack both — SDK29    // clients pre-flight this tag before opting in.30    slow_client_warning: { since: 'v1' },31    // SDK consumers can detect `KnownDaemonEvent` schema support without32    // pinning against this SDK release — `narrowDaemonEvent` falls back33    // to `kind: 'unknown'` for daemons that don't advertise the tag,34    // so the tag is purely informational.35    typed_event_schema: { since: 'v1' },36    session_set_model: { since: 'v1' },37    client_identity: { since: 'v1' },38    client_heartbeat: { since: 'v1' },39    session_permission_vote: { since: 'v1' },40    permission_vote: { since: 'v1' },41    workspace_mcp: { since: 'v1' },42    workspace_skills: { since: 'v1' },43    workspace_providers: { since: 'v1' },44    auth_provider_install: { since: 'v1' },45    // Workspace memory CRUD (`GET/POST /workspace/memory`). Daemon exposes46    // hierarchical QWEN.md state and accepts append/replace writes scoped47    // to either the bound workspace or the global ~/.qwen directory.48    workspace_memory: { since: 'v1' },49    workspace_memory_remember: {50        since: 'v1',51        modes: ['workspace', 'clean'],52    },53    workspace_memory_forget: { since: 'v1' },54    workspace_memory_dream: { since: 'v1' },55    // Workspace agents CRUD (`GET/POST /workspace/agents` +56    // `GET/POST/DELETE /workspace/agents/:agentType`). Wraps57    // `SubagentManager` over HTTP so remote clients can list / read /58    // create / update / delete project- and user-level subagent59    // definitions. Built-in / extension agents stay read-only.60    workspace_agents: { since: 'v1' },61    workspace_agent_generate: { since: 'v1' },62    workspace_env: { since: 'v1' },63    workspace_preflight: { since: 'v1' },64    session_context: { since: 'v1' },65    session_context_usage: { since: 'v1' },66    session_supported_commands: { since: 'v1' },67    session_tasks: { since: 'v1' },68    session_stats: { since: 'v1' },69    session_lsp: { since: 'v1' },70    session_status: { since: 'v1' },71    session_close: { since: 'v1' },72    session_archive: { since: 'v1' },73    session_metadata: { since: 'v1' },74    session_organization: { since: 'v1' },75    session_export: { since: 'v1' },76    // Daemon supports the MCP client guardrail surface: an in-process77    // counter exposed on `GET /workspace/mcp`, a `--mcp-client-budget=N`78    // flag with `--mcp-budget-mode={enforce, warn, off}`, and a79    // `disabledReason: 'budget'` tag on per-server cells when refused at80    // discovery. `modes` enumerates the implemented behaviors.81    mcp_guardrails: { since: 'v1', modes: ['warn', 'enforce'] },82    workspace_mcp_manage: { since: 'v1' },83    // Daemon emits typed push events for MCP budget state crossings:84    // `mcp_budget_warning` and `mcp_child_refused_batch`. Always-on;85    // orthogonal to `mcp_guardrails` (the snapshot surface).86    mcp_guardrail_events: { since: 'v1' },87    // Always-on. Daemon supports runtime MCP server mutation via88    // `POST /workspace/mcp/servers` (add) and89    // `DELETE /workspace/mcp/servers/:name` (remove). SDK clients90    // pre-flight this tag before calling those routes.91    mcp_server_runtime_mutation: { since: 'v1' },92    // Daemon supports the read-only workspace file surface:93    // `GET /file`, `GET /list`, `GET /glob`, `GET /stat`. The four94    // routes are gated as a single feature because they share the same95    // backing `WorkspaceFileSystem` boundary and failure shape.96    workspace_file_read: { since: 'v1' },97    // Daemon supports bounded raw byte reads via `GET /file/bytes`.98    // Separate from `workspace_file_read` because older daemons may99    // advertise the text/list/stat/glob surface without byte-window100    // support.101    workspace_file_bytes: { since: 'v1' },102    // Daemon supports hash-aware text mutation routes103    // (`POST /file/write`, `POST /file/edit`) behind the strict mutation104    // gate. Clients should still pre-flight `require_auth` separately for105    // deployment posture; this tag only means the route contract exists.106    workspace_file_write: { since: 'v1' },107    // Daemon hosts the session-level approval-mode108    // control route `POST /session/:id/approval-mode` (gated by the109    // mutation gate, strict). The route accepts `{mode, persist?}` —110    // `persist:true` also writes `tools.approvalMode` to workspace111    // settings via the daemon's `loadedSettings` handle. SDK helper:112    // `DaemonClient.setSessionApprovalMode`.113    session_approval_mode_control: { since: 'v1' },114    // `POST /workspace/tools/:name/enable` toggles a115    // tool name in the workspace's `tools.disabled` settings list. The116    // bridge writes the settings file directly (no ACP roundtrip) and117    // fan-outs a `tool_toggled` event to all live session SSE buses.118    // Already-registered tools in active sessions are NOT retroactively119    // unregistered — the toggle takes effect on the next ACP child spawn120    // (`tools.disabled` is consulted at `Config` construction time).121    workspace_tool_toggle: { since: 'v1' },122    workspace_settings: { since: 'v1' },123    // `GET /workspace/permissions` is always available when this tag is124    // advertised. `POST /workspace/permissions` updates the active ACP125    // child and returns `permission_session_required` when no live ACP126    // session exists; the tag means the route contract exists, not that127    // the current daemon state can accept a write.128    workspace_permissions: { since: 'v1' },129    workspace_voice: { since: 'v1' },130    workspace_voice_transcription: { since: 'v1', modes: ['batch'] },131    // Inspect bound workspace trust and request local operator action.132    // Remote clients cannot directly write trustedFolders.json.133    workspace_trust: { since: 'v1' },134    // `POST /workspace/init` scaffolds an empty135    // `QWEN.md` (or whatever `getCurrentGeminiMdFilename()` returns) at136    // the bound workspace root. Body: `{force?: boolean}`. Default137    // refuses with 409 when the file already exists; `force: true`138    // overwrites. Mechanical only — does NOT call the LLM. To AI-fill139    // the file, the caller should follow up with140    // `POST /session/:id/prompt`.141    workspace_init: { since: 'v1' },142    // `POST /workspace/setup-github` installs the fixed143    // qwen-code-action workflow set into the bound workspace after144    // explicit consent. The route reuses the interactive `/setup-github`145    // release lookup, workflow download, and `.gitignore` update logic.146    workspace_github_setup: { since: 'v1' },147    // `POST /workspace/mcp/:server/restart` performs148    // a single-server MCP restart (disconnect + reconnect + rediscover)149    // through the ACP child's `McpClientManager`. Pre-checks the live150    // budget snapshot: when the target server is not151    // already in `reservedSlots` AND the live count would exceed the152    // configured budget under `enforce` mode, returns 200 with153    // `{restarted:false, skipped:true, reason:'budget_would_exceed'}`154    // rather than triggering a refusal cascade. Other skip reasons:155    // `'in_flight'` (concurrent discovery in progress), `'disabled'`156    // (server is configured but explicitly disabled).157    workspace_mcp_restart: { since: 'v1' },158    // Daemon hosts `POST /session/:id/recap`, which159    // generates a one-sentence "where did I leave off" summary by160    // running `generateSessionRecap` (`core/services/sessionRecap.ts`) as161    // a side-query against the fast model. Non-strict mutation gate —162    // posture mirrors `/session/:id/prompt` (token cost, not state163    // mutation). The route returns `{sessionId, recap}` where `recap`164    // may be `null` for too-short histories or transient model failures165    // (best-effort, never throws). SDK helper: `DaemonClient.recapSession`.166    session_recap: { since: 'v1' },167    // Side question (/btw) against the session's conversation context.168    // Single-turn, tool-free LLM call via runForkedAgent (cache path).169    session_btw: { since: 'v1' },170    // Direct daemon-side shell execution for an existing session.171    // Advertised CONDITIONALLY: operators must explicitly enable it and172    // configure bearer auth. Clients must still send a session-bound173    // X-Qwen-Client-Id when calling the route.174    session_shell_command: { since: 'v1' },175    // Daemon hosts a workspace-shared MCP transport176    // pool (`QwenAgent.mcpPool`); `GET /workspace/mcp` reflects pool-level177    // accounting (`entryCount`, `entrySummary` on each per-server cell).178    // Advertised CONDITIONALLY — the kill switch179    // `QWEN_SERVE_NO_MCP_POOL=1` env var falls back to per-session MCP180    // clients and the tag is omitted so SDK consumers181    // pre-flighting on the tag get accurate "pool is on" semantics.182    mcp_workspace_pool: { since: 'v1' },183    // `POST /workspace/mcp/:server/restart`184    // accepts an optional `?entryIndex=N` (or `*`) query parameter185    // and may return the new `{entries: RestartResult[]}` shape when186    // the pool holds multiple entries for the same server name (e.g.187    // sessions injected divergent OAuth headers). Single-entry188    // restarts continue to return the legacy `{restarted, durationMs}`189    // shape for compatibility with pre-F2 SDK clients. Advertised190    // CONDITIONALLY in lockstep with `mcp_workspace_pool`: pool191    // off → both tags absent, pool on → both tags present. Operators192    // pre-flighting on this tag can branch on whether the response193    // shape may include `entries[]`.194    mcp_pool_restart: { since: 'v1' },195    // Daemon was booted with `--require-auth` (or196    // `requireAuth: true`), so even loopback callers must carry a bearer197    // token. Advertised CONDITIONALLY — only when the flag is on — so198    // SDK clients can branch on its presence to surface a clear "this199    // deployment requires auth" hint instead of speculatively trying200    // requests and parsing the resulting 401 body. Loopback developer201    // defaults (no flag) omit the tag, preserving the bit-for-bit shape202    // older clients expect.203    require_auth: { since: 'v1' },204    // Daemon was booted with `--allow-origin <pattern>`205    // (at least one entry, including the `*` literal). Advertised206    // CONDITIONALLY — only when the flag is set — so browser SDK clients207    // can pre-flight whether the daemon will honor their cross-origin208    // request before issuing it (and parsing a 403). The configured209    // pattern list is intentionally NOT echoed in the capabilities210    // envelope — browser webui knows its own origin, and surfacing the211    // list would let an unauthenticated `/capabilities` reader212    // enumerate every trusted origin, which is useful recon for a213    // misconfigured deployment.214    allow_origin: { since: 'v1' },215    // Daemon exposes the device-flow auth surface216    // (`POST /workspace/auth/device-flow`, GET/DELETE on `/:id`, and217    // `GET /workspace/auth/status`). Advertised UNCONDITIONALLY: the218    // routes themselves return `400 unsupported_provider` if the daemon219    // can't satisfy a specific provider, so clients always probe via the220    // route. The list of supported providers is surfaced through the221    // status route (extension data on `/capabilities` would inflate the222    // descriptor shape; we keep the registry uniform).223    auth_device_flow: { since: 'v1' },224    permission_mediation: {225        since: 'v1',226        modes: ['first-responder', 'designated', 'consensus', 'local-only'],227    },228    prompt_absolute_deadline: { since: 'v1' },229    writer_idle_timeout: { since: 'v1' },230    non_blocking_prompt: { since: 'v1' },231    session_language: { since: 'v1' },232    session_rewind: { since: 'v1' },233    workspace_hooks: { since: 'v1' },234    session_hooks: { since: 'v1' },235    workspace_extensions: { since: 'v1' },236    session_branch: { since: 'v1' },237    rate_limit: { since: 'v1' },238    workspace_reload: { since: 'v1' },239    // Phase 2 "reverse tool channel" (issue #5626). A connected WS client (e.g.240    // the Chrome extension) can host an MCP server that the daemon's agent241    // calls by carrying `mcp_message` JSON-RPC frames over the daemon WS,242    // reusing the SDK-MCP-server control-plane pattern. Inbound WS frame types:243    // `mcp_register` { server }, `mcp_message` { id, server, payload }244    // (bidirectional, request/response correlated by `id`), `mcp_unregister`245    // { server }. Advertised CONDITIONALLY so clients pre-flight this tag before246    // attempting to register a client-hosted server. `runQwenServe` enables it247    // only when explicitly requested by option or env.248    client_mcp_over_ws: { since: 'v1' },249    // Plan C "CDP tunnel" (issue #5626): the daemon exposes a `/cdp` WebSocket250    // where a loopback CDP client drives ONE real tab251    // via the extension's `chrome.debugger`, tunneled over `/acp` as `cdp_*`252    // frames. Advertised when explicitly enabled or when the daemon is serving a253    // Chrome extension origin.254    cdp_tunnel_over_ws: { since: 'v1' },255    // Daemon hosts the `/voice/stream` WebSocket: the browser captures audio and256    // streams raw PCM, the daemon transcribes server-side via the configured257    // `voiceModel` (credentials never reach the client). Advertised258    // UNCONDITIONALLY (like `auth_device_flow`): presence means the endpoint259    // exists, not that a voice model is configured. The WS returns an `error`260    // frame when no transcribable `voiceModel` is set, so clients probe by261    // connecting rather than reading ambient settings into `/capabilities` (which262    // would make the envelope depend on the user's home config). `modes`263    // enumerates the two transcription paths (realtime vs. on-stop batch).264    voice_transcribe: { since: 'v1', modes: ['streaming', 'batch'] },265};266/**267 * Subset of `ServeFeature` whose advertisement depends on runtime config268 * (currently just `require_auth`, which is announced only when the269 * daemon was started with `--require-auth`). Each entry pairs the270 * feature key with a predicate over `AdvertiseFeatureToggles` — the271 * toggle decision lives next to the feature key, so adding a new272 * conditional tag is **two coordinated changes** instead of four:273 *274 * 1. Register the tag in `SERVE_CAPABILITY_REGISTRY` above with its275 *    `since` protocol version (just like baseline tags).276 * 2. Add an entry to THIS Map mapping the tag to a toggle predicate277 *    (extend `AdvertiseFeatureToggles` first if the predicate needs a278 *    new field to read).279 *280 * The previous `Set` + per-feature `if`-branch shape needed FOUR281 * coordinated changes (registry, set, toggles interface, predicate282 * branch) and silently fail-CLOSED when the branch was missed —283 * fail-CLOSED is good, but invisible to the contributor adding the284 * tag. The Map shape collapses the predicate-decision and the285 * set-membership into one entry, so a future contributor either286 * registers the predicate (advertised when toggle on) or doesn't287 * register the tag in the Map at all (advertised unconditionally288 * like baseline tags) — both are intentional, neither is a silent289 * miss.290 *291 * Reviewed-through-failure: the292 * `every conditional tag advertises when its toggle is on` test in293 * `server.test.ts` iterates this Map's keys, so a future tag added294 * here whose predicate isn't honored by `getAdvertisedServeFeatures`295 * fails the suite — adoption-of-record for the Map shape rather than296 * relying on a hand-maintained invariant.297 */298export const CONDITIONAL_SERVE_FEATURES = new Map([299    ['require_auth', (toggles) => toggles.requireAuth === true],300    ['mcp_workspace_pool', (toggles) => toggles.mcpPoolActive === true],301    ['mcp_pool_restart', (toggles) => toggles.mcpPoolActive === true],302    ['allow_origin', (toggles) => toggles.allowOriginActive === true],303    [304        'prompt_absolute_deadline',305        (toggles) => typeof toggles.promptDeadlineMs === 'number' &&306            toggles.promptDeadlineMs > 0,307    ],308    [309        'writer_idle_timeout',310        (toggles) => typeof toggles.writerIdleTimeoutMs === 'number' &&311            toggles.writerIdleTimeoutMs > 0,312    ],313    ['workspace_settings', (toggles) => toggles.persistSettingAvailable === true],314    ['workspace_voice', (toggles) => toggles.persistSettingAvailable === true],315    [316        'workspace_voice_transcription',317        (toggles) => toggles.voiceTranscriptionAvailable === true,318    ],319    [320        'session_shell_command',321        (toggles) => toggles.sessionShellCommandEnabled === true,322    ],323    ['rate_limit', (toggles) => toggles.rateLimit === true],324    ['workspace_reload', (toggles) => toggles.reloadAvailable === true],325    ['client_mcp_over_ws', (toggles) => toggles.clientMcpOverWsEnabled === true],326    ['cdp_tunnel_over_ws', (toggles) => toggles.cdpTunnelOverWsEnabled === true],327    [328        // Advertised whenever the `/voice/stream` WS endpoint exists. A configured329        // token (or `--require-auth`) no longer suppresses it: browsers can't set330        // an `Authorization` header on a WebSocket, so the Web Shell carries the331        // bearer token in the `Sec-WebSocket-Protocol` subprotocol, which the ACP332        // upgrade listener verifies (see acp-http/index.ts).333        'voice_transcribe',334        (toggles) => toggles.voiceWsAvailable !== false,335    ],336]);337export const SERVE_FEATURES = Object.freeze(Object.keys(SERVE_CAPABILITY_REGISTRY));338function serveProtocolVersionIndex(version) {339    return SUPPORTED_SERVE_PROTOCOL_VERSIONS.indexOf(version);340}341function isFeatureAvailableInProtocol(feature, protocolVersion) {342    return (serveProtocolVersionIndex(SERVE_CAPABILITY_REGISTRY[feature].since) <=343        serveProtocolVersionIndex(protocolVersion));344}345export function getRegisteredServeFeatures() {346    return [...SERVE_FEATURES];347}348export function getAdvertisedServeFeatures(protocolVersion = SERVE_PROTOCOL_VERSION, toggles = {}) {349    return SERVE_FEATURES.filter((feature) => {350        if (!isFeatureAvailableInProtocol(feature, protocolVersion))351            return false;352        // Conditional tags route through the per-feature toggle predicate;353        // baseline tags (no Map entry) advertise unconditionally. Without354        // this gate every daemon would advertise the conditional tags355        // regardless of operator opt-in, breaking the "tag presence =356        // behavior is on" contract clients depend on.357        const predicate = CONDITIONAL_SERVE_FEATURES.get(feature);358        if (predicate !== undefined)359            return predicate(toggles);360        return true;361    });362}363export function getServeFeatures() {364    return getAdvertisedServeFeatures();365}366export function getServeProtocolVersions() {367    return {368        current: SERVE_PROTOCOL_VERSION,369        supported: [...SUPPORTED_SERVE_PROTOCOL_VERSIONS],370    };371}372//# sourceMappingURL=capabilities.js.map
basant307/AI_Governance_Project · CoolFace