diff --git a/api-reference/openapi.en.json b/api-reference/openapi.en.json index 2ebeee20..1a90df89 100644 --- a/api-reference/openapi.en.json +++ b/api-reference/openapi.en.json @@ -170,6 +170,10 @@ }, { "name": "AI SRE/Knowledge" + }, + { + "name": "AI SRE/Artifacts", + "description": "AI SRE artifact gallery — publish, browse, and publicly share agent-produced files." } ], "paths": { @@ -30814,6 +30818,961 @@ } } } + }, + "/safari/artifact/gallery/list": { + "post": { + "operationId": "artifact-read-list", + "summary": "List artifacts", + "description": "List published artifacts visible to the caller, with pagination and title search.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- `scope` selects `all` (default — the caller's own personal artifacts plus every team the caller belongs to), `personal` (only the caller's own), or `team` (only team-owned artifacts of the caller's teams).\n- `team_ids` narrows further to specific teams, intersected with the caller's visibility — teams the caller does not belong to return nothing.\n- Default sort is `updated_at` descending; set `orderby` to `created_at` to change the field and `asc: true` to flip direction.\n- For an `app_key` call, visibility is evaluated against the key owner's identity — the list shows that member's personal artifacts and their teams' artifacts.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-list", + "metadata": { + "sidebarTitle": "List artifacts" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactListRequest" + }, + "example": { + "scope": "all", + "page": 1, + "limit": 20 + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactListResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + }, + { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu", + "title": "rum_recommendation", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 3790925372131, + "creator_name": "牛伟利", + "is_mine": false, + "can_edit": true, + "session_id": "sess_QXUC9C2PYWP5EE7vETR3UD", + "session_title": "生成 RUM 文档多格式", + "file_id": "pf_Jnc4E5YBcWLGzunB4ntP9s", + "name": "rum_recommendation.pdf", + "size": 137107, + "content_type": "application/pdf", + "created_at": 1785229432881, + "updated_at": 1785741344822 + } + ], + "total": 17 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/get": { + "post": { + "operationId": "artifact-read-get", + "summary": "Get artifact detail", + "description": "Get a single published artifact by ID.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Visibility is account-wide: any valid `app_key` can read any artifact in the account. `is_mine` and `can_edit` are computed relative to the key owner.\n- When `share_enabled` is true and `file_id` differs from `share_file_id`, the public snapshot is stale — refresh it with `/safari/artifact/gallery/share/sync`.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-get", + "metadata": { + "sidebarTitle": "Get artifact detail" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/file-state": { + "post": { + "operationId": "artifact-read-get-file-state", + "summary": "Get file publish state", + "description": "Check which presented files already have a live published artifact.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- At most 50 `file_ids` per call; duplicates and empty strings are ignored.\n- Files with no live published artifact are simply absent from `items` — match results by the echoed `file_id`.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-get-file-state", + "metadata": { + "sidebarTitle": "Get file publish state" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactFileStateRequest" + }, + "example": { + "file_ids": [ + "pf_SdhEA5fbZJGnHzwrNJMMSB", + "pf_9kLm2nQpRsTuVwXyZaBcDe" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactFileStateResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/sign": { + "post": { + "operationId": "artifact-read-sign", + "summary": "Create signed file URLs", + "description": "Create short-lived signed URLs to download or preview a presented file.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Both URLs expire after `expires_in` seconds (300). Sign again to get fresh URLs.\n- Returned URLs are relative — prepend `https://api.flashcat.cloud` before use, then follow them with `GET /safari/artifact/stream`.\n- The signed token is bound to the calling account and the app_key owner's identity, so a leaked URL does not work for another account or member.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-sign", + "metadata": { + "sidebarTitle": "Create signed file URLs" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactSignRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SignedURLs" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "download_url": "/safari/artifact/stream?mode=download&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "preview_url": "/safari/artifact/stream?mode=preview&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "expires_in": 300, + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/stream": { + "get": { + "operationId": "artifact-read-stream", + "summary": "Download or preview a file", + "description": "Download or preview a file's bytes using a signed token.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **100 requests/minute**; **10 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Success has two forms: a `302` redirect to a short-lived presigned object-storage URL (files stored in S3-compatible storage), or a `200` binary stream (files hosted on a self-hosted runner). Follow redirects.\n- Responses carry `Cache-Control: private, no-store` — they are never cached by the gateway.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-stream", + "metadata": { + "sidebarTitle": "Download or preview a file" + } + }, + "parameters": [ + { + "name": "t", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Signed token issued by `POST /safari/artifact/sign`. Bound to the calling account and person; valid for 5 minutes." + }, + { + "name": "mode", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "download", + "preview" + ], + "default": "download" + }, + "description": "`download` (default) serves the file as an attachment; `preview` serves it inline for browser display. Any other value falls back to `download`." + } + ], + "responses": { + "200": { + "description": "File bytes, proxied, when the file is hosted on a self-hosted runner. Content-Disposition follows `mode`.", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "302": { + "description": "Redirect to a short-lived presigned object-storage URL when the file lives in S3-compatible storage. Follow the `Location` header; no body." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/publish-from-file": { + "post": { + "operationId": "artifact-write-publish", + "summary": "Publish file as artifact", + "description": "Publish a session-produced file to the artifact gallery.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Allowed file types: HTML/Markdown, images, PDF, text/data/code files, and zip/tar archives, matched by extension. Files over 16 MiB are rejected.\n- Publishing is an upsert keyed by the source session and workspace path — republishing the same file replaces the artifact's bytes under a fresh `file_id`, which makes an existing public snapshot stale until synced.\n- The artifact inherits personal/team scope from the source session; move it afterwards with `/safari/artifact/gallery/update` if needed.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-publish", + "metadata": { + "sidebarTitle": "Publish file as artifact" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactPublishFromFileRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "title": "SK 海力士 2026 Q2 财报深度分析" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactPublishResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/update": { + "post": { + "operationId": "artifact-write-update", + "summary": "Update artifact", + "description": "Rename an artifact or transfer it between personal and team scope.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Only the provided fields change — omit `title` or `team_id` to leave them unchanged.\n- `team_id: 0` moves the artifact to personal scope (only the creator can manage it); a positive `team_id` requires the caller (for `app_key` calls, the key owner) to be a member of that team.\n- Returns the full artifact after the update.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-update", + "metadata": { + "sidebarTitle": "Update artifact" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactUpdateRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785829000000, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/delete": { + "post": { + "operationId": "artifact-write-delete", + "summary": "Remove artifact from gallery", + "description": "Detach an artifact from the gallery; the source file stays with its session.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- This is a detach, not a byte delete: the underlying presented file stays with the source session and can be published again.\n- If public sharing was enabled, the public objects are destroyed in the same operation and the link stops resolving.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-delete", + "metadata": { + "sidebarTitle": "Remove artifact from gallery" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/enable": { + "post": { + "operationId": "artifact-write-share-enable", + "summary": "Enable public sharing", + "description": "Turn on anonymous public sharing for an artifact and return its public link.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- The public link is anonymous — anyone with it can view the content, and the link may be forwarded. Content is copied to public CDN objects; the gallery API is not involved when the link is viewed.\n- Idempotent: enabling an already-shared artifact returns the existing link unchanged, and re-enabling after a revoke brings the same link back — the link is keyed by artifact ID.\n- Artifacts over 16 MiB cannot be shared; the call fails with `InvalidParameter`.\n- Sharing is a snapshot: later republishes do not update the public content until you call `/safari/artifact/gallery/share/sync`.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-enable", + "metadata": { + "sidebarTitle": "Enable public sharing" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785747928665 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/sync": { + "post": { + "operationId": "artifact-write-share-sync", + "summary": "Update shared snapshot", + "description": "Refresh the public snapshot of a shared artifact with its latest content.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Fails with `InvalidParameter` when sharing is not enabled — enable it first.\n- The link never changes; only the snapshot bytes and `shared_at` are refreshed.\n- Use `share_file_id != file_id` on the artifact detail to detect a stale snapshot before syncing.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-sync", + "metadata": { + "sidebarTitle": "Update shared snapshot" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785829900000 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/revoke": { + "post": { + "operationId": "artifact-write-share-revoke", + "summary": "Revoke public sharing", + "description": "Turn off public sharing; the link stops resolving immediately.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- The public CDN objects are deleted, so the link stops resolving; this is a no-op when the artifact is not shared.\n- Re-enabling later returns the same `public_url` — the link is keyed by artifact ID, so revoke is not a way to rotate the link.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-revoke", + "metadata": { + "sidebarTitle": "Revoke public sharing" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } } }, "components": { @@ -60620,6 +61579,424 @@ "description": "When false (default), deletion fails with a `ReferenceExist` error listing the members that still hold the role in `data.refs`. When true, the role is first revoked from all holders and then deleted." } } + }, + "ArtifactIdRequest": { + "type": "object", + "description": "Identify one gallery artifact.", + "properties": { + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactListRequest": { + "type": "object", + "description": "Filter and paginate the artifact gallery.", + "properties": { + "scope": { + "type": "string", + "enum": [ + "all", + "personal", + "team" + ], + "default": "all", + "description": "Visibility scope. `all` (default) lists the caller's own personal artifacts plus artifacts of every team the caller belongs to; `personal` lists only the caller's own; `team` lists only team-owned artifacts of the caller's teams." + }, + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Restrict to artifacts owned by these team IDs, intersected with the caller's visibility — teams the caller does not belong to silently return nothing." + }, + "query": { + "type": "string", + "description": "Case-insensitive substring match on the artifact title." + }, + "page": { + "type": "integer", + "default": 1, + "minimum": 1, + "description": "Page number, 1-based." + }, + "limit": { + "type": "integer", + "default": 20, + "maximum": 100, + "description": "Page size. Defaults to 20; capped at 100." + }, + "orderby": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ], + "description": "Sort field: `created_at` or `updated_at`. Empty means `updated_at` descending." + }, + "asc": { + "type": "boolean", + "description": "Sort ascending when true, descending when false. Applies only when `orderby` is set." + } + } + }, + "ArtifactListResponse": { + "type": "object", + "description": "A page of gallery artifacts.", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublishedArtifactItem" + }, + "description": "Artifacts on this page." + }, + "total": { + "type": "integer", + "format": "int64", + "description": "Total number of artifacts matching the filter across all pages." + } + } + }, + "PublishedArtifactItem": { + "type": "object", + "description": "One published artifact in the gallery. Time fields are Unix timestamps in milliseconds.", + "properties": { + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "title": { + "type": "string", + "description": "Display title in the gallery." + }, + "team_id": { + "type": "integer", + "format": "int64", + "description": "Owning team ID. `0` means a personal artifact (creator-only management); a positive value means team-owned." + }, + "team_name": { + "type": "string", + "description": "Owning team's display name. Omitted for personal artifacts." + }, + "person_id": { + "type": "integer", + "format": "int64", + "description": "Person (member) ID of the creator." + }, + "creator_name": { + "type": "string", + "description": "Creator's display name." + }, + "is_mine": { + "type": "boolean", + "description": "Whether the caller is the creator." + }, + "can_edit": { + "type": "boolean", + "description": "Whether the caller may manage this artifact (rename, transfer, delete, share): the creator, any member of the owning team, or a manager of the source session." + }, + "session_id": { + "type": "string", + "description": "Source session ID (`sess_` prefix) that produced the file." + }, + "session_title": { + "type": "string", + "description": "Source session's title. Omitted when the session has been deleted." + }, + "file_id": { + "type": "string", + "description": "Presented-file ID (`pf_` prefix) currently holding the artifact's bytes. Changes on every republish." + }, + "name": { + "type": "string", + "description": "Underlying file name including extension." + }, + "size": { + "type": "integer", + "format": "int64", + "description": "File size in bytes." + }, + "content_type": { + "type": "string", + "description": "MIME type of the file." + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the artifact was published." + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the artifact was last updated (rename, transfer, or republish)." + }, + "share_enabled": { + "type": "boolean", + "description": "Whether anonymous public sharing is on. Omitted when false." + }, + "public_url": { + "type": "string", + "description": "Anonymous public link — a console `/share/artifact/` page served entirely from CDN. Present only while shared; anyone with the link can view it, no login required." + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "Person ID of the member who enabled sharing. Present only while shared." + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the last share enable or snapshot sync. Present only while shared." + }, + "share_file_id": { + "type": "string", + "description": "Presented-file ID the public snapshot was materialized from. When `share_enabled` is true and `file_id` differs from `share_file_id`, the public snapshot is stale — call `/safari/artifact/gallery/share/sync` to refresh it." + } + }, + "required": [ + "artifact_id", + "title", + "team_id", + "person_id", + "creator_name", + "is_mine", + "can_edit", + "session_id", + "file_id", + "name", + "size", + "content_type", + "created_at", + "updated_at" + ] + }, + "ArtifactUpdateRequest": { + "type": "object", + "description": "Partial update of a gallery artifact. Only provided fields change.", + "properties": { + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "title": { + "type": [ + "string", + "null" + ], + "description": "New title. Trimmed; must be non-empty when provided. Omit to leave unchanged." + }, + "team_id": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "Transfer target scope. `0` moves the artifact to personal scope (only the creator can manage it); a positive value moves it to a team the caller must belong to. Omit to leave unchanged." + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactPublishFromFileRequest": { + "type": "object", + "description": "Publish a session-produced file to the artifact gallery.", + "properties": { + "file_id": { + "type": "string", + "description": "Presented-file ID (`pf_` prefix) of a file produced in a session, as shown on the file card in chat." + }, + "title": { + "type": "string", + "description": "Gallery display title. Trimmed; must be non-empty." + } + }, + "required": [ + "file_id", + "title" + ] + }, + "ArtifactPublishResponse": { + "type": "object", + "description": "Result of publishing a file to the gallery.", + "properties": { + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "title": { + "type": "string", + "description": "Gallery display title." + }, + "gallery_path": { + "type": "string", + "description": "Console-relative path of the artifact page: `/ai-sre/artifacts/`." + } + }, + "required": [ + "artifact_id", + "title", + "gallery_path" + ] + }, + "ArtifactFileStateRequest": { + "type": "object", + "description": "Probe publish state for a batch of presented files.", + "properties": { + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 50, + "minItems": 1, + "description": "Presented-file IDs (`pf_` prefix) to probe. At most 50 per call; duplicates and empty strings are ignored." + } + }, + "required": [ + "file_ids" + ] + }, + "ArtifactFileStateItem": { + "type": "object", + "description": "Live publish state of one presented file. Returned only for files that have a live published artifact.", + "properties": { + "file_id": { + "type": "string", + "description": "Echoes the requested presented-file ID." + }, + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "title": { + "type": "string", + "description": "Gallery display title of the published artifact." + }, + "gallery_path": { + "type": "string", + "description": "Console-relative path of the artifact page: `/ai-sre/artifacts/`." + } + }, + "required": [ + "file_id", + "artifact_id", + "title", + "gallery_path" + ] + }, + "ArtifactFileStateResponse": { + "type": "object", + "description": "Publish states keyed by file.", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ArtifactFileStateItem" + }, + "description": "One entry per requested file that has a live published artifact; files without one are omitted." + } + }, + "required": [ + "items" + ] + }, + "ArtifactShareState": { + "type": "object", + "description": "Public-share state of an artifact.", + "properties": { + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "share_enabled": { + "type": "boolean", + "description": "Always `true` in this response." + }, + "public_url": { + "type": "string", + "description": "Anonymous public link served entirely from CDN. Anyone with the link can view the content, no login required." + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "Person ID of the member who enabled sharing." + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the last share enable or snapshot sync." + } + }, + "required": [ + "artifact_id", + "share_enabled", + "public_url", + "shared_by", + "shared_at" + ] + }, + "ArtifactSignRequest": { + "type": "object", + "description": "Request signed download/preview URLs for a presented file.", + "properties": { + "file_id": { + "type": "string", + "description": "Presented-file ID (`pf_` prefix) to sign." + }, + "share_token": { + "type": "string", + "description": "Optional session share-link token. Needed only when the caller reaches the file through a shared session link rather than account membership." + } + }, + "required": [ + "file_id" + ] + }, + "SignedURLs": { + "type": "object", + "description": "Short-lived signed URLs for downloading or previewing a file.", + "properties": { + "download_url": { + "type": "string", + "description": "Relative URL (`/safari/artifact/stream?...`) that serves the file as an attachment. Prepend the API base `https://api.flashcat.cloud` before use; expires with `expires_in`." + }, + "preview_url": { + "type": "string", + "description": "Same as `download_url` but served inline for browser preview." + }, + "expires_in": { + "type": "integer", + "description": "Validity of both URLs in seconds (300)." + }, + "name": { + "type": "string", + "description": "File name including extension." + }, + "size": { + "type": "integer", + "format": "int64", + "description": "File size in bytes." + }, + "content_type": { + "type": "string", + "description": "MIME type of the file." + } + }, + "required": [ + "download_url", + "preview_url", + "expires_in", + "name", + "size", + "content_type" + ] } } } diff --git a/api-reference/openapi.zh.json b/api-reference/openapi.zh.json index 16fd93a4..43e8ac4e 100644 --- a/api-reference/openapi.zh.json +++ b/api-reference/openapi.zh.json @@ -170,6 +170,10 @@ }, { "name": "AI SRE/知识" + }, + { + "name": "AI SRE/产物", + "description": "AI SRE 产物库 —— 发布、浏览并公开分享智能体生成的文件。" } ], "paths": { @@ -30814,6 +30818,961 @@ } } } + }, + "/safari/artifact/gallery/list": { + "post": { + "operationId": "artifact-read-list", + "summary": "查询产物列表", + "description": "分页查询调用者可见的产物列表,支持按标题搜索。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- `scope` 可选 `all`(默认 —— 调用者自己的个人产物 + 其所属全部团队的产物)、`personal`(仅自己创建的)、`team`(仅所属团队的团队产物)。\n- `team_ids` 可在可见范围内进一步限定团队 —— 调用者不属于的团队不会有任何返回。\n- 默认按 `updated_at` 倒序;设置 `orderby` 为 `created_at` 可更换排序字段,`asc: true` 改为升序。\n- 使用 `app_key` 调用时,可见范围按 key 所属成员的身份计算 —— 返回该成员的个人产物及其所在团队的产物。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-list", + "metadata": { + "sidebarTitle": "查询产物列表" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactListRequest" + }, + "example": { + "scope": "all", + "page": 1, + "limit": 20 + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactListResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + }, + { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu", + "title": "rum_recommendation", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 3790925372131, + "creator_name": "牛伟利", + "is_mine": false, + "can_edit": true, + "session_id": "sess_QXUC9C2PYWP5EE7vETR3UD", + "session_title": "生成 RUM 文档多格式", + "file_id": "pf_Jnc4E5YBcWLGzunB4ntP9s", + "name": "rum_recommendation.pdf", + "size": 137107, + "content_type": "application/pdf", + "created_at": 1785229432881, + "updated_at": 1785741344822 + } + ], + "total": 17 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/get": { + "post": { + "operationId": "artifact-read-get", + "summary": "获取产物详情", + "description": "根据产物 ID 获取产物详情。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 可见范围为整个账号:任何有效 `app_key` 都能读取账号内任意产物。`is_mine` 和 `can_edit` 相对于 key 所属成员计算。\n- 当 `share_enabled` 为 true 且 `file_id` 与 `share_file_id` 不一致时,公开快照已过期 —— 调用 `/safari/artifact/gallery/share/sync` 刷新。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-get", + "metadata": { + "sidebarTitle": "获取产物详情" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/file-state": { + "post": { + "operationId": "artifact-read-get-file-state", + "summary": "查询文件发布状态", + "description": "批量查询文件是否已发布为产物。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 单次最多传入 50 个 `file_ids`;重复和空字符串会被忽略。\n- 没有已发布产物的文件不会出现在 `items` 中 —— 按回显的 `file_id` 对应结果。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-get-file-state", + "metadata": { + "sidebarTitle": "查询文件发布状态" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactFileStateRequest" + }, + "example": { + "file_ids": [ + "pf_SdhEA5fbZJGnHzwrNJMMSB", + "pf_9kLm2nQpRsTuVwXyZaBcDe" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactFileStateResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/sign": { + "post": { + "operationId": "artifact-read-sign", + "summary": "签发文件访问链接", + "description": "为文件签发短期有效的下载/预览链接。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 两个链接在 `expires_in` 秒(300 秒)后过期,需重新签发获取新链接。\n- 返回的是相对链接 —— 使用前拼接 `https://api.flashcat.cloud`,然后以 `GET /safari/artifact/stream` 访问。\n- 签名令牌与调用账号及 app_key 所属成员身份绑定 —— 链接泄露给其他账号或成员不可用。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-sign", + "metadata": { + "sidebarTitle": "签发文件访问链接" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactSignRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SignedURLs" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "download_url": "/safari/artifact/stream?mode=download&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "preview_url": "/safari/artifact/stream?mode=preview&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "expires_in": 300, + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/stream": { + "get": { + "operationId": "artifact-read-stream", + "summary": "下载或预览文件", + "description": "使用签名令牌下载或预览文件内容。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **100 次/分钟**;**10 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 成功响应有两种形式:302 重定向到短期有效的对象存储预签名 URL(存储在 S3 兼容存储中的文件),或 200 二进制流(托管在自托管 runner 上的文件)。请跟随重定向。\n- 响应携带 `Cache-Control: private, no-store` —— 网关不会缓存。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-stream", + "metadata": { + "sidebarTitle": "下载或预览文件" + } + }, + "parameters": [ + { + "name": "t", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "由 `POST /safari/artifact/sign` 签发的令牌。与调用账号及成员绑定,有效期 5 分钟。" + }, + { + "name": "mode", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "download", + "preview" + ], + "default": "download" + }, + "description": "`download`(默认)以附件形式返回文件;`preview` 以 inline 方式返回供浏览器预览。其他取值按 `download` 处理。" + } + ], + "responses": { + "200": { + "description": "文件内容流。当文件托管在自托管 runner 上时由服务端代理返回,Content-Disposition 随 `mode` 变化。", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "302": { + "description": "当文件存储在 S3 兼容存储中时,302 重定向到短期有效的预签名 URL。请跟随 `Location` 头,无响应体。" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/publish-from-file": { + "post": { + "operationId": "artifact-write-publish", + "summary": "发布文件为产物", + "description": "将会话中生成的文件发布到产物库。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 允许的文件类型:HTML/Markdown、图片、PDF、文本/数据/代码文件以及 zip/tar 压缩包(按扩展名判断)。超过 16 MiB 的文件会被拒绝。\n- 发布按(来源会话 + 工作区路径)做 upsert —— 重复发布同一文件会替换产物内容并使用新的 `file_id`,已有的公开快照会因此过期,需执行分享同步刷新。\n- 产物继承来源会话的个人/团队空间;如需调整,发布后调用 `/safari/artifact/gallery/update` 转移。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-publish", + "metadata": { + "sidebarTitle": "发布文件为产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactPublishFromFileRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "title": "SK 海力士 2026 Q2 财报深度分析" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactPublishResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/update": { + "post": { + "operationId": "artifact-write-update", + "summary": "更新产物", + "description": "更新产物标题,或在个人/团队空间之间转移产物。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 仅更新传入的字段 —— 不传 `title` 或 `team_id` 则对应字段保持不变。\n- `team_id: 0` 表示转为个人产物(仅创建者可管理);传入大于 0 的 `team_id` 时,调用者(`app_key` 调用时为 key 所属成员)必须是该团队成员。\n- 返回更新后的完整产物。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-update", + "metadata": { + "sidebarTitle": "更新产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactUpdateRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785829000000, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/delete": { + "post": { + "operationId": "artifact-write-delete", + "summary": "从产物库移除产物", + "description": "将产物从产物库移除;来源文件仍保留在会话中。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 该操作只是从产物库移除,不会删除文件本身 —— 来源文件仍保留在会话中,可再次发布。\n- 如果产物已开启公开分享,公开内容会在同一操作中被销毁,公开链接立即失效。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-delete", + "metadata": { + "sidebarTitle": "从产物库移除产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/enable": { + "post": { + "operationId": "artifact-write-share-enable", + "summary": "开启公开分享", + "description": "开启产物的匿名公开分享并返回公开链接。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 公开链接是匿名的 —— 任何拿到链接的人都可以查看内容,链接也可能被继续转发。内容会被复制到公开 CDN 对象上,访问链接时不经过本 API。\n- 幂等:对已分享的产物重复开启会原样返回已有链接;撤销后重新开启也会得到同一个链接 —— 链接以产物 ID 为标识。\n- 超过 16 MiB 的产物无法分享,调用返回 `InvalidParameter`。\n- 分享是内容快照:之后重新发布产物不会自动更新公开内容,需调用 `/safari/artifact/gallery/share/sync` 同步。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-enable", + "metadata": { + "sidebarTitle": "开启公开分享" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785747928665 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/sync": { + "post": { + "operationId": "artifact-write-share-sync", + "summary": "更新公开分享快照", + "description": "将公开分享的内容快照更新为产物最新内容。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 未开启分享时调用返回 `InvalidParameter` —— 请先开启分享。\n- 公开链接不变,仅更新快照内容和 `shared_at`。\n- 可通过产物详情中 `share_file_id != file_id` 判断快照是否过期,再决定是否同步。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-sync", + "metadata": { + "sidebarTitle": "更新公开分享快照" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785829900000 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/revoke": { + "post": { + "operationId": "artifact-write-share-revoke", + "summary": "撤销公开分享", + "description": "撤销产物的公开分享,公开链接立即失效。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 公开 CDN 对象会被删除,链接随即失效;产物未开启分享时该操作为空操作。\n- 之后重新开启分享会返回同一个 `public_url` —— 链接以产物 ID 为标识,撤销并不能用于更换链接。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-revoke", + "metadata": { + "sidebarTitle": "撤销公开分享" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } } }, "components": { @@ -60620,6 +61579,424 @@ "description": "为 false(默认)时,若仍有成员持有该角色,删除失败并返回 `ReferenceExist` 错误,`data.refs` 中列出持有成员;为 true 时先从所有持有成员解除该角色,再删除角色。" } } + }, + "ArtifactIdRequest": { + "type": "object", + "description": "指定一个产物库中的产物。", + "properties": { + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactListRequest": { + "type": "object", + "description": "产物库列表的过滤与分页参数。", + "properties": { + "scope": { + "type": "string", + "enum": [ + "all", + "personal", + "team" + ], + "default": "all", + "description": "可见范围。`all`(默认)= 调用者自己的个人产物 + 其所在团队的产物;`personal` = 仅调用者自己创建的;`team` = 仅调用者所属团队的团队产物。" + }, + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "只看指定团队的产物,与调用者可见范围取交集 —— 不属于调用者的团队不会有任何返回。" + }, + "query": { + "type": "string", + "description": "按产物标题做大小写不敏感的子串匹配。" + }, + "page": { + "type": "integer", + "default": 1, + "minimum": 1, + "description": "页码,从 1 开始。" + }, + "limit": { + "type": "integer", + "default": 20, + "maximum": 100, + "description": "每页条数。默认 20,最大 100。" + }, + "orderby": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ], + "description": "排序字段:`created_at` 或 `updated_at`。留空按 `updated_at` 倒序。" + }, + "asc": { + "type": "boolean", + "description": "为 true 时升序,为 false 时降序。仅在设置了 `orderby` 时生效。" + } + } + }, + "ArtifactListResponse": { + "type": "object", + "description": "一页产物列表。", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublishedArtifactItem" + }, + "description": "当前页的产物列表。" + }, + "total": { + "type": "integer", + "format": "int64", + "description": "符合过滤条件的产物总数(跨所有页)。" + } + } + }, + "PublishedArtifactItem": { + "type": "object", + "description": "产物库中的一个已发布产物。时间字段均为 Unix 毫秒时间戳。", + "properties": { + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "title": { + "type": "string", + "description": "产物在产物库中的展示标题。" + }, + "team_id": { + "type": "integer", + "format": "int64", + "description": "归属团队 ID。`0` 表示个人产物(仅创建者可管理);大于 0 表示团队产物。" + }, + "team_name": { + "type": "string", + "description": "归属团队的名称。个人产物不返回该字段。" + }, + "person_id": { + "type": "integer", + "format": "int64", + "description": "创建者的成员 ID。" + }, + "creator_name": { + "type": "string", + "description": "创建者的显示名称。" + }, + "is_mine": { + "type": "boolean", + "description": "调用者是否为创建者。" + }, + "can_edit": { + "type": "boolean", + "description": "调用者是否可管理该产物(重命名、转移、删除、分享):创建者、归属团队任一成员、或来源会话的管理者。" + }, + "session_id": { + "type": "string", + "description": "产出该文件的来源会话 ID(`sess_` 前缀)。" + }, + "session_title": { + "type": "string", + "description": "来源会话的标题。会话已删除时不返回该字段。" + }, + "file_id": { + "type": "string", + "description": "当前承载产物内容的文件 ID(`pf_` 前缀)。每次重新发布都会变化。" + }, + "name": { + "type": "string", + "description": "底层文件名(含扩展名)。" + }, + "size": { + "type": "integer", + "format": "int64", + "description": "文件大小,单位字节。" + }, + "content_type": { + "type": "string", + "description": "文件的 MIME 类型。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "产物发布的 Unix 毫秒时间戳。" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "产物最近一次更新(重命名、转移或重新发布)的 Unix 毫秒时间戳。" + }, + "share_enabled": { + "type": "boolean", + "description": "是否已开启匿名公开分享。为 false 时不返回该字段。" + }, + "public_url": { + "type": "string", + "description": "匿名公开链接 —— 由 CDN 承载的控制台 `/share/artifact/` 页面。仅在分享开启时返回;任何拿到链接的人无需登录即可查看。" + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "开启分享的成员 ID。仅在分享开启时返回。" + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "最近一次开启分享或更新快照的 Unix 毫秒时间戳。仅在分享开启时返回。" + }, + "share_file_id": { + "type": "string", + "description": "公开快照所基于的文件 ID。当 `share_enabled` 为 true 且 `file_id` 与 `share_file_id` 不一致时,公开快照已过期 —— 调用 `/safari/artifact/gallery/share/sync` 刷新。" + } + }, + "required": [ + "artifact_id", + "title", + "team_id", + "person_id", + "creator_name", + "is_mine", + "can_edit", + "session_id", + "file_id", + "name", + "size", + "content_type", + "created_at", + "updated_at" + ] + }, + "ArtifactUpdateRequest": { + "type": "object", + "description": "产物的部分字段更新。仅更新传入的字段。", + "properties": { + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "title": { + "type": [ + "string", + "null" + ], + "description": "新标题。会去除首尾空白,传入时不能为空。不传则不变。" + }, + "team_id": { + "type": [ + "integer", + "null" + ], + "format": "int64", + "description": "转移目标空间。`0` 表示转为个人产物(仅创建者可管理);大于 0 表示转移到调用者所属的团队。不传则不变。" + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactPublishFromFileRequest": { + "type": "object", + "description": "把会话中生成的文件发布到产物库。", + "properties": { + "file_id": { + "type": "string", + "description": "会话中生成的文件 ID(`pf_` 前缀),即会话文件卡片上的文件标识。" + }, + "title": { + "type": "string", + "description": "产物在产物库中的展示标题。会去除首尾空白,不能为空。" + } + }, + "required": [ + "file_id", + "title" + ] + }, + "ArtifactPublishResponse": { + "type": "object", + "description": "发布产物的结果。", + "properties": { + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "title": { + "type": "string", + "description": "产物展示标题。" + }, + "gallery_path": { + "type": "string", + "description": "产物页面的控制台相对路径:`/ai-sre/artifacts/`。" + } + }, + "required": [ + "artifact_id", + "title", + "gallery_path" + ] + }, + "ArtifactFileStateRequest": { + "type": "object", + "description": "批量查询文件的产物发布状态。", + "properties": { + "file_ids": { + "type": "array", + "items": { + "type": "string" + }, + "maxItems": 50, + "minItems": 1, + "description": "要查询的文件 ID(`pf_` 前缀)。单次最多 50 个;重复和空字符串会被忽略。" + } + }, + "required": [ + "file_ids" + ] + }, + "ArtifactFileStateItem": { + "type": "object", + "description": "单个文件的发布状态。仅当文件存在已发布的产物时才会返回。", + "properties": { + "file_id": { + "type": "string", + "description": "回显请求中的文件 ID。" + }, + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "title": { + "type": "string", + "description": "已发布产物的展示标题。" + }, + "gallery_path": { + "type": "string", + "description": "产物页面的控制台相对路径:`/ai-sre/artifacts/`。" + } + }, + "required": [ + "file_id", + "artifact_id", + "title", + "gallery_path" + ] + }, + "ArtifactFileStateResponse": { + "type": "object", + "description": "按文件返回的发布状态。", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ArtifactFileStateItem" + }, + "description": "每个存在已发布产物的请求文件对应一条记录;未发布的文件不会出现在列表中。" + } + }, + "required": [ + "items" + ] + }, + "ArtifactShareState": { + "type": "object", + "description": "产物的公开分享状态。", + "properties": { + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "share_enabled": { + "type": "boolean", + "description": "本响应中固定为 `true`。" + }, + "public_url": { + "type": "string", + "description": "完全由 CDN 承载的匿名公开链接。任何拿到链接的人无需登录即可查看内容。" + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "开启分享的成员 ID。" + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "最近一次开启分享或更新快照的 Unix 毫秒时间戳。" + } + }, + "required": [ + "artifact_id", + "share_enabled", + "public_url", + "shared_by", + "shared_at" + ] + }, + "ArtifactSignRequest": { + "type": "object", + "description": "为文件申请签名下载/预览链接。", + "properties": { + "file_id": { + "type": "string", + "description": "要签发的文件 ID(`pf_` 前缀)。" + }, + "share_token": { + "type": "string", + "description": "可选的会话分享链接令牌。仅当调用者通过会话分享链接(而非账号成员身份)访问文件时需要。" + } + }, + "required": [ + "file_id" + ] + }, + "SignedURLs": { + "type": "object", + "description": "短期有效的文件下载/预览签名链接。", + "properties": { + "download_url": { + "type": "string", + "description": "以下载方式(attachment)返回文件的相对链接(`/safari/artifact/stream?...`)。使用前需拼接 API 域名 `https://api.flashcat.cloud`;有效期见 `expires_in`。" + }, + "preview_url": { + "type": "string", + "description": "与 `download_url` 相同,但以 inline 方式返回,用于浏览器内预览。" + }, + "expires_in": { + "type": "integer", + "description": "两个链接的有效期,单位秒(300)。" + }, + "name": { + "type": "string", + "description": "文件名(含扩展名)。" + }, + "size": { + "type": "integer", + "format": "int64", + "description": "文件大小,单位字节。" + }, + "content_type": { + "type": "string", + "description": "文件的 MIME 类型。" + } + }, + "required": [ + "download_url", + "preview_url", + "expires_in", + "name", + "size", + "content_type" + ] } } } diff --git a/api-reference/safari.openapi.en.json b/api-reference/safari.openapi.en.json index 07877eae..8c513b5e 100644 --- a/api-reference/safari.openapi.en.json +++ b/api-reference/safari.openapi.en.json @@ -38,6 +38,10 @@ }, { "name": "AI SRE/Knowledge" + }, + { + "name": "AI SRE/Artifacts", + "description": "AI SRE artifact gallery — publish, browse, and publicly share agent-produced files." } ], "paths": { @@ -4023,164 +4027,1119 @@ } } } - } - }, - "components": { - "securitySchemes": { - "AppKeyAuth": { - "type": "apiKey", - "in": "query", - "name": "app_key", - "description": "App key issued from the Flashduty console. Required on every public API call. Keep it secret — it grants the same access as the owning account." - }, - "AutomationTriggerBearerAuth": { - "type": "http", - "scheme": "bearer", - "description": "Bearer token generated for one Automation HTTP POST trigger. This is not an app_key." - } }, - "responses": { - "BadRequest": { - "description": "Invalid request — usually a missing or malformed parameter.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "missingParameter": { - "summary": "Missing required parameter", - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "InvalidParameter", - "message": "The specified parameter skill_id is not valid." - } - } - } - } + "/safari/artifact/gallery/list": { + "post": { + "operationId": "artifact-read-list", + "summary": "List artifacts", + "description": "List published artifacts visible to the caller, with pagination and title search.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] } - } - }, - "Unauthorized": { - "description": "Missing or invalid app_key.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "missingAppKey": { - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "Unauthorized", - "message": "You are unauthorized." - } - } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- `scope` selects `all` (default — the caller's own personal artifacts plus every team the caller belongs to), `personal` (only the caller's own), or `team` (only team-owned artifacts of the caller's teams).\n- `team_ids` narrows further to specific teams, intersected with the caller's visibility — teams the caller does not belong to return nothing.\n- Default sort is `updated_at` descending; set `orderby` to `created_at` to change the field and `asc: true` to flip direction.\n- For an `app_key` call, visibility is evaluated against the key owner's identity — the list shows that member's personal artifacts and their teams' artifacts.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-list", + "metadata": { + "sidebarTitle": "List artifacts" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactListRequest" + }, + "example": { + "scope": "all", + "page": 1, + "limit": 20 } } } - } - }, - "Forbidden": { - "description": "The app_key is valid but lacks permission for this operation.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "accessDenied": { - "value": { + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactListResponse" + } + } + } + ] + }, + "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "AccessDenied", - "message": "Access Denied." + "data": { + "items": [ + { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + }, + { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu", + "title": "rum_recommendation", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 3790925372131, + "creator_name": "牛伟利", + "is_mine": false, + "can_edit": true, + "session_id": "sess_QXUC9C2PYWP5EE7vETR3UD", + "session_title": "生成 RUM 文档多格式", + "file_id": "pf_Jnc4E5YBcWLGzunB4ntP9s", + "name": "rum_recommendation.pdf", + "size": 137107, + "content_type": "application/pdf", + "created_at": 1785229432881, + "updated_at": 1785741344822 + } + ], + "total": 17 } } } } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" } } - }, - "TooManyRequests": { - "description": "Rate limit hit. Either the global API limit or a per-account limit.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "rateLimited": { - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "RequestTooFrequently", - "message": "Request too frequently." - } - } + } + }, + "/safari/artifact/gallery/get": { + "post": { + "operationId": "artifact-read-get", + "summary": "Get artifact detail", + "description": "Get a single published artifact by ID.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Visibility is account-wide: any valid `app_key` can read any artifact in the account. `is_mine` and `can_edit` are computed relative to the key owner.\n- When `share_enabled` is true and `file_id` differs from `share_file_id`, the public snapshot is stale — refresh it with `/safari/artifact/gallery/share/sync`.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-get", + "metadata": { + "sidebarTitle": "Get artifact detail" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" } } } - } - }, - "ServerError": { - "description": "Unexpected server-side error. Include the request_id when reporting.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "internal": { - "value": { + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "InternalError", - "message": "We encountered an internal error, and it has been reported. Please try again later." + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" } } } } - } - } - } - }, - "schemas": { - "A2AAgentCreateRequest": { - "type": "object", - "description": "Registration parameters for a new A2A agent.", - "properties": { - "agent_name": { - "type": "string", - "description": "Agent display name.", - "maxLength": 128 - }, - "instructions": { - "type": "string", - "description": "Natural-language instructions for the remote agent: a Markdown document with optional `summary` frontmatter and a non-empty body, at most 50 KiB (51200 bytes). Required — a deprecated `description` field is still accepted for legacy clients and, if both are sent, must exactly match `instructions`.", - "maxLength": 51200 }, - "card_url": { - "type": "string", - "description": "URL of the remote agent card. Must be an absolute `http` or `https` URL with a non-empty host; reachability is enforced by the execution environment, not at creation time." + "400": { + "$ref": "#/components/responses/BadRequest" }, - "auth_type": { - "type": "string", - "description": "Authentication type for reaching the remote agent: `none` (default when omitted), `api_key`, or `bearer`.", - "enum": [ - "none", - "api_key", - "bearer" - ] + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "auth_config": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Authentication config key-values, e.g. the API key or bearer token. Values under credential-looking keys (name containing KEY, SECRET, TOKEN, PASSWORD, etc.) are masked back in responses." + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/file-state": { + "post": { + "operationId": "artifact-read-get-file-state", + "summary": "Get file publish state", + "description": "Check which presented files already have a live published artifact.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- At most 50 `file_ids` per call; duplicates and empty strings are ignored.\n- Files with no live published artifact are simply absent from `items` — match results by the echoed `file_id`.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-get-file-state", + "metadata": { + "sidebarTitle": "Get file publish state" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactFileStateRequest" + }, + "example": { + "file_ids": [ + "pf_SdhEA5fbZJGnHzwrNJMMSB", + "pf_9kLm2nQpRsTuVwXyZaBcDe" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactFileStateResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/sign": { + "post": { + "operationId": "artifact-read-sign", + "summary": "Create signed file URLs", + "description": "Create short-lived signed URLs to download or preview a presented file.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Both URLs expire after `expires_in` seconds (300). Sign again to get fresh URLs.\n- Returned URLs are relative — prepend `https://api.flashcat.cloud` before use, then follow them with `GET /safari/artifact/stream`.\n- The signed token is bound to the calling account and the app_key owner's identity, so a leaked URL does not work for another account or member.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-sign", + "metadata": { + "sidebarTitle": "Create signed file URLs" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactSignRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SignedURLs" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "download_url": "/safari/artifact/stream?mode=download&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "preview_url": "/safari/artifact/stream?mode=preview&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "expires_in": 300, + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/stream": { + "get": { + "operationId": "artifact-read-stream", + "summary": "Download or preview a file", + "description": "Download or preview a file's bytes using a signed token.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **100 requests/minute**; **10 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Success has two forms: a `302` redirect to a short-lived presigned object-storage URL (files stored in S3-compatible storage), or a `200` binary stream (files hosted on a self-hosted runner). Follow redirects.\n- Responses carry `Cache-Control: private, no-store` — they are never cached by the gateway.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-read-stream", + "metadata": { + "sidebarTitle": "Download or preview a file" + } + }, + "parameters": [ + { + "name": "t", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "Signed token issued by `POST /safari/artifact/sign`. Bound to the calling account and person; valid for 5 minutes." + }, + { + "name": "mode", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "download", + "preview" + ], + "default": "download" + }, + "description": "`download` (default) serves the file as an attachment; `preview` serves it inline for browser display. Any other value falls back to `download`." + } + ], + "responses": { + "200": { + "description": "File bytes, proxied, when the file is hosted on a self-hosted runner. Content-Disposition follows `mode`.", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "302": { + "description": "Redirect to a short-lived presigned object-storage URL when the file lives in S3-compatible storage. Follow the `Location` header; no body." + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/publish-from-file": { + "post": { + "operationId": "artifact-write-publish", + "summary": "Publish file as artifact", + "description": "Publish a session-produced file to the artifact gallery.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Allowed file types: HTML/Markdown, images, PDF, text/data/code files, and zip/tar archives, matched by extension. Files over 16 MiB are rejected.\n- Publishing is an upsert keyed by the source session and workspace path — republishing the same file replaces the artifact's bytes under a fresh `file_id`, which makes an existing public snapshot stale until synced.\n- The artifact inherits personal/team scope from the source session; move it afterwards with `/safari/artifact/gallery/update` if needed.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-publish", + "metadata": { + "sidebarTitle": "Publish file as artifact" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactPublishFromFileRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "title": "SK 海力士 2026 Q2 财报深度分析" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactPublishResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/update": { + "post": { + "operationId": "artifact-write-update", + "summary": "Update artifact", + "description": "Rename an artifact or transfer it between personal and team scope.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Only the provided fields change — omit `title` or `team_id` to leave them unchanged.\n- `team_id: 0` moves the artifact to personal scope (only the creator can manage it); a positive `team_id` requires the caller (for `app_key` calls, the key owner) to be a member of that team.\n- Returns the full artifact after the update.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-update", + "metadata": { + "sidebarTitle": "Update artifact" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactUpdateRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785829000000, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/delete": { + "post": { + "operationId": "artifact-write-delete", + "summary": "Remove artifact from gallery", + "description": "Detach an artifact from the gallery; the source file stays with its session.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- This is a detach, not a byte delete: the underlying presented file stays with the source session and can be published again.\n- If public sharing was enabled, the public objects are destroyed in the same operation and the link stops resolving.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-delete", + "metadata": { + "sidebarTitle": "Remove artifact from gallery" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/enable": { + "post": { + "operationId": "artifact-write-share-enable", + "summary": "Enable public sharing", + "description": "Turn on anonymous public sharing for an artifact and return its public link.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- The public link is anonymous — anyone with it can view the content, and the link may be forwarded. Content is copied to public CDN objects; the gallery API is not involved when the link is viewed.\n- Idempotent: enabling an already-shared artifact returns the existing link unchanged, and re-enabling after a revoke brings the same link back — the link is keyed by artifact ID.\n- Artifacts over 16 MiB cannot be shared; the call fails with `InvalidParameter`.\n- Sharing is a snapshot: later republishes do not update the public content until you call `/safari/artifact/gallery/share/sync`.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-enable", + "metadata": { + "sidebarTitle": "Enable public sharing" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785747928665 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/sync": { + "post": { + "operationId": "artifact-write-share-sync", + "summary": "Update shared snapshot", + "description": "Refresh the public snapshot of a shared artifact with its latest content.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- Fails with `InvalidParameter` when sharing is not enabled — enable it first.\n- The link never changes; only the snapshot bytes and `shared_at` are refreshed.\n- Use `share_file_id != file_id` on the artifact detail to detect a stale snapshot before syncing.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-sync", + "metadata": { + "sidebarTitle": "Update shared snapshot" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785829900000 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/revoke": { + "post": { + "operationId": "artifact-write-share-revoke", + "summary": "Revoke public sharing", + "description": "Turn off public sharing; the link stops resolving immediately.", + "tags": [ + "AI SRE/Artifacts" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | None — any valid `app_key` can call this operation |\n\n## Usage\n\n- The public CDN objects are deleted, so the link stops resolving; this is a no-op when the artifact is not shared.\n- Re-enabling later returns the same `public_url` — the link is keyed by artifact ID, so revoke is not a way to rotate the link.\n- Every call is recorded in the account audit log.\n", + "href": "/en/api-reference/ai-sre/artifacts/artifact-write-share-revoke", + "metadata": { + "sidebarTitle": "Revoke public sharing" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + } + }, + "components": { + "securitySchemes": { + "AppKeyAuth": { + "type": "apiKey", + "in": "query", + "name": "app_key", + "description": "App key issued from the Flashduty console. Required on every public API call. Keep it secret — it grants the same access as the owning account." + }, + "AutomationTriggerBearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "Bearer token generated for one Automation HTTP POST trigger. This is not an app_key." + } + }, + "responses": { + "BadRequest": { + "description": "Invalid request — usually a missing or malformed parameter.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "missingParameter": { + "summary": "Missing required parameter", + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InvalidParameter", + "message": "The specified parameter skill_id is not valid." + } + } + } + } + } + } + }, + "Unauthorized": { + "description": "Missing or invalid app_key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "missingAppKey": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "Unauthorized", + "message": "You are unauthorized." + } + } + } + } + } + } + }, + "Forbidden": { + "description": "The app_key is valid but lacks permission for this operation.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "accessDenied": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "AccessDenied", + "message": "Access Denied." + } + } + } + } + } + } + }, + "TooManyRequests": { + "description": "Rate limit hit. Either the global API limit or a per-account limit.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "rateLimited": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestTooFrequently", + "message": "Request too frequently." + } + } + } + } + } + } + }, + "ServerError": { + "description": "Unexpected server-side error. Include the request_id when reporting.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "internal": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InternalError", + "message": "We encountered an internal error, and it has been reported. Please try again later." + } + } + } + } + } + } + } + }, + "schemas": { + "A2AAgentCreateRequest": { + "type": "object", + "description": "Registration parameters for a new A2A agent.", + "properties": { + "agent_name": { + "type": "string", + "description": "Agent display name.", + "maxLength": 128 + }, + "instructions": { + "type": "string", + "description": "Natural-language instructions for the remote agent: a Markdown document with optional `summary` frontmatter and a non-empty body, at most 50 KiB (51200 bytes). Required — a deprecated `description` field is still accepted for legacy clients and, if both are sent, must exactly match `instructions`.", + "maxLength": 51200 + }, + "card_url": { + "type": "string", + "description": "URL of the remote agent card. Must be an absolute `http` or `https` URL with a non-empty host; reachability is enforced by the execution environment, not at creation time." + }, + "auth_type": { + "type": "string", + "description": "Authentication type for reaching the remote agent: `none` (default when omitted), `api_key`, or `bearer`.", + "enum": [ + "none", + "api_key", + "bearer" + ] + }, + "auth_config": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Authentication config key-values, e.g. the API key or bearer token. Values under credential-looking keys (name containing KEY, SECRET, TOKEN, PASSWORD, etc.) are masked back in responses." }, "streaming": { "type": "boolean", @@ -5737,19 +6696,239 @@ }, "server_name": { "type": "string", - "description": "MCP server name, unique within its scope (account-wide or one team), case-insensitive." + "description": "MCP server name, unique within its scope (account-wide or one team), case-insensitive." + }, + "description": { + "type": "string", + "description": "Server description." + }, + "ai_description": { + "type": "string", + "description": "LLM-generated description, preferred over `description` when present. Omitted when not yet generated." + }, + "transport": { + "type": "string", + "description": "Transport protocol. One of: `stdio` (standard I/O to a local subprocess), `sse` (standalone SSE, the legacy MCP transport), `streamable-http` (the newer HTTP streaming transport).", + "enum": [ + "stdio", + "sse", + "streamable-http" + ] + }, + "command": { + "type": "string", + "description": "Executable command (stdio transport only)." + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Command arguments (stdio transport)." + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables (stdio transport). Secret values are masked." + }, + "url": { + "type": "string", + "description": "Server URL (sse / streamable-http transport)." + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP headers (sse / streamable-http). Secret values are masked." + }, + "proxy_url": { + "type": "string", + "description": "Outbound proxy URL used to reach the server." + }, + "status": { + "type": "string", + "description": "Server status.", + "enum": [ + "enabled", + "disabled" + ] + }, + "connect_timeout": { + "type": "integer", + "description": "Connection timeout in seconds (0 = server default, 10s)." + }, + "call_timeout": { + "type": "integer", + "description": "Tool-call timeout in seconds (0 = server default, 60s)." + }, + "allow_insecure_oauth_http": { + "type": "boolean", + "description": "Allow this server's OAuth token exchange over plaintext HTTP; testing use only. Omitted when false." + }, + "allow_insecure_tls_skip_verify": { + "type": "boolean", + "description": "Skip TLS certificate verification when connecting to this server; testing use only. Omitted when false." + }, + "auth_mode": { + "type": "string", + "description": "Authentication mode. One of: `shared` (a single static credential saved on the resource and shared by all callers in the account; the default — an empty value behaves the same), `per_user_secret` (each user stores their own secret per `secret_schema`, injected per user at runtime), `per_user_oauth` (each user completes their own OAuth grant; discovery and registration run lazily on first use).", + "enum": [ + "shared", + "per_user_secret", + "per_user_oauth" + ] + }, + "secret_schema": { + "type": "string", + "description": "JSON-encoded secret schema (per_user_secret mode)." + }, + "oauth_metadata": { + "type": "string", + "description": "JSON-encoded OAuth metadata (per_user_oauth mode)." + }, + "source_template_name": { + "type": "string", + "description": "Marketplace template this connector was installed from; empty for user-authored." + }, + "created_by": { + "type": "integer", + "description": "Member ID that created the server.", + "format": "int64" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "Creation time. Unix timestamp in milliseconds." + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Last update time. Unix timestamp in milliseconds." + } + }, + "required": [ + "server_id", + "account_id", + "team_id", + "can_edit", + "server_name", + "description", + "transport", + "status", + "connect_timeout", + "call_timeout", + "created_by", + "created_at", + "updated_at", + "environments" + ] + }, + "MCPServerListRequest": { + "type": "object", + "description": "Pagination, scope, and search filters for listing MCP servers.", + "properties": { + "p": { + "type": "integer", + "description": "Page number, 1-based.", + "default": 1 + }, + "limit": { + "type": "integer", + "description": "Page size.", + "default": 20 + }, + "scope": { + "type": "string", + "description": "Restrict results to a scope: `account` for account-wide rows only, `team` for the caller's own visible team rows only, or omit (defaults to `all`) for both, subject to team_ids/include_account.", + "enum": [ + "all", + "account", + "team" + ] + }, + "query": { + "type": "string", + "maxLength": 128, + "description": "Case-insensitive substring search across name, description, AI-generated description, server ID, transport, URL, command, and source template name." + }, + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Filter to these team IDs; empty = the caller's visible set." + }, + "include_account": { + "type": [ + "boolean", + "null" + ], + "description": "Include account-scoped (team_id=0) rows. Defaults to true." + } + } + }, + "MCPServerListResponse": { + "type": "object", + "description": "Paginated MCP server list.", + "properties": { + "total": { + "type": "integer", + "description": "Total number of matching servers.", + "format": "int64" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MCPServerItem" + }, + "description": "MCP servers on this page." + } + }, + "required": [ + "total", + "servers" + ] + }, + "MCPServerStatusRequest": { + "type": "object", + "description": "MCP server enable/disable by ID.", + "properties": { + "server_id": { + "type": "string", + "description": "Target MCP server ID, from the list returned by `POST /safari/mcp/server/list`." + } + }, + "required": [ + "server_id" + ] + }, + "MCPServerUpdateRequest": { + "type": "object", + "description": "Partial update of an MCP server. Omit a field to leave it unchanged.", + "properties": { + "server_id": { + "type": "string", + "description": "Target MCP server ID, from the list returned by `POST /safari/mcp/server/list`." + }, + "server_name": { + "type": "string", + "description": "New name; omitted or empty leaves it unchanged.", + "minLength": 1, + "maxLength": 255 }, "description": { "type": "string", - "description": "Server description." - }, - "ai_description": { - "type": "string", - "description": "LLM-generated description, preferred over `description` when present. Omitted when not yet generated." + "description": "New description; omitted or empty leaves it unchanged.", + "minLength": 1, + "maxLength": 1024 }, "transport": { "type": "string", - "description": "Transport protocol. One of: `stdio` (standard I/O to a local subprocess), `sse` (standalone SSE, the legacy MCP transport), `streamable-http` (the newer HTTP streaming transport).", + "description": "Transport protocol; when switching, also supply the matching fields (`command`/`args`/`env` for `stdio`, `url`/`headers` for `sse` / `streamable-http`); omitted or empty leaves it unchanged.", "enum": [ "stdio", "sse", @@ -5758,21 +6937,21 @@ }, "command": { "type": "string", - "description": "Executable command (stdio transport only)." + "description": "Executable command (stdio transport)." }, "args": { "type": "array", "items": { "type": "string" }, - "description": "Command arguments (stdio transport)." + "description": "Command arguments (`stdio` transport); replaces the whole list — pass `[]` to clear, omit to leave unchanged." }, "env": { "type": "object", "additionalProperties": { "type": "string" }, - "description": "Environment variables (stdio transport). Secret values are masked." + "description": "Environment variables (`stdio` transport); replaces the whole map, but a sensitive key sent back masked or as an empty string keeps its stored value; omit to leave unchanged." }, "url": { "type": "string", @@ -5783,1042 +6962,1128 @@ "additionalProperties": { "type": "string" }, - "description": "HTTP headers (sse / streamable-http). Secret values are masked." - }, - "proxy_url": { - "type": "string", - "description": "Outbound proxy URL used to reach the server." - }, - "status": { - "type": "string", - "description": "Server status.", - "enum": [ - "enabled", - "disabled" - ] + "description": "HTTP headers (`sse` / `streamable-http` transport); replaces the whole map, with the same masked/empty-value preservation as `env`; omit to leave unchanged." }, "connect_timeout": { "type": "integer", - "description": "Connection timeout in seconds (0 = server default, 10s)." + "description": "Connection timeout in seconds. 0 = default (10s)." }, "call_timeout": { "type": "integer", - "description": "Tool-call timeout in seconds (0 = server default, 60s)." - }, - "allow_insecure_oauth_http": { - "type": "boolean", - "description": "Allow this server's OAuth token exchange over plaintext HTTP; testing use only. Omitted when false." - }, - "allow_insecure_tls_skip_verify": { - "type": "boolean", - "description": "Skip TLS certificate verification when connecting to this server; testing use only. Omitted when false." + "description": "Tool-call timeout in seconds. 0 = default (60s)." }, "auth_mode": { "type": "string", - "description": "Authentication mode. One of: `shared` (a single static credential saved on the resource and shared by all callers in the account; the default — an empty value behaves the same), `per_user_secret` (each user stores their own secret per `secret_schema`, injected per user at runtime), `per_user_oauth` (each user completes their own OAuth grant; discovery and registration run lazily on first use).", - "enum": [ - "shared", - "per_user_secret", - "per_user_oauth" - ] + "description": "Authentication mode: shared (default), per_user_secret, or per_user_oauth." }, "secret_schema": { "type": "string", - "description": "JSON-encoded secret schema (per_user_secret mode)." + "description": "JSON secret schema; required when auth_mode=per_user_secret." }, "oauth_metadata": { "type": "string", - "description": "JSON-encoded OAuth metadata (per_user_oauth mode)." + "description": "JSON OAuth metadata; reserved for per_user_oauth." }, - "source_template_name": { + "team_id": { + "type": [ + "integer", + "null" + ], + "description": "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.", + "format": "int64" + }, + "environments": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "description": "Execution environments this server is callable from: `cloud` and/or BYOC runner environment IDs. Omit (null) to leave unchanged; send a list to set it — an empty list clears the restriction back to all environments." + }, + "allow_insecure_oauth_http": { + "type": [ + "boolean", + "null" + ], + "description": "Allow OAuth token exchange over plaintext HTTP. Omit to leave unchanged." + }, + "allow_insecure_tls_skip_verify": { + "type": [ + "boolean", + "null" + ], + "description": "Skip TLS certificate verification. Omit to leave unchanged." + } + }, + "required": [ + "server_id" + ] + }, + "ManualRunRuleResult": { + "type": "object", + "description": "Result of manually running an Automation rule outside its schedule.", + "properties": { + "rule_id": { "type": "string", - "description": "Marketplace template this connector was installed from; empty for user-authored." + "description": "Rule ID that was run." }, - "created_by": { - "type": "integer", - "description": "Member ID that created the server.", - "format": "int64" + "trigger_kind": { + "type": "string", + "enum": [ + "manual" + ], + "description": "Always manual for this operation." }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "Creation time. Unix timestamp in milliseconds." + "preflight": { + "$ref": "#/components/schemas/PreflightResult" }, - "updated_at": { - "type": "integer", - "format": "int64", - "description": "Last update time. Unix timestamp in milliseconds." + "run": { + "$ref": "#/components/schemas/AutomationRunView" } }, "required": [ - "server_id", - "account_id", - "team_id", - "can_edit", - "server_name", - "description", - "transport", - "status", - "connect_timeout", - "call_timeout", - "created_by", - "created_at", - "updated_at", - "environments" + "rule_id", + "trigger_kind", + "preflight" ] }, - "MCPServerListRequest": { + "PreflightResult": { "type": "object", - "description": "Pagination, scope, and search filters for listing MCP servers.", + "description": "Readiness checks computed before a manual run is allowed to start.", "properties": { - "p": { - "type": "integer", - "description": "Page number, 1-based.", - "default": 1 + "ok": { + "type": "boolean", + "description": "Whether all readiness checks passed. Always true in a response that reaches the caller — a failed preflight returns a 400/403 error instead of a payload with ok=false." }, - "limit": { - "type": "integer", - "description": "Page size.", - "default": 20 + "checks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Names of the readiness checks performed, in order. Current fixed set: rule_loaded, actor_authorized, app_allowed, runtime_scope_resolved, rule_config_valid." }, "scope": { "type": "string", - "description": "Restrict results to a scope: `account` for account-wide rows only, `team` for the caller's own visible team rows only, or omit (defaults to `all`) for both, subject to team_ids/include_account.", "enum": [ - "all", - "account", + "person", "team" - ] + ], + "description": "Resolved run scope for this run; mirrors the rule's run_scope. One of: `person` (personal rule, runs as its creator), `team` (team rule, runs under the owning team)." }, - "query": { + "owner_id": { + "type": "integer", + "format": "int64", + "description": "Rule owner person ID." + }, + "team_id": { + "type": "integer", + "format": "int64", + "description": "Rule's scope team ID; 0 means a personal rule." + }, + "app_name": { "type": "string", - "maxLength": 128, - "description": "Case-insensitive substring search across name, description, AI-generated description, server ID, transport, URL, command, and source template name." + "description": "App the rule is scoped to. Currently always ai-sre; manual runs are only supported for that app." }, - "team_ids": { + "warnings": { "type": "array", "items": { - "type": "integer", - "format": "int64" + "type": "string" }, - "description": "Filter to these team IDs; empty = the caller's visible set." + "description": "Non-fatal warnings surfaced during preflight. Omitted or empty when there are none." + } + }, + "required": [ + "ok", + "checks", + "scope", + "owner_id", + "team_id", + "app_name" + ] + }, + "ResponseEnvelope": { + "type": "object", + "description": "Standard response envelope used by every Flashduty public API. On success `data` contains the endpoint-specific payload and `error` is absent. On failure `error` is present and `data` is absent. `request_id` is always present and is also mirrored in the `Flashcat-Request-Id` response header.", + "properties": { + "request_id": { + "type": "string", + "description": "Unique ID for this request. Mirrored in the Flashcat-Request-Id header. Include it when reporting issues.", + "example": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" }, - "include_account": { - "type": [ - "boolean", - "null" - ], - "description": "Include account-scoped (team_id=0) rows. Defaults to true." + "error": { + "$ref": "#/components/schemas/DutyError" + }, + "data": { + "description": "Endpoint-specific payload. See each operation's 200 response schema." } - } + }, + "required": [ + "request_id" + ] + }, + "SessionDeleteRequest": { + "type": "object", + "description": "Session deletion by ID.", + "properties": { + "session_id": { + "type": "string", + "description": "Target session ID, from the list returned by `POST /safari/session/list`.", + "minLength": 1 + } + }, + "required": [ + "session_id" + ] + }, + "SessionExportRequest": { + "type": "object", + "description": "Export the full event transcript of one session as a streaming NDJSON body.", + "properties": { + "session_id": { + "type": "string", + "description": "Target session ID, from the list returned by `POST /safari/session/list`." + }, + "include_subagents": { + "type": "boolean", + "description": "When true, each subagent_dispatch line is followed by the child session's full event stream, bracketed by its own session_meta. Defaults to false." + } + }, + "required": [ + "session_id" + ] }, - "MCPServerListResponse": { + "SessionGetRequest": { "type": "object", - "description": "Paginated MCP server list.", + "description": "Fetch one session plus a backward-paged window of its most recent events.", "properties": { - "total": { + "session_id": { + "type": "string", + "description": "Target session ID, from the list returned by `POST /safari/session/list`.", + "minLength": 1 + }, + "share_token": { + "type": "string", + "description": "Share token for accessing a session through its share link. Omit it for normal account-authorized access.", + "maxLength": 512 + }, + "num_recent_events": { "type": "integer", - "description": "Total number of matching servers.", - "format": "int64" + "description": "Legacy page size: number of most-recent events to return. Superseded by `limit` when both are set; 0 uses the server default (100).", + "minimum": 0, + "maximum": 1000 }, - "servers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MCPServerItem" - }, - "description": "MCP servers on this page." + "limit": { + "type": "integer", + "description": "Page size for events; takes precedence over `num_recent_events`. 0 uses the server default (100).", + "minimum": 0, + "maximum": 1000 + }, + "search_after_ctx": { + "type": "string", + "description": "Opaque keyset cursor from a previous response; pass it back to fetch the next older page.", + "maxLength": 4096 } }, "required": [ - "total", - "servers" + "session_id" ] }, - "MCPServerStatusRequest": { + "SessionGetResponse": { "type": "object", - "description": "MCP server enable/disable by ID.", + "description": "A session plus a backward-paged window of its events.", "properties": { - "server_id": { + "session": { + "$ref": "#/components/schemas/SessionItem" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventItem" + }, + "description": "Recent events, ascending by (created_at, event_id)." + }, + "has_more_older": { + "type": "boolean", + "description": "True when older events remain beyond this page." + }, + "search_after_ctx": { "type": "string", - "description": "Target MCP server ID, from the list returned by `POST /safari/mcp/server/list`." + "description": "Opaque keyset cursor; pass back as search_after_ctx to fetch the next older page. Omitted when has_more_older is false." + }, + "suggest_init": { + "type": "boolean", + "description": "Account-wide onboarding flag: true when the account has zero knowledge packs in any scope; not specific to this session." } }, "required": [ - "server_id" + "session", + "events", + "has_more_older", + "suggest_init" ] }, - "MCPServerUpdateRequest": { + "SessionItem": { "type": "object", - "description": "Partial update of an MCP server. Omit a field to leave it unchanged.", + "description": "One agent session row.", "properties": { - "server_id": { + "session_id": { "type": "string", - "description": "Target MCP server ID, from the list returned by `POST /safari/mcp/server/list`." + "description": "Session identifier." }, - "server_name": { + "parent_session_id": { "type": "string", - "description": "New name; omitted or empty leaves it unchanged.", - "minLength": 1, - "maxLength": 255 + "description": "Parent session id for subagent (child) sessions; empty otherwise." }, - "description": { + "session_name": { "type": "string", - "description": "New description; omitted or empty leaves it unchanged.", - "minLength": 1, - "maxLength": 1024 + "description": "Session title; may be empty for untitled sessions." }, - "transport": { + "app_name": { "type": "string", - "description": "Transport protocol; when switching, also supply the matching fields (`command`/`args`/`env` for `stdio`, `url`/`headers` for `sse` / `streamable-http`); omitted or empty leaves it unchanged.", + "description": "Agent app that owns the session." + }, + "entry_kind": { + "type": "string", + "description": "Surface that created the session. One of:\n| Value | Meaning |\n| --- | --- |\n| `web` | Created from the web console |\n| `im` | Created from an IM client (IM bot / IM H5) |\n| `api` | Created via the public API |\n| `automation` | Created by an automation rule (unattended run) |\n| `subagent` | Child session spawned by a parent's agent_dispatch (audit label; at runtime it executes on the web tool surface) |", "enum": [ - "stdio", - "sse", - "streamable-http" + "web", + "im", + "api", + "automation", + "subagent" ] }, - "command": { + "person_id": { "type": "string", - "description": "Executable command (stdio transport)." + "description": "Creator person id." }, - "args": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Command arguments (`stdio` transport); replaces the whole list — pass `[]` to clear, omit to leave unchanged." + "team_id": { + "type": "integer", + "format": "int64", + "description": "Owning team id; 0 means no team is bound. Immutable after create." }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Environment variables (`stdio` transport); replaces the whole map, but a sensitive key sent back masked or as an empty string keeps its stored value; omit to leave unchanged." + "team_name": { + "type": "string", + "description": "Resolved team name; empty for unbound rows or deleted teams." }, - "url": { + "is_mine": { + "type": "boolean", + "description": "True when the caller created this session." + }, + "can_view": { + "type": "boolean", + "description": "True when the caller can view this session." + }, + "can_continue": { + "type": "boolean", + "description": "True when the caller can add a new turn to this session." + }, + "can_manage": { + "type": "boolean", + "description": "True when the caller may rename/archive/delete the session; personal sessions are creator-only, team sessions allow the creator, account admin, or team member." + }, + "can_fork": { + "type": "boolean", + "description": "True when the caller can fork this session." + }, + "access_source": { "type": "string", - "description": "Server URL (sse / streamable-http transport)." + "description": "How the caller received access to this session. Omitted when no access source is resolved. One of:\n| Value | Meaning |\n| --- | --- |\n| `owner` | Caller is the session creator (full access) |\n| `team_member` | Caller belongs to the session's bound team (full access) |\n| `manager` | Manager grant (reserved; never produced by the current version) |\n| `share_link` | Granted via a valid share link (view/fork only; cannot continue or manage) |\n| `participant` | Same-account non-member granted via a participable team session (view/continue/fork only) |", + "enum": [ + "owner", + "team_member", + "manager", + "share_link", + "participant" + ] }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "HTTP headers (`sse` / `streamable-http` transport); replaces the whole map, with the same masked/empty-value preservation as `env`; omit to leave unchanged." + "share_enabled": { + "type": "boolean", + "description": "True when the session's share link is active." }, - "connect_timeout": { + "share_version": { "type": "integer", - "description": "Connection timeout in seconds. 0 = default (10s)." + "format": "int64", + "description": "Revision of the share link; it increases when sharing is revoked." }, - "call_timeout": { + "shared_at": { "type": "integer", - "description": "Tool-call timeout in seconds. 0 = default (60s)." + "format": "int64", + "description": "Unix timestamp in milliseconds when sharing was last enabled; 0 if never shared." }, - "auth_mode": { + "shared_by": { + "type": "integer", + "format": "int64", + "description": "Person ID that most recently enabled sharing; 0 if never shared." + }, + "status": { "type": "string", - "description": "Authentication mode: shared (default), per_user_secret, or per_user_oauth." + "description": "Lifecycle status. One of: `enabled` (active), `deleted` (soft-deleted, no longer accessible).", + "enum": [ + "enabled", + "deleted" + ] }, - "secret_schema": { + "incognito": { + "type": "boolean", + "description": "True for incognito (non-persisted-memory) sessions." + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the session was created." + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the last session update." + }, + "template_staging_round_id": { "type": "string", - "description": "JSON secret schema; required when auth_mode=per_user_secret." + "description": "Current save→validate round id (template-assistant only); empty otherwise." + }, + "state": { + "type": "object", + "additionalProperties": true, + "description": "Raw session-state bag (session-scoped keys). Omitted when empty." + }, + "bound_environment": { + "$ref": "#/components/schemas/EnvironmentBinding" + }, + "context_resolved": { + "$ref": "#/components/schemas/ContextResolvedItem" + }, + "token_usage": { + "$ref": "#/components/schemas/SessionTokenUsage" + }, + "current_context_tokens": { + "type": "integer", + "format": "int64", + "description": "Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed." + }, + "context_window": { + "type": "integer", + "format": "int64", + "description": "The bound model's max context size in tokens. 0 means unknown." + }, + "archived_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when archived; 0 means not archived." + }, + "pinned_at": { + "type": "integer", + "format": "int64", + "description": "Caller's per-user pin time as a Unix timestamp in milliseconds; 0 means not pinned." + }, + "last_event_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the most recent assistant-side event." + }, + "is_running": { + "type": "boolean", + "description": "True when an agent turn is currently in flight for this session." }, - "oauth_metadata": { - "type": "string", - "description": "JSON OAuth metadata; reserved for per_user_oauth." + "has_unread": { + "type": "boolean", + "description": "True when there is assistant output the caller has not yet viewed." }, - "team_id": { - "type": [ - "integer", - "null" - ], - "description": "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.", - "format": "int64" + "current_turn_started_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the current or most recent round started; 0 if no round has started yet." }, - "environments": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - }, - "description": "Execution environments this server is callable from: `cloud` and/or BYOC runner environment IDs. Omit (null) to leave unchanged; send a list to set it — an empty list clears the restriction back to all environments." + "current_turn_active_ms": { + "type": "integer", + "format": "int64", + "description": "Active working duration in milliseconds for the current or most recent round, excluding time spent waiting on ask_user; resets to 0 at the start of each new round." }, - "allow_insecure_oauth_http": { - "type": [ - "boolean", - "null" - ], - "description": "Allow OAuth token exchange over plaintext HTTP. Omit to leave unchanged." + "current_turn_wait_ms": { + "type": "integer", + "format": "int64", + "description": "Accumulated ask_user human-wait duration in milliseconds for the current round; resets to 0 at the start of each new round." }, - "allow_insecure_tls_skip_verify": { - "type": [ - "boolean", - "null" - ], - "description": "Skip TLS certificate verification. Omit to leave unchanged." + "current_turn_tokens": { + "type": "integer", + "format": "int64", + "description": "Total tokens (input+output+reasoning) for the in-flight round across the parent and its subagents; only computed by session/get while the session is running, always 0 in session/list responses and when idle." } }, "required": [ - "server_id" + "session_id", + "session_name", + "app_name", + "person_id", + "team_id", + "is_mine", + "can_view", + "can_continue", + "can_manage", + "can_fork", + "share_enabled", + "share_version", + "shared_at", + "shared_by", + "status", + "incognito", + "created_at", + "updated_at", + "current_context_tokens", + "context_window", + "archived_at", + "pinned_at", + "is_running", + "has_unread", + "current_turn_started_at", + "current_turn_active_ms", + "current_turn_wait_ms", + "current_turn_tokens" ] }, - "ManualRunRuleResult": { + "SessionListRequest": { "type": "object", - "description": "Result of manually running an Automation rule outside its schedule.", + "description": "Filters for listing agent sessions. `all` visibility means the caller's own personal sessions plus accessible team sessions; account admins do not see other users' personal sessions.", "properties": { - "rule_id": { + "app_name": { "type": "string", - "description": "Rule ID that was run." + "description": "Agent app whose sessions to list. One of:\n| Value | Meaning |\n| --- | --- |\n| `ask-ai` | Ask AI assistant |\n| `support` | Customer-support agent |\n| `support-website` | Website support agent (exposed over A2A, not built into the console) |\n| `support-flashcat` | Flashcat-site support agent (exposed over A2A) |\n| `ai-sre` | The AI SRE main app |\n| `template-assistant` | Notification-template assistant (template editing/validation) |\n| `swe` | Internal benchmarking app (not customer-facing) |", + "enum": [ + "ask-ai", + "support", + "support-website", + "support-flashcat", + "ai-sre", + "template-assistant", + "swe" + ] }, - "trigger_kind": { + "p": { + "type": "integer", + "description": "Page number, 1-based.", + "default": 1, + "minimum": 1 + }, + "limit": { + "type": "integer", + "description": "Page size, 1–100.", + "minimum": 1, + "maximum": 100, + "default": 20 + }, + "orderby": { "type": "string", + "description": "Sort field: `created_at` by creation time, `updated_at` by last update; defaults to `updated_at` when omitted.", "enum": [ - "manual" - ], - "description": "Always manual for this operation." + "created_at", + "updated_at" + ] }, - "preflight": { - "$ref": "#/components/schemas/PreflightResult" + "asc": { + "type": "boolean", + "description": "Ascending order when true, descending when false. Only honored together with `orderby`; when `orderby` is omitted the sort is always `updated_at` descending." }, - "run": { - "$ref": "#/components/schemas/AutomationRunView" - } - }, - "required": [ - "rule_id", - "trigger_kind", - "preflight" - ] - }, - "PreflightResult": { - "type": "object", - "description": "Readiness checks computed before a manual run is allowed to start.", - "properties": { - "ok": { + "include_subagent_sessions": { "type": "boolean", - "description": "Whether all readiness checks passed. Always true in a response that reaches the caller — a failed preflight returns a 400/403 error instead of a payload with ok=false." + "description": "Include subagent-dispatched sessions in the list." }, - "checks": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Names of the readiness checks performed, in order. Current fixed set: rule_loaded, actor_authorized, app_allowed, runtime_scope_resolved, rule_config_valid." + "keyword": { + "type": "string", + "description": "Filter by session-name keyword.", + "maxLength": 64 }, "scope": { "type": "string", + "description": "Visibility scope: `all` (own personal + accessible team sessions), `personal`, or `team`; default `all`.", "enum": [ - "person", + "all", + "personal", "team" - ], - "description": "Resolved run scope for this run; mirrors the rule's run_scope. One of: `person` (personal rule, runs as its creator), `team` (team rule, runs under the owning team)." - }, - "owner_id": { - "type": "integer", - "format": "int64", - "description": "Rule owner person ID." - }, - "team_id": { - "type": "integer", - "format": "int64", - "description": "Rule's scope team ID; 0 means a personal rule." - }, - "app_name": { - "type": "string", - "description": "App the rule is scoped to. Currently always ai-sre; manual runs are only supported for that app." + ] }, - "warnings": { + "team_ids": { "type": "array", "items": { - "type": "string" + "type": "integer", + "format": "int64" }, - "description": "Non-fatal warnings surfaced during preflight. Omitted or empty when there are none." - } - }, - "required": [ - "ok", - "checks", - "scope", - "owner_id", - "team_id", - "app_name" - ] - }, - "ResponseEnvelope": { - "type": "object", - "description": "Standard response envelope used by every Flashduty public API. On success `data` contains the endpoint-specific payload and `error` is absent. On failure `error` is present and `data` is absent. `request_id` is always present and is also mirrored in the `Flashcat-Request-Id` response header.", - "properties": { - "request_id": { - "type": "string", - "description": "Unique ID for this request. Mirrored in the Flashcat-Request-Id header. Include it when reporting issues.", - "example": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" + "description": "Optional explicit team filter; intersects with `scope` and never expands access." }, - "error": { - "$ref": "#/components/schemas/DutyError" + "entry_kinds": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "web", + "im", + "api", + "automation" + ] + }, + "description": "Restrict to sessions produced by these surfaces; empty returns every kind." }, - "data": { - "description": "Endpoint-specific payload. See each operation's 200 response schema." - } - }, - "required": [ - "request_id" - ] - }, - "SessionDeleteRequest": { - "type": "object", - "description": "Session deletion by ID.", - "properties": { - "session_id": { + "status": { "type": "string", - "description": "Target session ID, from the list returned by `POST /safari/session/list`.", - "minLength": 1 + "description": "Archive bucket: active (default) returns un-archived, archived returns archived, all returns both.", + "enum": [ + "active", + "archived", + "all" + ] } }, "required": [ - "session_id" + "app_name" ] }, - "SessionExportRequest": { + "SessionListResponse": { "type": "object", - "description": "Export the full event transcript of one session as a streaming NDJSON body.", + "description": "A page of agent sessions.", "properties": { - "session_id": { - "type": "string", - "description": "Target session ID, from the list returned by `POST /safari/session/list`." + "total": { + "type": "integer", + "format": "int64", + "description": "Total number of sessions matching the filter (ignoring pagination)." + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionItem" + }, + "description": "The page of sessions." }, - "include_subagents": { + "suggest_init": { "type": "boolean", - "description": "When true, each subagent_dispatch line is followed by the child session's full event stream, bracketed by its own session_meta. Defaults to false." + "description": "Account-wide onboarding flag: true when the account has zero knowledge packs in any scope; not dependent on this call's filters." } }, "required": [ - "session_id" + "total", + "sessions", + "suggest_init" ] }, - "SessionGetRequest": { + "SessionTokenUsage": { "type": "object", - "description": "Fetch one session plus a backward-paged window of its most recent events.", + "description": "Cumulative session-level token rollup across all turns. The account-billing source of truth.", "properties": { - "session_id": { - "type": "string", - "description": "Target session ID, from the list returned by `POST /safari/session/list`.", - "minLength": 1 - }, - "share_token": { - "type": "string", - "description": "Share token for accessing a session through its share link. Omit it for normal account-authorized access.", - "maxLength": 512 + "input_tokens": { + "type": "integer", + "format": "int64", + "description": "Total prompt (input) tokens, including the cached portion." }, - "num_recent_events": { + "cached_tokens": { "type": "integer", - "description": "Legacy page size: number of most-recent events to return. Superseded by `limit` when both are set; 0 uses the server default (100).", - "minimum": 0, - "maximum": 1000 + "format": "int64", + "description": "Portion of input_tokens served from the prompt cache." }, - "limit": { + "output_tokens": { "type": "integer", - "description": "Page size for events; takes precedence over `num_recent_events`. 0 uses the server default (100).", - "minimum": 0, - "maximum": 1000 + "format": "int64", + "description": "Total generated (output) tokens." }, - "search_after_ctx": { + "reasoning_tokens": { + "type": "integer", + "format": "int64", + "description": "Total reasoning/thinking tokens." + } + }, + "required": [ + "input_tokens", + "cached_tokens", + "output_tokens", + "reasoning_tokens" + ] + }, + "SkillDeleteRequest": { + "type": "object", + "description": "Skill deletion by ID.", + "properties": { + "skill_id": { "type": "string", - "description": "Opaque keyset cursor from a previous response; pass it back to fetch the next older page.", - "maxLength": 4096 + "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." } }, "required": [ - "session_id" + "skill_id" ] }, - "SessionGetResponse": { + "SkillGetRequest": { "type": "object", - "description": "A session plus a backward-paged window of its events.", + "description": "Skill lookup by ID.", "properties": { - "session": { - "$ref": "#/components/schemas/SessionItem" - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventItem" - }, - "description": "Recent events, ascending by (created_at, event_id)." - }, - "has_more_older": { - "type": "boolean", - "description": "True when older events remain beyond this page." - }, - "search_after_ctx": { + "skill_id": { "type": "string", - "description": "Opaque keyset cursor; pass back as search_after_ctx to fetch the next older page. Omitted when has_more_older is false." - }, - "suggest_init": { - "type": "boolean", - "description": "Account-wide onboarding flag: true when the account has zero knowledge packs in any scope; not specific to this session." + "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." } }, "required": [ - "session", - "events", - "has_more_older", - "suggest_init" + "skill_id" ] }, - "SessionItem": { + "SkillItem": { "type": "object", - "description": "One agent session row.", + "description": "An AI SRE skill — a packaged SKILL.md bundle the agent can load.", "properties": { - "session_id": { + "skill_id": { "type": "string", - "description": "Session identifier." + "description": "Unique skill ID (prefix `skill_`)." }, - "parent_session_id": { - "type": "string", - "description": "Parent session id for subagent (child) sessions; empty otherwise." + "account_id": { + "type": "integer", + "description": "Owning account ID.", + "format": "int64" }, - "session_name": { - "type": "string", - "description": "Session title; may be empty for untitled sessions." + "team_id": { + "type": "integer", + "description": "Team scope: 0 = account-wide; >0 = the owning team.", + "format": "int64" }, - "app_name": { + "skill_name": { "type": "string", - "description": "Agent app that owns the session." + "description": "Skill name, unique within its scope (account-wide or within one team)." }, - "entry_kind": { + "description": { "type": "string", - "description": "Surface that created the session. One of:\n| Value | Meaning |\n| --- | --- |\n| `web` | Created from the web console |\n| `im` | Created from an IM client (IM bot / IM H5) |\n| `api` | Created via the public API |\n| `automation` | Created by an automation rule (unattended run) |\n| `subagent` | Child session spawned by a parent's agent_dispatch (audit label; at runtime it executes on the web tool surface) |", - "enum": [ - "web", - "im", - "api", - "automation", - "subagent" - ] + "description": "Human-readable description from the SKILL.md frontmatter." }, - "person_id": { + "description_en": { "type": "string", - "description": "Creator person id." - }, - "team_id": { - "type": "integer", - "format": "int64", - "description": "Owning team id; 0 means no team is bound. Immutable after create." + "description": "Optional English description. English-locale UI responses prefer this over `description`; the skill catalog also uses it as a stable selection signal when `description` is localized for display." }, - "team_name": { + "content": { "type": "string", - "description": "Resolved team name; empty for unbound rows or deleted teams." - }, - "is_mine": { - "type": "boolean", - "description": "True when the caller created this session." - }, - "can_view": { - "type": "boolean", - "description": "True when the caller can view this session." - }, - "can_continue": { - "type": "boolean", - "description": "True when the caller can add a new turn to this session." - }, - "can_manage": { - "type": "boolean", - "description": "True when the caller may rename/archive/delete the session; personal sessions are creator-only, team sessions allow the creator, account admin, or team member." - }, - "can_fork": { - "type": "boolean", - "description": "True when the caller can fork this session." + "description": "Full SKILL.md content. Omitted in list responses." }, - "access_source": { + "version": { "type": "string", - "description": "How the caller received access to this session. Omitted when no access source is resolved. One of:\n| Value | Meaning |\n| --- | --- |\n| `owner` | Caller is the session creator (full access) |\n| `team_member` | Caller belongs to the session's bound team (full access) |\n| `manager` | Manager grant (reserved; never produced by the current version) |\n| `share_link` | Granted via a valid share link (view/fork only; cannot continue or manage) |\n| `participant` | Same-account non-member granted via a participable team session (view/continue/fork only) |", - "enum": [ - "owner", - "team_member", - "manager", - "share_link", - "participant" - ] - }, - "share_enabled": { - "type": "boolean", - "description": "True when the session's share link is active." - }, - "share_version": { - "type": "integer", - "format": "int64", - "description": "Revision of the share link; it increases when sharing is revoked." - }, - "shared_at": { - "type": "integer", - "format": "int64", - "description": "Unix timestamp in milliseconds when sharing was last enabled; 0 if never shared." + "description": "Skill version from the frontmatter." }, - "shared_by": { - "type": "integer", - "format": "int64", - "description": "Person ID that most recently enabled sharing; 0 if never shared." + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Tags parsed from the frontmatter." }, - "status": { + "author": { "type": "string", - "description": "Lifecycle status. One of: `enabled` (active), `deleted` (soft-deleted, no longer accessible).", - "enum": [ - "enabled", - "deleted" - ] - }, - "incognito": { - "type": "boolean", - "description": "True for incognito (non-persisted-memory) sessions." - }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "Unix timestamp in milliseconds when the session was created." - }, - "updated_at": { - "type": "integer", - "format": "int64", - "description": "Unix timestamp in milliseconds of the last session update." + "description": "Skill author." }, - "template_staging_round_id": { + "license": { "type": "string", - "description": "Current save→validate round id (template-assistant only); empty otherwise." - }, - "state": { - "type": "object", - "additionalProperties": true, - "description": "Raw session-state bag (session-scoped keys). Omitted when empty." - }, - "bound_environment": { - "$ref": "#/components/schemas/EnvironmentBinding" + "description": "Skill license." }, - "context_resolved": { - "$ref": "#/components/schemas/ContextResolvedItem" + "tools": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Required tools (builtin or `mcp:server/tool`)." }, - "token_usage": { - "$ref": "#/components/schemas/SessionTokenUsage" + "venues": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Execution-environment kinds (EnvironmentKind strings, e.g. `byoc`) the skill is restricted to. Omitted when empty, which means the skill is available in all venues." }, - "current_context_tokens": { - "type": "integer", - "format": "int64", - "description": "Size in tokens of the LLM context window as of the most recent turn. 0 means no turn has completed." + "s3_key": { + "type": "string", + "description": "Object-storage key of the skill zip." }, - "context_window": { - "type": "integer", - "format": "int64", - "description": "The bound model's max context size in tokens. 0 means unknown." + "checksum": { + "type": "string", + "description": "SHA-256 checksum of the skill zip." }, - "archived_at": { + "status": { + "type": "string", + "description": "Skill status. Deleted skills are excluded from every API response, so only these two values are ever returned.", + "enum": [ + "enabled", + "disabled" + ] + }, + "created_by": { "type": "integer", - "format": "int64", - "description": "Unix timestamp in milliseconds when archived; 0 means not archived." + "description": "Member ID that created the skill.", + "format": "int64" }, - "pinned_at": { + "created_at": { "type": "integer", "format": "int64", - "description": "Caller's per-user pin time as a Unix timestamp in milliseconds; 0 means not pinned." + "description": "Creation time. Unix timestamp in milliseconds." }, - "last_event_at": { + "updated_at": { "type": "integer", "format": "int64", - "description": "Unix timestamp in milliseconds of the most recent assistant-side event." + "description": "Last update time. Unix timestamp in milliseconds." }, - "is_running": { + "can_edit": { "type": "boolean", - "description": "True when an agent turn is currently in flight for this session." + "description": "Whether the caller may edit this skill." }, - "has_unread": { - "type": "boolean", - "description": "True when there is assistant output the caller has not yet viewed." + "source_template_name": { + "type": "string", + "description": "Marketplace template this skill was installed from; empty for user-authored." }, - "current_turn_started_at": { - "type": "integer", - "format": "int64", - "description": "Unix timestamp in milliseconds when the current or most recent round started; 0 if no round has started yet." + "source_template_version": { + "type": "string", + "description": "Template version at install time." }, - "current_turn_active_ms": { - "type": "integer", - "format": "int64", - "description": "Active working duration in milliseconds for the current or most recent round, excluding time spent waiting on ask_user; resets to 0 at the start of each new round." + "update_available": { + "type": "boolean", + "description": "True when the marketplace has a newer template version." }, - "current_turn_wait_ms": { - "type": "integer", - "format": "int64", - "description": "Accumulated ask_user human-wait duration in milliseconds for the current round; resets to 0 at the start of each new round." + "is_modified": { + "type": "boolean", + "description": "True when a marketplace-sourced skill was edited locally (auto-update skips it)." }, - "current_turn_tokens": { - "type": "integer", - "format": "int64", - "description": "Total tokens (input+output+reasoning) for the in-flight round across the parent and its subagents; only computed by session/get while the session is running, always 0 in session/list responses and when idle." + "created": { + "type": "boolean", + "description": "Set only on install-from-session responses: true = fresh install, false = in-place update." } }, "required": [ - "session_id", - "session_name", - "app_name", - "person_id", + "skill_id", + "account_id", "team_id", - "is_mine", - "can_view", - "can_continue", - "can_manage", - "can_fork", - "share_enabled", - "share_version", - "shared_at", - "shared_by", + "skill_name", + "description", "status", - "incognito", + "created_by", "created_at", "updated_at", - "current_context_tokens", - "context_window", - "archived_at", - "pinned_at", - "is_running", - "has_unread", - "current_turn_started_at", - "current_turn_active_ms", - "current_turn_wait_ms", - "current_turn_tokens" + "can_edit", + "update_available", + "is_modified" ] }, - "SessionListRequest": { + "SkillListRequest": { "type": "object", - "description": "Filters for listing agent sessions. `all` visibility means the caller's own personal sessions plus accessible team sessions; account admins do not see other users' personal sessions.", + "description": "Pagination, search, and team filter for listing skills.", "properties": { - "app_name": { - "type": "string", - "description": "Agent app whose sessions to list. One of:\n| Value | Meaning |\n| --- | --- |\n| `ask-ai` | Ask AI assistant |\n| `support` | Customer-support agent |\n| `support-website` | Website support agent (exposed over A2A, not built into the console) |\n| `support-flashcat` | Flashcat-site support agent (exposed over A2A) |\n| `ai-sre` | The AI SRE main app |\n| `template-assistant` | Notification-template assistant (template editing/validation) |\n| `swe` | Internal benchmarking app (not customer-facing) |", - "enum": [ - "ask-ai", - "support", - "support-website", - "support-flashcat", - "ai-sre", - "template-assistant", - "swe" - ] - }, "p": { "type": "integer", "description": "Page number, 1-based.", - "default": 1, - "minimum": 1 + "default": 1 }, "limit": { "type": "integer", - "description": "Page size, 1–100.", - "minimum": 1, - "maximum": 100, + "description": "Page size.", "default": 20 }, - "orderby": { - "type": "string", - "description": "Sort field: `created_at` by creation time, `updated_at` by last update; defaults to `updated_at` when omitted.", - "enum": [ - "created_at", - "updated_at" - ] - }, - "asc": { - "type": "boolean", - "description": "Ascending order when true, descending when false. Only honored together with `orderby`; when `orderby` is omitted the sort is always `updated_at` descending." - }, - "include_subagent_sessions": { - "type": "boolean", - "description": "Include subagent-dispatched sessions in the list." - }, - "keyword": { - "type": "string", - "description": "Filter by session-name keyword.", - "maxLength": 64 - }, "scope": { "type": "string", - "description": "Visibility scope: `all` (own personal + accessible team sessions), `personal`, or `team`; default `all`.", + "description": "Restrict results to `all` (default), `account`-only (team_id=0), or `team`-only (excludes account-scoped rows). Overrides `include_account` when set.", "enum": [ "all", - "personal", + "account", "team" ] }, + "query": { + "type": "string", + "description": "Free-text search across skill name, description, English description, skill ID, marketplace source template name, and author.", + "maxLength": 128 + }, "team_ids": { "type": "array", "items": { "type": "integer", "format": "int64" }, - "description": "Optional explicit team filter; intersects with `scope` and never expands access." - }, - "entry_kinds": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "web", - "im", - "api", - "automation" - ] - }, - "description": "Restrict to sessions produced by these surfaces; empty returns every kind." + "description": "Filter to these team IDs; empty = the caller's visible set." }, - "status": { - "type": "string", - "description": "Archive bucket: active (default) returns un-archived, archived returns archived, all returns both.", - "enum": [ - "active", - "archived", - "all" - ] + "include_account": { + "type": [ + "boolean", + "null" + ], + "description": "Include account-scoped (team_id=0) rows. Defaults to true. Ignored when `scope` is `account` or `team`." } - }, - "required": [ - "app_name" - ] + } }, - "SessionListResponse": { + "SkillListResponse": { "type": "object", - "description": "A page of agent sessions.", + "description": "Paginated skill list.", "properties": { "total": { "type": "integer", - "format": "int64", - "description": "Total number of sessions matching the filter (ignoring pagination)." + "description": "Total number of matching skills.", + "format": "int64" }, - "sessions": { + "skills": { "type": "array", "items": { - "$ref": "#/components/schemas/SessionItem" + "$ref": "#/components/schemas/SkillItem" }, - "description": "The page of sessions." - }, - "suggest_init": { - "type": "boolean", - "description": "Account-wide onboarding flag: true when the account has zero knowledge packs in any scope; not dependent on this call's filters." + "description": "Skills on this page." } }, "required": [ "total", - "sessions", - "suggest_init" + "skills" ] }, - "SessionTokenUsage": { + "SkillStatusRequest": { "type": "object", - "description": "Cumulative session-level token rollup across all turns. The account-billing source of truth.", + "description": "Skill enable/disable by ID.", "properties": { - "input_tokens": { - "type": "integer", - "format": "int64", - "description": "Total prompt (input) tokens, including the cached portion." - }, - "cached_tokens": { - "type": "integer", - "format": "int64", - "description": "Portion of input_tokens served from the prompt cache." - }, - "output_tokens": { - "type": "integer", - "format": "int64", - "description": "Total generated (output) tokens." - }, - "reasoning_tokens": { - "type": "integer", - "format": "int64", - "description": "Total reasoning/thinking tokens." + "skill_id": { + "type": "string", + "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." } }, "required": [ - "input_tokens", - "cached_tokens", - "output_tokens", - "reasoning_tokens" + "skill_id" ] }, - "SkillDeleteRequest": { + "SkillUpdateRequest": { "type": "object", - "description": "Skill deletion by ID.", + "description": "Editable skill metadata.", "properties": { "skill_id": { "type": "string", "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." + }, + "description": { + "type": "string", + "description": "New description. Cannot contain `<` or `>`. Sending an empty string leaves the current value unchanged — there is no way to clear it via this field.", + "maxLength": 1024 + }, + "description_en": { + "type": [ + "string", + "null" + ], + "description": "New English description. Cannot contain `<` or `>`. Omit to leave unchanged; send an empty string to explicitly clear it.", + "maxLength": 1024 + }, + "team_id": { + "type": [ + "integer", + "null" + ], + "description": "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.", + "format": "int64" } }, "required": [ "skill_id" ] }, - "SkillGetRequest": { + "SkillUploadRequest": { "type": "object", - "description": "Skill lookup by ID.", + "description": "Multipart form for uploading a skill archive.", "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "Skill archive (.skill / .zip / .tar.gz / .tgz). Max 100MB; oversized files are rejected before the body is read." + }, + "team_id": { + "type": "integer", + "description": "Team scope for the created/upserted skill: 0 = account-wide. Ignored when replacing a specific skill via `skill_id`.", + "format": "int64" + }, + "replace": { + "type": "boolean", + "description": "When true, overwrite an existing skill instead of failing on a name collision — matched by `skill_id` if provided, otherwise by skill name." + }, "skill_id": { "type": "string", - "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." + "description": "Existing skill ID to target when replacing a specific skill (requires `replace=true`)." } }, "required": [ - "skill_id" + "file" ] }, - "SkillItem": { + "KnowledgePackItem": { "type": "object", - "description": "An AI SRE skill — a packaged SKILL.md bundle the agent can load.", + "description": "A knowledge pack — a versioned file tree staged into every AI SRE sandbox at session start. One pack exists per (account, scope, scope_id).", "properties": { - "skill_id": { + "pack_id": { "type": "string", - "description": "Unique skill ID (prefix `skill_`)." + "description": "Knowledge pack ID (`kpk_` prefix)." }, "account_id": { "type": "integer", - "description": "Owning account ID.", + "description": "Account that owns the pack.", "format": "int64" }, - "team_id": { + "scope": { + "type": "string", + "description": "Pack scope. `channel` is a legacy scope; new packs are `account` or `team`.", + "enum": [ + "account", + "team", + "channel" + ] + }, + "scope_id": { "type": "integer", - "description": "Team scope: 0 = account-wide; >0 = the owning team.", + "description": "Scope owner: the account ID for `account` scope, the team ID for `team` scope.", "format": "int64" }, - "skill_name": { - "type": "string", - "description": "Skill name, unique within its scope (account-wide or within one team)." - }, - "description": { + "team_name": { "type": "string", - "description": "Human-readable description from the SKILL.md frontmatter." + "description": "Display name of the owning team (team scope only). Omitted when empty (account scope, or the team name could not be resolved)." }, - "description_en": { - "type": "string", - "description": "Optional English description. English-locale UI responses prefer this over `description`; the skill catalog also uses it as a stable selection signal when `description` is localized for display." + "file_count": { + "type": "integer", + "description": "Number of files in the pack." }, - "content": { - "type": "string", - "description": "Full SKILL.md content. Omitted in list responses." + "total_bytes": { + "type": "integer", + "description": "Total size of all files in bytes.", + "format": "int64" }, "version": { - "type": "string", - "description": "Skill version from the frontmatter." + "type": "integer", + "description": "Pack version, incremented on every file change." }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Tags parsed from the frontmatter." + "duty_version": { + "type": "integer", + "description": "Pack version at which DUTY.md was last authored or re-affirmed. When `version` is greater, DUTY.md no longer reflects every file in the pack." }, - "author": { - "type": "string", - "description": "Skill author." + "created_by": { + "type": "integer", + "description": "Person ID of the member who created the pack.", + "format": "int64" }, - "license": { - "type": "string", - "description": "Skill license." + "created_at_ms": { + "type": "integer", + "description": "Unix timestamp in milliseconds when the pack was created.", + "format": "int64" }, - "tools": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Required tools (builtin or `mcp:server/tool`)." + "updated_at_ms": { + "type": "integer", + "description": "Unix timestamp in milliseconds when the pack was last modified.", + "format": "int64" }, - "venues": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Execution-environment kinds (EnvironmentKind strings, e.g. `byoc`) the skill is restricted to. Omitted when empty, which means the skill is available in all venues." + "can_edit": { + "type": "boolean", + "description": "Whether the caller can edit this pack." + } + }, + "required": [ + "pack_id", + "account_id", + "scope", + "scope_id", + "file_count", + "total_bytes", + "version", + "duty_version", + "created_by", + "created_at_ms", + "updated_at_ms", + "can_edit" + ] + }, + "KnowledgeFileItem": { + "type": "object", + "description": "Metadata of one file inside a knowledge pack. Content is fetched separately via file/get.", + "properties": { + "file_id": { + "type": "string", + "description": "File ID (`kfl_` prefix)." }, - "s3_key": { + "pack_id": { "type": "string", - "description": "Object-storage key of the skill zip." + "description": "ID of the knowledge pack that contains the file." }, - "checksum": { + "rel_path": { "type": "string", - "description": "SHA-256 checksum of the skill zip." + "description": "Path relative to the pack root, e.g. `runbooks/restart.md`." }, - "status": { + "content_type": { "type": "string", - "description": "Skill status. Deleted skills are excluded from every API response, so only these two values are ever returned.", - "enum": [ - "enabled", - "disabled" - ] + "description": "MIME type; inferred from the file extension when not set on upload." }, - "created_by": { + "size_bytes": { "type": "integer", - "description": "Member ID that created the skill.", + "description": "File size in bytes.", "format": "int64" }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "Creation time. Unix timestamp in milliseconds." + "checksum": { + "type": "string", + "description": "SHA-256 hex digest of the file content." }, - "updated_at": { + "updated_by": { "type": "integer", - "format": "int64", - "description": "Last update time. Unix timestamp in milliseconds." - }, - "can_edit": { - "type": "boolean", - "description": "Whether the caller may edit this skill." + "description": "Person ID of the member who last modified the file.", + "format": "int64" }, - "source_template_name": { + "updated_at_ms": { + "type": "integer", + "description": "Unix timestamp in milliseconds when the file was last modified.", + "format": "int64" + } + }, + "required": [ + "file_id", + "pack_id", + "rel_path", + "content_type", + "size_bytes", + "checksum", + "updated_by", + "updated_at_ms" + ] + }, + "KnowledgeWarning": { + "type": "object", + "description": "Non-blocking annotation returned by file uploads and deletions, e.g. references that point at a removed file.", + "properties": { + "code": { "type": "string", - "description": "Marketplace template this skill was installed from; empty for user-authored." + "description": "Warning code. One of: `unresolved_reference` (an @ref in the written file's content points to a file that does not exist in the pack; `ref` carries it), `still_referenced_by` (the deleted file is still @ref-referenced by other files in the pack; `refs` lists the referrers).", + "enum": [ + "unresolved_reference", + "still_referenced_by" + ] }, - "source_template_version": { + "ref": { "type": "string", - "description": "Template version at install time." - }, - "update_available": { - "type": "boolean", - "description": "True when the marketplace has a newer template version." - }, - "is_modified": { - "type": "boolean", - "description": "True when a marketplace-sourced skill was edited locally (auto-update skips it)." + "description": "Single reference related to the warning." }, - "created": { - "type": "boolean", - "description": "Set only on install-from-session responses: true = fresh install, false = in-place update." + "refs": { + "type": "array", + "description": "Multiple references related to the warning.", + "items": { + "type": "string" + } } }, "required": [ - "skill_id", - "account_id", - "team_id", - "skill_name", - "description", - "status", - "created_by", - "created_at", - "updated_at", - "can_edit", - "update_available", - "is_modified" + "code" ] }, - "SkillListRequest": { + "KnowledgeGetRequest": { "type": "object", - "description": "Pagination, search, and team filter for listing skills.", + "description": "No request fields — the account-scope pack is always targeted.", + "properties": {} + }, + "KnowledgePackListRequest": { + "type": "object", + "description": "Filter and pagination for the pack list.", "properties": { "p": { "type": "integer", - "description": "Page number, 1-based.", - "default": 1 + "description": "Page number, 1-based; returns all results when both `p` and `limit` are unset." }, "limit": { "type": "integer", - "description": "Page size.", - "default": 20 + "description": "Page size." }, "scope": { "type": "string", - "description": "Restrict results to `all` (default), `account`-only (team_id=0), or `team`-only (excludes account-scoped rows). Overrides `include_account` when set.", + "description": "Restrict to one scope; `all` (default) overrides `include_account`. One of: `all` (account scope plus visible team scopes), `account` (account-level packs only), `team` (team-level packs only, can be combined with `team_ids`).", "enum": [ "all", "account", @@ -6827,605 +8092,717 @@ }, "query": { "type": "string", - "description": "Free-text search across skill name, description, English description, skill ID, marketplace source template name, and author.", + "description": "Case-insensitive substring filter over pack ID, scope, scope ID/account ID, and team name.", "maxLength": 128 }, "team_ids": { "type": "array", + "description": "Restrict to these team IDs; for non-admins the list is intersected with their own teams.", "items": { "type": "integer", "format": "int64" - }, - "description": "Filter to these team IDs; empty = the caller's visible set." + } }, "include_account": { "type": [ "boolean", "null" ], - "description": "Include account-scoped (team_id=0) rows. Defaults to true. Ignored when `scope` is `account` or `team`." + "description": "Include the account-scope pack; defaults to true." } } }, - "SkillListResponse": { + "KnowledgePackEnsureRequest": { "type": "object", - "description": "Paginated skill list.", + "description": "Scope at which to ensure a knowledge pack exists.", "properties": { - "total": { + "scope": { + "type": "string", + "description": "Scope of the pack to ensure. One of: `account` (account-level pack; scope_id is forced to the caller's account ID and only account admins may create it; first creation seeds a default DUTY.md), `team` (team-level pack; the `scope_id` team ID is required and the caller must belong to that team).", + "enum": [ + "account", + "team" + ] + }, + "scope_id": { "type": "integer", - "description": "Total number of matching skills.", + "description": "Team ID; required for `team` scope, ignored for `account` scope.", "format": "int64" + } + }, + "required": [ + "scope" + ] + }, + "KnowledgePackUpdateRequest": { + "type": "object", + "description": "Move a knowledge pack to a different scope.", + "properties": { + "pack_id": { + "type": "string", + "description": "Knowledge pack ID to update." }, - "skills": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillItem" - }, - "description": "Skills on this page." + "scope": { + "type": [ + "string", + "null" + ], + "description": "Destination scope; omit for a no-op that returns the current pack.", + "enum": [ + "account", + "team" + ] + }, + "scope_id": { + "type": [ + "integer", + "null" + ], + "description": "Destination team ID; required when `scope` is `team`, set automatically for `account`.", + "format": "int64" } }, "required": [ - "total", - "skills" + "pack_id" ] }, - "SkillStatusRequest": { + "KnowledgePackDeleteRequest": { "type": "object", - "description": "Skill enable/disable by ID.", + "description": "Pack to delete.", "properties": { - "skill_id": { + "pack_id": { "type": "string", - "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." + "description": "Knowledge pack ID to delete." } }, "required": [ - "skill_id" + "pack_id" ] }, - "SkillUpdateRequest": { + "KnowledgePackDeleteResponse": { "type": "object", - "description": "Editable skill metadata.", + "description": "Deletion result.", "properties": { - "skill_id": { + "ok": { + "type": "boolean", + "description": "True when the pack was deleted." + } + }, + "required": [ + "ok" + ] + }, + "KnowledgeFileListRequest": { + "type": "object", + "description": "Which pack's files to list.", + "properties": { + "pack_id": { "type": "string", - "description": "Target skill ID, from the list returned by `POST /safari/skill/list`." + "description": "Knowledge pack ID; defaults to the caller's account-scope pack." }, - "description": { + "p": { + "type": "integer", + "description": "Page number, 1-based. Accepted but currently ignored — the response always contains the full file list." + }, + "limit": { + "type": "integer", + "description": "Page size. Accepted but currently ignored — the response always contains the full file list." + } + } + }, + "KnowledgeFileGetRequest": { + "type": "object", + "description": "Which file to fetch.", + "properties": { + "pack_id": { "type": "string", - "description": "New description. Cannot contain `<` or `>`. Sending an empty string leaves the current value unchanged — there is no way to clear it via this field.", - "maxLength": 1024 + "description": "Knowledge pack ID; defaults to the caller's account-scope pack." }, - "description_en": { - "type": [ - "string", - "null" - ], - "description": "New English description. Cannot contain `<` or `>`. Omit to leave unchanged; send an empty string to explicitly clear it.", - "maxLength": 1024 + "rel_path": { + "type": "string", + "description": "Path of the file relative to the pack root." + } + }, + "required": [ + "rel_path" + ] + }, + "KnowledgeFilePutRequest": { + "type": "object", + "description": "File to create or overwrite. The body is base64 text in `content_b64`, not a multipart upload.", + "properties": { + "pack_id": { + "type": "string", + "description": "Knowledge pack ID; defaults to the caller's account-scope pack." }, - "team_id": { - "type": [ - "integer", - "null" - ], - "description": "Reassign team scope: 0 = account-wide; >0 = team. Omit to leave unchanged.", + "rel_path": { + "type": "string", + "description": "Destination path relative to the pack root; existing files are overwritten." + }, + "content_b64": { + "type": "string", + "description": "Base64-encoded file content; must decode to valid UTF-8 text (binary is rejected). Per-file limit 1 MiB." + }, + "content_type": { + "type": "string", + "description": "MIME type; inferred from the file extension when omitted." + } + }, + "required": [ + "rel_path" + ] + }, + "KnowledgeGetResponse": { + "type": "object", + "description": "Account-scope pack metadata plus its file list.", + "properties": { + "pack": { + "$ref": "#/components/schemas/KnowledgePackItem" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "description": "Array of files in this knowledge pack; empty array when the pack has no files." + } + }, + "required": [ + "pack", + "files" + ] + }, + "KnowledgePackListResponse": { + "type": "object", + "description": "Visible packs and the total after filtering.", + "properties": { + "packs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgePackItem" + }, + "description": "Array of visible knowledge packs after filtering (current page), used with `total` for pagination." + }, + "total": { + "type": "integer", + "description": "Total number of packs after filtering, before pagination.", + "format": "int64" + } + }, + "required": [ + "packs", + "total" + ] + }, + "KnowledgeFileListResponse": { + "type": "object", + "description": "Files in the pack.", + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "description": "Array of files in the specified knowledge pack; empty array when the pack has no files." + }, + "total": { + "type": "integer", + "description": "Total number of files in the pack.", "format": "int64" } }, "required": [ - "skill_id" + "files", + "total" + ] + }, + "KnowledgeFileGetResponse": { + "type": "object", + "description": "File metadata plus its base64-encoded content.", + "properties": { + "file": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "content_b64": { + "type": "string", + "description": "Base64-encoded file content; decodes to UTF-8 text." + } + }, + "required": [ + "file", + "content_b64" + ] + }, + "KnowledgeFilePutResponse": { + "type": "object", + "description": "The written file plus any non-blocking warnings.", + "properties": { + "file": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "warnings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeWarning" + }, + "description": "Non-blocking warnings after a successful write; `code=unresolved_reference` means an @ref in the file content points to a file that does not exist in the pack. Absent when there are no warnings (omitempty)." + } + }, + "required": [ + "file" ] }, - "SkillUploadRequest": { + "KnowledgeFileDeleteRequest": { "type": "object", - "description": "Multipart form for uploading a skill archive.", + "description": "File to remove from a knowledge pack.", "properties": { - "file": { + "pack_id": { "type": "string", - "format": "binary", - "description": "Skill archive (.skill / .zip / .tar.gz / .tgz). Max 100MB; oversized files are rejected before the body is read." + "description": "Knowledge pack ID; defaults to the caller's account-scope pack." }, - "team_id": { - "type": "integer", - "description": "Team scope for the created/upserted skill: 0 = account-wide. Ignored when replacing a specific skill via `skill_id`.", - "format": "int64" + "rel_path": { + "type": "string", + "description": "Path of the file relative to the pack root." }, - "replace": { + "force": { "type": "boolean", - "description": "When true, overwrite an existing skill instead of failing on a name collision — matched by `skill_id` if provided, otherwise by skill name." - }, - "skill_id": { - "type": "string", - "description": "Existing skill ID to target when replacing a specific skill (requires `replace=true`)." + "description": "Delete even when other pack files reference this file; the referrers are then returned as warnings instead of blocking the delete." } }, "required": [ - "file" + "rel_path" ] }, - "KnowledgePackItem": { + "KnowledgeFileDeleteResponse": { "type": "object", - "description": "A knowledge pack — a versioned file tree staged into every AI SRE sandbox at session start. One pack exists per (account, scope, scope_id).", + "description": "Deletion result; empty unless warnings were raised.", "properties": { - "pack_id": { + "warnings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeWarning" + }, + "description": "Non-blocking warnings after deletion; `code=still_referenced_by` means the (force-)deleted file is still @ref-referenced by other files in the pack (`refs` lists the referrers). Absent when there are no warnings (omitempty)." + } + } + }, + "ArtifactIdRequest": { + "type": "object", + "description": "Identify one gallery artifact.", + "properties": { + "artifact_id": { "type": "string", - "description": "Knowledge pack ID (`kpk_` prefix)." - }, - "account_id": { - "type": "integer", - "description": "Account that owns the pack.", - "format": "int64" - }, + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactListRequest": { + "type": "object", + "description": "Filter and paginate the artifact gallery.", + "properties": { "scope": { "type": "string", - "description": "Pack scope. `channel` is a legacy scope; new packs are `account` or `team`.", "enum": [ - "account", - "team", - "channel" - ] + "all", + "personal", + "team" + ], + "default": "all", + "description": "Visibility scope. `all` (default) lists the caller's own personal artifacts plus artifacts of every team the caller belongs to; `personal` lists only the caller's own; `team` lists only team-owned artifacts of the caller's teams." }, - "scope_id": { - "type": "integer", - "description": "Scope owner: the account ID for `account` scope, the team ID for `team` scope.", - "format": "int64" + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "Restrict to artifacts owned by these team IDs, intersected with the caller's visibility — teams the caller does not belong to silently return nothing." }, - "team_name": { + "query": { "type": "string", - "description": "Display name of the owning team (team scope only). Omitted when empty (account scope, or the team name could not be resolved)." - }, - "file_count": { - "type": "integer", - "description": "Number of files in the pack." - }, - "total_bytes": { - "type": "integer", - "description": "Total size of all files in bytes.", - "format": "int64" + "description": "Case-insensitive substring match on the artifact title." }, - "version": { + "page": { "type": "integer", - "description": "Pack version, incremented on every file change." + "default": 1, + "minimum": 1, + "description": "Page number, 1-based." }, - "duty_version": { + "limit": { "type": "integer", - "description": "Pack version at which DUTY.md was last authored or re-affirmed. When `version` is greater, DUTY.md no longer reflects every file in the pack." + "default": 20, + "maximum": 100, + "description": "Page size. Defaults to 20; capped at 100." }, - "created_by": { - "type": "integer", - "description": "Person ID of the member who created the pack.", - "format": "int64" + "orderby": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ], + "description": "Sort field: `created_at` or `updated_at`. Empty means `updated_at` descending." }, - "created_at_ms": { - "type": "integer", - "description": "Unix timestamp in milliseconds when the pack was created.", - "format": "int64" + "asc": { + "type": "boolean", + "description": "Sort ascending when true, descending when false. Applies only when `orderby` is set." + } + } + }, + "ArtifactListResponse": { + "type": "object", + "description": "A page of gallery artifacts.", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublishedArtifactItem" + }, + "description": "Artifacts on this page." }, - "updated_at_ms": { + "total": { "type": "integer", - "description": "Unix timestamp in milliseconds when the pack was last modified.", - "format": "int64" - }, - "can_edit": { - "type": "boolean", - "description": "Whether the caller can edit this pack." + "format": "int64", + "description": "Total number of artifacts matching the filter across all pages." } - }, - "required": [ - "pack_id", - "account_id", - "scope", - "scope_id", - "file_count", - "total_bytes", - "version", - "duty_version", - "created_by", - "created_at_ms", - "updated_at_ms", - "can_edit" - ] + } }, - "KnowledgeFileItem": { + "PublishedArtifactItem": { "type": "object", - "description": "Metadata of one file inside a knowledge pack. Content is fetched separately via file/get.", + "description": "One published artifact in the gallery. Time fields are Unix timestamps in milliseconds.", "properties": { - "file_id": { + "artifact_id": { "type": "string", - "description": "File ID (`kfl_` prefix)." + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." }, - "pack_id": { + "title": { "type": "string", - "description": "ID of the knowledge pack that contains the file." + "description": "Display title in the gallery." }, - "rel_path": { - "type": "string", - "description": "Path relative to the pack root, e.g. `runbooks/restart.md`." + "team_id": { + "type": "integer", + "format": "int64", + "description": "Owning team ID. `0` means a personal artifact (creator-only management); a positive value means team-owned." }, - "content_type": { + "team_name": { "type": "string", - "description": "MIME type; inferred from the file extension when not set on upload." + "description": "Owning team's display name. Omitted for personal artifacts." }, - "size_bytes": { + "person_id": { "type": "integer", - "description": "File size in bytes.", - "format": "int64" + "format": "int64", + "description": "Person (member) ID of the creator." }, - "checksum": { + "creator_name": { "type": "string", - "description": "SHA-256 hex digest of the file content." + "description": "Creator's display name." }, - "updated_by": { - "type": "integer", - "description": "Person ID of the member who last modified the file.", - "format": "int64" + "is_mine": { + "type": "boolean", + "description": "Whether the caller is the creator." }, - "updated_at_ms": { - "type": "integer", - "description": "Unix timestamp in milliseconds when the file was last modified.", - "format": "int64" - } - }, - "required": [ - "file_id", - "pack_id", - "rel_path", - "content_type", - "size_bytes", - "checksum", - "updated_by", - "updated_at_ms" - ] - }, - "KnowledgeWarning": { - "type": "object", - "description": "Non-blocking annotation returned by file uploads and deletions, e.g. references that point at a removed file.", - "properties": { - "code": { + "can_edit": { + "type": "boolean", + "description": "Whether the caller may manage this artifact (rename, transfer, delete, share): the creator, any member of the owning team, or a manager of the source session." + }, + "session_id": { "type": "string", - "description": "Warning code. One of: `unresolved_reference` (an @ref in the written file's content points to a file that does not exist in the pack; `ref` carries it), `still_referenced_by` (the deleted file is still @ref-referenced by other files in the pack; `refs` lists the referrers).", - "enum": [ - "unresolved_reference", - "still_referenced_by" - ] + "description": "Source session ID (`sess_` prefix) that produced the file." }, - "ref": { + "session_title": { "type": "string", - "description": "Single reference related to the warning." + "description": "Source session's title. Omitted when the session has been deleted." }, - "refs": { - "type": "array", - "description": "Multiple references related to the warning.", - "items": { - "type": "string" - } - } - }, - "required": [ - "code" - ] - }, - "KnowledgeGetRequest": { - "type": "object", - "description": "No request fields — the account-scope pack is always targeted.", - "properties": {} - }, - "KnowledgePackListRequest": { - "type": "object", - "description": "Filter and pagination for the pack list.", - "properties": { - "p": { - "type": "integer", - "description": "Page number, 1-based; returns all results when both `p` and `limit` are unset." + "file_id": { + "type": "string", + "description": "Presented-file ID (`pf_` prefix) currently holding the artifact's bytes. Changes on every republish." }, - "limit": { + "name": { + "type": "string", + "description": "Underlying file name including extension." + }, + "size": { "type": "integer", - "description": "Page size." + "format": "int64", + "description": "File size in bytes." }, - "scope": { + "content_type": { "type": "string", - "description": "Restrict to one scope; `all` (default) overrides `include_account`. One of: `all` (account scope plus visible team scopes), `account` (account-level packs only), `team` (team-level packs only, can be combined with `team_ids`).", - "enum": [ - "all", - "account", - "team" - ] + "description": "MIME type of the file." + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the artifact was published." }, - "query": { - "type": "string", - "description": "Case-insensitive substring filter over pack ID, scope, scope ID/account ID, and team name.", - "maxLength": 128 + "updated_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds when the artifact was last updated (rename, transfer, or republish)." }, - "team_ids": { - "type": "array", - "description": "Restrict to these team IDs; for non-admins the list is intersected with their own teams.", - "items": { - "type": "integer", - "format": "int64" - } + "share_enabled": { + "type": "boolean", + "description": "Whether anonymous public sharing is on. Omitted when false." }, - "include_account": { - "type": [ - "boolean", - "null" - ], - "description": "Include the account-scope pack; defaults to true." - } - } - }, - "KnowledgePackEnsureRequest": { - "type": "object", - "description": "Scope at which to ensure a knowledge pack exists.", - "properties": { - "scope": { + "public_url": { "type": "string", - "description": "Scope of the pack to ensure. One of: `account` (account-level pack; scope_id is forced to the caller's account ID and only account admins may create it; first creation seeds a default DUTY.md), `team` (team-level pack; the `scope_id` team ID is required and the caller must belong to that team).", - "enum": [ - "account", - "team" - ] + "description": "Anonymous public link — a console `/share/artifact/` page served entirely from CDN. Present only while shared; anyone with the link can view it, no login required." }, - "scope_id": { + "shared_by": { "type": "integer", - "description": "Team ID; required for `team` scope, ignored for `account` scope.", - "format": "int64" + "format": "int64", + "description": "Person ID of the member who enabled sharing. Present only while shared." + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the last share enable or snapshot sync. Present only while shared." + }, + "share_file_id": { + "type": "string", + "description": "Presented-file ID the public snapshot was materialized from. When `share_enabled` is true and `file_id` differs from `share_file_id`, the public snapshot is stale — call `/safari/artifact/gallery/share/sync` to refresh it." } }, "required": [ - "scope" + "artifact_id", + "title", + "team_id", + "person_id", + "creator_name", + "is_mine", + "can_edit", + "session_id", + "file_id", + "name", + "size", + "content_type", + "created_at", + "updated_at" ] }, - "KnowledgePackUpdateRequest": { + "ArtifactUpdateRequest": { "type": "object", - "description": "Move a knowledge pack to a different scope.", + "description": "Partial update of a gallery artifact. Only provided fields change.", "properties": { - "pack_id": { + "artifact_id": { "type": "string", - "description": "Knowledge pack ID to update." + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." }, - "scope": { + "title": { "type": [ "string", "null" ], - "description": "Destination scope; omit for a no-op that returns the current pack.", - "enum": [ - "account", - "team" - ] + "description": "New title. Trimmed; must be non-empty when provided. Omit to leave unchanged." }, - "scope_id": { + "team_id": { "type": [ "integer", "null" ], - "description": "Destination team ID; required when `scope` is `team`, set automatically for `account`.", - "format": "int64" - } - }, - "required": [ - "pack_id" - ] - }, - "KnowledgePackDeleteRequest": { - "type": "object", - "description": "Pack to delete.", - "properties": { - "pack_id": { - "type": "string", - "description": "Knowledge pack ID to delete." - } - }, - "required": [ - "pack_id" - ] - }, - "KnowledgePackDeleteResponse": { - "type": "object", - "description": "Deletion result.", - "properties": { - "ok": { - "type": "boolean", - "description": "True when the pack was deleted." + "format": "int64", + "description": "Transfer target scope. `0` moves the artifact to personal scope (only the creator can manage it); a positive value moves it to a team the caller must belong to. Omit to leave unchanged." } }, "required": [ - "ok" + "artifact_id" ] }, - "KnowledgeFileListRequest": { - "type": "object", - "description": "Which pack's files to list.", - "properties": { - "pack_id": { - "type": "string", - "description": "Knowledge pack ID; defaults to the caller's account-scope pack." - }, - "p": { - "type": "integer", - "description": "Page number, 1-based. Accepted but currently ignored — the response always contains the full file list." - }, - "limit": { - "type": "integer", - "description": "Page size. Accepted but currently ignored — the response always contains the full file list." - } - } - }, - "KnowledgeFileGetRequest": { + "ArtifactPublishFromFileRequest": { "type": "object", - "description": "Which file to fetch.", + "description": "Publish a session-produced file to the artifact gallery.", "properties": { - "pack_id": { + "file_id": { "type": "string", - "description": "Knowledge pack ID; defaults to the caller's account-scope pack." + "description": "Presented-file ID (`pf_` prefix) of a file produced in a session, as shown on the file card in chat." }, - "rel_path": { + "title": { "type": "string", - "description": "Path of the file relative to the pack root." + "description": "Gallery display title. Trimmed; must be non-empty." } }, "required": [ - "rel_path" + "file_id", + "title" ] }, - "KnowledgeFilePutRequest": { + "ArtifactPublishResponse": { "type": "object", - "description": "File to create or overwrite. The body is base64 text in `content_b64`, not a multipart upload.", + "description": "Result of publishing a file to the gallery.", "properties": { - "pack_id": { - "type": "string", - "description": "Knowledge pack ID; defaults to the caller's account-scope pack." - }, - "rel_path": { + "artifact_id": { "type": "string", - "description": "Destination path relative to the pack root; existing files are overwritten." + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." }, - "content_b64": { + "title": { "type": "string", - "description": "Base64-encoded file content; must decode to valid UTF-8 text (binary is rejected). Per-file limit 1 MiB." + "description": "Gallery display title." }, - "content_type": { + "gallery_path": { "type": "string", - "description": "MIME type; inferred from the file extension when omitted." + "description": "Console-relative path of the artifact page: `/ai-sre/artifacts/`." } }, "required": [ - "rel_path" + "artifact_id", + "title", + "gallery_path" ] }, - "KnowledgeGetResponse": { + "ArtifactFileStateRequest": { "type": "object", - "description": "Account-scope pack metadata plus its file list.", + "description": "Probe publish state for a batch of presented files.", "properties": { - "pack": { - "$ref": "#/components/schemas/KnowledgePackItem" - }, - "files": { + "file_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "type": "string" }, - "description": "Array of files in this knowledge pack; empty array when the pack has no files." + "maxItems": 50, + "minItems": 1, + "description": "Presented-file IDs (`pf_` prefix) to probe. At most 50 per call; duplicates and empty strings are ignored." } }, "required": [ - "pack", - "files" + "file_ids" ] }, - "KnowledgePackListResponse": { + "ArtifactFileStateItem": { "type": "object", - "description": "Visible packs and the total after filtering.", + "description": "Live publish state of one presented file. Returned only for files that have a live published artifact.", "properties": { - "packs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgePackItem" - }, - "description": "Array of visible knowledge packs after filtering (current page), used with `total` for pagination." + "file_id": { + "type": "string", + "description": "Echoes the requested presented-file ID." }, - "total": { - "type": "integer", - "description": "Total number of packs after filtering, before pagination.", - "format": "int64" + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." + }, + "title": { + "type": "string", + "description": "Gallery display title of the published artifact." + }, + "gallery_path": { + "type": "string", + "description": "Console-relative path of the artifact page: `/ai-sre/artifacts/`." } }, "required": [ - "packs", - "total" + "file_id", + "artifact_id", + "title", + "gallery_path" ] }, - "KnowledgeFileListResponse": { + "ArtifactFileStateResponse": { "type": "object", - "description": "Files in the pack.", + "description": "Publish states keyed by file.", "properties": { - "files": { + "items": { "type": "array", "items": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "$ref": "#/components/schemas/ArtifactFileStateItem" }, - "description": "Array of files in the specified knowledge pack; empty array when the pack has no files." - }, - "total": { - "type": "integer", - "description": "Total number of files in the pack.", - "format": "int64" + "description": "One entry per requested file that has a live published artifact; files without one are omitted." } }, "required": [ - "files", - "total" + "items" ] }, - "KnowledgeFileGetResponse": { + "ArtifactShareState": { "type": "object", - "description": "File metadata plus its base64-encoded content.", + "description": "Public-share state of an artifact.", "properties": { - "file": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "artifact_id": { + "type": "string", + "description": "Artifact ID (`art_` prefix). Also the key of the public-share link." }, - "content_b64": { + "share_enabled": { + "type": "boolean", + "description": "Always `true` in this response." + }, + "public_url": { "type": "string", - "description": "Base64-encoded file content; decodes to UTF-8 text." + "description": "Anonymous public link served entirely from CDN. Anyone with the link can view the content, no login required." + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "Person ID of the member who enabled sharing." + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "Unix timestamp in milliseconds of the last share enable or snapshot sync." } }, "required": [ - "file", - "content_b64" + "artifact_id", + "share_enabled", + "public_url", + "shared_by", + "shared_at" ] }, - "KnowledgeFilePutResponse": { + "ArtifactSignRequest": { "type": "object", - "description": "The written file plus any non-blocking warnings.", + "description": "Request signed download/preview URLs for a presented file.", "properties": { - "file": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "file_id": { + "type": "string", + "description": "Presented-file ID (`pf_` prefix) to sign." }, - "warnings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgeWarning" - }, - "description": "Non-blocking warnings after a successful write; `code=unresolved_reference` means an @ref in the file content points to a file that does not exist in the pack. Absent when there are no warnings (omitempty)." + "share_token": { + "type": "string", + "description": "Optional session share-link token. Needed only when the caller reaches the file through a shared session link rather than account membership." } }, "required": [ - "file" + "file_id" ] }, - "KnowledgeFileDeleteRequest": { + "SignedURLs": { "type": "object", - "description": "File to remove from a knowledge pack.", + "description": "Short-lived signed URLs for downloading or previewing a file.", "properties": { - "pack_id": { + "download_url": { "type": "string", - "description": "Knowledge pack ID; defaults to the caller's account-scope pack." + "description": "Relative URL (`/safari/artifact/stream?...`) that serves the file as an attachment. Prepend the API base `https://api.flashcat.cloud` before use; expires with `expires_in`." }, - "rel_path": { + "preview_url": { "type": "string", - "description": "Path of the file relative to the pack root." + "description": "Same as `download_url` but served inline for browser preview." }, - "force": { - "type": "boolean", - "description": "Delete even when other pack files reference this file; the referrers are then returned as warnings instead of blocking the delete." + "expires_in": { + "type": "integer", + "description": "Validity of both URLs in seconds (300)." + }, + "name": { + "type": "string", + "description": "File name including extension." + }, + "size": { + "type": "integer", + "format": "int64", + "description": "File size in bytes." + }, + "content_type": { + "type": "string", + "description": "MIME type of the file." } }, "required": [ - "rel_path" + "download_url", + "preview_url", + "expires_in", + "name", + "size", + "content_type" ] - }, - "KnowledgeFileDeleteResponse": { - "type": "object", - "description": "Deletion result; empty unless warnings were raised.", - "properties": { - "warnings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgeWarning" - }, - "description": "Non-blocking warnings after deletion; `code=still_referenced_by` means the (force-)deleted file is still @ref-referenced by other files in the pack (`refs` lists the referrers). Absent when there are no warnings (omitempty)." - } - } } } } diff --git a/api-reference/safari.openapi.zh.json b/api-reference/safari.openapi.zh.json index 738ed23a..f7e1fc84 100644 --- a/api-reference/safari.openapi.zh.json +++ b/api-reference/safari.openapi.zh.json @@ -38,6 +38,10 @@ }, { "name": "AI SRE/知识" + }, + { + "name": "AI SRE/产物", + "description": "AI SRE 产物库 —— 发布、浏览并公开分享智能体生成的文件。" } ], "paths": { @@ -4023,167 +4027,1122 @@ } } } - } - }, - "components": { - "securitySchemes": { - "AppKeyAuth": { - "type": "apiKey", - "in": "query", - "name": "app_key", - "description": "App key issued from the Flashduty console. Required on every public API call. Keep it secret — it grants the same access as the owning account." - }, - "AutomationTriggerBearerAuth": { - "type": "http", - "scheme": "bearer", - "description": "自动化 HTTP POST 触发器生成的一次性 Bearer Token。不要把它当作 app_key 使用。" - } }, - "responses": { - "BadRequest": { - "description": "Invalid request — usually a missing or malformed parameter.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "missingParameter": { - "summary": "Missing required parameter", - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "InvalidParameter", - "message": "The specified parameter skill_id is not valid." - } - } - } - } + "/safari/artifact/gallery/list": { + "post": { + "operationId": "artifact-read-list", + "summary": "查询产物列表", + "description": "分页查询调用者可见的产物列表,支持按标题搜索。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] } - } - }, - "Unauthorized": { - "description": "Missing or invalid app_key.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "missingAppKey": { - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "Unauthorized", - "message": "You are unauthorized." - } - } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- `scope` 可选 `all`(默认 —— 调用者自己的个人产物 + 其所属全部团队的产物)、`personal`(仅自己创建的)、`team`(仅所属团队的团队产物)。\n- `team_ids` 可在可见范围内进一步限定团队 —— 调用者不属于的团队不会有任何返回。\n- 默认按 `updated_at` 倒序;设置 `orderby` 为 `created_at` 可更换排序字段,`asc: true` 改为升序。\n- 使用 `app_key` 调用时,可见范围按 key 所属成员的身份计算 —— 返回该成员的个人产物及其所在团队的产物。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-list", + "metadata": { + "sidebarTitle": "查询产物列表" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactListRequest" + }, + "example": { + "scope": "all", + "page": 1, + "limit": 20 } } } - } - }, - "Forbidden": { - "description": "The app_key is valid but lacks permission for this operation.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "accessDenied": { - "value": { + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactListResponse" + } + } + } + ] + }, + "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "AccessDenied", - "message": "Access Denied." + "data": { + "items": [ + { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + }, + { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu", + "title": "rum_recommendation", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 3790925372131, + "creator_name": "牛伟利", + "is_mine": false, + "can_edit": true, + "session_id": "sess_QXUC9C2PYWP5EE7vETR3UD", + "session_title": "生成 RUM 文档多格式", + "file_id": "pf_Jnc4E5YBcWLGzunB4ntP9s", + "name": "rum_recommendation.pdf", + "size": 137107, + "content_type": "application/pdf", + "created_at": 1785229432881, + "updated_at": 1785741344822 + } + ], + "total": 17 } } } } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" } } - }, - "TooManyRequests": { - "description": "Rate limit hit. Either the global API limit or a per-account limit.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "rateLimited": { - "value": { - "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "RequestTooFrequently", - "message": "Request too frequently." - } - } + } + }, + "/safari/artifact/gallery/get": { + "post": { + "operationId": "artifact-read-get", + "summary": "获取产物详情", + "description": "根据产物 ID 获取产物详情。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 可见范围为整个账号:任何有效 `app_key` 都能读取账号内任意产物。`is_mine` 和 `can_edit` 相对于 key 所属成员计算。\n- 当 `share_enabled` 为 true 且 `file_id` 与 `share_file_id` 不一致时,公开快照已过期 —— 调用 `/safari/artifact/gallery/share/sync` 刷新。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-get", + "metadata": { + "sidebarTitle": "获取产物详情" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" } } } - } - }, - "ServerError": { - "description": "Unexpected server-side error. Include the request_id when reporting.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse" - }, - "examples": { - "internal": { - "value": { + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", - "error": { - "code": "InternalError", - "message": "We encountered an internal error, and it has been reported. Please try again later." + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785747910219, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" } } } } - } - } - } - }, - "schemas": { - "A2AAgentCreateRequest": { - "type": "object", - "description": "新建 A2A 智能体的注册参数。", - "properties": { - "agent_name": { - "type": "string", - "description": "智能体显示名称。", - "maxLength": 128 - }, - "instructions": { - "type": "string", - "description": "远程智能体的自然语言指令:Markdown 文档,可带 `summary` frontmatter,正文非空,最大 50 KiB(51200 字节)。必填 —— 已弃用的 `description` 字段仍保留以兼容旧客户端,若两者同时传入则必须与 `instructions` 完全一致。", - "maxLength": 51200 }, - "card_url": { - "type": "string", - "description": "远程智能体卡片的 URL。必须是 host 非空的绝对 `http` 或 `https` URL;可达性由执行环境在运行时验证,创建时不检查。" + "400": { + "$ref": "#/components/responses/BadRequest" }, - "auth_type": { - "type": "string", - "description": "访问远程智能体的认证类型:`none`(省略时默认)、`api_key` 或 `bearer`。", - "enum": [ - "none", - "api_key", - "bearer" - ] + "401": { + "$ref": "#/components/responses/Unauthorized" }, - "auth_config": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "认证配置键值,例如 API key 或 bearer token。键名疑似凭据(包含 KEY、SECRET、TOKEN、PASSWORD 等)的值在响应中会被脱敏。" + "429": { + "$ref": "#/components/responses/TooManyRequests" }, - "streaming": { - "type": "boolean", + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/file-state": { + "post": { + "operationId": "artifact-read-get-file-state", + "summary": "查询文件发布状态", + "description": "批量查询文件是否已发布为产物。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 单次最多传入 50 个 `file_ids`;重复和空字符串会被忽略。\n- 没有已发布产物的文件不会出现在 `items` 中 —— 按回显的 `file_id` 对应结果。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-get-file-state", + "metadata": { + "sidebarTitle": "查询文件发布状态" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactFileStateRequest" + }, + "example": { + "file_ids": [ + "pf_SdhEA5fbZJGnHzwrNJMMSB", + "pf_9kLm2nQpRsTuVwXyZaBcDe" + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactFileStateResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/sign": { + "post": { + "operationId": "artifact-read-sign", + "summary": "签发文件访问链接", + "description": "为文件签发短期有效的下载/预览链接。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **1,000 次/分钟**;**50 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 两个链接在 `expires_in` 秒(300 秒)后过期,需重新签发获取新链接。\n- 返回的是相对链接 —— 使用前拼接 `https://api.flashcat.cloud`,然后以 `GET /safari/artifact/stream` 访问。\n- 签名令牌与调用账号及 app_key 所属成员身份绑定 —— 链接泄露给其他账号或成员不可用。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-sign", + "metadata": { + "sidebarTitle": "签发文件访问链接" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactSignRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/SignedURLs" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "download_url": "/safari/artifact/stream?mode=download&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "preview_url": "/safari/artifact/stream?mode=preview&t=cGZfU2RoRUE1ZmJaSkduSHp3ck5KTU1TQnxzZXNzX1ZDYlZQWnJxOVlveUJ1OHNOQ21xVXl8MjQ1MTAwMjc1MTEzMXwyNDc2NDQ0MjEyMTMxfDB8MHwxNzg4NTI5NjI2NTU0.hPQHab0MBNbnrHYwc6VwDJbGonIamfWUr0yeSmLHYAs", + "expires_in": 300, + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/stream": { + "get": { + "operationId": "artifact-read-stream", + "summary": "下载或预览文件", + "description": "使用签名令牌下载或预览文件内容。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **100 次/分钟**;**10 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 成功响应有两种形式:302 重定向到短期有效的对象存储预签名 URL(存储在 S3 兼容存储中的文件),或 200 二进制流(托管在自托管 runner 上的文件)。请跟随重定向。\n- 响应携带 `Cache-Control: private, no-store` —— 网关不会缓存。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-read-stream", + "metadata": { + "sidebarTitle": "下载或预览文件" + } + }, + "parameters": [ + { + "name": "t", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "由 `POST /safari/artifact/sign` 签发的令牌。与调用账号及成员绑定,有效期 5 分钟。" + }, + { + "name": "mode", + "in": "query", + "required": false, + "schema": { + "type": "string", + "enum": [ + "download", + "preview" + ], + "default": "download" + }, + "description": "`download`(默认)以附件形式返回文件;`preview` 以 inline 方式返回供浏览器预览。其他取值按 `download` 处理。" + } + ], + "responses": { + "200": { + "description": "文件内容流。当文件托管在自托管 runner 上时由服务端代理返回,Content-Disposition 随 `mode` 变化。", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "302": { + "description": "当文件存储在 S3 兼容存储中时,302 重定向到短期有效的预签名 URL。请跟随 `Location` 头,无响应体。" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/publish-from-file": { + "post": { + "operationId": "artifact-write-publish", + "summary": "发布文件为产物", + "description": "将会话中生成的文件发布到产物库。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 允许的文件类型:HTML/Markdown、图片、PDF、文本/数据/代码文件以及 zip/tar 压缩包(按扩展名判断)。超过 16 MiB 的文件会被拒绝。\n- 发布按(来源会话 + 工作区路径)做 upsert —— 重复发布同一文件会替换产物内容并使用新的 `file_id`,已有的公开快照会因此过期,需执行分享同步刷新。\n- 产物继承来源会话的个人/团队空间;如需调整,发布后调用 `/safari/artifact/gallery/update` 转移。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-publish", + "metadata": { + "sidebarTitle": "发布文件为产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactPublishFromFileRequest" + }, + "example": { + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "title": "SK 海力士 2026 Q2 财报深度分析" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactPublishResponse" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析", + "gallery_path": "/ai-sre/artifacts/art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/update": { + "post": { + "operationId": "artifact-write-update", + "summary": "更新产物", + "description": "更新产物标题,或在个人/团队空间之间转移产物。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 仅更新传入的字段 —— 不传 `title` 或 `team_id` 则对应字段保持不变。\n- `team_id: 0` 表示转为个人产物(仅创建者可管理);传入大于 0 的 `team_id` 时,调用者(`app_key` 调用时为 key 所属成员)必须是该团队成员。\n- 返回更新后的完整产物。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-update", + "metadata": { + "sidebarTitle": "更新产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactUpdateRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/PublishedArtifactItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "title": "SK 海力士 2026 Q2 财报深度分析(终稿)", + "team_id": 2477033058131, + "team_name": "研发团队", + "person_id": 2476444212131, + "creator_name": "yushuangyu", + "is_mine": true, + "can_edit": true, + "session_id": "sess_VCbVPZrq9YoyBu8sNCmqUy", + "session_title": "分析海力士财报并发布报告", + "file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB", + "name": "sk-hynix-q2-2026-report.html", + "size": 18996, + "content_type": "text/html", + "created_at": 1785293373899, + "updated_at": 1785829000000, + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 3790925372131, + "shared_at": 1785747928665, + "share_file_id": "pf_SdhEA5fbZJGnHzwrNJMMSB" + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/delete": { + "post": { + "operationId": "artifact-write-delete", + "summary": "从产物库移除产物", + "description": "将产物从产物库移除;来源文件仍保留在会话中。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 该操作只是从产物库移除,不会删除文件本身 —— 来源文件仍保留在会话中,可再次发布。\n- 如果产物已开启公开分享,公开内容会在同一操作中被销毁,公开链接立即失效。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-delete", + "metadata": { + "sidebarTitle": "从产物库移除产物" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_CWQzDU2PXSRJvKDhQbuMKu" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/enable": { + "post": { + "operationId": "artifact-write-share-enable", + "summary": "开启公开分享", + "description": "开启产物的匿名公开分享并返回公开链接。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 公开链接是匿名的 —— 任何拿到链接的人都可以查看内容,链接也可能被继续转发。内容会被复制到公开 CDN 对象上,访问链接时不经过本 API。\n- 幂等:对已分享的产物重复开启会原样返回已有链接;撤销后重新开启也会得到同一个链接 —— 链接以产物 ID 为标识。\n- 超过 16 MiB 的产物无法分享,调用返回 `InvalidParameter`。\n- 分享是内容快照:之后重新发布产物不会自动更新公开内容,需调用 `/safari/artifact/gallery/share/sync` 同步。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-enable", + "metadata": { + "sidebarTitle": "开启公开分享" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785747928665 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/sync": { + "post": { + "operationId": "artifact-write-share-sync", + "summary": "更新公开分享快照", + "description": "将公开分享的内容快照更新为产物最新内容。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 未开启分享时调用返回 `InvalidParameter` —— 请先开启分享。\n- 公开链接不变,仅更新快照内容和 `shared_at`。\n- 可通过产物详情中 `share_file_id != file_id` 判断快照是否过期,再决定是否同步。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-sync", + "metadata": { + "sidebarTitle": "更新公开分享快照" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/ArtifactShareState" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg", + "share_enabled": true, + "public_url": "https://console.flashcat.cloud/share/artifact/art_VnfrWic8UbB9q3EfYR4Gmg", + "shared_by": 2476444212131, + "shared_at": 1785829900000 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/safari/artifact/gallery/share/revoke": { + "post": { + "operationId": "artifact-write-share-revoke", + "summary": "撤销公开分享", + "description": "撤销产物的公开分享,公开链接立即失效。", + "tags": [ + "AI SRE/产物" + ], + "security": [ + { + "AppKeyAuth": [] + } + ], + "x-mint": { + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | 每个 `app_key` **300 次/分钟**;**20 次/秒** |\n| 权限要求 | 无 —— 持有有效的 `app_key` 即可调用 |\n\n## 使用说明\n\n- 公开 CDN 对象会被删除,链接随即失效;产物未开启分享时该操作为空操作。\n- 之后重新开启分享会返回同一个 `public_url` —— 链接以产物 ID 为标识,撤销并不能用于更换链接。\n- 每次调用都会记录到账户审计日志。\n", + "href": "/zh/api-reference/ai-sre/artifacts/artifact-write-share-revoke", + "metadata": { + "sidebarTitle": "撤销公开分享" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArtifactIdRequest" + }, + "example": { + "artifact_id": "art_VnfrWic8UbB9q3EfYR4Gmg" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/ResponseEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "null", + "description": "Always null on success." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": null + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + } + }, + "components": { + "securitySchemes": { + "AppKeyAuth": { + "type": "apiKey", + "in": "query", + "name": "app_key", + "description": "App key issued from the Flashduty console. Required on every public API call. Keep it secret — it grants the same access as the owning account." + }, + "AutomationTriggerBearerAuth": { + "type": "http", + "scheme": "bearer", + "description": "自动化 HTTP POST 触发器生成的一次性 Bearer Token。不要把它当作 app_key 使用。" + } + }, + "responses": { + "BadRequest": { + "description": "Invalid request — usually a missing or malformed parameter.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "missingParameter": { + "summary": "Missing required parameter", + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InvalidParameter", + "message": "The specified parameter skill_id is not valid." + } + } + } + } + } + } + }, + "Unauthorized": { + "description": "Missing or invalid app_key.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "missingAppKey": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "Unauthorized", + "message": "You are unauthorized." + } + } + } + } + } + } + }, + "Forbidden": { + "description": "The app_key is valid but lacks permission for this operation.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "accessDenied": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "AccessDenied", + "message": "Access Denied." + } + } + } + } + } + } + }, + "TooManyRequests": { + "description": "Rate limit hit. Either the global API limit or a per-account limit.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "rateLimited": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "RequestTooFrequently", + "message": "Request too frequently." + } + } + } + } + } + } + }, + "ServerError": { + "description": "Unexpected server-side error. Include the request_id when reporting.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + }, + "examples": { + "internal": { + "value": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "InternalError", + "message": "We encountered an internal error, and it has been reported. Please try again later." + } + } + } + } + } + } + } + }, + "schemas": { + "A2AAgentCreateRequest": { + "type": "object", + "description": "新建 A2A 智能体的注册参数。", + "properties": { + "agent_name": { + "type": "string", + "description": "智能体显示名称。", + "maxLength": 128 + }, + "instructions": { + "type": "string", + "description": "远程智能体的自然语言指令:Markdown 文档,可带 `summary` frontmatter,正文非空,最大 50 KiB(51200 字节)。必填 —— 已弃用的 `description` 字段仍保留以兼容旧客户端,若两者同时传入则必须与 `instructions` 完全一致。", + "maxLength": 51200 + }, + "card_url": { + "type": "string", + "description": "远程智能体卡片的 URL。必须是 host 非空的绝对 `http` 或 `https` URL;可达性由执行环境在运行时验证,创建时不检查。" + }, + "auth_type": { + "type": "string", + "description": "访问远程智能体的认证类型:`none`(省略时默认)、`api_key` 或 `bearer`。", + "enum": [ + "none", + "api_key", + "bearer" + ] + }, + "auth_config": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "认证配置键值,例如 API key 或 bearer token。键名疑似凭据(包含 KEY、SECRET、TOKEN、PASSWORD 等)的值在响应中会被脱敏。" + }, + "streaming": { + "type": "boolean", "description": "远程智能体是否支持流式响应。" }, "team_id": { @@ -5737,19 +6696,239 @@ }, "server_name": { "type": "string", - "description": "MCP 服务器名称,在其作用域(账户范围或单个团队)内唯一,大小写不敏感。" + "description": "MCP 服务器名称,在其作用域(账户范围或单个团队)内唯一,大小写不敏感。" + }, + "description": { + "type": "string", + "description": "服务器描述。" + }, + "ai_description": { + "type": "string", + "description": "LLM 生成的描述,存在时优先于 `description`;尚未生成时省略。" + }, + "transport": { + "type": "string", + "description": "传输协议。可选值:`stdio`(标准输入输出,本地子进程方式)、`sse`(独立 SSE,旧版 MCP 传输协议)、`streamable-http`(较新的 HTTP 流式传输协议)。", + "enum": [ + "stdio", + "sse", + "streamable-http" + ] + }, + "command": { + "type": "string", + "description": "可执行命令(仅 stdio 传输)。" + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "命令参数(stdio 传输)。" + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "环境变量(stdio 传输);密钥值已脱敏。" + }, + "url": { + "type": "string", + "description": "服务器 URL(sse / streamable-http 传输)。" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "HTTP 头(sse / streamable-http);密钥值已脱敏。" + }, + "proxy_url": { + "type": "string", + "description": "访问服务器使用的出站代理 URL。" + }, + "status": { + "type": "string", + "description": "服务器状态。", + "enum": [ + "enabled", + "disabled" + ] + }, + "connect_timeout": { + "type": "integer", + "description": "连接超时,单位秒(0 表示默认 10 秒)。" + }, + "call_timeout": { + "type": "integer", + "description": "工具调用超时,单位秒(0 表示默认 60 秒)。" + }, + "allow_insecure_oauth_http": { + "type": "boolean", + "description": "允许该服务器的 OAuth token 交换走明文 HTTP;仅供测试使用。为 false 时省略。" + }, + "allow_insecure_tls_skip_verify": { + "type": "boolean", + "description": "连接该服务器时跳过 TLS 证书校验;仅供测试使用。为 false 时省略。" + }, + "auth_mode": { + "type": "string", + "description": "认证模式。可选值:`shared`(共享凭据:配置资源时保存一份静态凭据,账户内所有调用方共用;缺省及空字符串等同于此值)、`per_user_secret`(按人密钥:每位使用者按 `secret_schema` 各自保存密钥,运行时按人注入)、`per_user_oauth`(按人 OAuth:每位使用者各自完成 OAuth 授权,首次调用时按需完成发现与注册)。", + "enum": [ + "shared", + "per_user_secret", + "per_user_oauth" + ] + }, + "secret_schema": { + "type": "string", + "description": "JSON 编码的密钥 schema(per_user_secret 模式)。" + }, + "oauth_metadata": { + "type": "string", + "description": "JSON 编码的 OAuth 元数据(per_user_oauth 模式)。" + }, + "source_template_name": { + "type": "string", + "description": "该连接器安装来源的市场模板名称;自建为空。" + }, + "created_by": { + "type": "integer", + "description": "创建该服务器的成员 ID。", + "format": "int64" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "创建时间,Unix 毫秒时间戳。" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "最近更新时间,Unix 毫秒时间戳。" + } + }, + "required": [ + "server_id", + "account_id", + "team_id", + "can_edit", + "server_name", + "description", + "transport", + "status", + "connect_timeout", + "call_timeout", + "created_by", + "created_at", + "updated_at", + "environments" + ] + }, + "MCPServerListRequest": { + "type": "object", + "description": "MCP 服务器列表的分页、范围与搜索过滤条件。", + "properties": { + "p": { + "type": "integer", + "description": "页码,从 1 开始。", + "default": 1 + }, + "limit": { + "type": "integer", + "description": "每页数量。", + "default": 20 + }, + "scope": { + "type": "string", + "description": "结果范围:account 仅返回账户级记录,team 仅返回调用者可见的团队级记录,省略则默认为 all(返回两者,仍受 team_ids/include_account 约束)。", + "enum": [ + "all", + "account", + "team" + ] + }, + "query": { + "type": "string", + "maxLength": 128, + "description": "对名称、描述、AI 生成描述、服务器 ID、传输协议、URL、命令、市场模板名称进行不区分大小写的子串搜索。" + }, + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "按团队 ID 过滤;为空则使用调用者可见范围。" + }, + "include_account": { + "type": [ + "boolean", + "null" + ], + "description": "是否包含账户级(team_id=0)记录,默认 true。" + } + } + }, + "MCPServerListResponse": { + "type": "object", + "description": "分页的 MCP 服务器列表。", + "properties": { + "total": { + "type": "integer", + "description": "匹配的服务器总数。", + "format": "int64" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MCPServerItem" + }, + "description": "当前页的 MCP 服务器。" + } + }, + "required": [ + "total", + "servers" + ] + }, + "MCPServerStatusRequest": { + "type": "object", + "description": "按 ID 启用/禁用 MCP 服务器。", + "properties": { + "server_id": { + "type": "string", + "description": "目标 MCP 服务器 ID,取自 `POST /safari/mcp/server/list` 返回的列表。" + } + }, + "required": [ + "server_id" + ] + }, + "MCPServerUpdateRequest": { + "type": "object", + "description": "MCP 服务器的部分更新;省略字段表示不变。", + "properties": { + "server_id": { + "type": "string", + "description": "目标 MCP 服务器 ID,取自 `POST /safari/mcp/server/list` 返回的列表。" + }, + "server_name": { + "type": "string", + "description": "新名称;省略或留空则不修改。", + "minLength": 1, + "maxLength": 255 }, "description": { "type": "string", - "description": "服务器描述。" - }, - "ai_description": { - "type": "string", - "description": "LLM 生成的描述,存在时优先于 `description`;尚未生成时省略。" + "description": "新描述;省略或留空则不修改。", + "minLength": 1, + "maxLength": 1024 }, "transport": { "type": "string", - "description": "传输协议。可选值:`stdio`(标准输入输出,本地子进程方式)、`sse`(独立 SSE,旧版 MCP 传输协议)、`streamable-http`(较新的 HTTP 流式传输协议)。", + "description": "传输协议;切换时应一并提供对应字段(`stdio` 用 `command`/`args`/`env`,`sse` / `streamable-http` 用 `url`/`headers`);省略或留空则不修改。", "enum": [ "stdio", "sse", @@ -5758,21 +6937,21 @@ }, "command": { "type": "string", - "description": "可执行命令(仅 stdio 传输)。" + "description": "可执行命令(stdio 传输)。" }, "args": { "type": "array", "items": { "type": "string" }, - "description": "命令参数(stdio 传输)。" + "description": "命令参数(`stdio` 传输);整体替换:传 `[]` 可清空,省略则不修改。" }, "env": { "type": "object", "additionalProperties": { "type": "string" }, - "description": "环境变量(stdio 传输);密钥值已脱敏。" + "description": "环境变量(`stdio` 传输);整体替换,但敏感键回传掩码值或空字符串时保留服务端存储的原值;省略则不修改。" }, "url": { "type": "string", @@ -5783,1042 +6962,1128 @@ "additionalProperties": { "type": "string" }, - "description": "HTTP 头(sse / streamable-http);密钥值已脱敏。" - }, - "proxy_url": { - "type": "string", - "description": "访问服务器使用的出站代理 URL。" - }, - "status": { - "type": "string", - "description": "服务器状态。", - "enum": [ - "enabled", - "disabled" - ] + "description": "HTTP 头(`sse` / `streamable-http` 传输);整体替换,掩码/空值保留规则同 `env`;省略则不修改。" }, "connect_timeout": { "type": "integer", - "description": "连接超时,单位秒(0 表示默认 10 秒)。" + "description": "连接超时,单位秒。0 表示默认(10 秒)。" }, "call_timeout": { "type": "integer", - "description": "工具调用超时,单位秒(0 表示默认 60 秒)。" - }, - "allow_insecure_oauth_http": { - "type": "boolean", - "description": "允许该服务器的 OAuth token 交换走明文 HTTP;仅供测试使用。为 false 时省略。" - }, - "allow_insecure_tls_skip_verify": { - "type": "boolean", - "description": "连接该服务器时跳过 TLS 证书校验;仅供测试使用。为 false 时省略。" + "description": "工具调用超时,单位秒。0 表示默认(60 秒)。" }, "auth_mode": { "type": "string", - "description": "认证模式。可选值:`shared`(共享凭据:配置资源时保存一份静态凭据,账户内所有调用方共用;缺省及空字符串等同于此值)、`per_user_secret`(按人密钥:每位使用者按 `secret_schema` 各自保存密钥,运行时按人注入)、`per_user_oauth`(按人 OAuth:每位使用者各自完成 OAuth 授权,首次调用时按需完成发现与注册)。", - "enum": [ - "shared", - "per_user_secret", - "per_user_oauth" - ] + "description": "认证模式:shared(默认)、per_user_secret 或 per_user_oauth。" }, "secret_schema": { "type": "string", - "description": "JSON 编码的密钥 schema(per_user_secret 模式)。" + "description": "JSON 密钥 schema;auth_mode=per_user_secret 时必填。" }, "oauth_metadata": { "type": "string", - "description": "JSON 编码的 OAuth 元数据(per_user_oauth 模式)。" + "description": "JSON OAuth 元数据;为 per_user_oauth 预留。" }, - "source_template_name": { + "team_id": { + "type": [ + "integer", + "null" + ], + "description": "重新分配团队范围:0 表示账户级;>0 表示团队。省略则不变。", + "format": "int64" + }, + "environments": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + }, + "description": "该服务器可运行的执行环境:`cloud` 和/或 BYOC Runner 的环境 ID 列表。省略(null)表示保持不变;传入列表即整体设置——空列表表示清除限制、恢复为所有环境。" + }, + "allow_insecure_oauth_http": { + "type": [ + "boolean", + "null" + ], + "description": "是否允许 OAuth 令牌交换使用明文 HTTP。省略表示不变。" + }, + "allow_insecure_tls_skip_verify": { + "type": [ + "boolean", + "null" + ], + "description": "是否跳过 TLS 证书校验。省略表示不变。" + } + }, + "required": [ + "server_id" + ] + }, + "ManualRunRuleResult": { + "type": "object", + "description": "手动运行一次自动化规则(跳过其计划触发时间)的结果。", + "properties": { + "rule_id": { "type": "string", - "description": "该连接器安装来源的市场模板名称;自建为空。" + "description": "被运行的规则 ID。" }, - "created_by": { - "type": "integer", - "description": "创建该服务器的成员 ID。", - "format": "int64" + "trigger_kind": { + "type": "string", + "enum": [ + "manual" + ], + "description": "该操作固定为 manual。" }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "创建时间,Unix 毫秒时间戳。" + "preflight": { + "$ref": "#/components/schemas/PreflightResult" }, - "updated_at": { - "type": "integer", - "format": "int64", - "description": "最近更新时间,Unix 毫秒时间戳。" + "run": { + "$ref": "#/components/schemas/AutomationRunView" } }, "required": [ - "server_id", - "account_id", - "team_id", - "can_edit", - "server_name", - "description", - "transport", - "status", - "connect_timeout", - "call_timeout", - "created_by", - "created_at", - "updated_at", - "environments" + "rule_id", + "trigger_kind", + "preflight" ] }, - "MCPServerListRequest": { + "PreflightResult": { "type": "object", - "description": "MCP 服务器列表的分页、范围与搜索过滤条件。", + "description": "在允许发起手动运行前计算出的就绪检查结果。", "properties": { - "p": { - "type": "integer", - "description": "页码,从 1 开始。", - "default": 1 + "ok": { + "type": "boolean", + "description": "全部就绪检查是否通过。凡是能返回给调用者的响应中该值恒为 true——预检失败会直接返回 400/403 错误,而不是 ok=false 的响应体。" }, - "limit": { - "type": "integer", - "description": "每页数量。", - "default": 20 + "checks": { + "type": "array", + "items": { + "type": "string" + }, + "description": "按执行顺序列出的就绪检查项名称。当前固定为:rule_loaded、actor_authorized、app_allowed、runtime_scope_resolved、rule_config_valid。" }, "scope": { "type": "string", - "description": "结果范围:account 仅返回账户级记录,team 仅返回调用者可见的团队级记录,省略则默认为 all(返回两者,仍受 team_ids/include_account 约束)。", "enum": [ - "all", - "account", + "person", "team" - ] + ], + "description": "本次运行解析出的作用域,与规则的 run_scope 一致。可选值:`person`(个人规则,以创建者身份运行)、`team`(团队规则,归属团队运行)。" }, - "query": { + "owner_id": { + "type": "integer", + "format": "int64", + "description": "规则所有者 person ID。" + }, + "team_id": { + "type": "integer", + "format": "int64", + "description": "规则的作用域团队 ID;0 表示个人规则。" + }, + "app_name": { "type": "string", - "maxLength": 128, - "description": "对名称、描述、AI 生成描述、服务器 ID、传输协议、URL、命令、市场模板名称进行不区分大小写的子串搜索。" + "description": "规则所属的 App。当前始终为 ai-sre;手动运行目前仅支持该 App。" }, - "team_ids": { + "warnings": { "type": "array", "items": { - "type": "integer", - "format": "int64" + "type": "string" }, - "description": "按团队 ID 过滤;为空则使用调用者可见范围。" + "description": "预检过程中给出的非致命警告。没有警告时省略或为空数组。" + } + }, + "required": [ + "ok", + "checks", + "scope", + "owner_id", + "team_id", + "app_name" + ] + }, + "ResponseEnvelope": { + "type": "object", + "description": "Standard response envelope used by every Flashduty public API. On success `data` contains the endpoint-specific payload and `error` is absent. On failure `error` is present and `data` is absent. `request_id` is always present and is also mirrored in the `Flashcat-Request-Id` response header.", + "properties": { + "request_id": { + "type": "string", + "description": "本次请求的唯一 ID,与 Flashcat-Request-Id 响应头一致。反馈问题时请携带该 ID。", + "example": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" }, - "include_account": { - "type": [ - "boolean", - "null" - ], - "description": "是否包含账户级(team_id=0)记录,默认 true。" + "error": { + "$ref": "#/components/schemas/DutyError" + }, + "data": { + "description": "端点专属数据负载,具体结构见各操作 200 响应中的 schema。" } - } + }, + "required": [ + "request_id" + ] + }, + "SessionDeleteRequest": { + "type": "object", + "description": "按 ID 删除会话。", + "properties": { + "session_id": { + "type": "string", + "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。", + "minLength": 1 + } + }, + "required": [ + "session_id" + ] + }, + "SessionExportRequest": { + "type": "object", + "description": "以流式 NDJSON 导出单个会话的完整事件记录。", + "properties": { + "session_id": { + "type": "string", + "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。" + }, + "include_subagents": { + "type": "boolean", + "description": "为 true 时,每条 subagent_dispatch 行后会跟随子会话的完整事件流,并以其自身的 session_meta 包裹。默认 false。" + } + }, + "required": [ + "session_id" + ] }, - "MCPServerListResponse": { + "SessionGetRequest": { "type": "object", - "description": "分页的 MCP 服务器列表。", + "description": "查询单个会话,并返回其最近事件的一页(向更早方向分页)。", "properties": { - "total": { + "session_id": { + "type": "string", + "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。", + "minLength": 1 + }, + "share_token": { + "type": "string", + "description": "通过分享链接访问会话时使用的分享令牌;常规账户授权访问时省略。", + "maxLength": 512 + }, + "num_recent_events": { "type": "integer", - "description": "匹配的服务器总数。", - "format": "int64" + "description": "旧版每页数量:返回的最近事件数。与 `limit` 同时设置时以 `limit` 为准;0 使用服务端默认值(100)。", + "minimum": 0, + "maximum": 1000 }, - "servers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MCPServerItem" - }, - "description": "当前页的 MCP 服务器。" + "limit": { + "type": "integer", + "description": "事件每页数量;优先于 `num_recent_events`。0 使用服务端默认值(100)。", + "minimum": 0, + "maximum": 1000 + }, + "search_after_ctx": { + "type": "string", + "description": "上一次响应返回的不透明游标;回传以获取更早的一页。", + "maxLength": 4096 } }, "required": [ - "total", - "servers" + "session_id" ] }, - "MCPServerStatusRequest": { + "SessionGetResponse": { "type": "object", - "description": "按 ID 启用/禁用 MCP 服务器。", + "description": "一个会话及其事件的一页(向更早方向分页)。", "properties": { - "server_id": { + "session": { + "$ref": "#/components/schemas/SessionItem" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventItem" + }, + "description": "最近事件,按 (created_at, event_id) 升序排列。" + }, + "has_more_older": { + "type": "boolean", + "description": "当本页之外仍有更早的事件时为 true。" + }, + "search_after_ctx": { "type": "string", - "description": "目标 MCP 服务器 ID,取自 `POST /safari/mcp/server/list` 返回的列表。" + "description": "不透明游标;作为 search_after_ctx 回传以获取更早的一页。has_more_older 为 false 时省略。" + }, + "suggest_init": { + "type": "boolean", + "description": "账户级引导标志:当账户在任何范围内都没有知识包时为 true;并非该会话独有的属性。" } }, "required": [ - "server_id" + "session", + "events", + "has_more_older", + "suggest_init" ] }, - "MCPServerUpdateRequest": { + "SessionItem": { "type": "object", - "description": "MCP 服务器的部分更新;省略字段表示不变。", + "description": "单条智能体会话记录。", "properties": { - "server_id": { + "session_id": { "type": "string", - "description": "目标 MCP 服务器 ID,取自 `POST /safari/mcp/server/list` 返回的列表。" + "description": "会话标识。" }, - "server_name": { + "parent_session_id": { "type": "string", - "description": "新名称;省略或留空则不修改。", - "minLength": 1, - "maxLength": 255 + "description": "子智能体(子)会话的父会话 ID;否则为空。" }, - "description": { + "session_name": { "type": "string", - "description": "新描述;省略或留空则不修改。", - "minLength": 1, - "maxLength": 1024 + "description": "会话标题;未命名会话可能为空。" }, - "transport": { + "app_name": { "type": "string", - "description": "传输协议;切换时应一并提供对应字段(`stdio` 用 `command`/`args`/`env`,`sse` / `streamable-http` 用 `url`/`headers`);省略或留空则不修改。", + "description": "拥有该会话的智能体应用。" + }, + "entry_kind": { + "type": "string", + "description": "创建该会话的入口来源。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `web` | Web 控制台界面创建 |\n| `im` | IM 客户端(IM 机器人/IM 端 H5)创建 |\n| `api` | 通过公开 API 创建 |\n| `automation` | 由自动化规则触发创建(无人值守运行) |\n| `subagent` | 父会话经 agent_dispatch 派生的子会话(审计标签;运行时按 web 工具面执行) |", "enum": [ - "stdio", - "sse", - "streamable-http" + "web", + "im", + "api", + "automation", + "subagent" ] }, - "command": { + "person_id": { "type": "string", - "description": "可执行命令(stdio 传输)。" + "description": "创建者人员 ID。" }, - "args": { - "type": "array", - "items": { - "type": "string" - }, - "description": "命令参数(`stdio` 传输);整体替换:传 `[]` 可清空,省略则不修改。" + "team_id": { + "type": "integer", + "format": "int64", + "description": "所属团队 ID;0 表示未绑定团队。创建后不可变更。" }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "环境变量(`stdio` 传输);整体替换,但敏感键回传掩码值或空字符串时保留服务端存储的原值;省略则不修改。" + "team_name": { + "type": "string", + "description": "解析出的团队名称;未绑定或团队已删除时为空。" }, - "url": { + "is_mine": { + "type": "boolean", + "description": "当该会话由调用者创建时为 true。" + }, + "can_view": { + "type": "boolean", + "description": "调用者可查看此会话时为 true。" + }, + "can_continue": { + "type": "boolean", + "description": "调用者可在此会话中继续发起新轮次时为 true。" + }, + "can_manage": { + "type": "boolean", + "description": "当调用者可重命名/归档/删除该会话时为 true;个人会话仅创建者可管理,团队会话允许创建者、账户管理员或团队成员管理。" + }, + "can_fork": { + "type": "boolean", + "description": "调用者可从此会话创建分支时为 true。" + }, + "access_source": { "type": "string", - "description": "服务器 URL(sse / streamable-http 传输)。" + "description": "调用者获得该会话访问权限的方式;未解析到访问来源时省略。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `owner` | 调用者是会话创建者(完全权限) |\n| `team_member` | 调用者是会话绑定团队的成员(完全权限) |\n| `manager` | 管理者授权(保留值;当前版本不产生该来源) |\n| `share_link` | 通过有效分享链接获得(仅查看/复刻,不能继续或管理) |\n| `participant` | 同账户非成员通过可参与的团队会话获得(仅查看/继续/复刻) |", + "enum": [ + "owner", + "team_member", + "manager", + "share_link", + "participant" + ] }, - "headers": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "HTTP 头(`sse` / `streamable-http` 传输);整体替换,掩码/空值保留规则同 `env`;省略则不修改。" + "share_enabled": { + "type": "boolean", + "description": "会话的分享链接处于启用状态时为 true。" }, - "connect_timeout": { + "share_version": { "type": "integer", - "description": "连接超时,单位秒。0 表示默认(10 秒)。" + "format": "int64", + "description": "分享链接的版本号;撤销分享时会递增。" }, - "call_timeout": { + "shared_at": { "type": "integer", - "description": "工具调用超时,单位秒。0 表示默认(60 秒)。" + "format": "int64", + "description": "最近一次启用分享的时间,Unix 毫秒时间戳;从未分享时为 0。" }, - "auth_mode": { + "shared_by": { + "type": "integer", + "format": "int64", + "description": "最近一次启用分享的人员 ID;从未分享时为 0。" + }, + "status": { "type": "string", - "description": "认证模式:shared(默认)、per_user_secret 或 per_user_oauth。" + "description": "生命周期状态。可选值:`enabled`(正常可用)、`deleted`(已删除,软删除,不可再访问)。", + "enum": [ + "enabled", + "deleted" + ] }, - "secret_schema": { + "incognito": { + "type": "boolean", + "description": "无痕(不持久化记忆)会话时为 true。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "会话创建时间,Unix 毫秒时间戳。" + }, + "updated_at": { + "type": "integer", + "format": "int64", + "description": "会话最近更新时间,Unix 毫秒时间戳。" + }, + "template_staging_round_id": { "type": "string", - "description": "JSON 密钥 schema;auth_mode=per_user_secret 时必填。" + "description": "当前 save→validate 轮次 ID(仅 template-assistant);否则为空。" + }, + "state": { + "type": "object", + "additionalProperties": true, + "description": "原始会话状态包(会话级键)。为空时省略。" + }, + "bound_environment": { + "$ref": "#/components/schemas/EnvironmentBinding" + }, + "context_resolved": { + "$ref": "#/components/schemas/ContextResolvedItem" + }, + "token_usage": { + "$ref": "#/components/schemas/SessionTokenUsage" + }, + "current_context_tokens": { + "type": "integer", + "format": "int64", + "description": "截至最近一轮的 LLM 上下文窗口的 token 数。0 表示尚无已完成的轮次。" + }, + "context_window": { + "type": "integer", + "format": "int64", + "description": "所绑定模型的最大上下文 token 数。0 表示未知。" + }, + "archived_at": { + "type": "integer", + "format": "int64", + "description": "归档时间,Unix 毫秒时间戳;0 表示未归档。" + }, + "pinned_at": { + "type": "integer", + "format": "int64", + "description": "调用者的个人置顶时间,Unix 毫秒时间戳;0 表示未置顶。" + }, + "last_event_at": { + "type": "integer", + "format": "int64", + "description": "最近一条助手侧事件的时间,Unix 毫秒时间戳。" + }, + "is_running": { + "type": "boolean", + "description": "当该会话当前有正在进行的智能体轮次时为 true。" }, - "oauth_metadata": { - "type": "string", - "description": "JSON OAuth 元数据;为 per_user_oauth 预留。" + "has_unread": { + "type": "boolean", + "description": "当存在调用者尚未查看的助手输出时为 true。" }, - "team_id": { - "type": [ - "integer", - "null" - ], - "description": "重新分配团队范围:0 表示账户级;>0 表示团队。省略则不变。", - "format": "int64" + "current_turn_started_at": { + "type": "integer", + "format": "int64", + "description": "本轮(当前或最近一轮)开始时间,Unix 毫秒时间戳;尚未开始任何轮次时为 0。" }, - "environments": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - }, - "description": "该服务器可运行的执行环境:`cloud` 和/或 BYOC Runner 的环境 ID 列表。省略(null)表示保持不变;传入列表即整体设置——空列表表示清除限制、恢复为所有环境。" + "current_turn_active_ms": { + "type": "integer", + "format": "int64", + "description": "本轮(当前或最近一轮)的实际工作时长(毫秒),不含等待 ask_user 的时间;每次新轮次开始时重置为 0。" }, - "allow_insecure_oauth_http": { - "type": [ - "boolean", - "null" - ], - "description": "是否允许 OAuth 令牌交换使用明文 HTTP。省略表示不变。" + "current_turn_wait_ms": { + "type": "integer", + "format": "int64", + "description": "当前轮次累计的 ask_user 人工等待时长(毫秒);每次新轮次开始时重置为 0。" }, - "allow_insecure_tls_skip_verify": { - "type": [ - "boolean", - "null" - ], - "description": "是否跳过 TLS 证书校验。省略表示不变。" + "current_turn_tokens": { + "type": "integer", + "format": "int64", + "description": "当前进行中轮次的 token 总数(输入+输出+推理),涵盖父会话及其所有子智能体;仅由 session/get 在会话运行时计算,session/list 响应及空闲时恒为 0。" } }, "required": [ - "server_id" + "session_id", + "session_name", + "app_name", + "person_id", + "team_id", + "is_mine", + "can_view", + "can_continue", + "can_manage", + "can_fork", + "share_enabled", + "share_version", + "shared_at", + "shared_by", + "status", + "incognito", + "created_at", + "updated_at", + "current_context_tokens", + "context_window", + "archived_at", + "pinned_at", + "is_running", + "has_unread", + "current_turn_started_at", + "current_turn_active_ms", + "current_turn_wait_ms", + "current_turn_tokens" ] }, - "ManualRunRuleResult": { + "SessionListRequest": { "type": "object", - "description": "手动运行一次自动化规则(跳过其计划触发时间)的结果。", + "description": "查询智能体会话列表的过滤条件。`all` 表示调用者自己的个人会话和可访问团队的团队会话;账户管理员不可见他人的个人会话。", "properties": { - "rule_id": { + "app_name": { "type": "string", - "description": "被运行的规则 ID。" + "description": "要查询其会话的智能体应用。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `ask-ai` | Ask AI 问答助手 |\n| `support` | 客服支持智能体 |\n| `support-website` | 官网站点客服智能体(经 A2A 暴露,不内置于控制台) |\n| `support-flashcat` | Flashcat 站点客服智能体(经 A2A 暴露) |\n| `ai-sre` | AI SRE 主应用 |\n| `template-assistant` | 通知模板助手(模板编辑/校验场景) |\n| `swe` | 内部基准评测应用(不对外开放) |", + "enum": [ + "ask-ai", + "support", + "support-website", + "support-flashcat", + "ai-sre", + "template-assistant", + "swe" + ] }, - "trigger_kind": { + "p": { + "type": "integer", + "description": "页码,从 1 开始。", + "default": 1, + "minimum": 1 + }, + "limit": { + "type": "integer", + "description": "每页数量,1–100。", + "minimum": 1, + "maximum": 100, + "default": 20 + }, + "orderby": { "type": "string", + "description": "排序字段:`created_at` 按创建时间,`updated_at` 按最近更新时间;省略时默认 `updated_at`。", "enum": [ - "manual" - ], - "description": "该操作固定为 manual。" + "created_at", + "updated_at" + ] }, - "preflight": { - "$ref": "#/components/schemas/PreflightResult" + "asc": { + "type": "boolean", + "description": "为 true 时升序,false 时降序。仅在与 `orderby` 同时提供时生效;省略 `orderby` 时始终按 `updated_at` 降序。" }, - "run": { - "$ref": "#/components/schemas/AutomationRunView" - } - }, - "required": [ - "rule_id", - "trigger_kind", - "preflight" - ] - }, - "PreflightResult": { - "type": "object", - "description": "在允许发起手动运行前计算出的就绪检查结果。", - "properties": { - "ok": { + "include_subagent_sessions": { "type": "boolean", - "description": "全部就绪检查是否通过。凡是能返回给调用者的响应中该值恒为 true——预检失败会直接返回 400/403 错误,而不是 ok=false 的响应体。" + "description": "是否在列表中包含子智能体派生的会话。" }, - "checks": { - "type": "array", - "items": { - "type": "string" - }, - "description": "按执行顺序列出的就绪检查项名称。当前固定为:rule_loaded、actor_authorized、app_allowed、runtime_scope_resolved、rule_config_valid。" + "keyword": { + "type": "string", + "description": "按会话名称关键字过滤。", + "maxLength": 64 }, "scope": { "type": "string", + "description": "可见范围:`all`(自己的个人会话 + 可访问团队会话)、`personal` 或 `team`;默认 `all`。", "enum": [ - "person", + "all", + "personal", "team" - ], - "description": "本次运行解析出的作用域,与规则的 run_scope 一致。可选值:`person`(个人规则,以创建者身份运行)、`team`(团队规则,归属团队运行)。" - }, - "owner_id": { - "type": "integer", - "format": "int64", - "description": "规则所有者 person ID。" - }, - "team_id": { - "type": "integer", - "format": "int64", - "description": "规则的作用域团队 ID;0 表示个人规则。" - }, - "app_name": { - "type": "string", - "description": "规则所属的 App。当前始终为 ai-sre;手动运行目前仅支持该 App。" + ] }, - "warnings": { + "team_ids": { "type": "array", "items": { - "type": "string" + "type": "integer", + "format": "int64" }, - "description": "预检过程中给出的非致命警告。没有警告时省略或为空数组。" - } - }, - "required": [ - "ok", - "checks", - "scope", - "owner_id", - "team_id", - "app_name" - ] - }, - "ResponseEnvelope": { - "type": "object", - "description": "Standard response envelope used by every Flashduty public API. On success `data` contains the endpoint-specific payload and `error` is absent. On failure `error` is present and `data` is absent. `request_id` is always present and is also mirrored in the `Flashcat-Request-Id` response header.", - "properties": { - "request_id": { - "type": "string", - "description": "本次请求的唯一 ID,与 Flashcat-Request-Id 响应头一致。反馈问题时请携带该 ID。", - "example": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4" + "description": "可选的团队过滤;与 `scope` 取交集,且不会扩大访问范围。" }, - "error": { - "$ref": "#/components/schemas/DutyError" + "entry_kinds": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "web", + "im", + "api", + "automation" + ] + }, + "description": "仅返回由这些入口产生的会话;为空则返回所有类型。" }, - "data": { - "description": "端点专属数据负载,具体结构见各操作 200 响应中的 schema。" - } - }, - "required": [ - "request_id" - ] - }, - "SessionDeleteRequest": { - "type": "object", - "description": "按 ID 删除会话。", - "properties": { - "session_id": { + "status": { "type": "string", - "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。", - "minLength": 1 + "description": "归档分桶:active(默认)返回未归档,archived 返回已归档,all 返回全部。", + "enum": [ + "active", + "archived", + "all" + ] } }, "required": [ - "session_id" + "app_name" ] }, - "SessionExportRequest": { + "SessionListResponse": { "type": "object", - "description": "以流式 NDJSON 导出单个会话的完整事件记录。", + "description": "一页智能体会话。", "properties": { - "session_id": { - "type": "string", - "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。" + "total": { + "type": "integer", + "format": "int64", + "description": "匹配过滤条件的会话总数(忽略分页)。" + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SessionItem" + }, + "description": "当前页的会话。" }, - "include_subagents": { + "suggest_init": { "type": "boolean", - "description": "为 true 时,每条 subagent_dispatch 行后会跟随子会话的完整事件流,并以其自身的 session_meta 包裹。默认 false。" + "description": "账户级引导标志:当账户在任何范围内都没有知识包时为 true;与本次调用的过滤条件无关。" } }, "required": [ - "session_id" + "total", + "sessions", + "suggest_init" ] }, - "SessionGetRequest": { + "SessionTokenUsage": { "type": "object", - "description": "查询单个会话,并返回其最近事件的一页(向更早方向分页)。", + "description": "跨所有轮次的会话级 token 累计汇总。账户计费的权威来源。", "properties": { - "session_id": { - "type": "string", - "description": "目标会话 ID,取自 `POST /safari/session/list` 返回的列表。", - "minLength": 1 - }, - "share_token": { - "type": "string", - "description": "通过分享链接访问会话时使用的分享令牌;常规账户授权访问时省略。", - "maxLength": 512 + "input_tokens": { + "type": "integer", + "format": "int64", + "description": "提示(输入)token 总数,含缓存部分。" }, - "num_recent_events": { + "cached_tokens": { "type": "integer", - "description": "旧版每页数量:返回的最近事件数。与 `limit` 同时设置时以 `limit` 为准;0 使用服务端默认值(100)。", - "minimum": 0, - "maximum": 1000 + "format": "int64", + "description": "input_tokens 中由提示缓存命中的部分。" }, - "limit": { + "output_tokens": { "type": "integer", - "description": "事件每页数量;优先于 `num_recent_events`。0 使用服务端默认值(100)。", - "minimum": 0, - "maximum": 1000 + "format": "int64", + "description": "生成(输出)token 总数。" }, - "search_after_ctx": { + "reasoning_tokens": { + "type": "integer", + "format": "int64", + "description": "推理/思考 token 总数。" + } + }, + "required": [ + "input_tokens", + "cached_tokens", + "output_tokens", + "reasoning_tokens" + ] + }, + "SkillDeleteRequest": { + "type": "object", + "description": "按 ID 删除技能。", + "properties": { + "skill_id": { "type": "string", - "description": "上一次响应返回的不透明游标;回传以获取更早的一页。", - "maxLength": 4096 + "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" } }, "required": [ - "session_id" + "skill_id" ] }, - "SessionGetResponse": { + "SkillGetRequest": { "type": "object", - "description": "一个会话及其事件的一页(向更早方向分页)。", + "description": "按 ID 查询技能。", "properties": { - "session": { - "$ref": "#/components/schemas/SessionItem" - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventItem" - }, - "description": "最近事件,按 (created_at, event_id) 升序排列。" - }, - "has_more_older": { - "type": "boolean", - "description": "当本页之外仍有更早的事件时为 true。" - }, - "search_after_ctx": { + "skill_id": { "type": "string", - "description": "不透明游标;作为 search_after_ctx 回传以获取更早的一页。has_more_older 为 false 时省略。" - }, - "suggest_init": { - "type": "boolean", - "description": "账户级引导标志:当账户在任何范围内都没有知识包时为 true;并非该会话独有的属性。" + "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" } }, "required": [ - "session", - "events", - "has_more_older", - "suggest_init" + "skill_id" ] }, - "SessionItem": { + "SkillItem": { "type": "object", - "description": "单条智能体会话记录。", + "description": "AI SRE 技能 —— 智能体可加载的 SKILL.md 打包资源。", "properties": { - "session_id": { + "skill_id": { "type": "string", - "description": "会话标识。" + "description": "技能唯一 ID(前缀 `skill_`)。" }, - "parent_session_id": { - "type": "string", - "description": "子智能体(子)会话的父会话 ID;否则为空。" + "account_id": { + "type": "integer", + "description": "所属账户 ID。", + "format": "int64" }, - "session_name": { - "type": "string", - "description": "会话标题;未命名会话可能为空。" + "team_id": { + "type": "integer", + "description": "团队范围:0 表示账户级;>0 表示所属团队。", + "format": "int64" }, - "app_name": { + "skill_name": { "type": "string", - "description": "拥有该会话的智能体应用。" + "description": "技能名称,在其作用域内唯一(账户范围或单个团队内)。" }, - "entry_kind": { + "description": { "type": "string", - "description": "创建该会话的入口来源。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `web` | Web 控制台界面创建 |\n| `im` | IM 客户端(IM 机器人/IM 端 H5)创建 |\n| `api` | 通过公开 API 创建 |\n| `automation` | 由自动化规则触发创建(无人值守运行) |\n| `subagent` | 父会话经 agent_dispatch 派生的子会话(审计标签;运行时按 web 工具面执行) |", - "enum": [ - "web", - "im", - "api", - "automation", - "subagent" - ] + "description": "来自 SKILL.md frontmatter 的可读描述。" }, - "person_id": { + "description_en": { "type": "string", - "description": "创建者人员 ID。" - }, - "team_id": { - "type": "integer", - "format": "int64", - "description": "所属团队 ID;0 表示未绑定团队。创建后不可变更。" + "description": "可选的英文描述。英文语言环境下的界面响应优先使用该字段而非 `description`;当 `description` 被本地化展示时,技能目录也会用它作为稳定的选型信号。" }, - "team_name": { + "content": { "type": "string", - "description": "解析出的团队名称;未绑定或团队已删除时为空。" - }, - "is_mine": { - "type": "boolean", - "description": "当该会话由调用者创建时为 true。" - }, - "can_view": { - "type": "boolean", - "description": "调用者可查看此会话时为 true。" - }, - "can_continue": { - "type": "boolean", - "description": "调用者可在此会话中继续发起新轮次时为 true。" - }, - "can_manage": { - "type": "boolean", - "description": "当调用者可重命名/归档/删除该会话时为 true;个人会话仅创建者可管理,团队会话允许创建者、账户管理员或团队成员管理。" - }, - "can_fork": { - "type": "boolean", - "description": "调用者可从此会话创建分支时为 true。" + "description": "完整的 SKILL.md 内容;列表响应中省略。" }, - "access_source": { + "version": { "type": "string", - "description": "调用者获得该会话访问权限的方式;未解析到访问来源时省略。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `owner` | 调用者是会话创建者(完全权限) |\n| `team_member` | 调用者是会话绑定团队的成员(完全权限) |\n| `manager` | 管理者授权(保留值;当前版本不产生该来源) |\n| `share_link` | 通过有效分享链接获得(仅查看/复刻,不能继续或管理) |\n| `participant` | 同账户非成员通过可参与的团队会话获得(仅查看/继续/复刻) |", - "enum": [ - "owner", - "team_member", - "manager", - "share_link", - "participant" - ] - }, - "share_enabled": { - "type": "boolean", - "description": "会话的分享链接处于启用状态时为 true。" - }, - "share_version": { - "type": "integer", - "format": "int64", - "description": "分享链接的版本号;撤销分享时会递增。" - }, - "shared_at": { - "type": "integer", - "format": "int64", - "description": "最近一次启用分享的时间,Unix 毫秒时间戳;从未分享时为 0。" + "description": "frontmatter 中的技能版本。" }, - "shared_by": { - "type": "integer", - "format": "int64", - "description": "最近一次启用分享的人员 ID;从未分享时为 0。" + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "从 frontmatter 解析的标签。" }, - "status": { + "author": { "type": "string", - "description": "生命周期状态。可选值:`enabled`(正常可用)、`deleted`(已删除,软删除,不可再访问)。", - "enum": [ - "enabled", - "deleted" - ] - }, - "incognito": { - "type": "boolean", - "description": "无痕(不持久化记忆)会话时为 true。" - }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "会话创建时间,Unix 毫秒时间戳。" - }, - "updated_at": { - "type": "integer", - "format": "int64", - "description": "会话最近更新时间,Unix 毫秒时间戳。" + "description": "技能作者。" }, - "template_staging_round_id": { + "license": { "type": "string", - "description": "当前 save→validate 轮次 ID(仅 template-assistant);否则为空。" - }, - "state": { - "type": "object", - "additionalProperties": true, - "description": "原始会话状态包(会话级键)。为空时省略。" - }, - "bound_environment": { - "$ref": "#/components/schemas/EnvironmentBinding" + "description": "技能许可证。" }, - "context_resolved": { - "$ref": "#/components/schemas/ContextResolvedItem" + "tools": { + "type": "array", + "items": { + "type": "string" + }, + "description": "所需工具(内置或 `mcp:server/tool`)。" }, - "token_usage": { - "$ref": "#/components/schemas/SessionTokenUsage" + "venues": { + "type": "array", + "items": { + "type": "string" + }, + "description": "限定技能可用的执行环境类型(EnvironmentKind 字符串,如 `byoc`);为空时省略,表示所有环境均可用。" }, - "current_context_tokens": { - "type": "integer", - "format": "int64", - "description": "截至最近一轮的 LLM 上下文窗口的 token 数。0 表示尚无已完成的轮次。" + "s3_key": { + "type": "string", + "description": "技能压缩包在对象存储中的 key。" }, - "context_window": { - "type": "integer", - "format": "int64", - "description": "所绑定模型的最大上下文 token 数。0 表示未知。" + "checksum": { + "type": "string", + "description": "技能压缩包的 SHA-256 校验和。" }, - "archived_at": { + "status": { + "type": "string", + "description": "技能状态。已删除的技能不会出现在任何 API 响应中,因此只会返回这两种状态。", + "enum": [ + "enabled", + "disabled" + ] + }, + "created_by": { "type": "integer", - "format": "int64", - "description": "归档时间,Unix 毫秒时间戳;0 表示未归档。" + "description": "创建该技能的成员 ID。", + "format": "int64" }, - "pinned_at": { + "created_at": { "type": "integer", "format": "int64", - "description": "调用者的个人置顶时间,Unix 毫秒时间戳;0 表示未置顶。" + "description": "创建时间,Unix 毫秒时间戳。" }, - "last_event_at": { + "updated_at": { "type": "integer", "format": "int64", - "description": "最近一条助手侧事件的时间,Unix 毫秒时间戳。" + "description": "最近更新时间,Unix 毫秒时间戳。" }, - "is_running": { + "can_edit": { "type": "boolean", - "description": "当该会话当前有正在进行的智能体轮次时为 true。" + "description": "调用者是否可编辑该技能。" }, - "has_unread": { - "type": "boolean", - "description": "当存在调用者尚未查看的助手输出时为 true。" + "source_template_name": { + "type": "string", + "description": "该技能安装来源的市场模板名称;自建技能为空。" }, - "current_turn_started_at": { - "type": "integer", - "format": "int64", - "description": "本轮(当前或最近一轮)开始时间,Unix 毫秒时间戳;尚未开始任何轮次时为 0。" + "source_template_version": { + "type": "string", + "description": "安装时的模板版本。" }, - "current_turn_active_ms": { - "type": "integer", - "format": "int64", - "description": "本轮(当前或最近一轮)的实际工作时长(毫秒),不含等待 ask_user 的时间;每次新轮次开始时重置为 0。" + "update_available": { + "type": "boolean", + "description": "当市场存在更新版本时为 true。" }, - "current_turn_wait_ms": { - "type": "integer", - "format": "int64", - "description": "当前轮次累计的 ask_user 人工等待时长(毫秒);每次新轮次开始时重置为 0。" + "is_modified": { + "type": "boolean", + "description": "当市场来源技能被本地修改时为 true(自动更新将跳过)。" }, - "current_turn_tokens": { - "type": "integer", - "format": "int64", - "description": "当前进行中轮次的 token 总数(输入+输出+推理),涵盖父会话及其所有子智能体;仅由 session/get 在会话运行时计算,session/list 响应及空闲时恒为 0。" + "created": { + "type": "boolean", + "description": "仅在“从会话安装”响应中出现:true 表示新建,false 表示原地更新。" } }, "required": [ - "session_id", - "session_name", - "app_name", - "person_id", + "skill_id", + "account_id", "team_id", - "is_mine", - "can_view", - "can_continue", - "can_manage", - "can_fork", - "share_enabled", - "share_version", - "shared_at", - "shared_by", + "skill_name", + "description", "status", - "incognito", + "created_by", "created_at", "updated_at", - "current_context_tokens", - "context_window", - "archived_at", - "pinned_at", - "is_running", - "has_unread", - "current_turn_started_at", - "current_turn_active_ms", - "current_turn_wait_ms", - "current_turn_tokens" + "can_edit", + "update_available", + "is_modified" ] }, - "SessionListRequest": { + "SkillListRequest": { "type": "object", - "description": "查询智能体会话列表的过滤条件。`all` 表示调用者自己的个人会话和可访问团队的团队会话;账户管理员不可见他人的个人会话。", + "description": "技能列表的分页、搜索与团队过滤条件。", "properties": { - "app_name": { - "type": "string", - "description": "要查询其会话的智能体应用。可选值:\n| 值 | 含义 |\n| --- | --- |\n| `ask-ai` | Ask AI 问答助手 |\n| `support` | 客服支持智能体 |\n| `support-website` | 官网站点客服智能体(经 A2A 暴露,不内置于控制台) |\n| `support-flashcat` | Flashcat 站点客服智能体(经 A2A 暴露) |\n| `ai-sre` | AI SRE 主应用 |\n| `template-assistant` | 通知模板助手(模板编辑/校验场景) |\n| `swe` | 内部基准评测应用(不对外开放) |", - "enum": [ - "ask-ai", - "support", - "support-website", - "support-flashcat", - "ai-sre", - "template-assistant", - "swe" - ] - }, "p": { "type": "integer", "description": "页码,从 1 开始。", - "default": 1, - "minimum": 1 + "default": 1 }, "limit": { "type": "integer", - "description": "每页数量,1–100。", - "minimum": 1, - "maximum": 100, + "description": "每页数量。", "default": 20 }, - "orderby": { - "type": "string", - "description": "排序字段:`created_at` 按创建时间,`updated_at` 按最近更新时间;省略时默认 `updated_at`。", - "enum": [ - "created_at", - "updated_at" - ] - }, - "asc": { - "type": "boolean", - "description": "为 true 时升序,false 时降序。仅在与 `orderby` 同时提供时生效;省略 `orderby` 时始终按 `updated_at` 降序。" - }, - "include_subagent_sessions": { - "type": "boolean", - "description": "是否在列表中包含子智能体派生的会话。" - }, - "keyword": { - "type": "string", - "description": "按会话名称关键字过滤。", - "maxLength": 64 - }, "scope": { "type": "string", - "description": "可见范围:`all`(自己的个人会话 + 可访问团队会话)、`personal` 或 `team`;默认 `all`。", + "description": "将结果限制为 `all`(默认)、仅 `account`(team_id=0)、或仅 `team`(排除账户级记录);设置后会覆盖 `include_account`。", "enum": [ "all", - "personal", + "account", "team" ] }, + "query": { + "type": "string", + "description": "跨技能名称、描述、英文描述、技能 ID、市场来源模板名称与作者的全文搜索。", + "maxLength": 128 + }, "team_ids": { "type": "array", "items": { "type": "integer", "format": "int64" }, - "description": "可选的团队过滤;与 `scope` 取交集,且不会扩大访问范围。" - }, - "entry_kinds": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "web", - "im", - "api", - "automation" - ] - }, - "description": "仅返回由这些入口产生的会话;为空则返回所有类型。" + "description": "按团队 ID 过滤;为空则使用调用者可见范围。" }, - "status": { - "type": "string", - "description": "归档分桶:active(默认)返回未归档,archived 返回已归档,all 返回全部。", - "enum": [ - "active", - "archived", - "all" - ] + "include_account": { + "type": [ + "boolean", + "null" + ], + "description": "是否包含账户级(team_id=0)记录,默认 true。当 `scope` 为 `account` 或 `team` 时该字段会被忽略。" } - }, - "required": [ - "app_name" - ] + } }, - "SessionListResponse": { + "SkillListResponse": { "type": "object", - "description": "一页智能体会话。", + "description": "分页的技能列表。", "properties": { "total": { "type": "integer", - "format": "int64", - "description": "匹配过滤条件的会话总数(忽略分页)。" + "description": "匹配的技能总数。", + "format": "int64" }, - "sessions": { + "skills": { "type": "array", "items": { - "$ref": "#/components/schemas/SessionItem" + "$ref": "#/components/schemas/SkillItem" }, - "description": "当前页的会话。" - }, - "suggest_init": { - "type": "boolean", - "description": "账户级引导标志:当账户在任何范围内都没有知识包时为 true;与本次调用的过滤条件无关。" + "description": "当前页的技能。" } }, "required": [ "total", - "sessions", - "suggest_init" + "skills" ] }, - "SessionTokenUsage": { + "SkillStatusRequest": { "type": "object", - "description": "跨所有轮次的会话级 token 累计汇总。账户计费的权威来源。", + "description": "按 ID 启用/禁用技能。", "properties": { - "input_tokens": { - "type": "integer", - "format": "int64", - "description": "提示(输入)token 总数,含缓存部分。" - }, - "cached_tokens": { - "type": "integer", - "format": "int64", - "description": "input_tokens 中由提示缓存命中的部分。" - }, - "output_tokens": { - "type": "integer", - "format": "int64", - "description": "生成(输出)token 总数。" - }, - "reasoning_tokens": { - "type": "integer", - "format": "int64", - "description": "推理/思考 token 总数。" + "skill_id": { + "type": "string", + "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" } }, "required": [ - "input_tokens", - "cached_tokens", - "output_tokens", - "reasoning_tokens" + "skill_id" ] }, - "SkillDeleteRequest": { + "SkillUpdateRequest": { "type": "object", - "description": "按 ID 删除技能。", + "description": "可编辑的技能元数据。", "properties": { "skill_id": { "type": "string", "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" + }, + "description": { + "type": "string", + "description": "新的描述,不能包含 `<` 或 `>`。传入空字符串不会清空当前值 —— 该字段无法用于清空描述。", + "maxLength": 1024 + }, + "description_en": { + "type": [ + "string", + "null" + ], + "description": "新的英文描述,不能包含 `<` 或 `>`。省略表示不变;传入空字符串可显式清空。", + "maxLength": 1024 + }, + "team_id": { + "type": [ + "integer", + "null" + ], + "description": "重新分配团队范围:0 表示账户级;>0 表示团队。省略则不变。", + "format": "int64" } }, "required": [ "skill_id" ] }, - "SkillGetRequest": { + "SkillUploadRequest": { "type": "object", - "description": "按 ID 查询技能。", + "description": "上传技能压缩包的 multipart 表单。", "properties": { + "file": { + "type": "string", + "format": "binary", + "description": "技能压缩包(.skill / .zip / .tar.gz / .tgz),最大 100MB;超限文件会在读取正文前即被拒绝。" + }, + "team_id": { + "type": "integer", + "description": "新建/upsert 技能的团队范围:0 表示账户级。通过 `skill_id` 定向替换时会忽略该字段。", + "format": "int64" + }, + "replace": { + "type": "boolean", + "description": "为 true 时覆盖已有技能而非在名称冲突时报错 —— 若提供 `skill_id` 则按其匹配,否则按技能名称匹配。" + }, "skill_id": { "type": "string", - "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" + "description": "定向替换指定技能时的技能 ID(需配合 `replace=true`)。" } }, "required": [ - "skill_id" + "file" ] }, - "SkillItem": { + "KnowledgePackItem": { "type": "object", - "description": "AI SRE 技能 —— 智能体可加载的 SKILL.md 打包资源。", + "description": "知识包——一棵带版本号的文件树,AI SRE 会话启动时会注入到每个沙箱中。每个(账户, scope, scope_id)仅有一个知识包。", "properties": { - "skill_id": { + "pack_id": { "type": "string", - "description": "技能唯一 ID(前缀 `skill_`)。" + "description": "知识包 ID(`kpk_` 前缀)。" }, "account_id": { "type": "integer", - "description": "所属账户 ID。", + "description": "知识包所属账户 ID。", "format": "int64" }, - "team_id": { + "scope": { + "type": "string", + "description": "知识包范围;`channel` 为历史遗留范围,新建知识包为 `account` 或 `team`。", + "enum": [ + "account", + "team", + "channel" + ] + }, + "scope_id": { "type": "integer", - "description": "团队范围:0 表示账户级;>0 表示所属团队。", + "description": "范围归属 ID:账户范围为账户 ID,团队范围为团队 ID。", "format": "int64" }, - "skill_name": { - "type": "string", - "description": "技能名称,在其作用域内唯一(账户范围或单个团队内)。" - }, - "description": { + "team_name": { "type": "string", - "description": "来自 SKILL.md frontmatter 的可读描述。" + "description": "所属团队的显示名称(仅团队范围);为空时省略(账户范围,或团队名称解析失败)。" }, - "description_en": { - "type": "string", - "description": "可选的英文描述。英文语言环境下的界面响应优先使用该字段而非 `description`;当 `description` 被本地化展示时,技能目录也会用它作为稳定的选型信号。" + "file_count": { + "type": "integer", + "description": "包内文件数量。" }, - "content": { - "type": "string", - "description": "完整的 SKILL.md 内容;列表响应中省略。" + "total_bytes": { + "type": "integer", + "description": "包内所有文件的总字节数。", + "format": "int64" }, "version": { - "type": "string", - "description": "frontmatter 中的技能版本。" + "type": "integer", + "description": "知识包版本号,文件每次变更时递增。" }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "description": "从 frontmatter 解析的标签。" + "duty_version": { + "type": "integer", + "description": "最后一次撰写或确认 DUTY.md 时的知识库版本。当 `version` 大于该值时,说明 DUTY.md 已不能反映知识库的全部文件。" }, - "author": { - "type": "string", - "description": "技能作者。" + "created_by": { + "type": "integer", + "description": "创建者成员 ID。", + "format": "int64" }, - "license": { - "type": "string", - "description": "技能许可证。" + "created_at_ms": { + "type": "integer", + "description": "创建时间的 Unix 毫秒时间戳。", + "format": "int64" }, - "tools": { - "type": "array", - "items": { - "type": "string" - }, - "description": "所需工具(内置或 `mcp:server/tool`)。" + "updated_at_ms": { + "type": "integer", + "description": "最近修改时间的 Unix 毫秒时间戳。", + "format": "int64" }, - "venues": { - "type": "array", - "items": { - "type": "string" - }, - "description": "限定技能可用的执行环境类型(EnvironmentKind 字符串,如 `byoc`);为空时省略,表示所有环境均可用。" + "can_edit": { + "type": "boolean", + "description": "调用者是否有权限编辑该知识包。" + } + }, + "required": [ + "pack_id", + "account_id", + "scope", + "scope_id", + "file_count", + "total_bytes", + "version", + "duty_version", + "created_by", + "created_at_ms", + "updated_at_ms", + "can_edit" + ] + }, + "KnowledgeFileItem": { + "type": "object", + "description": "知识包内单个文件的元数据;文件内容需通过 file/get 单独获取。", + "properties": { + "file_id": { + "type": "string", + "description": "文件 ID(`kfl_` 前缀)。" }, - "s3_key": { + "pack_id": { "type": "string", - "description": "技能压缩包在对象存储中的 key。" + "description": "文件所属的知识包 ID。" }, - "checksum": { + "rel_path": { "type": "string", - "description": "技能压缩包的 SHA-256 校验和。" + "description": "相对于知识包根目录的路径,如 `runbooks/restart.md`。" }, - "status": { + "content_type": { "type": "string", - "description": "技能状态。已删除的技能不会出现在任何 API 响应中,因此只会返回这两种状态。", - "enum": [ - "enabled", - "disabled" - ] + "description": "MIME 类型;上传时未指定则按扩展名推断。" }, - "created_by": { + "size_bytes": { "type": "integer", - "description": "创建该技能的成员 ID。", + "description": "文件大小(字节)。", "format": "int64" }, - "created_at": { - "type": "integer", - "format": "int64", - "description": "创建时间,Unix 毫秒时间戳。" + "checksum": { + "type": "string", + "description": "文件内容的 SHA-256 十六进制摘要。" }, - "updated_at": { + "updated_by": { "type": "integer", - "format": "int64", - "description": "最近更新时间,Unix 毫秒时间戳。" - }, - "can_edit": { - "type": "boolean", - "description": "调用者是否可编辑该技能。" + "description": "最近修改者成员 ID。", + "format": "int64" }, - "source_template_name": { + "updated_at_ms": { + "type": "integer", + "description": "最近修改时间的 Unix 毫秒时间戳。", + "format": "int64" + } + }, + "required": [ + "file_id", + "pack_id", + "rel_path", + "content_type", + "size_bytes", + "checksum", + "updated_by", + "updated_at_ms" + ] + }, + "KnowledgeWarning": { + "type": "object", + "description": "文件上传/删除返回的非阻塞提示,例如指向已删除文件的引用。", + "properties": { + "code": { "type": "string", - "description": "该技能安装来源的市场模板名称;自建技能为空。" + "description": "提示码。可选值:`unresolved_reference`(写入的文件内容中 @ref 引用了包内不存在的文件,`ref` 给出该引用)、`still_referenced_by`(被删除的文件仍被包内其他文件 @ref 引用,`refs` 列出引用方)。", + "enum": [ + "unresolved_reference", + "still_referenced_by" + ] }, - "source_template_version": { + "ref": { "type": "string", - "description": "安装时的模板版本。" - }, - "update_available": { - "type": "boolean", - "description": "当市场存在更新版本时为 true。" - }, - "is_modified": { - "type": "boolean", - "description": "当市场来源技能被本地修改时为 true(自动更新将跳过)。" + "description": "与提示相关的单个引用。" }, - "created": { - "type": "boolean", - "description": "仅在“从会话安装”响应中出现:true 表示新建,false 表示原地更新。" + "refs": { + "type": "array", + "description": "与提示相关的多个引用。", + "items": { + "type": "string" + } } }, "required": [ - "skill_id", - "account_id", - "team_id", - "skill_name", - "description", - "status", - "created_by", - "created_at", - "updated_at", - "can_edit", - "update_available", - "is_modified" + "code" ] }, - "SkillListRequest": { + "KnowledgeGetRequest": { "type": "object", - "description": "技能列表的分页、搜索与团队过滤条件。", + "description": "无请求字段——固定查询账户范围知识包。", + "properties": {} + }, + "KnowledgePackListRequest": { + "type": "object", + "description": "知识包列表的过滤与分页参数。", "properties": { "p": { "type": "integer", - "description": "页码,从 1 开始。", - "default": 1 + "description": "页码,从 1 开始;`p` 与 `limit` 均未设置时返回全部结果。" }, "limit": { "type": "integer", - "description": "每页数量。", - "default": 20 + "description": "每页条数。" }, "scope": { "type": "string", - "description": "将结果限制为 `all`(默认)、仅 `account`(team_id=0)、或仅 `team`(排除账户级记录);设置后会覆盖 `include_account`。", + "description": "限定范围;`all`(默认)会覆盖 `include_account`。可选值:`all`(账户范围 + 可见团队范围)、`account`(仅账户级知识包)、`team`(仅团队级知识包,可配合 `team_ids` 过滤)。", "enum": [ "all", "account", @@ -6827,605 +8092,717 @@ }, "query": { "type": "string", - "description": "跨技能名称、描述、英文描述、技能 ID、市场来源模板名称与作者的全文搜索。", + "description": "对知识包 ID、范围、范围 ID/账户 ID 和团队名称做大小写不敏感的子串过滤。", "maxLength": 128 }, "team_ids": { "type": "array", + "description": "限定团队 ID;非管理员会与其所属团队取交集。", "items": { "type": "integer", "format": "int64" - }, - "description": "按团队 ID 过滤;为空则使用调用者可见范围。" + } }, "include_account": { "type": [ "boolean", "null" ], - "description": "是否包含账户级(team_id=0)记录,默认 true。当 `scope` 为 `account` 或 `team` 时该字段会被忽略。" + "description": "是否包含账户范围的知识包;默认为 true。" } } }, - "SkillListResponse": { + "KnowledgePackEnsureRequest": { "type": "object", - "description": "分页的技能列表。", + "description": "要确保存在知识包的范围。", "properties": { - "total": { + "scope": { + "type": "string", + "description": "知识包范围。可选值:`account`(账户级知识包,scope_id 固定为调用方账户 ID,仅账户管理员可创建;首次创建会播种默认 DUTY.md)、`team`(团队级知识包,必须提供 `scope_id` 团队 ID,调用者需属于该团队)。", + "enum": [ + "account", + "team" + ] + }, + "scope_id": { "type": "integer", - "description": "匹配的技能总数。", + "description": "团队 ID;团队范围必填,账户范围忽略。", "format": "int64" + } + }, + "required": [ + "scope" + ] + }, + "KnowledgePackUpdateRequest": { + "type": "object", + "description": "将知识包移动到其他范围。", + "properties": { + "pack_id": { + "type": "string", + "description": "要更新的知识包 ID。" }, - "skills": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SkillItem" - }, - "description": "当前页的技能。" + "scope": { + "type": [ + "string", + "null" + ], + "description": "目标范围;不传则为空操作,返回当前知识包。", + "enum": [ + "account", + "team" + ] + }, + "scope_id": { + "type": [ + "integer", + "null" + ], + "description": "目标团队 ID;`scope` 为 `team` 时必填,为 `account` 时自动设置。", + "format": "int64" } }, "required": [ - "total", - "skills" + "pack_id" ] }, - "SkillStatusRequest": { + "KnowledgePackDeleteRequest": { "type": "object", - "description": "按 ID 启用/禁用技能。", + "description": "要删除的知识包。", "properties": { - "skill_id": { + "pack_id": { "type": "string", - "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" + "description": "要删除的知识包 ID。" } }, "required": [ - "skill_id" + "pack_id" ] }, - "SkillUpdateRequest": { + "KnowledgePackDeleteResponse": { "type": "object", - "description": "可编辑的技能元数据。", + "description": "删除结果。", "properties": { - "skill_id": { + "ok": { + "type": "boolean", + "description": "知识包删除成功时为 true。" + } + }, + "required": [ + "ok" + ] + }, + "KnowledgeFileListRequest": { + "type": "object", + "description": "要列出文件的知识包。", + "properties": { + "pack_id": { "type": "string", - "description": "目标技能 ID,取自 `POST /safari/skill/list` 返回的列表。" + "description": "知识包 ID;默认为调用者的账户范围知识包。" }, - "description": { + "p": { + "type": "integer", + "description": "页码,从 1 开始。目前接受但忽略——响应始终返回完整文件列表。" + }, + "limit": { + "type": "integer", + "description": "每页条数。目前接受但忽略——响应始终返回完整文件列表。" + } + } + }, + "KnowledgeFileGetRequest": { + "type": "object", + "description": "要获取的文件。", + "properties": { + "pack_id": { "type": "string", - "description": "新的描述,不能包含 `<` 或 `>`。传入空字符串不会清空当前值 —— 该字段无法用于清空描述。", - "maxLength": 1024 + "description": "知识包 ID;默认为调用者的账户范围知识包。" }, - "description_en": { - "type": [ - "string", - "null" - ], - "description": "新的英文描述,不能包含 `<` 或 `>`。省略表示不变;传入空字符串可显式清空。", - "maxLength": 1024 + "rel_path": { + "type": "string", + "description": "文件相对于知识包根目录的路径。" + } + }, + "required": [ + "rel_path" + ] + }, + "KnowledgeFilePutRequest": { + "type": "object", + "description": "要创建或覆盖的文件。文件体通过 `content_b64` 以 Base64 文本上传,不是 multipart 上传。", + "properties": { + "pack_id": { + "type": "string", + "description": "知识包 ID;默认为调用者的账户范围知识包。" }, - "team_id": { - "type": [ - "integer", - "null" - ], - "description": "重新分配团队范围:0 表示账户级;>0 表示团队。省略则不变。", + "rel_path": { + "type": "string", + "description": "相对于知识包根目录的目标路径;已存在的文件会被覆盖。" + }, + "content_b64": { + "type": "string", + "description": "Base64 编码的文件内容;解码后必须是合法的 UTF-8 文本(拒绝二进制)。单文件上限 1 MiB。" + }, + "content_type": { + "type": "string", + "description": "MIME 类型;留空时按扩展名推断。" + } + }, + "required": [ + "rel_path" + ] + }, + "KnowledgeGetResponse": { + "type": "object", + "description": "账户范围知识包元数据及其文件列表。", + "properties": { + "pack": { + "$ref": "#/components/schemas/KnowledgePackItem" + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "description": "该知识包内的文件数组;无文件时为空数组。" + } + }, + "required": [ + "pack", + "files" + ] + }, + "KnowledgePackListResponse": { + "type": "object", + "description": "可见的知识包及过滤后的总数。", + "properties": { + "packs": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgePackItem" + }, + "description": "过滤后当前可见的知识包数组(当前页),与 `total` 配合分页。" + }, + "total": { + "type": "integer", + "description": "过滤后、分页前的知识包总数。", + "format": "int64" + } + }, + "required": [ + "packs", + "total" + ] + }, + "KnowledgeFileListResponse": { + "type": "object", + "description": "知识包内的文件。", + "properties": { + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "description": "指定知识包内的文件数组;无文件时为空数组。" + }, + "total": { + "type": "integer", + "description": "知识包内文件总数。", "format": "int64" } }, "required": [ - "skill_id" + "files", + "total" + ] + }, + "KnowledgeFileGetResponse": { + "type": "object", + "description": "文件元数据及其 Base64 编码的内容。", + "properties": { + "file": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "content_b64": { + "type": "string", + "description": "Base64 编码的文件内容;解码后为 UTF-8 文本。" + } + }, + "required": [ + "file", + "content_b64" + ] + }, + "KnowledgeFilePutResponse": { + "type": "object", + "description": "写入的文件及非阻塞提示。", + "properties": { + "file": { + "$ref": "#/components/schemas/KnowledgeFileItem" + }, + "warnings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeWarning" + }, + "description": "写入成功后的非阻塞警告数组;`code=unresolved_reference` 表示文件内容中的 @ref 引用了包内不存在的文件。无警告时该字段缺省(omitempty)。" + } + }, + "required": [ + "file" ] }, - "SkillUploadRequest": { + "KnowledgeFileDeleteRequest": { "type": "object", - "description": "上传技能压缩包的 multipart 表单。", + "description": "要从知识包中删除的文件。", "properties": { - "file": { + "pack_id": { "type": "string", - "format": "binary", - "description": "技能压缩包(.skill / .zip / .tar.gz / .tgz),最大 100MB;超限文件会在读取正文前即被拒绝。" + "description": "知识包 ID;默认为调用者的账户范围知识包。" }, - "team_id": { - "type": "integer", - "description": "新建/upsert 技能的团队范围:0 表示账户级。通过 `skill_id` 定向替换时会忽略该字段。", - "format": "int64" + "rel_path": { + "type": "string", + "description": "文件相对于知识包根目录的路径。" }, - "replace": { + "force": { "type": "boolean", - "description": "为 true 时覆盖已有技能而非在名称冲突时报错 —— 若提供 `skill_id` 则按其匹配,否则按技能名称匹配。" - }, - "skill_id": { - "type": "string", - "description": "定向替换指定技能时的技能 ID(需配合 `replace=true`)。" + "description": "即使其他包内文件仍引用该文件也强制删除;此时引用方会以警告形式返回,而不再阻止删除。" } }, "required": [ - "file" + "rel_path" ] }, - "KnowledgePackItem": { + "KnowledgeFileDeleteResponse": { "type": "object", - "description": "知识包——一棵带版本号的文件树,AI SRE 会话启动时会注入到每个沙箱中。每个(账户, scope, scope_id)仅有一个知识包。", + "description": "删除结果;无警告时为空对象。", "properties": { - "pack_id": { + "warnings": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeWarning" + }, + "description": "删除后的非阻塞警告数组;`code=still_referenced_by` 表示被(强制)删除的文件仍被包内其他文件 @ref 引用(`refs` 列出引用方)。无警告时该字段缺省(omitempty)。" + } + } + }, + "ArtifactIdRequest": { + "type": "object", + "description": "指定一个产物库中的产物。", + "properties": { + "artifact_id": { "type": "string", - "description": "知识包 ID(`kpk_` 前缀)。" - }, - "account_id": { - "type": "integer", - "description": "知识包所属账户 ID。", - "format": "int64" - }, + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + } + }, + "required": [ + "artifact_id" + ] + }, + "ArtifactListRequest": { + "type": "object", + "description": "产物库列表的过滤与分页参数。", + "properties": { "scope": { "type": "string", - "description": "知识包范围;`channel` 为历史遗留范围,新建知识包为 `account` 或 `team`。", "enum": [ - "account", - "team", - "channel" - ] + "all", + "personal", + "team" + ], + "default": "all", + "description": "可见范围。`all`(默认)= 调用者自己的个人产物 + 其所在团队的产物;`personal` = 仅调用者自己创建的;`team` = 仅调用者所属团队的团队产物。" }, - "scope_id": { - "type": "integer", - "description": "范围归属 ID:账户范围为账户 ID,团队范围为团队 ID。", - "format": "int64" + "team_ids": { + "type": "array", + "items": { + "type": "integer", + "format": "int64" + }, + "description": "只看指定团队的产物,与调用者可见范围取交集 —— 不属于调用者的团队不会有任何返回。" }, - "team_name": { + "query": { "type": "string", - "description": "所属团队的显示名称(仅团队范围);为空时省略(账户范围,或团队名称解析失败)。" - }, - "file_count": { - "type": "integer", - "description": "包内文件数量。" - }, - "total_bytes": { - "type": "integer", - "description": "包内所有文件的总字节数。", - "format": "int64" + "description": "按产物标题做大小写不敏感的子串匹配。" }, - "version": { + "page": { "type": "integer", - "description": "知识包版本号,文件每次变更时递增。" + "default": 1, + "minimum": 1, + "description": "页码,从 1 开始。" }, - "duty_version": { + "limit": { "type": "integer", - "description": "最后一次撰写或确认 DUTY.md 时的知识库版本。当 `version` 大于该值时,说明 DUTY.md 已不能反映知识库的全部文件。" + "default": 20, + "maximum": 100, + "description": "每页条数。默认 20,最大 100。" }, - "created_by": { - "type": "integer", - "description": "创建者成员 ID。", - "format": "int64" + "orderby": { + "type": "string", + "enum": [ + "created_at", + "updated_at" + ], + "description": "排序字段:`created_at` 或 `updated_at`。留空按 `updated_at` 倒序。" }, - "created_at_ms": { - "type": "integer", - "description": "创建时间的 Unix 毫秒时间戳。", - "format": "int64" + "asc": { + "type": "boolean", + "description": "为 true 时升序,为 false 时降序。仅在设置了 `orderby` 时生效。" + } + } + }, + "ArtifactListResponse": { + "type": "object", + "description": "一页产物列表。", + "properties": { + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublishedArtifactItem" + }, + "description": "当前页的产物列表。" }, - "updated_at_ms": { + "total": { "type": "integer", - "description": "最近修改时间的 Unix 毫秒时间戳。", - "format": "int64" - }, - "can_edit": { - "type": "boolean", - "description": "调用者是否有权限编辑该知识包。" + "format": "int64", + "description": "符合过滤条件的产物总数(跨所有页)。" } - }, - "required": [ - "pack_id", - "account_id", - "scope", - "scope_id", - "file_count", - "total_bytes", - "version", - "duty_version", - "created_by", - "created_at_ms", - "updated_at_ms", - "can_edit" - ] + } }, - "KnowledgeFileItem": { + "PublishedArtifactItem": { "type": "object", - "description": "知识包内单个文件的元数据;文件内容需通过 file/get 单独获取。", + "description": "产物库中的一个已发布产物。时间字段均为 Unix 毫秒时间戳。", "properties": { - "file_id": { + "artifact_id": { "type": "string", - "description": "文件 ID(`kfl_` 前缀)。" + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" }, - "pack_id": { + "title": { "type": "string", - "description": "文件所属的知识包 ID。" + "description": "产物在产物库中的展示标题。" }, - "rel_path": { - "type": "string", - "description": "相对于知识包根目录的路径,如 `runbooks/restart.md`。" + "team_id": { + "type": "integer", + "format": "int64", + "description": "归属团队 ID。`0` 表示个人产物(仅创建者可管理);大于 0 表示团队产物。" }, - "content_type": { + "team_name": { "type": "string", - "description": "MIME 类型;上传时未指定则按扩展名推断。" + "description": "归属团队的名称。个人产物不返回该字段。" }, - "size_bytes": { + "person_id": { "type": "integer", - "description": "文件大小(字节)。", - "format": "int64" + "format": "int64", + "description": "创建者的成员 ID。" }, - "checksum": { + "creator_name": { "type": "string", - "description": "文件内容的 SHA-256 十六进制摘要。" + "description": "创建者的显示名称。" }, - "updated_by": { - "type": "integer", - "description": "最近修改者成员 ID。", - "format": "int64" + "is_mine": { + "type": "boolean", + "description": "调用者是否为创建者。" }, - "updated_at_ms": { - "type": "integer", - "description": "最近修改时间的 Unix 毫秒时间戳。", - "format": "int64" - } - }, - "required": [ - "file_id", - "pack_id", - "rel_path", - "content_type", - "size_bytes", - "checksum", - "updated_by", - "updated_at_ms" - ] - }, - "KnowledgeWarning": { - "type": "object", - "description": "文件上传/删除返回的非阻塞提示,例如指向已删除文件的引用。", - "properties": { - "code": { + "can_edit": { + "type": "boolean", + "description": "调用者是否可管理该产物(重命名、转移、删除、分享):创建者、归属团队任一成员、或来源会话的管理者。" + }, + "session_id": { "type": "string", - "description": "提示码。可选值:`unresolved_reference`(写入的文件内容中 @ref 引用了包内不存在的文件,`ref` 给出该引用)、`still_referenced_by`(被删除的文件仍被包内其他文件 @ref 引用,`refs` 列出引用方)。", - "enum": [ - "unresolved_reference", - "still_referenced_by" - ] + "description": "产出该文件的来源会话 ID(`sess_` 前缀)。" }, - "ref": { + "session_title": { "type": "string", - "description": "与提示相关的单个引用。" + "description": "来源会话的标题。会话已删除时不返回该字段。" }, - "refs": { - "type": "array", - "description": "与提示相关的多个引用。", - "items": { - "type": "string" - } - } - }, - "required": [ - "code" - ] - }, - "KnowledgeGetRequest": { - "type": "object", - "description": "无请求字段——固定查询账户范围知识包。", - "properties": {} - }, - "KnowledgePackListRequest": { - "type": "object", - "description": "知识包列表的过滤与分页参数。", - "properties": { - "p": { - "type": "integer", - "description": "页码,从 1 开始;`p` 与 `limit` 均未设置时返回全部结果。" + "file_id": { + "type": "string", + "description": "当前承载产物内容的文件 ID(`pf_` 前缀)。每次重新发布都会变化。" }, - "limit": { + "name": { + "type": "string", + "description": "底层文件名(含扩展名)。" + }, + "size": { "type": "integer", - "description": "每页条数。" + "format": "int64", + "description": "文件大小,单位字节。" }, - "scope": { + "content_type": { "type": "string", - "description": "限定范围;`all`(默认)会覆盖 `include_account`。可选值:`all`(账户范围 + 可见团队范围)、`account`(仅账户级知识包)、`team`(仅团队级知识包,可配合 `team_ids` 过滤)。", - "enum": [ - "all", - "account", - "team" - ] + "description": "文件的 MIME 类型。" + }, + "created_at": { + "type": "integer", + "format": "int64", + "description": "产物发布的 Unix 毫秒时间戳。" }, - "query": { - "type": "string", - "description": "对知识包 ID、范围、范围 ID/账户 ID 和团队名称做大小写不敏感的子串过滤。", - "maxLength": 128 + "updated_at": { + "type": "integer", + "format": "int64", + "description": "产物最近一次更新(重命名、转移或重新发布)的 Unix 毫秒时间戳。" }, - "team_ids": { - "type": "array", - "description": "限定团队 ID;非管理员会与其所属团队取交集。", - "items": { - "type": "integer", - "format": "int64" - } + "share_enabled": { + "type": "boolean", + "description": "是否已开启匿名公开分享。为 false 时不返回该字段。" }, - "include_account": { - "type": [ - "boolean", - "null" - ], - "description": "是否包含账户范围的知识包;默认为 true。" - } - } - }, - "KnowledgePackEnsureRequest": { - "type": "object", - "description": "要确保存在知识包的范围。", - "properties": { - "scope": { + "public_url": { "type": "string", - "description": "知识包范围。可选值:`account`(账户级知识包,scope_id 固定为调用方账户 ID,仅账户管理员可创建;首次创建会播种默认 DUTY.md)、`team`(团队级知识包,必须提供 `scope_id` 团队 ID,调用者需属于该团队)。", - "enum": [ - "account", - "team" - ] + "description": "匿名公开链接 —— 由 CDN 承载的控制台 `/share/artifact/` 页面。仅在分享开启时返回;任何拿到链接的人无需登录即可查看。" }, - "scope_id": { + "shared_by": { "type": "integer", - "description": "团队 ID;团队范围必填,账户范围忽略。", - "format": "int64" + "format": "int64", + "description": "开启分享的成员 ID。仅在分享开启时返回。" + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "最近一次开启分享或更新快照的 Unix 毫秒时间戳。仅在分享开启时返回。" + }, + "share_file_id": { + "type": "string", + "description": "公开快照所基于的文件 ID。当 `share_enabled` 为 true 且 `file_id` 与 `share_file_id` 不一致时,公开快照已过期 —— 调用 `/safari/artifact/gallery/share/sync` 刷新。" } }, "required": [ - "scope" + "artifact_id", + "title", + "team_id", + "person_id", + "creator_name", + "is_mine", + "can_edit", + "session_id", + "file_id", + "name", + "size", + "content_type", + "created_at", + "updated_at" ] }, - "KnowledgePackUpdateRequest": { + "ArtifactUpdateRequest": { "type": "object", - "description": "将知识包移动到其他范围。", + "description": "产物的部分字段更新。仅更新传入的字段。", "properties": { - "pack_id": { + "artifact_id": { "type": "string", - "description": "要更新的知识包 ID。" + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" }, - "scope": { + "title": { "type": [ "string", "null" ], - "description": "目标范围;不传则为空操作,返回当前知识包。", - "enum": [ - "account", - "team" - ] + "description": "新标题。会去除首尾空白,传入时不能为空。不传则不变。" }, - "scope_id": { + "team_id": { "type": [ "integer", "null" ], - "description": "目标团队 ID;`scope` 为 `team` 时必填,为 `account` 时自动设置。", - "format": "int64" - } - }, - "required": [ - "pack_id" - ] - }, - "KnowledgePackDeleteRequest": { - "type": "object", - "description": "要删除的知识包。", - "properties": { - "pack_id": { - "type": "string", - "description": "要删除的知识包 ID。" - } - }, - "required": [ - "pack_id" - ] - }, - "KnowledgePackDeleteResponse": { - "type": "object", - "description": "删除结果。", - "properties": { - "ok": { - "type": "boolean", - "description": "知识包删除成功时为 true。" + "format": "int64", + "description": "转移目标空间。`0` 表示转为个人产物(仅创建者可管理);大于 0 表示转移到调用者所属的团队。不传则不变。" } }, "required": [ - "ok" + "artifact_id" ] }, - "KnowledgeFileListRequest": { - "type": "object", - "description": "要列出文件的知识包。", - "properties": { - "pack_id": { - "type": "string", - "description": "知识包 ID;默认为调用者的账户范围知识包。" - }, - "p": { - "type": "integer", - "description": "页码,从 1 开始。目前接受但忽略——响应始终返回完整文件列表。" - }, - "limit": { - "type": "integer", - "description": "每页条数。目前接受但忽略——响应始终返回完整文件列表。" - } - } - }, - "KnowledgeFileGetRequest": { + "ArtifactPublishFromFileRequest": { "type": "object", - "description": "要获取的文件。", + "description": "把会话中生成的文件发布到产物库。", "properties": { - "pack_id": { + "file_id": { "type": "string", - "description": "知识包 ID;默认为调用者的账户范围知识包。" + "description": "会话中生成的文件 ID(`pf_` 前缀),即会话文件卡片上的文件标识。" }, - "rel_path": { + "title": { "type": "string", - "description": "文件相对于知识包根目录的路径。" + "description": "产物在产物库中的展示标题。会去除首尾空白,不能为空。" } }, "required": [ - "rel_path" + "file_id", + "title" ] }, - "KnowledgeFilePutRequest": { + "ArtifactPublishResponse": { "type": "object", - "description": "要创建或覆盖的文件。文件体通过 `content_b64` 以 Base64 文本上传,不是 multipart 上传。", + "description": "发布产物的结果。", "properties": { - "pack_id": { - "type": "string", - "description": "知识包 ID;默认为调用者的账户范围知识包。" - }, - "rel_path": { + "artifact_id": { "type": "string", - "description": "相对于知识包根目录的目标路径;已存在的文件会被覆盖。" + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" }, - "content_b64": { + "title": { "type": "string", - "description": "Base64 编码的文件内容;解码后必须是合法的 UTF-8 文本(拒绝二进制)。单文件上限 1 MiB。" + "description": "产物展示标题。" }, - "content_type": { + "gallery_path": { "type": "string", - "description": "MIME 类型;留空时按扩展名推断。" + "description": "产物页面的控制台相对路径:`/ai-sre/artifacts/`。" } }, "required": [ - "rel_path" + "artifact_id", + "title", + "gallery_path" ] }, - "KnowledgeGetResponse": { + "ArtifactFileStateRequest": { "type": "object", - "description": "账户范围知识包元数据及其文件列表。", + "description": "批量查询文件的产物发布状态。", "properties": { - "pack": { - "$ref": "#/components/schemas/KnowledgePackItem" - }, - "files": { + "file_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "type": "string" }, - "description": "该知识包内的文件数组;无文件时为空数组。" + "maxItems": 50, + "minItems": 1, + "description": "要查询的文件 ID(`pf_` 前缀)。单次最多 50 个;重复和空字符串会被忽略。" } }, "required": [ - "pack", - "files" + "file_ids" ] }, - "KnowledgePackListResponse": { + "ArtifactFileStateItem": { "type": "object", - "description": "可见的知识包及过滤后的总数。", + "description": "单个文件的发布状态。仅当文件存在已发布的产物时才会返回。", "properties": { - "packs": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgePackItem" - }, - "description": "过滤后当前可见的知识包数组(当前页),与 `total` 配合分页。" + "file_id": { + "type": "string", + "description": "回显请求中的文件 ID。" }, - "total": { - "type": "integer", - "description": "过滤后、分页前的知识包总数。", - "format": "int64" + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" + }, + "title": { + "type": "string", + "description": "已发布产物的展示标题。" + }, + "gallery_path": { + "type": "string", + "description": "产物页面的控制台相对路径:`/ai-sre/artifacts/`。" } }, "required": [ - "packs", - "total" + "file_id", + "artifact_id", + "title", + "gallery_path" ] }, - "KnowledgeFileListResponse": { + "ArtifactFileStateResponse": { "type": "object", - "description": "知识包内的文件。", + "description": "按文件返回的发布状态。", "properties": { - "files": { + "items": { "type": "array", "items": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "$ref": "#/components/schemas/ArtifactFileStateItem" }, - "description": "指定知识包内的文件数组;无文件时为空数组。" - }, - "total": { - "type": "integer", - "description": "知识包内文件总数。", - "format": "int64" + "description": "每个存在已发布产物的请求文件对应一条记录;未发布的文件不会出现在列表中。" } }, "required": [ - "files", - "total" + "items" ] }, - "KnowledgeFileGetResponse": { + "ArtifactShareState": { "type": "object", - "description": "文件元数据及其 Base64 编码的内容。", + "description": "产物的公开分享状态。", "properties": { - "file": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "artifact_id": { + "type": "string", + "description": "产物 ID(`art_` 前缀)。同时也是公开分享链接的标识。" }, - "content_b64": { + "share_enabled": { + "type": "boolean", + "description": "本响应中固定为 `true`。" + }, + "public_url": { "type": "string", - "description": "Base64 编码的文件内容;解码后为 UTF-8 文本。" + "description": "完全由 CDN 承载的匿名公开链接。任何拿到链接的人无需登录即可查看内容。" + }, + "shared_by": { + "type": "integer", + "format": "int64", + "description": "开启分享的成员 ID。" + }, + "shared_at": { + "type": "integer", + "format": "int64", + "description": "最近一次开启分享或更新快照的 Unix 毫秒时间戳。" } }, "required": [ - "file", - "content_b64" + "artifact_id", + "share_enabled", + "public_url", + "shared_by", + "shared_at" ] }, - "KnowledgeFilePutResponse": { + "ArtifactSignRequest": { "type": "object", - "description": "写入的文件及非阻塞提示。", + "description": "为文件申请签名下载/预览链接。", "properties": { - "file": { - "$ref": "#/components/schemas/KnowledgeFileItem" + "file_id": { + "type": "string", + "description": "要签发的文件 ID(`pf_` 前缀)。" }, - "warnings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgeWarning" - }, - "description": "写入成功后的非阻塞警告数组;`code=unresolved_reference` 表示文件内容中的 @ref 引用了包内不存在的文件。无警告时该字段缺省(omitempty)。" + "share_token": { + "type": "string", + "description": "可选的会话分享链接令牌。仅当调用者通过会话分享链接(而非账号成员身份)访问文件时需要。" } }, "required": [ - "file" + "file_id" ] }, - "KnowledgeFileDeleteRequest": { + "SignedURLs": { "type": "object", - "description": "要从知识包中删除的文件。", + "description": "短期有效的文件下载/预览签名链接。", "properties": { - "pack_id": { + "download_url": { "type": "string", - "description": "知识包 ID;默认为调用者的账户范围知识包。" + "description": "以下载方式(attachment)返回文件的相对链接(`/safari/artifact/stream?...`)。使用前需拼接 API 域名 `https://api.flashcat.cloud`;有效期见 `expires_in`。" }, - "rel_path": { + "preview_url": { "type": "string", - "description": "文件相对于知识包根目录的路径。" + "description": "与 `download_url` 相同,但以 inline 方式返回,用于浏览器内预览。" }, - "force": { - "type": "boolean", - "description": "即使其他包内文件仍引用该文件也强制删除;此时引用方会以警告形式返回,而不再阻止删除。" + "expires_in": { + "type": "integer", + "description": "两个链接的有效期,单位秒(300)。" + }, + "name": { + "type": "string", + "description": "文件名(含扩展名)。" + }, + "size": { + "type": "integer", + "format": "int64", + "description": "文件大小,单位字节。" + }, + "content_type": { + "type": "string", + "description": "文件的 MIME 类型。" } }, "required": [ - "rel_path" + "download_url", + "preview_url", + "expires_in", + "name", + "size", + "content_type" ] - }, - "KnowledgeFileDeleteResponse": { - "type": "object", - "description": "删除结果;无警告时为空对象。", - "properties": { - "warnings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KnowledgeWarning" - }, - "description": "删除后的非阻塞警告数组;`code=still_referenced_by` 表示被(强制)删除的文件仍被包内其他文件 @ref 引用(`refs` 列出引用方)。无警告时该字段缺省(omitempty)。" - } - } } } } diff --git a/docs.json b/docs.json index d34ad5e2..9300ccea 100644 --- a/docs.json +++ b/docs.json @@ -2692,6 +2692,23 @@ "POST /safari/a2a-agent/delete" ] }, + { + "group": "产物", + "icon": "images", + "pages": [ + "POST /safari/artifact/gallery/list", + "POST /safari/artifact/gallery/get", + "POST /safari/artifact/gallery/file-state", + "POST /safari/artifact/sign", + "GET /safari/artifact/stream", + "POST /safari/artifact/gallery/publish-from-file", + "POST /safari/artifact/gallery/update", + "POST /safari/artifact/gallery/delete", + "POST /safari/artifact/gallery/share/enable", + "POST /safari/artifact/gallery/share/sync", + "POST /safari/artifact/gallery/share/revoke" + ] + }, { "group": "知识", "icon": "book", @@ -4071,6 +4088,23 @@ "POST /safari/a2a-agent/delete" ] }, + { + "group": "Artifacts", + "icon": "images", + "pages": [ + "POST /safari/artifact/gallery/list", + "POST /safari/artifact/gallery/get", + "POST /safari/artifact/gallery/file-state", + "POST /safari/artifact/sign", + "GET /safari/artifact/stream", + "POST /safari/artifact/gallery/publish-from-file", + "POST /safari/artifact/gallery/update", + "POST /safari/artifact/gallery/delete", + "POST /safari/artifact/gallery/share/enable", + "POST /safari/artifact/gallery/share/sync", + "POST /safari/artifact/gallery/share/revoke" + ] + }, { "group": "Knowledge", "icon": "book", diff --git a/en/openapi/api-catalog.mdx b/en/openapi/api-catalog.mdx index 2cb1e50b..b0d7611a 100644 --- a/en/openapi/api-catalog.mdx +++ b/en/openapi/api-catalog.mdx @@ -3,7 +3,7 @@ title: "API Catalog" description: "Complete list of Flashduty Open API endpoints, organized by product module with links to detailed documentation" --- -Flashduty Open API provides **336** endpoints covering five major modules: On-call, Monitors, RUM, AI SRE, and Platform. All endpoints use unified authentication and request specifications. See [Quick Start](/en/openapi/introduction) for details. +Flashduty Open API provides **347** endpoints covering five major modules: On-call, Monitors, RUM, AI SRE, and Platform. All endpoints use unified authentication and request specifications. See [Quick Start](/en/openapi/introduction) for details. All endpoint URLs use `https://api.flashcat.cloud` as the base, authenticated via APP Key through query string. @@ -428,7 +428,7 @@ All endpoint URLs use `https://api.flashcat.cloud` as the base, authenticated vi - + ### Skills @@ -502,6 +502,22 @@ All endpoint URLs use `https://api.flashcat.cloud` as the base, authenticated vi | POST | [`/safari/knowledge/file/put`](/en/api-reference/ai-sre/knowledge/knowledge-file-write-put) | Upload knowledge file | | POST | [`/safari/knowledge/file/delete`](/en/api-reference/ai-sre/knowledge/knowledge-file-write-delete) | Delete knowledge file | +### Artifacts + +| Method | Endpoint | Description | +| :--- | :--- | :--- | +| POST | [`/safari/artifact/gallery/list`](/en/api-reference/ai-sre/artifacts/artifact-read-list) | List artifacts | +| POST | [`/safari/artifact/gallery/get`](/en/api-reference/ai-sre/artifacts/artifact-read-get) | Get artifact detail | +| POST | [`/safari/artifact/gallery/file-state`](/en/api-reference/ai-sre/artifacts/artifact-read-get-file-state) | Get file publish state | +| POST | [`/safari/artifact/sign`](/en/api-reference/ai-sre/artifacts/artifact-read-sign) | Create signed file URLs | +| GET | [`/safari/artifact/stream`](/en/api-reference/ai-sre/artifacts/artifact-read-stream) | Download or preview a file | +| 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/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 | + diff --git a/zh/openapi/api-catalog.mdx b/zh/openapi/api-catalog.mdx index 9d99925e..718f3d53 100644 --- a/zh/openapi/api-catalog.mdx +++ b/zh/openapi/api-catalog.mdx @@ -3,7 +3,7 @@ title: "API 目录" description: "Flashduty Open API 接口完整列表,按产品模块组织并链接到详细文档" --- -Flashduty Open API 提供 **336** 个接口,覆盖 On-call、Monitors、RUM、AI SRE 和平台五个主要模块。所有接口使用统一认证方式和请求规范。详情参见[快速开始](/zh/openapi/introduction)。 +Flashduty Open API 提供 **347** 个接口,覆盖 On-call、Monitors、RUM、AI SRE 和平台五个主要模块。所有接口使用统一认证方式和请求规范。详情参见[快速开始](/zh/openapi/introduction)。 所有接口 URL 均以 `https://api.flashcat.cloud` 为 base,通过 query string 中的 APP Key 认证。 @@ -428,7 +428,7 @@ Flashduty Open API 提供 **336** 个接口,覆盖 On-call、Monitors、RUM、 - + ### 技能 @@ -502,6 +502,22 @@ Flashduty Open API 提供 **336** 个接口,覆盖 On-call、Monitors、RUM、 | POST | [`/safari/knowledge/file/put`](/zh/api-reference/ai-sre/knowledge/knowledge-file-write-put) | 上传知识文件 | | POST | [`/safari/knowledge/file/delete`](/zh/api-reference/ai-sre/knowledge/knowledge-file-write-delete) | 删除知识文件 | +### 产物 + +| 方法 | 接口 | 描述 | +| :--- | :--- | :--- | +| POST | [`/safari/artifact/gallery/list`](/zh/api-reference/ai-sre/artifacts/artifact-read-list) | 查询产物列表 | +| POST | [`/safari/artifact/gallery/get`](/zh/api-reference/ai-sre/artifacts/artifact-read-get) | 获取产物详情 | +| POST | [`/safari/artifact/gallery/file-state`](/zh/api-reference/ai-sre/artifacts/artifact-read-get-file-state) | 查询文件发布状态 | +| POST | [`/safari/artifact/sign`](/zh/api-reference/ai-sre/artifacts/artifact-read-sign) | 签发文件访问链接 | +| GET | [`/safari/artifact/stream`](/zh/api-reference/ai-sre/artifacts/artifact-read-stream) | 下载或预览文件 | +| 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/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) | 撤销公开分享 | +