diff --git a/website/content/de/users/features/channels/wecom.md b/website/content/de/users/features/channels/wecom.md index 21e5a6d5377..1961ac8c16d 100644 --- a/website/content/de/users/features/channels/wecom.md +++ b/website/content/de/users/features/channels/wecom.md @@ -2,6 +2,9 @@ Dieser Leitfaden behandelt die Einrichtung von Qwen Code mit einem intelligenten WeCom-Roboter (企业微信智能机器人). +Für selbst erstellte Anwendungen mit Corp ID, Agent ID, Token und EncodingAESKey +verwende den separaten [WeCom Callback channel](./wecom-callback). + ## Voraussetzungen - Ein WeCom-Organisationskonto diff --git a/website/content/en/developers/daemon-rest-api-reference.md b/website/content/en/developers/daemon-rest-api-reference.md index 344c1d0d8b2..4f4b93ef542 100644 --- a/website/content/en/developers/daemon-rest-api-reference.md +++ b/website/content/en/developers/daemon-rest-api-reference.md @@ -123,12 +123,13 @@ operations when they share ownership and an SDK family. | Workspace runtime status | [`GET /workspace/mcp`](./qwen-serve-protocol.md#get-workspacemcp) · [`GET /workspace/skills`](./qwen-serve-protocol.md#get-workspaceskills) · [`GET /workspace/providers`](./qwen-serve-protocol.md#get-workspaceproviders) · [`GET /workspace/env`](./qwen-serve-protocol.md#get-workspaceenv) · [`GET /workspace/preflight`](./qwen-serve-protocol.md#get-workspacepreflight) | `workspace_mcp`, `workspace_skills`, `workspace_providers`, `workspace_env`, `workspace_preflight`; legacy-primary | `DaemonClient.workspaceMcp`, `workspaceSkills`, `workspaceProviders`, `workspaceEnv`, `workspacePreflight` | | File mutation | [`POST /file/write`](./qwen-serve-protocol.md#post-filewrite) · [`POST /file/edit`](./qwen-serve-protocol.md#post-fileedit) | `workspace_file_write`; legacy-primary | `DaemonClient.writeWorkspaceFile`, `DaemonClient.editWorkspaceFile` | | Directory mutation | [`POST /workspaces/:workspace/directory/create`](./qwen-serve-protocol.md#post-workspacesworkspacedirectorycreate) · [`POST /workspaces/:workspace/directory/delete`](./qwen-serve-protocol.md#post-workspacesworkspacedirectorydelete) | `workspace_qualified_rest_core` plus `workspace_directory_create` or `workspace_directory_delete`; selected-runtime, trusted local only | `WorkspaceDaemonClient.createWorkspaceDirectory`, `WorkspaceDaemonClient.deleteWorkspaceDirectory` | -| Session inspection and tasks | [`GET /session/:id/supported-commands`](./qwen-serve-protocol.md#get-sessionidsupported-commands) · [`GET /session/:id/tasks`](./qwen-serve-protocol.md#get-sessionidtasks) · [`POST /session/:id/tasks/:taskId/workflow-action`](./qwen-serve-protocol.md#post-sessionidtaskstaskidworkflow-action) · [`GET /session/:id/lsp`](./qwen-serve-protocol.md#get-sessionidlsp) · [`GET /session/:id/resources`](./qwen-serve-protocol.md#get-sessionidresources) | `session_supported_commands`, `session_tasks`, `session_lsp`, `session_resources`; live-session-owner | `DaemonClient.sessionSupportedCommands`, `sessionTasks`, `sessionWorkflowTaskAction`, `sessionLspStatus`, `sessionResources` | +| Session inspection and tasks | [`GET /session/:id/supported-commands`](./qwen-serve-protocol.md#get-sessionidsupported-commands) · [`GET /session/:id/tasks`](./qwen-serve-protocol.md#get-sessionidtasks) · [`GET /session/:id/tasks/:taskId/runtime`](./qwen-serve-protocol.md#get-sessionidtaskstaskidruntime) · [`POST /session/:id/tasks/:taskId/workflow-action`](./qwen-serve-protocol.md#post-sessionidtaskstaskidworkflow-action) · [`GET /session/:id/lsp`](./qwen-serve-protocol.md#get-sessionidlsp) · [`GET /session/:id/resources`](./qwen-serve-protocol.md#get-sessionidresources) | `session_supported_commands`, `session_tasks`, `session_lsp`, `session_resources`; live-session-owner | `DaemonClient.sessionSupportedCommands`, `sessionTasks`, `sessionTaskRuntime`, `sessionWorkflowTaskAction`, `sessionLspStatus`, `sessionResources` | | Workspace-qualified history | [`GET /workspaces/:workspace/session/:id/transcript`](./qwen-serve-protocol.md#get-workspacesworkspacesessionidtranscript) · [`GET /workspaces/:workspace/session/:id/export`](./qwen-serve-protocol.md#get-workspacesworkspacesessionidexport) · [`GET /workspaces/:workspace/session/:id/archive/export`](./qwen-serve-protocol.md#get-workspacesworkspacesessionidarchiveexport) | `workspace_persisted_transcript`, `workspace_session_export`, `workspace_archived_session_export`; persisted-workspace | `WorkspaceDaemonClient.getSessionTranscriptPage`, `exportSession`, `exportArchivedSession` | | Worktree recovery | [`POST /session/:id/worktree-reset`](./qwen-serve-protocol.md#post-sessionidworktree-reset) | `session_worktree_reset_v1`; live-session-owner | `DaemonClient.resetWorktreeSession` | | Persisted session catalog | [`POST /sessions/catalog`](./qwen-serve-protocol.md#post-sessionscatalog) · [`GET /workspace/:id/session-info`](./qwen-serve-protocol.md#get-workspaceidsession-info-and-get-workspacesworkspacesession-info) · [`GET /workspaces/:workspace/session-info`](./qwen-serve-protocol.md#get-workspaceidsession-info-and-get-workspacesworkspacesession-info) · [`GET /workspace/:id/sessions`](./qwen-serve-protocol.md#get-workspaceidsessions-and-get-workspacesworkspacesessions) · [`GET /workspaces/:workspace/sessions`](./qwen-serve-protocol.md#get-workspaceidsessions-and-get-workspacesworkspacesessions) · [`GET /workspaces/:workspace/sessions/live-state`](./qwen-serve-protocol.md#get-workspacesworkspacesessionslive-state) | `session_info`, `session_list`, `workspace_session_live_state`; persisted-workspace; `session_catalog_batch`; persisted-workspace per member | `DaemonClient.getStandaloneSession`, `listWorkspaceSessions`, `listSessionsCatalog`, `getWorkspaceSessionLiveState` | | Session organization | [`GET /workspace/:id/session-groups`](./qwen-serve-protocol.md#get-workspaceidsession-groups) · [`POST /workspace/:id/session-groups`](./qwen-serve-protocol.md#post-workspaceidsession-groups) · [`PATCH /workspace/:id/session-groups/:groupId`](./qwen-serve-protocol.md#patch-workspaceidsession-groupsgroupid) · [`DELETE /workspace/:id/session-groups/:groupId`](./qwen-serve-protocol.md#delete-workspaceidsession-groupsgroupid) · [`PATCH /session/:id/organization`](./qwen-serve-protocol.md#patch-sessionidorganization-and-patch-workspacesworkspacesessionidorganization) · [`PATCH /workspaces/:workspace/session/:id/organization`](./qwen-serve-protocol.md#patch-sessionidorganization-and-patch-workspacesworkspacesessionidorganization) | `session_organization`; legacy-primary or persisted-workspace | `DaemonClient.listSessionGroups`, `createSessionGroup`, `updateSessionGroup`, `deleteSessionGroup`, `updateSessionOrganization`; `WorkspaceDaemonClient.updateSessionOrganization` | | Bulk persisted-session changes | [`POST /sessions/delete`](./qwen-serve-protocol.md#post-sessionsdelete) · [`POST /sessions/archive`](./qwen-serve-protocol.md#post-sessionsarchive) · [`POST /sessions/unarchive`](./qwen-serve-protocol.md#post-sessionsunarchive) | `session_archive`; legacy-primary | `DaemonClient.deleteSessionsData`, `archiveSessionsData`, `unarchiveSessionsData` | +| Message deletion | [`GET /session/:id/message-groups`](./qwen-serve-protocol.md#get-sessionidmessage-groups) · [`POST /session/:id/messages/delete`](./qwen-serve-protocol.md#post-sessionidmessagesdelete) | `session_message_deletion`; live-session-owner | `DaemonClient.getMessageGroups`, `DaemonClient.deleteMessageGroups` | | Optional session controls | [`POST /session/:id/recap`](./qwen-serve-protocol.md#post-sessionidrecap) · [`POST /session/:id/generate`](./qwen-serve-protocol.md#post-sessionidgenerate) · [`POST /session/:id/approval-mode`](./qwen-serve-protocol.md#post-sessionidapproval-mode) | `session_recap`, `session_generation`, `session_approval_mode_control`; live-session-owner | `DaemonClient.recapSession`, raw REST for generation, `DaemonClient.setSessionApprovalMode` | | Workspace configuration | [`POST /workspace/tools/:name/enable`](./qwen-serve-protocol.md#post-workspacetoolsnameenable) · [`POST /workspace/skills/:name/enable`](./qwen-serve-protocol.md#post-workspaceskillsnameenable) · [`POST /workspace/skills/enable`](./qwen-serve-protocol.md#post-workspaceskillsenable) · [`POST /workspace/init`](./qwen-serve-protocol.md#post-workspaceinit) · [`POST /workspace/mcp/reload`](./qwen-serve-protocol.md#post-workspacemcpreload) · [`POST /workspace/mcp/:server/restart`](./qwen-serve-protocol.md#post-workspacemcpserverrestart) · [`POST /language`](./qwen-serve-protocol.md#post-language) | `workspace_tool_toggle`, `workspace_skill_settings_toggle`, `workspace_skill_settings_batch_toggle`, `workspace_init`, `workspace_mcp_manage`, `workspace_mcp_restart`, `user_language_sync`; legacy-primary or process-global | `DaemonClient.setWorkspaceToolEnabled`, `setWorkspaceSkillEnabled`, `setWorkspaceSkillsEnabled`, `initWorkspace`, `reloadWorkspaceMcp`, `restartMcpServer`, `setUserLanguage` | | Skill runtime refresh | [`POST /workspace/skills/refresh`](./qwen-serve-protocol.md#post-workspaceskillsrefresh) | `workspace_session_refresh`; legacy-primary | `DaemonClient.refreshWorkspaceSkills` | diff --git a/website/content/en/developers/development/telemetry.md b/website/content/en/developers/development/telemetry.md index 3c71dc66809..ed0845776a3 100644 --- a/website/content/en/developers/development/telemetry.md +++ b/website/content/en/developers/development/telemetry.md @@ -71,7 +71,7 @@ These settings can be overridden by environment variables or CLI flags. | `enabled` | `QWEN_TELEMETRY_ENABLED` | `--telemetry` / `--no-telemetry` | Enable or disable telemetry | `true`/`false` | `false` | | `target` | `QWEN_TELEMETRY_TARGET` | `--telemetry-target ` _(deprecated)_ | Informational destination label; does not control exporter routing — set `otlpEndpoint` or `outfile` to configure where data is sent | `"gcp"`/`"local"` | `"local"` | | `otlpEndpoint` | `QWEN_TELEMETRY_OTLP_ENDPOINT` | `--telemetry-otlp-endpoint ` | OTLP collector endpoint | URL string | `http://localhost:4317` | -| `otlpProtocol` | `QWEN_TELEMETRY_OTLP_PROTOCOL` | `--telemetry-otlp-protocol ` | OTLP transport protocol | `"grpc"`/`"http"` | `"grpc"` | +| `otlpProtocol` | `QWEN_TELEMETRY_OTLP_PROTOCOL` | `--telemetry-otlp-protocol ` | OTLP exporter protocol: `grpc` = gRPC/protobuf; `http` = HTTP/JSON | `"grpc"`/`"http"` | `"grpc"` | | `otlpTracesEndpoint` | `QWEN_TELEMETRY_OTLP_TRACES_ENDPOINT` | - | Per-signal endpoint override for traces (HTTP only) | URL string | - | | `otlpLogsEndpoint` | `QWEN_TELEMETRY_OTLP_LOGS_ENDPOINT` | - | Per-signal endpoint override for logs (HTTP only) | URL string | - | | `otlpMetricsEndpoint` | `QWEN_TELEMETRY_OTLP_METRICS_ENDPOINT` | - | Per-signal endpoint override for metrics (HTTP only) | URL string | - | @@ -145,6 +145,11 @@ sinks; non-internal API response telemetry can populate `response_text`, so OTel logs, UI telemetry, and chat recording may receive response text independently of this setting. QwenLogger does not include `response_text`. +**HTTP OTLP encoding:** `otlpProtocol: "http"` sends OTLP/JSON with +`Content-Type: application/json` for traces, logs, and metrics. Use a collector +or receiver that supports OTLP/HTTP JSON. The `http` value does not select +OTLP/HTTP protobuf encoding. + **HTTP OTLP signal routing:** When using HTTP protocol (`otlpProtocol: "http"`), Qwen Code automatically appends signal-specific paths (`/v1/traces`, `/v1/logs`, `/v1/metrics`) to the base `otlpEndpoint`. For example, `http://collector:4318` @@ -771,7 +776,8 @@ The following events are logged: - **Attributes**: `prompt_id` (string), `finish_reason` (string), `result` (string) - `loop_detected`: Loop detected during agent execution. _(Note: emitted without `qwen-code.` prefix — pre-existing inconsistency.)_ - - **Attributes**: `loop_type` (string), `prompt_id` (string) + - **Attributes**: `loop_type` (string), `prompt_id` (string), `loop_reason` (string, optional), `tool_call_count` (int, optional), `tool_call_limit` (int, optional) + - `loop_reason` names the cause when one loop type covers several: `explicit_cap`, `backstop` or `stuck_repetition` for `turn_tool_call_cap`, and `repeated_call` or `provider_duplicate_id` for `global_tool_call_duplicate`. `tool_call_count` and `tool_call_limit` are set for `turn_tool_call_cap` only. `tool_call_count` is the count the cap compared with the limit; daemon/ACP sessions check the cap per batch before running it, so their count includes the whole skipped batch. - `kitty_sequence_overflow`: Kitty graphics protocol sequence exceeded buffer size. _(Note: emitted without `qwen-code.` prefix — pre-existing inconsistency.)_ - **Attributes**: `sequence_length` (int), `truncated_sequence` (string, first 20 chars) diff --git a/website/content/en/developers/qwen-serve-protocol.md b/website/content/en/developers/qwen-serve-protocol.md index b180f8ccdaf..c4552f15403 100644 --- a/website/content/en/developers/qwen-serve-protocol.md +++ b/website/content/en/developers/qwen-serve-protocol.md @@ -239,9 +239,9 @@ registry. Clients **must** gate UI off `features`, not off `mode` (per design 'mcp_workspace_pool', 'mcp_pool_restart', 'require_auth', 'allow_origin', 'auth_device_flow', 'permission_mediation', 'prompt_absolute_deadline', 'writer_idle_timeout', - 'non_blocking_prompt', 'session_language', 'user_language_sync', 'session_rewind', + 'non_blocking_prompt', 'session_language', 'user_language_sync', 'session_message_deletion', 'session_rewind', 'workspace_hooks', 'session_hooks', 'workspace_extensions', - 'session_branch', 'rate_limit', 'workspace_reload', 'channel_delivery', + 'session_branch', 'rate_limit', 'workspace_reload', 'workspace_model_reload', 'channel_delivery', 'multi_workspace_sessions', 'multi_workspace_session_rewind', 'multi_workspace_session_shell', 'persistent_workspace_registration', 'workspace_display_name', 'workspace_runtime_removal', 'workspace_runtime', @@ -254,7 +254,7 @@ registry. Clients **must** gate UI off `features`, not off `mode` (per design 'client_mcp_over_ws', 'cdp_tunnel_over_ws', 'browser_automation_mcp'] ``` -> Conditional tags appear only when their matching deployment toggle is on (see the table below). F3's `permission_mediation` tag is always-on and carries `modes: ['first-responder', 'designated', 'consensus', 'local-only']` so SDK clients can introspect the build-supported set; the runtime-active strategy is at `body.policy.permission`. +> Conditional tags appear only when their deployment or runtime availability condition is met (see the table below). F3's `permission_mediation` tag is always-on and carries `modes: ['first-responder', 'designated', 'consensus', 'local-only']` so SDK clients can introspect the build-supported set; the runtime-active strategy is at `body.policy.permission`. `session_startup_config` advertises optional `startupConfig: { modelServiceId, reasoningEffort? }` on ordinary and standalone creation. Always preflight this tag: older ordinary-create routes may silently ignore the object. `modelServiceId` is required (1–256 characters); `reasoningEffort` accepts `none`, `default`, `low`, `medium`, `high`, `xhigh`, or `max` when supported by the selected model. Omission applies only the model, without a reasoning setter or implicit `default`. The object cannot be combined with the legacy top-level `modelServiceId` or explicit single scope and implies a new thread. Startup never saves shared defaults; later session behavior is unchanged. @@ -601,6 +601,7 @@ operator diagnostic snapshot documented below. | Tag | Advertised when … | | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `agent_collaboration_v1` | at least one registered workspace opted into persistent workspace Agent collaboration via `experimental.agentCollaboration` (any scope, workspace included) or `QWEN_CODE_ENABLE_AGENT_COLLABORATION=1`. Present means `/workspaces/:workspace/agent/*` and the Agent Host transport routes are mounted; `workspaces[].agentCollaborationEnabled` identifies each workspace's opt-in state, and clients must only show or call the collaboration surface for enabled entries. Older daemons omit that workspace field, so clients may fall back to this top-level tag for compatibility. Absent means collaboration is not served on this response — the tag is recomputed per request, not snapshotted at boot — so a client must drop the entry point rather than render it and let the calls 404. First enabling it requires a restart; toggling it for a workspace while another already has it on takes effect on the next request. Distinct from the unconditional `workspace_agents` tag, which is subagent-definition CRUD. | +| `session_message_deletion` | the private Hosted Harness profile is not active. Lists and deletes message groups through live-session-owner routes; ordinary local daemon sessions support physical deletion, while Managed authoritative storage and SSH workspaces reject it. | | `hosted_harness_private_v1` | the private Hosted Harness profile is active; only its authenticated session API is available. | | `managed_session_journal_delta_v1` | the private Hosted Harness profile is active (same condition as `hosted_harness_private_v1`) and this build can open Managed Session journals containing `message.delta` records. A control plane requires the tag at capability negotiation, so a rolled-back Harness predating those records is refused once there instead of failing every Session open (`managed_session_open_failed`). | | `require_auth` | the daemon was started with `--require-auth` (or `requireAuth: true` via the embedded API). Bearer token is mandatory on every normal API route, including `/health` on loopback binds; channel webhook ingress keeps its independent shared-secret authentication, and Web Shell document and asset routes remain pre-auth. | @@ -2490,6 +2491,43 @@ top-level session). Agents launched by the top-level session omit `parentAgentId` and `parentName`; clients should treat all three fields as optional and fall back to a flat list when they are absent. +### `GET /session/:id/tasks/:taskId/runtime` + +On-demand detail for one shell task. `GET /session/:id/tasks` carries lifecycle +facts only; this route reads the captured output file and probes the process +tree, so an open detail panel asks for it separately. + +The ACP method is `_qwen/session/task_runtime`, with `sessionId` and `taskId` +parameters. Both transports resolve the live session owner; ACP also requires +the connection to own the session. + +The path's `taskId` is resolved against the session's own shell registry; +callers never pass a file path. + +```json +{ + "v": 1, + "sessionId": "", + "taskId": "bg_0123abcd", + "runtime": { + "memoryBytes": 104857600, + "outputBytes": 65536, + "outputModifiedAt": 1700000000000, + "outputTail": "ready in 120ms\n", + "outputTailTruncated": true + } +} +``` + +`runtime` is `null` when the task is unknown, has been evicted, or is not a +shell task. `memoryBytes` is the resident set of the shell's process tree, +sampled only while the task is running; shared pages are counted once per +process, and a container sandbox may expose only the wrapper process. +`outputTail` is capped at the same 8 KiB limit used by the completion +notification, and `outputTailTruncated` marks a tail that starts mid-file. +`outputError` is set when the file cannot be read; fields from a part that did +succeed may still be present. + ### `POST /session/:id/tasks/:taskId/workflow-action` Controls a workflow run, or starts a new one. The ACP method is @@ -3013,7 +3051,7 @@ On success the route returns `200` with the replacement session's create-shape r This route never reads `X-Qwen-Client-Id`, so the replacement spawns unattached — but the success response is not registration-free. A fresh transfer mints an owner-style `clientId` for that spawn, registers it on the replacement, and reports it in the body; an idempotent resume of a committed transfer reports none. The minted id is not an attachment (the replacement's attach count is unchanged, so `killSession` with `requireZeroAttaches` still sees it as unattached), but a live client registration is exactly what holds the daemon's idle cleanup off: detach an id you do not keep using, or the replacement stays live indefinitely — and a replacement that is still live with no marker is the shape a later reset refuses to dismantle. Treat the response as the replacement's identity, and attach it through the normal `POST /session/:id/load` or `/resume` surface when a registered client is required. -The transfer is serialized per checkout against worktree restores and other resets, refuses to run while either session is busy (prompt in flight or a pending interaction), and arms an admission barrier on the superseded session: while it is armed, exactly eight writers are refused with `worktree_reset_active` at admission — `POST /session/:id/prompt`, `/rewind`, `/cd`, `/branch`, `/fork`, `/shell`, `/goal`, and `/tasks/:taskId/workflow-action`. The other seven are fenced because each moves the session cwd or starts work in it without passing prompt admission: a shell command runs in the session's effective cwd, which for a relocated worktree session is the checkout itself; a fork agent runs its tools in that cwd; a rewind restores files relative to it; a cd moves it, including into a subdir of the checkout; a branch mutates the superseded session's persisted history and spawns a derivative session while ownership is in flux; a goal `resume` promotes a queued turn and queues a continuation; and a workflow action runs a saved workflow or a caller-supplied script, or restarts a live run, through the session's own tool registry. `POST /session/:id/continue` is not a ninth gate: it drives an accepted continuation through the fenced prompt admission and is refused there. That list is the whole fence, and the fence is not everything that reaches the child — the release and stop paths stay usable mid-transfer by design (cancelling a task, clearing a goal, detaching a client, killing the session), and the daemon-internal background-notification enqueue — a sub-session's completion acknowledgement to its parent, which no HTTP route calls — is unfenced as well, because it enqueues a notification rather than starting a turn. The transfer's last step severs the superseded session's client registrations, clears its in-memory worktree association, and reports whether the superseded session is actually gone. A survivor — a child that still holds background work, so the idle close the last detach triggers is refused and deferred — is surfaced rather than papered over: the daemon logs it, the barrier stays armed on that entry as the only fence left, and the `200` body carries `supersededSessionLive: true` so the caller knows the superseded id is still live and re-attachable inside the checkout whose ownership just moved. Crash safety is per window, and the write order defines the windows: the old sidecar's `supersededBy` link is written first, then the replacement's sidecar carrying `supersedes`, and the marker flips last. A crash before the flip therefore leaves the old session authoritative in one of three shapes a retry tells apart — no links at all (the replacement is an ordinary orphan and a retry simply starts a fresh transfer), the backward link alone with the replacement's sidecar missing (a retry fails closed with `worktree_reset_invalid_state` and leaves the interrupted state untouched for operator repair), or both links present (a retry rolls the partial transfer back and completes a fresh one — unless the interrupted replacement cannot be removed because a client is still attached to it, in which case the rollback fails closed with `worktree_reset_invalid_state` and keeps both links, so a later retry converges once that client is gone). A crash after the flip leaves the replacement authoritative and a retry resumes as a no-op. The old session is never deleted and the worktree checkout is never removed by reset. +The transfer is serialized per checkout against worktree restores and other resets, refuses to run while either session is busy (prompt in flight or a pending interaction), and arms an admission barrier on the superseded session: while it is armed, nine writers are refused with `worktree_reset_active` at admission — `POST /session/:id/prompt`, `/rewind`, `/cd`, `/branch`, `/fork`, `/shell`, `/goal`, `/tasks/:taskId/workflow-action`, and `/messages/delete`. The other eight are fenced because each moves the session cwd or starts work in it without passing prompt admission: a shell command runs in the session's effective cwd, which for a relocated worktree session is the checkout itself; a fork agent runs its tools in that cwd; a rewind restores files relative to it; a cd moves it, including into a subdir of the checkout; a branch mutates the superseded session's persisted history and spawns a derivative session while ownership is in flux; a goal `resume` promotes a queued turn and queues a continuation; message deletion rewrites persisted history; and a workflow action runs a saved workflow or a caller-supplied script, or restarts a live run, through the session's own tool registry. `POST /session/:id/continue` is not a tenth gate: it drives an accepted continuation through the fenced prompt admission and is refused there. That list is the whole fence, and the fence is not everything that reaches the child — the release and stop paths stay usable mid-transfer by design (cancelling a task, clearing a goal, detaching a client, killing the session), and the daemon-internal background-notification enqueue — a sub-session's completion acknowledgement to its parent, which no HTTP route calls — is unfenced as well, because it enqueues a notification rather than starting a turn. The transfer's last step severs the superseded session's client registrations, clears its in-memory worktree association, and reports whether the superseded session is actually gone. A survivor — a child that still holds background work, so the idle close the last detach triggers is refused and deferred — is surfaced rather than papered over: the daemon logs it, the barrier stays armed on that entry as the only fence left, and the `200` body carries `supersededSessionLive: true` so the caller knows the superseded id is still live and re-attachable inside the checkout whose ownership just moved. Crash safety is per window, and the write order defines the windows: the old sidecar's `supersededBy` link is written first, then the replacement's sidecar carrying `supersedes`, and the marker flips last. A crash before the flip therefore leaves the old session authoritative in one of three shapes a retry tells apart — no links at all (the replacement is an ordinary orphan and a retry simply starts a fresh transfer), the backward link alone with the replacement's sidecar missing (a retry fails closed with `worktree_reset_invalid_state` and leaves the interrupted state untouched for operator repair), or both links present (a retry rolls the partial transfer back and completes a fresh one — unless the interrupted replacement cannot be removed because a client is still attached to it, in which case the rollback fails closed with `worktree_reset_invalid_state` and keeps both links, so a later retry converges once that client is gone). A crash after the flip leaves the replacement authoritative and a retry resumes as a no-op. The old session is never deleted and the worktree checkout is never removed by reset. **Errors:** @@ -3651,6 +3689,71 @@ switch details from `_meta.qwenModelSwitch`. On success, publishes `model_switched` to the SSE stream. On failure, publishes `model_switch_failed` (so passive subscribers see the failure, not just the caller). Races against the agent channel exit so a wedged child can't block the HTTP handler. A successful switch also records the session model in the session JSONL on a best-effort basis; when the record is written, daemon load/resume attempts to restore this session's model before authentication. If the recorded model can no longer be applied (model removed, credentials unavailable), restore uses a same-id registry route when one exists — for a runtime-snapshot record that can be a different endpoint than the recorded binding — and continues on the `settings.model.name` default only when no route resolves. `settings.model.name` is still updated as the default for **new** sessions. +### `GET /session/:id/message-groups` + +Capability tag: `session_message_deletion`. Bridge → ACP extMethod `qwen/status/session/message_groups`. This is a live-session-owner read: it resolves the session's owning runtime and never falls back to the primary workspace. The daemon-wide capability does not imply support for every session; SSH workspaces and Managed authoritative storage refuse deletion. + +Lists safely attributable prompt/reply or command-display groups in active-history order. It does not acquire a writer lease for an ordinary legacy recorder. The read waits for earlier history mutations; a returned snapshot may become stale as the session changes. Deletion itself requires an idle session. + +Response (200): + +```json +{ + "snapshot": "", + "groups": [ + { + "id": "", + "promptId": "", + "recordIds": ["", ""] + } + ] +} +``` + +`promptId` and `promptIds` are optional prompt identities; `recordIds` identifies the group's saved records. An empty history returns `groups: []`. Treat group IDs and the snapshot as opaque; pass the returned snapshot unchanged when deleting. + +Errors include `404` for an unknown session, `400 unsupported_operation` for an SSH workspace, and `422 session_message_deletion_unsupported` for unsupported storage or unavailable recording. Existing writer-integrity errors may also return `409 session_writer_conflict`, `409 session_writer_lost`, `409 session_transcript_changed`, or `503 session_writer_unavailable`. + +### `POST /session/:id/messages/delete` + +Capability tag: `session_message_deletion`. Bridge → ACP extMethod `qwen/control/session/messages_delete`. Strict mutation gate; live-session-owner scoped with no fallback to the primary workspace. Accepts optional `X-Qwen-Client-Id` for event attribution. + +Request: + +```json +{ "snapshot": "", "groupIds": [""] } +``` + +Both fields are required. `snapshot` must be non-empty; `groupIds` must contain distinct non-empty strings. The selected groups must still exist in that snapshot. The server rejects busy sessions rather than queuing deletion behind a model turn. + +Response (200): + +```json +{ + "deletedGroupIds": [""], + "deletedPromptIds": [""] +} +``` + +Selected original records and their owned raw compression context are physically removed from JSONL. Existing summary text, command effects, and project files are preserved. Deleting every group leaves the session available for later prompts. + +Errors: + +- `400 invalid_message_groups` — malformed, unknown, or duplicate group selection. +- `400 invalid_client_id` — malformed attribution header. +- `404` — session unknown. +- `409 session_busy` — prompt or history mutation active; retry after it finishes. +- `409 worktree_reset_active` — this session is being superseded by a worktree reset; reattach to the replacement session. +- `409 message_groups_conflict` — selection snapshot is stale; fetch groups again and require a new selection before retrying. +- `409 message_history_invalid` — history cannot be safely attributed; changing the selection token alone cannot repair it. +- `400 unsupported_operation` / `422 session_message_deletion_unsupported` — session/workspace storage does not support this operation; refreshing and repeating the request will not enable it. +- `409 session_writer_conflict`, `409 session_writer_lost`, `409 session_transcript_changed`, or `503 session_writer_unavailable` — exclusive transcript ownership could not be established or retained. +- `503 message_deletion_context_unavailable` — deletion **has persisted**, but the live context could not be installed. The JSON body includes `persisted: true`, `deletedGroupIds`, and `deletedPromptIds`. Reconcile the UI from authoritative history and reopen the session before sending another prompt. Do not retry deletion as though nothing was removed; later prompts remain refused until context recovery succeeds. + +Subsequent history-dependent mutations and model requests while context recovery is pending return 503 `session_history_context_unavailable` without `persisted` or deletion identities. Loading or resuming the session retries installing authoritative JSONL context; only successful installation reopens model admission. Lifecycle close remains available. + +After persistence the session publishes `session_messages_deleted` with data `{sessionId, deletedGroupIds, deletedPromptIds}` and optional `originatorClientId` on the **event envelope**. Internal API-history identities are not published on this wire. This event also occurs for the persisted-503 path. Peers must invalidate cached transcript, navigation, search and selection state and reload authoritative history. The originator must reconcile both successful HTTP responses and `persisted: true` errors; it must not rely on processing its own SSE echo. + ### `POST /session/:id/recap` Capability tag: `session_recap`. Bridge → ACP extMethod `qwen/control/session/recap`. @@ -4345,6 +4448,10 @@ The TypeScript SDK automatically uses this protocol for attachments larger than See the [complete design](../design/session-attachment-chunk-upload.md) for lifecycle and compatibility details. +### Model catalog reload + +The `workspace_model_reload` capability advertises `POST /workspaces/:workspace/models/reload`. This authenticated, trusted, selected-runtime mutation re-reads user- and workspace-scope settings, refreshes the selected runtime environment overlay, and re-applies the environment in its ACP child before rebuilding model-provider configuration. Environment changes support provider credentials and can include other environment keys; they are not reported in the response or a settings event. The daemon process environment and workflow enablement remain unchanged. It returns `{ "status": "applied" | "deferred" | "failed" }`; `deferred` means no live ACP child applied the catalog. It does not fetch a platform catalog, write settings, switch the active conversation model, or broadcast a settings event. Read an existing session's `/session/:id/context` afterward for its current selectable models; before session creation, read the selected workspace's providers. Unknown, untrusted, draining, or closed runtimes do not fall back to primary. + ## Streaming wire format Events are emitted as standard EventSource frames. The daemon writes one `data:` line per frame (the JSON has no embedded newlines after `JSON.stringify`); the SDK parser at `packages/sdk-typescript/src/daemon/sse.ts` handles both that and the spec-allowed multi-`data:` form on the receive side. diff --git a/website/content/en/users/configuration/settings.md b/website/content/en/users/configuration/settings.md index c48b00306da..6e6c31bc215 100644 --- a/website/content/en/users/configuration/settings.md +++ b/website/content/en/users/configuration/settings.md @@ -654,6 +654,7 @@ Persistent sub-session concurrency settings for [`qwen serve`](../qwen-serve). C | Setting | Type | Description | Default | | -------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | +| `experimental.mods` | boolean | Load experimental [Mods](../extension/mods) from enabled extensions in trusted local interactive terminal and headless sessions. Available as **Mods (Experimental)** in `/settings`. Requires restart. Does not install or enable extensions. | `false` | | `experimental.sessionWorkflow` | boolean | Show the daemon Web Shell Session Workflow DAG and present the existing `plan` approval mode as **Plan & Review**. This changes presentation only: it does not add an approval mode, alter Todo execution behavior, or schedule dependencies. Changes take effect without restarting. | `false` | | `experimental.cron` | boolean | Enable in-session cron/loop tools (`cron_create`, `cron_list`, `cron_delete`) so the model can create recurring prompts. Can be disabled via the `QWEN_CODE_DISABLE_CRON=1` environment variable. Requires restart. | `true` | | `experimental.todoStopGuard` | boolean | Allow daemon and ACP sessions to continue after a natural model stop when the current work chain successfully wrote an unfinished top-level Todo list. Requires `tools.todoWrite.enabled`. Adds at most two consecutive primary-model calls without new user input; mid-turn user input starts a fresh two-attempt stage. It is not restored after process restart and is forced off in safe, bare, and Approval `plan` modes. Requires restart. | `false` | @@ -691,7 +692,7 @@ Configures logging and metrics collection for Qwen Code. For more information, s | `telemetry.enabled` | boolean | Whether or not telemetry is enabled. | | | `telemetry.target` | string | Informational label for the telemetry destination (`local` or `gcp`). Does not control exporter routing; set `telemetry.otlpEndpoint` or `telemetry.outfile` to configure where data is sent. | | | `telemetry.otlpEndpoint` | string | The endpoint for the OTLP Exporter. | | -| `telemetry.otlpProtocol` | string | The protocol for the OTLP Exporter (`grpc` or `http`). | | +| `telemetry.otlpProtocol` | string | The OTLP exporter protocol: `grpc` (gRPC/protobuf) or `http` (HTTP/JSON, `application/json`). | | | `telemetry.logPrompts` | boolean | Whether or not to include user prompt content and API request/response text in the logs. | | | `telemetry.userId` | string | Stable end-user identifier written to GenAI spans as the ARMS extension `gen_ai.user.id`. Prefer a pseudonymous value. Do not set a process-wide value for a shared multi-user daemon or channel instance. | | | `telemetry.includeSensitiveSpanAttributes` | boolean | When enabled, attaches verbatim user prompts, system prompts, tool inputs/outputs, and model responses to native OTel span attributes (in addition to log-to-span bridge spans). ⚠️ Streams sensitive data — file contents, shell commands, conversation history — to your OTLP backend. | `false` | @@ -832,7 +833,7 @@ For authentication-related variables (like `OPENAI_*`) and the recommended `.qwe | `QWEN_TELEMETRY_ENABLED` | Set to `true` or `1` to enable telemetry. Any other value is treated as disabling it. | Overrides the `telemetry.enabled` setting. | | `QWEN_TELEMETRY_TARGET` | Sets an informational label for the telemetry destination (`local` or `gcp`). Does not control routing; use `QWEN_TELEMETRY_OTLP_ENDPOINT` or `QWEN_TELEMETRY_OUTFILE` to configure where data is sent. | Overrides the `telemetry.target` setting. | | `QWEN_TELEMETRY_OTLP_ENDPOINT` | Sets the OTLP endpoint for telemetry. | Overrides the `telemetry.otlpEndpoint` setting. | -| `QWEN_TELEMETRY_OTLP_PROTOCOL` | Sets the OTLP protocol (`grpc` or `http`). | Overrides the `telemetry.otlpProtocol` setting. | +| `QWEN_TELEMETRY_OTLP_PROTOCOL` | Sets the OTLP exporter protocol: `grpc` (gRPC/protobuf) or `http` (HTTP/JSON, `application/json`). | Overrides the `telemetry.otlpProtocol` setting. | | `QWEN_TELEMETRY_LOG_PROMPTS` | Set to `true` or `1` to enable or disable logging of user prompts. Any other value is treated as disabling it. | Overrides the `telemetry.logPrompts` setting. | | `QWEN_TELEMETRY_USER_ID` | Sets a stable end-user identifier on interaction, LLM, Tool, and Agent spans as `gen_ai.user.id`. Prefer a pseudonymous value. | Overrides `telemetry.userId` after trimming. A blank value falls back to settings. This is process-wide and must not be used as per-request identity in a shared multi-user process. | | `QWEN_TELEMETRY_INCLUDE_SENSITIVE_SPAN_ATTRIBUTES` | Set to `true` or `1` to attach verbatim user prompts, system prompts, tool I/O, and model responses to native OTel span attributes (and keep `function_args`, `error`, `error.message`, and `error_message` on log-to-span bridge spans, plus `prompt` / `request_text` / `response_text` when `telemetry.logPrompts` is also enabled). Any other value disables it. | Overrides the `telemetry.includeSensitiveSpanAttributes` setting. ⚠️ Streams sensitive data to your OTLP backend. | diff --git a/website/content/en/users/extension/mods.md b/website/content/en/users/extension/mods.md index 2292a416f9a..0d1c6b0987c 100644 --- a/website/content/en/users/extension/mods.md +++ b/website/content/en/users/extension/mods.md @@ -4,7 +4,11 @@ Qwen Code can run a small subset of the Claude Code Mod API in headless and inte This feature is opt-in and requires a trusted workspace and an enabled Claude or native Qwen extension. Agent Plugins v1 packages continue to ignore hooks. -Add this setting to your Qwen settings: +In the Ink or OpenTUI terminal, open `/settings`, search for **Mods**, and turn on +**Mods (Experimental)**. The setting is disabled by default. Changes take effect +after restarting Qwen Code; the current session keeps its startup state. + +You can also configure the same setting directly, including for headless use: ```json { @@ -12,6 +16,11 @@ Add this setting to your Qwen settings: } ``` +Set `experimental.mods` to `false` and restart to disable Mod loading. The switch +does not install or enable extensions. With no Mod extensions installed, enabling +it leaves the session running normally. Existing workspace trust and host +restrictions still apply. + Create a plugin directory with these files: ```text @@ -85,11 +94,29 @@ the current Mod command; the next command waits for worker cancellation to settle. Clear, resume and branch retain the loaded modules and their variables, send `session.end` for the replaced session, and do not repeat `session.start`. -Interactive support currently covers commands, lifecycle and logs. Explicit -`tool.call`, `tool.check` and `tool.*` registrations reject initialization before -a model request. Broad wildcard or negated patterns that also match tool events -receive a visible diagnostic and apply only to supported events. Tool -interception below is available only in headless sessions. +Interactive tool middleware runs for model-initiated tools in the local Ink and +OpenTUI main session. Explicit tool subscriptions and matching wildcard patterns +use the same tool protocol as headless sessions. Client-initiated slash-command +tools keep their native path and do not trigger a model continuation. + +A synthetic result or denial completes the logical call without inventing a +native invocation. Every real attempt retains its execution facts and write +observations, even when a Mod selects an earlier result or withholds a later +result. The model and recording receive one final result after Mod unwind. +Native permission denials and required approvals remain authoritative. + +The 60-second root wall-clock limit still includes native execution and human +approval waiting. Expiry cancels outstanding work and drains it before releasing +the queue; old approvals cannot start a new execution. Esc and accepted session +switches also revoke the old session's queued tools and approval callbacks. + +Tool middleware uses one native scheduler batch per attempt, so classic +PostToolBatch hooks run for each real attempt, never for synthetic results or +logical aggregation. Each attempt validates independently; the native scheduler's +three-identical-validation-failures diagnostic does not accumulate across Mod +attempts. Invalid inputs remain rejected and the 64-attempt limit remains. +Sessions without tool middleware retain their existing scheduler, parallelism +and validation diagnostic behavior. If Mod initialization fails in an interactive terminal, the error remains visible and the input stays available for the built-in `/help` and `/quit` commands. @@ -105,7 +132,7 @@ this recovery exception. | Entry | `register(on, options)`; async registration is awaited; typed non-secret options are copied and deeply frozen | | Lifecycle | All modules register before the initial `session.start` chain; `session.end` on exit and conversation replacement; clear/resume do not reload modules | | Commands | Register during `session.start` using name, description and optional argumentHint; intercept registered Mod commands across plugins; return `{text}` or `{}` | -| Tools | In headless sessions only, intercept existing main-session tools with `tool.call`; inspect and restrict native permission decisions with `tool.check` | +| Tools | In headless and local terminal model turns, intercept existing main-session tools with `tool.call`; inspect and restrict native permission decisions with `tool.check` | | Middleware | Patterns and recursive matchers; explicit, repeatable `next(e)`; `.catch` and per-plugin failure recovery; frozen input and host identity | | API events | `command.register` and `ui.log` calls pass through the middleware chain with host-supplied origin | | Logging | `$.ui.log` returns void and queues delivery; transcript logs use stderr in text mode or `system/ui_log` in JSON modes; debug logs stay in the debug logger; neither becomes model input | @@ -172,8 +199,8 @@ export function register(on) { Every ordinary `tool.call` next starts a fresh native tool attempt: validation, classic hooks, permissions, approvals and invocation guards all still apply. -Additional attempts count against `--max-tool-calls`; catch replay adds no -attempt. Sessions with tool middleware serialize tool calls, including parallel +In headless runs, additional attempts count against `--max-tool-calls`; catch replay adds no +attempt. Interactive terminals retain their existing usage counters and do not add a run-budget setting. Sessions with tool middleware serialize tool calls, including parallel next calls, because rewritten arguments may introduce side effects. One root call permits at most 64 native attempts and 32 outstanding host operations. The 60-second root limit includes native execution and time spent waiting for diff --git a/website/content/en/users/features/channels/_meta.ts b/website/content/en/users/features/channels/_meta.ts index ac6960d2dda..cdf8e71d880 100644 --- a/website/content/en/users/features/channels/_meta.ts +++ b/website/content/en/users/features/channels/_meta.ts @@ -11,4 +11,5 @@ export default { gitlab: 'GitLab', plugins: 'Plugins', 'email': 'Email', + 'wecom-callback': 'WeCom Callback', }; diff --git a/website/content/en/users/features/channels/overview.md b/website/content/en/users/features/channels/overview.md index de060e969ac..4252626f15d 100644 --- a/website/content/en/users/features/channels/overview.md +++ b/website/content/en/users/features/channels/overview.md @@ -17,7 +17,7 @@ All channels share one agent process with isolated sessions per user. Each chann ## Quick Start -1. Set up a bot or authenticated workspace account (see channel-specific guides: [Telegram](./telegram), [WeChat](./weixin), [QQ Bot](./qqbot), [DingTalk](./dingtalk), [DingTalk Workspace](./dws), [WeCom](./wecom), [Feishu](./feishu), [GitHub](./github), [Email](./email)) +1. Set up a bot or authenticated workspace account (see channel-specific guides: [Telegram](./telegram), [WeChat](./weixin), [QQ Bot](./qqbot), [DingTalk](./dingtalk), [DingTalk Workspace](./dws), [WeCom](./wecom), [WeCom Callback](./wecom-callback), [Feishu](./feishu), [GitHub](./github), [Email](./email)) 2. Add the channel configuration to `~/.qwen/settings.json` 3. Run `qwen channel start` to start all channels, or `qwen channel start ` for a single channel @@ -51,8 +51,8 @@ Channels are configured under the `channels` key in `settings.json`. Each channe | Option | Required | Description | | ------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `type` | Yes | Channel type: `telegram`, `weixin`, `qq`, `dingtalk`, `dws`, `wecom`, `feishu`, `github`, `gitlab`, `email`, or a custom type from an extension (see [Plugins](./plugins)) | -| `token` | Telegram | Bot token. Supports `$ENV_VAR` syntax to read from environment variables. Not needed for WeChat, DingTalk, WeCom, or Feishu | +| `type` | Yes | Channel type: `telegram`, `weixin`, `qq`, `dingtalk`, `dws`, `wecom`, `wecom-callback`, `feishu`, `github`, `gitlab`, `email`, or a custom type from an extension (see [Plugins](./plugins)) | +| `token` | See description | Required for Telegram (bot token) and WeCom Callback (verification token). Supports `$ENV_VAR` syntax. See [WeCom Callback](./wecom-callback) for application credentials | | `clientId` | DingTalk, Feishu | DingTalk AppKey or Feishu App ID. Supports `$ENV_VAR` syntax | | `clientSecret` | DingTalk, Feishu | DingTalk AppSecret or Feishu App Secret. Supports `$ENV_VAR` syntax | | `botId` | WeCom | WeCom intelligent robot Bot ID. Supports `$ENV_VAR` syntax. See [WeCom](./wecom) | diff --git a/website/content/en/users/features/channels/wecom-callback.md b/website/content/en/users/features/channels/wecom-callback.md new file mode 100644 index 00000000000..a75c4a8d003 --- /dev/null +++ b/website/content/en/users/features/channels/wecom-callback.md @@ -0,0 +1,107 @@ +# WeCom Self-Built Application Callback + +Use `wecom-callback` to chat with Qwen Code through a WeCom self-built application +(企业微信自建应用). It receives encrypted HTTP callbacks and sends text replies +through the application API. For intelligent robots with a Bot ID and Secret, +use the separate [WeCom WebSocket channel](./wecom). + +## Prerequisites + +- A WeCom administrator and a self-built application with permission to receive + messages and send application messages. +- The enterprise Corp ID, application Agent ID and Secret, and the callback Token + and EncodingAESKey configured under the application's message receiving settings. +- A public HTTPS callback URL forwarded to the local listener. Configure the + application's trusted outbound IPs and visible member range as required by + your tenant. Keep the Qwen host's clock synchronized. + +See the official [callback encryption](https://developer.work.weixin.qq.com/document/path/90968) +and [application message API](https://developer.work.weixin.qq.com/document/path/90236) +documentation for platform requirements. + +## Configuration + +Add a named channel to `~/.qwen/settings.json`: + +```json +{ + "channels": { + "my-wecom-app": { + "type": "wecom-callback", + "corpId": "$WECOM_CORP_ID", + "agentId": "$WECOM_AGENT_ID", + "corpSecret": "$WECOM_APP_SECRET", + "token": "$WECOM_CALLBACK_TOKEN", + "encodingAESKey": "$WECOM_ENCODING_AES_KEY", + "webhookHost": "127.0.0.1", + "webhookPort": 8645, + "privatePolicy": "allowlist", + "allowedUsers": ["zhangsan"], + "sessionScope": "user", + "groupPolicy": "disabled", + "cwd": "/path/to/your/project" + } + } +} +``` + +Set the five environment variables in the process that starts Qwen Code. Agent +ID is a positive integer **string**, such as `"1000002"`. EncodingAESKey is the +43-character key from the WeCom console. The callback Token is distinct from +the application Secret and the API access token; Qwen obtains API access tokens +automatically. The three secret fields are masked in channel management. + +The listener defaults to `127.0.0.1:8645`, with the fixed path `/wecom/callback`. +Forward your public URL to that path, preserving its query string and body. For +container deployments, bind to a reachable interface explicitly. Use separate +channel names and ports for multiple applications. + +## Running and platform verification + +1. Run `qwen channel start my-wecom-app`. +2. In the self-built application's message receiving settings, enter the public + HTTPS URL ending in `/wecom/callback`, Token, and EncodingAESKey. Save the + settings; Qwen decrypts and returns the verification challenge. +3. Add a test member to the application's visible range and put their WeCom + User ID in `allowedUsers`. Send a text message to the application. Expect a + text reply from that application. +4. Send from a visible member outside `allowedUsers`. Expect no agent task or + reply. Qwen still acknowledges the callback to avoid platform retries. +5. Request a long reply containing Chinese text and emoji. Expect ordered text + messages, each within the platform's 2048-byte limit. +6. Stop the channel and confirm the local listener closes. + +If URL verification fails, check the reverse proxy, callback path, clock, Corp +ID, Token and EncodingAESKey. If callbacks arrive but replies fail, check the +Agent ID, application Secret, application visibility and trusted outbound IPs. +Application secrets and callback keys must never be posted in logs or issues. + +## Supported behavior and limits + +- Text private messages and text replies. Shared access policies, pairing, + commands, permissions and sessions apply. Use member User IDs for access rules. +- Proactive messages can target individual users. Groups, threads, media, + application events and streaming replies are not supported. +- Callbacks are authenticated, decrypted and acknowledged before agent execution + finishes. Replies use the application message API, rather than the callback's + synchronous response. Admitted message IDs are suppressed in memory for at + least five minutes and until their signed timestamp window expires; requests + outside the five-minute signature timestamp window fail. Rejected senders do + not consume retry-cache capacity. +- In-flight work is not durably queued. Restarting the process after an + acknowledgement can lose that task, and restarting clears the retry cache. + +## Testing without an enterprise tenant + +From `packages/channels/wecom`, run: + +```bash +npx vitest run src/wecom-callback-crypto.test.ts src/wecom-callback-adapter.test.ts src/callback-plugin.test.ts src/WeComAdapter.test.ts +``` + +The tests use an independent published SDK encryption fixture, real loopback HTTP +requests, the shared inbound access checks and mocked application API responses. +They cover handshake verification, authentication failures, retries, recipient +isolation, Unicode splitting and shutdown. They do not need real credentials or +send messages to WeCom. The platform verification steps above are still needed +to validate a real tenant's public URL, permissions and network configuration. diff --git a/website/content/en/users/features/channels/wecom.md b/website/content/en/users/features/channels/wecom.md index ae4a76ecda2..c431d04ae3c 100644 --- a/website/content/en/users/features/channels/wecom.md +++ b/website/content/en/users/features/channels/wecom.md @@ -2,6 +2,9 @@ This guide covers setting up Qwen Code with a WeCom intelligent robot (企业微信智能机器人). +For a self-built application using Corp ID, Agent ID, Token and EncodingAESKey, +use the separate [WeCom Callback channel](./wecom-callback). + ## Prerequisites - A WeCom organization account diff --git a/website/content/en/users/qwen-serve.md b/website/content/en/users/qwen-serve.md index 2b17315cbb7..2eaa9e151f9 100644 --- a/website/content/en/users/qwen-serve.md +++ b/website/content/en/users/qwen-serve.md @@ -119,6 +119,25 @@ curl http://127.0.0.1:4170/daemon/status The `workspaceCwd` field surfaces the primary compatibility workspace so clients can intentionally omit `cwd` on `POST /session`. Current clients should select a trusted entry from `workspaces[]` and send that entry's `cwd` when targeting a runtime explicitly. The `limits.maxPendingPromptsPerSession` field advertises the active per-session prompt admission cap; `null` means the cap is disabled. `limits.maxTotalSessions` advertises the optional daemon-wide fresh-session cap; `null` means unlimited. +### Inspect and control a local daemon + +`qwen serve` publishes a local discovery record for each live listener, so a second terminal does not have to parse stdout or search `ps`: + +```bash +qwen daemon start --port 4170 # detached; waits for the discovery record +qwen daemon status # ports, PIDs, workspaces, and health +qwen daemon status --json # one JSON object per daemon +qwen daemon status --detail # version, uptime, sessions, and issues +qwen daemon logs --port 4170 -f # follow the recorded log file +qwen daemon stop --port 4170 # SIGTERM the recorded process +``` + +Records live under the runtime directory (`$QWEN_RUNTIME_DIR`, `$QWEN_HOME`, or `~/.qwen`) in `daemon/daemon-.json`. They are `0600` on POSIX and never contain the bearer token. A record left by a crashed daemon is reported as `stale`; `status --prune` or `stop` removes it. `stop` sends `SIGTERM` only after the listener answers `/health` or its PID is still alive; `--force` adds `SIGKILL` after the graceful timeout. When more than one daemon is registered, pass `--port`; `stop` also accepts `--all`. + +`status --detail` asks the daemon for its own status: version, uptime, active sessions, and issue count. It sends `QWEN_SERVER_TOKEN` only when the record says the daemon uses a bearer and the recorded process is alive, addressable, and started when the record says it did; a record whose recorded PID namespace this process cannot address is never sent the bearer. Without a token, a daemon that requires a bearer reports that authentication is required. + +`qwen daemon start` spawns `qwen serve` detached, waits for its discovery record, and prints the bound URL, PID, log path, and stop command. It drops the launching session's identifiers and private capability bindings so the child is a fresh daemon, keeps the operator's model/auth environment, and passes `--no-token-qr` so the startup log cannot contain a token. Workspaces are canonicalized, relative runtime directories are pinned to the launching directory before spawn, and Windows drive-relative roots are resolved against the launching drive. A non-loopback bind requires `QWEN_SERVER_TOKEN`; use `qwen serve` directly if you want an ephemeral token. The startup log is `0600` and is removed after a successful start. `--wait` waits until the runtime is serving (no `daemon_runtime_starting` issue and not `error`); a timeout keeps the startup log and exits non-zero. + ### Workspace registration capacity The default registration limit is 256. Set `QWEN_SERVE_MAX_WORKSPACES=25 qwen serve --workspace /path/to/project` to lower it; accepted values are decimal integers from 1 through 256. Invalid values fail startup. This is a daemon startup setting: project `.env` and project `settings.env` cannot change it. Embedded callers may set `ServeOptions.maxRegisteredWorkspaces`, which takes precedence over the startup environment. diff --git a/website/content/ja/developers/daemon-ui/sidebar-customization.md b/website/content/ja/developers/daemon-ui/sidebar-customization.md index 17dcc229085..1912bce2d0a 100644 --- a/website/content/ja/developers/daemon-ui/sidebar-customization.md +++ b/website/content/ja/developers/daemon-ui/sidebar-customization.md @@ -4,14 +4,14 @@ ## サイドバーの有効化 -サイドバーは**デフォルトで無効**です。`sidebar` prop を渡して有効化します。 +サイドバーはデフォルトで**Homeのみ**です: 下部に折りたたみコントロール付きの300pxセッションカラムです。`sidebar` を省略するか、`true` または `{}` を渡すとこのデフォルトが使用されます。非表示にするには `sidebar={false}` または `{ enabled: false }` を渡します。 ```tsx import { WebShellWithProviders } from '@qwen-code/web-shell'; ; @@ -19,26 +19,9 @@ import { WebShellWithProviders } from '@qwen-code/web-shell'; ## レイアウト概要 -``` -┌─────────────────────────────────────┐ -│ ① ブランディング (topRow) │ ✅ カスタマイズ可 -├─────────────────────────────────────┤ -│ ② プライマリナビゲーション │ ✅ カスタマイズ可 -│ [+ New task] [🧩 Plugins] │ -│ [📅 Scheduled] [🎯 Goals] │ -│ [custom render...] │ -├─────────────────────────────────────┤ -│ ③ プロジェクトヘッダー │ ✅ 表示/非表示 -│ 📁 Projects ▼ [🔍] [+] │ -│ Session list entries... │ -│ 📦 Archived sessions │ -├─────────────────────────────────────┤ -│ ④ フッターアクションバー │ ✅ カスタマイズ可 -│ [⚙ Settings] v0.19 [☀] [▦] [◧] │ -├─────────────────────────────────────┤ -│ ⑤ リサイズハンドル │ ❌ カスタマイズ不可 -└─────────────────────────────────────┘ -``` +Homeのみのホストでは、ブランディング、New task、プロジェクト、アーカイブされたセッション、折りたたみコントロールが1つのカラムに表示されます。利用可能な別のプライマリエントリまたはフッターアクションを設定すると、デフォルトの300pxセカンダリカラムの横に56pxのアイコンレールが追加されます。プライマリエントリはレールに配置され、フッターアクションとバージョンは More に表示されます。カスタムレンダラーは広いHomeカラムに残ります。公開のレイアウトオプションはありません。 + +**破壊的変更:** 以前は、`sidebar` を省略するとサイドバーが非表示になり、サイドバーが有効な状態でナビゲーション/フッターのアイテムリストを省略するとすべての組み込みアイテムが公開されていました。ホストは以前の非表示デフォルトを維持するために `sidebar={false}` を渡すか、必要なエントリを明示的にリストする必要があります。スタンドアロンWeb Shellはフルメニューを明示的に設定します。`branding.hideWhenCompact` のデフォルトは `false` になりました。コンパクトドロワーで非表示のブランドを維持するには `true` に設定します。 ## カスタマイズ可能な領域 @@ -47,7 +30,7 @@ import { WebShellWithProviders } from '@qwen-code/web-shell'; ```ts interface WebShellSidebarBranding { render?: () => ReactNode; // replace the entire branding row - hideWhenCompact?: boolean; // hide when sidebar is collapsed (default: true) + hideWhenCompact?: boolean; // hide branding in the compact drawer (default: false) } ``` @@ -56,9 +39,9 @@ interface WebShellSidebarBranding { | `undefined`(デフォルト) | 解決されたブランド: `brand` prop → デーモン `GET /brand` → 組み込みの Qwen ロゴ + "Qwen Code" テキスト | | `false` | ブランディング行全体を非表示 | | `{ render: () => }` | カスタムコンテンツで全面置換 | -| `{ hideWhenCompact: false }` | 折りたたみアイコンレールモードでもブランディングを表示 | +| `{ hideWhenCompact: false }` | コンパクトドロワーでもブランディングを表示(デフォルト) | -デフォルト行はデータ駆動であり、固定ではありません。`ui.brand` 設定を配信するデーモンはテキストを変更し、マークを差し替えます。また、埋め込みホストはシェルコンポーネントの `brand` prop で両方をオーバーライドできます(`onBrandResolved` はホスト自身のクローム用の結果を報告します)。`branding.render` は引き続き最高優先度のオーバーライドであり、prop やデーモンで解決された値に優先します。 +デフォルト行はデータ駆動であり、固定ではありません。`ui.brand` 設定を配信するデーモンはテキストを変更し、マークを差し替えます。また、埋め込みホストはシェルコンポーネントの `brand` prop で両方をオーバーライドできます(`onBrandResolved` はホスト自身のクローム用の結果を報告します)。`branding.render` は引き続き最高優先度のオーバーライドであり、prop やデーモンで解決された値に優先します(以前と同様です)。 ```tsx sidebar={{ @@ -79,27 +62,31 @@ sidebar={{ type WebShellSidebarPrimaryNavItem = | 'newTask' // ✏️ New Task button | 'plugins' // 🧩 Plugins button + | 'channels' // Channel sessions and settings + | 'live' // Live sessions and settings; requires showLive + | 'workflows' // Requires workflow support + | 'managed' // Requires a managed-agent provider | 'scheduledTasks' // 📅 Scheduled Tasks button | 'goals'; // 🎯 Goals button interface WebShellSidebarPrimaryNavOptions { - items?: readonly WebShellSidebarPrimaryNavItem[]; // which built-in buttons to show (default: all) + items?: readonly WebShellSidebarPrimaryNavItem[]; // which built-in buttons to show (default: ['newTask']) render?: () => ReactNode; // additional custom content after built-in buttons } ``` プライマリナビゲーション領域には、`items` で制御される組み込みボタンが含まれます。 -- `items` を指定しない場合、すべてのボタンが表示される -- `items` を指定した場合、リストされたボタンのみ表示される -- カスタムコンテンツは `render()` で組み込みボタンの後に追加できる +- `items` を指定しない場合は New task のみ表示されます。Home は常に存在します +- `items` を指定した場合、リストされたボタンのみ表示されます +- カスタムコンテンツは `render()` で組み込みボタンの後に追加できます -| 値 | 効果 | -| ------------------------------------------ | ------------------------------------------ | -| `undefined`(デフォルト) | すべての組み込みボタンを表示 | -| `{ items: ['plugins'] }` | Plugins ボタンのみ | -| `{ items: ['plugins', 'scheduledTasks'] }` | Plugins + Scheduled Tasks | -| `{ items: [], render: () => ... }` | 組み込みをすべて非表示、カスタムコンテンツのみ | +| 値 | 効果 | +| ------------------------------------------ | --------------------------------------------------- | +| `undefined`(デフォルト) | New task を含む Home | +| `{ items: ['plugins'] }` | Home と Plugins。New task ボタンなし | +| `{ items: ['plugins', 'scheduledTasks'] }` | Home + Plugins + Scheduled Tasks | +| `{ items: [], render: () => ... }` | カスタムコンテンツを含む Home。組み込みプライマリボタンなし | ```tsx sidebar={{ @@ -119,26 +106,30 @@ sidebar={{ ```ts type WebShellSidebarFooterItem = | 'settings' // ⚙ Settings panel + | 'update' // Update action when supported + | 'localFiles' // Local-files bridge when available + | 'desktopRelay' // Use this computer through the existing desktop relay + | 'workspacesOverview' // Workspace management when unlocked | 'version' // version label (e.g. "v0.19.10") | 'theme' // ☀/🌙 light/dark toggle | 'sessionsOverview' // ▦ session overview panel - | 'splitView' // ◧ split view (large screens only) + | 'splitView' // ◧ split view (shell containers at least 1024px wide) | 'daemonStatus' // 📊 daemon status panel | 'collapse'; // ◁/▷ collapse/expand toggle interface WebShellSidebarFooterOptions { - items?: readonly WebShellSidebarFooterItem[]; // which built-in items to show (default: all) - render?: () => ReactNode; // custom content rendered on the left side, before built-in items + items?: readonly WebShellSidebarFooterItem[]; // which built-in items to show (default: ['collapse']) + render?: () => ReactNode; // custom content in the Home footer } ``` -| 値 | 効果 | -| -------------------------------------------- | ----------------------- | -| `undefined`(デフォルト) | すべてのアイテムを表示 | -| `false` | フッターは非表示。モバイルドロワーはクローズコントロールのみ保持 | +| 値 | 効果 | +| -------------------------------------------- | ----------------------------------------------------------------------- | +| `undefined`(デフォルト) | 折りたたみ/展開のみ | +| `false` | フッター非表示。モバイルドロワーはクローズコントロールのみ保持 | | `{ items: ['settings', 'theme', 'collapse'] }` | リストされたアイテムのみ表示。モバイルドロワーは常にクローズコントロールを保持 | -フッターは狭い幅に自動適応します。一定の閾値以下ではラベルが非表示になり、バージョン表示も削除されます。 +レールレイアウトでは、フッターアクションとバージョンは More に表示され、折りたたみ/展開コントロールのみがレール下部に残ります。Homeのみのモードでは、既存のコンパクトフッター動作が維持されます。 ```tsx sidebar={{ @@ -146,7 +137,7 @@ sidebar={{ }} ``` -`render()` によるカスタムコンテンツは、フッターの左側、組み込みアイテムより前に表示されます。 +`render()` によるカスタムコンテンツは、広いHomeフッターに表示されます。 ```tsx sidebar={{ @@ -161,13 +152,13 @@ sidebar={{ }} ``` -**注:** `'scheduledTasks'` と `'goals'` はプライマリナビゲーション領域(②)に移動されており、デフォルトで表示されます。これらは `footer.items` ではなく `primaryNav.items` で制御されます。 +**注:** `'scheduledTasks'` と `'goals'` はプライマリナビゲーション領域(②)に移動されており、埋め込みホストでは明示的な設定が必要です。これらは `footer.items` ではなく `primaryNav.items` で制御されます。 ### その他のトップレベルオプション ```ts interface WebShellSidebarOptions { - enabled?: boolean; // show/hide sidebar (default: true when passed) + enabled?: boolean; // show/hide sidebar (default: true, including when sidebar is omitted) defaultCollapsed?: boolean; // initial collapsed state (persisted in localStorage) showCompactToggle?: boolean; // show the collapse button in the chat area (default: true) showSessionSourceSwitch?: boolean; // show the Tasks/Channels switch (default: true) @@ -190,7 +181,7 @@ sidebar={{ }} ``` -これにより Tasks/Channels スイッチが削除され、アクティブ、アーカイブ、プライマリ、セカンダリのすべてのセッションクエリが `sourceType: "default"` に固定されます。このオプションを省略すると、現在のスイッチとチャネルセッションへのアクセスは変更されません。 +これにより Tasks/Channels スイッチが削除され、アクティブ、アーカイブ、プライマリ、セカンダリのすべてのセッションクエリが `sourceType: "default"` に固定されます。このオプションを省略すると、チャネルセッションへのアクセスが維持されます。専用 Channels レールエントリが利用可能な場合、それはソースタブを置き換えます。そうでない場合、タブは残ります。 ### Live コンバセーション — `showLive` @@ -201,9 +192,12 @@ Live コンバセーションは、デフォルトでは埋め込みホストか ```tsx sidebar={{ showLive: true, + primaryNav: { items: ['newTask', 'live'] }, }} ``` +`live` プライマリエントリがない場合、`showLive: true` は Home の既存 Live グループと Experimental 配下の設定を保持します。エントリがある場合、Live は独自のセッションカラムと設定ページを開きます。 + ### ③ プロジェクトヘッダー — `hideProjectHeader` 「Projects」ヘッダー行(折りたたみトグル、検索アイコン、ワークスペース追加ボタンを含む行)の表示/非表示を制御します。デフォルトは `false`(表示)です。 @@ -284,8 +278,9 @@ sidebar={{ ### ⑤ リサイズハンドル -- 右端のドラッグハンドルでサイドバー幅をリサイズ -- 幅は localStorage に永続化される +- サイドバー幅をリサイズするための右端のドラッグハンドル +- 幅は localStorage にサイドバーの合計幅として永続化されます。デフォルトは Homeのみで300px、レール付きで356pxです。復元された幅は Homeのみで最小220px、レール付きで276pxにクランプされます。古い値が56px blindly 増加されることはありません。折りたたみ閾値以下にドラッグしてもサイドバーは折りたたまれます。 +- 環境パネルのドックブレークポイントはレールの56pxを無視します(メッセージ領域がそれらを譲るため)、パネルは Homeのみのサイドバーと同じウィンドウ幅でドッキングを続けます(例: 1440pxウィンドウ)。 - 設定不可 ## ランタイム動作 prop @@ -303,14 +298,16 @@ sidebar={{ ## 折りたたみ状態とモバイル状態 -| 状態 | 動作 | -| ---------- | ------------------------------------------------------- | -| 展開状態 | テキストラベル付きの完全なサイドバー | -| 折りたたみ | アイコンレールモード(ロゴ、ペンアイコン、アクションアイコンのみ) | -| モバイル | ドロワーはコンテナの 70% を使用(幅制限あり)。バックドロップとフッターのクローズコントロール付き | +| 状態 | 動作 | +| ---------- | ---------------------------------------------------------------------------------------------------------------- | +| 展開状態 | テキストラベル付きの完全なサイドバー | +| 折りたたみ | Homeのみ: 既存の56pxストリップとホバーセッション。レール付き: 56pxプライマリレールのみ | +| モバイル | ドロワーは幅制限内でコンテナの70%を使用し、バックドロップと所有カラム下部のクローズコントロール付き | 折りたたみ状態は `qwen-code-web-shell-sidebar-collapsed` キーで `localStorage` に永続化されます。 +サイドバー、そのコンパクトドロワー、および空のチャットのウェルカムクロームはシェルコンテナ幅(760pxでコンパクト)に従います。チャットメッセージコンテンツはビューポートベースのブレークポイントを保持します。スプリット可用性は1024pxを使用し、スプリットサイドバールームは1200pxを使用します。キーボード折りたたみは、非表示のセカンダリカラムからフォーカスをレールコントロールに移動します。 + リサイズされたデスクトップ幅は展開レイアウトでのみ復元されます。モバイルドロワーを開閉しても、その幅や永続化されたデスクトップの折りたたみ設定は上書きされません。 ## ソースコードの場所 @@ -322,5 +319,5 @@ sidebar={{ | WorkspaceSection | `packages/web-shell/client/components/sidebar/WorkspaceSection.tsx` | | Sidebar styles | `packages/web-shell/client/components/sidebar/WebShellSidebar.module.css` | | App integration | `packages/web-shell/client/App.tsx`(`WebShellSidebar` を検索) | -| Entry point (dev) | `packages/web-shell/client/main.tsx`(`sidebar: { enabled: true, showLive: true }`) | +| Entry point (dev) | `packages/web-shell/client/main.tsx`(明示的なプライマリおよびフッターアイテムリスト) | diff --git a/website/content/ja/users/features/approval-mode.md b/website/content/ja/users/features/approval-mode.md index b58fd3fc6da..f2280ee9046 100644 --- a/website/content/ja/users/features/approval-mode.md +++ b/website/content/ja/users/features/approval-mode.md @@ -245,7 +245,7 @@ settings.json で自然言語のヒントを使用して、分類器の判断を - **ハードルールは有効なまま**: `permissions.deny` ルールは、分類器が実行される前にアクションをブロックします。 - **過度に広い許可ルールは Auto Mode 中に削除される**: たとえば、`permissions.allow: ["Bash"]` (すべてのシェルコマンドを許可) は分類器を無効化します。Auto Mode に入ると、分類器が機能できるようにこのようなルールは一時的に無効化されます。Auto Mode を離れるとルールは復元されます。ディスク上の設定が変更されることはありません。 - **Fail-closed**: 分類器 API に到達できない場合、アクションは許可されるのではなくブロックされます。2 回連続で利用できない呼び出しがあった後、次のツール呼び出しは手動承認にフォールバックします。 -- **ループガード**: 3 回連続でポリシーによってブロックされた後、次の呼び出しも手動承認にフォールバックし、エージェントが行き詰まったアプローチでループしないようにします。 +- **ループガード**: 3 回連続でブロックされると — 分類器ポリシーによるブロックと決定論的な破壊的コマンドによるブロックの両方がカウントされます — 3 回目のブロックに達した呼び出しも手動承認にフォールバックし、エージェントが行き詰まったアプローチでループしないようにします。 ### 例 diff --git a/website/content/ja/users/features/channels/wecom-callback.md b/website/content/ja/users/features/channels/wecom-callback.md new file mode 100644 index 00000000000..addfd8b8f22 --- /dev/null +++ b/website/content/ja/users/features/channels/wecom-callback.md @@ -0,0 +1,69 @@ +# WeCom 自作アプリケーションコールバック + +`wecom-callback` を使用して、WeCom 自作アプリケーション(企业微信自建应用)を通じて Qwen Code とチャットします。暗号化された HTTP コールバックを受信し、アプリケーション API を通じてテキスト返信を送信します。Bot ID と Secret を持つインテリジェントロボットの場合は、別の [WeCom WebSocket チャネル](./wecom) を使用してください。 + +## 前提条件 + +- WeCom 管理者、およびメッセージの受信とアプリケーションメッセージの送信権限を持つ自作アプリケーション。 +- 企業 Corp ID、アプリケーション Agent ID と Secret、およびアプリケーションのメッセージ受信設定で設定されたコールバック Token と EncodingAESKey。 +- ローカルリスナーに転送されるパブリック HTTPS コールバック URL。テナントの要件に従って、アプリケーションの信頼できる送信元 IP と表示メンバー範囲を設定します。Qwen ホストの時計を同期し続けてください。 + +プラットフォームの要件については、公式の [コールバック暗号化](https://developer.work.weixin.qq.com/document/path/90968) および [アプリケーションメッセージ API](https://developer.work.weixin.qq.com/document/path/90236) のドキュメントを参照してください。 + +## 設定 + +`~/.qwen/settings.json` に名前付きチャネルを追加します: + +```json +{ + "channels": { + "my-wecom-app": { + "type": "wecom-callback", + "corpId": "$WECOM_CORP_ID", + "agentId": "$WECOM_AGENT_ID", + "corpSecret": "$WECOM_APP_SECRET", + "token": "$WECOM_CALLBACK_TOKEN", + "encodingAESKey": "$WECOM_ENCODING_AES_KEY", + "webhookHost": "127.0.0.1", + "webhookPort": 8645, + "privatePolicy": "allowlist", + "allowedUsers": ["zhangsan"], + "sessionScope": "user", + "groupPolicy": "disabled", + "cwd": "/path/to/your/project" + } + } +} +``` + +Qwen Code を起動するプロセスで 5 つの環境変数を設定します。Agent ID は正の整数の**文字列**です(例: `"1000002"`)。EncodingAESKey は WeCom コンソールの 43 文字のキーです。コールバック Token はアプリケーション Secret および API アクセストークンとは異なります。Qwen は API アクセストークンを自動的に取得します。3 つのシークレットフィールドはチャネル管理でマスクされます。 + +リスナーのデフォルトは `127.0.0.1:8645` で、固定パスは `/wecom/callback` です。パブリック URL をそのパスに転送し、クエリ文字列と本文を保持します。コンテナデプロイメントの場合は、到達可能なインターフェースに明示的にバインドします。複数のアプリケーションには別のチャネル名とポートを使用します。 + +## 実行とプラットフォームの検証 + +1. `qwen channel start my-wecom-app` を実行します。 +2. 自作アプリケーションのメッセージ受信設定で、`/wecom/callback` で終わるパブリック HTTPS URL、Token、および EncodingAESKey を入力します。設定を保存すると、Qwen が復号化して検証チャレンジを返します。 +3. テストメンバーをアプリケーションの表示範囲に追加し、その WeCom User ID を `allowedUsers` に入れます。アプリケーションにテキストメッセージを送信します。そのアプリケーションからテキスト返信が返ってくることを確認します。 +4. `allowedUsers` の外にある表示メンバーから送信します。エージェントタスクや返信は返ってこないことを確認します。Qwen はプラットフォームの再試行を回避するためにコールバックに確認応答します。 +5. 中国語テキストと絵文字を含む長い返信を要求します。プラットフォームの 2048 バイト制限内の順序付けされたテキストメッセージが返ってくることを確認します。 +6. チャネルを停止し、ローカルリスナーが閉じることを確認します。 + +URL 検証が失敗した場合は、リバースプロキシ、コールバックパス、時計、Corp ID、Token、および EncodingAESKey を確認します。コールバックは届くが返信が失敗する場合は、Agent ID、アプリケーション Secret、アプリケーションの可視性、および信頼できる送信元 IP を確認します。アプリケーションシークレットとコールバックキーは、ログや issue に絶対に投稿しないでください。 + +## サポートされる動作と制限 + +- テキストのプライベートメッセージとテキスト返信。共有アクセスポリシー、ペアリング、コマンド、権限、およびセッションが適用されます。アクセスルールにはメンバー User ID を使用します。 +- プロアクティブメッセージは個々のユーザーをターゲットにできます。グループ、スレッド、メディア、アプリケーションイベント、およびストリーミング返信はサポートされていません。 +- コールバックはエージェントの実行が完了する前に認証、復号化、および確認応答されます。返信はコールバックの同期応答ではなく、アプリケーションメッセージ API を使用します。受け入れられたメッセージ ID は、少なくとも 5 分間、および署名付きタイムスタンプウィンドウが期限切れになるまで、メモリで抑制されます。5 分の署名タイムスタンプウィンドウ外の要求は失敗します。拒否された送信者は再試行キャッシュ容量を消費しません。 +- 処理中の作業は永続的にキューイングされません。確認応答後にプロセスを再起動すると、そのタスクが失われる可能性があり、再起動すると再試行キャッシュがクリアされます。 + +## 企業テナントなしでのテスト + +`packages/channels/wecom` から、以下を実行します: + +```bash +npx vitest run src/wecom-callback-crypto.test.ts src/wecom-callback-adapter.test.ts src/callback-plugin.test.ts src/WeComAdapter.test.ts +``` + +テストは、独立した公開 SDK 暗号化フィクスチャ、実際のループバック HTTP リクエスト、共有インバウンドアクセスチェック、およびモックされたアプリケーション API 応答を使用します。ハンドシェイク検証、認証失敗、再試行、受信者分離、Unicode 分割、およびシャットダウンをカバーします。実際の認証情報や WeCom へのメッセージ送信は必要ありません。実際のテナントのパブリック URL、権限、およびネットワーク設定を検証するには、上記のプラットフォーム検証手順が依然として必要です。 \ No newline at end of file diff --git a/website/content/ja/users/support/troubleshooting.md b/website/content/ja/users/support/troubleshooting.md index fd7675619d4..90965a8196c 100644 --- a/website/content/ja/users/support/troubleshooting.md +++ b/website/content/ja/users/support/troubleshooting.md @@ -59,6 +59,28 @@ - A: `--safe-mode` フラグを付けて Qwen Code を起動し、セッション中のすべてのカスタマイズ(コンテキストファイル、hook、extension、skill、MCP server、カスタム subagent(組み込み subagent のみ読み込み)、権限ルール、設定由来の承認モードオーバーライド、メモリ機能、サンドボックス設定)を無効にします。注: CLI フラグ `--yolo` と `--approval-mode` はセーフモードでも有効のままです。セーフモードで問題が解消する場合は、カスタマイズを 1 つずつ再度有効にして、原因を特定します。 - 例: `qwen --safe-mode` - 代替案: CLI がフラグを受け付けられない場合は、環境変数 `QWEN_CODE_SAFE_MODE=true` を設定します。 + - 注: フラグと異なり、エクスポートされた `QWEN_CODE_SAFE_MODE` は子プロセスに継承されます。そのシェルから起動された `qwen serve` デーモンやエディターは、すべてのセッションをセーフモードでホストするため、それらのセッションも抑制されます。つまり、セッションレコードを書き込まず、他のセッションからもそれらを一覧表示したりメッセージを送信したりできなくなります。デーモンに対しては、変数をエクスポートしたままにせず、アンセットしてください。 + - 注: ここでの「MCP server」とは、`settings.json` / プロジェクトの `.mcp.json` で設定されたサーバー、つまりセーフモードが隔離の対象とするローカルなアンビエント状態を指します。現在の呼び出しに対して明示的に提供する MCP server(埋め込み ACP クライアントの `session/new` の `mcpServers` や `--mcp-config`)はローカル/アンビエント状態ではなく、セーフモードでも引き続き適用されます。 + +## Attachment upload がリバースプロキシ配下で HTTP 413 を返す + +プロキシは、attachment がデーモンの 8 MiB ファイル制限内であっても、`qwen serve` に到達する前にリクエストを拒否する場合があります。デーモンの body 制限だけを上げても、プロキシの制限は変わりません。 + +`session_attachment_chunk_upload` をサポートするデーモンと TypeScript SDK/Web Shell ビルドを使用してください。512 KiB を超える attachment は、バイト数や画質を変更せずに 512 KiB リクエストでアップロードされます。古いデーモンは引き続き単一リクエストのアップロードパスを保持します。プロキシを運用している場合は、元の attachment リクエストに対応できるようにリクエスト body の制限を引き上げることもできます。制限が 512 KiB 未満のプロキシは、引き続き設定変更が必要です。 + +attachment の制限は 8 MiB のままです。アップロードがタイムアウトしたりデーモンが再起動したりした場合は、UI から attachment を再試行してください。レスポンスが失われた完了済みのアップロードはセッションの attachment に残る場合がありますが、キャンセルはすでに完了したファイルを削除しません。 + +## 一部のネットワークで接続がリセットされる(TLS スタック選択型ミドルボックス) + +- **問題:** 一部のキャリアや企業ネットワークでは、TLS ClientHello 直後に API エンドポイントへの HTTPS 接続がリセット(TCP RST)されます。Qwen Code はバジェットが尽きるまでリトライを続けます(`ECONNRESET` はリトライ可能なトランスポートエラーです)。その後、一般的な接続エラーが表示されます。一方で、同じマシンとネットワークの同じ瞬間に `curl`、Python、その他のツールは正常に動作します。 +- **原因:** 一部のミドルボックスは TLS ClientHello のフィンガープリントで接続をフィルタリングし、特定の TLS スタック世代が発信する接続をリセットします。影響を受けるリンクでは、OpenSSL 3.0 世代のスタックと Electron/BoringSSL(VS Code 拡張機能とデーモンが使用)がリセットされますが、OpenSSL 3.5 世代のスタック(現在の Node.js ビルドを含む)は正常に接続されます。 +- **解決策:** 3.5 世代の TLS スタックで接続を行います。 + - **CLI:** サポートされている Node.js ラインの最新ビルドを使用します(現在の Node 22/24/25 ビルドはいずれも 3.5 世代の OpenSSL を搭載しています)。同じメジャーバージョンの古いビルドは影響を受ける世代のままの場合があります。古いマイナーバージョンに固定するのではなく、更新してください。 + - **VS Code 拡張機能 / デーモン:** 拡張機能は Electron の TLS スタックで動作し、クライアント側で変更できません。小さなローカルリレー(3.5 世代スタック上のローカルプロセス)を実行し、モデルのベース URL オーバーライド(`settings.json` のカスタムエンドポイント、または CLI の `--openaiBaseUrl`)を介して Qwen Code をそこに指向することで、TLS 送信が新しいスタックで行われます。 +- **不安定なネットワークと区別する方法:** + - リセットは決定的です。すべての試行が即座に失敗し、リトライしても回復しません。 + - 同じマシンと瞬間に、他の TLS スタックが同じエンドポイントに対して成功します。 + - TLS バージョンの固定や DNS 解決の変更は効果がありません。 ## 一般的なエラーメッセージと解決策 @@ -99,7 +121,7 @@ - **tmux でのトラックパッドスクロールが会話のスクロールではなくプロンプト履歴を変更する** - **問題:** tmux セッションでは、トラックパッドやホイールのスクロールが、`Up Arrow` または `Down Arrow` を押した場合と同様に、以前のプロンプトを循環させることがあります。 - **原因:** tmux はホイールジェスチャを単純な矢印キーシーケンスに変換することがあります。qwen-code がそれらを受け取る時点では、実際の矢印キーの押下と区別が付きません。 - - **解決策:** スクリーンリーダーモードが無効になっている場合、`ui.useTerminalBuffer` が有効であることを確認してください。その後、`Shift+Up` / `Shift+Down` を使用するか、tmux がホイールイベントをアプリに転送する場合(`ui.mouseTracking` が必要)はマウスホイールを使用します。ホストのスクロールバックを優先する場合は、ホイールイベントに対する tmux のマウスバインディングを調整してください。 + - **解決策:** スクリーンリーダーモードが無効になっている場合、`ui.useTerminalBuffer` が有効であることを確認してください。その後、`Shift+Up` / `Shift+Down` を使用するか、tmux がホイールイベントをアプリに転送する場合(`ui.mouseTracking` が必要)はマウスホイールを使用します。composer が空の状態でプレーンな `Up Arrow` / `Down Arrow` をビューポートに渡すには、`ui.arrowKeysScrollTranscript` を `true` に設定します。入力履歴は `Ctrl+P` / `Ctrl+N` でアクセスできます。ホストのスクロールバックを優先する場合は、ホイールイベントに対する tmux のマウスバインディングを調整してください。 - **右クリックが機能しない、リンクが開かない、またはターミナルでテキストを選択できない** - **問題:** Qwen Code の実行中、ネイティブの右クリックコンテキストメニュー、OSC 8 ハイパーリンククリック、およびターミナルネイティブのテキスト選択が機能しなくなります。 diff --git a/website/content/ko/developers/tools/shell.md b/website/content/ko/developers/tools/shell.md index f4c4219a7b4..470f1c6c7dc 100644 --- a/website/content/ko/developers/tools/shell.md +++ b/website/content/ko/developers/tools/shell.md @@ -16,19 +16,19 @@ Windows에서는 명령이 `cmd.exe /c`로 실행됩니다. 다른 플랫폼에 - `command` (string, 필수): 실행할 정확한 셸 명령. - `description` (string, 선택 사항): 명령의 목적에 대한 간단한 설명으로, 사용자에게 표시됩니다. - `directory` (string, 선택 사항): 명령을 실행할 디렉토리(프로젝트 루트 기준). 제공되지 않으면 명령은 프로젝트 루트에서 실행됩니다. -- `is_background` (boolean, 필수): 명령을 백그라운드에서 실행할지 여부. 이 매개변수는 명령 실행 모드에 대한 명시적 의사 결정을 보장하기 위해 필요합니다. 개발 서버, 감시자 또는 데몬과 같이 추가 명령을 차단하지 않고 계속 실행되어야 하는 장기간 실행 프로세스의 경우 true로 설정합니다. 진행하기 전에 완료되어야 하는 일회성 명령의 경우 false로 설정합니다. +- `is_background` (boolean, 선택 사항): 명령을 백그라운드에서 실행할지 여부. 지정하지 않으면 기본값은 `false`(포그라운드 실행)입니다. 추가 명령을 차단하지 않고 계속 실행되어야 하는 개발 서버, 감시자 또는 데몬과 같은 장기간 실행 프로세스의 경우 true로 설정합니다. ## Qwen Code에서 `run_shell_command` 사용 방법 -`run_shell_command`를 사용할 때, 명령은 서브프로세스로 실행됩니다. `is_background` 매개변수를 사용하거나 명령에 명시적으로 `&`를 추가하여 명령이 백그라운드에서 실행될지 포그라운드에서 실행될지 제어할 수 있습니다. 이 도구는 실행에 대한 자세한 정보를 반환합니다: +`run_shell_command`를 사용할 때, 명령은 서브프로세스로 실행됩니다. 백그라운드에서 실행될지 포그라운드에서 실행될지는 `is_background` 매개변수에 의해 제어됩니다. `is_background: true`일 때 뒤에 `&`를 추가하지 마십시오: 관리되는 백그라운드 경로가 자체적으로 백그라운딩 메커니즘이며, 도구는 명령을 실행하는 대신 호출을 거부합니다. 이 도구는 실행에 대한 자세한 정보를 반환합니다: -### 필수 백그라운드 매개변수 +### `is_background` 매개변수 -`is_background` 매개변수는 모든 명령 실행에 대해 **필수**입니다. 이 설계는 LLM(및 사용자)이 각 명령이 백그라운드에서 실행될지 포그라운드에서 실행될지 명시적으로 결정하도록 하여, 의도적이고 예측 가능한 명령 실행 동작을 촉진합니다. 이 매개변수를 필수로 함으로써 장기간 실행 프로세스를 처리할 때 의도하지 않은 포그라운드 실행 폴백을 방지하여 후속 작업을 차단할 수 있습니다. +`is_background`는 선택 사항입니다 — 도구의 스키마는 `command`만 요구하며 — 생략하면 명령이 포그라운드에서 실행됩니다. 따라서 명시적이어야 하는 결정은 반대쪽입니다: 스스로 중지되지 않는 명령은 `is_background: true`로 표시해야 합니다. 포그라운드에 두면 타임아웃될 때까지 턴이 차단되기 때문입니다. ### 백그라운드 vs 포그라운드 실행 -이 도구는 명시적 선택에 따라 백그라운드와 포그라운드 실행을 지능적으로 처리합니다: +이 도구는 해당 매개변수에 따라 백그라운드 및 포그라운드 실행을 처리합니다: **백그라운드 실행 사용 (`is_background: true`):** @@ -36,15 +36,11 @@ Windows에서는 명령이 `cmd.exe /c`로 실행됩니다. 다른 플랫폼에 - 빌드 감시자: `npm run watch`, `webpack --watch` - 데이터베이스 서버: `mongod`, `mysql`, `redis-server` - 웹 서버: `python -m http.server`, `php -S localhost:8000` -- 수동으로 중지될 때까지 무기한 실행될 것으로 예상되는 모든 명령 +- 수동으로 중지될 때까지 무한정 실행될 것으로 예상되는 모든 명령 -**포그라운드 실행 사용 (`is_background: false`):** +**포그라운드 실행 사용(기본값):** 빌드, 설치, git 작업, 테스트 실행과 같이 자체적으로 완료되는 명령의 경우. -- 일회성 명령: `ls`, `cat`, `grep` -- 빌드 명령: `npm run build`, `make` -- 설치 명령: `npm install`, `pip install` -- Git 작업: `git commit`, `git push` -- 테스트 실행: `npm test`, `pytest` +파일 읽기 및 검색은 이 도구의 포그라운드 후보가 전혀 아닙니다: 도구의 설명에서도 제외하는 `cat`, `grep`, `find` 대신 전용 도구(`read_file`, `grep_search`, `glob`)를 사용하십시오. ### 실행 정보 @@ -65,7 +61,7 @@ Windows에서는 명령이 `cmd.exe /c`로 실행됩니다. 다른 플랫폼에 run_shell_command(command="Your commands.", description="Your description of the command.", directory="Your execution directory.", is_background=false) ``` -**참고:** `is_background` 매개변수는 필수이며 모든 명령 실행에 대해 명시적으로 지정해야 합니다. +**참고:** `is_background`는 선택 사항입니다. 생략하면 명령이 포그라운드에서 실행되므로 명시적 `true`가 필요한 경우는 스스로 중지되지 않는 명령입니다. ## `run_shell_command` 예시 @@ -87,11 +83,7 @@ run_shell_command(command="./my_script.sh", directory="scripts", description="Ru run_shell_command(command="npm run dev", description="Start development server in background", is_background=true) ``` -백그라운드 서버 시작(명시적 &를 사용한 대안): - -```bash -run_shell_command(command="npm run dev &", description="Start development server in background", is_background=false) -``` +참고: 명시적 `&`는 관리되는 백그라운드 메커니즘이 아닙니다. `is_background: true`일 때 도구는 명령이 실행되기 전에 순수한 후행 `&`를 거부하며, `is_background=false`일 때 셸이 프로세스를 분리하므로 호출이 반환되어도 프로세스가 추적되지 않습니다 — 백그라운드 작업 항목이 없으며 `task_stop`이 도달할 수 없습니다. 백그라운드에서 실행되어야 하는 명령에는 항상 `is_background=true`를 사용하십시오. 포그라운드에서 빌드 명령 실행: @@ -171,8 +163,8 @@ run_shell_command(command="docker-compose up", description="Start all services", - **보안:** 특히 사용자 입력에서 구성된 명령을 실행할 때 보안 취약점을 방지하기 위해 주의하세요. - **오류 처리:** 명령이 성공적으로 실행되었는지 확인하려면 `Stderr`, `Error` 및 `Exit Code` 필드를 확인하세요. -- **백그라운드 프로세스:** `is_background=true`일 때 또는 명령에 `&`가 포함되어 있을 때, 도구는 즉시 반환되고 프로세스는 백그라운드에서 계속 실행됩니다. `Background PIDs` 필드에는 백그라운드 프로세스의 프로세스 ID가 포함됩니다. -- **백그라운드 실행 선택:** `is_background` 매개변수는 필수이며 실행 모드에 대한 명시적 제어를 제공합니다. 수동 백그라운드 실행을 위해 명령에 `&`를 추가할 수도 있지만, `is_background` 매개변수는 여전히 지정해야 합니다. 이 매개변수는 더 명확한 의도를 제공하며 백그라운드 실행 설정을 자동으로 처리합니다. +- **백그라운드 프로세스:** `is_background=true`만 추적되는 백그라운드 프로세스를 시작합니다: 도구는 셸 ID와 PID를 즉시 반환하고 프로세스는 계속 실행됩니다. 셸 레벨의 `&`는 백그라운드 프로세스를 생성하지 않습니다 — `is_background: true`일 때 도구는 순수한 후행 `&`를 거부하며, `is_background=false`일 때 셸은 도구가 추적하지 없이 명령을 분리합니다. +- **백그라운드 실행 선택:** `is_background` 매개변수는 선택 사항입니다(스키마는 `command`만 요구함). 스스로 중지되지 않는 명령에 대해 `is_background: true`를 설정하십시오. 포그라운드에 두면 타임아웃될 때까지 턴이 차단됩니다. 셸 레벨의 `&` 대신 `is_background`를 사용하십시오. 더 명확한 의도를 제공하며 도구가 백그라운드 실행 설정을 관리합니다. - **명령 설명:** `is_background=true`를 사용할 때, 명령 설명에 실행 모드를 명확히 표시하는 `[background]` 표시기가 포함됩니다. ## 환경 변수 diff --git a/website/content/ko/users/features/channels/wecom.md b/website/content/ko/users/features/channels/wecom.md index f434f1bba70..a12046cec48 100644 --- a/website/content/ko/users/features/channels/wecom.md +++ b/website/content/ko/users/features/channels/wecom.md @@ -2,6 +2,8 @@ 이 가이드에서는 WeCom 지능형 로봇(企业微信智能机器人)과 함께 Qwen Code를 설정하는 방법을 다룹니다. +Corp ID, Agent ID, Token, EncodingAESKey를 사용하는 자체 구축 애플리케이션의 경우, 별도의 [WeCom Callback 채널](./wecom-callback)을 사용하세요. + ## 사전 준비 사항 - WeCom 조직 계정 diff --git a/website/content/pt-BR/users/features/channels/wecom.md b/website/content/pt-BR/users/features/channels/wecom.md index 9bd20e81f57..22ce84d2dad 100644 --- a/website/content/pt-BR/users/features/channels/wecom.md +++ b/website/content/pt-BR/users/features/channels/wecom.md @@ -2,6 +2,9 @@ Este guia aborda a configuração do Qwen Code com um robô inteligente do WeCom. +Para um aplicativo próprio usando Corp ID, Agent ID, Token e EncodingAESKey, +use o [canal WeCom Callback](./wecom-callback) separado. + ## Pré-requisitos - Uma conta de organização do WeCom diff --git a/website/content/ru/users/features/channels/wecom.md b/website/content/ru/users/features/channels/wecom.md index 8a3fe26e6bf..8dad73d38a3 100644 --- a/website/content/ru/users/features/channels/wecom.md +++ b/website/content/ru/users/features/channels/wecom.md @@ -2,6 +2,8 @@ В этом руководстве описывается настройка Qwen Code для работы с интеллектуальным роботом WeCom (企业微信智能机器人). +Для self-built приложений с использованием Corp ID, Agent ID, Token и EncodingAESKey используйте отдельный [канал WeCom Callback](./wecom-callback). + ## Необходимые условия - Аккаунт организации WeCom diff --git a/website/content/ru/users/integration-github-action.md b/website/content/ru/users/integration-github-action.md index 1679aac245e..5f81d8b5914 100644 --- a/website/content/ru/users/integration-github-action.md +++ b/website/content/ru/users/integration-github-action.md @@ -112,19 +112,19 @@ gha-creds-*.json - qwen*api_key: *(Optional)\_ API key для Qwen API. -- qwen*cli_version: *(Optional, default: `latest`)\_ Версия устанавливаемого Qwen Code CLI. Может быть "latest", "preview", "nightly", конкретный номер версии, git-ветка, тег или коммит. Дополнительную информацию см. в разделе [Qwen Code CLI releases](https://github.com/QwenLM/qwen-code-action/blob/main/docs/releases.md). +- qwen*cli_version: *(Optional, default: `latest`)\_ Версия устанавливаемого Qwen Code CLI. Может быть "latest", "preview", "nightly", конкретный номер версии, git-ветка, тег или коммит. Дополнительную информацию см. в разделе [Qwen Code CLI releases](https://github.com/QwenLM/qwen-code/releases). - qwen*debug: *(Optional)\_ Включить логирование отладки и потоковую передачу вывода. - qwen*model: *(Optional)\_ Модель для использования с Qwen Code. -- prompt: _(Optional, default: `You are a helpful assistant.`)_ Строка, передаваемая в [`--prompt` аргумент](https://github.com/QwenLM/qwen-code-action/blob/main/docs/cli/configuration.md#command-line-arguments) Qwen Code CLI. +- prompt: _(Optional, default: `You are a helpful assistant.`)_ Строка, передаваемая в [`--prompt` argument](./configuration/settings.md#command-line-arguments-table) Qwen Code CLI. -- settings: _(Optional)_ JSON-строка, записываемая в `.qwen/settings.json` для настройки _проектных_ параметров CLI. Подробнее см. в документации по [файлам настроек](https://github.com/QwenLM/qwen-code-action/blob/main/docs/cli/configuration.md#settings-files). +- settings: _(Optional)_ JSON-строка, записываемая в `.qwen/settings.json` для настройки _проектных_ параметров CLI. Подробнее см. в документации по [settings files](./configuration/settings.md#settings-files). -- use*qwen_code_assist: *(Optional, default: `false`)\_ Использовать ли Code Assist для доступа к моделям Qwen Code вместо стандартного Qwen Code API key. Дополнительную информацию см. в [документации Qwen Code CLI](https://github.com/QwenLM/qwen-code-action/blob/main/docs/cli/authentication.md). +- use*qwen_code_assist: *(Optional, default: `false`)\_ Использовать ли Code Assist для доступа к моделям Qwen Code вместо стандартного Qwen Code API key. Дополнительную информацию см. в [Qwen Code CLI documentation](./configuration/auth.md). -- use*vertex_ai: *(Optional, default: `false`)\_ Использовать ли Vertex AI для доступа к моделям Qwen Code вместо стандартного Qwen Code API key. Дополнительную информацию см. в [документации Qwen Code CLI](https://github.com/QwenLM/qwen-code-action/blob/main/docs/cli/authentication.md). +- use*vertex_ai: *(Optional, default: `false`)\_ Использовать ли Vertex AI для доступа к моделям Qwen Code вместо стандартного Qwen Code API key. Дополнительную информацию см. в [Qwen Code CLI documentation](./configuration/auth.md). - extensions: _(Optional)_ Список расширений Qwen Code CLI для установки. @@ -215,6 +215,7 @@ Qwen Code CLI можно расширить дополнительным фун ## Кастомизация Создайте файл QWEN.md в корне вашего репозитория, чтобы предоставить специфичный для проекта контекст и инструкции для [Qwen Code CLI](./common-workflow). Это полезно для определения соглашений по написанию кода, архитектурных паттернов или других правил, которым модель должна следовать в данном репозитории. + ## Участие в разработке Будем рады вашему вкладу! Ознакомьтесь с **руководством по участию в разработке** Qwen Code CLI, чтобы узнать подробнее о том, как начать. @@ -225,4 +226,4 @@ Qwen Code CLI можно расширить дополнительным фун [Qwen Code CLI]: https://github.com/QwenLM/qwen-code-action/ [variables]: https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-variables#creating-configuration-variables-for-a-repository [GitHub CLI]: https://docs.github.com/en/github-cli/github-cli -[QWEN.md]: https://github.com/QwenLM/qwen-code-action/blob/main/docs/cli/configuration.md#context-files-hierarchical-instructional-context \ No newline at end of file +[QWEN.md]: ./configuration/settings.md#context-files-hierarchical-instructional-context \ No newline at end of file diff --git a/website/content/zh/users/features/approval-mode.md b/website/content/zh/users/features/approval-mode.md index 3ce5386eee3..0e7e79c35c6 100644 --- a/website/content/zh/users/features/approval-mode.md +++ b/website/content/zh/users/features/approval-mode.md @@ -259,8 +259,8 @@ Shell 命令、网络调用和工作区外的编辑,并自动批准 - **硬规则仍然有效**:`permissions.deny` 规则会在分类器运行之前阻止操作。 - **在 Auto 模式下会剥离过于宽泛的允许规则**:例如, `permissions.allow: ["Bash"]`(允许所有 Shell 命令)会使分类器失效;进入 Auto 模式会暂时禁用此类规则,以便分类器发挥作用。当你离开 Auto 模式时,这些规则会恢复。磁盘上的设置永远不会被修改。 -- **故障关闭**:当分类器 API 无法访问时,操作会被阻止而不是允许。在连续两次不可用调用后,下一次工具调用将回退到手动审批。 -- **循环防护**:在连续三次策略阻止后,下一次调用也会回退到手动审批,从而避免代理陷入死胡同方法中循环。 +- **fail closed(失败即拒绝)**:当分类器 API 无法访问时,操作会被阻止而不是允许。在连续两次不可用调用后,下一次工具调用将回退到手动审批。 +- **循环防护**:在连续三次阻止后——分类器策略阻止和确定性破坏性命令阻止都计算在内——达到第三次阻止的那次调用也会回退到手动审批,从而避免代理陷入死胡同式的循环。 ### 示例 diff --git a/website/content/zh/users/features/channels/dingtalk.md b/website/content/zh/users/features/channels/dingtalk.md index 4fc1e323838..db835df1ff4 100644 --- a/website/content/zh/users/features/channels/dingtalk.md +++ b/website/content/zh/users/features/channels/dingtalk.md @@ -31,10 +31,10 @@ "clientId": "$DINGTALK_CLIENT_ID", "clientSecret": "$DINGTALK_CLIENT_SECRET", "useConnectionManager": true, - "senderPolicy": "open", + "privatePolicy": "open", "sessionScope": "user", "cwd": "/path/to/your/project", - "instructions": "你是一个通过钉钉响应的简洁编码助手。", + "instructions": "You are a concise coding assistant responding via DingTalk.", "groupPolicy": "open", "atSender": true, "groups": { @@ -97,7 +97,7 @@ export DINGTALK_CLIENT_SECRET= ### Turn 输出模式 -[共享的 `outputMode` 设置](https://qwenlm.github.io/qwen-code-docs/en/users/features/channels/overview/#turn-output-mode) 控制 DingTalk 何时投递助手结果。DingTalk 是目前唯一集成此策略的适配器。默认值为 `per_turn`,包括省略 `outputMode` 时: +[共享的 `outputMode` 设置](./overview#turn-output-mode) 控制 DingTalk 何时投递助手结果。DingTalk 是目前唯一集成此策略的适配器。默认值为 `per_turn`,包括省略 `outputMode` 时: - `per_task`:等待主任务及其关联的后台任务和通知完成,然后投递一个最终结果卡片,包含该任务最后一个非空助手回复。 - `per_response`:每个完整的助手回复都有自己独立的结果卡片。Token 片段更新当前卡片,不会创建新卡片。后台助手回复也会单独投递。 @@ -229,7 +229,7 @@ qwen channel start ## 提示 - **使用钉钉 markdown 感知指令**——钉钉支持标题、粗体、链接、代码块和表格。由于窄屏可能会水平滚动,请保持表格紧凑。 -- **限制访问**——在组织环境下,`senderPolicy: "open"` 可能可以接受。如需更严格的控制,使用 `"allowlist"` 或 `"pairing"`。参见[私聊配对](./overview#dm-pairing)了解详情。 +- **限制访问**——在组织环境下,`privatePolicy: "open"` 可能可以接受。如需更严格的控制,使用 `"allowlist"` 或 `"pairing"`。参见[私聊配对](./overview#dm-pairing)了解详情。 - **引用消息**——引用(回复)用户消息会将引用的文本作为上下文提供给代理。富文本引用保留其文本顺序并附加嵌入的图片。如果引用的消息是图片、文件、音频或视频消息,机器人会以与直接发送时相同的方式下载并附加它。暂不支持引用机器人回复。 ## 故障排查 diff --git a/website/content/zh/users/features/channels/wecom-callback.md b/website/content/zh/users/features/channels/wecom-callback.md new file mode 100644 index 00000000000..b9583dd54ad --- /dev/null +++ b/website/content/zh/users/features/channels/wecom-callback.md @@ -0,0 +1,69 @@ +# WeCom 自建应用回调 + +通过 `wecom-callback` 可以使用企业微信自建应用与 Qwen Code 进行对话。它接收加密的 HTTP 回调并通过应用 API 发送文本回复。对于具有 Bot ID 和 Secret 的智能机器人,请使用独立的 [WeCom WebSocket channel](./wecom)。 + +## 前提条件 + +- 需要一位企业微信管理员和一个有权接收消息、发送应用消息的自建应用。 +- 企业 Corp ID、应用 Agent ID 和 Secret,以及在应用消息接收设置中配置的回调 Token 和 EncodingAESKey。 +- 一个转发到本地监听器的公开 HTTPS 回调 URL。根据租户要求配置应用的可信出站 IP 和可见成员范围。保持 Qwen 主机的时钟同步。 + +请参阅官方[回调加密](https://developer.work.weixin.qq.com/document/path/90968)和[应用消息 API](https://developer.work.weixin.qq.com/document/path/90236)文档了解平台要求。 + +## 配置 + +在 `~/.qwen/settings.json` 中添加一个命名 channel: + +```json +{ + "channels": { + "my-wecom-app": { + "type": "wecom-callback", + "corpId": "$WECOM_CORP_ID", + "agentId": "$WECOM_AGENT_ID", + "corpSecret": "$WECOM_APP_SECRET", + "token": "$WECOM_CALLBACK_TOKEN", + "encodingAESKey": "$WECOM_ENCODING_AES_KEY", + "webhookHost": "127.0.0.1", + "webhookPort": 8645, + "privatePolicy": "allowlist", + "allowedUsers": ["zhangsan"], + "sessionScope": "user", + "groupPolicy": "disabled", + "cwd": "/path/to/your/project" + } + } +} +``` + +在启动 Qwen Code 的进程中设置这五个环境变量。Agent ID 是正整数**字符串**,如 `"1000002"`。EncodingAESKey 是企业微信控制台中的 43 位密钥。回调 Token 与应用 Secret 和 API 访问令牌不同;Qwen 会自动获取 API 访问令牌。这三个密钥字段在 channel 管理中会被遮蔽。 + +监听器默认绑定到 `127.0.0.1:8645`,路径固定为 `/wecom/callback`。将公开 URL 转发到该路径,保留其查询字符串和请求体。对于容器部署,显式绑定到可达的网络接口。多个应用需使用不同的 channel 名称和端口。 + +## 运行与平台验证 + +1. 运行 `qwen channel start my-wecom-app`。 +2. 在自建应用的消息接收设置中,输入以 `/wecom/callback` 结尾的公开 HTTPS URL、Token 和 EncodingAESKey。保存设置;Qwen 会解密并返回验证挑战。 +3. 将测试成员加入应用的可见范围,并将其企业微信 User ID 填入 `allowedUsers`。向应用发送文本消息,预期会收到该应用的文本回复。 +4. 从 `allowedUsers` 之外的可见成员发送消息,预期不会触发 agent 任务或回复。Qwen 仍会确认回调以避免平台重试。 +5. 请求包含中文文本和表情的长回复,预期收到按顺序发送的文本消息,每条不超过平台的 2048 字节限制。 +6. 停止 channel 并确认本地监听器已关闭。 + +如果 URL 验证失败,检查反向代理、回调路径、时钟、Corp ID、Token 和 EncodingAESKey。如果回调到达但回复失败,检查 Agent ID、应用 Secret、应用可见性和可信出站 IP。应用密钥和回调密钥绝不能出现在日志或 issue 中。 + +## 支持的行为与限制 + +- 文本私聊和文本回复。共享访问策略、配对、命令、权限和会话适用。使用成员 User ID 配置访问规则。 +- 主动消息可以针对单个用户。不支持群组、话题、媒体、应用事件和流式回复。 +- 回调在 agent 执行完成前进行身份验证、解密和确认。回复使用应用消息 API,而非回调的同步响应。已接受的消息 ID 在内存中至少抑制五分钟,直至其签名时间戳窗口过期;签名时间戳窗口超出五分钟的请求会失败。被拒绝的发送者不消耗重试缓存容量。 +- 进行中的工作不会持久排队。在确认回调后重启进程可能会丢失该任务,重启还会清除重试缓存。 + +## 无需企业租户的测试 + +在 `packages/channels/wecom` 目录下运行: + +```bash +npx vitest run src/wecom-callback-crypto.test.ts src/wecom-callback-adapter.test.ts src/callback-plugin.test.ts src/WeComAdapter.test.ts +``` + +测试使用独立发布的 SDK 加密工具、真实的本地回环 HTTP 请求、共享的入站访问检查和模拟的应用 API 响应。它们覆盖握手验证、身份验证失败、重试、接收者隔离、Unicode 分割和关闭场景。测试不需要真实凭证,也不会向企业微信发送消息。上述平台验证步骤仍需用于验证真实租户的公开 URL、权限和网络配置。 \ No newline at end of file diff --git a/website/content/zh/users/features/channels/wecom.md b/website/content/zh/users/features/channels/wecom.md index 218d0aebf6a..878cdde9cf1 100644 --- a/website/content/zh/users/features/channels/wecom.md +++ b/website/content/zh/users/features/channels/wecom.md @@ -2,6 +2,8 @@ 本指南介绍如何将 Qwen Code 与 WeCom 智能机器人(企业微信智能机器人)配合使用。 +如果使用 Corp ID、Agent ID、Token 和 EncodingAESKey 的自建应用,请使用单独的 [WeCom Callback channel](./wecom-callback)。 + ## 前提条件 - 一个 WeCom 组织账号 diff --git a/website/content/zh/users/features/sandbox.md b/website/content/zh/users/features/sandbox.md index e5fc4d798af..fc095ed7f28 100644 --- a/website/content/zh/users/features/sandbox.md +++ b/website/content/zh/users/features/sandbox.md @@ -1,6 +1,55 @@ # 沙箱 -本文档说明如何在沙箱中运行 Qwen Code,以降低工具执行 shell 命令或修改文件时的风险。 +本文档说明 Linux 上的工具执行限制以及现有的整个 CLI 沙箱方法。 + +## Linux 工具执行沙箱 + +在 Linux 上,将 `bwrap`(Bubblewrap)安装在 `/usr/bin/bwrap`(受支持的系统路径)以获得最强的支持边界,然后在**用户**设置(`~/.qwen/settings.json`,或 `QWEN_HOME` 选择的目录)或管理员**系统**设置中启用工具执行沙箱: + +```json +{ + "tools": { + "executionSandbox": { + "backend": "auto", + "filesystem": "workspace-write", + "network": "closed" + } + } +} +``` + +`filesystem` 和 `network` 是必需的。`backend` 默认为 `auto`,会首先尝试 bwrap。当 bwrap 不可用且 `network` 为 `open` 时,`auto` 可以在具有 Landlock ABI 3 或更高版本的内核上使用捆绑的 Landlock 助手。将 `backend` 设置为 `bwrap` 或 `landlock` 以要求使用该确切实现。Landlock 无法强制执行 `network: closed` 或隐藏显式屏蔽的路径。任一要求都会在命令启动前拒绝 Landlock;自动回退永远不会丢弃剩余的路径掩码。使用 `read-only` 拒绝工作区写入,或使用 `workspace-write` 允许在当前规范工作区内写入。每个命令还会获得私有临时空间。普通的 `includeDirectories` 设置不会授予写入权限。保持工作区与 Qwen 安装、用户配置和运行时状态目录分离。 + +CLI、模型传输、身份验证和会话存储保留在宿主机上。Shell、终端 `!`、提示 shell 插值、Monitor、Read/Write/Edit 以及受支持的嵌套进程内 Agent/Code Mode 调用使用相同的运行时策略。使用 bwrap 时,`network: closed` 会阻止命令的普通宿主/外部 IP 连接,而 `open` 则共享宿主网络。读取仍然很广泛:此模式不会隐藏宿主机密,也不会承诺隔离路径名 Unix 套接字和宿主服务。审批和 YOLO 不会扩展文件系统或网络策略。标准流是调用者显式提供的能力:已连接的套接字、TTY 或字符设备即使在 `network: closed` 下仍然可用。封闭网络不会撤销那些现有的描述符;当不需要该访问时,避免将宿主服务连接作为 stdin 提供。宿主支持的文件和命名 FIFO stdin 需要捆绑的 Linux x64/arm64 输入助手,并通过私有 Unix 流套接字复制,其写入端已关闭;常规文件偏移量仅通过从该套接字消耗的字节数推进,空闲的 FIFO 写入器不会延迟命令完成。直接读取 fd 0:此输入是套接字而不是 FIFO,不支持通过 `/dev/stdin` 或 `/proc/self/fd/0` 重新打开。SIGINT/SIGTERM 取消命令但仍会排空已接受的 stdout/stderr 字节;关闭的输出会以退出码 141 停止该流,而另一个有效输出继续排空。 + +Landlock 报告 `partial` 强制执行,因为它限制路径名读取、写入和目录变更,但不创建 PID 或网络命名空间,并且目前无法限制每个元数据操作,包括 `chmod`、`chown`、扩展属性和时间戳。Landlock 命令可以看到宿主进程并使用宿主网络。分离的后代保留 Landlock 文件系统限制,但不会获得 bwrap 的 PID 命名空间生命周期边界。打开的文件描述符保留其现有访问权限。Qwen Code 通过私有只读套接字流式传输宿主支持的文件和命名 FIFO stdin,而不是将这些描述符传递给有效载荷;套接字、字符设备和匿名管道可以共享。输出标准流仍然是调用者的显式目标。执行状态描述符在有效载荷 exec 时关闭。 + +Landlock 还具有更窄的可写设备表面:它授予对 `/dev/null` 的写入,但不创建 bwrap 的私有 `/dev` 和 `/proc` 挂载。打开 `/dev/full` 或 `/dev/tty` 进行写入以及在 `/dev/shm` 中创建文件被拒绝,因此需要这些操作的程序可能会失败。使用通过 `TMPDIR`、`TMP` 和 `TEMP` 公开的私有临时目录来存储临时文件。已打开的、调用者提供的标准流保留其现有访问权限。启动时的 Landlock 策略探测需要 `/usr/bin/true`;缺少它的宿主即使其内核支持 Landlock 也会使探测失败。 + +工作区设置无法启用、禁用或修改此策略,即使在受信任的项目中也是如此。有效优先级是系统优先于用户优先于系统默认值,选择完整的策略对象。值必须是字面量;环境替换、未知字段和不完整的对象会被拒绝。`--bare` 和 `--safe-mode` 保留操作员限制。更改需要新的运行时。格式错误或不可读的系统默认、用户或系统设置文件现在会阻止启动(配置退出码 52),而不是将未知策略静默重置为空设置。错误会指明文件和修复操作。修复其 JSON 对象或恢复读取访问权限,然后重启。用户 `.corrupted` 副本仅供参考;原始操作员文件永远不会被清除。仅限工作区的恢复仍然可用,无需操作员限制。格式错误的链接工作区设置(包括链接的 `.qwen` 目录或硬链接文件)需要手动修复;自动恢复会在更改原始文件或之前的损坏副本之前拒绝。有效的链接 JSON 仍然可读。 + +此初始公共模式支持普通的无头 CLI 和两个终端 UI。ACP、`qwen serve` 和 Web 终端明确拒绝它。MCP/LSP、可执行 hook/extension、worktree/arena 管理、外部代理、speculative execution、文件检查点、自动 skill/memory 维护以及未移植的进程或变更工具被禁用或拒绝。普通的同工作区进程内代理无法添加 hook、MCP 服务器、外部执行器或 worktree 隔离。自定义状态命令、外部 Markdown/图像渲染器、IDE 检测和连接、文件恢复以及宿主 Git diff 预览被禁用。仅限对话的回退仍然可用。 + +显式操作员诊断(如 `/doctor`、通过 `/memory` 打开或编辑文件和文件夹,以及通过 `/skills` 更改设置)仍然是受信任的宿主操作。这些管理命令不会作为模型可调用的命令公开。选择 skill 会填充输入字段;提交 `/skill-name` 当前会在准备参数或更新使用记录之前报告不支持的模式错误。skill 命令执行和模型 Skill 工具在其准备写入被限制之前仍然不可用。即使更改已保存的设置,自动 memory 和 skill 维护在此运行时中仍然禁用。 + +在运行任务之前检查和验证活动边界: + +```bash +qwen sandbox +qwen sandbox --verify +qwen sandbox -- sh -c 'printf "confined command\n"' +qwen -y -p "Update the project and run its tests" +``` + +链接的 Git worktree 仅获得对已准入工作区的写入权限。该工作区之外的 Git 公共目录和 worktree 元数据保持只读;此模式不会隐式授权更改另一个 checkout 的仓库元数据。需要这些写入的操作可能会被拒绝。 + +报告会指明工具边界、请求/有效的后端、强制执行级别、适用时的 Landlock ABI、工作区、文件系统和命令网络策略。`--verify` 检查工作区写入、对宿主机上已知可写文件的拒绝、预期的私有或共享 PID 命名空间身份,以及选定的网络命名空间。通过的后端探测建立准入,而不是在所有继承的环境设置下经过身份验证的完整会话可行性。之后运行普通任务以测试其所需的工具和服务。如果验证无法创建其宿主侧临时装置,请检查 `TMPDIR` 和目录权限;这与边界拒绝不同。后端设置错误在有效载荷执行之前失败,并且永远不会在宿主机上重新运行命令。单命令子命令转发字面参数和重定向的 stdin,同时逐字节保留 stdout 和 stderr;使用终端 `!` 进行交互式 PTY 命令。 + +### 从整个 CLI bwrap 迁移 + +替换 `--sandbox bwrap` 并移除 `tools.sandbox: "bwrap"`、`QWEN_SANDBOX=bwrap`、`QWEN_SANDBOX_NET` 和 `QWEN_SANDBOX_PROXY_COMMAND`,然后按上述方式配置 `tools.executionSandbox`。从旧沙箱外部的 shell 重启;继承的 `SANDBOX=bwrap` 会被拒绝。`proxied` 不是受支持的网络策略。不要将此模式与 Docker、Podman、Seatbelt 或继承的整个 CLI 沙箱标记结合使用。旧的 bwrap 设置会产生显式迁移错误。 + +下面的 Docker、Podman 和 macOS Seatbelt 方法仍然在现有环境中运行整个 CLI。 ## 前提条件 @@ -99,6 +148,8 @@ qwen -p "运行测试套件" > > 如果设置了 `QWEN_SANDBOX`,它将**覆盖** CLI 标志和 `settings.json`。 +`--sandbox` 和 `-s` 接受可选值:`true`、`false`、`docker`、`podman` 或 `sandbox-exec`。对于带提示的自动选择,使用 `qwen --sandbox=true "query"` 或 `qwen --sandbox -p "query"`。与旧的仅布尔值解析器不同,`qwen --sandbox "query"` 现在将 `query` 视为后端值并拒绝未知后端。`--` 之前的选项即使在后置词之后也使用相同的语法;在提示中使用字面选项文本请使用 `-p "..."`。`--` 停止选项解析,但其尾部标记不会作为默认命令提示转发。会话沙箱标志在管理子命令上不被接受,其 `-s` 别名保留自己的含义。 + ### 配置沙箱镜像(Docker/Podman) - **CLI 标志**:`--sandbox-image ` diff --git a/website/last-sync.json b/website/last-sync.json index 5a602441381..189303e2544 100644 --- a/website/last-sync.json +++ b/website/last-sync.json @@ -1,6 +1,6 @@ { - "commit": "5936225e43f9c58ee772df49ccf445c8d8a60341", - "timestamp": "2026-10-19T23:42:36.627Z", + "commit": "d098ff23af254626096fe178aed5a4f5ec750e31", + "timestamp": "2026-10-20T23:15:07.673Z", "files": { "docs/developers/contributing.md": { "langs": { @@ -186,7 +186,7 @@ "ja": "74b5c9d4f326e081a35eeca62e74cb804885338c964337423a59ef5a28fc787e", "ru": "74b5c9d4f326e081a35eeca62e74cb804885338c964337423a59ef5a28fc787e", "pt-BR": "0dfbe39363a16a368dbb7a93ab4c55a5490f5620c72f29f68716b2e1661a0179", - "ko": "0dfbe39363a16a368dbb7a93ab4c55a5490f5620c72f29f68716b2e1661a0179" + "ko": "74b5c9d4f326e081a35eeca62e74cb804885338c964337423a59ef5a28fc787e" } }, "docs/developers/tools/todo-write.md": { @@ -829,9 +829,9 @@ }, "docs/users/features/approval-mode.md": { "langs": { - "zh": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", + "zh": "27b57a1f1408d6810b0531c7fe9fee42622d3a5fbd37bb3d11835640a4040fd2", "fr": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", - "ja": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", + "ja": "27b57a1f1408d6810b0531c7fe9fee42622d3a5fbd37bb3d11835640a4040fd2", "ru": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", "pt-BR": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", "de": "af521b5e8e2200b80c044182119a4a17a6434ac33f9d4dce5d5c7e78eb6b724b", @@ -851,7 +851,7 @@ }, "docs/users/features/channels/dingtalk.md": { "langs": { - "zh": "b84b236d0245387c08c5a5d2249c5d07cf6a045a1646f3c9ca139c359ea08e22", + "zh": "f79bb271c6923b30cfe7a9a120ed71e988a105dc75c5b28283e7d0ec6b745a73", "fr": "1dd9b556c49892afc93d72b759af2fe6921deb9333c1144a6c2330e35ae1af02", "ja": "b84b236d0245387c08c5a5d2249c5d07cf6a045a1646f3c9ca139c359ea08e22", "ru": "1dd9b556c49892afc93d72b759af2fe6921deb9333c1144a6c2330e35ae1af02", @@ -873,13 +873,13 @@ }, "docs/users/features/channels/wecom.md": { "langs": { - "zh": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", + "zh": "7859054ae36ab3aef5400b2b8cf6e79637ce38d3540cc08f5b4cd6e8dd0d9483", "fr": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", "ja": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", - "ru": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", - "pt-BR": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", - "de": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe", - "ko": "68e72fb703427c984c26b855fd47f54b8723ef6ea6c0b3c2ca713f5b6ac740fe" + "ru": "7859054ae36ab3aef5400b2b8cf6e79637ce38d3540cc08f5b4cd6e8dd0d9483", + "pt-BR": "7859054ae36ab3aef5400b2b8cf6e79637ce38d3540cc08f5b4cd6e8dd0d9483", + "de": "7859054ae36ab3aef5400b2b8cf6e79637ce38d3540cc08f5b4cd6e8dd0d9483", + "ko": "7859054ae36ab3aef5400b2b8cf6e79637ce38d3540cc08f5b4cd6e8dd0d9483" } }, "docs/users/features/headless.md": { @@ -931,7 +931,7 @@ "zh": "1ab917b1306d1de83469c3fc69f93f12b145695e59c313904181a99677fdc621", "fr": "25ace22f078a8fa13d618e29ac9e1575199cae084470a5031825465822a6b700", "ja": "1ab917b1306d1de83469c3fc69f93f12b145695e59c313904181a99677fdc621", - "ru": "25ace22f078a8fa13d618e29ac9e1575199cae084470a5031825465822a6b700", + "ru": "1ab917b1306d1de83469c3fc69f93f12b145695e59c313904181a99677fdc621", "pt-BR": "25ace22f078a8fa13d618e29ac9e1575199cae084470a5031825465822a6b700", "de": "25ace22f078a8fa13d618e29ac9e1575199cae084470a5031825465822a6b700", "ko": "25ace22f078a8fa13d618e29ac9e1575199cae084470a5031825465822a6b700" @@ -996,7 +996,7 @@ "langs": { "fr": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", "de": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", - "ja": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", + "ja": "bff0149848ce6dbee280bbbd0c52e31159d4695c8f53131683ba6ced935fef6a", "zh": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", "ru": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", "pt-BR": "10490fb7eacdf7eb56d36ae16d07b1e17731bede2e016bc7cdb32d08940b04d5", @@ -1095,7 +1095,7 @@ "langs": { "fr": "ec5b413deb39c1eb2909ca1c5710be203e1d6c97ca44fe3a24567ed522766699", "ja": "57362261766c8f7ddf545342c32e0d9c07ea27449b8b5ad9b92b7911ed43fc8f", - "zh": "ec5b413deb39c1eb2909ca1c5710be203e1d6c97ca44fe3a24567ed522766699", + "zh": "57362261766c8f7ddf545342c32e0d9c07ea27449b8b5ad9b92b7911ed43fc8f", "ru": "ec5b413deb39c1eb2909ca1c5710be203e1d6c97ca44fe3a24567ed522766699", "pt-BR": "ec5b413deb39c1eb2909ca1c5710be203e1d6c97ca44fe3a24567ed522766699", "ko": "ec5b413deb39c1eb2909ca1c5710be203e1d6c97ca44fe3a24567ed522766699", @@ -1171,7 +1171,7 @@ "docs/users/support/troubleshooting.md": { "langs": { "fr": "ea10ea6b5367c09699b31be6bebdd8c296f69a49dfb0d75d3d2dbd31253723ba", - "ja": "ea10ea6b5367c09699b31be6bebdd8c296f69a49dfb0d75d3d2dbd31253723ba", + "ja": "9852533d7ee9641e6a83442a95b59ebf9bcbc69e3d533053fd93ca31e26dade9", "ru": "ea10ea6b5367c09699b31be6bebdd8c296f69a49dfb0d75d3d2dbd31253723ba", "pt-BR": "ea10ea6b5367c09699b31be6bebdd8c296f69a49dfb0d75d3d2dbd31253723ba", "zh": "ea10ea6b5367c09699b31be6bebdd8c296f69a49dfb0d75d3d2dbd31253723ba", @@ -1519,6 +1519,12 @@ "pt-BR": "295649941c85e98fdbe9abe84d3109fe7415ab7a7d752e3f7eade0cadbfeb625", "ko": "295649941c85e98fdbe9abe84d3109fe7415ab7a7d752e3f7eade0cadbfeb625" } + }, + "docs/users/features/channels/wecom-callback.md": { + "langs": { + "zh": "6e39115d3681d10339fb81fe57d8a165e9afc54c3ad1948ea498a935ee5b0fc3", + "ja": "6e39115d3681d10339fb81fe57d8a165e9afc54c3ad1948ea498a935ee5b0fc3" + } } } }