diff --git a/en/ai-sre/agents.mdx b/en/ai-sre/agents.mdx
index 42308ead..b483da79 100644
--- a/en/ai-sre/agents.mdx
+++ b/en/ai-sre/agents.mdx
@@ -49,13 +49,22 @@ The platform ships several **bootstrap Subagents** that work out of the box:
| `general` | General-purpose worker with full tool access. Use it to offload work whose intermediate steps would otherwise pollute the parent's context (long reports, multi-file edits, end-to-end builds, repeated probing). Dispatch several in parallel for independent units of work |
| `explore` | Read-only investigator restricted to `grep` / `glob` / `read`; it cannot write or execute. Returns a compact summary rather than raw search output — ideal for locating evidence inside a codebase or knowledge dump |
-**In the conversation**: each dispatch also appears as a **task card** in the conversation stream and shares the same status lifecycle as an A2A delegation (initializing / in progress / awaiting input / awaiting authorization / completed / failed (including orphaned) / interrupted / killed). The difference is the badge — a Subagent task card carries an `Agent` badge, whereas an A2A delegation carries an `A2A` badge.
+**In the conversation**: each dispatch also appears as a **task card** in the conversation stream and shares the same status lifecycle as an A2A delegation (initializing / in progress / awaiting input / awaiting authorization / completed / failed (including orphaned) / interrupted / killed). The difference is the badge — a Subagent task card carries an `Agent` badge, whereas an A2A delegation carries an `A2A` badge. A Subagent card's reference line also leads its right-hand side with the **execution environment this dispatch runs in** (the cloud environment, or the BYOC Runner's name), so you can see where a subtask executed straight from the card — see "Dispatch target environment" below.
**Runtime limits**:
- **Concurrency cap**: a session runs **at most 20 Subagents concurrently** (`TaskMaxConcurrentPerSession = 20`). This caps how many run at the same instant, not how many a session dispatches over its lifetime — completing one frees a slot, so total work is unbounded. Dispatching past the cap errors (`too many active subagents (… running, limit 20)`), prompting the model to stop and wait for the running ones to finish before dispatching the rest.
- **Nesting depth**: a dispatch chain nests **at most 3 levels deep** (`TaskMaxNestingDepth = 3`), preventing a Subagent from spawning itself indefinitely.
+**Dispatch target environment**: `agent_dispatch` also takes an optional `environment` argument that decides **which execution environment** the subtask runs in. Its values are:
+
+- **Omitted (the default)**: the subagent runs in the **same** environment as the current session and **shares its filesystem** — it can read your workspace's files directly. Naming the environment the session is *already* in (say, passing `cloud` from a cloud session) has the same result: an inheritance, not a second sandbox.
+- **`cloud`**: runs in a **fresh cloud environment** — an ephemeral container with its own filesystem and the account's default egress policy.
+- **A BYOC Runner's environment_id**: runs on **that Runner**. The selectable targets are **account-scope Runners** plus the Runners of the **team the session is bound to**, and online ones only; same-named Runners are tagged with their team so they can be told apart. `agent_dispatch`'s own description lists them, and each Runner's id is the value to pass. Naming an unusable Runner (offline, nonexistent, or belonging to another team) makes the dispatch fail outright with the reason — it is never silently re-routed elsewhere.
+- **Subagents only**: passing `environment` for an A2A Agent is rejected — a remote agent executes in its own environment and has no placement to choose; to move work to another environment, dispatch a Subagent.
+
+**A cross-environment dispatch cannot read the parent's files**: a child session shares the parent's sandbox / workspace only when its execution environment is **exactly the same**; dispatched to another environment, it gets **its own** sandbox or Runner scratch directory and **cannot see your (the parent's) files**. So put everything it needs **in the prompt** (file contents, paths, parameters, constraints) instead of writing "read file x in the workspace", and expect results back as **text** or through an external system (git, a ticket). See [Environments (BYOC)](/en/ai-sre/environments) for how the environments themselves differ and how to pick one.
+
**Subagents currently have no user-facing create / management UI.** They are a built-in runtime capability: you can observe Subagent task cards and their sub-session traces within a session, but you cannot add, edit, or delete a Subagent the way you can an A2A Agent. This page (Agents) currently manages only A2A Agents.
diff --git a/en/ai-sre/artifacts.mdx b/en/ai-sre/artifacts.mdx
index 42f18040..0b732859 100644
--- a/en/ai-sre/artifacts.mdx
+++ b/en/ai-sre/artifacts.mdx
@@ -1,6 +1,6 @@
---
title: Artifacts
-description: The artifact gallery collects files that AI SRE sessions produce with the present_files tool and publish with the publish_artifact tool — web pages, reports, images, PDFs, source code, data files, and more (for example, /insight reports). Search, filter or change scope, rename, share (account-only or a public link), download, and delete them here.
+description: The artifact gallery collects files that AI SRE sessions produce with the present_files tool and then publish either with the publish_artifact tool (HTML and Markdown only) or through the console's Publish to artifact gallery action (images, PDFs, source code, data files, and more) — for example, /insight reports. Search, filter or change scope, rename, pin, share (account-only or a public link), download, and delete them here.
keywords: ["AI SRE", "Artifacts", "present_files", "publish_artifact", "insight report", "artifact gallery", "public link", "share"]
sidebarTitle: Artifacts
---
@@ -13,7 +13,10 @@ sidebarTitle: Artifacts
---
-An artifact is a file AI SRE produces in a session with the `present_files` tool and then publishes to the artifact gallery with the `publish_artifact` tool. The most typical case is a self-contained HTML report or page — for example, the operational insight report generated by typing `/insight` in a session — but publishable types go well beyond that:
+An artifact is a file AI SRE produces in a session with the `present_files` tool and then publishes to the artifact gallery. There are two publish paths, and they accept different types:
+
+- **The agent's `publish_artifact` tool**: publishes HTML / Markdown documents only (`.html` `.htm` `.md` `.markdown`) — a self-contained report or page, most typically the operational insight report generated by typing `/insight` in a session. Every other type is rejected with `unsupported_format`.
+- **The "Publish to artifact gallery" button next to a `present_files` file in the console** (see [Creating an artifact](#creating-an-artifact) below): publishes the wider set of types:
| Category | Common extensions |
|---|---|
@@ -24,7 +27,7 @@ An artifact is a file AI SRE produces in a session with the `present_files` tool
| Source code | `.py` `.go` `.js` `.mjs` `.ts` `.jsx` `.tsx` `.java` `.c` `.h` `.cpp` `.cs` `.rb` `.rs` `.php` `.sh` `.sql` `.kt` `.swift` `.scala` `.css` `.vue` `.svelte` `.proto` `.tf` `.hcl`, and more |
| Archives | `.zip` `.tar` `.gz` `.tgz` |
-Files outside the publishable range don't show a "Publish to artifact gallery" button in the session.
+Files outside the table above don't show a "Publish to artifact gallery" button in the session.
A published artifact initially inherits its source session's scope: artifacts from a personal session belong to their creator ("Personal"); artifacts from a session bound to a team belong to that team and can be shared with other account members. Users with edit permission can change the scope later.
@@ -56,22 +59,41 @@ The top-right of the list page offers a **Card view / List view** toggle; your c
| From conversation | Title of the source conversation; click to jump to it. Shows plain text "Conversation deleted" when the source conversation was deleted or has no title |
| Created at | Artifact creation time |
| Updated at | Artifact last-updated time |
+| Pin | An icon-only button (the column has no header); click it to pin or unpin the artifact for yourself — see [Pinning](#pinning) below |
| Actions | Rename, download, delete, etc. |
The **Created at** and **Updated at** columns support header sorting (ascending / descending toggle); sorting is executed server-side, with **Updated at descending** as the default.
-Scope, search term, sorting, and page number are all reflected in the page URL (for example `?scope=team&teams=1,2&q=rebuild&page=2`), so copying the address shares or bookmarks a **filtered** list; returning from an artifact's detail page also lands back on the same filters and position instead of resetting to the default view.
+Whichever sort you pick, **the artifacts you pinned always come first** (most recently pinned first), and the chosen sort orders the rest.
+
+Scope, search term, sorting, page number, and page size are all reflected in the page URL (for example `?scope=team&teams=1,2&q=rebuild&page=2&size=48`), so copying the address shares or bookmarks a **filtered** list; returning from an artifact's detail page also lands back on the same filters and position instead of resetting to the default view.
+
+Page size can be **12 / 24 / 48**, defaulting to **24** (the URL's `?size=`; a value equal to the default is not written to the address). If the address is hand-edited to some other number, the nearest option is used rather than the literal value.
### Artifact cards
Each card shows:
-- A kind icon in the preview area: determined by file extension and content type — images, PDF, HTML, Markdown, tables (CSV / TSV), JSON, archives, and source code each get a dedicated icon, falling back to a generic file icon when the type can't be recognized;
+- A preview area rendering a **thumbnail of the artifact's own content**: HTML takes the head of the file (up to 256 KiB) and scales a desktop page laid out at 1200×630 into the box, Markdown is typeset onto that same scaled page, images show the image itself, and plain text shows its first 24 lines (up to 1400 characters). PDF and every other non-renderable kind show an empty frame. The thumbnail is a non-interactive still image — it renders inside a `sandbox=''` frame, so scripts don't run and links and buttons don't respond. It is also lazy: nothing is signed or downloaded until the card comes near the viewport (about 400px), and an empty skeleton frame shows until then;
+- The kind icon: determined by file extension and content type — images, PDF, HTML, Markdown, tables (CSV / TSV), JSON, archives, and source code each get a dedicated icon, falling back to a generic file icon when the type can't be recognized. It is a 30×30 badge straddling the divider between thumbnail and body, rather than filling the preview area;
- The title (single line, truncated beyond that; hover to see the full title);
-- A single **Edited …** relative timestamp (the artifact's last update time; for an artifact never updated after creation, the update time is its creation time) — just now / N minutes ago / N hours ago / N days ago. Relative time is used at any age: an older artifact still shows "Nd ago" instead of falling back to a concrete date. Hover the timestamp for the exact time down to the minute, and note that the **Created at** / **Updated at** columns of the list view give exact times directly;
-- A scope badge in the bottom right: team artifacts show the team name (highlighted green); personal artifacts show the creator's name (gray).
+- A single **Edited …** relative timestamp in the grey caption line under the title (the artifact's last update time; for an artifact never updated after creation, the update time is its creation time) — just now / N minutes ago / N hours ago / N days ago. Relative time is used at any age: an older artifact still shows "Nd ago" instead of falling back to a concrete date. Hover the timestamp for the exact time down to the minute, and note that the **Created at** / **Updated at** columns of the list view give exact times directly;
+- The same caption line also carries the scope (a small icon plus a name: team artifacts show the team name, personal artifacts show the creator's name) — cards no longer use coloured scope badges; badges appear only in the list view and on the detail page. When the artifact has a live public link, the caption line leads with a small globe glyph that has a tooltip; a pinned artifact shows a pin glyph at the end of the caption line.
+
+Clicking the card body opens the artifact's detail page. Hovering over a card reveals a "More actions" button in the top-right corner (always visible on touch devices), with an equally hover-only pin button beside it — one click pins or unpins. The public-link preview inside the share panel uses this same thumbnail.
+
+### Pinning
-Clicking the card body opens the artifact's detail page. Hovering over a card reveals a "More actions" button in the top-right corner (always visible on touch devices).
+Every list row and every card has a pin button — on a card it sits in the top-right corner next to "More actions" and appears on hover (always visible on touch devices); in the list it is a fixed-width column of its own, between "Updated at" and the action buttons. The first item in the "More actions" menu is the same toggle, labelled "Pin" or "Unpin" according to the current state.
+
+Pinning is **a personal preference, not an edit**:
+
+- You can pin any artifact you can see — other people's personal artifacts and team artifacts included — with **no edit permission required** (rename, change scope, and delete do require it);
+- A pin only floats the artifact to the top of **your own** gallery. It does not change the artifact's content, title, or scope, and it does not affect the order anyone else sees;
+- Pinning and unpinning are both idempotent: re-pinning only refreshes the pin time (a later pin time sorts higher), and unpinning an artifact that was never pinned does not fail;
+- Pinning refetches the whole page. The server owns the ordering, so pinning an artifact on page 2 moves it to page 1 rather than reordering in place.
+
+The `pinned_at` field on a gallery list item is **the calling person's own** pin time (unix milliseconds); it is absent when the artifact is not pinned — so the same artifact can carry a different `pinned_at` for each member.
### Creating an artifact
@@ -93,6 +115,7 @@ The "More actions" menu on each card offers:
| Action | Notes |
|---|---|
+| Pin / Unpin | The menu's first item, labelled "Pin" (when not pinned) or "Unpin". Pinning is a **personal preference** and skips the edit-permission check entirely — you can pin any artifact you can see, and it only changes the order of your own list without modifying the artifact (see [Pinning](#pinning) above) |
| Copy link | Copies the full URL of the artifact's detail page, which you can share with other account members |
| Download | Only appears when the artifact is linked to a file (`file_id` is non-empty); downloads the original file |
| Rename | Only appears when you have edit permission on the artifact; opens a dialog to change the title |
@@ -120,6 +143,10 @@ Below the title there is a **source-session strip**:
The body renders the artifact according to its actual content type (for example, an HTML report renders inline as a page).
+
+On a phone the detail page degrades to a **read-only reader**: the title is no longer editable inline, the share, change-scope, and delete entries are all hidden, and the toolbar drops the back-to-list button, the file-kind icon, the scope badge, the public-link badge, the "Edited …" time, and the "Recently updated from session" entry, keeping only the title and the artifact body. When the artifact is linked to a file, **Download** remains available under "More actions". The list page and card view are unaffected.
+
+
## Share an artifact
---
@@ -159,6 +186,28 @@ Visitors who open a public link see a read-only content page: next to the title,
Artifacts larger than **16 MiB** can't generate a public link — you'll see a notice that the artifact exceeds the size limit. You can still share it using "Account only" mode.
+## Update a published artifact
+
+---
+
+Republishing the same file does **not** silently overwrite a published artifact. When the agent updates one with `publish_artifact`, it must pass both the artifact's `artifact_id` and the `if_revision` returned by the previous publication (an opaque revision fingerprint shaped like `rev_…`):
+
+| Case | Result envelope | Outcome |
+|---|---|---|
+| `artifact_id` and `if_revision` both match | `status: published`, with an `operation` (`created` / `updated` / `unchanged`) and a new `revision` | The artifact is updated in place (an `unchanged` operation means the content did not change); the link and artifact ID stay the same |
+| Only a file path was given (no `artifact_id`) and the content differs from the published version | `status: needs_review`, `reason: explicit_artifact_id_required` | Nothing is published |
+| `artifact_id` was given but `if_revision` was not | `status: needs_review`, `reason: revision_required` | Nothing is published |
+| `if_revision` does not match the current revision (the artifact changed after you got the revision) | `status: needs_review`, `reason: revision_conflict` | Nothing is published |
+
+On `needs_review`, the agent also writes a copy of the **currently published content** into the session workspace along with the current `revision`; it reads that copy, merges deliberately, and retries with the same `artifact_id` and the new `revision` instead of overwriting blindly.
+
+A few more details:
+
+- When the content is byte-identical to the published version, the result is `operation: unchanged` and the artifact is left alone.
+- Re-presenting the same file with `present_files` does **not** update a published artifact — it only offers preview and download; publishing an update goes through `publish_artifact`.
+- If the artifact was deleted, a path-based republish fails with `artifact_deleted` instead of reviving it; only the "Publish to artifact gallery" button next to the `present_files` file in a session restores a deleted artifact.
+- Failures come back with a code: `unsupported_format` / `permission_denied` / `artifact_deleted` / `storage_unavailable` / `file_too_large` / `invalid_path`, of which `storage_unavailable` is retryable. When the commit cannot be confirmed the tool returns `status: unknown` with an `op_…` operation ID, and the agent does not create a second artifact.
+
## Permissions
---
@@ -171,7 +220,7 @@ Whether an artifact is editable (rename, change scope, delete) is determined by
| Account Owner / admin | Has management access to any artifact in the account, personal or team scope |
| Team member (team artifacts only) | When an artifact belongs to a team (`team_id > 0`), other members of that team can also manage it |
-Artifacts you cannot edit only expose read-only actions such as "Copy link" and "Download" — the "Rename" and "Delete" buttons do not appear.
+Artifacts you cannot edit only expose read-only actions such as "Pin", "Copy link", and "Download" — the "Rename" and "Delete" buttons do not appear. **Pinning is not part of that check**: it is a personal preference rather than an edit, so you can pin any artifact you can see (other members' personal artifacts included), and it only changes your own list order, affecting nobody else.
This differs from the automation rule permission model: the account Owner / admins have management access to **any** artifact, including other members' personal artifacts — there is no "no exemption for personal resources" restriction here.
diff --git a/en/ai-sre/automations.mdx b/en/ai-sre/automations.mdx
index ca06040f..16525a24 100644
--- a/en/ai-sre/automations.mdx
+++ b/en/ai-sre/automations.mdx
@@ -158,6 +158,9 @@ Add the **On-call incident** trigger when you want AI SRE to start automatically
Select one or more severities from `Critical`, `Warning`, and `Info`. When this trigger is enabled, at least one channel and one severity are required.
+
+ **Merge similar incidents into one analysis** is on by default: a new incident from this rule is first compared with the incidents being analysed now (or analysed within the last 10 minutes), and one scoring at least 0.9 joins that analysis instead of starting a session of its own; with it off, every incident is analysed separately. See "Merging similar incidents into one analysis" below.
+
If you create or update a rule through the API, use these fields:
@@ -167,11 +170,33 @@ If you create or update a rule through the API, use these fields:
| `oncall_incident_trigger_enabled` | boolean | Whether the On-call incident trigger is enabled. |
| `oncall_incident_channel_ids` | int64[] | On-call channel IDs to watch. Creating or enabling this trigger requires at least one valid ID. |
| `oncall_incident_severities` | string[] | Incident severities to watch. Supported values are `Critical`, `Warning`, and `Info`; creating or enabling this trigger requires at least one value. |
+| `oncall_incident_merge_similar` | boolean | Whether **Merge similar incidents into one analysis** is on. On by default; omitting this field is treated as on. |
-When a matching event arrives, the system creates a run with `trigger_kind: "oncall_incident"` and passes event context such as `incident_id`, `channel_id`, and `severity` into the session. The same trigger and the same `incident_id` reuse the same run, avoiding duplicate hidden sessions for one incident.
+When a matching event arrives, the system creates a run with `trigger_kind: "oncall_incident"` and passes event context such as `incident_id`, `channel_id`, and `severity` into the session. The same incident — the same trigger and the same `incident_id` — always reuses the same run, avoiding duplicate hidden sessions for one incident; alongside that, when the switch is on, a **different** incident similar enough to one already under analysis also joins that run instead of starting a session of its own, as described under "Merging similar incidents into one analysis" below.
When the run finishes, AI SRE writes one summary comment back to the incident that triggered it: conclusion first, body kept concise, with a link to the full session at the end. That write-back record also carries a persistent **Continue conversation** entry, so you can open that session straight from the conclusion on the incident timeline — see [Incident Timeline · AI SRE Action Records](/en/on-call/incident/search-view-incident#ai-sre-action-records). The comment travels through the incident's existing notification chain (for example, incident card refreshes and thread replies in IM), so whoever is watching the incident sees the analysis without opening the console. This applies to every rule with the On-call incident trigger enabled — including rules with custom prompts — with no extra configuration.
+#### Merging similar incidents into one analysis
+
+**Merge similar incidents into one analysis** (`oncall_incident_merge_similar`, on by default) decides whether an incident event starts a session of its own or joins an analysis already running under **the same rule**. When it is on, an incident that matches the trigger is first compared for similarity against that rule's runs:
+
+- At a similarity of **0.9 or above**, the incident joins the highest-scoring analysis instead of starting a hidden session of its own. 0.9 is a fixed platform value, the same bar [intelligent grouping](/en/on-call/channel/noise-reduction) uses by default, and it does not move with a channel's grouping configuration.
+- Below 0.9, the incident starts a run of its own as usual.
+- With the switch off, no similarity comparison happens and every incident is analysed separately.
+
+The candidates are the incident that analysis is working on plus the incidents already merged into it. The merge affects only AI SRE's run routing: **it does not combine the two incidents in Flashduty**, and the bound incident of the session does not change — both incidents still exist, notify, and escalate on their own. A merged-in incident has no session of its own, so it also gets no summary comment of its own; if the analysis judges that it shares the root cause, it leaves a cross-reference comment on that incident's timeline and covers it in the conclusion.
+
+In that analysis's chat, a merge renders as one process line: `Joined this analysis` + `# ` (the merged incident's name, linking to that incident's own page in a new tab), labelled `Similarity ` (two decimal places). In run history, that run likewise shows which analysis took it.
+
+Two platform limits are fixed and not configurable:
+
+- **One analysis takes in at most 20 incidents.** Once it is full it stops receiving similar incidents, and later ones start a new run, which carries on absorbing.
+- **A finished run can only be joined within 10 minutes of finishing.** A run still `running` or `retrying` is always joinable; a run that finished well (`succeeded` / `partial`) stays joinable for 10 minutes after it completed, and after that a similar incident starts a new run. Failed, skipped, and abandoned runs are never joined.
+
+
+The similarity comparison depends on the account's AIOps capability and on a Pro or higher subscription: if AIOps is switched off or the account's edition does not qualify, no comparison runs and every incident starts a run of its own.
+
+
## Run History
---
@@ -182,7 +207,7 @@ Run history is shown as a table with these columns:
| Column | Notes |
|---|---|
-| Name | The hidden session behind this run. The backend batch-resolves each run's session title (`session_name`, the hidden session's auto-generated name); when it can't be resolved, the cell falls back to the session ID (`session_id`) |
+| Name | The hidden session behind this run. The backend batch-resolves each run's session title (`session_name`, the hidden session's auto-generated name); when it can't be resolved, the cell falls back to the session ID (`session_id`). A run merged into another analysis (see "Merging similar incidents into one analysis" above) has no session of its own, so this column shows the incident it is about (`# `) and names, on the line below, the analysis it joined |
| Started at | The start time of the run |
| Duration | How long the run took |
| Triggers | The trigger type of the run, such as `Schedule`, `HTTP POST`, `On-call incident`, or `Run manually` |
@@ -197,7 +222,8 @@ Run status values:
| `succeeded` | Succeeded |
| `partial` | Partially succeeded |
| `failed` | Failed |
-| `skipped` | Skipped |
+| `skipped` | Skipped. Two situations share this status: the trigger decided not to run (for example, the rule's owner is no longer active, or the schedule was stopped), or the incident was merged into another analysis already running (see "Merging similar incidents into one analysis" above) |
+| `blocked` | Blocked. The run produced real output but stopped on a connector waiting for a human — a connector that is not set up yet, or one that needs an admin to complete authorization in the console |
| `abandoned` | Abandoned (terminated by the system after running too long without completing) |
Three filters are available above the table:
@@ -206,9 +232,25 @@ Three filters are available above the table:
- **Status**: filter by the run statuses above, or choose **All statuses**.
- **Trigger type**: choose from `All trigger types` / `Run manually` / `Schedule` / `HTTP POST` / `On-call incident`.
-Run records returned by the API also include `trigger_kind`, which can be `schedule`, `manual`, `http_post`, `oncall_incident`, or `debug`. `manual` means the run was started through the run-now API, and `oncall_incident` means it was started by a matching On-call incident event.
+Run records returned by the API also include `trigger_kind`, which can be `schedule`, `manual`, `http_post`, `oncall_incident`, or `debug`. `manual` means the run was started through the run-now API, and `oncall_incident` means it was started by a matching On-call incident event. A run merged into another analysis carries no `session_id` of its own: it carries that analysis's session ID in `joined_session_id` and its session name in `joined_session_name`, and both being empty means the run has a hidden session of its own.
+
+Click a row to jump to the chat page of the hidden session for that run (`chat?session_id=`), where you can view the full messages, tool calls, and artifacts of that run. A run merged into another analysis has no session of its own, so clicking it opens the analysis that took over the incident.
-Click any row to jump to the chat page of the hidden session for that run (`chat?session_id=`), where you can view the full messages, tool calls, and artifacts of that run.
+### The reason line on an unsuccessful run
+
+When a run's status is `failed`, `blocked`, `abandoned`, or `skipped`, the **Name** cell shows one extra line under the session name: the **reason** — clamped to one line, with the full text on hover. If the run has no session name to show, the reason takes the cell instead. The reason line sometimes carries a link beside it, which opens in a new tab.
+
+Reasons the platform itself decides on have fixed wording:
+
+| `error_code` | Reason shown | Link |
+|---|---|---|
+| `BalanceNotEnough` | AI SRE is not enabled, or this period's credits or balance are used up. This run did not execute. | Go to AI SRE billing (`/wallet/plan?product=ai-sre`) |
+| `NoLicense` | The On-call subscription has expired or its plan does not include AI SRE. AI SRE is paused. | Go to On-Call subscription (`/wallet/plan?product=oncall`) |
+| `task_run_stale` | The run did not finish in time and was abandoned. | None |
+| `interrupted` | This run was interrupted. | None |
+| `no_final_response` | The run ended without a final reply. | None |
+| `similar_incident_fan_in` | Joined “``”; shows “another analysis” when the name cannot be resolved | None |
+| Any other `error_code` | The reason text recorded by the backend, shown as is; if there is none, the `error_code` itself | None |
Run history is embedded in the rule's detail page, and opening the detail page itself already requires edit permission on that rule — a rule you cannot edit cannot be opened at all (it shows "Automation rule not found or access denied"), so its run history is likewise unreachable.
diff --git a/en/ai-sre/environments.mdx b/en/ai-sre/environments.mdx
index 402ae75e..8e5cf723 100644
--- a/en/ai-sre/environments.mdx
+++ b/en/ai-sre/environments.mdx
@@ -73,6 +73,18 @@ Go to **Environments** in the AI SRE sidebar and switch to the **Cloud** tab at
Environment variables are visible in plaintext to everyone who uses this cloud environment template — do not put secrets or credentials here.
+**Platform-reserved environment variable names.** Some names are injected by the platform itself into the session's runtime environment, and a template may not set them — a template that does is **rejected at save time** (the server is the single source of truth for this list; a hint list in the UI can lag behind it):
+
+| Reserved name | Notes |
+|---|---|
+| `FLASHDUTY_APP_KEY` / `FLASHDUTY_BASE_URL` | The Flashduty API credential and endpoint, used by the built-in `flashduty` skill and by the runtime. |
+| `FLASHDUTY_DOCS_URL` | The static address of the official docs bundle; the built-in `flashduty-docs` skill downloads its copy of the docs from here. |
+| `GH_TOKEN`, the `GH_TOKEN_` prefix, `GH_PROMPT_DISABLED`, the `GIT_CONFIG_` prefix | The GitHub-side token and git configuration the platform injects (an account can have more than one GitHub App installation, so names such as `GH_TOKEN_1` and `GH_TOKEN_2` are reserved too). |
+| `PATH` / `HOME` | The runtime environment's base variables. |
+| `GITLAB_TOKEN`, `GITLAB_ACCESS_TOKEN`, `OAUTH_TOKEN`, `GITLAB_HOST`, `GL_HOST`, `GITLAB_URI`, `GITLAB_API_HOST`, `GITLAB_SSH_HOST`, `GITLAB_SUBFOLDER`, `CA_CERT`, `SKIP_TLS_VERIFY`, the `GLAB_` prefix | GitLab / `glab` authentication, host and TLS configuration; letting a template override any one of these is the same as letting a session bypass the bot identity the platform injects and redirect `glab`'s transport. |
+
+The platform injects these names **before every command**; a same-named entry in the template is discarded and the platform's value wins (this holds identically on the cloud Sandbox and on a BYOC Runner). So use the template's environment variables for business-related, non-sensitive configuration only — when a secret is needed, use MCP member authorization or the relevant App integration.
+
The setup script runs inside a **fresh sandbox (Ubuntu 24.04, running as root)**, **before the agent starts** — typically used to `apt install` packages the agent needs.
### Delete a cloud environment template
@@ -413,6 +425,15 @@ Environment selection is locked once per session: the Environment determined whe
When you open a historical session, the selector shows the Environment that session originally locked to, along with its current status, in read-only mode. If the bound Runner is offline or deleted, the session cannot continue sending messages; reconnect that Runner or start a new session and use the cloud Sandbox.
+### A child session's environment
+
+A dispatched subagent brings its own **child session**, whose execution-environment rules differ from the root session's:
+
+- **It inherits the parent by default**: when `agent_dispatch` carries no `environment`, the child session **shares the parent's sandbox / workspace**, so it can read the parent's files directly — and the environment shown on the child session panel is therefore the same as the parent's.
+- **Only a cross-environment dispatch gets its own environment**: when the subtask is sent to another environment (via `agent_dispatch`'s `environment` argument — another BYOC Runner or the cloud environment, see [Agent](/en/ai-sre/agents)), the child session gets **its own** sandbox or Runner scratch directory and cannot read the parent's files.
+- **The child session panel offers no environment selector**: it only shows that child session's bound environment and its current status, in **read-only** form — the cloud environment, or the self-hosted Runner's name — reusing the same status vocabulary as the root session's read-only display (no extra marking while it is healthy; a short label and a hover explanation for offline / degraded / never connected / deleted).
+- **An A2A child session shows no runtime environment**: the remote agent executes in its own environment, and the platform does not choose that location.
+
## Scope
---
diff --git a/en/ai-sre/im.mdx b/en/ai-sre/im.mdx
index caeb7410..62d18353 100644
--- a/en/ai-sre/im.mdx
+++ b/en/ai-sre/im.mdx
@@ -79,6 +79,21 @@ In a connected IM group where **Allow AI SRE conversations in IM** is enabled, *
The **reply mode** is configurable (off / first / all), controlling whether AI SRE @mentions the person who asked, and whether it replies **in-thread** or in the main channel. In a busy large group, in-thread replies keep the investigation discussion focused without flooding the channel.
+### How a Reply Reaches the Chat
+
+AI SRE's replies in IM are delivered **only through the `reply` tool** — ordinary assistant text is private working text and is never posted automatically. Once you hand it an investigation, every message that appears in the group is one it deliberately submitted, and it is one of two kinds:
+
+- **Result**: an answer to your concrete question, or an actionable interim finding.
+- **Progress**: a one-or-two-sentence update while work is still going on — confirmed progress, what it is currently waiting on, a real blocker. A progress message is **not the answer**: it does not finish the turn, and the result is delivered separately as usual.
+
+If a turn ends naturally with the conclusion written as ordinary text instead of submitted through `reply`, the system adds one automatic **delivery-only** correction: that correction exposes `reply` alone and runs no investigative or mutating operations; if nothing has been submitted even then, the turn ends with an error — that draft never appears in the group. Non-natural endings — a cancellation, an interruption by a new message, a dispatched subagent, a wait on a subagent's reply, a truncated output — do not trigger this correction.
+
+This delivery contract applies only to **human-initiated IM requests**: console sessions still answer with streaming text, and war-room auto-diagnosis is an unattended background turn that also keeps native streaming output.
+
+
+A single reply also has a soft reading-length budget: when it is too long, `reply` refuses it once and suggests trimming the repetition, or publishing the detailed evidence and comparisons as an artifact and sending only the key conclusions plus the artifact link into the chat. That is why a long analysis in IM usually arrives as "conclusions + artifact link" rather than one very long message — see [Artifacts](/en/ai-sre/artifacts). If you explicitly ask to have the full report pasted directly into the chat, this constraint does not apply.
+
+
## War Room Auto-Diagnosis
---
@@ -120,6 +135,21 @@ While any standing task is alive, the IM session's root message stays open (the
Notification rounds follow a silent "no message = no news" semantics: if a notification round has nothing new to deliver, AI SRE closes that round silently — **no placeholder receipt** is posted to the chat, and the monitoring card is not re-posted either. A new message appears in the chat only when there is a real new finding.
+### Progress Messages During a Long Investigation
+
+When an investigation runs long, the chat can stay quiet for a while — interim prose is not delivered, and the monitoring card only appears once a standing task exists. So when an IM request has been **silent for about 60 seconds** with no public reply submitted, the system may add a transient reminder telling the model how long you have already been waiting, and lets the model decide whether to post a progress message:
+
+- **Reminder cadence**: two reminders for the same request are at least **3 minutes** apart, and one uninterrupted silence stretch gets **at most two reminders**. Any successfully submitted reply (result or progress) opens a new silence stretch and resets the count, but does not shorten that 3-minute minimum gap.
+- **Where the waiting time starts**: at the moment your message entered the platform's pending queue, so queueing time counts — not when the agent actually started working on it.
+- **A reminder sends nothing by itself**: it only tells the model how long you have waited; it never writes anything to the group on the model's behalf. When the wait was already explained and nothing has changed, or the model is waiting on information from you, sending nothing is the correct choice.
+- **A progress message is not the answer**: it says "still investigating, what it is stuck on, what it checks next", does not finish the turn, and the result follows separately.
+
+
+The "silence" here is not the same thing as the "no message = no news" rule above: that rule is about a **notification round** with nothing new, which posts no placeholder receipt; this one is about **a person waiting** while the agent has posted nothing public yet.
+
+
+Waiting reminders appear only on **attended IM requests**; the console, API, and automation channels have none.
+
## Connections and Authorization
---
diff --git a/en/ai-sre/mcp.mdx b/en/ai-sre/mcp.mdx
index 6804a8c3..920f641d 100644
--- a/en/ai-sre/mcp.mdx
+++ b/en/ai-sre/mcp.mdx
@@ -72,6 +72,22 @@ Installed MCP servers record a `source_template_name` field that points back to
A template that **requires a Runner** (the `requires_runner` flag in its details) can only be used in an environment where you have deployed a BYOC Runner; cloud Sandboxes do not support it. `requires_runner` is independent of transport, and today's 50-template catalog contains both kinds: 23 **HTTP Streaming** templates that need no Runner, and 27 **stdio** templates that do — including Alibaba Cloud OpenAPI, Alibaba Cloud Observability, Nightingale (n9e), Gitee, GreptimeDB, Prometheus, Grafana (Self-Hosted), Kafka (Confluent), AWS, ClickHouse, MySQL, PostgreSQL, Alibaba Cloud RDS, Redis, and MongoDB. Those stdio templates are launched locally on each environment via `uvx` / `npx`, and a cloud Sandbox cannot launch local subprocesses; confirm your account has a working BYOC Runner configured before installing. See [Environments (BYOC)](/en/ai-sre/environments).
+### Systems only Monitors can query
+
+The catalog also contains a class of rows for **systems only Monitors can query** (currently Loki, VictoriaLogs, and Oracle): they appear under their category (Observability / Database), and when MCP / App / Skill rows are mixed on the same screen they carry a `Monitors` kind tag. They are **not connectors you can onboard from the marketplace** — they are entry points meaning "once you add a datasource in Monitors, AI SRE can query this system" — so they never appear in the **Recommended** block above (that block lists connectable templates).
+
+Clicking such a row (or its **+**, which opens the same panel) opens a panel with **no Connect button**, showing only:
+
+- The account's **datasources of that type** and their status, e.g. "N connected · runs through the alert engine — no URL, token or Runner needed"; with none yet it reads "No 〈type〉 datasource yet · add one in Monitors and you can query it, no Runner needed". The row's **+** appears only while the account has no datasource of that type and you have permission to add one; otherwise that slot shows "N connected" or "Added by a Monitors admin".
+- When datasources of that type exist, the primary button is **Try it in chat**: it opens a new session prefilled with a question against your datasource (it is not sent automatically).
+- With none yet, it offers **Add more in Monitors →**, opening Monitors → Datasources → Add in a new tab (the type preselected); a member without add permission only sees "A Monitors admin adds it and this updates automatically" and is not offered that action.
+
+Some MCP templates support both routes — a direct MCP connector and the Monitors one (for example Prometheus, MySQL, PostgreSQL, ClickHouse, Elasticsearch, SLS). Those rows are tagged `MCP · Monitors` and their dialog adds an "Access method" choice between the two.
+
+
+Row names share a namespace with template names: if a system already has a template of the same name, only the template row is shown (the template row is the one carrying connection parameters). On a private deployment **without the Monitors module**, these rows are not listed and a template's dialog offers no Monitors route — the list API returns `monit_available: false` and the console hides the related affordances based on it.
+
+
## Adding an MCP Server
---
@@ -88,7 +104,7 @@ Go to **Plugins → MCP**. The list page has two entries in the top-right corner
| Name | string | Yes | The server name, used as the identifier when agents call it (e.g., `sqlite-explorer` in `mcp:sqlite-explorer/query`). Must start with a letter; may only contain letters, digits, `-`, and `_`; length 1–255. **Case-insensitive and unique** within an account; cannot duplicate a built-in server name. |
| Transport | enum | Yes | How the agent communicates with the server. See "Transport" below. |
| Scope | Shared / Team | Yes | The scope of this MCP server: **Shared** (visible account-wide) or a specific **Team** (visible only to members of that team). See "Scope" below. |
-| Execution Environment | Cloud / BYOC Runners (multiple) | No | Controls where this MCP server is available. Select Cloud and one or more visible BYOC Runners; leave empty to make it available everywhere. It does not route calls: an MCP call still runs in the current AI SRE session's environment. For a service available only on a private network, select only the Runner(s) that can reach it. See [Environments (BYOC)](/en/ai-sre/environments). |
+| Execution Environment | Cloud / BYOC Runners (multiple) | No | Controls where this MCP server is available. Select Cloud and one or more visible BYOC Runners; leave empty to make it available everywhere. It does not route calls: an MCP call still runs in the current AI SRE session's environment. For a service available only on a private network, select only the Runner(s) that can reach it. Conversely, when this list **does not** include the session's own environment, that session cannot call it directly — but the agent still knows the server exists and which environment can run it; see "Cross-environment connectors" below. See [Environments (BYOC)](/en/ai-sre/environments). |
| Description | string | Yes | Describes what this server does, for identification in the list. |
@@ -204,6 +220,19 @@ To confirm which tools a given MCP server actually exposes in a particular envir
Reading Flashduty incidents, alerts, and other data is a **built-in** agent capability: the **Flashduty MCP server** is injected directly into the agent at the start of every session by the runtime, bypassing this page's MCP server list API — it does not appear in the server list above, and there is nothing to configure, enable, or view here for it. This capability is maintained by the platform and is available to every account by default.
+### Cross-environment connectors
+
+An MCP server can be bound to execution environments **other than the current session's own**. That session cannot call it directly — execution environments do not share a network, and a tool call is always made from the session's own environment. But the agent does not conclude the connector is absent:
+
+- **The agent knows about it from the start**: at session start the agent sees the connectors that are connected but bound to another execution environment — their names, what they do, and **which execution environments they run in**. The same connector can resolve to different names in different environments, so directory entries carry a team annotation to keep them distinguishable.
+- **A search states where it can run**: when a search matches such a connector, the result carries the environments it runs in.
+- **Asking for it by name is not answered with "not found"**: when the agent looks a connector up by name, it gets an explanation that the connector is connected but bound to another execution environment, plus how to use it — rather than being told the account has no such thing.
+- **The way to use it is to dispatch a subagent into that environment**: the agent hands the work to a subagent running in the target execution environment (`agent_dispatch`'s `environment` argument — see [Agent](/en/ai-sre/agents)), and that subagent discovers and calls the server's tools on its own side, then brings the results back into the conversation.
+- **Only environments it can actually dispatch to are listed**: the targets are **account-scope Runners** plus the Runners of the **team the current session is bound to**, and online ones (plus the bound cloud environment). Environments that are offline, never connected, deleted, or scoped to a team this session cannot dispatch to are annotated with the reason, and the agent says so plainly instead of pretending the call is possible.
+- **Only the conversation's main session sees this directory**: a dispatched subagent has no dispatch capability of its own and reports only the connectors it can call directly in its own environment; when another environment is needed, the subagent reports back to its parent.
+
+Conversely, to make a connector directly usable **here**, add the current environment to that server's Execution Environment list (provided the server is reachable from this session). See [Environments (BYOC)](/en/ai-sre/environments) for how the environments themselves differ.
+
## Scope
---
diff --git a/en/ai-sre/sessions.mdx b/en/ai-sre/sessions.mdx
index 3b67ed6d..68f1cb78 100644
--- a/en/ai-sre/sessions.mdx
+++ b/en/ai-sre/sessions.mdx
@@ -158,6 +158,10 @@ The moment you send, the frontend optimistically marks the turn as "running"; th
While a turn is running, **the Send button changes to a Stop button**. Clicking Stop immediately interrupts the current turn: the UI reflects this right away, and the interrupted turn is labeled "Interrupted" and remains visible after a page refresh.
+
+The session stream pushes progress as **frames**, and every frame may carry `is_error` and `error_code` to mark a **runtime failure**. The test is deliberately narrow: a frame only carries them once the runtime has converted a real failure into a user-facing error and stamped that marker on the persisted event row. **The platform's internal control rows (bookkeeping events such as turn settlement and automation turn outcomes) and raw provider / model error text never enter this path.** So a frame with an `error_code` can be consumed as "a runtime error the platform is exposing to the user"; conversely, the absence of these fields does not mean a frame carries no internal diagnostics. This is not the same thing as the `error` line in [Session data export](#session-data-export) below, which reports the export stream itself failing: `is_error` / `error_code` ride on ordinary event frames, while `error` is a separate line appended when the stream itself breaks.
+
+
### Queueing Messages While Running
The input box remains active while a turn is running: you can keep typing and send messages, which are queued and executed in order after the current turn completes. Queued messages appear in a collapsible card above the input box, with a header showing the queue count (e.g. "3 queued"); each queued message can be edited or removed individually, and when more than one message is queued, the card also offers a **Clear all** action in its top-right corner.
@@ -257,7 +261,7 @@ Channel differences: only the console (`web`) renders this card. IM and API chan
### Subagents
-When the agent delegates a subtask, a clickable **dispatch reference line** appears in the conversation: a status ring, a type badge (Agent / A2A), the subtask's name and current intent, and right-aligned status and elapsed time; once finished it adds the tool-call count / token usage / total duration, and on failure a red reason line appears underneath. The reference line is a compact single row and carries **no stop button** — stopping lives in the task panel's detail header (see "Task Panel and Background Tasks" below). Clicking the line opens a subagent session panel on the right, side by side with the main conversation — the main chat area shrinks accordingly rather than being covered by a modal. The panel can be expanded to fill the main area, or collapsed back to the side-by-side layout.
+When the agent delegates a subtask, a clickable **dispatch reference line** appears in the conversation: a status ring, a type badge (Agent / A2A), the subtask's name and current intent, and right-aligned **target execution environment**, status and elapsed time; once finished it adds the tool-call count / token usage / total duration, and on failure a red reason line appears underneath. The environment segment states the location only — the **cloud environment**, or the BYOC Runner's name (just "self-hosted" when no name resolves) — and carries **no status dot**; an A2A delegation (the remote agent executes on its own side) and legacy tasks with no environment binding show no such segment. The reference line is a compact single row and carries **no stop button** — stopping lives in the task panel's detail header (see "Task Panel and Background Tasks" below). Clicking the line opens a subagent session panel on the right, side by side with the main conversation — the main chat area shrinks accordingly rather than being covered by a modal. The panel can be expanded to fill the main area, or collapsed back to the side-by-side layout.
Besides running, completed, failed, and interrupted, a subtask can also sit in one of two "needs you" states:
diff --git a/en/ai-sre/skills.mdx b/en/ai-sre/skills.mdx
index 73311775..1a138fae 100644
--- a/en/ai-sre/skills.mdx
+++ b/en/ai-sre/skills.mdx
@@ -84,6 +84,12 @@ Tools can be specified in two ways:
The AI SRE runtime bundles a few skills that are available without installation. `flashduty` is one such reference: it uses the `fduty` CLI to cover the entire Flashduty API, allowing the agent to investigate incidents, read AI insights, query alerts, correlate changes, and more — use it as a template when writing your own skills. Another bundled skill is `github`, which the agent self-selects from `` to let AI SRE work directly inside a GitHub repository — explore code, investigate PRs and commits, and open a PR or issue on request; it requires the GitHub App (cloud) or the runner host's `gh` (BYOC). A third bundled skill is `gitlab`, symmetric with `github` in capability: the agent self-selects it to work directly inside a GitLab repository — explore code, trace MRs and issues, and open an MR or issue on request; it requires the GitLab App (cloud) or the runner host's `glab` (BYOC). See [Apps](/en/ai-sre/apps).
+
+There is also `flashduty-docs`: it downloads Flashduty's **official documentation** (both the Chinese and English page sets, the glossary and the Open API specs) into the runtime environment and answers questions about **how the product itself works** by searching it by topic — features, settings, limits, plans, compatibility, SDK behaviour and performance impact, API fields and errors — across On-call, RUM, Monitors, AI SRE, Status Page, the platform and Open API. Its answers cite public docs.flashduty.com links and are drawn from the docs alone; where the docs do not cover something it says so plainly. It only reads documentation and does not replace hands-on work (actual RUM instrumentation belongs to `rum`), and the docs bundle's address is injected by the platform, so there is nothing to configure.
+
+
+
+Those are examples, not the whole set: the runtime also bundles `init`, `insight`, `artifact-design`, `alert-governance`, `monit-query` and more, and the set grows between releases. What the agent actually sees in a session is ``; to find out which bundled skills a given session has, just ask the agent.
## Install from Marketplace
diff --git a/en/developer/cli.mdx b/en/developer/cli.mdx
index bc135366..5474910d 100644
--- a/en/developer/cli.mdx
+++ b/en/developer/cli.mdx
@@ -341,7 +341,9 @@ Core fields for `application-create` / `application-update`:
| `--is-private` | Restrict access to team members only |
| `--no-geo` | Disable geographic inference |
| `--no-ip` | Disable IP collection |
-| `--data` | Add `alerting` and `tracing` objects when you need notification or trace-link configuration |
+| `--data` | Add `alerting` (notification configuration), `links` (external-link integration), `repositories` (linked source repositories), and `tracing` (trace-link configuration) objects |
+
+`repositories` inside `--data` links the application to the source code repositories that build it: an ordered array whose first entry is the primary repository, at most 10 entries, each entry a `repo` (GitHub repository in `owner/name` form, required) plus an optional `subdir` (the directory holding the application inside the repository, relative to the repository root — `.` is the repository root, and an empty value is saved as `.`). Passing it on create establishes the links; on update the field is presence-based — passing it replaces the whole list, an empty array clears every link, and omitting it leaves them unchanged. `application-info`, `application-infos`, and `application-list` all echo the field back. Linking grants no access by itself: AI sessions can only read repositories granted to the account's GitHub App installations (see [Apps](/en/ai-sre/apps)). This field is currently available through the API and CLI only; the console's application management page does not expose repository links.
`application-webhook-test` returns `ok`, `status_code`, and `message`, which makes it suitable for verifying that a RUM alert webhook really accepts a sample delivery from Flashduty.
@@ -683,7 +685,7 @@ In `json`/`toon` mode the rows default to the compact fields `incident_id`, `tit
### Full command coverage
-Beyond the curated commands above, the CLI now provides **full coverage** of the Flashduty OpenAPI through a spec-driven code generator. The OpenAPI spec the generator reads contains **340 API operations**, and the CLI generates resource-organized commands for **336** of them; the remaining four are provided by hand-written commands — the streaming export `session-read-export` (`session export`), the multipart uploads `mapping-data-write-upload` and `skill-write-upload` (`enrichment mapping-data-upload`, `safari skill-upload`), and the path-parameterized `monit-prometheus-read-label-values` (`monit prometheus-api-v1-label-{label_name}-values`, see "Query Prometheus label values" below) — and are organized into top-level command groups alongside the generated ones. In addition to the On-call domain (incident, incident-trigger-subscription, change, channel, field, status-page, template, and more), it also covers:
+Beyond the curated commands above, the CLI now provides **full coverage** of the Flashduty OpenAPI through a spec-driven code generator. The OpenAPI spec the generator reads contains **338 API operations**, and the CLI generates resource-organized commands for **334** of them; the remaining four are provided by hand-written commands — the streaming export `session-read-export` (`session export`), the multipart uploads `mapping-data-write-upload` and `skill-write-upload` (`enrichment mapping-data-upload`, `safari skill-upload`), and the path-parameterized `monit-prometheus-read-label-values` (`monit prometheus-api-v1-label-{label_name}-values`, see "Query Prometheus label values" below) — and are organized into top-level command groups alongside the generated ones. In addition to the On-call domain (incident, incident-trigger-subscription, change, channel, field, status-page, template, and more), it also covers:
- **AI SRE (`safari`)**: a2a-agents, artifacts, automations, knowledge, mcp-servers, sessions, skills, and more
- **Alerting & noise reduction**: alert, alert-event, enrichment (alert-rules, rule-sets), route
diff --git a/en/developer/go-sdk.mdx b/en/developer/go-sdk.mdx
index 53e28ee9..1b832fef 100644
--- a/en/developer/go-sdk.mdx
+++ b/en/developer/go-sdk.mdx
@@ -184,13 +184,13 @@ Endpoints are grouped by service and hang off the client: the call convention is
`client.Artifacts` (AI SRE artifacts) covers the 11 operations under `/safari/artifact/*`: on the gallery read side `ReadGet` (get a single published artifact by ID), `ReadList` (list artifacts visible to the caller, with title substring search and `scope` (`all` / `personal` / `team`) plus `team_ids` filtering), and `ReadGetFileState` (probe up to 50 presented-file IDs (`pf_` prefix) in one call for ones that already have a live published artifact); on the file side `ReadSign` (issue short-lived download/preview URLs for a presented file, valid 5 minutes, `expires_in` is fixed at 300) and `ReadStream` (download or preview the file's bytes with a signed token — the success body is a file, not a JSON envelope, with the raw bytes on `Response.Raw`); on the write side `WritePublish` (publish a session-produced file to the gallery), `WriteUpdate` (rename the artifact or transfer it between personal and team scope), and `WriteDelete` (detach it from the gallery; the source file stays with its session); public sharing `WriteShareEnable` (turn on anonymous public sharing and return the public link — anyone with the link can view it, no login required), `WriteShareRevoke` (turn sharing off; the link stops resolving immediately), and `WriteShareSync` (refresh the public snapshot with the latest content — when `share_enabled` is true and `share_file_id` differs from `file_id`, the snapshot is stale and calling this refreshes it). Exported types include `PublishedArtifactItem`, `ArtifactShareState`, `SignedUrLs`, and the various `Artifact*Request` / `Artifact*Response` structs.
-`client.Members.MemberNotify` (`POST /member/notify`, `memberNotify`) sends an email to account members on behalf of the caller, and is callable only with a credential minted for an AI SRE session — any other credential, a plain `app_key` included, is rejected with `AccessDenied` (check it with `IsAccessDenied` / `ErrorCodeOf`). Request fields: `subject` (required, 1–200 characters) and `html` (required, the entire email body the recipients see, up to 102,400 bytes), while `person_ids` is optional (at most 20, no duplicates; omitted or empty sends to the caller only). `script`, `meta`, `link`, and `base` tags and `on*` event handlers are silently stripped from `html` before sending; a `style`, `svg`, `iframe`, `object`, `embed`, `form`, `input`, or `button` tag, an `img` without an `https` `src`, or a link outside `http`/`https`/`mailto` instead fails the call with `InvalidParameter` before anything is queued, naming each offending construct. Delivery is asynchronous, so `accepted` only means the email was queued; the response returns one entry per recipient in `recipients[]` (`person_id` plus `status`), where `status` is `accepted` or `skipped`, and a skipped entry carries a `reason` of `not_member`, `no_email`, `email_disabled`, `duplicate`, `rate_limited` (at most 20 emails per recipient per hour), or `send_failed`.
+`client.Members.MemberNotify` (`POST /member/notify`, `memberNotify`) sends an email to account members on behalf of the caller, and is callable only with a credential minted for an AI SRE session — any other credential, a plain `app_key` included, is rejected with `AccessDenied` (check it with `IsAccessDenied` / `ErrorCodeOf`). Request fields: `subject` (required, 1–200 characters) and `html` (required, the entire email body the recipients see, up to 102,400 bytes), while `person_ids` is optional (at most 20, no duplicates; omitted or empty sends to the caller only). `script`, `meta`, `link`, and `base` tags and `on*` event handlers are silently stripped from `html` before sending; a `style`, `svg`, `iframe`, `object`, `embed`, `form`, `input`, or `button` tag, an `img` without an `https` `src`, or a link outside `http`/`https`/`mailto` instead fails the call with `InvalidParameter` before anything is queued, naming each offending construct. Delivery is asynchronous, so `accepted` only means the email was queued; the response's `recipients[]` returns one entry per recipient (`person_id` plus `status`), where `status` is `accepted` or `skipped`, and a skipped entry carries a `reason` of `not_member`, `no_email`, `email_disabled`, `duplicate`, `rate_limited` (at most 20 emails per recipient per hour), or `send_failed`. The response carries only two fields, `recipients[]` and an optional `agent_instructions`, and never echoes the sanitized email HTML back; `agent_instructions` is returned when the submitted `html` does not follow the default email layout (no `max-width:600px` wrapper table) and holds guidance addressed to the calling AI SRE agent on how to conform — advisory only, so a layout the caller deliberately chose needs no change.
`client.Diagnostics.QueryData` runs a synchronous query via `POST /monit/query/data` and returns a stable `query_result.v1` structured result (`result.kind` is one of `frames`, `records`, or `samples`). This API requires monit-edge v0.65.0 or later. For log-pattern and metric-trend analysis, use `client.DataSources.ToolsInvoke` with `prometheus.metric_trends`, `loki.log_patterns`, or `victorialogs.log_patterns`.
`client.Diagnostics.QueryExplore` (`POST /monit/query/explore`, `monit-read-query-explore`) runs an explore query against a configured data source and returns the data source's native `explore_result.v1` shape: `ExploreData.format` is always `explore_result.v1`; `result.kind` is `frames` (columnar tables or time series, see `ExploreFrame` / `ExploreField`), `samples` (instant values with labels, `ExploreSample`), or `logs` (log entries as `ExploreLogEntry`, at most 1,000, with `has_more` set when truncated and `applied_limit` reporting the effective cap); and `execution.effective_step_seconds` echoes the step actually used after applying `max_data_points` and `min_step_seconds`. Request fields: `datasource_id` (from `/monit/datasource/list`, and it must belong to the authenticated account), `expr` (a query expression in the data source's native language — PromQL, LogsQL, SQL, and so on), `args` (macro substitutions keyed by variable name, used for Grafana-style variables; required in the request, so pass an empty `map[string]string{}` when you have none — a nil Go map serializes to `null`, which does not satisfy the schema), and `execution` (`QueryExploreExecution`: `kind` is `instant`, `range`, or `window`; `to_ms` is required for all three; `range` additionally requires `from_ms` and `max_data_points`, `window` additionally requires `from_ms`; `min_step_seconds` is accepted only for `range`). How it differs from `QueryData`: reach for `QueryExplore` when you need the data source's native result shape (raw logs included) and `QueryData` when you need the stable `query_result.v1` contract. This API requires the **data source view** (`monit`) permission and monit-edge v0.68.0 or later in supported deployments.
-Beyond application management, `client.Applications` (RUM applications) covers the five **RUM remote configuration** methods: `RemoteConfigReadGet` (`POST /rum/application/remote-config/get`, read the live configuration and its `version`), `RemoteConfigReadHistoryList` (`/rum/application/remote-config/history/list`, page through published history versions), `RemoteConfigReadPreview` (`/rum/application/remote-config/preview`, evaluate a draft configuration against a simulated client's `env` / `app_version` / `sdk` and return the matching rule index `hit_rule_index` plus the `values` finally delivered, without publishing), `RemoteConfigWriteUpdate` (`/rum/application/remote-config/update`, publish a complete new configuration), and `RemoteConfigWriteHistoryRevert` (`/rum/application/remote-config/history/revert`, republish a history version's content as a new version). Main types: `RemoteConfig` (`enabled`, `activation`, `default`, `rules`, `custom`, `refresh_on_foreground`), `RemoteConfigValues` (the four SDK knobs a configuration can set), `RemoteConfigRule` (`match` conditions plus the `set` values), `RemoteConfigHistoryItem` (with `version`, `reason`, `content_hash`, and `updated_by`), and the `GetRemoteConfigRequest` / `ListRemoteConfigHistoryRequest` / `PreviewRemoteConfigRequest` / `UpdateRemoteConfigRequest` / `RevertRemoteConfigRequest` structs with their matching `*Response` types.
+Beyond application management, `client.Applications` (RUM applications) covers the five **RUM remote configuration** methods: `RemoteConfigReadGet` (`POST /rum/application/remote-config/get`, read the live configuration and its `version`), `RemoteConfigReadHistoryList` (`/rum/application/remote-config/history/list`, page through published history versions), `RemoteConfigReadPreview` (`/rum/application/remote-config/preview`, evaluate a draft configuration against a simulated client's `env` / `app_version` / `sdk` and return the matching rule index `hit_rule_index` plus the `values` finally delivered, without publishing), `RemoteConfigWriteUpdate` (`/rum/application/remote-config/update`, publish a complete new configuration), and `RemoteConfigWriteHistoryRevert` (`/rum/application/remote-config/history/revert`, republish a history version's content as a new version). Main types: `RemoteConfig` (`enabled`, `activation`, `default`, `rules`, `custom`, `refresh_on_foreground`), `RemoteConfigValues` (the four SDK knobs a configuration can set), `RemoteConfigRule` (`match` conditions plus the `set` values), `RemoteConfigHistoryItem` (with `version`, `reason`, `content_hash`, and `updated_by`), and the `GetRemoteConfigRequest` / `ListRemoteConfigHistoryRequest` / `PreviewRemoteConfigRequest` / `UpdateRemoteConfigRequest` / `RevertRemoteConfigRequest` structs with their matching `*Response` types. The application's own `WriteCreate` / `WriteUpdate` / `ReadInfo` / `ReadInfos` / `ReadList` also carry `repositories` (`[]RUMApplicationRepository`, a body-only field rather than a top-level flag): it links the application to the source code repositories that build it — ordered, first entry is the primary repository, at most 10 entries, each one a `Repo` (GitHub repository in `owner/name` form, required) plus an optional `Subdir` (the directory holding the application inside the repository, relative to the repository root — `.` is the repository root, an empty value is saved as `.`). Passing it on create establishes the links; on update the field is presence-based — passing it replaces the whole list, an empty array clears the links, and omitting it leaves them unchanged; `ReadInfo` / `ReadInfos` / `ReadList` echo it back on `RUMApplicationItem`. Linking grants no access by itself: AI sessions can only read repositories granted to the account's GitHub App installations. This field is currently available through the API and SDK only; the console's application management page does not expose repository links.
`client.DataSources.ToolsInvoke` (`POST /monit/datasource/tools/invoke`, `monit-datasource-tools-invoke`) executes one deterministic tool against a configured datasource: `tool` is a single tool name prefixed by the datasource type (e.g. `mysql.overview`), and `params` is the tool-specific JSON parameters (omitted means `{}`; an explicit `null` is invalid). Alongside diagnostic tools, the entry supports `.query` query tools (`prometheus`, `mysql`, `postgres`, `oracle`, `clickhouse`, `elasticsearch`, `loki`, `victorialogs`, `sls`, `tencent_cls`); the `/monit/query/data` entry stays unchanged. It requires all currently online routable Edge sessions in the cluster to support the v0.71.0 base invoke protocol (individual tools may require a newer implementation), and there is no tool catalog, no automatic replay, and no fallback to legacy diagnose. The request body limit is 128 KiB, the complete success response limit is 10 MiB, and the tool timeout is at most 25 seconds; the response is a `DatasourceToolResult` (`data` is tool-specific JSON, never null, `summary` is optional, and a `truncated` object with `reason` indicates truncation).
@@ -253,6 +253,16 @@ zero := inc.StartTime.IsZero() // whether it's the unset sentinel
**Types shared by requests and responses are the same.** `AlertRuleV2` (the return type of `AlertRules.ReadInfoV2` / `WriteCreateV2` / `WriteUpdateV2`, i.e. `POST /monit/rule/v2/info`, `/v2/create`, and `/v2/update`) keeps `created_at` / `updated_at` as bare `int64` Unix seconds with `omitempty`, not `Timestamp`. Such fields have no `Unix()` / `String()` / `IsZero()` methods, so convert the seconds yourself — writing `rule.UpdatedAt.Unix()` as in the `Timestamp` examples will not compile.
+
+`AlertRuleV2`'s `investigation_targets` (the same return type of `AlertRules.ReadInfoV2` / `WriteCreateV2` / `WriteUpdateV2`) changed shape as well, so callers still sending the old fields have to migrate:
+
+- The field itself is `[]InvestigationTarget`, at most 20 entries with no duplicates; on the update endpoint it is presence-based — omitting it keeps the saved configuration, sending `[]` clears it.
+- Each entry is a **closed tagged union**, and `Kind` decides which sub-object you must supply: `dashboard` (opens a dashboard panel) requires `Dashboard` and `query` (opens an Explore query) requires `Query`; supplying the other one is rejected, and so is an unknown field inside an entry.
+- `TimeRange` (`InvestigationTimeRange`: `BeforeSeconds` and `AfterSeconds`) is required on **every** saved entry — both directions are non-negative and at least one is greater than zero.
+- The `Dashboard` entry (`DashboardInvestigationTarget`) replaces the old `VariableBindings` (keyed by dashboard variable name, with values of `{source: event_label, key: label name}`) with `Variables`: a `map[string]string` keyed by variable name whose values are literal, and may reference event labels through `{{ }}` templates (the old `InvestigationVariableBinding` type is gone). `DashboardID` and `Variables` are required and `TargetID` is optional; `DashboardID` and `TargetID` must both be canonical UUIDv7 values.
+- The `Query` entry uses `QueryInvestigationTarget`: both `DatasourceID` (the data source ID) and `Query` are required, and `Query` is a `DashboardQuery` — `Mode` is `instant`, `range`, or `window`, `Expr` is an expression in the target data source's own language that may use `{{ }}` templates, `Args` are named parameters passed through verbatim and must not contain templates, and `MinStepSeconds` is only accepted when `Mode` is `range`.
+
+
## Pagination
---
diff --git a/en/monitors/data-sources/data-sources.mdx b/en/monitors/data-sources/data-sources.mdx
index f0b64e65..49347742 100644
--- a/en/monitors/data-sources/data-sources.mdx
+++ b/en/monitors/data-sources/data-sources.mdx
@@ -36,7 +36,7 @@ Of these, the 5 **diagnostic-only types** — Redis Node, Redis Sentinel, MongoD
The data source list shows only the data sources you are allowed to query, with the following information:
-- **Name**: The identifier for the data source
+- **Name**: The identifier for the data source; when the row carries a team authorization, the name is followed by **Managing team** and **Query-allowed team** tags (see [Authorization tags in the Name column](#authorization-tags-in-the-name-column))
- **Type**: Data source type with icon
- **Connection URL**: The access address of the data source
- **Associated Alert Engine**: The bound alert engine cluster name, with engine online status indicator
@@ -45,7 +45,17 @@ The data source list shows only the data sources you are allowed to query, with
The list is filtered by query permission: when you have no query (readonly) permission on a data source, the whole row is absent from the response — you can neither see it nor find it with the search box — so the list may hold fewer rows than the account actually has. The owner account, the Admin role, the creator, and members of the managing team or the readonly teams always see it. See the "Data source permissions (team authorization)" section below for the authorization model.
-You can filter data sources by name or type using the search box. The list auto-refreshes every 5 seconds to reflect real-time engine connection status.
+You can filter data sources by name, type, connection address, or note using the search box. The list auto-refreshes every 5 seconds to reflect real-time engine connection status.
+
+### Authorization tags in the Name column
+
+The Name column states the authorization role of each data source, so a glance is enough to tell which ones have an owner:
+
+- A row with `manage_team_id` carries a green **Managing team** tag showing the team name.
+- Every team in `readonly_team_ids` gets its own grey **Query-allowed team** tag, also showing the team name.
+- Hovering a tag names the role it stands for (managing team or query-allowed team).
+- Rows with neither field set carry no tag at all — the whole tenant being able to query and manage is the norm and needs no label.
+- Team names are fetched asynchronously: while the managing team's name is still in flight the row shows a placeholder skeleton, so it never flashes a wrong "Deleted team", and a name that still cannot be resolved once the lookup settles renders as **Deleted team** — that authorization no longer matches anything (after a team is deleted, the creator or an administrator can take over and fix it; see "Validation and normalization" above).
### Enable datasource
@@ -89,14 +99,26 @@ There are exactly three legal authorization configurations:
### What authorization changes
- **List**: only data sources you are allowed to query are returned. A data source you may not query is absent from the response entirely; each row carries a `my_perm` field whose value is `manage` or `readonly`, plus the authorization fields (`manage_team_id`, `readonly_team_ids`, `creator_id`). The frontend only consumes these decisions; it never computes them.
-- **Management operations**: the data source detail (which returns connection credentials), edit, delete and the enable toggle all require `manage`, otherwise the request is rejected with an access-denied error (`only the managing team can manage this datasource`).
+- **Management operations**: the data source detail (which returns connection credentials), edit, delete and the enable toggle all require `manage`, otherwise the request is rejected with an access-denied error (`only the managing team can manage this datasource`). For rows you lack `manage` on, the console offers a read-only view instead (see "Edit and delete"): it renders from the list row with every credential masked and does not change this server-side gate.
- **Query and reference**: instant queries, data exploration, dashboard panel queries, datasource tool calls, and referencing the data source in an alert rule all require `readonly`; without it the request is refused explicitly, without exposing data source details.
- **Saving alert rules**: saving a rule requires the author to hold `readonly` on every data source the rule references, including data sources matched by a name pattern at save time; otherwise the save is rejected and names the data source that may not be referenced. The only exemption is a pure disable (a field update that only sets `enabled` to `false`). Once saved, rules are executed under the system identity, so later changes to member permissions do not affect them.
- **Audit**: changes to the authorization fields are written as a `datasource_auth_update` audit record.
### How to configure it
-The authorization fields travel with the data source create and update requests (`POST /monit/datasource/create`, `POST /monit/datasource/update`): you can set them while creating a data source, and changing them on an existing data source is a management operation that requires `manage` on that data source. Omitting an authorization field in an update keeps the current value; passing an explicit `null` clears it. The console data source form does not offer inputs for these two fields yet, so they are set through the API.
+The create and edit drawers each hold a team input per field, which takes effect once you save and leaves the scope unrestricted when left empty:
+
+| Form field | Field name | Selection | Effect |
+|------------|------------|-----------|--------|
+| **Managing team** | `manage_team_id` | Single select | Members of the managing team can edit, delete, enable or disable this data source. Leave empty to let everyone manage it; the creator and admins can always manage it |
+| **Query-allowed teams** | `readonly_team_ids` | Multi select | Restricts which teams can query and reference this data source. Leave empty to let everyone query it; the managing team can always query it |
+
+Both values travel with the data source create and update requests (`POST /monit/datasource/create`, `POST /monit/datasource/update`): you can set them while creating a data source, and changing the authorization fields of an existing data source is a management operation that requires `manage` on that data source. They are normalized before submission: clearing `manage_team_id` becomes an explicit `null`, and `readonly_team_ids` deduplicated to an empty list also becomes `null`. In an update request a missing key keeps the current value while `null` clears it, so **clearing the selection is what revokes that authorization**. You can also submit both fields straight through the API instead of using the console.
+
+Two constraints on the console side:
+
+- **A managing team is required once query-allowed teams are set**: if you pick query-allowed teams without a managing team, the form reports "A managing team is required when query-allowed teams are set" under **Managing team** before submitting, rather than letting the backend answer with a 400.
+- **You can only point the managing team at a team you belong to**: except for the owner account and the Admin role, the **Managing team** candidates are limited to the current user's own teams — pointing the managing team at a team you are not in costs you management of that data source the moment you save (the creator has a fallback, an ordinary managing-team member does not). A team you are not a member of that is already selected still shows its name normally.
Authorization is granted at team granularity only; per-member authorization is not supported. To authorize an individual member, create a small team for them.
@@ -431,16 +453,23 @@ Diagnostic-only types cannot enter the query workbench, so the list offers no **
- The account must have AI SRE enabled, otherwise the row offers edit and delete only, with no primary action at all.
- It requires the AI SRE chat permission (`AiSreChatVisit`): without it the button is visible but disabled.
-### The reverse entry in the Name column
-
-The Name column also carries an **Ask in AI SRE →** link that takes you from this data source straight into an AI SRE conversation with a prefilled question naming the data source and its type (prefilled only, never sent automatically). It appears only when the data source is enabled, the account has AI SRE enabled, and the current user holds both the AI SRE access permission and the chat permission (`AiSreChatVisit`) — AI SRE cannot query a disabled data source, so no entry is offered for one.
-
## Edit and delete
- **Edit**: Click the **Edit** button in the data source list to modify the configuration and save.
+- **View**: for a row where you hold readonly permission only, hovering that same edit entry reads **View** and opens a read-only drawer titled **View data source**.
- **Delete**: Click the **Delete** button in the data source list and confirm to delete the data source.
-Both go through the data source `manage` check first: you must be the creator of the data source, the owner account or an Admin, or a member of its managing team; otherwise the request is rejected (`only the managing team can manage this datasource`). The edit drawer loads the data source detail, and the detail returns connection credentials — which is why viewing the connection configuration, editing, deleting and enabling all sit behind the same gate. See the "Data source permissions (team authorization)" section for the decision rules.
+Editing, deleting and toggling all go through the data source `manage` check first: you must be the creator of the data source, the owner account or an Admin, or a member of its managing team; otherwise the request is rejected (`only the managing team can manage this datasource`). The edit drawer loads the data source detail, and the detail returns connection credentials — which is why viewing the credentials, editing, deleting and enabling all sit behind the same gate. See the "Data source permissions (team authorization)" section for the decision rules.
+
+The edit entry itself carries no row-level gate, but it still requires the account permission to edit data sources (`MonitDatasourceUpdate`): without it the entry is disabled as well.
+
+### What the read-only view shows
+
+When you hold readonly permission only on a data source, the console does not drop the entry — it downgrades it to read-only:
+
+- The drawer is titled **View data source**, the whole form is non-editable, and the data source type picker plus the Tencent Cloud CLS connection test are disabled along with it.
+- The basic fields come from the list row; the connection settings render in their usual form layout, but every string field shows `******` (switch, numeric and enum fields are left empty). The read-only view deliberately never calls the data source detail endpoint (that endpoint is gated on `manage`), so real credentials are never sent to the browser.
+- The **OK** button is disabled and the submit path applies the same hard stop; the **Delete** button and the **Enable datasource** switch become disabled controls whose hover card lists who can operate instead (organization admin, creator, managing team).
Before deleting a data source, ensure no alert rules reference it, otherwise the related alert rules will not execute properly.
diff --git a/en/monitors/explore/explore.mdx b/en/monitors/explore/explore.mdx
index 550cab20..32d894a3 100644
--- a/en/monitors/explore/explore.mdx
+++ b/en/monitors/explore/explore.mdx
@@ -90,6 +90,16 @@ Each successful query is automatically saved to query history, which stores only
The workbench encodes the current data source, query expression, time range, and query type into URL parameters. Copy the browser address bar to share the current query state — opening the link restores it automatically. If the data source in the link doesn't exist, is disabled, or the user lacks permissions, the page prompts and does not auto-execute.
+## Data Source Permissions and Denials
+
+The workbench's data source options include only data sources you have query (readonly) permission on; a data source you may not query never appears as a candidate and its contents cannot be read (see "Data source permissions (team authorization)" in [Data source management](/en/monitors/data-sources/data-sources) for the authorization model).
+
+When a query is stopped by data source authorization on the server (HTTP 403, error code `AccessDenied` — for instance when permissions changed after the page was opened), the workbench does not fall back to the generic query-failure hint but states:
+
+> No access to this data source. Contact the data source's managing team or an administrator.
+
+The result area also shows the raw backend message and the RequestId of that request for troubleshooting. To restore access, ask the data source's managing team or an administrator to adjust that data source's **Managing team** and **Query-allowed teams** on the data source management page.
+
## Edge Version Requirements
The Query Workbench relies on monit-edge to execute queries. If the Edge version in the data source's cluster is too low or versions are mixed, the page shows an upgrade prompt and disables querying. All Edge instances in the cluster must be upgraded to the required version before use.
diff --git a/en/on-call/analytics/insights.mdx b/en/on-call/analytics/insights.mdx
index 368d16ca..1767eb26 100644
--- a/en/on-call/analytics/insights.mdx
+++ b/en/on-call/analytics/insights.mdx
@@ -5,6 +5,10 @@ description: "Use insights for data analysis, with statistics and analysis of in
**Plan requirement**: This feature requires an On-call Standard or higher subscription. [Learn more](https://flashcat.cloud/flashduty/price/)
+
+**The dashboard can be empty while your subscription is expired**: once your On-call subscription expires or is disabled, alert ingestion has stopped and the dashboard produces no new analytics data, so the default time window is naturally empty — this is not data loss. A non-dismissable banner stays at the top of the page stating that the subscription has expired, that alert ingestion has stopped so no new analytics data is produced, and that historical data is unaffected and can still be viewed and exported. Historical data stays viewable and exportable, and everything resumes automatically after renewal. For the account-level explanation, see [What happens when your subscription expires](/en/platform/pricing#what-happens-when-your-subscription-expires).
+
+
## Key Features
diff --git a/en/on-call/comparison/vs-pagerduty.mdx b/en/on-call/comparison/vs-pagerduty.mdx
index 5d1dc688..9d5880ac 100644
--- a/en/on-call/comparison/vs-pagerduty.mdx
+++ b/en/on-call/comparison/vs-pagerduty.mdx
@@ -230,7 +230,7 @@ Before reaching the team, alerts pass through routing, filtering, and transforma
| Tool | Flashduty | PagerDuty |
| --- | --- | --- |
| **[Open API](/en/openapi/api-catalog)** | 330+ endpoints covering On-call, Monitors, RUM, AI SRE, and platform management, with bilingual docs | ✅ Full-featured REST API, mature documentation |
-| **[CLI](/en/developer/cli)** | 336 API operation commands + built-in Agent Skills, ready to hand directly to AI coding tools like Claude Code, Cursor, and Codex | No official CLI actively promoted: the community's most-used `pagerduty-cli` is an employee's personal project (officially not endorsed, and the author has announced it's archived); the official go-pagerduty library ships a limited `pd` command-line tool |
+| **[CLI](/en/developer/cli)** | 334 generated API operation commands (plus four hand-written ones) + built-in Agent Skills, ready to hand directly to AI coding tools like Claude Code, Cursor, and Codex | No official CLI actively promoted: the community's most-used `pagerduty-cli` is an employee's personal project (officially not endorsed, and the author has announced it's archived); the official go-pagerduty library ships a limited `pd` command-line tool |
| **SDK** | [Go SDK](/en/developer/go-sdk): a go-github-style wrapper covering 330+ API operations across 39 services | PagerDuty officially maintains go-pagerduty, python-pagerduty, and other client libraries |
| **[Terraform Provider](/en/developer/terraform)** | 12 resource types + 13 data source types, managing collaboration spaces, escalation policies, schedules, and more as IaC | ✅ Official Terraform Provider, mature ecosystem |
| **[MCP Server](/en/developer/mcp-server)** | 8 tool sets with 23 tools, deployable remotely, via Docker, or from source | ✅ Official MCP Server |
diff --git a/en/on-call/integration/sso/keycloak.mdx b/en/on-call/integration/sso/keycloak.mdx
index 5e46fad2..5f336e5e 100644
--- a/en/on-call/integration/sso/keycloak.mdx
+++ b/en/on-call/integration/sso/keycloak.mdx
@@ -93,7 +93,7 @@ Create user (must bind an email address):

-**Login test**: Visit `console.flashcat.cloud`, select SSO login, and enter your organization's login domain in the domain field (configured on the **Platform Management → Organization → Organization Information → Organization Profile** page). You can also initiate single sign-on directly at `{domain}.sso.flashcat.cloud`.
+**Login test**: Visit `console.flashcat.cloud` and select SSO login. The SSO sign-in page defaults to **business email** sign-in — enter your business email to locate your organization and enter its authentication flow. If you only know the company domain, click **Sign in with company domain** to switch to the domain input, then enter your organization's login domain (configured on the **Platform Management → Organization → Organization Information → Organization Profile** page). You can also initiate single sign-on directly at `{domain}.sso.flashcat.cloud`.

diff --git a/en/on-call/integration/webhooks/alert-webhook.mdx b/en/on-call/integration/webhooks/alert-webhook.mdx
index ec60001d..9411154d 100644
--- a/en/on-call/integration/webhooks/alert-webhook.mdx
+++ b/en/on-call/integration/webhooks/alert-webhook.mdx
@@ -209,6 +209,10 @@ Enter the integration name and description for easy management.
Disabling TLS certificate verification may expose you to man-in-the-middle attacks. Only disable it in test environments or when using self-signed certificates.
+
+**Custom request body** and **value mapping** are supported by **Incident Webhook** only. An **Alert Webhook** always pushes the [Payload structure](#request-payload) described above and cannot reorganize it. See the **Custom Request Body** and **Value Mapping** sections of [Incident Webhook](/en/on-call/integration/webhooks/incident-webhook).
+
+
## 4. Invocation History
Alert Webhook provides a complete invocation history, helping you troubleshoot push delivery and debug callback interfaces.
diff --git a/en/on-call/integration/webhooks/custom-actions.mdx b/en/on-call/integration/webhooks/custom-actions.mdx
index 45831c4d..70b1ca04 100644
--- a/en/on-call/integration/webhooks/custom-actions.mdx
+++ b/en/on-call/integration/webhooks/custom-actions.mdx
@@ -80,6 +80,7 @@ layer_idx | int | Yes | Current escalation layer, starting from 0
type | string | Yes | Assignment method, enum values: assign (assignment), reassign (reassignment), escalate (layer escalation), reopen (assignment after reopen)
id | string | Yes | Assignment record ID
assigned_at | int64 | Yes | Assignment time, Unix seconds timestamp
+effective_person_ids | []int64 | No | Person IDs that ultimately take effect for this assignment: the union of directly assigned people and the people resolved by the escalation rule, deduplicated and sorted in ascending ID order
**LinkItem**:
diff --git a/en/on-call/integration/webhooks/incident-webhook.mdx b/en/on-call/integration/webhooks/incident-webhook.mdx
index 1c127143..f24ae9cb 100644
--- a/en/on-call/integration/webhooks/incident-webhook.mdx
+++ b/en/on-call/integration/webhooks/incident-webhook.mdx
@@ -95,6 +95,7 @@ layer_idx | int | Yes | Current escalation layer, starting from 0
type | string | Yes | Assignment method, enum values: assign (assignment), reassign (reassignment), escalate (layer escalation), reopen (assignment after reopen)
id | string | Yes | Assignment record ID
assigned_at | int64 | Yes | Assignment time, Unix seconds timestamp
+effective_person_ids | []int64 | No | Person IDs that ultimately take effect for this assignment: the union of directly assigned people and the people resolved by the escalation rule, deduplicated and sorted in ascending ID order
**LinkItem**:
diff --git a/en/on-call/integration/webhooks/jira-sync.mdx b/en/on-call/integration/webhooks/jira-sync.mdx
index de668bfb..a95d87e5 100644
--- a/en/on-call/integration/webhooks/jira-sync.mdx
+++ b/en/on-call/integration/webhooks/jira-sync.mdx
@@ -49,7 +49,12 @@ In Integration Center, select **Webhook**, choose **Jira Sync** integration, and
- **Issue Type**: Select the issue type to sync to in Jira.
- **Channel**: Select channels where this integration takes effect; only incidents in these channels can sync to Jira.
- **Severity Mapping**: Cannot be configured if the selected issue type doesn't support the priority field. The **Jira Priority** (result field) is required for each mapping row; the integration cannot be saved if any row is missing it.
-- **Custom Field Mapping**: Optionally sync incident labels, all labels, or custom field content to Jira fields (only text-type fields supported). Both the **Key** (incident field) and the **Result Field** (Jira field) are required for each mapping row; the integration cannot be saved if any row is missing either.
+- **Status Mapping**: Maps the three Flashduty progresses **Triggered**, **Processing** and **Closed** to Jira statuses. The options are all statuses Jira provides for the selected **Project ID** + **Issue Type**.
+ - Leave all three empty to use the [default mapping](#status-mapping). As soon as you configure one of them, the other two become required and the integration cannot be saved without them (the error is **Jira status is required**).
+ - Changing the **Issue Type** clears the configured status mapping. Changing the **Project ID** clears the selected **Issue Type**, so you need to select the issue type again and re-check whether the status mapping still applies.
+ - When no mapping is configured, the **Status Mapping** field on the integration detail page shows **Use default mapping**.
+- **Custom Field Mapping**: Optionally sync some or all incident labels, custom field content, and incident properties to Jira fields (only text-type fields supported). Both the **Key** (incident field) and the **Result Field** (Jira field) are required for each mapping row; the integration cannot be saved if any row is missing either.
+ - Apart from **All labels**, the **Key** dropdown options fall into three groups: **Incident properties** (**Incident ID**, **Incident link**), **Labels** (an individual label name), and **Fields**. Selecting **Incident ID** writes the incident ID; selecting **Incident link** writes the URL of the incident detail page in Flashduty.
### 3. About Updates
@@ -73,11 +78,17 @@ In Integration Center, select **Webhook**, choose **Jira Sync** integration, and
#### Status Mapping
+When all three fields of **Status Mapping** in the integration configuration are left empty, the system syncs incident progress using the default mapping below:
+
| Jira | Flashduty |
| ---------- | -------- |
-| Todo | Triggered |
-| In Progress | Processing |
-| Done | Resolved |
+| To Do (待办) | Triggered |
+| In Progress (处理中) | Processing |
+| Done (完成) | Closed |
+
+The default mapping matches by name: the target names are `待办` / `处理中` / `完成` in a Chinese Jira and `To Do` / `In Progress` / `Done` in an English Jira. If the current status of the issue already carries the target name, no transition is needed; otherwise the system looks for a transition whose name equals the target name.
+
+If your Jira workflow uses other status or transition names, the default mapping may find no matching transition. In that case, assign one Jira status to each progress in **Status Mapping** of the integration configuration. Once configured, status synchronization follows that mapping only; if one of the mapped Jira statuses has been deleted, the integration detail page shows **Status not found (ID: xxx)**.
### 5. Notes
diff --git a/en/on-call/quickstart/mobile-app.mdx b/en/on-call/quickstart/mobile-app.mdx
index 6597449e..360b2681 100644
--- a/en/on-call/quickstart/mobile-app.mdx
+++ b/en/on-call/quickstart/mobile-app.mdx
@@ -56,7 +56,8 @@ Tap the filter icon in the upper-right of the list to combine the following filt
| --- | --- |
| **Progress** | All / Open / Triggered / Processing / Closed (defaults to Open) |
| **Severity** | Critical / Warning / Info, multi-select |
-| **Incident title / Incident ID** | Fuzzy search by keyword |
+| **Incident title** | Fuzzy search by keyword |
+| **Incident ID** | Enter the full incident ID or the 6-character incident short number (for example `56E25B`) to match exactly; any other keyword is matched against the **incident title**. Uses the same query semantics as **Search** in the console incident list |
| **Related to me** | Show only incidents related to you |
| **People** | Filter by Creator, Responder, Ack'd by, or Closed by |
| **Channel** | Filter by the incident's channel |
diff --git a/en/on-call/statuspage/statuspage.mdx b/en/on-call/statuspage/statuspage.mdx
index 58b9774c..e57c4ca6 100644
--- a/en/on-call/statuspage/statuspage.mdx
+++ b/en/on-call/statuspage/statuspage.mdx
@@ -5,6 +5,15 @@ description: "Achieve efficient internal and external communication during incid
**Plan requirement**: All plans can create public status pages (Free and Standard: up to 1, Pro: up to 5). Internal status pages are available on the Pro plan only (up to 20). [Learn more](https://flashcat.cloud/flashduty/price/)
+
+**Two admission rules for creating a status page**: both the list-page entry and the create page itself are checked, so opening the create page directly does not get around them.
+
+- **You cannot create one while the subscription is expired or disabled**: the create button on the list page is disabled, and opening the create page directly returns an empty state page whose title says the subscription has expired and creation is temporarily unavailable, and whose body adds that creating status pages resumes after renewal and that already-created status pages are still displayed normally and remain editable at any time. Its primary button becomes **Renew now** (alongside **Back to list**). Existing status pages keep being displayed normally — publishing events, editing and subscriber notifications are all unaffected — and you can create new ones again once you renew.
+- **Internal status pages require Pro or higher**: the create page can be opened directly (`/status-page/create?type=internal`), so on top of the plan gate on the list-page entry, the create page validates the plan again; when the plan is insufficient it returns the same kind of empty state page, this time titled as a missing permission, and its body says internal status pages are only available on Pro or higher and repeats that already-created status pages are still displayed normally and remain editable at any time. The primary button is **Upgrade** (alongside **Back to list**).
+
+For how an expired subscription affects everything else, see [What happens when your subscription expires](/en/platform/pricing#what-happens-when-your-subscription-expires).
+
+
Service interruptions are inevitable, but communication efficiency can be systematically improved.
When service status fluctuates, relying on manual emails or instant messages for notifications can no longer meet enterprise requirements for information timeliness and consistency. Flashduty Status Page provides a standardized, unified information publishing window to ensure that both internal and external stakeholders maintain real-time awareness of service status.
diff --git a/en/openapi/api-catalog.mdx b/en/openapi/api-catalog.mdx
index 54265f2f..2419f081 100644
--- a/en/openapi/api-catalog.mdx
+++ b/en/openapi/api-catalog.mdx
@@ -291,7 +291,7 @@ All endpoint URLs use `https://api.flashcat.cloud` as the base, authenticated vi
| POST | [`/monit/rule/counter/channel`](/en/api-reference/monitors/alert-rules/monit-rule-read-counter-channel) | Get rule counts by channel |
-Alert rule import and export are available in the console only (the YAML rule bundle flow, see [Alert Rule Quickstart](/en/monitors/quickstart/quickstart#import-alert-rules)): the Monitors backend has retired these two endpoints, and their API-reference entries will be removed on the next generation run.
+Alert rule import and export are available in the console only (the YAML rule bundle flow, see [Alert Rule Quickstart](/en/monitors/quickstart/quickstart#import-alert-rules)): the Monitors backend has retired `POST /monit/rule/export` and `POST /monit/rule/import`, and the API reference no longer carries those two endpoints.
### Data sources
@@ -513,10 +513,18 @@ Only plain human messages are projected: rows produced by internal producers (au
| POST | [`/safari/artifact/gallery/publish-from-file`](/en/api-reference/ai-sre/artifacts/artifact-write-publish) | Publish file as artifact |
| POST | [`/safari/artifact/gallery/update`](/en/api-reference/ai-sre/artifacts/artifact-write-update) | Update artifact |
| POST | [`/safari/artifact/gallery/delete`](/en/api-reference/ai-sre/artifacts/artifact-write-delete) | Remove artifact from gallery |
+| POST | `/safari/artifact/gallery/pin` | Pin an artifact to the top of the caller's own gallery |
+| POST | `/safari/artifact/gallery/unpin` | Remove the caller's own pin |
| POST | [`/safari/artifact/gallery/share/enable`](/en/api-reference/ai-sre/artifacts/artifact-write-share-enable) | Enable public sharing |
| POST | [`/safari/artifact/gallery/share/sync`](/en/api-reference/ai-sre/artifacts/artifact-write-share-sync) | Update shared snapshot |
| POST | [`/safari/artifact/gallery/share/revoke`](/en/api-reference/ai-sre/artifacts/artifact-write-share-revoke) | Revoke public sharing |
+
+`/safari/artifact/gallery/pin` and `/safari/artifact/gallery/unpin` are new endpoints with no API-reference page generated yet (every other endpoint in this table is clickable), which is why those two rows list the path alone.
+
+Both take `artifact_id` (required), return an empty response on success, and never modify the artifact itself. A pin is a **per-caller** preference rather than an edit to the artifact: the artifact stays visible to the whole account, and only the caller's own gallery order changes — the artifact list hoists the caller's pinned artifacts to the front and returns `pinned_at` on those rows (Unix milliseconds; the field is omitted for unpinned rows). Any account member may pin any artifact they can see; pinning requires the artifact to exist (a missing one returns 404), while unpinning runs no existence check. Both endpoints are idempotent: re-pinning refreshes the pin time, and unpinning an artifact that was never pinned succeeds.
+
+
diff --git a/en/platform/permission-design.mdx b/en/platform/permission-design.mdx
index 8be13312..7723d0ee 100644
--- a/en/platform/permission-design.mdx
+++ b/en/platform/permission-design.mdx
@@ -238,9 +238,19 @@ Flashduty **controls data permissions based on teams**, applied in the following
| **Template Management** | Creator, owner account, and responsible team members can modify template basic information, channel template configurations, etc. |
| **Service Calendar** | Creator, owner account, and responsible team members can modify calendar basic information, holiday settings, etc. |
| **Integration Management** | Creator, owner account, and responsible team members can manage integration configurations |
+| **Channel-exclusive integrations** | The owner account, members with the Admin role, the channel creator, and members of any managing team can manage the integrations under that channel; when the channel has no managing team configured, this falls back to the channel's own team |
| **Mapping Rules** | Creator, owner account, and responsible team members can manage mapping rule configurations |
| **Datasources** | Creator, owner account, and managing team members can edit, delete, enable or disable monitoring datasources, view their connection credentials, and change the authorization configuration; members of the managing team and of the readonly teams can query and reference the datasource; when neither is configured, the whole tenant can read and write (existing datasources keep their current behavior) |
+
+**Channel-exclusive integrations vs. shared integrations**: an integration created under a channel (its `ref_id` looks like `c_`) is a **channel-exclusive integration**; every other integration is a **shared integration**. The two follow different permission rules.
+
+- **Functional permissions**: enabling, disabling, deleting and saving the configuration of a channel-exclusive integration can be done with either **Channels Manage** or **Integrations Manage** — one of the two is enough. This is because **Channels Manage** already covers creating, editing, deleting, enabling, disabling and rotating the key of the integrations inside that channel.
+- **Data permissions**: a channel-exclusive integration is judged by the channel's **managing team**; when the channel has no managing team configured, it falls back to the channel's own team. A shared integration is judged by the integration's own creator and responsible team.
+- **The insufficient-permission message differs**: a channel-exclusive integration says "No permission. Only account admins, channel creators or management teams can operate.", while a shared integration says "You do not have permission to operate. Only account administrators, integration creators, or team managers are allowed to operate."
+- Only an integration whose `ref_id` looks like `c_` counts as channel-exclusive: a shared integration opened from the integration detail with a `channel_id` query parameter is still judged as a shared integration.
+
+
When you don't have data permissions for a resource, the system will display the following message:
diff --git a/en/platform/pricing.mdx b/en/platform/pricing.mdx
index 82e64559..c23a965a 100644
--- a/en/platform/pricing.mdx
+++ b/en/platform/pricing.mdx
@@ -306,6 +306,66 @@ The **Transactions** tab lists **Time**, **Type**, **Description**, **Amount**,
---
+## What happens when your subscription expires
+
+When your On-call subscription **has expired or is disabled**, Flashduty does two things: it **stops ingesting new alerts**, and it **freezes every create entry point**. Existing configuration and historical data are not deleted, and everything resumes automatically once you renew.
+
+### What counts as expired
+
+Either condition below marks the account as expired across the product:
+
+| Condition | Description |
+| --- | --- |
+| **Subscription disabled** | The subscription resource's status is not `enabled` |
+| **Subscription past its end date** | The current time has reached the subscription's expiry time (`expired_at`) |
+
+Checking the expiry time alone would miss the "resource disabled but the end date has not arrived yet" case — which the backend rejects while the front end would let it through — so either condition on its own counts as expired. On-Premises deployments are not subject to subscription control and are never treated as expired.
+
+### Expiry reminders and how to renew
+
+- The **subscription entry at the bottom of the left navigation** turns amber when **less than 10 days** remain, showing the time left until expiry; hovering explains that alert ingestion will stop after that. Once expired it turns red and shows how long ago it expired; hovering adds that alert ingestion has stopped and that you can click to renew. Clicking the entry opens the **Plan** page (`/wallet/plan`).
+- The **owner account** can click **Renew now** on the banner. **Member accounts** have no renewal permission and are told to ask the account owner to renew.
+
+### Alert ingestion and the banners on each list page
+
+Alert ingestion stops once the subscription expires, and the alert list, incident list, integration center and insights dashboard each show a **non-dismissable** banner at the top explaining what that page is affected by:
+
+| Page | What the banner tells you |
+| --- | --- |
+| Alert list | Alert ingestion has stopped; historical alerts remain viewable and actionable, and ingestion resumes automatically after renewal |
+| Incident list | Alert ingestion has stopped, so no new incidents will be created; existing incidents can still be handled |
+| Integration center | All integrations have stopped receiving alert events; configured integrations are not deleted, and they resume automatically after renewal with no reconfiguration needed |
+| Insights | Alert ingestion has stopped, so no new analytics data is produced; historical data is unaffected and can still be viewed and exported |
+
+In the integration center every integration card also becomes non-interactive, with a tooltip explaining that the expired subscription blocks creating new integrations and that renewal restores it immediately, while existing integrations are unaffected. The insights dashboard keeps producing no new analytics data, so the default time window is naturally empty — this is not data loss. See [Insights](/en/on-call/analytics/insights) for details.
+
+### Create entries that are frozen
+
+These create entries are disabled while the subscription is expired. Hovering explains that the expired subscription blocks creating a new 〈object〉 and that existing ones are unaffected — "object" being the object named in this column, so the channel entry reads "…cannot create a new channel. Existing ones are not affected." The tooltip also carries the renewal path: **Renew now** for the owner account, "Please ask the account owner to renew." for member accounts.
+
+| Object | Entry that is disabled |
+| --- | --- |
+| Channels | **Create channel** on the channel list |
+| Schedules | **Create schedule** on the schedule page |
+| Incidents | The create entry on the incident list |
+| War rooms | **Create war room** in the incident detail |
+| Status pages | **Create public status page** / **Create internal status page** on the status page list, plus the create page itself |
+| Members | **Invite members** under member management |
+| Custom fields | **Create custom field** on the custom fields page |
+| Notification templates | The create entries on the templates page |
+| Service calendars | **Add Calendar** on the service calendar page |
+| Post-mortems | **Create Post-Mortem** on the post-mortem panel |
+
+
+Expiry only freezes **creation**. Existing objects can still be **edited, enabled, disabled and deleted**: a channel's noise-reduction configuration and escalation rules remain editable, and a status page that already exists keeps being displayed and stays editable at any time.
+
+
+### How things recover after renewal
+
+After you renew or re-enable the subscription you **do not reconfigure anything**: alert events resume being ingested, the frozen create entries become usable immediately, and configured integrations, existing incidents, historical alerts and historical analytics data all stay as they were. AI SRE is also paused while the On-call subscription is lapsed and resumes automatically on renewal — see [AI SRE Product Overview · When the subscription lapses](/en/ai-sre/overview#when-the-subscription-lapses).
+
+---
+
## FAQ
---
diff --git a/en/platform/team-members.mdx b/en/platform/team-members.mdx
index ca6cd7cf..9990c17f 100644
--- a/en/platform/team-members.mdx
+++ b/en/platform/team-members.mdx
@@ -136,6 +136,7 @@ For example, entering a member's ID (purely numeric) locates that member directl
- Daily invitation limit is 200 people, with a maximum of 20 per invitation and up to 5 resends per day
- Unactivated accounts cannot receive alert-related notifications
- When the organization configures an email-domain allowlist under [join restrictions](/en/platform/organization-info#join-restrictions), one-by-one invitations only accept addresses on that allowlist (the invite dialog lists the allowed domains); phone invitations are unaffected, and link joins are checked against the email domain used to sign in
+ - When the subscription is expired or disabled, the **Invite members** entry is disabled: hovering tells you that the expired subscription blocks creating a new member and that existing members are not affected. The owner account can click **Renew now**, while member accounts are told to ask the account owner to renew. The entry becomes usable again as soon as you renew, and existing members are unaffected. See [What happens when your subscription expires](/en/platform/pricing#what-happens-when-your-subscription-expires)
### Changing Roles
diff --git a/en/rum/others/data-collection.mdx b/en/rum/others/data-collection.mdx
index c66089e4..91943849 100644
--- a/en/rum/others/data-collection.mdx
+++ b/en/rum/others/data-collection.mdx
@@ -52,7 +52,7 @@ All RUM events contain the following default attributes:
| Metric | Type | Description |
|--------|------|-------------|
- | `session_duration` | number | Session duration in milliseconds |
+ | `session_duration` | number | Session duration in milliseconds, measured from the session's earliest view and equal to `client_end_time` - `client_time`; a view that occurred before the other views but was processed later (such as a mobile app launch view) still counts |
| `session_view_count` | number | Number of views in the session |
| `session_action_count` | number | Number of user actions in the session |
| `session_error_count` | number | Number of errors in the session |
diff --git a/zh/ai-sre/agents.mdx b/zh/ai-sre/agents.mdx
index a96de1b3..b9743aa0 100644
--- a/zh/ai-sre/agents.mdx
+++ b/zh/ai-sre/agents.mdx
@@ -49,13 +49,22 @@ A2A Agent 的列表与管理入口在 **插件 → Agents** 页面(菜单标
| `general` | 通用执行器,拥有完整工具权限,适合把中间步骤会污染主对话上下文的工作(长报告、多文件改动、端到端构建、反复探查)整体外包出去;可并行派发多个处理相互独立的工作单元 |
| `explore` | 只读调查器,仅限 `grep` / `glob` / `read`,不能写入或执行;返回浓缩摘要而非原始搜索输出,适合在代码库或知识转储中定位证据 |
-**会话中的呈现**:每次派发在对话流里同样以一张**任务卡片**呈现,与 A2A 委派共用同一套状态生命周期(初始化 / 进行中 / 等待输入 / 等待授权 / 完成 / 失败(含孤儿任务)/ 中断 / 已终止)。区别在于徽标——Subagent 任务卡片带 `Agent` 徽标,而 A2A 委派带 `A2A` 徽标。
+**会话中的呈现**:每次派发在对话流里同样以一张**任务卡片**呈现,与 A2A 委派共用同一套状态生命周期(初始化 / 进行中 / 等待输入 / 等待授权 / 完成 / 失败(含孤儿任务)/ 中断 / 已终止)。区别在于徽标——Subagent 任务卡片带 `Agent` 徽标,而 A2A 委派带 `A2A` 徽标。Subagent 卡片的引用行右侧还会先标出**这次派发落在哪个执行环境**(云端环境,或该 BYOC Runner 的名称),因此从卡片上就能看出子任务在哪儿执行,详见下文「派发目标环境」。
**运行约束**:
- **并发上限**:每个会话**最多同时运行 20 个 Subagent**(`TaskMaxConcurrentPerSession = 20`)。这里限制的是「同一时刻并发运行」的数量而非会话累计派发数——每完成一个就释放一个名额,因此会话整体可完成的工作量不受限。达到上限时再派发会报错(`too many active subagents (… running, limit 20)`),提示模型先停下等待运行中的任务完成、再派发其余的。
- **嵌套深度**:派发链**最多嵌套 3 层**(`TaskMaxNestingDepth = 3`),防止 Subagent 无限自我派生。
+**派发目标环境**:`agent_dispatch` 还有一个可选参数 `environment`,决定这个子任务在**哪个运行环境**里执行。它的取值为:
+
+- **省略(默认)**:子代理与当前会话在**同一个**环境里运行,**共用同一套文件系统**——它能直接看到你 workspace 里的文件。点名会话**已经在**的那个环境(例如在一个云端会话里传 `cloud`)结果相同,也是继承,不会另起一个沙箱。
+- **`cloud`**:在一个**全新的云端环境**里运行——临时容器、独立的文件系统,出网按账户的默认策略。
+- **某个 BYOC Runner 的 environment_id**:在**那台 Runner** 上运行。可选目标是**账户级 Runner**,加上**当前会话所绑定团队**的 Runner,且只列出**在线**的;同名 Runner 会带团队后缀以示区分。`agent_dispatch` 的说明里直接给出这份清单,每个 Runner 的 id 就是可传的值。传了不可用的 Runner(离线、不存在、属于别的团队)时派发会被直接拒绝并给出原因,不会静默改派到别处。
+- **只对 Subagent 有效**:给 A2A Agent 传 `environment` 会被拒绝——远端 Agent 在它自己的环境里执行,没有可选位置;要换环境就派一个 Subagent 去做。
+
+**跨环境派发读不到父会话的文件**:子会话只有在自己的执行环境与父会话**完全相同**时才共用父会话的沙箱 / workspace;一旦派到另一个环境,它就会得到**自己的**沙箱或 Runner scratch 目录,**看不到你(父会话)的文件**。因此把它需要的一切**写进 prompt**(文件内容、路径、参数、约束),不要只写「读 workspace 里的某个文件」;结果会以**文本**回来,或者经外部系统(git、工单)流转。运行环境本身的区别与选择方式见 [运行环境(BYOC)](/zh/ai-sre/environments)。
+
**Subagent 目前没有面向用户的创建 / 管理界面。** 它是一项内置运行能力:您可以在会话中观察 Subagent 任务卡片及其子会话过程,但无法像 A2A Agent 那样新增、编辑或删除 Subagent。本页(Agents)当前管理的仅是 A2A Agent。
diff --git a/zh/ai-sre/artifacts.mdx b/zh/ai-sre/artifacts.mdx
index a8b08e12..65279e94 100644
--- a/zh/ai-sre/artifacts.mdx
+++ b/zh/ai-sre/artifacts.mdx
@@ -1,6 +1,6 @@
---
title: 产物
-description: 产物库集中呈现 AI SRE 会话中用 present_files 工具产出、再经 publish_artifact 工具发布的文件(网页、报告、图片、PDF、源码与数据文件等,例如 /insight 报告),支持搜索、按范围筛选与修改范围、重命名、分享(账户内或公开链接)、下载和删除。
+description: 产物库集中呈现 AI SRE 会话中用 present_files 工具产出的文件:经 publish_artifact 工具发布(限 HTML 与 Markdown),或在控制台用「发布到产物库」提交(图片、PDF、源码、数据文件等更多类型),例如 /insight 报告。支持搜索、按范围筛选与修改范围、重命名、置顶、分享(账户内或公开链接)、下载和删除。
keywords: ["AI SRE", "产物", "Artifacts", "present_files", "publish_artifact", "insight 报告", "产物库", "公开链接", "分享"]
sidebarTitle: 产物
---
@@ -13,7 +13,10 @@ sidebarTitle: 产物
---
-产物(Artifact)是 AI SRE 在会话中用 `present_files` 工具产出、再经 `publish_artifact` 工具发布到产物库的文件。最典型的是一份自包含的 HTML 报告或页面——例如在会话里输入 `/insight` 生成的运营洞察报告——但可发布的类型不止于此:
+产物(Artifact)是 AI SRE 在会话中用 `present_files` 工具产出、再发布到产物库的文件。发布有两条路径,两者能接受的类型并不相同:
+
+- **Agent 工具 `publish_artifact`**:只发布 HTML / Markdown 文档(`.html` `.htm` `.md` `.markdown`)——一份自包含的报告或页面,最典型的是在会话里输入 `/insight` 生成的运营洞察报告。其它类型一律被拒绝,返回 `unsupported_format`。
+- **控制台里 `present_files` 文件旁的「发布到产物库」按钮**(见下文 [新建产物](#新建产物)):可发布的类型更宽:
| 类别 | 常见扩展名 |
|---|---|
@@ -24,7 +27,7 @@ sidebarTitle: 产物
| 源码 | `.py` `.go` `.js` `.mjs` `.ts` `.jsx` `.tsx` `.java` `.c` `.h` `.cpp` `.cs` `.rb` `.rs` `.php` `.sh` `.sql` `.kt` `.swift` `.scala` `.css` `.vue` `.svelte` `.proto` `.tf` `.hcl` 等 |
| 压缩包 | `.zip` `.tar` `.gz` `.tgz` |
-不在可发布范围内的文件,会话中不会出现「发布到产物库」按钮。
+不在上表范围内的文件,会话中不会出现「发布到产物库」按钮。
发布后的产物初始继承来源会话的作用域:来自个人会话的产物归创建者「个人」所有;来自绑定了团队的会话的产物归该「团队」所有,可分享给账户内的其它成员查看。拥有编辑权限时,之后还可以修改产物范围。
@@ -56,22 +59,41 @@ sidebarTitle: 产物
| 来自对话 | 产物来源会话标题,点击跳转到对应会话;来源会话已删除或无标题时显示「会话已删除」纯文本 |
| 生成时间 | 产物创建时间 |
| 修改时间 | 产物最后更新时间 |
+| 置顶 | 一个只有图标的按钮(该列没有表头),点击即可为自己置顶或取消置顶,详见下文 [置顶](#置顶) |
| 操作 | 重命名、下载、删除等 |
**生成时间** 与 **修改时间** 列支持点击列头排序(升序 / 降序切换),排序在服务端执行;默认按 **修改时间降序**。
-范围、搜索词、排序与页码都会反映在页面地址上(形如 `?scope=team&teams=1,2&q=重建&page=2`),因此直接复制地址就能分享或收藏一份**已筛选**的列表;从产物详情页返回时也会回到原来的筛选与位置,而不是被重置成默认视图。
+无论选哪种排序,**你置顶的产物始终排在最前面**(最近置顶的排在更前),其后才按所选字段排序。
+
+范围、搜索词、排序、页码与每页条数都会反映在页面地址上(形如 `?scope=team&teams=1,2&q=重建&page=2&size=48`),因此直接复制地址就能分享或收藏一份**已筛选**的列表;从产物详情页返回时也会回到原来的筛选与位置,而不是被重置成默认视图。
+
+每页条数可选 **12 / 24 / 48**,默认 **24**(对应地址里的 `?size=`,等于默认值时不会写进地址)。地址里被手工改成其它数值时,会取最接近的一档生效,而不是照单执行。
### 产物卡片
每张卡片展示:
-- 顶部预览区的类型图标:按文件扩展名与内容类型区分,图片、PDF、HTML、Markdown、表格(CSV / TSV)、JSON、压缩包、源码各有专属图标,无法识别时回退为通用文件图标;
+- 顶部预览区渲染的是产物**自身内容的缩略图**:HTML 取文件开头(最多 256 KiB)按一个 1200×630 的桌面页面缩放成小页面,Markdown 在同样的缩放页上排版,图片直接显示图片本身,纯文本显示开头 24 行(最多 1400 字符);PDF 以及其它无法渲染的类型显示一个空框。缩略图是一张不可交互的静态画面——它以 `sandbox=''` 渲染,脚本不会执行,链接与按钮也点不动。它还是懒加载的:卡片接近视口(约 400px)之前不签名、不下载,显示为空框骨架;
+- 类型图标:按文件扩展名与内容类型区分,图片、PDF、HTML、Markdown、表格(CSV / TSV)、JSON、压缩包、源码各有专属图标,无法识别时回退为通用文件图标。它是骑在缩略图与正文之间分隔线上的 30×30 角标,不再占据预览区;
- 标题(单行显示,超出省略;鼠标悬停可看到完整标题);
-- 时间信息为一条 **「编辑于 …」** 相对时间(取产物的最后更新时间;创建后再未更新过的产物,更新时间即创建时间)——刚刚 / N 分钟前 / N 小时前 / N 天前;相对时间适用于任何时长,**再久的产物也只显示「N 天前」,不会回退为具体日期**;鼠标悬停该时间可看到精确到分钟的时刻,列表视图的 **生成时间 / 修改时间** 两列则直接给出精确时刻;
-- 右下角的作用域徽标:团队产物显示团队名称(绿色高亮),个人产物显示创建者姓名(灰色)。
+- 时间信息为标题下方灰色说明行里的 **「编辑于 …」** 相对时间(取产物的最后更新时间;创建后再未更新过的产物,更新时间即创建时间)——刚刚 / N 分钟前 / N 小时前 / N 天前;相对时间适用于任何时长,**再久的产物也只显示「N 天前」,不会回退为具体日期**;鼠标悬停该时间可看到精确到分钟的时刻,列表视图的 **生成时间 / 修改时间** 两列则直接给出精确时刻;
+- 同一条说明行里还有作用域(小图标 + 名称:团队产物显示团队名称,个人产物显示创建者姓名)——卡片上不使用彩色作用域徽标,徽标只出现在列表视图与详情页;产物开启了公开链接时,说明行开头还会多一个带 tooltip 的小地球字形;已置顶的产物在说明行末尾显示一个图钉字形。
+
+点击卡片正文会打开该产物的详情页;鼠标悬停在卡片上会在右上角露出「更多操作」按钮(触屏设备上始终可见),它旁边还有一个同样是悬停露出的图钉按钮,点一下即可置顶或取消置顶。分享面板里的公开链接预览用的也是这套缩略图。
+
+### 置顶
-点击卡片正文会打开该产物的详情页;鼠标悬停在卡片上会在右上角露出「更多操作」按钮(触屏设备上始终可见)。
+列表视图的每一行和每张卡片都有图钉按钮——卡片上的图钉与「更多操作」一起放在右上角,悬停露出(触屏设备常显);列表行里的图钉是一个固定宽度的独立列,位于「修改时间」与操作按钮之间。「更多操作」菜单的第一项也是这个开关,按当前状态显示为「置顶」或「取消置顶」。
+
+置顶是**每个人自己的偏好**,不是一次编辑:
+
+- 任何你能看到的产物都可以置顶——他人的个人产物、团队产物都包括在内,**不需要编辑权限**(重命名、修改范围、删除才需要);
+- 置顶只把产物排到**你自己的**产物库最前面,不修改产物的内容、标题或作用范围,也不影响其它成员看到的顺序;
+- 置顶与取消置顶都是幂等的:重复置顶只会刷新置顶时间(置顶时间较晚的排在更前面),取消一个从未置顶过的产物也不会报错;
+- 置顶后列表会整页重新拉取。排序由服务端负责,所以在第 2 页置顶的产物会移动到第 1 页,而不是就地重排。
+
+产物列表项上的 `pinned_at` 字段就是**调用者本人的**置顶时间(Unix 毫秒),未置顶时不返回该字段——同一条产物在不同成员那里可以有不同的 `pinned_at`。
### 新建产物
@@ -93,6 +115,7 @@ Agent 会先向你确认目标读者、内容/数据来源、交互与视觉风
| 操作 | 说明 |
|---|---|
+| 置顶 / 取消置顶 | 菜单的第一项,按当前状态显示为「置顶」(未置顶时)或「取消置顶」。置顶是**个人偏好**,不经过编辑权限判断——任何你能看到的产物都能为自己置顶,且只改变你自己的列表顺序,不修改产物本身(详见上文 [置顶](#置顶)) |
| 复制链接 | 复制该产物详情页的完整 URL,可分享给账户内的其它成员打开 |
| 下载 | 仅当产物关联着文件(`file_id` 非空)时出现,下载原始文件 |
| 重命名 | 仅当你对该产物有编辑权限时出现;打开一个对话框修改标题 |
@@ -120,6 +143,10 @@ Agent 会先向你确认目标读者、内容/数据来源、交互与视觉风
正文区域按产物的实际内容类型渲染(例如 HTML 报告会直接内联展示为页面)。
+
+在手机上打开的详情页会退化为**只读阅读器**:标题不再可行内编辑,分享、修改范围、删除入口一并隐藏,工具栏也不再显示返回列表按钮、文件类型图标、作用域徽标、公开链接徽标、「编辑于」时间与「最近更新来自会话」入口,只保留标题与产物正文;产物关联着文件时,「更多操作」里仍可下载原件。列表页与卡片视图不受影响。
+
+
## 分享产物
---
@@ -159,6 +186,28 @@ Agent 会先向你确认目标读者、内容/数据来源、交互与视觉风
超过 **16 MiB** 的产物无法生成公开链接,会提示该产物体积超限。此时仍可使用「仅账户内」模式分享。
+## 更新已发布产物
+
+---
+
+重新发布同一个文件**不会**静默覆盖已发布的产物。Agent 用 `publish_artifact` 更新时,必须同时带上该产物的 `artifact_id` 和上一次发布返回的 `if_revision`(一个不透明的版本指纹,形如 `rev_…`):
+
+| 情况 | 返回 | 结果 |
+|---|---|---|
+| `artifact_id` 与 `if_revision` 都对得上 | `status: published`,附 `operation`(`created` / `updated` / `unchanged`)与新的 `revision` | 产物原地更新(`unchanged` 表示内容没变),链接与产物 ID 不变 |
+| 只给了文件路径(没给 `artifact_id`),且内容与已发布版本不同 | `status: needs_review`,`reason: explicit_artifact_id_required` | 什么都不发布 |
+| 给了 `artifact_id` 但没给 `if_revision` | `status: needs_review`,`reason: revision_required` | 什么都不发布 |
+| `if_revision` 与当前版本不一致(产物在你拿到 revision 之后被改过) | `status: needs_review`,`reason: revision_conflict` | 什么都不发布 |
+
+返回 `needs_review` 时,Agent 会同时把**当前已发布内容**的副本写进会话工作区,并带上当前的 `revision`;它先读这份副本、确认合并后再用同一个 `artifact_id` 与新的 `revision` 重试,而不是直接覆盖。
+
+另外几点:
+
+- 内容与已发布版本逐字节一致时返回 `operation: unchanged`,产物不会被改动。
+- 用 `present_files` 重新展示同一个文件**不会**更新已发布的产物——它只提供预览与下载,发布更新要走 `publish_artifact`。
+- 产物已被删除时,Agent 用路径重新发布会以 `artifact_deleted` 失败,而不是把删掉的产物复活;只有你在会话里点 `present_files` 文件旁的「发布到产物库」按钮,才会恢复一个已删除的产物。
+- 发布失败会带一个错误码返回:`unsupported_format` / `permission_denied` / `artifact_deleted` / `storage_unavailable` / `file_too_large` / `invalid_path`,其中 `storage_unavailable` 可以重试。提交结果无法确认时返回 `status: unknown` 与一个 `op_…` 操作 ID,此时 Agent 不会另建一份产物。
+
## 权限
---
@@ -171,7 +220,7 @@ Agent 会先向你确认目标读者、内容/数据来源、交互与视觉风
| 账户 Owner / 管理员 | 对账户内任意产物(无论个人还是团队作用域)都有管理权限 |
| 团队成员(仅团队产物) | 当产物属于某个团队(`team_id > 0`)时,该团队的其它成员也可以管理它 |
-没有编辑权限的产物,卡片与详情页只提供「复制链接」「下载」等只读操作,「重命名」「删除」按钮不会出现。
+没有编辑权限的产物,卡片与详情页只提供「置顶」「复制链接」「下载」等只读操作,「重命名」「删除」按钮不会出现。其中**置顶不参与这套判断**:它是个人偏好而不是编辑,任何你能看到的产物(包括他人的个人产物)都能为自己置顶,只改变你自己的列表顺序,不影响任何人。
这与自动化规则的权限模型不同:账户 Owner / 管理员对**任意**产物(含他人的个人产物)都有管理权限,不存在『个人资源管理员无豁免』的限制。
diff --git a/zh/ai-sre/automations.mdx b/zh/ai-sre/automations.mdx
index dc5fa288..30b5a4a0 100644
--- a/zh/ai-sre/automations.mdx
+++ b/zh/ai-sre/automations.mdx
@@ -158,6 +158,9 @@ curl -X POST 'https://<触发地址>' \
在 **严重程度** 中选择 `Critical`、`Warning`、`Info` 中的一个或多个值。启用该触发器时,协作空间和严重程度都至少需要一个值。
+
+ **相似故障合并分析** 开关默认开启:开启后,本规则触发的新故障会先与正在分析中(或 10 分钟内刚分析完)的故障比较相似度,相似度不低于 0.9 的故障并入那次分析、不再单独开会话;关闭后每个故障各自分析。详见下文「相似故障合并分析」。
+
如果通过 API 创建或更新规则,对应字段如下:
@@ -167,11 +170,33 @@ curl -X POST 'https://<触发地址>' \
| `oncall_incident_trigger_enabled` | boolean | 是否启用 On-call 故障触发器。 |
| `oncall_incident_channel_ids` | int64[] | 监听的 On-call 协作空间 ID 列表;创建或启用该触发器时至少需要一个有效 ID。 |
| `oncall_incident_severities` | string[] | 监听的故障严重程度,支持 `Critical`、`Warning`、`Info`;创建或启用该触发器时至少需要一个值。 |
+| `oncall_incident_merge_similar` | boolean | 是否启用「相似故障合并分析」。默认开启;不传这个字段时按开启处理。 |
-匹配事件到达后,系统会以 `oncall_incident` 作为 `trigger_kind` 创建运行,并把 `incident_id`、`channel_id`、`severity` 等事件上下文传给会话。相同触发器与相同 `incident_id` 会复用同一次运行,避免同一故障重复拉起多个隐藏会话。
+匹配事件到达后,系统会以 `oncall_incident` 作为 `trigger_kind` 创建运行,并把 `incident_id`、`channel_id`、`severity` 等事件上下文传给会话。同一个故障——相同触发器与相同 `incident_id`——始终复用同一次运行,避免同一故障重复拉起多个隐藏会话;与此并列,开关开启时,与正在分析的故障足够相似的**另一个**故障也会并入那一次运行、而不是新建会话,见下文「相似故障合并分析」。
运行结束时,AI SRE 会把一条总结评论写回触发它的故障:结论写在最前面、正文简明,末尾附本次运行的完整会话链接。这条写回记录本身也带有一个常显的 **接续对话** 入口,在故障时间线上读结论时就能直接进入当次会话,见[故障时间线 · AI SRE 代操作记录](/zh/on-call/incident/search-view-incident#ai-sre-代操作记录)。评论沿故障现有的通知链路送达(例如 IM 里故障卡片的刷新与回帖),关注这条故障的人不用打开控制台就能看到分析结果。该行为对所有启用 On-call 故障触发的规则自动生效——包括自定义提示词的规则,无需额外配置。
+#### 相似故障合并分析
+
+「相似故障合并分析」(`oncall_incident_merge_similar`,默认开启)决定一个故障事件是新建会话,还是并入本规则下已经在跑的分析。开启后,命中该触发器的故障会先与**同一条规则**的运行比较相似度:
+
+- 相似度**不低于 0.9** 时,该故障并入得分最高的那次分析,不再新建隐藏会话。0.9 是平台固定值,与 On-call [智能聚合](/zh/on-call/channel/noise-reduction)的默认合并阈值同一口径,不随协作空间的聚合配置变化。
+- 相似度低于 0.9 时,该故障照常拉起一次自己的运行。
+- 关闭该开关后,不做相似度比较,每个故障各自分析。
+
+被比较的候选是那次运行正在分析的故障,以及此前已并入它的故障。合并只影响 AI SRE 的运行路由:**它不会在 Flashduty 中把两个故障合成一个**,本次会话绑定的故障也不变,两个故障各自存在、各自走通知与升级。被并入的故障没有自己的会话,因此也没有属于它自己的总结评论;本次分析若判断它与主故障同源,会在它的时间线上留下一条交叉引用评论,并把它一并写进结论。
+
+在该分析的对话里,并入渲染为一条过程记录:`已并入本次分析` + `# `(被并入故障的名称,链接到该故障自己的页面,在新标签页打开),并标注 `相似度 <得分>`(保留两位小数)。在运行历史里,这次运行同样会标出它并入了哪次分析。
+
+两条平台限制是固定的,不可配置:
+
+- **一次分析最多并入 20 个故障**。达到上限的那次分析不再接收相似故障,后来的相似故障另起一次运行,由新运行继续接收并入。
+- **已结束的运行只在结束后 10 分钟内可被并入**。仍在运行(`running` / `retrying`)的运行随时可被并入;成功或部分成功结束(`succeeded` / `partial`)的运行在结束后 10 分钟内仍可被并入,此后到达的相似故障另起一次运行。失败、跳过、放弃的运行不再被并入。
+
+
+相似度比较依赖账户的 AIOps 能力与专业版及以上订阅:平台关闭 AIOps、或账户版本不满足时,比较不会执行,每个故障各自拉起一次运行。
+
+
## 运行历史
---
@@ -182,7 +207,7 @@ curl -X POST 'https://<触发地址>' \
| 列 | 说明 |
|---|---|
-| 名称 | 本次运行对应的隐藏会话名称。后端会为每条运行批量解析其隐藏会话的标题(`session_name`,即该隐藏会话自动生成的会话名);解析不到时回退显示会话 ID(`session_id`) |
+| 名称 | 本次运行对应的隐藏会话名称。后端会为每条运行批量解析其隐藏会话的标题(`session_name`,即该隐藏会话自动生成的会话名);解析不到时回退显示会话 ID(`session_id`)。被并入其它分析的运行(见上文「相似故障合并分析」)没有自己的会话,这一列改显示它对应的故障(`# `),并在下一行标明它并入了哪次分析 |
| 执行时间 | 本次运行的开始时间 |
| 耗时 | 本次运行的持续时长 |
| 触发方式 | 本次运行的触发类型标签,如 `定时`(schedule)、`HTTP POST`、`On-call incident` 或 `手动执行` |
@@ -197,7 +222,8 @@ curl -X POST 'https://<触发地址>' \
| `succeeded` | 成功 |
| `partial` | 部分成功 |
| `failed` | 失败 |
-| `skipped` | 已跳过 |
+| `skipped` | 已跳过。两种情形共用这个状态:触发时判定不执行(例如规则所有者已失效、周期已停止),或该故障被并入另一个正在运行的分析(见上文「相似故障合并分析」) |
+| `blocked` | 已阻塞。本次运行产生了真实输出,但停在等待人工处理的连接器上——某个连接器还没配置,或需要管理员在控制台完成授权 |
| `abandoned` | 已放弃(长时间未完成被系统终止) |
表格上方提供三个筛选项:
@@ -206,9 +232,25 @@ curl -X POST 'https://<触发地址>' \
- **状态**:按上表中的运行状态过滤,或选 **全部状态**。
- **触发类型**:`全部触发类型` / `手动执行` / `定时` / `HTTP POST` / `On-call incident` 五选一。
-API 返回的运行记录还包含 `trigger_kind`,可能取值为 `schedule`、`manual`、`http_post`、`oncall_incident` 或 `debug`。其中 `manual` 表示通过立即执行接口启动,`oncall_incident` 表示由匹配的 On-call 故障事件启动。
+API 返回的运行记录还包含 `trigger_kind`,可能取值为 `schedule`、`manual`、`http_post`、`oncall_incident` 或 `debug`。其中 `manual` 表示通过立即执行接口启动,`oncall_incident` 表示由匹配的 On-call 故障事件启动。被并入其它分析的运行不返回自己的 `session_id`,而是返回它并入的那次分析的会话 ID `joined_session_id` 与会话名 `joined_session_name`;这两个字段为空,表示这次运行有自己的隐藏会话。
+
+点击一行,会跳转到这次运行对应的隐藏会话对话页(`chat?session_id=<会话ID>`),让你查看该次运行完整的消息、工具调用与产物。被并入其它分析的运行没有自己的会话,点击它会打开接管这个故障的那次分析。
-点击任意一行,会跳转到这次运行对应的隐藏会话对话页(`chat?session_id=<会话ID>`),让你查看该次运行完整的消息、工具调用与产物。
+### 未成功运行的原因行
+
+运行状态为 `failed`、`blocked`、`abandoned`、`skipped` 之一时,「名称」单元格会在会话名下方多显示一行 **原因**——文本按一行截断,鼠标悬停可看全文。若这次运行没有可显示的会话名,原因行会直接占据这一格。原因行右侧有时还带一个跳转入口,在新标签页打开。
+
+平台自己判定的原因有固定文案:
+
+| `error_code` | 显示的原因 | 跳转入口 |
+|---|---|---|
+| `BalanceNotEnough` | AI SRE 未开通,或本周期额度/余额不足,本次未执行 | 前往 AI SRE 计费(`/wallet/plan?product=ai-sre`) |
+| `NoLicense` | On-call 订阅已失效或版本不含 AI SRE,AI SRE 已暂停 | 前往 On-Call 订阅(`/wallet/plan?product=oncall`) |
+| `task_run_stale` | 执行超时未完成,已放弃 | 无 |
+| `interrupted` | 本次执行被中断 | 无 |
+| `no_final_response` | 执行已结束,但没有生成最终回复 | 无 |
+| `similar_incident_fan_in` | 已并入「`<分析名>`」;分析名解析不到时显示「另一次分析」 | 无 |
+| 其它 `error_code` | 直接显示后端记录的原因文本;没有文本时显示 `error_code` 本身 | 无 |
运行历史内嵌在规则详情页中,而打开详情页本身就要求你对该规则有编辑权限——没有编辑权限的规则连详情页都无法打开(会提示「自动化规则不存在或无权访问」),因此其运行历史也无法查看。
diff --git a/zh/ai-sre/environments.mdx b/zh/ai-sre/environments.mdx
index ae0047b5..a5fd1a98 100644
--- a/zh/ai-sre/environments.mdx
+++ b/zh/ai-sre/environments.mdx
@@ -73,6 +73,18 @@ AI SRE 提供两类运行环境:
环境变量以明文形式对所有使用该云端环境模板的人可见——请勿在这里填写密钥或凭据。
+**平台保留的环境变量名。** 有一部分变量名由平台自己注入到会话的运行环境里,模板不得设置——写了会在**保存时被拒绝**(服务端是这份名单的唯一真相源,界面上的提示清单可能滞后):
+
+| 保留名 | 说明 |
+|---|---|
+| `FLASHDUTY_APP_KEY` / `FLASHDUTY_BASE_URL` | Flashduty API 的凭据与地址,供内置的 `flashduty` Skill 与运行时调用。 |
+| `FLASHDUTY_DOCS_URL` | 官方文档包的静态地址;内置的 `flashduty-docs` Skill 从这里下载文档副本。 |
+| `GH_TOKEN`、`GH_TOKEN_` 前缀、`GH_PROMPT_DISABLED`、`GIT_CONFIG_` 前缀 | GitHub 侧由平台注入的令牌与 git 配置(一个账户可能有多个 GitHub App 安装,因此 `GH_TOKEN_1`、`GH_TOKEN_2` 这类也保留)。 |
+| `PATH` / `HOME` | 运行环境的基础环境变量。 |
+| `GITLAB_TOKEN`、`GITLAB_ACCESS_TOKEN`、`OAUTH_TOKEN`、`GITLAB_HOST`、`GL_HOST`、`GITLAB_URI`、`GITLAB_API_HOST`、`GITLAB_SSH_HOST`、`GITLAB_SUBFOLDER`、`CA_CERT`、`SKIP_TLS_VERIFY`、`GLAB_` 前缀 | GitLab / `glab` 的认证、主机与 TLS 配置;允许模板覆盖其中任何一项,都等于让会话绕过平台注入的机器人身份去改 `glab` 的传输目标。 |
+
+这些名字在**每次执行命令前**由平台注入,覆盖平台的同名项会被丢弃,以平台值为准(这一点在云端 Sandbox 与 BYOC Runner 上一致)。因此模板里的环境变量只用来放业务相关的非敏感配置——需要密钥时,请用 MCP 的成员授权或相应的 App 集成。
+
启动脚本在**全新沙箱(Ubuntu 24.04,以 root 身份运行)**内、**Agent 启动前**执行,典型用途是用 `apt` 安装 Agent 需要的软件包。
### 删除云端环境模板
@@ -413,6 +425,15 @@ permission:
历史会话打开时,选择器会以只读形式展示这条会话当初锁定的环境及其当前状态。如果绑定的 Runner 已离线或被删除,该会话不能继续发送消息;请先恢复 Runner,或新建会话改用云端 Sandbox。
+### 子会话的执行环境
+
+派发出去的子代理会带出一条自己的**子会话**,它的执行环境规则与根会话不同:
+
+- **默认继承父会话**:`agent_dispatch` 不指定 `environment` 时,子会话与父会话**共用同一个沙箱 / workspace**,因此它能直接看到父会话的文件——子会话面板里显示的环境也就与父会话相同。
+- **只有跨环境派发才有自己的环境**:把子任务派到另一个环境(`agent_dispatch` 的 `environment` 参数,例如另一个 BYOC Runner 或云端环境,见 [Agent](/zh/ai-sre/agents))时,子会话会得到**自己的**沙箱 / Runner scratch 目录,读不到父会话的文件。
+- **子会话面板不提供环境选择器**:它只以**只读**方式展示该子会话绑定的环境及其当前状态——云端环境,或自托管 Runner 的名称,与根会话的只读展示共用同一套状态语义(正常运行时不额外标注;离线 / 性能下降 / 未连接 / 已删除时给出短标签与悬停说明)。
+- **A2A 子会话不显示运行环境**:远端 Agent 在它自己的环境里执行,位置不由本平台决定。
+
## 作用域
---
diff --git a/zh/ai-sre/im.mdx b/zh/ai-sre/im.mdx
index f7e54c6a..fdc6e724 100644
--- a/zh/ai-sre/im.mdx
+++ b/zh/ai-sre/im.mdx
@@ -79,6 +79,21 @@ IM 交互依赖你已在 Flashduty 中接入对应平台的机器人(用于告
**回复模式**可配置(off / first / all),决定 AI SRE 是否在回复时 @ 提问者、以及是在**线程内**还是主频道作答。在嘈杂的大群里,线程内回复能让排查讨论保持聚拢、不刷屏。
+### 答复如何抵达聊天
+
+AI SRE 在 IM 里的答复**只经 `reply` 工具投递**——模型输出的普通文本是内部草稿,不会自动发到聊天。你把一次排查交给它之后,群里出现的每一条消息都是它主动提交的,属于两类之一:
+
+- **结论**:回答你的具体问题,或给出一个可行动的阶段性结论。
+- **进展**:工作仍在进行时的一两句话更新——确认的进展、当前在等什么、遇到的真实阻塞。进展消息**不是结论**,它不结束这轮任务,结论会照常单独投递。
+
+如果一轮以自然收尾结束,而结论只写成了普通文本、没有经 `reply` 提交,系统会自动补一次**只允许投递**的纠正:这次纠正只开放 `reply`,不执行任何调查或变更类操作;纠正后仍未提交,该轮以错误结束——那份草稿始终不会出现在群里。取消、被新消息打断、派发子代理、等待子代理回报、输出被截断等非自然结束的回合不会触发这次纠正。
+
+这套交付契约只作用于**由人发起的 IM 请求**:控制台会话仍按流式文本作答;作战室自动诊断是无人值守的后台回合,也保持原生流式输出。
+
+
+单条答复还有阅读长度上的软约束:过长时 `reply` 会拒绝一次,并建议精简重复内容,或把详细的证据与对比发布为产物、只把关键结论和产物链接发到聊天。因此 IM 里的长分析通常长成「结论 + 产物链接」,而不是一条很长的消息,详见[产物](/zh/ai-sre/artifacts)。当你在消息里明确要求「把完整报告直接贴在聊天里」时,这条约束不生效。
+
+
## 作战室自动诊断
---
@@ -120,6 +135,21 @@ Agent 的当前回合结束时,如果它留下的**常驻进程类任务**还
通知回合遵循「无消息 = 没有新进展」的静默语义:如果某个通知回合没有新内容要投递,AI SRE 会静默关闭该回合,**不向聊天发送任何占位回执**,也不会重复投递监控中卡片;只有真正有新结论时才会在聊天里出现新消息。
+### 长排查中的进展消息
+
+排查链路长的时候,聊天里可能长时间没有动静——过程性文字不下发,监控中卡片也要等出现常驻任务才会发。为此,当一次 IM 请求**连续静默约 60 秒**仍没有任何公开回复提交时,系统会视情况给模型加一条瞬态提醒,把「你已经等了多久」告诉它,由它自己决定要不要发一条进展消息:
+
+- **提醒节奏**:同一请求两次提醒至少间隔 **3 分钟**,一段连续静默里**最多提醒 2 次**。任何一次提交成功的回复(结论或进展)都会开启新的静默段、重新计数,但不会缩短那 3 分钟的最小间隔。
+- **等待时长从哪算起**:从你的消息进入平台待处理队列的时刻起算,排队时间同样计入——不是从 Agent 真正开始处理的时刻起算。
+- **提醒不会自己发消息**:它只是把等待时长告诉模型,不替它往群里写任何东西;已经说过的等待、且没有新变化时,或者模型正在等你补充信息时,不发才是正确的。
+- **进展消息不是结论**:它表示「还在查、卡在哪、下一步看什么」,不结束这轮任务,结论随后单独投递。
+
+
+这里的「静默」和上一段的「无消息 = 没有新进展」不是一回事:那一条讲的是**通知回合**没有新内容时不发占位回执;这里讲的是**人在等**、而 Agent 还没有任何公开答复。
+
+
+等待提醒只出现在**有人值守的 IM 请求**上;控制台、API 与自动化渠道都没有等待提醒。
+
## 连接与授权
---
diff --git a/zh/ai-sre/mcp.mdx b/zh/ai-sre/mcp.mdx
index 80ac4826..897587e6 100644
--- a/zh/ai-sre/mcp.mdx
+++ b/zh/ai-sre/mcp.mdx
@@ -72,6 +72,22 @@ sidebarTitle: MCP
需要 **BYOC Runner** 的模板(详情中的 `requires_runner` 标识)只能在您自己部署了 Runner 的运行环境中使用,云 Sandbox 不支持。`requires_runner` 与传输方式相互独立,当前市场的 50 个模板里两种都有:23 个是 **HTTP 流式**、不需要 Runner,另外 27 个是 **stdio**、需要 Runner——包括阿里云 OpenAPI、阿里云可观测、夜莺 Nightingale、Gitee、GreptimeDB、Prometheus、Grafana (Self-Hosted)、Kafka (Confluent)、AWS、ClickHouse、MySQL、PostgreSQL、阿里云 RDS、Redis、MongoDB 等。这些 stdio 模板以 `uvx` / `npx` 命令在各环境上本地启动,云 Sandbox 无法启动本地子进程,因此安装前请确认您的账户已配置可用的 BYOC Runner,详见 [运行环境(BYOC)](/zh/ai-sre/environments)。
+### 只能由 Monitors 查询的系统
+
+目录里还有一类行,代表**只有 Monitors 能查询的系统**(当前是 Loki、VictoriaLogs、Oracle):它们列在对应类别下(可观测 / 数据库);同一屏混有 MCP / App / Skill 等多种类型时,行上带 `Monitors` 类型标签。它们**不是可以通过市场接入的连接器**,只是「在 Monitors 里添加数据源之后,AI SRE 就能查这个系统」的入口,因此不会出现在上方的 **推荐** 区(推荐区是可连接模板的清单)。
+
+点击这样的一行(或它右侧的 **+**,两者打开的是同一个面板)后,面板里**没有「连接」按钮**,只有:
+
+- 账户里**该类型的数据源清单**与状态,形如「已接入 N 个 · 走告警引擎,不用地址、令牌和 Runner」;还没有这类数据源时,显示「还没有 〈类型〉 数据源 · 在 Monitors 里添加后就能查,不用 Runner」。行右侧的 **+** 只在账户还没有该类型数据源、且你有添加数据源权限时才出现,否则该位置显示「已接入 N 个」或「由 Monitors 管理员添加」。
+- 已经接入该类型数据源时,主按钮是 **在对话里试试**:用你的数据源预填一条提问并新建会话(不会自动发送)。
+- 还没有这类数据源时,给出 **去 Monitors 添加更多 →**,新开一页到 Monitors → 数据源 → 添加(类型已选好);没有添加权限的成员只看到「由 Monitors 管理员添加后,这里自动更新」,不给出这个动作。
+
+有些 MCP 模板同时支持「直连 MCP」和「走 Monitors」两条路(例如 Prometheus、MySQL、PostgreSQL、ClickHouse、Elasticsearch、SLS),这类行的标签会写成 `MCP · Monitors`,连接对话框里多一个「接入方式」二选一。
+
+
+行名与模板名共用同一套命名空间:如果某个系统已经有同名模板,就只显示模板行(模板行才是带连接参数的那一行)。私有化部署若**不含 Monitors 模块**,上述行不会列出,模板的对话框里也不提供 Monitors 这一路——列表接口会返回 `monit_available: false`,控制台据此隐藏相关入口。
+
+
## 添加 MCP 服务器
---
@@ -88,7 +104,7 @@ sidebarTitle: MCP
| 名称 | string | 是 | 服务器名,会作为 Agent 调用时的标识(如 `mcp:sqlite-explorer/query` 中的 `sqlite-explorer`)。须以字母开头,仅含字母、数字、`-`、`_`,长度 1–255。同一账户内**不区分大小写、不可重名**,也不能与内置服务器同名 |
| 传输方式 | 枚举 | 是 | Agent 与服务器通信的方式,见下文「传输方式」 |
| 范围 | 共享 / 团队 | 是 | 该 MCP 服务器的作用域:**共享**(账户内全局可见)或某个**团队**(仅该团队成员可见)。详见下文「作用域」 |
-| 执行环境 | 云端环境 / BYOC Runner(可多选) | 否 | 决定该 MCP 服务器在哪些执行环境中可用。可以选择云端环境和一个或多个当前可见的 BYOC Runner;不选择时默认在所有环境可用。它不指定调用路由:MCP 调用仍在当前 AI SRE 会话自身的执行环境中运行。若服务只在某个内网可访问,请只选择能访问它的 Runner。详见 [运行环境(BYOC)](/zh/ai-sre/environments) |
+| 执行环境 | 云端环境 / BYOC Runner(可多选) | 否 | 决定该 MCP 服务器在哪些执行环境中可用。可以选择云端环境和一个或多个当前可见的 BYOC Runner;不选择时默认在所有环境可用。它不指定调用路由:MCP 调用仍在当前 AI SRE 会话自身的执行环境中运行。若服务只在某个内网可访问,请只选择能访问它的 Runner。反过来,当这个列表**不包含**当前会话所在的环境时,该会话不能直接调用它——但 Agent 仍然知道它存在、以及它能在哪个环境里运行,见下文「跨环境连接器」。详见 [运行环境(BYOC)](/zh/ai-sre/environments) |
| 描述 | string | 是 | 描述此服务器的功能,便于在列表中识别 |
@@ -204,6 +220,19 @@ MCP 列表以表格展示每台服务器的**名称**(含 AI 描述)、**范
Agent 读取 Flashduty 故障、告警等数据的能力是**内置**的:**Flashduty MCP 服务器**在每个会话启动时由运行时直接注入给 Agent,不经过本页的 MCP 服务器列表接口——它不会出现在上方的服务器列表中,也无需(也无法)在此手动配置、启用或查看。该能力由平台维护,随账户默认可用。
+### 跨环境连接器
+
+一台 MCP 服务器可以被绑定到**当前会话之外的执行环境**。这时该会话不能直接调用它——不同执行环境之间网络并不互通,工具调用只在会话自身的环境里发起。但 Agent 不会因此以为它不存在:
+
+- **Agent 一开始就知道它**:会话启动时,Agent 会看到已连接、但绑定在别的执行环境上的连接器清单——名称、用途,以及**这台连接器在哪些执行环境里可用**。同一台连接器在不同环境里可能解析出不同的名称,目录里会带上团队标注以便区分。
+- **搜索会给出它的可用环境**:某次搜索正好匹配到这台连接器时,结果里会附带它能在哪些执行环境运行。
+- **点名提问不会得到「找不到」**:Agent 直接按名字找这台连接器时,会得到「已连接,但绑在别的执行环境」的说明以及该怎么用,而不是当作账户里没有它。
+- **用法是派一个在那个环境里运行的子代理过去**:Agent 会把任务派发给一个在目标执行环境里运行的子代理(`agent_dispatch` 的 `environment` 参数,见 [Agent](/zh/ai-sre/agents)),由子代理在自己那一侧发现并调用这台服务器的工具,再把结果带回当前对话。
+- **只列出现在真正派得过去的环境**:目录里的目标环境是**账户级 Runner** 加上**当前会话所绑定团队**的 Runner,且必须**在线**(以及绑定的云端环境);离线、从未连接、已删除、或属于会话不能派发的团队的环境会被标注出来并给出原因,Agent 会如实说明「现在没法派过去」,而不是假装能调。
+- **只有对话的主会话会看到这份目录**:派发出去的子代理没有自己的派发能力,它只报告自己所在的环境里能直接调用的连接器;需要换环境时由子代理回报父会话处理。
+
+反过来,如果想让某台连接器在**当前**环境里直接可用,把它加进该服务器的「执行环境」列表即可(前提是它对这个会话可达)。运行环境本身的区别见 [运行环境(BYOC)](/zh/ai-sre/environments)。
+
## 作用域
---
diff --git a/zh/ai-sre/sessions.mdx b/zh/ai-sre/sessions.mdx
index c0e20178..002f8bad 100644
--- a/zh/ai-sre/sessions.mdx
+++ b/zh/ai-sre/sessions.mdx
@@ -158,6 +158,10 @@ Subagent / A2A 子会话本身也不能单独开启分享——分享只能在**
回合运行期间,**发送按钮会变为停止按钮**。点击停止会立即中断当前回合:界面随即反馈,被中断的回合会带上「已中断」标记,刷新后依然可见。
+
+会话流以**帧**的形式推送进展,每一帧都可能携带 `is_error` 与 `error_code` 两个字段,用来标记**运行时失败**。判定刻意收得很窄:只有运行环境把一次真实失败转换成面向用户的错误、并在持久化的事件行上打了标记之后,这一帧才会带上它们;**平台内部的控制行(回合结算、自动化回合结果等簿记事件)与模型 / 供应商返回的原始错误文本都不会进入这条路径**。所以带 `error_code` 的帧可以当作「平台对用户暴露的运行时错误」来消费;反过来,没有这两个字段也不代表这一帧内部没有诊断信息。这与下方「[会话数据导出](#会话数据导出)」里那条表示导出流出错的 `error` 行不是一回事:`is_error` / `error_code` 挂在正常的事件帧上,`error` 是流本身出问题时追加的独立行。
+
+
### 运行中继续输入(排队)
回合运行期间输入框依然可用:您可以继续输入并发送,消息会进入队列,在当前回合结束后依次执行。排队消息以一张可折叠的卡片展示在输入框上方,标题显示排队条数(如「3 条排队」);队列中的消息可逐条编辑或移除,超过一条时卡片右上角还提供 **全部清空** 一键清空整个队列。
@@ -257,7 +261,7 @@ OAuth 授权链接有过期时间;过期后卡片会提示「授权链接已
### 子任务(Subagent)
-Agent 委派子任务时,对话中会出现一行可点击的**派发引用**:状态环 + 类型徽标(Agent / A2A)+ 子任务名称与当前意图,右侧对齐显示状态与耗时;结束后补上工具调用数 / Token 用量 / 总耗时,失败时在下方附一行红色的失败原因。引用行是紧凑的一行,**不带停止按钮**——停止操作统一放在任务面板的详情头部(见下文「任务面板与后台任务」)。点击引用行会在右侧打开一个与主对话并排的子会话面板——主对话区域随之收窄,而不是被弹窗遮挡;面板可展开为占满主区域的全屏视图,也可以收起回并排布局。
+Agent 委派子任务时,对话中会出现一行可点击的**派发引用**:状态环 + 类型徽标(Agent / A2A)+ 子任务名称与当前意图,右侧对齐显示**目标执行环境**、状态与耗时;结束后补上工具调用数 / Token 用量 / 总耗时,失败时在下方附一行红色的失败原因。环境这一段只标位置——**云端环境**,或该 BYOC Runner 的名称(解析不到名称时只显示「自托管」),**不带状态点**;A2A 委派(远端 Agent 在自己那边执行)与没有环境绑定的历史任务不显示这一段。引用行是紧凑的一行,**不带停止按钮**——停止操作统一放在任务面板的详情头部(见下文「任务面板与后台任务」)。点击引用行会在右侧打开一个与主对话并排的子会话面板——主对话区域随之收窄,而不是被弹窗遮挡;面板可展开为占满主区域的全屏视图,也可以收起回并排布局。
除进行中、完成、失败、已中断外,子任务还可能处于两种「需要你」的状态:
diff --git a/zh/ai-sre/skills.mdx b/zh/ai-sre/skills.mdx
index dfbc95a5..2db9a068 100644
--- a/zh/ai-sre/skills.mdx
+++ b/zh/ai-sre/skills.mdx
@@ -84,6 +84,12 @@ frontmatter 字段如下:
AI SRE 运行时内置了几个 Skill,无需安装即可使用。`flashduty` 是其中一个范例:它通过 `fduty` 命令行覆盖整个 Flashduty API,让 Agent 可以排障故障、读取 AI 详情、查询告警、关联变更等。您可以参考它来编写自己的 Skill。另一个内置 Skill 是 `github`,Agent 会从 `` 中自主选用它,让 AI SRE 直接在 GitHub 仓库里工作——探索代码、调查 PR / 提交、按需开 PR 或 Issue;它需要安装 GitHub App(云端)或运行环境主机上的 `gh`(BYOC)。第三个内置 Skill 是 `gitlab`,能力与 `github` 对称:Agent 自主选用它在 GitLab 仓库里探索代码、追溯 MR / Issue、按需开 MR 或 Issue;它需要安装 GitLab App(云端)或运行环境主机上的 `glab`(BYOC)。详见 [Apps](/zh/ai-sre/apps)。
+
+还有 `flashduty-docs`:它把 Flashduty 的**官方文档**(中英文两套页面、术语表与 Open API 规范)下载到运行环境本地,按主题检索后回答**产品本身怎么用**的问题——功能、设置、上限、套餐、兼容性、SDK 行为与影响、API 字段与错误码,覆盖 On-call、RUM、Monitors、AI SRE、状态页、平台与 Open API;回答会附上 docs.flashduty.com 的公开链接,并且只依据文档作答,文档没写到的地方会直说。它只读文档,不代替动手排查(RUM 的实际接入工作归 `rum`),文档包的地址由平台注入,无需配置。
+
+
+
+上面几个只是举例,不是全部:运行时还内置 `init`、`insight`、`artifact-design`、`alert-governance`、`monit-query` 等 Skill,并会随版本增补。会话里 Agent 实际看到的可用清单以 `` 为准;想确认某条会话里到底有哪些内置 Skill,直接问 Agent 即可。
## 从市场安装
diff --git a/zh/developer/cli.mdx b/zh/developer/cli.mdx
index 4b0bc314..d09401d7 100644
--- a/zh/developer/cli.mdx
+++ b/zh/developer/cli.mdx
@@ -341,7 +341,9 @@ flashduty rum application-delete # 删除应用
| `--is-private` | 是否仅允许团队成员访问 |
| `--no-geo` | 是否禁用地理位置推断 |
| `--no-ip` | 是否禁用 IP 采集 |
-| `--data` | 可补充 `alerting`(通知配置)和 `tracing`(链路追踪配置)对象 |
+| `--data` | 可补充 `alerting`(通知配置)、`links`(外部链接集成)、`repositories`(关联代码仓库)和 `tracing`(链路追踪配置)对象 |
+
+`--data` 里的 `repositories` 把应用关联到构建它的代码仓库:有序数组,第一项为主仓库,最多 10 项,每项为 `repo`(`owner/name` 形式的 GitHub 仓库,必填)加可选的 `subdir`(应用在仓库内所在的目录,相对仓库根目录,`.` 表示仓库根目录,传空值会保存为 `.`)。创建时传入即建立关联;更新时按 presence 处理——传入即整体替换、传空数组清空全部关联、省略则保持不变;`application-info`、`application-infos`、`application-list` 都会回显该字段。关联本身不授予任何访问权限:AI 会话只能读取账户 GitHub App 安装已授权的仓库(见 [应用与集成](/zh/ai-sre/apps))。该字段目前仅通过 API 与 CLI 提供,控制台的应用管理页不暴露仓库关联设置。
`application-webhook-test` 会返回 `ok`、`status_code` 和 `message`,可用于验证 RUM 告警 Webhook 是否真正收到了平台发出的测试事件。
@@ -683,7 +685,7 @@ flashduty insight incident-export [flags] # 导出筛选后的故障列表为
### 全量命令覆盖
-除上述精选命令外,CLI 现已通过 spec 驱动的代码生成实现对 Flashduty OpenAPI 的**全量覆盖**。CLI 生成器读取的 OpenAPI 规范含 **340 个 API 操作**,CLI 为其中 **336 个** 生成对应命令,其余 4 个以手工实现命令提供——流式导出的 `session-read-export`(`session export`)、multipart 表单上传的 `mapping-data-write-upload` 与 `skill-write-upload`(`enrichment mapping-data-upload`、`safari skill-upload`)、带路径参数的 `monit-prometheus-read-label-values`(`monit prometheus-api-v1-label-{label_name}-values`,见下文「查询 Prometheus 标签值」)——并与生成命令一并按资源组织为顶层命令组。除 On-call 域(incident、incident-trigger-subscription、change、channel、field、status-page、template 等)外,还覆盖了:
+除上述精选命令外,CLI 现已通过 spec 驱动的代码生成实现对 Flashduty OpenAPI 的**全量覆盖**。CLI 生成器读取的 OpenAPI 规范含 **338 个 API 操作**,CLI 为其中 **334 个** 生成对应命令,其余 4 个以手工实现命令提供——流式导出的 `session-read-export`(`session export`)、multipart 表单上传的 `mapping-data-write-upload` 与 `skill-write-upload`(`enrichment mapping-data-upload`、`safari skill-upload`)、带路径参数的 `monit-prometheus-read-label-values`(`monit prometheus-api-v1-label-{label_name}-values`,见下文「查询 Prometheus 标签值」)——并与生成命令一并按资源组织为顶层命令组。除 On-call 域(incident、incident-trigger-subscription、change、channel、field、status-page、template 等)外,还覆盖了:
- **AI SRE(`safari`)**:a2a-agents、artifacts、automations、knowledge、mcp-servers、sessions、skills 等
- **告警与降噪**:alert、alert-event、enrichment(alert-rules、rule-sets)、route
diff --git a/zh/developer/go-sdk.mdx b/zh/developer/go-sdk.mdx
index ee54a376..c72a1777 100644
--- a/zh/developer/go-sdk.mdx
+++ b/zh/developer/go-sdk.mdx
@@ -184,13 +184,13 @@ client, err := flashduty.NewClient("YOUR_APP_KEY",
`client.Artifacts`(AI SRE 产物)对应 `/safari/artifact/*` 的 11 个 API 操作:画廊读取侧 `ReadGet`(按 ID 获取单个已发布产物)、`ReadList`(分页列出调用方可见的产物,支持标题子串搜索、`scope`(`all` / `personal` / `team`)与 `team_ids` 过滤)、`ReadGetFileState`(批量探测会话展示文件(`pf_` 前缀)是否已有上线产物,单次至多 50 个 ID);文件侧 `ReadSign`(为展示文件签发短期有效的下载/预览 URL,有效期 5 分钟,`expires_in` 固定 300 秒)与 `ReadStream`(凭签名 token 下载或预览文件,成功响应体是文件而非 JSON 信封,原始字节放在 `Response.Raw`);写入侧 `WritePublish`(把会话产生的文件发布为画廊产物)、`WriteUpdate`(重命名或转移个人/团队作用域)、`WriteDelete`(从画廊移除,源文件仍保留在会话中);公开分享 `WriteShareEnable`(开启匿名公开分享并返回公开链接,任何人凭链接即可查看、无需登录)、`WriteShareRevoke`(关闭分享,链接立即失效)、`WriteShareSync`(把公开快照刷新为最新内容——当 `share_enabled` 为 true 且 `share_file_id` 与 `file_id` 不一致时表示快照已过期,调用它刷新)。相关导出类型包括 `PublishedArtifactItem`、`ArtifactShareState`、`SignedUrLs` 以及各 `Artifact*Request` / `Artifact*Response`。
-`client.Members.MemberNotify`(`POST /member/notify`,`memberNotify`)以调用方身份向账户成员发送邮件,仅可使用 AI SRE 会话凭据调用——其他凭据(包括普通 `app_key`)一律以 `AccessDenied` 拒绝(用 `IsAccessDenied` / `ErrorCodeOf` 判定)。请求字段:`subject`(必填,1–200 字符)与 `html`(必填,收件人看到的完整邮件正文,不超过 102,400 字节),`person_ids` 可选(至多 20 个且不可重复,省略或为空时只发送给调用方本人)。`html` 中的 `script`、`meta`、`link`、`base` 标签及 `on*` 事件属性会在发送前被静默清除;而 `style`、`svg`、`iframe`、`object`、`embed`、`form`、`input`、`button` 标签,没有 `https` `src` 的 `img`,或者链接不属于 `http`/`https`/`mailto` 的,会在入队前直接以 `InvalidParameter` 拒绝调用,并在错误信息中逐一列出触发的构造。投递是异步的,`accepted` 只表示邮件已入队;响应按收件人逐条返回 `recipients[]`(`person_id` 与 `status`),`status` 为 `accepted` 或 `skipped`,后者带 `reason`:`not_member`、`no_email`、`email_disabled`、`duplicate`、`rate_limited`(同一收件人每小时最多 20 封)、`send_failed`。
+`client.Members.MemberNotify`(`POST /member/notify`,`memberNotify`)以调用方身份向账户成员发送邮件,仅可使用 AI SRE 会话凭据调用——其他凭据(包括普通 `app_key`)一律以 `AccessDenied` 拒绝(用 `IsAccessDenied` / `ErrorCodeOf` 判定)。请求字段:`subject`(必填,1–200 字符)与 `html`(必填,收件人看到的完整邮件正文,不超过 102,400 字节),`person_ids` 可选(至多 20 个且不可重复,省略或为空时只发送给调用方本人)。`html` 中的 `script`、`meta`、`link`、`base` 标签及 `on*` 事件属性会在发送前被静默清除;而 `style`、`svg`、`iframe`、`object`、`embed`、`form`、`input`、`button` 标签,没有 `https` `src` 的 `img`,或者链接不属于 `http`/`https`/`mailto` 的,会在入队前直接以 `InvalidParameter` 拒绝调用,并在错误信息中逐一列出触发的构造。投递是异步的,`accepted` 只表示邮件已入队;响应的 `recipients[]` 按收件人逐条返回(`person_id` 与 `status`),`status` 为 `accepted` 或 `skipped`,后者带 `reason`:`not_member`、`no_email`、`email_disabled`、`duplicate`、`rate_limited`(同一收件人每小时最多 20 封)、`send_failed`。响应只有 `recipients[]` 和可选的 `agent_instructions` 两个字段,不回显清洗后的邮件 HTML;当提交的 `html` 不符合默认邮件版式(缺少 `max-width:600px` 外层包裹表格)时会返回 `agent_instructions`,内容是写给调用的 AI SRE agent 的版式对齐指引——仅为建议,调用方有意选择的格式无需改动。
`client.Diagnostics.QueryData` 通过 `POST /monit/query/data` 执行同步查询,返回稳定的 `query_result.v1` 结构化结果(`result.kind` 为 `frames` / `records` / `samples` 之一),要求 monit-edge v0.65.0 及以上版本。日志模式和指标趋势分析统一使用 `client.DataSources.ToolsInvoke`,工具名称为 `prometheus.metric_trends`、`loki.log_patterns` 或 `victorialogs.log_patterns`。
`client.Diagnostics.QueryExplore`(`POST /monit/query/explore`,`monit-read-query-explore`)对已配置的数据源执行探索查询,返回数据源原生形态的 `explore_result.v1`:`ExploreData.format` 恒为 `explore_result.v1`,`result.kind` 取 `frames`(列式表格或时序,见 `ExploreFrame` / `ExploreField`)、`samples`(带标签的瞬时值 `ExploreSample`)或 `logs`(日志条目 `ExploreLogEntry`,最多 1000 条,被截断时 `has_more` 为 true、`applied_limit` 给出实际生效的上限),`execution.effective_step_seconds` 回显应用 `max_data_points` 与 `min_step_seconds` 之后实际使用的步长。请求字段:`datasource_id`(取自 `/monit/datasource/list`,必须是当前账户下的数据源)、`expr`(数据源原生语言的查询表达式,如 PromQL、LogsQL、SQL)、`args`(Grafana 风格宏变量替换的字符串键值;请求中必填,没有宏变量时传空表 `map[string]string{}`——Go 的 `nil` map 会序列化为 `null`,不符合 schema)与 `execution`(`QueryExploreExecution`:`kind` 取 `instant` / `range` / `window`,`to_ms` 在三种模式下都必填,`range` 另需 `from_ms` 与 `max_data_points`,`window` 另需 `from_ms`,`min_step_seconds` 仅在 `range` 下接受)。与 `QueryData` 的分工:需要数据源原生结果形态(含原始日志)时用 `QueryExplore`,需要稳定的 `query_result.v1` 契约时用 `QueryData`。该接口需要「数据源查看」(`monit`)权限,并要求受支持的部署运行 monit-edge v0.68.0 及以上版本。
-`client.Applications`(RUM 应用)除应用管理外还覆盖 **RUM 远程配置**的 5 个方法:`RemoteConfigReadGet`(`POST /rum/application/remote-config/get`,读取当前生效的配置与 `version`)、`RemoteConfigReadHistoryList`(`/rum/application/remote-config/history/list`,分页列出已发布的历史版本)、`RemoteConfigReadPreview`(`/rum/application/remote-config/preview`,用模拟客户端的 `env` / `app_version` / `sdk` 上下文评估一份草稿配置,返回命中的 `hit_rule_index` 与最终下发的 `values`,不发布)、`RemoteConfigWriteUpdate`(`/rum/application/remote-config/update`,发布一份完整的新配置)、`RemoteConfigWriteHistoryRevert`(`/rum/application/remote-config/history/revert`,把历史版本的内容重新发布为新版本)。主要类型:`RemoteConfig`(`enabled`、`activation`、`default`、`rules`、`custom`、`refresh_on_foreground`)、`RemoteConfigValues`(四个可下调的 SDK 开关)、`RemoteConfigRule`(`match` 条件加 `set` 取值)、`RemoteConfigHistoryItem`(含 `version`、`reason`、`content_hash`、`updated_by`),以及 `GetRemoteConfigRequest` / `ListRemoteConfigHistoryRequest` / `PreviewRemoteConfigRequest` / `UpdateRemoteConfigRequest` / `RevertRemoteConfigRequest` 与各自的 `*Response`。
+`client.Applications`(RUM 应用)除应用管理外还覆盖 **RUM 远程配置**的 5 个方法:`RemoteConfigReadGet`(`POST /rum/application/remote-config/get`,读取当前生效的配置与 `version`)、`RemoteConfigReadHistoryList`(`/rum/application/remote-config/history/list`,分页列出已发布的历史版本)、`RemoteConfigReadPreview`(`/rum/application/remote-config/preview`,用模拟客户端的 `env` / `app_version` / `sdk` 上下文评估一份草稿配置,返回命中的 `hit_rule_index` 与最终下发的 `values`,不发布)、`RemoteConfigWriteUpdate`(`/rum/application/remote-config/update`,发布一份完整的新配置)、`RemoteConfigWriteHistoryRevert`(`/rum/application/remote-config/history/revert`,把历史版本的内容重新发布为新版本)。主要类型:`RemoteConfig`(`enabled`、`activation`、`default`、`rules`、`custom`、`refresh_on_foreground`)、`RemoteConfigValues`(四个可下调的 SDK 开关)、`RemoteConfigRule`(`match` 条件加 `set` 取值)、`RemoteConfigHistoryItem`(含 `version`、`reason`、`content_hash`、`updated_by`),以及 `GetRemoteConfigRequest` / `ListRemoteConfigHistoryRequest` / `PreviewRemoteConfigRequest` / `UpdateRemoteConfigRequest` / `RevertRemoteConfigRequest` 与各自的 `*Response`。应用本身的 `WriteCreate` / `WriteUpdate` / `ReadInfo` / `ReadInfos` / `ReadList` 还带 `repositories` 字段(`[]RUMApplicationRepository`,仅存在于请求体,不是顶层标志):把应用关联到构建它的代码仓库,有序,第一项为主仓库,最多 10 项;每项为 `Repo`(`owner/name` 形式的 GitHub 仓库,必填)加可选的 `Subdir`(应用在仓库内所在的目录,相对仓库根目录,`.` 表示仓库根目录,空值保存为 `.`)。创建时传入即建立关联;更新时按 presence 处理——传入即整体替换、传空数组清空关联、省略则保持不变;`ReadInfo` / `ReadInfos` / `ReadList` 会把它回显在 `RUMApplicationItem` 上。关联本身不授予任何访问权限:AI 会话只能读取账户 GitHub App 安装已授权的仓库。该字段目前只在 API/SDK 层提供,控制台的应用管理页不暴露仓库关联设置。
`client.DataSources.ToolsInvoke`(`POST /monit/datasource/tools/invoke`,`monit-datasource-tools-invoke`)在某个已配置数据源上执行一个确定性工具:`tool` 名称由数据源类型前缀修饰(如 `mysql.overview`),`params` 为工具专属 JSON 参数(省略视为 `{}`,显式 `null` 非法);除诊断工具外,入口支持 `.query` 查询工具(`prometheus`、`mysql`、`postgres`、`oracle`、`clickhouse`、`elasticsearch`、`loki`、`victorialogs`、`sls`、`tencent_cls`),`/monit/query/data` 入口保持不变。该接口要求集群中所有在线可路由的 Edge 会话都支持 v0.71.0 基础调用协议(单个工具可能要求更新的实现),无工具目录、无自动重放、也不会回退到旧版 diagnose。请求体上限 128 KiB,完整成功响应上限 10 MiB,单工具超时至多 25 秒;响应为 `DatasourceToolResult`(`data` 为工具专属 JSON、永不为 null,`summary` 可选,出现 `truncated` 时其 `reason` 说明截断原因)。
@@ -253,6 +253,16 @@ zero := inc.StartTime.IsZero() // 是否为未设置哨兵
**请求与响应共用的类型同样如此**:`AlertRuleV2`(`AlertRules.ReadInfoV2` / `WriteCreateV2` / `WriteUpdateV2` 的返回类型,对应 `POST /monit/rule/v2/info`、`/v2/create`、`/v2/update`)的 `created_at` / `updated_at` 在 SDK 中仍是裸 `int64`(Unix 秒,带 `omitempty`),而不是 `Timestamp`。这类字段不支持 `Unix()` / `String()` / `IsZero()`,需要自行把秒值转成时间;按 `Timestamp` 示例写 `rule.UpdatedAt.Unix()` 会编译不过。
+
+同样属于 `AlertRuleV2`(`AlertRules.ReadInfoV2` / `WriteCreateV2` / `WriteUpdateV2` 的返回类型)的 `investigation_targets` 也换了形状,仍在发送旧字段的调用方需要一并改:
+
+- 字段本身是 `[]InvestigationTarget`,最多 20 项且不允许重复;更新接口按 presence 处理——省略保留原配置,传 `[]` 清空。
+- 每个入口是**封闭的 tagged union**,`Kind` 决定必须提供哪个子对象:`dashboard`(打开仪表盘面板)必须给 `Dashboard`,`query`(打开 Explore 查询)必须给 `Query`,提供另一个会被拒绝,入口内出现未知字段同样会被拒绝。
+- `TimeRange`(`InvestigationTimeRange`:`BeforeSeconds` 与 `AfterSeconds`)在**每个**已保存的入口上都必填——两个方向都非负,且至少一个大于 0。
+- `Dashboard` 入口(`DashboardInvestigationTarget`)的变量由原来的 `VariableBindings`(键为仪表盘变量名,值为 `{source: event_label, key: 标签名}`)换成 `Variables`:`map[string]string`,键为变量名、值为字面取值,可用 `{{ }}` 模板引用事件标签(旧类型 `InvestigationVariableBinding` 已不存在)。`DashboardID` 与 `Variables` 必填,`TargetID` 可选;`DashboardID` 与 `TargetID` 都必须是规范的 UUIDv7。
+- `Query` 入口用 `QueryInvestigationTarget`:`DatasourceID`(数据源 ID,必填)与 `Query`(必填)都不可省,`Query` 为 `DashboardQuery`——`Mode` 取 `instant` / `range` / `window`,`Expr` 是目标数据源原生语法的表达式且可用 `{{ }}` 模板,`Args` 是原样透传的命名参数、不允许出现模板,`MinStepSeconds` 仅在 `Mode` 为 `range` 时接受。
+
+
## 分页
---
diff --git a/zh/monitors/data-sources/data-sources.mdx b/zh/monitors/data-sources/data-sources.mdx
index a8171436..c333d473 100644
--- a/zh/monitors/data-sources/data-sources.mdx
+++ b/zh/monitors/data-sources/data-sources.mdx
@@ -36,7 +36,7 @@ Monitors 支持以下 15 种数据源类型:
数据源列表只展示你有权查询的数据源,包括以下信息:
-- **名称**:数据源的标识名称
+- **名称**:数据源的标识名称;配置了团队授权的行,名称后面还会跟「负责团队」「可查询团队」标签(见[名称列上的授权标签](#名称列上的授权标签))
- **类型**:数据源类型及图标
- **连接地址**:数据源的访问地址
- **关联告警引擎**:绑定的告警引擎集群名称,附带引擎在线状态指示
@@ -45,7 +45,17 @@ Monitors 支持以下 15 种数据源类型:
列表按查询权限过滤:对某个数据源没有查询(readonly)权限时,这一行整条不会返回,既看不到也搜不到,因此列表条数可能少于账号内实际的数据源数量。账号主体、Admin 角色、数据源创建者、管理团队与只读团队的成员始终可见。授权模型见下方「数据源权限(团队授权)」一节。
-你可以通过搜索框按名称或类型过滤数据源。列表每 5 秒自动刷新,实时反映引擎连接状态。
+你可以通过搜索框按名称、类型、连接地址或备注过滤数据源。列表每 5 秒自动刷新,实时反映引擎连接状态。
+
+### 名称列上的授权标签
+
+名称列直接标出这条数据源的授权角色,扫一眼就能分辨哪些数据源是「有主」的:
+
+- 配置了 `manage_team_id` 的行,名称后跟一枚绿色**负责团队**标签,标签上是团队名。
+- `readonly_team_ids` 里的每个团队各一枚灰色**可查询团队**标签,标签上同样是团队名。
+- 悬浮任意一枚标签都会标明它的角色(负责团队 / 可查询团队)。
+- 两个字段都没配置的行不打任何标签——全租户可查可管是常态,不需要标出来。
+- 团队名是异步查来的:负责团队的名字还没到时会先显示占位骨架,避免闪一下错误的「已删除团队」;查完仍取不到名字时显示**已删除团队**,此时该条授权自然失效(团队被删除后,创建者或管理员可以接手修正,语义见上方「校验与归一化」)。
### 启用数据源
@@ -89,14 +99,26 @@ Monitors 支持以下 15 种数据源类型:
### 授权之后会发生什么
- **列表**:只返回你有权查询的数据源。没有 readonly 权限的数据源整条不出现在响应里;每行附带 `my_perm` 字段,取值为 `manage` 或 `readonly`,授权字段(`manage_team_id`、`readonly_team_ids`、`creator_id`)也随行返回。前端只消费这些判断,不自己计算。
-- **管理类操作**:查看连接凭据的数据源详情、编辑、删除、启停都要求 `manage`,否则返回无权限错误(`only the managing team can manage this datasource`)。
+- **管理类操作**:返回连接凭据的数据源详情、编辑、删除、启停都要求 `manage`,否则返回无权限错误(`only the managing team can manage this datasource`)。对没有 `manage` 权限的行,控制台另外提供一个只读视图(见「编辑和删除」一节):它按列表行渲染、连接凭据一律打码,不改变服务端的这道门。
- **查询与引用**:即时查询、数据探索、仪表盘面板取数、数据源工具调用,以及在告警规则中引用该数据源,都要求 `readonly`;无权时返回明确的拒绝,不暴露数据源细节。
- **告警规则保存**:保存规则时要求作者对规则引用到的每个数据源都有 `readonly`,包括名称通配在保存当下匹配到的数据源;不满足则拒绝保存并指明无权引用的数据源名称。唯一的豁免是纯停用(字段更新只含 `enabled` 且置为 `false`)。规则保存后由系统身份执行,成员权限之后的变化不影响已保存规则运行。
- **审计**:授权字段的变更会写入一条 `datasource_auth_update` 审计记录。
### 如何配置
-授权字段随数据源的创建与更新请求一起提交(`POST /monit/datasource/create`、`POST /monit/datasource/update`):创建时可以一并设置;修改已有数据源的授权字段是管理操作,需要具备该数据源的 `manage`。更新请求省略某个授权字段表示保持原值,显式传 `null` 表示清空。控制台的数据源表单目前没有这两个字段的输入项,需要通过 API 设置。
+新建与编辑抽屉里各有一个团队输入项,保存后生效,留空即不限制:
+
+| 表单字段 | 字段名 | 选择方式 | 作用 |
+|----------|--------|----------|------|
+| **负责团队** | `manage_team_id` | 单选 | 负责团队的成员可以编辑、删除、启停该数据源。不选则所有人可管理;创建者和管理员始终可管理 |
+| **可查询团队** | `readonly_team_ids` | 多选 | 限制哪些团队能查询和引用该数据源。不选则所有人可查询;负责团队始终可查询 |
+
+两个字段的取值随数据源的创建与更新请求一起提交(`POST /monit/datasource/create`、`POST /monit/datasource/update`):创建时可以一并设置;修改已有数据源的授权字段是管理操作,需要具备该数据源的 `manage`。提交前会做归一化:清空 `manage_team_id` 落成显式 `null`,`readonly_team_ids` 去重后为空同样落成 `null`。更新请求里缺键表示保持原值、`null` 表示清空,所以**清空选择就是取消对应授权**。不用控制台时,也可以直接通过 API 提交这两个字段。
+
+两条控制台侧的约束:
+
+- **可查询团队非空时「负责团队」必填**:只选了可查询团队而没选负责团队,表单在提交前就会在**负责团队**下方报「配置了可查询团队后,必须指定负责团队」,不必等后端 400。
+- **负责团队只能选自己所在的团队**:除主体账号与 Admin 角色外,**负责团队**的候选项只有当前用户所属的团队——把负责团队指给自己不在的团队,保存后就会当场失去这条数据源的管理权(创建者有兜底,普通管理成员没有)。已经选中、但自己不属于的团队仍会正常显示团队名。
授权粒度只到团队,不支持按成员单独授权;需要给个别成员授权时,可以为其建立一个小团队。
@@ -431,16 +453,23 @@ MongoDB 不支持客户端证书认证(mTLS)。
- 需要账户已开通 AI SRE,否则这一行只有编辑与删除,没有任何主操作。
- 需要 AI SRE 对话访问权限(`AiSreChatVisit`):没有该权限时按钮可见但为禁用状态。
-### 名称列的反向入口
-
-名称列上还有一个**在 AI SRE 里问问 →** 链接,用于从这条数据源直接进入 AI SRE 对话,并预填一句带上数据源名称与类型的提问(只预填,不自动发送)。它只在数据源处于启用状态、账户已开通 AI SRE、且当前用户既有 AI SRE 访问权限又有对话权限(`AiSreChatVisit`)时出现——停用的数据源 AI SRE 查不到,因此不给入口。
-
## 编辑和删除
- **编辑**:在数据源列表中点击**编辑**按钮,修改数据源配置后保存。
+- **查看**:对只有 readonly 权限的行,同一个编辑入口的悬浮提示变成**查看**,点开的是只读抽屉(标题**查看数据源**)。
- **删除**:在数据源列表中点击**删除**按钮,确认后删除数据源。
-两者都需要先通过数据源的 `manage` 校验:你必须是该数据源的创建者、主体账号或 Admin 角色,或者属于它的管理团队;否则请求会被拒绝(`only the managing team can manage this datasource`)。编辑抽屉加载的是数据源详情,详情会返回连接凭据,因此「查看连接配置」与编辑、删除、启停走同一道门。判定规则见「数据源权限(团队授权)」一节。
+编辑、删除、启停都需要先通过数据源的 `manage` 校验:你必须是该数据源的创建者、主体账号或 Admin 角色,或者属于它的管理团队;否则请求会被拒绝(`only the managing team can manage this datasource`)。编辑抽屉加载的是数据源详情,详情会返回连接凭据,因此「查看连接凭据」与编辑、删除、启停走同一道门。判定规则见「数据源权限(团队授权)」一节。
+
+编辑入口本身不做行级门控,但仍要求账号具备数据源编辑权限(`MonitDatasourceUpdate`):没有该权限时这个入口也是禁用的。
+
+### 只读视图能看到什么
+
+对某条数据源只有 readonly 权限时,控制台不会让入口消失,而是把它降级成只读:
+
+- 抽屉标题是**查看数据源**,整张表单不可编辑,数据源类型选择器与腾讯云 CLS 的连接测试一并禁用。
+- 基础字段取自列表行;连接配置按原表单样式展示,但字符串类字段一律显示 `******`(开关、数字、枚举类字段留空)。只读视图刻意不调用数据源详情接口(该接口按 `manage` 卡),真实凭据不会下发到浏览器。
+- **确定**按钮为禁用状态,提交路径也做了同样的硬闸;**删除**按钮与**启用数据源**开关改为禁用控件,悬浮提示会列出谁能操作(组织管理员、创建者、负责团队)。
删除数据源前,请确保没有告警规则引用该数据源,否则相关告警规则将无法正常执行。
diff --git a/zh/monitors/explore/explore.mdx b/zh/monitors/explore/explore.mdx
index 6502c61d..eca708c3 100644
--- a/zh/monitors/explore/explore.mdx
+++ b/zh/monitors/explore/explore.mdx
@@ -90,6 +90,16 @@ keywords: ["查询工作台", "Explore", "即时查询", "Prometheus", "Loki", "
工作台会将当前数据源、查询表达式、时间范围和查询类型编码到 URL 参数中。复制浏览器地址栏即可分享当前查询状态,打开链接后会自动恢复。若链接中的数据源不存在、已停用或无权限,页面会提示且不自动执行。
+## 数据源权限与拒绝提示
+
+工作台的数据源选项只包含你有查询(readonly)权限的数据源,无权查询的数据源不会出现在候选里,也查不到它的内容(授权模型见[数据源管理](/zh/monitors/data-sources/data-sources)的「数据源权限(团队授权)」一节)。
+
+如果一条查询在服务端被数据源级授权拦下(HTTP 403、错误码 `AccessDenied`,例如页面打开后权限被改动),工作台不会落进通用的查询失败提示,而是直接给出:
+
+> 无权访问该数据源,请联系数据源的负责团队或管理员。
+
+结果区下方同时展示后端返回的原始信息与本次请求的 RequestId,便于排查。要恢复访问,请联系数据源的负责团队或管理员,在数据源管理页调整该数据源的「负责团队」与「可查询团队」。
+
## Edge 版本要求
查询工作台依赖 monit-edge 执行实际查询。若数据源所在集群的 Edge 版本过低或版本不一致(混合版本),页面会显示升级提示并禁用查询。需要将集群内所有 Edge 升级至要求版本后才能使用。
diff --git a/zh/on-call/analytics/insights.mdx b/zh/on-call/analytics/insights.mdx
index e330cedf..505f772c 100644
--- a/zh/on-call/analytics/insights.mdx
+++ b/zh/on-call/analytics/insights.mdx
@@ -6,6 +6,10 @@ keywords: ["分析看板", "数据分析", "故障统计", "报表导出", "运
**版本要求**:此功能需要 On-call 标准版及以上订阅。[了解更多](https://flashcat.cloud/flashduty/price/)
+
+**订阅过期后看板可能是空的**:On-call 订阅过期或被停用后告警停止接入,看板此后不再产生新的统计数据,默认时间窗内自然是空的——这不是数据丢失。页面顶部会常驻一条不可关闭的提示条「订阅已过期,告警已停止接入,此后不再产生新的统计数据。历史数据不受影响,仍可查看与导出。」,历史数据仍可查看与导出,续费后自动恢复。账户级说明见[订阅过期后会发生什么](/zh/platform/pricing#订阅过期后会发生什么)。
+
+
## 关键特性
diff --git a/zh/on-call/comparison/vs-pagerduty.mdx b/zh/on-call/comparison/vs-pagerduty.mdx
index b70c3571..5faa34a1 100644
--- a/zh/on-call/comparison/vs-pagerduty.mdx
+++ b/zh/on-call/comparison/vs-pagerduty.mdx
@@ -230,7 +230,7 @@ PagerDuty AIOps 在超大规模事件关联场景上打磨多年,能力成熟
| 工具 | Flashduty | PagerDuty |
| --- | --- | --- |
| **[Open API](/zh/openapi/api-catalog)** | 330+ 个接口,覆盖 On-call、Monitors、RUM、AI SRE 与平台管理,双语文档 | ✅ REST API 完善,文档成熟 |
-| **[CLI](/zh/developer/cli)** | 336 个 API 操作命令 + 内置 Agent Skills,可直接配给 Claude Code、Cursor、Codex 等 AI 编程工具 | 无官方力推的完整 CLI:社区最常用的 `pagerduty-cli` 为员工个人项目(官方声明不背书、作者已宣布归档),官方 go-pagerduty 库附带功能有限的 `pd` 命令行小工具 |
+| **[CLI](/zh/developer/cli)** | 334 个生成的 API 操作命令(另有 4 个手工命令)+ 内置 Agent Skills,可直接配给 Claude Code、Cursor、Codex 等 AI 编程工具 | 无官方力推的完整 CLI:社区最常用的 `pagerduty-cli` 为员工个人项目(官方声明不背书、作者已宣布归档),官方 go-pagerduty 库附带功能有限的 `pd` 命令行小工具 |
| **SDK** | [Go SDK](/zh/developer/go-sdk):go-github 风格封装,覆盖 330+ 个 API 操作、39 个服务 | 官方维护 go-pagerduty、python-pagerduty 等客户端库 |
| **[Terraform Provider](/zh/developer/terraform)** | 12 类资源 + 13 类数据源,IaC 管理协作空间、分派策略、值班表等 | ✅ 官方 Terraform Provider,生态成熟 |
| **[MCP Server](/zh/developer/mcp-server)** | 8 个工具集 23 个工具,支持远程、Docker、源码三种部署 | ✅ 官方 MCP Server |
diff --git a/zh/on-call/integration/sso/keycloak.mdx b/zh/on-call/integration/sso/keycloak.mdx
index fdd741a6..d95a999f 100644
--- a/zh/on-call/integration/sso/keycloak.mdx
+++ b/zh/on-call/integration/sso/keycloak.mdx
@@ -94,7 +94,7 @@ Keycloak 是一个开源的身份和访问管理解决方案,提供了一套

-**登录测试**:访问 `console.flashcat.cloud`,选择 SSO 登录,在域名处填写组织的登录域名(在 **平台管理 → 组织 → 组织信息 → 组织资料** 页面配置);也可以直接访问 `{域名}.sso.flashcat.cloud` 发起单点登录。
+**登录测试**:访问 `console.flashcat.cloud`,选择 SSO 登录。SSO 登录页默认使用 **企业邮箱** 方式,填写您的企业邮箱即可定位到所属组织并进入该组织的认证流程;如果只知道企业域名,点击 **使用企业域名登录** 切换到域名输入方式,再填写组织的登录域名(在 **平台管理 → 组织 → 组织信息 → 组织资料** 页面配置)。也可以直接访问 `{域名}.sso.flashcat.cloud` 发起单点登录。

diff --git a/zh/on-call/integration/webhooks/alert-webhook.mdx b/zh/on-call/integration/webhooks/alert-webhook.mdx
index 705602d3..51086a9a 100644
--- a/zh/on-call/integration/webhooks/alert-webhook.mdx
+++ b/zh/on-call/integration/webhooks/alert-webhook.mdx
@@ -209,6 +209,10 @@ curl -X POST 'https://example.com/alert/webhook?a=a' \
关闭 TLS 证书验证可能存在中间人攻击风险,建议仅在测试环境或使用自签名证书时关闭。
+
+**自定义请求体** 与 **值映射** 仅 **故障 Webhook** 支持,**告警 Webhook** 的推送内容固定为上述 [Payload 结构](#请求-payload),不能重新组织。详见 [故障 Webhook](/zh/on-call/integration/webhooks/incident-webhook) 的 **自定义请求体** 与 **值映射** 章节。
+
+
## 四、调用历史
告警 Webhook 提供完整的调用历史记录,方便你排查推送是否成功以及调试回调接口。
diff --git a/zh/on-call/integration/webhooks/custom-actions.mdx b/zh/on-call/integration/webhooks/custom-actions.mdx
index 677261f3..8f9c03d9 100644
--- a/zh/on-call/integration/webhooks/custom-actions.mdx
+++ b/zh/on-call/integration/webhooks/custom-actions.mdx
@@ -68,6 +68,7 @@ layer_idx | int | 是 | 当前分派层级,从 0 开始
type | string | 是 | 分派方式,枚举值:assign(分派)、reassign(改派)、escalate(层级升级)、reopen(重新打开后分派)
id | string | 是 | 分派记录 ID
assigned_at | int64 | 是 | 分派时间,Unix 秒时间戳
+effective_person_ids | []int64 | 否 | 本次分派最终生效的人员 ID 列表,为直接分派的人员与按分派策略解析出的人员的并集,已去重并按 ID 升序排列
**LinkItem**:
diff --git a/zh/on-call/integration/webhooks/incident-webhook.mdx b/zh/on-call/integration/webhooks/incident-webhook.mdx
index 50bf968f..3fcbb779 100644
--- a/zh/on-call/integration/webhooks/incident-webhook.mdx
+++ b/zh/on-call/integration/webhooks/incident-webhook.mdx
@@ -95,6 +95,7 @@ layer_idx | int | 是 | 当前分派层级,从 0 开始
type | string | 是 | 分派方式,枚举值:assign(分派)、reassign(改派)、escalate(层级升级)、reopen(重新打开后分派)
id | string | 是 | 分派记录 ID
assigned_at | int64 | 是 | 分派时间,Unix 秒时间戳
+effective_person_ids | []int64 | 否 | 本次分派最终生效的人员 ID 列表,为直接分派的人员与按分派策略解析出的人员的并集,已去重并按 ID 升序排列
**LinkItem**:
diff --git a/zh/on-call/integration/webhooks/jira-sync.mdx b/zh/on-call/integration/webhooks/jira-sync.mdx
index fd9d5422..0f7f96af 100644
--- a/zh/on-call/integration/webhooks/jira-sync.mdx
+++ b/zh/on-call/integration/webhooks/jira-sync.mdx
@@ -50,7 +50,12 @@ keywords: ["Jira", "工单同步", "Webhook", "Issue跟踪", "故障关联"]
- **事务类型**:选择需要同步至 Jira 的事务类型。
- **协作空间**:选择该集成生效的协作空间,只有该协作空间内的故障才可以同步至 Jira 中。
- **严重程度映射**:如果选择的事务类型不支持优先级字段,则无法配置该映射关系。每行的 **Jira 优先级**(结果字段)为必填项,缺省时无法保存集成配置。
-- **自定义字段映射**:可以选择将故障的某些标签或所有标签以及自定义字段内容同步至 Jira 的字段中(仅支持文本类型的字段)。每行的 **Key**(故障字段)与 **结果字段**(Jira 字段)均为必填项,缺省时无法保存集成配置。
+- **状态映射**:将 Flashduty 的 **待处理**、**处理中**、**已关闭** 三个处理进度分别映射到 Jira 中的状态,可选值为当前 **项目 ID** + **事务类型** 下 Jira 提供的全部状态。
+ - 三项全部留空时沿用[默认映射](#状态映射);只要配置了其中任意一项,另外两项也变为必填,缺省时无法保存集成配置(报错为 **Jira 状态不能为空**)。
+ - 更换 **事务类型** 会清空已配置的状态映射;更换 **项目 ID** 会清空已选的 **事务类型**,需要重新选择事务类型,并复核状态映射是否仍然适用。
+ - 未配置时,集成详情页的 **状态映射** 一栏显示 **使用默认映射**。
+- **自定义字段映射**:可以选择将故障的某些标签或所有标签、自定义字段内容,以及故障自身的属性同步至 Jira 的字段中(仅支持文本类型的字段)。每行的 **Key**(故障字段)与 **结果字段**(Jira 字段)均为必填项,缺省时无法保存集成配置。
+ - **Key** 下拉中除 **所有标签** 外,其余可选项分为三组:**故障属性**(**故障 ID**、**故障链接**)、**标签**(具体的标签名)、**自定义字段**。选择 **故障 ID** 时写入该故障的故障 ID;选择 **故障链接** 时写入该故障在 Flashduty 中的详情页地址。
### 3. 关于更新
@@ -74,11 +79,17 @@ keywords: ["Jira", "工单同步", "Webhook", "Issue跟踪", "故障关联"]
#### 状态映射
+集成配置中的 **状态映射** 三项全部留空时,系统按下表的默认映射同步故障处理进度:
+
| Jira | Flashduty |
| ---------- | -------- |
-| Todo | 待处理 |
-| In Progress | 处理中 |
-| Done | 已解决 |
+| 待办(To Do) | 待处理 |
+| 处理中(In Progress) | 处理中 |
+| 完成(Done) | 已关闭 |
+
+默认映射按名称匹配,目标名称在中文 Jira 中为 `待办` / `处理中` / `完成`,在英文 Jira 中为 `To Do` / `In Progress` / `Done`:若事务当前状态名称已与目标名称一致,则无需流转;否则系统会查找名称等于目标名称的流转(transition)。
+
+如果您的 Jira 工作流使用其他状态名或流转名,默认映射可能找不到对应的流转,请在集成配置的 **状态映射** 中为每个处理进度直接指定一个 Jira 状态。配置后,状态同步完全以该映射为准;若映射中的某个 Jira 状态已被删除,集成详情页会显示 **未找到状态(ID:xxx)**。
### 5. 注意事项
diff --git a/zh/on-call/quickstart/mobile-app.mdx b/zh/on-call/quickstart/mobile-app.mdx
index d2ec040e..d4621b54 100644
--- a/zh/on-call/quickstart/mobile-app.mdx
+++ b/zh/on-call/quickstart/mobile-app.mdx
@@ -56,7 +56,8 @@ App 首页即故障列表,分为 **分派给我**(带故障数量角标)
| --- | --- |
| **处理进度** | 全部 / 未关闭 / 待处理 / 处理中 / 已关闭(默认仅显示未关闭) |
| **严重程度** | Critical / Warning / Info,可多选 |
-| **故障标题 / 故障 ID** | 输入关键字模糊搜索 |
+| **故障标题** | 输入关键字模糊搜索 |
+| **故障 ID** | 输入完整故障 ID 或 6 位故障短编号(如 `56E25B`)精确查询;输入其他关键字时按 **故障标题** 匹配。与控制台故障列表的 **键入检索** 使用同一套查询语义 |
| **与我相关** | 仅看与您相关的故障 |
| **人员** | 按发起人员、处理人员、认领人员、关闭人员筛选 |
| **协作空间** | 按故障所属协作空间筛选 |
diff --git a/zh/on-call/statuspage/statuspage.mdx b/zh/on-call/statuspage/statuspage.mdx
index c4c261af..86e24864 100644
--- a/zh/on-call/statuspage/statuspage.mdx
+++ b/zh/on-call/statuspage/statuspage.mdx
@@ -6,6 +6,15 @@ keywords: ["状态页", "服务状态", "可用性", "故障通报", "外部沟
**版本要求**:所有版本均可创建公开状态页(免费版和标准版最多 1 个,专业版最多 5 个)。内部状态页仅在专业版中提供(最多 20 个)。[了解更多](https://flashcat.cloud/flashduty/price/)
+
+**创建状态页的两条准入规则**:列表页入口与创建页本身都会校验,直接访问创建页也绕不过去。
+
+- **订阅过期或被停用时不能新建**:列表页的创建按钮被禁用;直接打开创建页会返回一个空状态页,标题为「订阅已过期,暂时无法新建」,正文为「续费后即可继续创建状态页。」与「已创建的状态页仍在正常对外展示,可随时编辑。」,主按钮变为 **立即续费**(另有 **返回列表**)。已创建的状态页仍正常对外展示,发布事件、编辑与订阅通知都不受影响,续费后即可继续创建。
+- **内部状态页要求专业版及以上**:创建页可被直接访问(`/status-page/create?type=internal`),所以除列表页入口上的版本门槛外,创建页内会再校验一次版本;版本不足时同样返回空状态页,标题为「暂无权限」,正文为「内部状态页仅向专业版及以上版本用户开放。」与「已创建的状态页仍在正常对外展示,可随时编辑。」,主按钮为 **去升级**(另有 **返回列表**)。
+
+订阅过期对其他功能的影响见[订阅过期后会发生什么](/zh/platform/pricing#订阅过期后会发生什么)。
+
+
服务中断不可避免,但沟通效率可以系统性提升。
当服务状态发生波动时,依赖人工邮件或即时消息的通知方式,已难以满足企业对信息时效性和一致性的要求。Flashduty 状态页通过提供标准化、统一的信息发布窗口,确保企业内部与外部对服务状态的认知保持实时同步。
diff --git a/zh/openapi/api-catalog.mdx b/zh/openapi/api-catalog.mdx
index 2b8550a7..8b741c67 100644
--- a/zh/openapi/api-catalog.mdx
+++ b/zh/openapi/api-catalog.mdx
@@ -291,7 +291,7 @@ Flashduty Open API 提供 **338** 个接口,覆盖 On-call、Monitors、RUM、
| POST | [`/monit/rule/counter/channel`](/zh/api-reference/monitors/alert-rules/monit-rule-read-counter-channel) | 按协作空间查询规则统计 |
-告警规则的导入与导出仅在控制台提供(YAML 规则包流程,见[告警规则入门指南](/zh/monitors/quickstart/quickstart#导入告警规则)):Monitors 服务端已下线这两个接口,API 参考中的对应条目将在下次生成时一并移除。
+告警规则的导入与导出仅在控制台提供(YAML 规则包流程,见[告警规则入门指南](/zh/monitors/quickstart/quickstart#导入告警规则)):Monitors 服务端已下线 `POST /monit/rule/export` 与 `POST /monit/rule/import`,API 参考中也已不再收录这两个接口。
### 告警数据源
@@ -513,10 +513,18 @@ Flashduty Open API 提供 **338** 个接口,覆盖 On-call、Monitors、RUM、
| POST | [`/safari/artifact/gallery/publish-from-file`](/zh/api-reference/ai-sre/artifacts/artifact-write-publish) | 发布文件为产物 |
| POST | [`/safari/artifact/gallery/update`](/zh/api-reference/ai-sre/artifacts/artifact-write-update) | 更新产物 |
| POST | [`/safari/artifact/gallery/delete`](/zh/api-reference/ai-sre/artifacts/artifact-write-delete) | 从产物库移除产物 |
+| POST | `/safari/artifact/gallery/pin` | 把产物置顶到调用者自己的画廊 |
+| POST | `/safari/artifact/gallery/unpin` | 取消调用者自己的置顶 |
| POST | [`/safari/artifact/gallery/share/enable`](/zh/api-reference/ai-sre/artifacts/artifact-write-share-enable) | 开启公开分享 |
| POST | [`/safari/artifact/gallery/share/sync`](/zh/api-reference/ai-sre/artifacts/artifact-write-share-sync) | 更新公开分享快照 |
| POST | [`/safari/artifact/gallery/share/revoke`](/zh/api-reference/ai-sre/artifacts/artifact-write-share-revoke) | 撤销公开分享 |
+
+`/safari/artifact/gallery/pin` 与 `/safari/artifact/gallery/unpin` 是新增接口,API 参考尚未生成对应页面(本表其余接口都可点入),因此上表这两行只列路径。
+
+两者都以 `artifact_id`(必填)为入参,成功时返回空响应,都不会改动产物本身。置顶是**按调用者个人**维护的偏好,不是对产物的编辑:产物仍对全账户可见,只有调用者自己看到的画廊顺序不同——产物列表把调用者置顶的产物排到前面,并在这些行上返回 `pinned_at`(Unix 毫秒,未置顶时不返回该字段)。任何账户成员都可以置顶自己看得到的产物;置顶要求产物存在(不存在返回 404),取消置顶不做存在性检查。两个接口都幂等:重复置顶会刷新置顶时间,取消一个从未置顶的产物也会成功。
+
+
diff --git a/zh/platform/permission-design.mdx b/zh/platform/permission-design.mdx
index f093836d..9464a86a 100644
--- a/zh/platform/permission-design.mdx
+++ b/zh/platform/permission-design.mdx
@@ -238,9 +238,19 @@ Flashduty **基于团队来控制数据权限**,并应用于以下场景:
| **模板管理** | 创建者、主体账号和负责团队成员,可以修改模板的基本信息、各通道模板配置等 |
| **服务日历** | 创建者、主体账号和负责团队成员,可以修改日历的基本信息、节假日设定等 |
| **集成管理** | 创建者、主体账号和负责团队成员,可以管理集成的配置 |
+| **协作空间专属集成** | 主体账号、Admin 角色成员、协作空间创建者和任一管理团队的成员,可以管理该协作空间下的专属集成;协作空间未配置管理团队时,回退为按协作空间所属团队判定 |
| **映射规则** | 创建者、主体账号和负责团队成员,可以管理映射规则的配置 |
| **数据源** | 创建者、主体账号和管理团队成员可以编辑、删除、启停监控告警的数据源、查看连接凭据并修改授权配置;管理团队与只读团队的成员可以查询和引用该数据源;两者都未配置时全租户可读写(存量数据源行为不变) |
+
+**协作空间专属集成与共享集成**:在协作空间下创建的集成(`ref_id` 形如 `c_`)是**专属集成**,其余是**共享集成**,两者的权限口径不同。
+
+- **功能权限**:专属集成的启停、删除与保存配置,除「集成管理」外,「协作空间管理」也可以操作——两者有其一即可。这是因为「协作空间管理」本身就包含其空间内集成的创建、编辑、删除、启停与轮换 Key。
+- **数据权限**:专属集成按协作空间的**管理团队**判定;协作空间未配置管理团队时按协作空间所属团队判定。共享集成按集成自身的创建者与负责团队判定。
+- **权限不足时的提示不同**:专属集成为「您没有操作权限。仅账户管理员、协作空间创建者或管理团队允许操作。」,共享集成为「您没有操作权限。仅账户管理员、集成创建者或管理团队允许操作。」。
+- 只有 `ref_id` 形如 `c_` 的集成才算专属集成:从集成详情带着 `channel_id` 查询参数打开的共享集成,仍按共享集成判定。
+
+
当您没有对应资源的数据权限时,系统会显示以下提示:
diff --git a/zh/platform/pricing.mdx b/zh/platform/pricing.mdx
index c1b076bc..f58d997f 100644
--- a/zh/platform/pricing.mdx
+++ b/zh/platform/pricing.mdx
@@ -306,6 +306,66 @@ RUM 采用 **按量付费**模式,根据实际使用的会话数量计费。
---
+## 订阅过期后会发生什么
+
+On-call 订阅**已过期或被停用**后,平台会做两件事:**停止接收新的告警事件**,并**冻结所有新建入口**。已有配置和历史数据不会被删除,续费后自动恢复。
+
+### 什么算订阅过期
+
+以下任一条件成立,全站即按「已过期」处理:
+
+| 条件 | 说明 |
+| --- | --- |
+| **订阅被停用** | 订阅资源的状态不是 `enabled` |
+| **订阅已到期** | 当前时间已达到订阅的到期时间(`expired_at`) |
+
+只看到期时间会漏掉「资源已停用但到期时间未到」这类后端会拒绝、前端却放行的情况,因此两者任一成立即算过期,私有化部署不做订阅管控、永远不会被判为过期。
+
+### 到期提醒与续费入口
+
+- **左侧导航的订阅入口**在到期前不足 **10 天**时变黄并显示「X 后到期」(X 为剩余时长),悬停提示「订阅将在 X 后到期,到期后告警将停止接入」;过期后转为红色并显示「订阅已到期 X」(X 为已过期时长),悬停提示「订阅已到期 X,告警已停止接入,点击续费」。点击该入口直接进入 **订阅** 页面(`/wallet/plan`)。
+- **主体账号**可以在页面提示条上点击 **立即续费**;**成员账号**没有续费权限,看到的是「请联系账户主体续费。」。
+
+### 告警接入与列表页提示
+
+订阅过期后告警停止接入,告警列表、故障列表、集成中心与分析看板顶部会各常驻一条**不可关闭**的提示条,说清该页受到的具体影响:
+
+| 页面 | 提示条文案 |
+| --- | --- |
+| 告警列表 | 「订阅已过期,新的告警事件已停止接入。历史告警仍可查看与处理,续费后自动恢复接入。」 |
+| 故障列表 | 「订阅已过期,新的告警事件已停止接入,不会再产生新故障。已有故障仍可正常处理。」 |
+| 集成中心 | 「订阅已过期,所有集成已停止接收告警事件。已配置的集成不会被删除,续费后自动恢复接入,无需重新配置。」 |
+| 分析看板 | 「订阅已过期,告警已停止接入,此后不再产生新的统计数据。历史数据不受影响,仍可查看与导出。」 |
+
+集成中心里每个集成卡片同时变为不可点击,悬停提示「订阅已过期,无法新建集成。续费后立即恢复,已有集成不受影响。」;分析看板因为不再产生新数据,默认时间窗内自然是空的,这不是数据丢失,详见[分析看板](/zh/on-call/analytics/insights)。
+
+### 被冻结的新建入口
+
+订阅过期后以下新建入口被禁用。鼠标悬停时的提示为「订阅已过期,无法新建〈对象〉。已有〈对象〉不受影响。」——其中的「对象」是本列的对象名,例如协作空间入口的提示是「订阅已过期,无法新建协作空间。已有协作空间不受影响。」;提示里同时给出续费路径:主体账号为 **立即续费**,成员账号为「请联系账户主体续费。」。
+
+| 对象 | 被禁用的入口 |
+| --- | --- |
+| 协作空间 | 协作空间列表的 **创建协作空间** |
+| 值班表 | 值班表页面的 **创建值班** |
+| 故障 | 故障列表的 **创建故障** |
+| 作战室 | 故障详情中的 **创建作战室** |
+| 状态页 | 状态页列表的 **创建公开页面** / **创建内部页面**,以及创建页本身 |
+| 成员 | 成员管理的 **邀请成员** |
+| 自定义字段 | 自定义字段页面的 **创建自定义字段** |
+| 通知模板 | 通知模板页面的新建入口 |
+| 服务日历 | 服务日历页面的 **新增日历** |
+| 复盘报告 | 复盘面板的 **创建复盘报告** |
+
+
+过期只冻结**新建**。已有对象的**编辑、启停、删除**等存量操作不受订阅过期影响:已有协作空间的降噪配置、分派策略仍可正常编辑,已创建的状态页仍正常对外展示且可随时编辑。
+
+
+### 续费后如何恢复
+
+续费或重新启用订阅后**不需要重新配置**:告警事件恢复接入、被冻结的新建入口立即恢复可用,已配置的集成、已有故障、历史告警与历史统计数据都保持原样。AI SRE 也会随 On-call 订阅失效而暂停、随续费自动恢复,规则见 [AI SRE 产品概述 · 订阅失效与恢复](/zh/ai-sre/overview#订阅失效与恢复)。
+
+---
+
## 常见问题
---
diff --git a/zh/platform/team-members.mdx b/zh/platform/team-members.mdx
index bbf9e67b..2aaa0ea1 100644
--- a/zh/platform/team-members.mdx
+++ b/zh/platform/team-members.mdx
@@ -137,6 +137,7 @@ keywords: ["团队管理", "成员管理", "邀请成员", "组织架构", "角
- 每天邀请上限为 200 人,单次邀请至多 20 人,重新发送邀请每天至多 5 次
- 未激活账号无法接收告警相关通知
- 组织在[加入限制](/zh/platform/organization-info#加入限制)中配置邮箱域名白名单后,逐个邀请只能填写白名单内的邮箱域名(邀请对话框内会直接标出允许的域名);手机号邀请不受该限制,通过邀请链接加入则按登录邮箱域名校验
+ - 订阅过期或被停用后,**邀请成员** 入口被禁用,悬停提示「订阅已过期,无法新建成员。已有成员不受影响。」——主体账号可点击 **立即续费**,成员账号提示「请联系账户主体续费。」;续费后入口立即恢复,已有成员不受影响。详见[订阅过期后会发生什么](/zh/platform/pricing#订阅过期后会发生什么)
### 变更角色
diff --git a/zh/rum/others/data-collection.mdx b/zh/rum/others/data-collection.mdx
index 287dc461..bbe3dc21 100644
--- a/zh/rum/others/data-collection.mdx
+++ b/zh/rum/others/data-collection.mdx
@@ -53,7 +53,7 @@ RUM Browser SDK 生成具有相关指标和属性的事件。每个 RUM 事件
| 指标 | 类型 | 描述 |
|------|------|------|
- | `session_duration` | 数字 | 会话持续时间(以毫秒为单位) |
+ | `session_duration` | 数字 | 会话持续时间(以毫秒为单位),从会话中最早的视图起算,等于 `client_end_time` - `client_time`;上报时间早于其他视图、但处理更晚的视图(如移动端的启动视图)同样计入 |
| `session_view_count` | 数字 | 会话中的视图数 |
| `session_action_count` | 数字 | 会话中的用户操作数 |
| `session_error_count` | 数字 | 会话中的错误数 |