From c3ba822f11bcda4fe58bb62c0faf34012ca07012 Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 20:08:48 +0200 Subject: [PATCH 1/7] docs: explain standalone workspaces and current account API --- .cspell.json | 2 + AGENTS.md | 1 + content/docs/deployment-models.mdx | 10 +- .../api-ref/agents-and-versions.mdx | 91 +++---- .../docs/developers/api-ref/architecture.mdx | 4 +- .../developers/api-ref/authentication.mdx | 103 +++++--- .../developers/api-ref/files-and-projects.mdx | 236 +++--------------- content/docs/developers/api-ref/meta.json | 8 +- content/docs/developers/api-ref/runs.mdx | 125 +++------- content/docs/developers/api-ref/schedules.mdx | 146 ++--------- content/docs/developers/api-ref/sharing.mdx | 30 +-- .../api-ref/threads-and-messages.mdx | 83 +++--- content/docs/developers/api-ref/usage.mdx | 144 ++--------- content/docs/features/open-architecture.mdx | 2 +- content/docs/index.mdx | 3 + content/docs/meta.json | 4 +- content/docs/workspaces/index.mdx | 55 ++++ content/docs/workspaces/linux.mdx | 122 +++++++++ content/docs/workspaces/macos.mdx | 96 +++++++ content/docs/workspaces/manage.mdx | 135 ++++++++++ content/docs/workspaces/meta.json | 12 + 21 files changed, 680 insertions(+), 732 deletions(-) create mode 100644 content/docs/workspaces/index.mdx create mode 100644 content/docs/workspaces/linux.mdx create mode 100644 content/docs/workspaces/macos.mdx create mode 100644 content/docs/workspaces/manage.mdx create mode 100644 content/docs/workspaces/meta.json diff --git a/.cspell.json b/.cspell.json index 31f2158..9e4d0fe 100644 --- a/.cspell.json +++ b/.cspell.json @@ -43,6 +43,7 @@ "Hubspot", "hypervisor", "Intercom", + "journalctl", "Kimi", "Linear", "MDX", @@ -82,6 +83,7 @@ "Supabase", "timecode", "uvx", + "uuidgen", "webhook" ] } diff --git a/AGENTS.md b/AGENTS.md index 9a0f3b9..7f79c51 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,6 +22,7 @@ The navigation keeps Home first and sorts the top-level sections alphabetically. - **Release notes** - `/release-notes`. Customer-facing changes, newest first. - **Remote access** - `/remote/*`. Ways to use Fluso away from the desktop app. - **Workflows** - `/workflows/*`. Six concrete stories: bug-to-PR, content launch, knowledge recall, meetings, morning brief, and research-to-deck. +- **Workspaces** - `/workspaces/*`. Independent daemon setup on macOS and Linux, provider and file access, updates, backups, and recovery. Request download links from support; do not publish unlisted release URLs. Account API access starts at `/developers/api-ref/authentication`; full endpoint reference is served at each workspace's `/docs`. The home page (`/`) is a router into the journey, with three sections: just landed, already set up, daily user. diff --git a/content/docs/deployment-models.mdx b/content/docs/deployment-models.mdx index dc29e58..6596a14 100644 --- a/content/docs/deployment-models.mdx +++ b/content/docs/deployment-models.mdx @@ -2,7 +2,7 @@ title: Deployment models sidebarTitle: Overview icon: server-cog -description: Compare Fluso Cloud, dedicated AWS VPC, planned Azure VNet, and scoped on-premises deployments. +description: Compare Fluso Cloud, a workspace on your machine, and Enterprise deployments. --- Your deployment model decides who operates Fluso and where its runtime, durable data, and diagnostics can live. It does not by itself control where connected Apps or external model providers process data. @@ -10,6 +10,7 @@ Your deployment model decides who operates Fluso and where its runtime, durable | Model | Availability | Operations | | --- | --- | --- | | Fluso Cloud | Available | Managed by Fluso | +| Personal daemon | Contact Fluso for a matching Mac or Linux package | You operate one owner's workspace | | Dedicated AWS VPC | Supported for scoped Enterprise deployments | Responsibilities agreed during deployment design | | Azure VNet | Coming soon | Not available today | | On-premises | Scoped Enterprise engagement | Responsibilities agreed before implementation | @@ -20,6 +21,12 @@ Fluso Cloud is the standard managed service. It runs on AWS with European hostin Choose it when you want to start without operating infrastructure. See [Legal](/resources/legal) for Fluso's privacy policy. +## A workspace on your machine + +Install the daemon to run one owner's workspace on an Apple Silicon Mac or a supported Linux host. Conversations and files stay on that host. You manage its provider credentials, service, updates, and backups. + +The desktop app connects to this workspace; it does not install or update the daemon. This is separate from an Enterprise deployment of the whole platform. Start with [macOS setup](/workspaces/macos) or [Linux and Raspberry Pi setup](/workspaces/linux). + ## Dedicated AWS VPC A dedicated AWS VPC deployment is available for Enterprise customers whose network or residency requirements do not fit the managed service. It is a scoped deployment, not a self-service switch. @@ -59,6 +66,7 @@ Traces can include prompt and response text. For a dedicated or on-premises depl ## Choose a model - Start with Fluso Cloud when managed operations and European hosting meet the requirement. +- Use a [personal daemon](/workspaces) when you want to operate a workspace on your own machine. - Scope an AWS VPC deployment when you need a dedicated AWS network boundary. - Treat Azure VNet as future availability, not a current option. - Scope on-premises only after Fluso validates the infrastructure, residency, provider, telemetry, and support requirements. diff --git a/content/docs/developers/api-ref/agents-and-versions.mdx b/content/docs/developers/api-ref/agents-and-versions.mdx index 7c9fa23..c371695 100644 --- a/content/docs/developers/api-ref/agents-and-versions.mdx +++ b/content/docs/developers/api-ref/agents-and-versions.mdx @@ -1,89 +1,54 @@ --- -title: Agents -sidebarTitle: Agents +title: Agents and configuration +sidebarTitle: Agents and configuration icon: bot -description: Create an Agent, update its current setup safely, and attach reference files. +description: Create a reusable Agent, choose its conversation, and update its instructions safely. --- -These are authenticated beta client routes. Agent fields use camelCase and configurations use `schemaVersion: 2`. The current model has instructions, skills, app policies, and knowledge files. It has no separate description, goal, or thread-management mode. +An Agent adds saved instructions and capabilities to a conversation. Start with [API access](/developers/api-ref/authentication) for credentials and your workspace's complete endpoint reference. -For the user workflow, see [Create and refine](/developers/agent-builder). +## Create an Agent -## Create and read +Send `POST /v1/agents` with a saved `Idempotency-Key` and a body such as: - - -Creating a definition does not create or run a conversation. The project must already exist. `Home` is the default. - -```bash -curl "$FLUSO_API/v1/agents" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "name": "Release reviewer", - "instructions": "Review supplied evidence. Separate blockers from missing information. Do not publish or contact anyone.", - "conversationStarters": ["Review this release candidate"], - "responseMode": "high", - "projectName": "Home", - "skills": ["pdf", "docx"], - "connectors": {} - }' +```json +{ + "name": "Meeting assistant", + "instructions": "Turn my notes into a short meeting brief. List decisions needed and questions still open." +} ``` -The `201` response contains `agentId`, `currentConfigId`, and `config`. Save the returned IDs. An optional UUID `creationId` supports retrying the same creation. +Keep the returned `agent_id`. To use the Agent, send `POST /v1/threads` with that ID and an `input` message. To change its setup through conversation, also set `purpose: "manager"`. + +Opening the same Agent and purpose returns its existing conversation. Two callers share that conversation and its queue. For independent ordinary chats, omit `agent_id`. -| Method and path | Result | -| --- | --- | -| `GET /v1/agents?page=1&limit=50&q=release` | Agent summaries and pagination. `purpose` is derived from instructions | -| `GET /v1/agents?all=true` | The complete bounded library in one response | -| `GET /v1/agents/{agentId}` | Current Agent record and configuration | -| `GET /v1/agents/{agentId}/chats` | Main conversation, delegated sessions, and Manager, including role and earlier-setup markers | +## Read the current configuration -## Update without overwriting someone else's edit +`GET /v1/agents/{agent_id}` returns the Agent and its `current_config_id`. Keep that ID before editing. - +## Publish an update -Read the Agent first. Send its `currentConfigId` as `baseConfigId`, with only the fields you intend to change. +Send only the changed fields to `PATCH /v1/agents/{agent_id}`, together with the ID you read: ```json { - "baseConfigId": "", - "instructions": "Review supplied evidence. Missing evidence remains unresolved. Do not publish or contact anyone." + "base_config_id": "CURRENT_CONFIG_ID", + "instructions": "Turn my notes into a short meeting brief. Start with the decisions I need to make." } ``` -A stale revision returns `409 STALE_AGENT_CONFIG`. Read again and reapply the intended change. Do not blindly resend all fields from an old form. - -Omitted fields stay unchanged. Submitted lists and connector maps replace the whole field. - -| Field | Meaning | -| --- | --- | -| `skills: null` | All skills available to an ordinary chat, including later additions | -| `skills: []` | No selected skills | -| `connectors: null` or `{}` | No app grants | -| `connectors: {"": "allow"}` | Selected connection permitted, subject to account and approval policies | -| `read_only` / `deny` | Restrict that connection to classified read-only tools, or block it | - -The main conversation follows the current setup on its next accepted turn. Running turns and existing delegated sessions retain their accepted configuration. Changing `projectName` does not move existing conversations. - -Configurations are stored internally, but there is no current public version-list or rollback endpoint. - -## Attach knowledge +An old `base_config_id` returns 409 instead of overwriting another edit. Read the latest configuration, review the difference, then retry your intended change. - +The main conversation uses saved changes on its next turn. Existing work sessions retain their configuration binding. `run.execution` records the configuration, provider, model, and reasoning effort used when execution starts. -Upload one multipart `file`, or use `POST /v1/agents/{agentId}/knowledge-files/from-workspace` with `{"path": "/projects/Home/files/rubric.md"}`. +## Copies and sharing -The response is a knowledge reference with `fileId`, `name`, `fileName`, and `bytes`. Attaching stores a copy. It does not add that reference to the active setup. PATCH `knowledgeFiles` with the full desired list and the latest `baseConfigId` to save it. Preserve entries you still want. +Duplication creates an independent Agent. Keep the same idempotency key when retrying duplication. The retry returns the original copy. -Limits: 25 knowledge entries, 20 MiB per file, 4,000 instruction characters, three starters of 200 characters each, and 100 Agents per user. Workspace storage limits also apply. +Public sharing is Cloud-only. A shared Agent snapshot excludes credentials, conversations, and schedules. After importing, inspect `setup_requirements` and complete setup in its manager conversation. The recipient supplies credentials and consent. -## Copy, share, or delete +## Delete an Agent -| Method and path | Behavior | -| --- | --- | -| `POST /v1/agents/{agentId}/duplicate` with `{}` | Independent setup and knowledge copy. No conversations or routines | -| `POST /v1/agents/{agentId}/star` with `{"starred": true}` | Set the library star | -| `DELETE /v1/agents/{agentId}` | Delete the Agent and its conversations, remove schedules, and revoke its public link | +`DELETE /v1/agents/{agent_id}` deletes the Agent and its owned conversations and revokes its shared Agent link. Save the results you need first. -Sharing uses [separate snapshot routes](/developers/api-ref/sharing). To do work, [open the main conversation and send a message](/developers/api-ref/threads-and-messages). +For exact fields and sharing operations, use the [workspace API reference](/developers/api-ref/authentication#choose-your-workspace). diff --git a/content/docs/developers/api-ref/architecture.mdx b/content/docs/developers/api-ref/architecture.mdx index 56f35ac..61f2712 100644 --- a/content/docs/developers/api-ref/architecture.mdx +++ b/content/docs/developers/api-ref/architecture.mdx @@ -48,4 +48,6 @@ A dropped stream does not prove a run failed. The client reconciles with saved h Use conversation history, routine state, and [run status](/developers/api-ref/runs) to distinguish queued work, approval waits, failures, and completed output. Avoid resubmitting an uncertain operation under a new ID without checking it. -See [Authentication](/developers/api-ref/authentication) for the current beta API boundary. +A [daemon workspace](/workspaces) runs the gateway and runtime on one owner's host. It stores its own account state and files. The desktop connects to it through authenticated native transport. Switching to Cloud does not move that data. + +See [API access](/developers/api-ref/authentication) for developer tokens, daemon pairing, and the account API reference. diff --git a/content/docs/developers/api-ref/authentication.mdx b/content/docs/developers/api-ref/authentication.mdx index ce2a193..5da9821 100644 --- a/content/docs/developers/api-ref/authentication.mdx +++ b/content/docs/developers/api-ref/authentication.mdx @@ -1,62 +1,85 @@ --- -title: Authentication -sidebarTitle: Authentication +title: API access +sidebarTitle: API access icon: key-round -description: Understand bearer authentication and the limits of the current beta client API. +description: Connect a script to your Cloud or daemon workspace, then send a first request. --- -This reference documents the routes used by Fluso clients on the current development branch. It is not a promise that every feature is enabled in every deployed build. These beta contracts may change. +The account API lets an integration use the same conversations, Agents, and files as Fluso. Cloud and the daemon share these operations. You do not need a separate API project or a runtime ID. -Use the API host and authenticated session for the same environment. Production uses `https://api.fluso.ai`. Development clients must use their development API and identity environment. +## Choose your workspace -```bash -export FLUSO_API='https://' -export FLUSO_TOKEN='' +| Workspace | API base URL | Full guide and endpoint reference | +| --- | --- | --- | +| Fluso Cloud | `https://api.fluso.ai/v1` | **Settings → Developer tokens → API reference** in your app | +| Fluso Dev | `https://api.dev.khichdi.cc/v1` | [Development API reference](https://api.dev.khichdi.cc/docs) | +| Daemon, default local port | `http://127.0.0.1:4111/v1` | Open `http://127.0.0.1:4111/docs` on its host or through your tunnel | +| Daemon, HTTPS | Your workspace URL followed by `/v1` | Your workspace URL followed by `/docs` | + +The reference includes request bodies, response fields, and runnable examples. Its OpenAPI schema is at `/v1/openapi.json`. A daemon lists the endpoints it supports. Cloud account administration and public sharing do not transfer to it. + + + These examples need the current account API. If **Developer tokens** is missing from your app, contact [support](mailto:support@premai.io) to confirm availability for your release. + + +## Get a token + +### Fluso Cloud -curl "$FLUSO_API/v1/auth/me" \ - -H "Authorization: Bearer $FLUSO_TOKEN" +In **Fluso → Settings → Developer tokens**, create a token. Choose **Read only** to inspect existing data, or **Read and write** to submit work and edit it. Use **Read and write** for the quickstart below. Copy the secret when shown. It cannot be retrieved later. + +A write token can also connect services and approve individual tool calls. Managing credentials and permanent tool permissions requires a signed-in app session. + +### Your daemon + +Configure the [daemon](/workspaces) and its model provider first. On its host, run `./fluso-daemon pair` from the installed package folder. Exchange the one-use code at the daemon's address: + +```bash +curl --fail-with-body --silent --show-error \ + 'http://127.0.0.1:4111/daemon/v1/pair' \ + -H 'Content-Type: application/json' \ + --data-raw '{"code":"PAIRING_CODE","name":"My integration"}' ``` -Keep tokens out of logs and source control. A token acts for its authenticated user. Supplying another user's IDs does not grant access. An `X-API-Key` header alone is not a replacement for the bearer session on these workspace proxy routes. +Replace `PAIRING_CODE` before running. Save the returned `token` and client `id` privately. Codes expire after ten minutes. A device token grants read/write access to the owner's workspace, including tool approvals. -## Read the authenticated identity +Use `pair --manage-host` only when the client also needs provider settings, host-file access, or permission to invite devices. Remote connections require [HTTPS or an SSH tunnel](/workspaces/linux#4-connect-from-your-mac). - +## Try a complete request -Returns `user` and `api_key_id`. The latter is an identifier, not a secret API key. +Open Bash in a new example directory. You need `curl`, `jq`, and `uuidgen`. The example below uses a daemon. For Cloud, use your environment's URL from the table above. Set the token you saved: -```json -{ - "user": { - "id": "22222222-2222-4222-8222-222222222222", - "email": "developer@example.com", - "name": "Developer", - "picture": null, - "created_at": "2026-10-08T09:00:00" - }, - "api_key_id": "33333333-3333-4333-8333-333333333333" -} +```bash +export FLUSO_API_URL='http://127.0.0.1:4111/v1' +export FLUSO_API_TOKEN='YOUR_TOKEN' ``` -## Client sign-in is not generic CLI onboarding +Keep real tokens out of source control, shared terminal recordings, and URLs. Use a secret store for deployed integrations. + +Download the example from that workspace. Read it before running: + +```bash +curl --fail --silent --show-error \ + "$FLUSO_API_URL/examples/quickstart.sh" -o quickstart.sh +cat quickstart.sh +``` + +Then run it in the same Bash shell: + +```bash +source quickstart.sh +``` -The backend has `/v1/auth/client/start`, `/complete`, `/exchange/{code}`, and `/refresh` routes for client handoff. The current frontend's completed handoff supports Telegram. Registering an arbitrary `client: "cli"` does not produce a working browser-to-CLI flow. +The script creates a conversation, requests a short answer, waits for the run, and reads every result page. It keeps `thread_id`, `run_id`, `input_key`, and `input_body` for follow-ups. -For the supported product flow, use [Telegram setup](/remote/telegram). For another integration, confirm its sign-in and token handling with [support](mailto:support@premai.io) before building against the beta routes. +A timeout does not cancel accepted work. Do not rerun the quickstart to recover it: that creates a new request. Read the [saved run and results](/developers/api-ref/runs) instead. -A supported client exchanges a short-lived, single-use code for an access token and refresh token bound to its client identity. `POST /v1/auth/client/refresh` accepts `{"refresh_token": "..."}` and returns refreshed credentials and `api_key_id`. +## Keep access under control -## Read failures correctly +Cloud developer tokens expire after 90 days by default. To rotate one, create a replacement in Settings, update your integration, verify it, then revoke the old token. Revocation can take up to 30 seconds to reach cached authentication and open streams. -| Response | Meaning | -| --- | --- | -| `401` | Missing, invalid, or expired authentication | -| `403` | The authenticated user is not allowed to complete the request | -| `404` | The requested resource is unavailable to this user | -| `409` | A state conflict requires inspection, such as a stale Agent revision | -| `429` | A rate or usage limit was reached. Respect retry guidance | -| `502` / `503` | A service or runtime is unavailable. An interrupted write may still need verification | +Daemon device tokens expire after 30 idle days. Authenticated use renews them. Revoke a device with `DELETE /daemon/v1/clients/{clientId}`. A device can revoke itself. Host-management access also permits revoking other devices. -Do not treat a network failure as proof that a write did not happen. Use operation-specific IDs and reread state before repeating work. +Native applications can use the Cloud browser sign-in flow instead of developer tokens. Follow **auth** in your app's **API reference**, including its code-verifier and refresh-token requirements. Do not reuse an old refresh token after rotation. -Next: [Agents](/developers/api-ref/agents-and-versions), [Threads and messages](/developers/api-ref/threads-and-messages), and [Runs](/developers/api-ref/runs). +Next: [conversations and results](/developers/api-ref/threads-and-messages). diff --git a/content/docs/developers/api-ref/files-and-projects.mdx b/content/docs/developers/api-ref/files-and-projects.mdx index 77124c4..9f2b687 100644 --- a/content/docs/developers/api-ref/files-and-projects.mdx +++ b/content/docs/developers/api-ref/files-and-projects.mdx @@ -1,233 +1,59 @@ --- title: Files and projects -sidebarTitle: Files & projects +sidebarTitle: Files and projects icon: folder-kanban -description: Work with durable project metadata and files in a user's Agent workspace. +description: Upload inputs, read saved output, and protect files from conflicting edits. --- -Workspace projects and files use the `/v1/agent/*` routes. They belong to the authenticated user's durable workspace, outside disposable runtime compute. +Files and projects belong to the authenticated workspace. You do not need a runtime ID. Use the paths returned by the API, including saved attachments. -An [Agent](/developers/api-ref/agents-and-versions) places its main conversation in the configured project when that conversation is first created. Changing its setup later does not move existing conversations. +## Projects -## List projects +Use `/v1/projects` to list or create projects. Read, update, or delete a project at `/v1/projects/{project_id}`. Project IDs are also storage names. Keep the exact returned value when building paths. - - -Returns workspace projects and their thread summaries. - -```bash title="Request" -curl "$FLUSO_API/v1/agent/projects?includeArchived=false" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```json title="Response: 200" -{ - "projects": [ - { - "name": "Release Review", - "goal": "Decide launch readiness", - "createdAt": "2026-08-31T09:00:00Z", - "updatedAt": "2026-08-31T09:00:00Z", - "threads": [] - } - ] -} -``` - -## Create a project - - - -Creates a workspace project and its context file. - -```bash title="Request" -curl "$FLUSO_API/v1/agent/projects" \ - -X POST \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "name": "Release Review", - "goal": "Decide launch readiness", - "instructions": "Keep evidence and decisions in this project." - }' -``` - -```json title="Response: 201" -{ - "name": "Release Review", - "goal": "Decide launch readiness", - "instructions": "Keep evidence and decisions in this project.", - "createdAt": "2026-08-31T09:00:00Z", - "updatedAt": "2026-08-31T09:00:00Z", - "threads": [] -} -``` - -## Update a project - - - -Updates the project named in the request body. - -```bash title="Request" -curl "$FLUSO_API/v1/agent/projects" \ - -X PATCH \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "name": "Release Review", - "goal": "Decide launch readiness and record follow-up owners" - }' -``` - -```json title="Response: 200, selected fields" -{ - "name": "Release Review", - "goal": "Decide launch readiness and record follow-up owners", - "updatedAt": "2026-08-31T09:05:00Z", - "threads": [] -} -``` - -## Export a project - - - -Streams the named project as a ZIP archive. - -```bash title="Request" -curl -G "$FLUSO_API/v1/agent/projects/export" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - --data-urlencode 'name=Release Review' \ - -o release-review.zip -``` - -```http title="Response" -HTTP/1.1 200 OK -Content-Type: application/zip -Content-Disposition: attachment; filename="Release Review.zip"; filename*=UTF-8''Release%20Review.zip -``` - -## List files - - - -Lists files or directories under a workspace path. - -```bash title="Request" -curl "$FLUSO_API/v1/agent/files?path=/files&recursive=false&includeStats=true" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```json title="Response: 200" -{ - "path": "/files", - "recursive": false, - "entries": [ - { - "path": "/files/release-brief.md", - "name": "release-brief.md", - "type": "file", - "size": 1240, - "modifiedAt": "2026-08-31T09:00:00Z", - "mimeType": "text/markdown" - } - ] -} -``` +Deleting a project deletes its conversations and files, then retires their schedules. Save needed results before deletion. See your [workspace's endpoint reference](/developers/api-ref/authentication#choose-your-workspace) for export and deletion behavior. ## Read a text file - - -Returns a UTF-8 text file as JSON. +After [setting your API URL and token](/developers/api-ref/authentication), run: -```bash title="Request" -curl -G "$FLUSO_API/v1/agent/files/read" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - --data-urlencode 'path=/files/release-brief.md' +```bash +curl --fail-with-body --silent --show-error --get \ + "$FLUSO_API_URL/files/read" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" \ + --data-urlencode 'path=/projects/Home/files/notes.md' ``` -```json title="Response: 200" -{ - "path": "/files/release-brief.md", - "content": "# Release candidate\n\nKnown issue: ...\n", - "encoding": "utf-8" -} -``` +Use `GET /v1/files` to browse and `GET /v1/files/download` for file bytes. A text read includes a `revision` for safe replacement. ## Upload a file -The current client uploads to temporary object storage, then confirms an import into the workspace. The older multipart `/v1/agent/files/upload` route is deprecated. - -1. `POST /v1/files/upload/presigned-url/get` with the file name, MIME type, size in bytes, and optional destination. -2. POST the returned `upload_fields` and file bytes as multipart form data to `upload_url`. The file is the last field. Do not send your Fluso bearer token to that storage URL. -3. `POST /v1/files/upload/presigned-url/confirm` with `{"id": ""}`. -4. Poll `GET /v1/files` for upload tracking and verify the destination file after the import finishes. - -```json title="Presign body" -{ - "file_name": "release-brief.md", - "mime_type": "text/markdown", - "file_size": 1240, - "target_path": "/projects/Home/files/release-brief.md", - "expected_revision": "missing" -} -``` - -Use the actual byte count. `expected_revision: "missing"` requires that no file exists at the destination when the import commits. To edit an existing file, supply its current revision hash. Omitting the field allows ordinary replacement in import order. - -Confirmation queues the import. It does not mean the file is already saved. Tracking rows remain for in-progress uploads, failures, and undismissed history warnings. Successful imports without warnings leave that tracking list. +1. Create an upload with `POST /v1/uploads`, its filename, MIME type, size, and a saved `Idempotency-Key`. +2. Send the bytes to `upload_url` in a multipart POST. Include all returned `upload_fields` unchanged, with the file last. +3. Confirm with `POST /v1/uploads/confirm` and `{"id":"UPLOAD_ID"}`. +4. Read `GET /v1/uploads/{upload_id}` until it finishes. +5. Attach its returned `target_path` to a conversation input. -An account can have at most four pending uploads. A fifth presign request returns `429`. Confirm active uploads before requesting more. For an abandoned upload, use `DELETE /v1/files/upload/presigned-url/cancel/{id}` to cancel its pending record. Removing an attachment from the composer is not proof that the upload record was cancelled. +Do not send your Fluso bearer token to `upload_url`. -If the destination changed, reread it before saving again. If prior file history cannot be preserved, inspect the reported failure or warning rather than treating the operation as a normal versioned save. Agent knowledge uses its [separate attach-and-save flow](/developers/api-ref/agents-and-versions#attach-knowledge). +A `confirmed` upload is ready to attach. `history_unavailable` also means the file was saved, but its new version was not recorded. Show that warning. For `failed`, `cancelled`, or `conflict`, inspect the saved error before starting another transfer. -## Download a file +If a request times out, read the existing upload. Starting another upload can duplicate work. Confirmed receipts do not guarantee that a file remains unchanged after later edits. - +The full reference includes runnable **upload**, **attach**, and **download** examples. It also covers expired transfer credentials and recovery without another transfer. -Streams a file with attachment headers. +## Edit without losing another change -```bash title="Request" -curl -G "$FLUSO_API/v1/agent/files/download" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - --data-urlencode 'path=/files/release-report.pdf' \ - -o release-report.pdf -``` - -```http title="Response" -HTTP/1.1 200 OK -Content-Type: application/pdf -Content-Disposition: attachment; filename="release-report.pdf"; filename*=UTF-8''release-report.pdf -``` +Use `PUT /v1/files/text` with `path`, `content`, and the latest `revision` to replace text. Use `overwrite: false` when creating a new file that must not replace an existing path. -## Delete a file +If another writer changed the file, read the new content and merge before retrying. Do not drop the revision check to force an overwrite. - - -Deletes one file, or a directory when `recursive=true`. - -```bash title="Request" -curl -G "$FLUSO_API/v1/agent/files" \ - -X DELETE \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - --data-urlencode 'path=/files/release-brief.md' \ - --data 'recursive=false' -``` - -```json title="Response: 200" -{ - "success": true, - "path": "/files/release-brief.md" -} -``` +Text writes allow 1 MiB of UTF-8 content. Text reads default to 5 MiB. Use uploads and downloads for larger or binary files. - - A recursive directory delete removes every child path. List the target first - when its contents are not already known. - + + Versions and comments apply to ordinary files under `/files/` and `/projects/{project_id}/files/`, excluding hidden directories. -## Next + Replacement refuses to lose existing history unless you explicitly permit it with `allow_history_loss: true`. Treat that option as a data-loss decision. For uploads, changing a failed request requires a new key. Retrying the old key returns its saved failure. -Read [Projects](/features/projects) for the product workflow, choose a project for [Agents](/developers/api-ref/agents-and-versions), or create project-bound [Threads and messages](/developers/api-ref/threads-and-messages). + A directory copy can partly succeed. Inspect the destination before retrying it. An attachment with `available: false` records historical output whose file is no longer at that path. + diff --git a/content/docs/developers/api-ref/meta.json b/content/docs/developers/api-ref/meta.json index 7bf9642..cb5d332 100644 --- a/content/docs/developers/api-ref/meta.json +++ b/content/docs/developers/api-ref/meta.json @@ -3,14 +3,14 @@ "icon": "code-xml", "defaultOpen": false, "pages": [ - "architecture", "authentication", - "agents-and-versions", "threads-and-messages", + "agents-and-versions", + "files-and-projects", "schedules", "runs", - "files-and-projects", "sharing", - "usage" + "usage", + "architecture" ] } diff --git a/content/docs/developers/api-ref/runs.mdx b/content/docs/developers/api-ref/runs.mdx index 5834e2b..b3b05f8 100644 --- a/content/docs/developers/api-ref/runs.mdx +++ b/content/docs/developers/api-ref/runs.mdx @@ -1,115 +1,46 @@ --- -title: Runs -sidebarTitle: Runs +title: Runs and recovery +sidebarTitle: Runs and recovery icon: activity -description: Inspect Agent runs and stop work that is still active. +description: Follow accepted work, recover after a disconnection, and stop a run deliberately. --- -Run IDs are request IDs. A run can be `queued`, `running`, `held`, `completed`, `failed`, or `cancelled`. +Submitting an input returns a receipt with `thread_id`, `input_id`, and `run_id`. Save all three. **202 means accepted, not completed.** -Runs originate from [messages](/developers/api-ref/threads-and-messages) and [schedules](/developers/api-ref/schedules). +Read `GET /v1/threads/{thread_id}/runs/{run_id}` to check progress. Read `GET /v1/threads/{thread_id}/state` for queued work and pending actions. -## List runs +## Understand the status - +| Status | What to do | +| --- | --- | +| `queued` or `running` | Wait, then read again. | +| `waiting_action` | Read the pending approval or connection request in thread state. | +| `held` | Inspect the state and queued inputs before resuming. | +| `completed` | Read all saved result pages and check the answer. | +| `failed` or `cancelled` | Read the error and any saved results before submitting more work. | -Returns recent runs, with optional thread, request, and attention filters. +A completed run can contain a failed tool call. A failed run can still have changed an external service. Check the results against the requested task. -```bash title="Request" -curl "$FLUSO_API/v1/agent-runs?threadId=thread_agent_release&limit=20" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` +## Recover after a timeout -```json title="Response: 200" -{ - "agent_runs": [ - { - "id": "req_review_01", - "request_id": "req_review_01", - "thread_id": "thread_agent_release", - "target_thread_id": "thread_agent_release", - "agent_id": "agt_0123456789ab4def8123456789abcdef", - "thread_title": "Release review", - "status": "running", - "origin": "user", - "created_at": "2026-08-31T09:00:00Z", - "started_at": "2026-08-31T09:00:01Z", - "updated_at": "2026-08-31T09:00:02Z", - "completed_at": null, - "outcome": null, - "result_ref": null, - "error": null - } - ], - "settlement_cursor": "2026-08-31T09:00:02Z/req_review_01" -} -``` +- If the submission response was lost, retry the same body with the same `Idempotency-Key`. +- If you received a receipt, read that run and its [saved items](/developers/api-ref/threads-and-messages#read-message-history). +- If a stream closes, reconnect for new events and read saved state for missed results. -Use `attention=true` for owned approvals and current failures, with approval requests first. It also adds `schedule_failures` for routine delivery problems that did not produce a run. `agentId`, `threadId`, `status`, repeated `requestId`, and `limit` can filter the result. Add `includeRoutineResults=true` to include completed top-level routine runs. Delegated completions remain in their conversations. +A client timeout does not cancel work. Creating a new key can start another execution. -The feed is filtered before limiting. It is a recent status view, not a complete audit archive. A failed request superseded by newer work is omitted from attention. +Input retry receipts remain until 24 hours after completion. After that, inspect the saved run before deciding whether to submit new work. Runs remain readable until their conversation is deleted. -## Read one run + + An error from `POST /v1/threads` can include `error.details.thread_id`. Save that ID and inspect the thread state. Its presence does not prove input acceptance. - + After resolving the cause, send the original input and key to that thread's `/inputs` endpoint. This can recover an uncertain acceptance within the retry window. + -Returns one matching run. Include `threadId` when request IDs may repeat across threads. +## Stop work -```bash title="Request" -curl "$FLUSO_API/v1/agent-runs/req_review_01?threadId=thread_agent_release" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` +Use `POST /v1/threads/{thread_id}/runs/{run_id}/cancel` for one run. Use `POST /v1/threads/{thread_id}/stop` for the conversation. -```json title="Response: 200" -{ - "id": "req_review_01", - "request_id": "req_review_01", - "thread_id": "thread_agent_release", - "target_thread_id": "thread_agent_release", - "agent_id": "agt_0123456789ab4def8123456789abcdef", - "status": "completed", - "origin": "user", - "created_at": "2026-08-31T09:00:00Z", - "started_at": "2026-08-31T09:00:01Z", - "updated_at": "2026-08-31T09:01:10Z", - "completed_at": "2026-08-31T09:01:10Z", - "outcome": "completed", - "result_ref": { - "thread_id": "thread_agent_release", - "request_id": "req_review_01" - }, - "error": null -} -``` +Read the state until execution stops. A cancellation acknowledgement does not prove that a tool has stopped, and cancellation cannot undo a completed external action. -## Stop a run - - - -Requests cancellation without starting a dormant runtime only to stop it. - -```bash title="Request" -curl "$FLUSO_API/v1/agent-runs/req_review_01/stop" \ - -X POST \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{"threadId":"thread_agent_release"}' -``` - -```json title="Response: 200" -{ - "thread_id": "thread_agent_release", - "request_id": "req_review_01", - "status": "cancelled" -} -``` - -The status can also be `already_terminal` when the run settled before cancellation reached it. Stopping does not undo completed tool actions or pause future routine occurrences. Manage the routine separately. - -## Read a durable settlement status - -`GET /v1/agent-runs/{requestId}/status?thread_id={threadId}` returns `state: active | awaiting_approval | settled` and `outcome: completed | failed | canceled | null`. This endpoint uses `thread_id` and the single-l spelling `canceled`, unlike the list projection above. A missing status is not proof of completion. - -## Next - -Inspect token cost in [Usage](/developers/api-ref/usage), or create recurring and one-time [Schedules](/developers/api-ref/schedules). +Use the **Recovery and cancellation** examples in your [workspace's full reference](/developers/api-ref/authentication#choose-your-workspace) for retries, held inputs, and cancellation. diff --git a/content/docs/developers/api-ref/schedules.mdx b/content/docs/developers/api-ref/schedules.mdx index 30c4738..7b96da3 100644 --- a/content/docs/developers/api-ref/schedules.mdx +++ b/content/docs/developers/api-ref/schedules.mdx @@ -2,146 +2,42 @@ title: Schedules sidebarTitle: Schedules icon: calendar-clock -description: Create, inspect, pause, and delete Agent schedules. +description: Schedule Agent work once or repeatedly, and manage future runs. --- -The UI calls these records **routines**. A schedule belongs to an Agent and one of that Agent's existing threads. Supply exactly one timing field: a five-field `cron` expression or a future `at` timestamp with an offset. `timezone` must be an IANA time zone. - -See [Threads and messages](/developers/api-ref/threads-and-messages) for the target thread and [Runs](/developers/api-ref/runs) for execution state. - -## List schedules - - - -Returns schedules for an Agent, optionally filtered by thread. - -```bash title="Request" -curl "$FLUSO_API/v1/agents/agt_0123456789ab4def8123456789abcdef/schedules?threadId=thread_agent_release&page=1&limit=100" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```json title="Response: 200" -{ - "schedules": [ - { - "id": "11111111-1111-4111-8111-111111111111", - "agentId": "agt_0123456789ab4def8123456789abcdef", - "threadId": "thread_agent_release", - "message": "Review the release candidate", - "cron": "0 9 * * 1-5", - "timezone": "Asia/Kolkata", - "status": "active", - "pauseReason": null, - "nextRunAt": "2026-09-01T03:30:00Z", - "attempts": 0, - "createdAt": "2026-08-31T09:00:00Z", - "updatedAt": "2026-08-31T09:00:00Z", - "firedAt": null, - "lastRun": null - } - ], - "pagination": { "page": 1, "limit": 100, "total": 1, "pages": 1 } -} -``` +A schedule sends a message to an Agent's conversation. The app calls these schedules **routines**. ## Create a schedule - - -Creates a recurring or one-time schedule. The caller supplies a UUID so retries can be idempotent. - -```bash title="Request" -curl "$FLUSO_API/v1/agents/agt_0123456789ab4def8123456789abcdef/schedules" \ - -X POST \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "id": "11111111-1111-4111-8111-111111111111", - "threadId": "thread_agent_release", - "message": "Review the release candidate", - "cron": "0 9 * * 1-5", - "timezone": "Asia/Kolkata" - }' -``` - -For a durable one-time schedule, replace `cron` with an offset-bearing `at` value: +Send `POST /v1/agents/{agent_id}/schedules` with a new UUID as `id`. Keep that ID when retrying creation. -```json title="One-time timing fields" -{ - "at": "2030-01-15T09:00:00+05:30", - "timezone": "Asia/Kolkata" -} -``` +For a weekday morning brief: -```json title="Response: 201" +```json { - "id": "11111111-1111-4111-8111-111111111111", - "agentId": "agt_0123456789ab4def8123456789abcdef", - "threadId": "thread_agent_release", - "message": "Review the release candidate", + "id": "REPLACE_WITH_NEW_UUID", + "thread_id": "AGENT_THREAD_ID", + "message": "Prepare a short morning brief. Start with what needs my attention today.", "cron": "0 9 * * 1-5", - "timezone": "Asia/Kolkata", - "status": "active", - "pauseReason": null, - "nextRunAt": "2026-09-01T03:30:00Z", - "attempts": 0, - "createdAt": "2026-08-31T09:00:00Z", - "updatedAt": "2026-08-31T09:00:00Z", - "firedAt": null, - "lastRun": null + "timezone": "Europe/Zurich" } ``` -Choose an `at` value in the future at submission time. Replaying the same ID and body while the schedule is live returns the existing schedule with `200`. Reusing the live ID with different fields returns a conflict. After a one-time schedule fires or a schedule is deleted, that ID returns `410` for 24 hours. - -## Pause or edit a schedule - - - -Updates submitted fields. `threadId` is always required to guard the target. - -```bash title="Request" -curl "$FLUSO_API/v1/agents/agt_0123456789ab4def8123456789abcdef/schedules/11111111-1111-4111-8111-111111111111" \ - -X PATCH \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{"threadId":"thread_agent_release","status":"paused"}' -``` - -```json title="Response: 200, selected fields" -{ - "id": "11111111-1111-4111-8111-111111111111", - "agentId": "agt_0123456789ab4def8123456789abcdef", - "threadId": "thread_agent_release", - "status": "paused", - "nextRunAt": "2026-09-01T03:30:00Z" -} -``` - -## Delete a schedule - - - -Deletes a schedule after matching it to the supplied thread. - -```bash title="Request" -curl "$FLUSO_API/v1/agents/agt_0123456789ab4def8123456789abcdef/schedules/11111111-1111-4111-8111-111111111111?threadId=thread_agent_release" \ - -X DELETE \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```http title="Response" -HTTP/1.1 204 No Content -``` +The conversation must belong to that Agent. Supply exactly one of `cron` or `at`. For a one-time run, use a future `at` timestamp with a UTC offset instead of `cron`. -## Delivery behavior +Use an IANA timezone. At daylight-saving changes, skipped local times move forward by the gap. Repeated times run at their first occurrence. -Each occurrence has a stable request ID and enters the target as `followUp`. Acceptance means queued or admitted, not completed. Another occurrence of the same recurring schedule waits while the prior request is queued, running, or held for approval. +## Review and change future work -Temporary delivery failures retry with delays. 24 failed delivery attempts pause the schedule. Refused requests and invariant conflicts can pause it immediately. Usage limits defer delivery. Editing or pausing a schedule does not cancel a request already admitted. +| Action | Endpoint | +| --- | --- | +| List all schedules | `GET /v1/schedules` | +| List one Agent's schedules | `GET /v1/agents/{agent_id}/schedules` | +| Edit or pause | `PATCH /v1/schedules/{schedule_id}` | +| Delete | `DELETE /v1/schedules/{schedule_id}` | -One-time schedules retire when accepted, even if the later Agent run fails. Deleting the target thread or Agent removes its schedules. See [Routines](/developers/schedules) for the user workflow and timezone controls. +List responses include last-run information. Use the fields in your [workspace's full reference](/developers/api-ref/authentication#choose-your-workspace) to edit the message, timing, or status. -## Next +Deleting a schedule prevents future submissions. It does not cancel already accepted work. [Cancel that run separately](/developers/api-ref/runs#stop-work) if needed. -Inspect each execution in [Runs](/developers/api-ref/runs), or manage the target in [Threads and messages](/developers/api-ref/threads-and-messages). +On a daemon, the host must be awake for work to run. After downtime, missed recurring runs are combined into one catch-up run. Failed scheduled work can pause the schedule for attention. diff --git a/content/docs/developers/api-ref/sharing.mdx b/content/docs/developers/api-ref/sharing.mdx index f38dace..57378c2 100644 --- a/content/docs/developers/api-ref/sharing.mdx +++ b/content/docs/developers/api-ref/sharing.mdx @@ -5,29 +5,29 @@ icon: share-2 description: Publish a revocable Agent snapshot and import an independent copy. --- -Sharing stores a public snapshot. It does not expose a live view of the owner's workspace. Owner routes require [authentication](/developers/api-ref/authentication). A public snapshot read does not. +Public sharing is Cloud-only. Sharing an Agent stores a public snapshot. It does not expose a live view of the owner's workspace. Owner routes require [authentication](/developers/api-ref/authentication). A public snapshot read does not. ## Publish and revoke | Method and path | Behavior | | --- | --- | -| `GET /v1/agents/{agentId}/share-options` | Available custom skills, knowledge, and custom connections | -| `GET /v1/agents/{agentId}/share` | Current link record or `null` | -| `POST /v1/agents/{agentId}/share` | Publish a snapshot | -| `POST /v1/agent-shares/{shareId}/refresh` | Replace the snapshot at the same link | -| `DELETE /v1/agent-shares/{shareId}` | Revoke the link | +| `GET /v1/agents/{agent_id}/share-options` | Available custom skills, knowledge, and custom connections | +| `GET /v1/agents/{agent_id}/share` | Current link record or `null` | +| `POST /v1/agents/{agent_id}/share` | Publish a snapshot | +| `POST /v1/agent-shares/{share_id}/refresh` | Replace the snapshot at the same link | +| `DELETE /v1/agent-shares/{share_id}` | Revoke the link | Publishing and refreshing accept a selection: ```json { "skills": [], - "knowledgeFiles": [], + "knowledge_files": [], "connectors": [] } ``` -Use IDs from share options for skills, knowledge names for `knowledgeFiles`, and server IDs for `connectors`. Instructions and portable settings are included even with empty selections. Selected skill folders and knowledge contents share limits of 20 MiB and 512 regular files. Symbolic links are rejected. Custom connection URLs with query parameters are excluded from share options. +Use IDs from share options for skills, knowledge names for `knowledge_files`, and server IDs for `connectors`. Instructions and portable settings are included even with empty selections. Selected skill folders and knowledge contents share limits of 20 MiB and 512 regular files. Symbolic links are rejected. Custom connection URLs with query parameters are excluded from share options. Review all selected contents and connection URLs before publishing. Stored authentication, conversations, routines, and project files are not bundled. See [Sharing and copies](/developers/guides/sharing) for the recipient experience. @@ -36,20 +36,20 @@ Review all selected contents and connection URLs before publishing. Stored authe | Method and path | Behavior | | --- | --- | | `GET /v1/agent-templates` | Fluso starting-point snapshots | -| `GET /v1/shared/agents/{shareId}` | Public setup, resource manifest, required filenames, and `publishedAt` | -| `POST /v1/shared/agents/{shareId}/import` | Authenticated import into the recipient's workspace | +| `GET /v1/shared/agents/{share_id}` | Public setup, resource manifest, required filenames, and `published_at` | +| `POST /v1/shared/agents/{share_id}/import` | Authenticated import into the recipient's workspace | ```json { "name": "My release reviewer", - "projectName": "Home", - "creationId": "11111111-1111-4111-8111-111111111111", - "publishedAt": "" + "project_id": "Home", + "creation_id": "11111111-1111-4111-8111-111111111111", + "published_at": "" } ``` -Reuse `creationId` to recover an uncertain import without creating another copy. A changed snapshot returns a conflict rather than silently importing something different from the preview. +Send a saved `Idempotency-Key` when importing. Reuse it and the unchanged body to recover an uncertain import without creating another copy. The `creation_id` field is also accepted, but must not conflict with that key. A changed snapshot returns a conflict rather than silently importing something different from the preview. -The recipient gets independent setup and selected resource copies. Imported custom skills receive distinct names. App credentials are never inherited. Connection setup may remain unfinished. +The recipient gets independent setup and selected resource copies. Imported custom skills receive distinct names. App credentials are never inherited. Read the imported Agent's `setup_requirements`, then complete missing connections and consent in its manager conversation. A revoked link becomes unavailable. Unsupported older snapshots may require republication by their owner. Revocation does not delete copies already imported. diff --git a/content/docs/developers/api-ref/threads-and-messages.mdx b/content/docs/developers/api-ref/threads-and-messages.mdx index b556a4e..89a4d3c 100644 --- a/content/docs/developers/api-ref/threads-and-messages.mdx +++ b/content/docs/developers/api-ref/threads-and-messages.mdx @@ -1,73 +1,56 @@ --- -title: Threads and messages -sidebarTitle: Threads and messages +title: Conversations and results +sidebarTitle: Conversations and results icon: messages-square -description: Open an Agent's main conversation, send a turn, and read durable history. +description: Submit work, continue a conversation, and read the complete saved answer. --- -A thread is a conversation stored in a project. Agent-owned conversations have a saved Agent binding. An ordinary chat cannot be converted by adding an Agent ID to a later message. +A **thread** is a conversation. An **input** is a message you submit. A **run** tracks the resulting work. -Each Agent has one main conversation and a separate Manager. Delegated sessions are created by the Agent's session tools. Do not create a new top-level thread for every message. +Start with the runnable [API quickstart](/developers/api-ref/authentication#try-a-complete-request). These are the operations behind it. -## Open the main conversation +## Create a conversation - +Send `POST /v1/threads` with an `Idempotency-Key` and an input body: -Use the Agent's configured project and its ID in `metadata`. Omit `thread_id` to let repeated opens resolve to the existing main conversation. - -```bash -curl "$FLUSO_API/v1/threads" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "project_id": "Home", - "metadata": {"agentId": ""} - }' +```json +{ + "input": "Explain three ways to check whether a source is reliable." +} ``` -Use the returned `thread_id` and `project_id`, including when they refer to an existing conversation. Adding `"role": "manager"` to the creation metadata opens that Agent's Manager instead. Ordinary thread creation omits the Agent metadata. +Save the key and body before sending. The receipt returns `thread_id` and `run_id`. Use those values rather than deriving one from the other. + +To open a conversation without starting work, omit `input` and `attachments`. To use a saved Agent, include `agent_id`. Its main conversation is reused. `purpose: "manager"` opens its setup conversation. ## Send a message - - -```bash -curl -N "$FLUSO_API/v1/chat" \ - -H "Authorization: Bearer $FLUSO_TOKEN" \ - -H 'Content-Type: application/json' \ - -d '{ - "threadId": "", - "projectName": "", - "agentId": "", - "requestId": "", - "message": "Review the release evidence already in this project.", - "streamingBehavior": "followUp" - }' -``` +Send `POST /v1/threads/{thread_id}/inputs` with the next `input` and a new key. If the conversation is busy, use `mode: "queue"` to wait or `mode: "steer"` to guide active work. -The response uses the AI SDK UI message stream over SSE. A request can be queued, running, or held for approval before it settles. Stream acceptance is not completion. Use [Runs](/developers/api-ref/runs) and persisted history to recover after a disconnect. +A retry must keep the original key and unchanged body. Reusing a key with different content returns 409. A new key means new work. -Use the same `requestId` when checking or retrying an ambiguous submission, with the same request content. Use a new ID for a genuinely new turn. +The **follow-up** example in your [workspace's reference](/developers/api-ref/authentication#choose-your-workspace) continues the quickstart conversation in the same shell. -`followUp` queues a separate turn if work is active. `steer` brings new input into the active turn where compatible, or starts it when idle. Input with a different saved Agent configuration must wait for its own turn. Do not assume every queued message can be steered. +## Read message history -## Read and organize conversations +Read `GET /v1/threads/{thread_id}/items?run_id={run_id}` for messages, tool results, and attachments belonging to a run. Omit the filter to read the conversation. -| Method and path | Behavior | -| --- | --- | -| `GET /v1/threads?page=1&limit=20` | General chat list. Excludes Agent-owned conversations | -| `GET /v1/agents/{agentId}/chats` | Agent conversations, including `isOrchestrator`, `createdByThreadId`, `previousConfig`, `forked`, and `role` | -| `GET /v1/threads/{threadId}?project_id=Home` | Thread metadata | -| `GET /v1/threads/{threadId}/messages?project_id=Home` | Persisted messages and tool-status projections | -| `PATCH /v1/threads/{threadId}?project_id=Home` | Title, supported metadata, pin, archive state, or labels | -| `DELETE /v1/threads/{threadId}?project_id=Home` | Delete an eligible thread and its schedules | +Entries are in `data`. Read every page: pass `next_cursor` as `after` with the unchanged `revision`, until `next_cursor` is null. Keep filters unchanged between pages. -Thread and message responses use snake_case. Message `content` and `metadata` can contain serialized JSON. Parse them before reading their fields. +If a snapshot edit or deletion causes a pagination conflict, discard the partial read and restart without `after` or `revision`. New messages alone do not invalidate the snapshot. -## Read message history +Read the saved [run status](/developers/api-ref/runs) as well as the messages. An empty page does not prove the run finished or failed. + +## Follow live progress + +`GET /v1/threads/{thread_id}/events` streams Server-Sent Events. Submitting with `stream: true` starts with an `input.accepted` receipt. Save it before processing later events. + +Streams do not replay missed events. After reconnecting, read saved state and items. Ignore unknown event types and keepalive comments. Replace temporary text with its saved item to avoid showing it twice. + +Use the reference's **watch.mjs** example for a client that combines live updates, result pages, and recovery. It requires Node 22 or later. -Use the messages endpoint above when reconnecting or keeping a copy of a conversation. Routine metadata identifies its scheduled origin. Collapsing it in the UI does not remove its full instructions from history. +## Delete a conversation -Agent and role bindings are fixed at creation. The main conversation uses the current Agent setup on a later turn. Delegated sessions keep their creation setup. The thread endpoints cannot archive or delete the main conversation. Deleting its project also removes it, as explained in [Files and context](/developers/guides/files). +`DELETE /v1/threads/{thread_id}` deletes an ordinary conversation and stops its execution. Save needed results first. Agent main and manager conversations require deleting their Agent instead. -For user-facing behavior, see [Conversations and sessions](/developers/guides/conversations). +Conversation deletion does not remove a separately published chat snapshot or all workspace files. diff --git a/content/docs/developers/api-ref/usage.mdx b/content/docs/developers/api-ref/usage.mdx index 501bb71..ff2591b 100644 --- a/content/docs/developers/api-ref/usage.mdx +++ b/content/docs/developers/api-ref/usage.mdx @@ -2,143 +2,29 @@ title: Usage sidebarTitle: Usage icon: chart-no-axes-combined -description: Read current-user totals, session summaries, and filtered raw usage entries. +description: Read recorded model usage for a workspace or one run. --- -Usage records are user-scoped. Token counts and costs use camelCase fields, and cost values are denominated in US dollars. +Usage reports describe recorded activity. They can arrive after execution and are not a final bill. A missing report or unknown price does not mean zero cost. -Filter entries by the request and thread identifiers returned from [Runs](/developers/api-ref/runs). +## Read workspace totals -## Read usage totals +Use `GET /v1/token-usage` for account totals and its session summaries. Public response fields use snake_case, such as `last_updated` and `entry_count`. - +## Inspect a run -Returns totals for the current user plus a map of session summaries. +`GET /v1/threads/{thread_id}/runs/{run_id}/usage` returns the run's recorded tokens and estimated cost. It returns `null` when no usage is reported. Cost can also be null when a recorded call has no price. -```bash title="Request" -curl "$FLUSO_API/v1/token-usage" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```json title="Response: 200, selected fields" -{ - "userId": "22222222-2222-4222-8222-222222222222", - "lastUpdated": "2026-08-31T09:05:00Z", - "totals": { - "tokens": { - "input": 120, - "output": 40, - "cacheRead": 0, - "cacheWrite": 0, - "total": 160 - }, - "cost": { - "input": 0.001, - "output": 0.002, - "cacheRead": 0, - "cacheWrite": 0, - "total": 0.003 - }, - "entryCount": 1 - } -} -``` - -## List session summaries - - - -Returns the session map from the totals response as an array. - -```bash title="Request" -curl "$FLUSO_API/v1/token-usage/sessions" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` +For individual records, use `GET /v1/token-usage/entries` with `thread_id` and `run_id`. Other filters include `provider`, `model`, `since`, and `limit`. -```json title="Response: 200, selected fields" -[ - { - "sessionId": "sess_ddf284f4", - "threadId": "thread_agent_release", - "firstSeen": "2026-08-31T09:00:00Z", - "lastUpdated": "2026-08-31T09:05:00Z", - "entryCount": 1, - "tokens": { - "input": 120, - "output": 40, - "cacheRead": 0, - "cacheWrite": 0, - "total": 160 - }, - "cost": { - "input": 0.001, - "output": 0.002, - "cacheRead": 0, - "cacheWrite": 0, - "total": 0.003 - } - } -] +```bash +curl --fail-with-body --silent --show-error --get \ + "$FLUSO_API_URL/token-usage/entries" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" \ + --data-urlencode "thread_id=$thread_id" \ + --data-urlencode "run_id=$run_id" ``` -## List usage entries - - - -Returns raw usage entries that match the submitted filters. - -```bash title="Request" -curl "$FLUSO_API/v1/token-usage/entries?threadId=thread_agent_release&limit=100" \ - -H "Authorization: Bearer $FLUSO_TOKEN" -``` - -```json title="Response: 200, selected fields" -[ - { - "schemaVersion": 2, - "eventId": "use_0123456789abcdef0123456789abcdef", - "source": "pi-provider", - "ts": "2026-08-31T09:05:00Z", - "sessionId": "sess_ddf284f4", - "threadId": "thread_agent_release", - "component": "chat-agent", - "measurement": "actual", - "requestId": "req_review_01", - "provider": "openrouter", - "status": "success", - "model": "provider/model-id", - "tokens": { - "input": 120, - "output": 40, - "cacheRead": 0, - "cacheWrite": 0, - "total": 160 - }, - "cost": { - "input": 0.001, - "output": 0.002, - "cacheRead": 0, - "cacheWrite": 0, - "total": 0.003 - }, - "costSource": "provider" - } -] -``` - -| Parameter | Matches | Default | -| --- | --- | --- | -| `sessionId` | Session ID | - | -| `threadId` | Thread ID | - | -| `requestId` | Request ID | - | -| `component` | Emitting component | - | -| `provider` | Model provider | - | -| `providerSessionId` | Provider session ID | - | -| `providerGenerationId` | Provider generation ID | - | -| `model` | Model ID | - | -| `since` | Entries at or after an RFC 3339 instant | - | -| `limit` | Maximum entries, from 1 through 1000 | `100` | - -## Next +Set the API URL, token, and IDs through the [quickstart](/developers/api-ref/authentication#try-a-complete-request) first. Read access is sufficient for usage queries. They do not start workspace compute. -Correlate costs with [Runs](/developers/api-ref/runs), or review the service boundaries in [Architecture](/developers/api-ref/architecture). +Use your [workspace's endpoint reference](/developers/api-ref/authentication#choose-your-workspace) for all fields and filters. For daemon inference charges, check the model provider's billing as well. diff --git a/content/docs/features/open-architecture.mdx b/content/docs/features/open-architecture.mdx index c75bd35..c930355 100644 --- a/content/docs/features/open-architecture.mdx +++ b/content/docs/features/open-architecture.mdx @@ -52,4 +52,4 @@ These exports are independent snapshots. Later Fluso changes do not silently rew ## APIs for continuous access -Use the [API reference](/developers/api-ref/authentication) when a one-time download is not enough. The beta client API exposes Agent setup, sharing, projects and files, conversations, schedules, runs, and usage. Authenticate each request with a user bearer token and store only the data your integration needs. +Use [API access](/developers/api-ref/authentication) when a one-time download is not enough. The account API covers Agents, conversations, files, schedules, runs, and usage on Cloud and [daemon workspaces](/workspaces). Authenticate with a Cloud developer token or a paired daemon token. Store only the data your integration needs. diff --git a/content/docs/index.mdx b/content/docs/index.mdx index e37dec3..15b1ac2 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -48,6 +48,9 @@ Pick a workflow that matches a problem you have right now. Each one is a story, ## When you need it + + Set up a workspace on your Mac or a Linux host, including a supported Raspberry Pi. + The five primitives: chat, apps, skills, tasks, memory. diff --git a/content/docs/meta.json b/content/docs/meta.json index 7435c5a..a04126c 100644 --- a/content/docs/meta.json +++ b/content/docs/meta.json @@ -25,6 +25,8 @@ "---Remote access---", "...remote", "---Workflows---", - "...workflows" + "...workflows", + "---Workspaces---", + "...workspaces" ] } diff --git a/content/docs/workspaces/index.mdx b/content/docs/workspaces/index.mdx new file mode 100644 index 0000000..e44548b --- /dev/null +++ b/content/docs/workspaces/index.mdx @@ -0,0 +1,55 @@ +--- +title: Run Fluso on your machine +sidebarTitle: Overview +icon: monitor +description: Keep a workspace on your Mac or a Linux machine, and use it from Fluso. +--- + +You can use Fluso Cloud, or run a workspace on a machine you own. For example, your Mac can work with a folder of reports. A Linux machine can keep a scheduled task running after you close your laptop. + +The **daemon** is the background program that runs this workspace. You install it separately from the Fluso desktop app. + +## Choose where the work runs + +| Workspace | Where work runs and files stay | What you manage | +| --- | --- | --- | +| Fluso Cloud | Fluso's managed service | Your work and app connections | +| This Mac | Your Apple Silicon Mac | The daemon, model provider, and backups | +| Remote workspace | Your Linux machine or another supported host | The daemon, secure connection, model provider, and backups | + +These workspaces keep separate conversations, Agents, files, and connections. Switching between them does not move or synchronize your work. + + + + Install the package, choose a model provider, and connect This Mac. + + + Use a Linux host or a Raspberry Pi with a supported operating system and KVM. + + + +[Contact us](mailto:support@premai.io?subject=Fluso%20daemon%20download) for the daemon download links. Include your operating system, processor type, and whether you use Fluso or Fluso Dev. We can confirm the matching package and desktop version. + +## What stays on your machine + +The daemon stores workspace files and conversations on its host. It has no access to your personal folders until you share them. + +Your chosen model provider receives the content needed for inference. Connected services receive their tool requests. Running the daemon locally does not make these calls offline or keep all processing on your machine. + +You supply your own provider credentials. The desktop interface still needs an internet connection and Fluso sign-in. You can configure the daemon and use its API without the desktop app. + +## Use it day to day + +Open **Workspaces** from the desktop account menu. Choose **This Mac** or a saved remote connection. Settings on this page apply to the selected machine. + +Closing the app or switching workspaces leaves accepted work running. Opening the app again starts on **Fluso Cloud**. Select your saved connection to return. + +The host must remain awake and connected for work to continue. After downtime, missed recurring runs are combined into one catch-up run. + + + Conversations, Agents, files, routines, and custom MCP connections use the selected workspace. Cloud billing, managed app connections, ingestion services, legacy Tasks, and public sharing are not available through the daemon. + + Each paired client has access to the same owner's workspace. Pairing does not create separate user accounts or private areas for different people. + + +Next, learn how to [share folders, update, and back up your workspace](/workspaces/manage). For scripts and integrations, start with [API access](/developers/api-ref/authentication). diff --git a/content/docs/workspaces/linux.mdx b/content/docs/workspaces/linux.mdx new file mode 100644 index 0000000..a523bd0 --- /dev/null +++ b/content/docs/workspaces/linux.mdx @@ -0,0 +1,122 @@ +--- +title: Set up on Linux or Raspberry Pi +sidebarTitle: Linux and Raspberry Pi +icon: server +description: Install the daemon on a Linux host and connect from your Mac through SSH. +--- + +A Linux host can keep your workspace running when your laptop is closed. You can connect from the Mac app or use the API directly. You do not need to install a daemon on the Mac. + +## 1. Check the host + +Use a 64-bit glibc Linux system with systemd: Ubuntu 24.04, Debian 13, or newer. The packages support x64 and ARM64. Allow at least 8 GiB of host memory and 8 GiB of free disk space, plus files and backups. + +The host needs **KVM**, Linux's hardware virtualization facility. A virtual machine also needs nested virtualization from its host provider. + +Run these commands on the Linux machine: + +```bash +uname -m +cat /etc/os-release +test -r /dev/kvm && test -w /dev/kvm && echo 'KVM access is ready' +``` + +If the last command does not print `KVM access is ready`, stop here. Ask your administrator to enable KVM and grant your user read/write access. After a group-permission change, sign out and sign in again. + + + A Raspberry Pi needs a 64-bit operating system and kernel with usable KVM. An ARM64 processor alone is not enough. Send the checks above to support if you are unsure about your Pi. + + +On Ubuntu or Debian, install the native library and download tools: + +```bash +sudo apt-get update +sudo apt-get install libcap-ng0 curl ca-certificates +``` + +## 2. Get and check the package + +[Email support@premai.io](mailto:support@premai.io?subject=Fluso%20daemon%20for%20Linux) for the download links and release version. Include the OS, architecture, KVM check, and whether your desktop app is Fluso or Fluso Dev. + +| `uname -m` result | Package | Checksum | +| --- | --- | --- | +| `aarch64` | `daemon-linux-arm64.tar.gz` | `daemon-linux-arm64.sha256` | +| `x86_64` | `daemon-linux-x64.tar.gz` | `daemon-linux-x64.sha256` | + +Use only links supplied by Fluso. No GitHub login, Docker, or compiler is needed. + +The commands below use **ARM64**, including a suitable Pi. On x64, replace `arm64` with `x64`. Replace the two URL placeholders with your links: + +```bash +mkdir -p "$HOME/Downloads/fluso-daemon" +cd "$HOME/Downloads/fluso-daemon" +curl --fail --location 'ARCHIVE_URL' -o daemon-linux-arm64.tar.gz +curl --fail --location 'CHECKSUM_URL' -o daemon-linux-arm64.sha256 +sha256sum -c daemon-linux-arm64.sha256 +``` + +Continue only if the check prints `OK`. Replace `RELEASE_VERSION` with the supplied version, then extract into a new, permanent folder: + +```bash +mkdir -p "$HOME/fluso/releases" +mkdir "$HOME/fluso/releases/RELEASE_VERSION" && + tar -xzf daemon-linux-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" +cd "$HOME/fluso/releases/RELEASE_VERSION" +``` + +Keep all extracted files together. Do not overwrite an installed package. Use [update](/workspaces/manage#update-the-daemon) for later releases. + +## 3. Start and configure + +Run as the user who will own the workspace, without `sudo`: + +```bash +./fluso-daemon setup --root "$HOME/fluso/state" --manage-host +./fluso-daemon configure --root "$HOME/fluso/state" --apply +./fluso-daemon status +``` + +Follow the prompts for your own model provider and key. Setup keeps data and keys under `~/fluso/state`, outside the package. It remembers this installation for later commands. + +Wait for the workspace to become ready. Setup also arranges for the user service to run after logout and at boot. If administrator approval is needed, it prints a `sudo loginctl enable-linger` command. Have your administrator run that exact command. + +## 4. Connect from your Mac + +On the Linux host, create a fresh pairing code from the package folder: + +```bash +./fluso-daemon pair --manage-host +``` + +Keep the code private. Each code works once and expires after ten minutes. This code gives the client workspace access and permission to change host settings. + +On your Mac, open Terminal. Replace `USER` and `HOST` with your Linux login and address: + +```bash +ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:4111:127.0.0.1:4111 USER@HOST +``` + +Leave this terminal open. Use the same local and remote port shown by setup. The daemon checks the port in incoming requests. + +1. Open **Workspaces** in the current Fluso desktop app. +2. Under **Connect a remote workspace**, enter a name, such as `Home Pi`. +3. Enter `http://127.0.0.1:4111` as the **Workspace URL**, then paste the pairing code. +4. Select **Connect**. Send a short message to check the workspace. + +Closing the tunnel disconnects the client. It does not stop work on the host. Reopen the tunnel to reconnect. Pairing remains valid unless revoked or unused for 30 days. + + + A Mac daemon or another tunnel may already use that port. For a new Linux installation, choose two free ports during setup, for example `--port 4211 --control-port 4212`. + + Forward `127.0.0.1:4211` to `127.0.0.1:4211` and use port `4211` in the workspace URL. For an existing host, stop its service before repeating setup with changed ports. Keep its original `--root`. + + + + For a host with DNS and a trusted TLS certificate, add `--public-origin https://workspace.example.com` during setup. Put an HTTPS reverse proxy in front of port `4111`. + + The proxy must preserve the requested Host, support streamed responses, and block `/control/*`. Keep both daemon ports on loopback. Port `4112` is private runtime control. Configure OAuth callbacks for the final HTTPS origin. + + Use the HTTPS URL and a fresh pairing code in Fluso. Ordinary remote HTTP is not accepted. Ask support for help if you do not manage this infrastructure. + + +Next: [configure files and backups](/workspaces/manage), or [use the API without a desktop client](/developers/api-ref/authentication). diff --git a/content/docs/workspaces/macos.mdx b/content/docs/workspaces/macos.mdx new file mode 100644 index 0000000..2053e65 --- /dev/null +++ b/content/docs/workspaces/macos.mdx @@ -0,0 +1,96 @@ +--- +title: Set up on macOS +sidebarTitle: macOS +icon: laptop +description: Install the daemon on an Apple Silicon Mac and connect it to Fluso. +--- + +This guide sets up a workspace on your Mac. You need an Apple Silicon Mac, internet access, and your own model provider key. Allow at least 8 GiB of host memory and 8 GiB of free disk space, plus room for your files and backups. + +The daemon runs separately from the desktop app. You do not need Docker, Bun, or a source checkout. + +## 1. Get the download links + +[Email support@premai.io](mailto:support@premai.io?subject=Fluso%20daemon%20for%20macOS) and ask for the macOS daemon package. Include your macOS version, chip, and whether you use Fluso or Fluso Dev. **Apple menu → About This Mac** shows your version and chip. + +Ask for the release version and these two links: + +- `daemon-darwin-arm64.tar.gz`: the package. +- `daemon-darwin-arm64.sha256`: its checksum. + +Use links supplied by Fluso. You do not need a GitHub account. Intel Macs are not supported by this package. + +## 2. Download and check the package + +Open Terminal. Replace `ARCHIVE_URL` and `CHECKSUM_URL` with the links you received, keeping the quotes: + +```bash +mkdir -p "$HOME/Downloads/fluso-daemon" +cd "$HOME/Downloads/fluso-daemon" +curl --fail --location 'ARCHIVE_URL' -o daemon-darwin-arm64.tar.gz +curl --fail --location 'CHECKSUM_URL' -o daemon-darwin-arm64.sha256 +shasum -a 256 -c daemon-darwin-arm64.sha256 +``` + +Continue only if the check prints `daemon-darwin-arm64.tar.gz: OK`. If it fails, download both files again from the matching release links. + +Replace `RELEASE_VERSION` below with the version supplied by support. Use a new folder for this release: + +```bash +mkdir -p "$HOME/fluso/releases" +mkdir "$HOME/fluso/releases/RELEASE_VERSION" && + tar -xzf daemon-darwin-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" +cd "$HOME/fluso/releases/RELEASE_VERSION" +``` + +Keep this folder in place. The executable needs the other packaged files beside it. For an existing installation, use [update](/workspaces/manage#update-the-daemon) instead of replacing files in its folder. + +## 3. Start your workspace + +Run these commands as your normal Mac user, without `sudo`: + +```bash +./fluso-daemon setup --manage-host +./fluso-daemon configure --apply +./fluso-daemon status +``` + +Setup checks the signed package and installs a background service. Follow the configuration prompts to choose a provider, model, and API key. Key entry stays hidden. Prem confidential inference needs only your Prem API key. + +Respond to macOS Keychain or folder-access prompts. Wait for `status` to report that the workspace is ready before sending work. + +`--manage-host` lets the connected app change the provider, resource limits, and shared folders. The [workspace settings guide](/workspaces/manage) explains these controls. + +## 4. Connect Fluso + +1. Open the matching, current [Fluso desktop app](https://fluso.ai/) and sign in. +2. Open **Workspaces** from the account menu. The menu-bar **View → Workspace settings** opens the same page. +3. Open the workspace selector and choose **This Mac**. Complete any native confirmation. +4. Send a small first message, such as: *"Help me plan a weekly review of my project notes."* + +Your existing Cloud conversations stay in Cloud. This workspace starts with its own data. + +The service starts when you log in. Closing Fluso leaves it running. Putting the Mac to sleep pauses its work. + +## If setup stops + +From the package folder, run: + +```bash +./fluso-daemon doctor +./fluso-daemon info +``` + +`doctor` explains installation problems. `info` shows the workspace paths and API address. + + + Check `./fluso-daemon status`. Start a stopped service with `./fluso-daemon start`. If configuration is missing, rerun `./fluso-daemon configure --apply`. + + In **Workspaces**, refresh the workspace list. A release mismatch requires a compatible desktop app and daemon. Changing the workspace's data directory does not fix it. + + If macOS cannot verify the developer, use **System Settings → Privacy & Security → Open Anyway** only for the package supplied by Fluso. Follow [Apple's instructions](https://support.apple.com/en-us/102445) after checking the download. For a damaged-file or malware warning, contact support with the exact message. + + Keep the original data and keys when diagnosing an existing installation. + + +Next: [give the workspace access to a folder](/workspaces/manage#share-a-folder). diff --git a/content/docs/workspaces/manage.mdx b/content/docs/workspaces/manage.mdx new file mode 100644 index 0000000..75d92e5 --- /dev/null +++ b/content/docs/workspaces/manage.mdx @@ -0,0 +1,135 @@ +--- +title: Manage your workspace +sidebarTitle: Settings and maintenance +icon: settings-2 +description: Choose a provider, share folders, adjust resources, and keep your workspace recoverable. +--- + +Open **Workspaces** in Fluso and select the machine you want to change. These settings belong to that host, even when you use the app on another Mac. + +To use the terminal instead, run the commands below from the installed package folder on the host. Add `--root /path/to/state` only when selecting a different installation. + +## Choose a model provider + +Open **Model provider**, enter your provider details, then **Save and apply**. You supply the credentials. The daemon does not include a Fluso Cloud inference allowance. + +| Provider | What you supply | +| --- | --- | +| OpenRouter or OpenAI | API key and model ID | +| Custom | An OpenAI-compatible base URL, API key, and exact model ID | +| Prem · Confidential | Prem API key, then **Enable confidential inference** | + +For terminal setup, use `./fluso-daemon configure --apply`. Prompts hide your keys. Pressing Enter keeps a saved value. + +Saved changes need Apply or a restart before they take effect. Applying settings can restart the workspace's compute. Finish active work first. An explicit interruption can stop a running task. + + + A custom endpoint must support streaming and tool calls, a 128K context window, and up to 16K output tokens. Use HTTPS for a remote provider. Local or trusted-network inference endpoints can use HTTP. + + `localhost` refers to the daemon's host. For a Pi workspace, it does not refer to the Mac running Fluso. Changing an endpoint requires entering its key again. + + Prem confidential inference verifies the provider and encrypts inference requests. It does not encrypt every workspace file or make external tools confidential. Image generation and speech synthesis are unavailable in this mode. Turning it off restores your saved standard provider. + + Provider failures do not silently switch to another provider. Media features can need separate provider credentials. Configure these with the same CLI command. + + +## Connect an app + +Open **Plugins → MCP → Add MCP** and choose **Custom server**. Enter the service's public MCP endpoint and supply its credentials or complete its OAuth sign-in. + +The daemon does not copy Cloud connections or provide the managed app catalog. Custom MCP destinations must be public: private network addresses and redirects are blocked. Review the service's permissions before you connect it. + +## Share a folder + +No personal host files are shared by default. Uploaded workspace files are separate from these grants. + +1. In **File access**, add the file or folder the Agent needs. +2. Keep **Read only** for reference material. Choose **Read and write** for files the Agent should edit. +3. Review the native confirmation and select **Save and apply**. + +For a report-writing task, share a reference folder read-only and a separate drafts folder with write access. Tell the Agent which folder holds each kind of file. + +On a remote workspace, enter paths on that remote host. A Mac path such as `/Users/alex/Drafts` appears to tools under `/mnt/host/Users/alex/Drafts`. + +Read-only access prevents edits, but content can still reach the chosen model provider. Share a specific folder instead of your entire home directory. + + + Use `./fluso-daemon host-files --apply` for the same settings in a terminal. + + A folder grant includes its descendants. A single-file grant permits in-place edits. Share the parent folder when a tool must rename or replace files. Overlapping grants are rejected. + + Remove a path and apply the change to revoke access. This does not delete content already copied into the workspace or conversation. Operating-system permissions still apply to writable grants. + + +## Adjust memory and storage + +The default sandbox has **3 GiB of RAM** and a **64 GiB disk budget**. The disk grows as needed: 56 GiB holds saved work and 8 GiB is for temporary work. + +Change these values in the workspace settings, or run `./fluso-daemon resources --apply`. Leave memory and disk space for the operating system and other applications. + +Runtime images, backups, and shared host folders are outside this disk budget. Reducing a limit must still leave room for saved data. Applying the change preserves saved files and replaces temporary storage. + +**Resources and storage** shows the data directory. On This Mac, **Open data folder** opens it in Finder. In a terminal, `./fluso-daemon info` shows the same paths. + +## Update the daemon + +Run this on the host: + +```bash +./fluso-daemon update +./fluso-daemon version +./fluso-daemon status +``` + +The command finds the latest release for the installed development or production channel. You do not need another download URL or a GitHub login. + +It checks the download and signature before stopping the service. Updates keep files, conversations, settings, and pairings. If the replacement cannot start correctly, the updater restores the previous package. + +Updates run only when requested. The desktop app updates separately. A stopped service stays stopped. Active work must finish unless you explicitly allow interruption. + +Keep the current and previous package folders for recovery. Internet access is required for release verification. + +## Back up your work + +Run these commands one at a time on the host. Use a new backup folder: + +```bash +./fluso-daemon shutdown +./fluso-daemon backup --backup-dir "$HOME/fluso/backups/first-backup" +./fluso-daemon start +``` + +Continue to backup only after shutdown succeeds. Shutdown refuses to interrupt active work. If backup fails, inspect the error and restart the service when ready. + +Keep the **encryption key and file-access settings separately**. The backup alone cannot recover an installation that has lost its key. + +- On macOS, the key is in the owner's Keychain. +- On Linux, the key is in the installation's `keys` folder. +- With `--root`, file-access settings are in `/sandbox/config.json`. The default Mac path is `~/.fluso/sandbox/config.json`. + +Backup files can contain readable documents. Store them privately and include them in your protected host backups. + + + Restore requires the original installation's encryption key. Stop the service, then run `./fluso-daemon restore --backup-dir /path/to/backup` and `./fluso-daemon start`. + + Restore keeps the pre-restore data separately. You must pair clients again, restore host-file access, and explicitly resume schedules. Pending work and approvals are not resumed automatically. + + Ask support before moving a workspace to another machine. Copying only the project-files folder or SQLite database is not a complete migration. + + +## When something needs attention + +Start with `./fluso-daemon status` and `./fluso-daemon doctor`. `./fluso-daemon doctor --fix` can repair startup or an interrupted update without resetting credentials or deleting workspace data. + +| Symptom | What to check | +| --- | --- | +| Settings cannot be changed | Reconnect with a code made by `pair --manage-host`. | +| Pairing code rejected | Generate a new code. Codes expire after ten minutes and work once. | +| Remote workspace unreachable | Check the host service and SSH tunnel or HTTPS address. | +| Configuration saved but not active | Apply it. If another client changed settings, reload and review before saving again. | +| Work pauses when the laptop closes | Keep the host awake, or use an always-on Linux host. | +| Model or tool call fails | Check that provider's credentials and the saved result before retrying work. | + +For logs, check `daemon.log` in the Mac data directory. On Linux, use `journalctl --user -u fluso-daemon` for the user service. Share the relevant error with [support](mailto:support@premai.io), without API keys or pairing codes. + +Disconnecting a reachable workspace revokes that client's access. Forgetting an offline connection cannot confirm revocation. Signing out or uninstalling the desktop app does not stop the daemon. diff --git a/content/docs/workspaces/meta.json b/content/docs/workspaces/meta.json new file mode 100644 index 0000000..5610ee8 --- /dev/null +++ b/content/docs/workspaces/meta.json @@ -0,0 +1,12 @@ +{ + "pages": [ + "!index", + "[monitor][Overview](/workspaces)", + "!macos", + "[laptop][macOS setup](/workspaces/macos)", + "!linux", + "[server][Linux and Raspberry Pi](/workspaces/linux)", + "!manage", + "[settings-2][Settings and maintenance](/workspaces/manage)" + ] +} From 68531cfc43ca9cd244bd86b30b40a1ef45e9fefd Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 20:22:31 +0200 Subject: [PATCH 2/7] docs: clarify setup failures and backup recovery --- content/docs/developers/api-ref/authentication.mdx | 2 +- content/docs/developers/api-ref/sharing.mdx | 3 +-- content/docs/workspaces/linux.mdx | 14 +++++++------- content/docs/workspaces/macos.mdx | 14 +++++++------- content/docs/workspaces/manage.mdx | 9 +++++++-- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/content/docs/developers/api-ref/authentication.mdx b/content/docs/developers/api-ref/authentication.mdx index 5da9821..d9eee49 100644 --- a/content/docs/developers/api-ref/authentication.mdx +++ b/content/docs/developers/api-ref/authentication.mdx @@ -47,7 +47,7 @@ Use `pair --manage-host` only when the client also needs provider settings, host ## Try a complete request -Open Bash in a new example directory. You need `curl`, `jq`, and `uuidgen`. The example below uses a daemon. For Cloud, use your environment's URL from the table above. Set the token you saved: +Open Bash in a new example directory. You need cURL 7.87 or later, `jq`, and `uuidgen` for the API examples. Check your version with `curl --version`. The example below uses a daemon. For Cloud, use your environment's URL from the table above. Set the token you saved: ```bash export FLUSO_API_URL='http://127.0.0.1:4111/v1' diff --git a/content/docs/developers/api-ref/sharing.mdx b/content/docs/developers/api-ref/sharing.mdx index 57378c2..47c6309 100644 --- a/content/docs/developers/api-ref/sharing.mdx +++ b/content/docs/developers/api-ref/sharing.mdx @@ -43,12 +43,11 @@ Review all selected contents and connection URLs before publishing. Stored authe { "name": "My release reviewer", "project_id": "Home", - "creation_id": "11111111-1111-4111-8111-111111111111", "published_at": "" } ``` -Send a saved `Idempotency-Key` when importing. Reuse it and the unchanged body to recover an uncertain import without creating another copy. The `creation_id` field is also accepted, but must not conflict with that key. A changed snapshot returns a conflict rather than silently importing something different from the preview. +Generate and save a new `Idempotency-Key` for each import. Reuse it and the unchanged body to recover an uncertain import without creating another copy. A changed snapshot returns a conflict rather than silently importing something different from the preview. The recipient gets independent setup and selected resource copies. Imported custom skills receive distinct names. App credentials are never inherited. Read the imported Agent's `setup_requirements`, then complete missing connections and consent in its manager conversation. diff --git a/content/docs/workspaces/linux.mdx b/content/docs/workspaces/linux.mdx index a523bd0..65f9bfb 100644 --- a/content/docs/workspaces/linux.mdx +++ b/content/docs/workspaces/linux.mdx @@ -48,19 +48,19 @@ Use only links supplied by Fluso. No GitHub login, Docker, or compiler is needed The commands below use **ARM64**, including a suitable Pi. On x64, replace `arm64` with `x64`. Replace the two URL placeholders with your links: ```bash -mkdir -p "$HOME/Downloads/fluso-daemon" -cd "$HOME/Downloads/fluso-daemon" -curl --fail --location 'ARCHIVE_URL' -o daemon-linux-arm64.tar.gz -curl --fail --location 'CHECKSUM_URL' -o daemon-linux-arm64.sha256 +mkdir -p "$HOME/Downloads/fluso-daemon" && +cd "$HOME/Downloads/fluso-daemon" && +curl --fail --location 'ARCHIVE_URL' -o daemon-linux-arm64.tar.gz && +curl --fail --location 'CHECKSUM_URL' -o daemon-linux-arm64.sha256 && sha256sum -c daemon-linux-arm64.sha256 ``` Continue only if the check prints `OK`. Replace `RELEASE_VERSION` with the supplied version, then extract into a new, permanent folder: ```bash -mkdir -p "$HOME/fluso/releases" +mkdir -p "$HOME/fluso/releases" && mkdir "$HOME/fluso/releases/RELEASE_VERSION" && - tar -xzf daemon-linux-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" +tar -xzf daemon-linux-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && cd "$HOME/fluso/releases/RELEASE_VERSION" ``` @@ -68,7 +68,7 @@ Keep all extracted files together. Do not overwrite an installed package. Use [u ## 3. Start and configure -Run as the user who will own the workspace, without `sudo`: +Run these commands one at a time as the workspace owner, without `sudo`. If a command fails, stop and check its error: ```bash ./fluso-daemon setup --root "$HOME/fluso/state" --manage-host diff --git a/content/docs/workspaces/macos.mdx b/content/docs/workspaces/macos.mdx index 2053e65..51ec745 100644 --- a/content/docs/workspaces/macos.mdx +++ b/content/docs/workspaces/macos.mdx @@ -25,10 +25,10 @@ Use links supplied by Fluso. You do not need a GitHub account. Intel Macs are no Open Terminal. Replace `ARCHIVE_URL` and `CHECKSUM_URL` with the links you received, keeping the quotes: ```bash -mkdir -p "$HOME/Downloads/fluso-daemon" -cd "$HOME/Downloads/fluso-daemon" -curl --fail --location 'ARCHIVE_URL' -o daemon-darwin-arm64.tar.gz -curl --fail --location 'CHECKSUM_URL' -o daemon-darwin-arm64.sha256 +mkdir -p "$HOME/Downloads/fluso-daemon" && +cd "$HOME/Downloads/fluso-daemon" && +curl --fail --location 'ARCHIVE_URL' -o daemon-darwin-arm64.tar.gz && +curl --fail --location 'CHECKSUM_URL' -o daemon-darwin-arm64.sha256 && shasum -a 256 -c daemon-darwin-arm64.sha256 ``` @@ -37,9 +37,9 @@ Continue only if the check prints `daemon-darwin-arm64.tar.gz: OK`. If it fails, Replace `RELEASE_VERSION` below with the version supplied by support. Use a new folder for this release: ```bash -mkdir -p "$HOME/fluso/releases" +mkdir -p "$HOME/fluso/releases" && mkdir "$HOME/fluso/releases/RELEASE_VERSION" && - tar -xzf daemon-darwin-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" +tar -xzf daemon-darwin-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && cd "$HOME/fluso/releases/RELEASE_VERSION" ``` @@ -47,7 +47,7 @@ Keep this folder in place. The executable needs the other packaged files beside ## 3. Start your workspace -Run these commands as your normal Mac user, without `sudo`: +Run these commands one at a time as your normal Mac user, without `sudo`. If a command fails, stop and check its error: ```bash ./fluso-daemon setup --manage-host diff --git a/content/docs/workspaces/manage.mdx b/content/docs/workspaces/manage.mdx index 75d92e5..bafd6d5 100644 --- a/content/docs/workspaces/manage.mdx +++ b/content/docs/workspaces/manage.mdx @@ -94,6 +94,7 @@ Keep the current and previous package folders for recovery. Internet access is r Run these commands one at a time on the host. Use a new backup folder: ```bash +mkdir -p "$HOME/fluso/backups" ./fluso-daemon shutdown ./fluso-daemon backup --backup-dir "$HOME/fluso/backups/first-backup" ./fluso-daemon start @@ -101,7 +102,9 @@ Run these commands one at a time on the host. Use a new backup folder: Continue to backup only after shutdown succeeds. Shutdown refuses to interrupt active work. If backup fails, inspect the error and restart the service when ready. -Keep the **encryption key and file-access settings separately**. The backup alone cannot recover an installation that has lost its key. +The backup includes workspace files and its database. It excludes shared host folders. Back up those folders separately. + +Keep the **encryption key and file-access settings separately**. The backup does not contain the key needed to read saved credentials. - On macOS, the key is in the owner's Keychain. - On Linux, the key is in the installation's `keys` folder. @@ -110,10 +113,12 @@ Keep the **encryption key and file-access settings separately**. The backup alon Backup files can contain readable documents. Store them privately and include them in your protected host backups. - Restore requires the original installation's encryption key. Stop the service, then run `./fluso-daemon restore --backup-dir /path/to/backup` and `./fluso-daemon start`. + Restore accepts a backup from the same installation. Keep its original encryption key available. Stop the service, then run `./fluso-daemon restore --backup-dir /path/to/backup`. Start the service after restore succeeds. Restore keeps the pre-restore data separately. You must pair clients again, restore host-file access, and explicitly resume schedules. Pending work and approvals are not resumed automatically. + If the key is lost, ask support about a credential reset. This can preserve files and conversations, but requires new credentials and connections. + Ask support before moving a workspace to another machine. Copying only the project-files folder or SQLite database is not a complete migration. From d47d52a8a91872590a0348f6805949c2e8645850 Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 20:31:35 +0200 Subject: [PATCH 3/7] docs: organize Fluso Local around setup and everyday use --- AGENTS.md | 2 +- content/docs/deployment-models.mdx | 8 +- .../developers/api-ref/authentication.mdx | 2 +- content/docs/index.mdx | 4 +- content/docs/meta.json | 6 +- content/docs/workspaces/index.mdx | 47 ++-- content/docs/workspaces/linux.mdx | 122 ----------- content/docs/workspaces/macos.mdx | 96 -------- content/docs/workspaces/meta.json | 8 +- content/docs/workspaces/setup.mdx | 206 ++++++++++++++++++ content/docs/workspaces/use.mdx | 58 +++++ public/_redirects | 6 + 12 files changed, 307 insertions(+), 258 deletions(-) delete mode 100644 content/docs/workspaces/linux.mdx delete mode 100644 content/docs/workspaces/macos.mdx create mode 100644 content/docs/workspaces/setup.mdx create mode 100644 content/docs/workspaces/use.mdx diff --git a/AGENTS.md b/AGENTS.md index 7f79c51..5d9ad53 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,12 +17,12 @@ The navigation keeps Home first and sorts the top-level sections alphabetically. - **App setup** - `/integrations/{github, gmail, google-calendar, slack}`. Per-app permissions and prompts, not feature pages. All of them connect through the Apps tab of the Add MCP dialog. - **Deployment models** - `/deployment-models`. Current availability and operating boundaries for Fluso Cloud, dedicated AWS VPC, planned Azure VNet, and scoped on-premises deployments. - **Features** - `/features/*`. Approvals and permissions, Apps and MCP servers, Chat, Confidential mode, Imports, Memory, Projects, Skills, and Tasks. +- **Fluso Local** - `/workspaces/*`. Setup and everyday use on your own machine, followed by settings and maintenance. Keep macOS and Linux as choices inside one setup guide. Request daemon download links from support; do not publish unlisted release URLs. Account API access starts at `/developers/api-ref/authentication`; full endpoint reference is served at each workspace's `/docs`. - **Get started** - `/going-deeper`, `/introduction`, `/quickstart`. These pages explain the path from first setup to daily use. - **Reference** - `/resources/{faq, legal, pricing}`. Legal links to the terms of service at `https://fluso.ai/terms` and privacy policy at `https://fluso.ai/privacy`; do not duplicate the policies in the docs. - **Release notes** - `/release-notes`. Customer-facing changes, newest first. - **Remote access** - `/remote/*`. Ways to use Fluso away from the desktop app. - **Workflows** - `/workflows/*`. Six concrete stories: bug-to-PR, content launch, knowledge recall, meetings, morning brief, and research-to-deck. -- **Workspaces** - `/workspaces/*`. Independent daemon setup on macOS and Linux, provider and file access, updates, backups, and recovery. Request download links from support; do not publish unlisted release URLs. Account API access starts at `/developers/api-ref/authentication`; full endpoint reference is served at each workspace's `/docs`. The home page (`/`) is a router into the journey, with three sections: just landed, already set up, daily user. diff --git a/content/docs/deployment-models.mdx b/content/docs/deployment-models.mdx index 6596a14..7b81954 100644 --- a/content/docs/deployment-models.mdx +++ b/content/docs/deployment-models.mdx @@ -10,7 +10,7 @@ Your deployment model decides who operates Fluso and where its runtime, durable | Model | Availability | Operations | | --- | --- | --- | | Fluso Cloud | Available | Managed by Fluso | -| Personal daemon | Contact Fluso for a matching Mac or Linux package | You operate one owner's workspace | +| Fluso Local | Contact Fluso for a matching Mac or Linux package | You operate one owner's workspace | | Dedicated AWS VPC | Supported for scoped Enterprise deployments | Responsibilities agreed during deployment design | | Azure VNet | Coming soon | Not available today | | On-premises | Scoped Enterprise engagement | Responsibilities agreed before implementation | @@ -21,11 +21,11 @@ Fluso Cloud is the standard managed service. It runs on AWS with European hostin Choose it when you want to start without operating infrastructure. See [Legal](/resources/legal) for Fluso's privacy policy. -## A workspace on your machine +## Fluso Local Install the daemon to run one owner's workspace on an Apple Silicon Mac or a supported Linux host. Conversations and files stay on that host. You manage its provider credentials, service, updates, and backups. -The desktop app connects to this workspace; it does not install or update the daemon. This is separate from an Enterprise deployment of the whole platform. Start with [macOS setup](/workspaces/macos) or [Linux and Raspberry Pi setup](/workspaces/linux). +The desktop app connects to this workspace. It does not install or update the daemon. This is separate from an Enterprise deployment of the whole platform. Start with [Set up Fluso Local](/workspaces/setup). ## Dedicated AWS VPC @@ -66,7 +66,7 @@ Traces can include prompt and response text. For a dedicated or on-premises depl ## Choose a model - Start with Fluso Cloud when managed operations and European hosting meet the requirement. -- Use a [personal daemon](/workspaces) when you want to operate a workspace on your own machine. +- Use [Fluso Local](/workspaces) when you want to operate a workspace on your own machine. - Scope an AWS VPC deployment when you need a dedicated AWS network boundary. - Treat Azure VNet as future availability, not a current option. - Scope on-premises only after Fluso validates the infrastructure, residency, provider, telemetry, and support requirements. diff --git a/content/docs/developers/api-ref/authentication.mdx b/content/docs/developers/api-ref/authentication.mdx index d9eee49..b5ff495 100644 --- a/content/docs/developers/api-ref/authentication.mdx +++ b/content/docs/developers/api-ref/authentication.mdx @@ -43,7 +43,7 @@ curl --fail-with-body --silent --show-error \ Replace `PAIRING_CODE` before running. Save the returned `token` and client `id` privately. Codes expire after ten minutes. A device token grants read/write access to the owner's workspace, including tool approvals. -Use `pair --manage-host` only when the client also needs provider settings, host-file access, or permission to invite devices. Remote connections require [HTTPS or an SSH tunnel](/workspaces/linux#4-connect-from-your-mac). +Use `pair --manage-host` only when the client also needs provider settings, host-file access, or permission to invite devices. Remote connections require [HTTPS or an SSH tunnel](/workspaces/setup#connect-from-another-mac). ## Try a complete request diff --git a/content/docs/index.mdx b/content/docs/index.mdx index 15b1ac2..5a95a8a 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -48,8 +48,8 @@ Pick a workflow that matches a problem you have right now. Each one is a story, ## When you need it - - Set up a workspace on your Mac or a Linux host, including a supported Raspberry Pi. + + Set up Fluso on your own machine and use it with your files, Agents, and routines. The five primitives: chat, apps, skills, tasks, memory. diff --git a/content/docs/meta.json b/content/docs/meta.json index a04126c..5319754 100644 --- a/content/docs/meta.json +++ b/content/docs/meta.json @@ -12,6 +12,8 @@ "[server-cog][Overview](/deployment-models)", "---Features---", "...features", + "---Fluso Local---", + "...workspaces", "---Get started---", "going-deeper", "introduction", @@ -25,8 +27,6 @@ "---Remote access---", "...remote", "---Workflows---", - "...workflows", - "---Workspaces---", - "...workspaces" + "...workflows" ] } diff --git a/content/docs/workspaces/index.mdx b/content/docs/workspaces/index.mdx index e44548b..8aa99bd 100644 --- a/content/docs/workspaces/index.mdx +++ b/content/docs/workspaces/index.mdx @@ -1,50 +1,47 @@ --- -title: Run Fluso on your machine +title: Fluso Local sidebarTitle: Overview icon: monitor -description: Keep a workspace on your Mac or a Linux machine, and use it from Fluso. +description: Set up Fluso on your own machine and use it for your daily work. --- -You can use Fluso Cloud, or run a workspace on a machine you own. For example, your Mac can work with a folder of reports. A Linux machine can keep a scheduled task running after you close your laptop. +Fluso Local runs your workspace on a computer you control. Use your Mac for work with local files, or a Linux machine for work that continues after you close your laptop. -The **daemon** is the background program that runs this workspace. You install it separately from the Fluso desktop app. +You still talk to Fluso in the desktop app. Your conversations, Agents, and workspace files stay on the machine you choose. -## Choose where the work runs +## Choose where to run it -| Workspace | Where work runs and files stay | What you manage | +| Where | Useful for | How you connect | | --- | --- | --- | -| Fluso Cloud | Fluso's managed service | Your work and app connections | -| This Mac | Your Apple Silicon Mac | The daemon, model provider, and backups | -| Remote workspace | Your Linux machine or another supported host | The daemon, secure connection, model provider, and backups | +| Your Apple Silicon Mac | Working with documents on your laptop | Choose **This Mac** in Fluso | +| A supported Linux machine | Keeping work running while your laptop is closed | Connect from Fluso on your Mac | -These workspaces keep separate conversations, Agents, files, and connections. Switching between them does not move or synchronize your work. +A Linux machine can be at home or on a server you manage. A Raspberry Pi works only with a supported 64-bit system and working hardware virtualization. - - Install the package, choose a model provider, and connect This Mac. + + Get the download, choose macOS or Linux, and connect the app. - - Use a Linux host or a Raspberry Pi with a supported operating system and KVM. + + Work with your files, keep an Agent, and schedule useful work. -[Contact us](mailto:support@premai.io?subject=Fluso%20daemon%20download) for the daemon download links. Include your operating system, processor type, and whether you use Fluso or Fluso Dev. We can confirm the matching package and desktop version. +## What you need -## What stays on your machine +You install a background program called the **daemon** on the machine that will do the work. It runs separately from the desktop app. You also supply a model provider key and keep that machine awake for work to continue. -The daemon stores workspace files and conversations on its host. It has no access to your personal folders until you share them. +[Contact us](mailto:support@premai.io?subject=Fluso%20Local%20download) for the daemon download links. Include your operating system, processor type, and whether you use Fluso or Fluso Dev. -Your chosen model provider receives the content needed for inference. Connected services receive their tool requests. Running the daemon locally does not make these calls offline or keep all processing on your machine. +The [setup guide](/workspaces/setup) checks your machine before installation. You do not need Docker or a source checkout. For use without the desktop app, follow [API access](/developers/api-ref/authentication) after setup. -You supply your own provider credentials. The desktop interface still needs an internet connection and Fluso sign-in. You can configure the daemon and use its API without the desktop app. +## Where your work stays -## Use it day to day +Each machine has its own conversations, Agents, files, and connections. They are separate from Fluso Cloud. Switching workspaces does not move or synchronize your work. -Open **Workspaces** from the desktop account menu. Choose **This Mac** or a saved remote connection. Settings on this page apply to the selected machine. +Fluso can use files you upload or folders you explicitly share. It has no access to your other personal folders by default. -Closing the app or switching workspaces leaves accepted work running. Opening the app again starts on **Fluso Cloud**. Select your saved connection to return. - -The host must remain awake and connected for work to continue. After downtime, missed recurring runs are combined into one catch-up run. +Your chosen model provider receives the content needed to answer. Connected services receive their tool requests. Local storage does not make all processing local. The desktop interface still needs internet access and Fluso sign-in. Conversations, Agents, files, routines, and custom MCP connections use the selected workspace. Cloud billing, managed app connections, ingestion services, legacy Tasks, and public sharing are not available through the daemon. @@ -52,4 +49,4 @@ The host must remain awake and connected for work to continue. After downtime, m Each paired client has access to the same owner's workspace. Pairing does not create separate user accounts or private areas for different people. -Next, learn how to [share folders, update, and back up your workspace](/workspaces/manage). For scripts and integrations, start with [API access](/developers/api-ref/authentication). +Start with [Set up Fluso Local](/workspaces/setup). Return to [settings and maintenance](/workspaces/manage) for provider changes, updates, and backups. diff --git a/content/docs/workspaces/linux.mdx b/content/docs/workspaces/linux.mdx deleted file mode 100644 index 65f9bfb..0000000 --- a/content/docs/workspaces/linux.mdx +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Set up on Linux or Raspberry Pi -sidebarTitle: Linux and Raspberry Pi -icon: server -description: Install the daemon on a Linux host and connect from your Mac through SSH. ---- - -A Linux host can keep your workspace running when your laptop is closed. You can connect from the Mac app or use the API directly. You do not need to install a daemon on the Mac. - -## 1. Check the host - -Use a 64-bit glibc Linux system with systemd: Ubuntu 24.04, Debian 13, or newer. The packages support x64 and ARM64. Allow at least 8 GiB of host memory and 8 GiB of free disk space, plus files and backups. - -The host needs **KVM**, Linux's hardware virtualization facility. A virtual machine also needs nested virtualization from its host provider. - -Run these commands on the Linux machine: - -```bash -uname -m -cat /etc/os-release -test -r /dev/kvm && test -w /dev/kvm && echo 'KVM access is ready' -``` - -If the last command does not print `KVM access is ready`, stop here. Ask your administrator to enable KVM and grant your user read/write access. After a group-permission change, sign out and sign in again. - - - A Raspberry Pi needs a 64-bit operating system and kernel with usable KVM. An ARM64 processor alone is not enough. Send the checks above to support if you are unsure about your Pi. - - -On Ubuntu or Debian, install the native library and download tools: - -```bash -sudo apt-get update -sudo apt-get install libcap-ng0 curl ca-certificates -``` - -## 2. Get and check the package - -[Email support@premai.io](mailto:support@premai.io?subject=Fluso%20daemon%20for%20Linux) for the download links and release version. Include the OS, architecture, KVM check, and whether your desktop app is Fluso or Fluso Dev. - -| `uname -m` result | Package | Checksum | -| --- | --- | --- | -| `aarch64` | `daemon-linux-arm64.tar.gz` | `daemon-linux-arm64.sha256` | -| `x86_64` | `daemon-linux-x64.tar.gz` | `daemon-linux-x64.sha256` | - -Use only links supplied by Fluso. No GitHub login, Docker, or compiler is needed. - -The commands below use **ARM64**, including a suitable Pi. On x64, replace `arm64` with `x64`. Replace the two URL placeholders with your links: - -```bash -mkdir -p "$HOME/Downloads/fluso-daemon" && -cd "$HOME/Downloads/fluso-daemon" && -curl --fail --location 'ARCHIVE_URL' -o daemon-linux-arm64.tar.gz && -curl --fail --location 'CHECKSUM_URL' -o daemon-linux-arm64.sha256 && -sha256sum -c daemon-linux-arm64.sha256 -``` - -Continue only if the check prints `OK`. Replace `RELEASE_VERSION` with the supplied version, then extract into a new, permanent folder: - -```bash -mkdir -p "$HOME/fluso/releases" && -mkdir "$HOME/fluso/releases/RELEASE_VERSION" && -tar -xzf daemon-linux-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && -cd "$HOME/fluso/releases/RELEASE_VERSION" -``` - -Keep all extracted files together. Do not overwrite an installed package. Use [update](/workspaces/manage#update-the-daemon) for later releases. - -## 3. Start and configure - -Run these commands one at a time as the workspace owner, without `sudo`. If a command fails, stop and check its error: - -```bash -./fluso-daemon setup --root "$HOME/fluso/state" --manage-host -./fluso-daemon configure --root "$HOME/fluso/state" --apply -./fluso-daemon status -``` - -Follow the prompts for your own model provider and key. Setup keeps data and keys under `~/fluso/state`, outside the package. It remembers this installation for later commands. - -Wait for the workspace to become ready. Setup also arranges for the user service to run after logout and at boot. If administrator approval is needed, it prints a `sudo loginctl enable-linger` command. Have your administrator run that exact command. - -## 4. Connect from your Mac - -On the Linux host, create a fresh pairing code from the package folder: - -```bash -./fluso-daemon pair --manage-host -``` - -Keep the code private. Each code works once and expires after ten minutes. This code gives the client workspace access and permission to change host settings. - -On your Mac, open Terminal. Replace `USER` and `HOST` with your Linux login and address: - -```bash -ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:4111:127.0.0.1:4111 USER@HOST -``` - -Leave this terminal open. Use the same local and remote port shown by setup. The daemon checks the port in incoming requests. - -1. Open **Workspaces** in the current Fluso desktop app. -2. Under **Connect a remote workspace**, enter a name, such as `Home Pi`. -3. Enter `http://127.0.0.1:4111` as the **Workspace URL**, then paste the pairing code. -4. Select **Connect**. Send a short message to check the workspace. - -Closing the tunnel disconnects the client. It does not stop work on the host. Reopen the tunnel to reconnect. Pairing remains valid unless revoked or unused for 30 days. - - - A Mac daemon or another tunnel may already use that port. For a new Linux installation, choose two free ports during setup, for example `--port 4211 --control-port 4212`. - - Forward `127.0.0.1:4211` to `127.0.0.1:4211` and use port `4211` in the workspace URL. For an existing host, stop its service before repeating setup with changed ports. Keep its original `--root`. - - - - For a host with DNS and a trusted TLS certificate, add `--public-origin https://workspace.example.com` during setup. Put an HTTPS reverse proxy in front of port `4111`. - - The proxy must preserve the requested Host, support streamed responses, and block `/control/*`. Keep both daemon ports on loopback. Port `4112` is private runtime control. Configure OAuth callbacks for the final HTTPS origin. - - Use the HTTPS URL and a fresh pairing code in Fluso. Ordinary remote HTTP is not accepted. Ask support for help if you do not manage this infrastructure. - - -Next: [configure files and backups](/workspaces/manage), or [use the API without a desktop client](/developers/api-ref/authentication). diff --git a/content/docs/workspaces/macos.mdx b/content/docs/workspaces/macos.mdx deleted file mode 100644 index 51ec745..0000000 --- a/content/docs/workspaces/macos.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: Set up on macOS -sidebarTitle: macOS -icon: laptop -description: Install the daemon on an Apple Silicon Mac and connect it to Fluso. ---- - -This guide sets up a workspace on your Mac. You need an Apple Silicon Mac, internet access, and your own model provider key. Allow at least 8 GiB of host memory and 8 GiB of free disk space, plus room for your files and backups. - -The daemon runs separately from the desktop app. You do not need Docker, Bun, or a source checkout. - -## 1. Get the download links - -[Email support@premai.io](mailto:support@premai.io?subject=Fluso%20daemon%20for%20macOS) and ask for the macOS daemon package. Include your macOS version, chip, and whether you use Fluso or Fluso Dev. **Apple menu → About This Mac** shows your version and chip. - -Ask for the release version and these two links: - -- `daemon-darwin-arm64.tar.gz`: the package. -- `daemon-darwin-arm64.sha256`: its checksum. - -Use links supplied by Fluso. You do not need a GitHub account. Intel Macs are not supported by this package. - -## 2. Download and check the package - -Open Terminal. Replace `ARCHIVE_URL` and `CHECKSUM_URL` with the links you received, keeping the quotes: - -```bash -mkdir -p "$HOME/Downloads/fluso-daemon" && -cd "$HOME/Downloads/fluso-daemon" && -curl --fail --location 'ARCHIVE_URL' -o daemon-darwin-arm64.tar.gz && -curl --fail --location 'CHECKSUM_URL' -o daemon-darwin-arm64.sha256 && -shasum -a 256 -c daemon-darwin-arm64.sha256 -``` - -Continue only if the check prints `daemon-darwin-arm64.tar.gz: OK`. If it fails, download both files again from the matching release links. - -Replace `RELEASE_VERSION` below with the version supplied by support. Use a new folder for this release: - -```bash -mkdir -p "$HOME/fluso/releases" && -mkdir "$HOME/fluso/releases/RELEASE_VERSION" && -tar -xzf daemon-darwin-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && -cd "$HOME/fluso/releases/RELEASE_VERSION" -``` - -Keep this folder in place. The executable needs the other packaged files beside it. For an existing installation, use [update](/workspaces/manage#update-the-daemon) instead of replacing files in its folder. - -## 3. Start your workspace - -Run these commands one at a time as your normal Mac user, without `sudo`. If a command fails, stop and check its error: - -```bash -./fluso-daemon setup --manage-host -./fluso-daemon configure --apply -./fluso-daemon status -``` - -Setup checks the signed package and installs a background service. Follow the configuration prompts to choose a provider, model, and API key. Key entry stays hidden. Prem confidential inference needs only your Prem API key. - -Respond to macOS Keychain or folder-access prompts. Wait for `status` to report that the workspace is ready before sending work. - -`--manage-host` lets the connected app change the provider, resource limits, and shared folders. The [workspace settings guide](/workspaces/manage) explains these controls. - -## 4. Connect Fluso - -1. Open the matching, current [Fluso desktop app](https://fluso.ai/) and sign in. -2. Open **Workspaces** from the account menu. The menu-bar **View → Workspace settings** opens the same page. -3. Open the workspace selector and choose **This Mac**. Complete any native confirmation. -4. Send a small first message, such as: *"Help me plan a weekly review of my project notes."* - -Your existing Cloud conversations stay in Cloud. This workspace starts with its own data. - -The service starts when you log in. Closing Fluso leaves it running. Putting the Mac to sleep pauses its work. - -## If setup stops - -From the package folder, run: - -```bash -./fluso-daemon doctor -./fluso-daemon info -``` - -`doctor` explains installation problems. `info` shows the workspace paths and API address. - - - Check `./fluso-daemon status`. Start a stopped service with `./fluso-daemon start`. If configuration is missing, rerun `./fluso-daemon configure --apply`. - - In **Workspaces**, refresh the workspace list. A release mismatch requires a compatible desktop app and daemon. Changing the workspace's data directory does not fix it. - - If macOS cannot verify the developer, use **System Settings → Privacy & Security → Open Anyway** only for the package supplied by Fluso. Follow [Apple's instructions](https://support.apple.com/en-us/102445) after checking the download. For a damaged-file or malware warning, contact support with the exact message. - - Keep the original data and keys when diagnosing an existing installation. - - -Next: [give the workspace access to a folder](/workspaces/manage#share-a-folder). diff --git a/content/docs/workspaces/meta.json b/content/docs/workspaces/meta.json index 5610ee8..fc64b41 100644 --- a/content/docs/workspaces/meta.json +++ b/content/docs/workspaces/meta.json @@ -2,10 +2,10 @@ "pages": [ "!index", "[monitor][Overview](/workspaces)", - "!macos", - "[laptop][macOS setup](/workspaces/macos)", - "!linux", - "[server][Linux and Raspberry Pi](/workspaces/linux)", + "!setup", + "[download][Setup](/workspaces/setup)", + "!use", + "[messages-square][Everyday use](/workspaces/use)", "!manage", "[settings-2][Settings and maintenance](/workspaces/manage)" ] diff --git a/content/docs/workspaces/setup.mdx b/content/docs/workspaces/setup.mdx new file mode 100644 index 0000000..ddb9280 --- /dev/null +++ b/content/docs/workspaces/setup.mdx @@ -0,0 +1,206 @@ +--- +title: Set up Fluso Local +sidebarTitle: Setup +icon: download +description: Install on macOS or Linux, choose a model provider, and connect Fluso. +--- + +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; + +Install the daemon on the machine that will run your work. You can use the same Mac as the desktop app, or connect to a separate Linux machine. + +You need internet access and your own model provider key. Allow at least 8 GiB of memory and 8 GiB of free disk space, plus room for files and backups. + +## 1. Get the download + +[Email support@premai.io](mailto:support@premai.io?subject=Fluso%20Local%20download) for the daemon package. Include your operating system, processor type, and whether you use Fluso or Fluso Dev. + +On a Mac, find these details in **Apple menu → About This Mac**. On Linux, run `uname -m` and `cat /etc/os-release`. + +Ask for the release version, archive URL, and matching checksum URL: + +| Machine | Archive | Checksum | +| --- | --- | --- | +| Apple Silicon Mac | `daemon-darwin-arm64.tar.gz` | `daemon-darwin-arm64.sha256` | +| Linux ARM64 (`aarch64`) | `daemon-linux-arm64.tar.gz` | `daemon-linux-arm64.sha256` | +| Linux x64 (`x86_64`) | `daemon-linux-x64.tar.gz` | `daemon-linux-x64.sha256` | + +Use links supplied by Fluso. You do not need a GitHub account, Docker, Bun, or a source checkout. + +## 2. Install on your machine + +Choose the operating system of the machine that will run Fluso: + + + + +You need an **Apple Silicon Mac**. This package does not support Intel Macs. + +**Download and check.** Open Terminal. Replace `ARCHIVE_URL` and `CHECKSUM_URL` with your links, keeping the quotes: + +```bash +mkdir -p "$HOME/Downloads/fluso-daemon" && +cd "$HOME/Downloads/fluso-daemon" && +curl --fail --location 'ARCHIVE_URL' -o daemon-darwin-arm64.tar.gz && +curl --fail --location 'CHECKSUM_URL' -o daemon-darwin-arm64.sha256 && +shasum -a 256 -c daemon-darwin-arm64.sha256 +``` + +Continue only if the check prints `daemon-darwin-arm64.tar.gz: OK`. If it fails, download both files again from the matching release links. + +**Extract the package.** Replace `RELEASE_VERSION` with the version supplied by support. Use a new folder: + +```bash +mkdir -p "$HOME/fluso/releases" && +mkdir "$HOME/fluso/releases/RELEASE_VERSION" && +tar -xzf daemon-darwin-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && +cd "$HOME/fluso/releases/RELEASE_VERSION" +``` + +Keep the whole folder in place. The executable needs the other files beside it. For later releases, use [update](/workspaces/manage#update-the-daemon). + +**Start and configure.** Run these commands one at a time as your normal Mac user, without `sudo`. If a command fails, stop and check its error: + +```bash +./fluso-daemon setup --manage-host +./fluso-daemon configure --apply +./fluso-daemon status +``` + +Setup checks the signed package and installs a background service. Follow the prompts to choose your model provider, model, and API key. Key entry stays hidden. Prem confidential inference needs only your Prem API key. + +Respond to macOS Keychain or folder-access prompts. Wait for `status` to report that the workspace is ready. + +The service starts when you log in. Closing Fluso leaves it running. Putting the Mac to sleep pauses its work. + + + + +Use **64-bit glibc Linux with systemd**: Ubuntu 24.04, Debian 13, or newer. The packages support ARM64 and x64. + +The host needs **KVM**, Linux's hardware virtualization facility. A virtual machine also needs nested virtualization from its host provider. + +**Check access to KVM.** Run this on the Linux machine: + +```bash +test -r /dev/kvm && test -w /dev/kvm && echo 'KVM access is ready' +``` + +If it does not print `KVM access is ready`, stop here. Ask your administrator to enable KVM and grant your user read/write access. After a group-permission change, sign out and sign in again. + + + A Raspberry Pi needs a supported 64-bit operating system and kernel with usable KVM. An ARM64 processor alone is not enough. Send your system details and KVM check to support if you are unsure. + + +On Ubuntu or Debian, install the native library and download tools: + +```bash +sudo apt-get update +sudo apt-get install libcap-ng0 curl ca-certificates +``` + +**Download and check.** These commands use ARM64. On x64, replace every `arm64` with `x64`. Replace the two URL placeholders with your links: + +```bash +mkdir -p "$HOME/Downloads/fluso-daemon" && +cd "$HOME/Downloads/fluso-daemon" && +curl --fail --location 'ARCHIVE_URL' -o daemon-linux-arm64.tar.gz && +curl --fail --location 'CHECKSUM_URL' -o daemon-linux-arm64.sha256 && +sha256sum -c daemon-linux-arm64.sha256 +``` + +Continue only if the check prints `OK`. If it fails, download both files again from the matching release links. + +**Extract the package.** Replace `RELEASE_VERSION` with the supplied version. Use a new folder: + +```bash +mkdir -p "$HOME/fluso/releases" && +mkdir "$HOME/fluso/releases/RELEASE_VERSION" && +tar -xzf daemon-linux-arm64.tar.gz -C "$HOME/fluso/releases/RELEASE_VERSION" && +cd "$HOME/fluso/releases/RELEASE_VERSION" +``` + +Keep all extracted files together. Do not overwrite an installed package. Use [update](/workspaces/manage#update-the-daemon) for later releases. + +**Start and configure.** Run these commands one at a time as the workspace owner, without `sudo`. If a command fails, stop and check its error: + +```bash +./fluso-daemon setup --root "$HOME/fluso/state" --manage-host +./fluso-daemon configure --root "$HOME/fluso/state" --apply +./fluso-daemon status +``` + +Follow the prompts for your own model provider and key. Setup keeps data and keys under `~/fluso/state`, outside the package. It remembers this installation for later commands. + +Wait for the workspace to become ready. Setup also arranges for the user service to run after logout and at boot. If administrator approval is needed, it prints a `sudo loginctl enable-linger` command. Have your administrator run that exact command. + + + + +`--manage-host` lets the connected app change the provider, resource limits, and shared folders. You can review these in [workspace settings](/workspaces/manage). + +## 3. Connect Fluso + +Open the matching, current [Fluso desktop app](https://fluso.ai/) on your Mac and sign in. + +### On the same Mac + +1. Open **Workspaces** from the account menu. **View → Workspace settings** opens the same page. +2. Open the workspace selector and choose **This Mac**. Complete any native confirmation. + +### Connect from another Mac + +The machine running Fluso must be reachable through SSH or HTTPS. For a Linux host, the SSH connection below is a way to start. You do not need a daemon on the Mac that connects to it. + +On the host, create a pairing code from the package folder: + +```bash +./fluso-daemon pair --manage-host +``` + +Keep the code private. Each code works once and expires after ten minutes. It gives the client workspace access and permission to change host settings. + +On your Mac, open Terminal. Replace `USER` and `HOST` with your host login and address: + +```bash +ssh -N -o ExitOnForwardFailure=yes -L 127.0.0.1:4111:127.0.0.1:4111 USER@HOST +``` + +Leave this terminal open. Use the same local and remote port shown by setup. The daemon checks the port in incoming requests. + +1. Open **Workspaces** in the current Fluso desktop app. +2. Under **Connect a remote workspace**, enter a name, such as `Home server`. +3. Enter `http://127.0.0.1:4111` as the **Workspace URL**, then paste the pairing code. +4. Select **Connect**. + +Closing the tunnel disconnects the client. It does not stop work on the host. Reopen the tunnel to reconnect. Pairing remains valid unless revoked or unused for 30 days. + + + A Mac daemon or another tunnel may already use that port. For a new host installation, choose two free ports during setup, for example `--port 4211 --control-port 4212`. + + Forward `127.0.0.1:4211` to `127.0.0.1:4211` and use port `4211` in the workspace URL. For an existing host, stop its service before repeating setup with changed ports. Keep its original `--root`. + + + + For a host with DNS and a trusted TLS certificate, add `--public-origin https://workspace.example.com` during setup. Put an HTTPS reverse proxy in front of port `4111`. + + The proxy must preserve the requested Host, support streamed responses, and block `/control/*`. Keep both daemon ports on loopback. Port `4112` is private runtime control. Configure OAuth callbacks for the final HTTPS origin. + + Use the HTTPS URL and a fresh pairing code in Fluso. Ordinary remote HTTP is not accepted. Ask support for help if you do not manage this infrastructure. + + +## 4. Try your first task + +Open a chat in the selected workspace. Attach a short document and ask Fluso to summarize its main points. + +Your Cloud conversations stay in Cloud. This workspace has its own data. Continue with [Use Fluso Local](/workspaces/use) for files, Agents, and routines. + + + From the package folder, run `./fluso-daemon status` and `./fluso-daemon doctor`. Use `./fluso-daemon info` to see its paths and API address. + + Start a stopped service with `./fluso-daemon start`. If configuration is missing, rerun `./fluso-daemon configure --apply`. + + In **Workspaces**, refresh the workspace list. A release mismatch requires a compatible desktop app and daemon. Changing the data directory does not fix it. Keep the original data and keys when diagnosing an existing installation. + + If macOS cannot verify the developer, use **System Settings → Privacy & Security → Open Anyway** only for the package supplied by Fluso. Follow [Apple's instructions](https://support.apple.com/en-us/102445) after checking the download. For a damaged-file or malware warning, contact support with the exact message. + diff --git a/content/docs/workspaces/use.mdx b/content/docs/workspaces/use.mdx new file mode 100644 index 0000000..730148e --- /dev/null +++ b/content/docs/workspaces/use.mdx @@ -0,0 +1,58 @@ +--- +title: Use Fluso Local +sidebarTitle: Everyday use +icon: messages-square +description: Work with your files, return to an Agent, and keep useful work running. +--- + +After [setup](/workspaces/setup), you use Fluso through the same desktop app. Start with a small task and check the result before giving it more work. + +## Open your workspace + +Open **Workspaces** from the account menu. Choose **This Mac** or the remote connection you saved. + +Each workspace has its own conversations, Agents, files, and connections. If your Cloud chats are missing, switch to **Fluso Cloud** to see them. + +When you reopen the app, it starts on Cloud. Select your saved local or remote workspace again. + +## Start with a document + +Open a chat and attach a document with the **+** button, or drag it into the conversation. Try a set of meeting notes: + +> *"Turn these notes into a short plan for next week. Separate agreed actions from open questions. Include an owner or date only where the notes give one."* + +Read the answer against your notes. Continue in the same chat to correct a detail or add another document. + +An attachment copies the file into the selected workspace. It does not give Fluso access to the rest of your computer. + +## Work with a folder + +For files you use repeatedly, [share a folder](/workspaces/manage#share-a-folder) in workspace settings. Keep reference files read-only. Grant write access only where you want Fluso to edit files. + +Tell Fluso the folder path shown by the settings and what you want: + +> *"Review the meeting notes in this folder. Give me a short list of decisions that are still open, with the source file for each. Leave the files unchanged."* + +For a remote workspace, shared folders belong to the remote machine. Attach a file from your Mac when you want to copy it there. + +## Keep an Agent for repeat work + +Create an [Agent](/developers/agent-builder) in this workspace to keep instructions for a job you return to. For example, keep an assistant for reviewing your project notes. + +Once its results are useful, ask in the Agent's working conversation: + +> *"Every Friday at 4 PM in Europe/Zurich, review this project's notes. Summarize progress, open decisions, and next week's priorities here."* + +Use your own time and timezone. Check the saved [routine](/developers/schedules) in **Routines**. Read its results in the conversation or **Inbox**. + +The machine running Fluso must stay awake and connected. Closing the desktop app leaves accepted work running. After downtime, missed recurring runs are combined into one catch-up run. + +## Return from another location + +For a remote machine, reopen your [SSH tunnel](/workspaces/setup#connect-from-another-mac) or use its configured HTTPS address. Your Mac must be able to reach that machine. Pairing alone does not make a home computer reachable over the internet. + +Select the saved connection to return to its work. Closing the tunnel disconnects your app, but leaves work running on the remote machine. + +To use an external service, [connect its custom MCP server](/workspaces/manage#connect-an-app) in this workspace. Cloud app connections do not carry over. + +For provider changes, storage, updates, and backups, open [settings and maintenance](/workspaces/manage). diff --git a/public/_redirects b/public/_redirects index b4c54d6..538f7ab 100644 --- a/public/_redirects +++ b/public/_redirects @@ -40,3 +40,9 @@ /developers/architecture /developers/api-ref/architecture 301 /developers/architecture/ /developers/api-ref/architecture 301 /llms.mdx/docs/developers/architecture/content.md /llms.mdx/docs/developers/api-ref/architecture/content.md 301 +/workspaces/macos /workspaces/setup 301 +/workspaces/macos/ /workspaces/setup 301 +/workspaces/linux /workspaces/setup 301 +/workspaces/linux/ /workspaces/setup 301 +/llms.mdx/docs/workspaces/macos/content.md /llms.mdx/docs/workspaces/setup/content.md 301 +/llms.mdx/docs/workspaces/linux/content.md /llms.mdx/docs/workspaces/setup/content.md 301 From b73bd8389a4900da9a16fdb37cf78d0b3b51632b Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 21:02:13 +0200 Subject: [PATCH 4/7] docs: separate Cloud and daemon API guides --- .cspell.json | 6 +- AGENTS.md | 5 +- app/global.css | 5 + content/docs/admin-panel.mdx | 2 +- .../api-ref => api}/architecture.mdx | 4 +- content/docs/api/cloud/apps.mdx | 44 ++++++ content/docs/api/cloud/client-sign-in.mdx | 49 +++++++ content/docs/api/cloud/meta.json | 11 ++ content/docs/api/cloud/sharing.mdx | 101 ++++++++++++++ content/docs/api/cloud/tokens.mdx | 68 +++++++++ content/docs/api/daemon/meta.json | 9 ++ content/docs/api/daemon/pairing.mdx | 71 ++++++++++ content/docs/api/daemon/settings.mdx | 75 ++++++++++ content/docs/api/first-request.mdx | 80 +++++++++++ content/docs/api/index.mdx | 57 ++++++++ content/docs/api/meta.json | 11 ++ content/docs/api/workflows/agents.mdx | 93 ++++++++++++ content/docs/api/workflows/connections.mdx | 88 ++++++++++++ content/docs/api/workflows/conversations.mdx | 86 ++++++++++++ content/docs/api/workflows/files.mdx | 132 ++++++++++++++++++ content/docs/api/workflows/meta.json | 16 +++ content/docs/api/workflows/runs.mdx | 91 ++++++++++++ .../api-ref => api/workflows}/schedules.mdx | 6 +- content/docs/api/workflows/streaming.mdx | 49 +++++++ .../api-ref => api/workflows}/usage.mdx | 4 +- content/docs/api/workflows/workspace.mdx | 64 +++++++++ content/docs/deployment-models.mdx | 2 +- .../api-ref/agents-and-versions.mdx | 54 ------- .../developers/api-ref/authentication.mdx | 85 ----------- .../developers/api-ref/files-and-projects.mdx | 59 -------- content/docs/developers/api-ref/meta.json | 16 --- content/docs/developers/api-ref/runs.mdx | 46 ------ content/docs/developers/api-ref/sharing.mdx | 54 ------- .../api-ref/threads-and-messages.mdx | 56 -------- content/docs/developers/meta.json | 3 +- content/docs/features/open-architecture.mdx | 4 +- content/docs/features/projects.mdx | 4 +- content/docs/index.mdx | 4 +- content/docs/meta.json | 2 + content/docs/workspaces/index.mdx | 2 +- public/_redirects | 33 ++++- public/images/api/developer-token-form.png | Bin 0 -> 71289 bytes 42 files changed, 1258 insertions(+), 393 deletions(-) rename content/docs/{developers/api-ref => api}/architecture.mdx (90%) create mode 100644 content/docs/api/cloud/apps.mdx create mode 100644 content/docs/api/cloud/client-sign-in.mdx create mode 100644 content/docs/api/cloud/meta.json create mode 100644 content/docs/api/cloud/sharing.mdx create mode 100644 content/docs/api/cloud/tokens.mdx create mode 100644 content/docs/api/daemon/meta.json create mode 100644 content/docs/api/daemon/pairing.mdx create mode 100644 content/docs/api/daemon/settings.mdx create mode 100644 content/docs/api/first-request.mdx create mode 100644 content/docs/api/index.mdx create mode 100644 content/docs/api/meta.json create mode 100644 content/docs/api/workflows/agents.mdx create mode 100644 content/docs/api/workflows/connections.mdx create mode 100644 content/docs/api/workflows/conversations.mdx create mode 100644 content/docs/api/workflows/files.mdx create mode 100644 content/docs/api/workflows/meta.json create mode 100644 content/docs/api/workflows/runs.mdx rename content/docs/{developers/api-ref => api/workflows}/schedules.mdx (71%) create mode 100644 content/docs/api/workflows/streaming.mdx rename content/docs/{developers/api-ref => api/workflows}/usage.mdx (72%) create mode 100644 content/docs/api/workflows/workspace.mdx delete mode 100644 content/docs/developers/api-ref/agents-and-versions.mdx delete mode 100644 content/docs/developers/api-ref/authentication.mdx delete mode 100644 content/docs/developers/api-ref/files-and-projects.mdx delete mode 100644 content/docs/developers/api-ref/meta.json delete mode 100644 content/docs/developers/api-ref/runs.mdx delete mode 100644 content/docs/developers/api-ref/sharing.mdx delete mode 100644 content/docs/developers/api-ref/threads-and-messages.mdx create mode 100644 public/images/api/developer-token-form.png diff --git a/.cspell.json b/.cspell.json index 9e4d0fe..066fcfb 100644 --- a/.cspell.json +++ b/.cspell.json @@ -84,6 +84,10 @@ "timecode", "uvx", "uuidgen", - "webhook" + "webhook", + "healthz", + "OpenRouter", + "versionable", + "xhigh" ] } diff --git a/AGENTS.md b/AGENTS.md index 5d9ad53..1b2d00a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,11 +13,12 @@ The navigation keeps Home first and sorts the top-level sections alphabetically. Agents follows the user journey. Other sections keep their existing order: - **Administration** - `/admin-panel`. Browser-admin access, dashboards, governance records, runtime controls, and network policy. -- **Agents (Beta)** - `/developers/*`. Start with a familiar task, then improvement and scheduling. Keep Examples, More about Agents (`/developers/guides/*`), and For developers (`/developers/api-ref/*`) collapsed by default. Introduce features when they help the reader's current task. Keep prompts natural and explain terms before using them. +- **Agents (Beta)** - `/developers/*`. Start with a familiar task, then improvement and scheduling. Keep Examples and More about Agents (`/developers/guides/*`) collapsed by default. Introduce features when they help the reader's current task. Keep prompts natural and explain terms before using them. - **App setup** - `/integrations/{github, gmail, google-calendar, slack}`. Per-app permissions and prompts, not feature pages. All of them connect through the Apps tab of the Add MCP dialog. - **Deployment models** - `/deployment-models`. Current availability and operating boundaries for Fluso Cloud, dedicated AWS VPC, planned Azure VNet, and scoped on-premises deployments. - **Features** - `/features/*`. Approvals and permissions, Apps and MCP servers, Chat, Confidential mode, Imports, Memory, Projects, Skills, and Tasks. -- **Fluso Local** - `/workspaces/*`. Setup and everyday use on your own machine, followed by settings and maintenance. Keep macOS and Linux as choices inside one setup guide. Request daemon download links from support; do not publish unlisted release URLs. Account API access starts at `/developers/api-ref/authentication`; full endpoint reference is served at each workspace's `/docs`. +- **Fluso API** - `/api/*`. Start with access and a first request. Keep Cloud and daemon credentials and administration separate. Document shared workflows once. Each deployment's `/docs` and `/v1/openapi.json` own exact request and response schemas. Keep endpoint tables collapsed after the workflow. Preserve old API URLs with redirects. +- **Fluso Local** - `/workspaces/*`. Setup and everyday use on your own machine, followed by settings and maintenance. Keep macOS and Linux as choices inside one setup guide. Request daemon download links from support; do not publish unlisted release URLs. API access is documented under Fluso API. - **Get started** - `/going-deeper`, `/introduction`, `/quickstart`. These pages explain the path from first setup to daily use. - **Reference** - `/resources/{faq, legal, pricing}`. Legal links to the terms of service at `https://fluso.ai/terms` and privacy policy at `https://fluso.ai/privacy`; do not duplicate the policies in the docs. - **Release notes** - `/release-notes`. Customer-facing changes, newest first. diff --git a/app/global.css b/app/global.css index 25d980c..ea1d67c 100644 --- a/app/global.css +++ b/app/global.css @@ -7,6 +7,11 @@ quotes: none; } +/* Keep reference descriptions readable beside long endpoint paths on phones. */ +#nd-page details td:last-child { + min-width: 14rem; +} + /* Fluso brand (ported from Mintlify docs.json colors) */ :root { --color-fd-primary: #5b21b6; diff --git a/content/docs/admin-panel.mdx b/content/docs/admin-panel.mdx index 3403be5..439ff2b 100644 --- a/content/docs/admin-panel.mdx +++ b/content/docs/admin-panel.mdx @@ -45,7 +45,7 @@ The **Admin** organization role does not grant access to Fluso Admin. Panel acce Each governance event records the actor, action, target, detail, and time. Current action types cover Microsoft Entra metadata, tenant configuration, teams, roles, access requests, and Agent access records. -This is not an Agent activity trace or a complete record of Admin mutations. It does not show tool calls, logins, prompts, document edits, messages, files, knowledge graphs, runtime controls, Sponsored Confidential changes, or network-policy changes. Inspect the Agent's conversation and [Inbox](/developers/guides/inbox) for work and failures, or use the [Runs API](/developers/api-ref/runs) for recorded request state. +This is not an Agent activity trace or a complete record of Admin mutations. It does not show tool calls, logins, prompts, document edits, messages, files, knowledge graphs, runtime controls, Sponsored Confidential changes, or network-policy changes. Inspect the Agent's conversation and [Inbox](/developers/guides/inbox) for work and failures, or use the [Runs API](/api/workflows/runs) for recorded request state. ## Runtime and user controls diff --git a/content/docs/developers/api-ref/architecture.mdx b/content/docs/api/architecture.mdx similarity index 90% rename from content/docs/developers/api-ref/architecture.mdx rename to content/docs/api/architecture.mdx index 61f2712..3d7ffa6 100644 --- a/content/docs/developers/api-ref/architecture.mdx +++ b/content/docs/api/architecture.mdx @@ -46,8 +46,8 @@ Delegated sessions return requested results to their parent. The main conversati A dropped stream does not prove a run failed. The client reconciles with saved history and status. Runtime recovery can resume interrupted work, but an external action that already succeeded is not automatically undone. -Use conversation history, routine state, and [run status](/developers/api-ref/runs) to distinguish queued work, approval waits, failures, and completed output. Avoid resubmitting an uncertain operation under a new ID without checking it. +Use conversation history, routine state, and [run status](/api/workflows/runs) to distinguish queued work, approval waits, failures, and completed output. Avoid resubmitting an uncertain operation under a new ID without checking it. A [daemon workspace](/workspaces) runs the gateway and runtime on one owner's host. It stores its own account state and files. The desktop connects to it through authenticated native transport. Switching to Cloud does not move that data. -See [API access](/developers/api-ref/authentication) for developer tokens, daemon pairing, and the account API reference. +See [API access](/api/first-request) for developer tokens, daemon pairing, and the account API reference. diff --git a/content/docs/api/cloud/apps.mdx b/content/docs/api/cloud/apps.mdx new file mode 100644 index 0000000..67799bc --- /dev/null +++ b/content/docs/api/cloud/apps.mdx @@ -0,0 +1,44 @@ +--- +title: Managed app connections +icon: plug +description: Connect a supported Cloud app when your integration needs its data or tools. +--- + +Cloud can guide users through connecting supported apps, such as Google Workspace. A connection belongs to the authenticated account. Completing sign-in does not remove its tool-approval rules. + +For a daemon or a custom server, use [custom MCP connections](/api/workflows/connections). + +## Connect the requested app + +For example, a morning-brief Agent may need a calendar connection. Its run can enter `waiting_action` with an `mcp_connect` action in thread state. + +1. Read the action to identify the requested `app_id`. +2. Check the managed connection list for that app. +3. Send `POST /v1/mcp-oauth/catalog/{app_id}/connect` if it needs connection. +4. If the response says `redirect`, open its `authorization_url` for the user. +5. Let the user complete the provider sign-in and Fluso callback. +6. Check connection status and thread state before continuing. + +A `connected` response needs no browser handoff. `PROVISIONING` means provider sign-in succeeded but Fluso setup is still finishing. A refresh can complete registration. It cannot authorize an unconnected app. + +## Complete a custom callback client + +Fluso's signed-in callback normally completes the handoff. A client that handles an allowed callback itself must submit its `completion_proof` under the initiating account. + +Send the proof to `POST /v1/mcp-oauth/catalog/connect/complete`. Optionally set `expected_server_id` to check the returned connection. Protect the proof like a temporary credential. It expires after five minutes. + +A provider redirect alone does not establish that the connection is usable. Wait for the completion response or verified connection status. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/mcp-oauth/catalog` | List app IDs and connection metadata. | + | `GET /v1/mcp-oauth/catalog/connections` | Read provider connection status without repairing it. | + | `POST /v1/mcp-oauth/catalog/{app_id}/connect` | Start authorization or return an existing usable connection. | + | `GET /v1/mcp-oauth/catalog/callback` | Provider/browser handoff. Do not use it for polling. | + | `POST /v1/mcp-oauth/catalog/connect/complete` | Complete the account-bound proof exchange. | + + Reads accept a developer read token. Connection changes require write access. The provider callback validates its own handoff state. + + Managed apps can be unavailable in a deployment, returning 503. `redirect_url`, when supplied, must match an allowed Fluso callback location. + diff --git a/content/docs/api/cloud/client-sign-in.mdx b/content/docs/api/cloud/client-sign-in.mdx new file mode 100644 index 0000000..d609a9e --- /dev/null +++ b/content/docs/api/cloud/client-sign-in.mdx @@ -0,0 +1,49 @@ +--- +title: Client sign-in +icon: log-in +description: Understand browser sign-in, access tokens, and refresh for Cloud clients. +--- + +Use a [developer token](/api/cloud/tokens) for your own script. Native clients can use a browser sign-in flow and a renewable client session. + +The endpoints below support Fluso's client integrations. They are not an open redirect-registration service. The current browser callback supports configured Fluso handoffs, including Telegram. Contact [support](mailto:support@premai.io) before building a new client sign-in handoff. + +## Exchange browser sign-in for a session + +1. Generate a random PKCE `code_verifier` with 43 to 128 allowed characters. +2. Compute its SHA-256 digest as unpadded base64url for `code_challenge`. +3. Start sign-in with `client`, `client_user_id`, and that challenge. +4. Open the returned sign-in URL in the user's browser. +5. Complete the browser step under the user's Clerk app session. +6. Exchange the returned code with the original verifier and client identifiers. + +The verifier can contain ASCII letters, digits, and `-._~`. PKCE binds the returned code to the client that started sign-in. Keep the verifier private until the exchange finishes. + +The state expires after ten minutes. The returned code expires after five minutes and works once. If the exchange response is lost, restart sign-in. + +The exchange returns `auth.access_token`, `auth.refresh_token`, `auth.expires_in`, `auth.session_id`, and the user. Use the access token in the `Authorization: Bearer` header. Store the refresh token privately. + +## Refresh without ending the session + +Send the current `refresh_token` to `POST /v1/auth/client/refresh`. Save both replacement tokens together. Allow only one refresh at a time per client session. + +The previous refresh token can recover the same token pair for 30 seconds. Reuse after that window revokes the session. Refresh extends the session's 30-day expiry. After a rejected refresh, sign in again. + +To sign out, send the refresh token to `/v1/auth/client/revoke`. Then remove the local credentials. + + + | Method and path | Required credential and use | + | --- | --- | + | `POST /v1/auth/client/start` | PKCE challenge. Begin the browser handoff. | + | `POST /v1/auth/client/complete` | Clerk session and returned state. Issue the one-use code. | + | `POST /v1/auth/client/exchange` | Code, client identifiers, and original verifier. Issue tokens. | + | `POST /v1/auth/client/sessions` | Clerk session. Create a client session for an already signed-in app. | + | `GET /v1/auth/client/sessions` | Clerk session. List up to 100 active sessions, newest first. | + | `DELETE /v1/auth/client/sessions/{session_id}` | Clerk session for any owned session, or a client token for itself. | + | `POST /v1/auth/client/refresh` | Current refresh token. Rotate the token pair. | + | `POST /v1/auth/client/revoke` | Refresh token. Revoke its session. Returns 204 even if already invalid. | + + A client token does not grant account administration. In particular, developer-token management requires Clerk sign-in. + + Use your [deployment's schema](/api#find-the-exact-fields) for identifier constraints and response fields. Do not mint a new session for each API request. + diff --git a/content/docs/api/cloud/meta.json b/content/docs/api/cloud/meta.json new file mode 100644 index 0000000..f56a7bc --- /dev/null +++ b/content/docs/api/cloud/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Cloud API", + "icon": "cloud", + "defaultOpen": false, + "pages": [ + "tokens", + "client-sign-in", + "apps", + "sharing" + ] +} diff --git a/content/docs/api/cloud/sharing.mdx b/content/docs/api/cloud/sharing.mdx new file mode 100644 index 0000000..cc6803b --- /dev/null +++ b/content/docs/api/cloud/sharing.mdx @@ -0,0 +1,101 @@ +--- +title: Sharing +sidebarTitle: Sharing +icon: share-2 +description: Share an Agent, conversation, or file, and understand what a public link exposes. +--- + +Public sharing is Cloud-only. Anyone with a published link can read its contents. Owner operations require [authentication](/api/cloud/tokens). Public reads do not. + +## Share an Agent + +An Agent link publishes a saved copy of selected setup. It does not expose the owner's live workspace. + +Read `GET /v1/agents/{agent_id}/share-options` to choose resources. Publish the selection through `POST /v1/agents/{agent_id}/share`: + +```json +{ + "skills": [], + "knowledge_files": [], + "connectors": [] +} +``` + +Use IDs from share options for skills, knowledge names for `knowledge_files`, and server IDs for `connectors`. Instructions and portable settings are included even with empty selections. Selected skill folders and knowledge contents share limits of 20 MiB and 512 regular files. Symbolic links are rejected. Custom connection URLs with query parameters are excluded from share options. + +Review all selected contents and connection URLs before publishing. Stored authentication, conversations, routines, and project files are not bundled. See [Sharing and copies](/developers/guides/sharing) for the recipient experience. + +## Preview and import + +Read `GET /v1/shared/agents/{share_id}` before importing. Send its exact `published_at` with the new name and project to `POST /v1/shared/agents/{share_id}/import`: + +```json +{ + "name": "My release reviewer", + "project_id": "Home", + "published_at": "" +} +``` + +Generate and save a new `Idempotency-Key` for each import. Reuse it and the unchanged body to recover an uncertain import without creating another copy. A changed snapshot returns a conflict rather than silently importing something different from the preview. + +The recipient gets independent setup and selected resource copies. Imported custom skills receive distinct names. App credentials are never inherited. Read the imported Agent's `setup_requirements`, then complete missing connections and consent in its manager conversation. + +A revoked link becomes unavailable. Unsupported older snapshots may require republication by their owner. Revocation does not delete copies already imported. + + + | Method and path | Behavior | + | --- | --- | + | `GET /v1/agents/{agent_id}/share-options` | Available custom skills, knowledge, and custom connections | + | `GET /v1/agents/{agent_id}/share` | Current link record or `null` | + | `POST /v1/agents/{agent_id}/share` | Publish a snapshot | + | `POST /v1/agent-shares/{share_id}/refresh` | Replace the snapshot at the same link | + | `DELETE /v1/agent-shares/{share_id}` | Revoke the link | + | `GET /v1/agent-templates` | Fluso starting-point snapshots | + | `GET /v1/shared/agents/{share_id}` | Public setup, resource manifest, required filenames, and `published_at` | + | `POST /v1/shared/agents/{share_id}/import` | Authenticated import into the recipient's workspace | + + +## Share a conversation + +Publish a conversation with `POST /v1/chat-shares` and its `thread_id`. The link shows a snapshot of its saved messages. Later messages remain private until you refresh that link. + +Revoke the link to prevent further public reads. The private conversation remains. Deleting the conversation itself does not revoke a separately published snapshot. + + + | Method and path | Use | + | --- | --- | + | `POST /v1/chat-shares/lookup-batch` | Find existing links for `thread_ids` without publishing. | + | `POST /v1/chat-shares` | Publish the current conversation or return its existing link. | + | `POST /v1/chat-shares/{share_id}/refresh` | Update saved messages at the same link. | + | `DELETE /v1/chat-shares/{share_id}` | Revoke the link while keeping the private conversation. | + | `GET /v1/shared/chats/{share_id}` | Read the public snapshot without authentication. | + + +## Share a file + +Choose `share_mode` explicitly when publishing a workspace file: + +| Mode | What readers get | +| --- | --- | +| `snapshot` | A saved copy. Later file changes require a refresh. | +| `live` | The current file at its workspace path. A view can start the owner's runtime. | + +Anyone with the link can download the file. Review its contents before publication. + +If an active link already exists, creation returns it even when you request a different mode. Revoke it before changing modes. Refreshing a live link converts it to a snapshot. A failed refresh preserves the existing link. + + + | Method and path | Use | + | --- | --- | + | `POST /v1/asset-shares/lookup-batch` | Find active links for workspace paths without publishing. | + | `POST /v1/asset-shares` | Publish with explicit `share_mode`. Creation can start the runtime in either mode. | + | `POST /v1/asset-shares/{share_id}/refresh` | Save the current file as a new snapshot at the same link. | + | `DELETE /v1/asset-shares/{share_id}` | Revoke the link while keeping the workspace file. | + | `GET /v1/shared/assets/{share_id}` | Read public bytes or follow a snapshot download redirect. | + | `GET /v1/shared/assets/{share_id}/{file_name}` | Read the same file with a descriptive URL suffix. | + + Public reads need no bearer token. Follow redirects and inspect `Content-Type` instead of parsing the response as JSON. + + Revocation blocks new public requests. A previously issued snapshot download URL can remain valid for five minutes. Revocation cannot remove copies someone already downloaded. + diff --git a/content/docs/api/cloud/tokens.mdx b/content/docs/api/cloud/tokens.mdx new file mode 100644 index 0000000..4c60e14 --- /dev/null +++ b/content/docs/api/cloud/tokens.mdx @@ -0,0 +1,68 @@ +--- +title: Developer tokens +icon: key-round +description: Create and maintain Cloud API access for your own integration. +--- + +A developer token lets a script use your Fluso Cloud account. Create it in the app, then store it as your integration's secret. + +## Create a token in Fluso + +1. Open **Settings → Developer tokens** in your Cloud workspace. +2. Click **Create token**. +3. Enter a name that identifies the integration, such as **Weekly report**. +4. Choose **Read only** to inspect data, or **Read and write** to submit work. +5. Choose an expiry, then click **Create token**. +6. Click **Copy token** before closing the result. + + + + Create developer token form with Weekly report, Read and write access, and a 90-day expiry. + + + +The secret appears once. If you lose it, revoke that token and create a replacement. + +## Check access + +After [setting the URL and token](/api/first-request#1-get-access), check the authenticated identity: + +```bash +curl --fail-with-body --silent --show-error \ + "$FLUSO_API_URL/auth/me" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" +``` + +This read does not start a runtime. Check the account and access level before your integration submits work. + +| Access | What it permits | +| --- | --- | +| `read` | Read your Agents, conversations, files, usage, and results | +| `write` | Also submit work, change files and schedules, connect services, and decide individual tool approvals | +| Signed-in app session | Manage developer tokens and permanent tool permissions | + +Access covers the account. A token is not restricted to one Agent or project. Work uses that account's limits. + +## Replace or revoke a token + +Create a replacement in Settings. Update your integration's secret, then check it with `/auth/me`. Revoke the old token after the replacement works. + +The default expiry is 90 days. Revocation reaches cached authentication and open streams within 30 seconds. Revocation does not stop accepted work or existing schedules. [Stop work](/api/workflows/runs#stop-work) or [pause schedules](/api/workflows/schedules) separately. + + + These four management routes require the owning account's Clerk app session. Developer tokens and native client tokens cannot manage developer tokens. + + | Method and path | Use | + | --- | --- | + | `GET /v1/account/tokens` | List metadata, including revoked tokens. Follow `next_cursor` as `after`. | + | `POST /v1/account/tokens` | Create with `name`, `access`, and optional `expires_at`. Requires `Idempotency-Key`. | + | `PATCH /v1/account/tokens/{token_id}` | Change `name` only. Replace the token to change access or expiry. | + | `DELETE /v1/account/tokens/{token_id}` | Revoke it. Repeated revocation returns 204. | + | `GET /v1/auth/me` | Read the authenticated identity. Accepts developer tokens too. | + + Creation returns 201 and the secret once. Retrying the same key and body returns 200 with `secret: null`. A changed body returns 409. If the first response was lost, revoke that token and create another with a new key. + + Omit `expires_at` for 90 days. Send `null` for no expiry, or a future timestamp with a timezone. An account can have at most 100 active tokens. + + +For a native application with an existing sign-in integration, see [client sessions](/api/cloud/client-sign-in). diff --git a/content/docs/api/daemon/meta.json b/content/docs/api/daemon/meta.json new file mode 100644 index 0000000..839afd6 --- /dev/null +++ b/content/docs/api/daemon/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Daemon API", + "icon": "server", + "defaultOpen": false, + "pages": [ + "pairing", + "settings" + ] +} diff --git a/content/docs/api/daemon/pairing.mdx b/content/docs/api/daemon/pairing.mdx new file mode 100644 index 0000000..f7ca039 --- /dev/null +++ b/content/docs/api/daemon/pairing.mdx @@ -0,0 +1,71 @@ +--- +title: Pair an API client +icon: link +description: Give a client access to your Fluso Local workspace. +--- + +A daemon belongs to one owner. Each paired device gets its own token for that owner's workspace. Pairing does not create a separate account or private set of files. + +[Set up Fluso Local](/workspaces/setup) and its provider before submitting work. For remote access, use HTTPS or an SSH tunnel. + +## Get a device token + +On the daemon's host, run this from the installed package folder. Use the same state root as your running daemon: + +```bash +./fluso-daemon pair --root /path/to/state +``` + +Exchange its one-use code from your client: + +```bash +curl --fail-with-body --silent --show-error \ + 'http://127.0.0.1:4111/daemon/v1/pair' \ + -H 'Content-Type: application/json' \ + --data-raw '{"code":"PAIRING_CODE","name":"Weekly report integration"}' +``` + +Replace the address for a remote daemon. Replace `PAIRING_CODE` with the code from the host. + +Save `token` privately. Keep the returned client `id` and `daemonId` with your connection settings. If that daemon identity changes later, pair again. + +Codes expire after ten minutes. A failed or lost exchange requires a new code. Device tokens expire after 30 idle days. Authenticated use renews them. + + + A normal device token grants read/write workspace access, including tool approvals. Add `--manage-host` only for clients that also need machine administration. + + +Use the token as `Authorization: Bearer YOUR_TOKEN`. Browser requests must come from the daemon's exact origin. Use a native or server client for an integration hosted elsewhere. For the [first request](/api/first-request), set `FLUSO_API_URL` to `http://127.0.0.1:4111/v1` or your remote equivalent. + +## Check support and readiness + +`GET /v1/capabilities` reports supported features. Use it to show only the controls available on that daemon. + +`GET /daemon/v1/discovery` reports identity and configuration state. Its `ready` status means configuration is ready. Check `/v1/agent/runtime/status` for runtime readiness. + +`GET /healthz` only proves the daemon is listening. A successful health check does not prove that the model provider works. + +## Remove a device + +Send `DELETE /daemon/v1/clients/{clientId}` to revoke a token. A device can revoke itself. Host-management access also permits listing and revoking other devices. + +Revocation closes that device's streams and prevents further access. It does not delete the workspace. + + + | Method and path | Access and use | + | --- | --- | + | `GET /healthz` | No token. Check liveness. | + | `POST /daemon/v1/pair` | One-use pairing code. Issue a device token. | + | `GET /daemon/v1/discovery` | Paired device. Read identity and configuration state. Host fields require host management. | + | `GET /v1/capabilities` | Paired device. Read feature support. | + | `GET /v1/confidential/attestation` | Paired device. Read the configured confidential-mode verification response. | + | `POST /daemon/v1/pairing-codes` | Host management. Create a code with `manageHost: true` or `false`. | + | `GET /daemon/v1/clients` | Host management. List current devices without token values. | + | `DELETE /daemon/v1/clients/{clientId}` | Self, or host management for another device. Revoke access. | + + Pairing returns `expiresAt` in Unix milliseconds. Device listings use `expires_at` and `manage_host`. Use the exact field names in each response. + + +After enabling and applying confidential mode, `/v1/confidential/attestation` reports `success`, `unavailable`, or `error`. `unavailable` can mean the mode is off or no key is active. + +Cloud sign-in, developer-token management, managed app catalogs, and public sharing are unavailable on a daemon. Use [custom MCP connections](/api/workflows/connections) and its own [host settings](/api/daemon/settings). diff --git a/content/docs/api/daemon/settings.mdx b/content/docs/api/daemon/settings.mdx new file mode 100644 index 0000000..387c7e3 --- /dev/null +++ b/content/docs/api/daemon/settings.mdx @@ -0,0 +1,75 @@ +--- +title: Host settings +icon: settings-2 +description: Change a daemon's model provider, sandbox resources, and shared folders safely. +--- + +These operations require a device paired with `--manage-host`. Ordinary pairing already permits workspace work. Host management adds authority over this machine's configuration and connected devices. + +Prefer [Workspace settings in Fluso](/workspaces/manage) for interactive setup. Use this API when your client needs to manage the same settings. + +## Read, save, then apply + +Saving settings does not apply them to the running sandbox. + +1. Read `GET /daemon/v1/settings` and `GET /daemon/v1/sandbox`. +2. Keep each response's `revision`. +3. Save the change with that resource's `expectedRevision`. +4. Keep the new revisions returned by the saves. +5. Apply both revisions with `POST /daemon/v1/apply`. +6. Read discovery and runtime status to check the outcome. + +For example, change the model while keeping the existing provider and key: + +```json +{ + "expectedRevision": "SETTINGS_REVISION_FROM_READ", + "settings": { + "FLUSO_MODEL": "MODEL_ID_SUPPORTED_BY_YOUR_PROVIDER" + } +} +``` + +Send that body to `PATCH /daemon/v1/settings`. Omitted fields remain unchanged. `null` clears a field. Returned `secrets` are presence flags, not key values. + +Apply the saved revisions with: + +```json +{ + "settingsRevision": "SETTINGS_REVISION_FROM_SAVE", + "sandboxRevision": "SANDBOX_REVISION_FROM_READ", + "interrupt": false +} +``` + +A 409 `INTERRUPTION_REQUIRED` means active work would be interrupted. Ask the owner before repeating with `interrupt: true`. + +## Change the provider or sandbox + +For an OpenAI-compatible endpoint, set `FLUSO_PROVIDER: "custom"`, `OPENAI_BASE_URL`, `FLUSO_MODEL`, and `OPENAI_API_KEY`. Include `/v1` in the base URL when the provider requires it. Other provider choices are `openrouter`, `openai`, and `pcci`. + +A saved key stays tied to its endpoint. Enter a key again when changing that destination. Audio has separate provider and key settings. The [daemon's schema](/api#find-the-exact-fields) lists every supported setting. + +Sandbox changes use `PUT /daemon/v1/sandbox` with `expectedRevision` and the complete `config`. Preserve all intended `hostFiles` grants. An empty list removes shared-folder access. + +Host paths refer to the daemon's machine, even when you connect remotely. Grant only the required folders. Writable grants permit changing or deleting the original host files. Workspace `/v1/files` paths do not grant access to arbitrary host paths. + + + | Method and path | Use | + | --- | --- | + | `GET /daemon/v1/settings` | Read saved provider settings and their revision. | + | `PATCH /daemon/v1/settings` | Save changed fields. `preview: true` validates without saving. | + | `GET /daemon/v1/sandbox` | Read configuration, revision, host RAM, and disk budgets. | + | `PUT /daemon/v1/sandbox` | Replace the sandbox configuration with revision protection. | + | `POST /daemon/v1/apply` | Apply the saved settings and sandbox revisions. | + + Sandbox `config` contains `runtimeProfile`, `hostFiles`, `memoryGiB`, and `diskGiB`. RAM is the VM allocation. Disk is the combined saved-work and temporary-work budget. It excludes images, backups, and shared host folders. Existing workspace data must fit a smaller budget before you apply it. + + On 409 `CONFIGURATION_CONFLICT`, reload both resources and review the newer changes. Do not substitute a fresh revision without reviewing them. + + During an update or apply, workspace routes can return 503 with `Retry-After`. Discovery shows `applying` or `configuration_error` where applicable. Resolve a configuration error before resubmitting work. A 423 locked credential store requires recovery on the host. + + Discovery exposes saved and applied revisions and `restartRequired` to host-authorized clients. After a lost apply response, read those values before another apply. + + +Binary updates, backups, and credential-store recovery use the [daemon CLI](/workspaces/manage). The settings API does not replace those operations. diff --git a/content/docs/api/first-request.mdx b/content/docs/api/first-request.mdx new file mode 100644 index 0000000..f1933ac --- /dev/null +++ b/content/docs/api/first-request.mdx @@ -0,0 +1,80 @@ +--- +title: Your first request +icon: terminal +description: Send a short task and read the complete answer from Cloud or your daemon. +--- + +This example creates a conversation, sends one message, and waits for its answer. It works with either workspace. + +## 1. Get access + +Create a Cloud [developer token](/api/cloud/tokens) with **Read and write** access. For Fluso Local, get a [paired device token](/api/daemon/pairing) after configuring your model provider. + +Open the **API reference** for your workspace to confirm API availability in that release. + +Open Bash in a new example directory. The examples need cURL 7.87 or later, `jq`, and `uuidgen`. + +Set your [workspace's base URL](/api#choose-your-workspace): + +```bash +export FLUSO_API_URL='https://api.fluso.ai/v1' +``` + +Read the token without displaying it or placing it in your shell history: + +```bash +read -r -s -p 'Fluso token: ' FLUSO_API_TOKEN +echo +export FLUSO_API_TOKEN +``` + +Use a secret store in a deployed application. Keep tokens out of URLs, source control, logs, and screenshots. + +## 2. Send a message and read the answer + +Download the example from your workspace: + +```bash +curl --fail --silent --show-error \ + "$FLUSO_API_URL/examples/quickstart.sh" -o quickstart.sh +cat quickstart.sh +``` + +Read the script before running it. Then run it in the same Bash shell: + +```bash +source quickstart.sh +``` + +The script submits a short request, waits for its run, and reads every result page. It retains the IDs for your next message. + +Here is the request pattern behind it: + +| Step | Request | Keep from the response | +| --- | --- | --- | +| Send work | `POST /v1/threads` with `input` and an `Idempotency-Key` | `thread_id`, `input_id`, `run_id` | +| Check progress | `GET /v1/threads/{thread_id}/runs/{run_id}` | Status and any error | +| Read the answer | `GET /v1/threads/{thread_id}/items?run_id={run_id}` | All result pages, including attachments | + +An **idempotency key** identifies one submitted message. Save it and the request body before sending. Reuse both if that request's response is lost. + +**202 means Fluso accepted the work.** Read the run status to learn whether it completed or needs your action. + + + A timeout does not cancel work. Do not rerun the quickstart to recover: it creates a new request. Read the [saved run](/api/workflows/runs) instead. + + +## 3. Continue the conversation + +Send the next message to `POST /v1/threads/{thread_id}/inputs` with a new key: + +```json +{ + "input": "Give me one practical example for each suggestion.", + "mode": "queue" +} +``` + +The reference's `follow-up.sh` runs this step using the IDs from the quickstart. Its `cleanup.sh` removes the example resources when you finish. + +Next, learn to [read conversations](/api/workflows/conversations), [attach a file](/api/workflows/files), or [use an Agent](/api/workflows/agents). diff --git a/content/docs/api/index.mdx b/content/docs/api/index.mdx new file mode 100644 index 0000000..a63f8e8 --- /dev/null +++ b/content/docs/api/index.mdx @@ -0,0 +1,57 @@ +--- +title: Fluso API +icon: code-xml +description: Give Fluso work from your own script or application, then read the result. +--- + +Use the API to send a message, attach a document, or run a saved Agent. Your integration uses the same workspace as the Fluso app. + +Start with one conversation. Add schedules, connections, or live updates when your application needs them. + + + Get access, submit a short task, and read the saved answer. + + +## Choose your workspace + +Cloud and Fluso Local share the conversation, Agent, file, and schedule APIs. Their credentials and administration differ. Data stays in the workspace you address. + +| | Cloud API | Daemon API | +| --- | --- | --- | +| Where work runs | Your Fluso Cloud workspace | Your own machine's Fluso Local workspace | +| Access for scripts | [Developer token](/api/cloud/tokens) | [Paired device token](/api/daemon/pairing) | +| Example base URL | `https://api.fluso.ai/v1` | `http://127.0.0.1:4111/v1` | +| Connected apps | Managed apps and custom MCP servers | Custom MCP servers | +| Public sharing | Agents, conversations, and files | Unavailable | +| Machine settings | Managed by the service | [Provider, sandbox, and devices](/api/daemon/settings) | + +Fluso Dev uses `https://api.dev.khichdi.cc/v1`. Use credentials from the same environment as the URL. + +For a remote daemon, use its HTTPS address or an [SSH tunnel](/workspaces/setup#connect-from-another-mac). A Cloud token cannot access a daemon. + +## Find the exact fields + +Open **Settings → Developer tokens → API reference** in Cloud. On a daemon's host, open `http://127.0.0.1:4111/docs`. + +Each workspace serves its full endpoint reference at `/docs` and its OpenAPI schema at `/v1/openapi.json`. These describe the fields and operations available in that deployment. The reference also supplies runnable examples under `/v1/examples/`. The daemon serves its schema and examples locally. Its interactive reference needs internet access to load the viewer. + +The guides below explain how to combine those operations. Endpoint tables sit inside each guide, after the main workflow. + + + + Create a developer token and choose its access. + + + Pair a client with your own workspace. + + + Continue work and retrieve the complete answer. + + + Handle a timeout without starting the same work twice. + + + + + These guides cover the current public `/v1` API. If your app lacks **Developer tokens**, contact [support](mailto:support@premai.io) about availability for your release. + diff --git a/content/docs/api/meta.json b/content/docs/api/meta.json new file mode 100644 index 0000000..79bec20 --- /dev/null +++ b/content/docs/api/meta.json @@ -0,0 +1,11 @@ +{ + "pages": [ + "!index", + "[code-xml][Overview](/api)", + "first-request", + "cloud", + "daemon", + "workflows", + "architecture" + ] +} diff --git a/content/docs/api/workflows/agents.mdx b/content/docs/api/workflows/agents.mdx new file mode 100644 index 0000000..7a8af91 --- /dev/null +++ b/content/docs/api/workflows/agents.mdx @@ -0,0 +1,93 @@ +--- +title: Agents and configuration +sidebarTitle: Agents and configuration +icon: bot +description: Create a reusable Agent, choose its conversation, and update its instructions safely. +--- + +An Agent adds saved instructions and capabilities to a conversation. Start with [API access](/api/first-request) for credentials and your workspace's complete endpoint reference. + +## Create an Agent + +Send `POST /v1/agents` with a saved `Idempotency-Key` and a body such as: + +```json +{ + "name": "Meeting assistant", + "instructions": "Turn my notes into a short meeting brief. List decisions needed and questions still open." +} +``` + +Keep the returned `agent_id`. To use the Agent, send `POST /v1/threads` with that ID and an `input` message. To change its setup through conversation, also set `purpose: "manager"`. + +Opening the same Agent and purpose returns its existing conversation. Two callers share that conversation and its queue. For independent ordinary chats, omit `agent_id`. + +## Read the current configuration + +`GET /v1/agents/{agent_id}` returns the Agent and its `current_config_id`. Keep that ID before editing. + +## Publish an update + +Send only the changed fields to `PATCH /v1/agents/{agent_id}`, together with the ID you read: + +```json +{ + "base_config_id": "CURRENT_CONFIG_ID", + "instructions": "Turn my notes into a short meeting brief. Start with the decisions I need to make." +} +``` + +An old `base_config_id` returns 409 instead of overwriting another edit. Read the latest configuration, review the difference, then retry your intended change. + +The main conversation uses saved changes on its next turn. Existing work sessions retain their configuration binding. `run.execution` records the configuration, provider, model, and reasoning effort used when execution starts. + +## Select capabilities and knowledge + +Use skill names from [skill inventory](/api/workflows/workspace#choose-skills-for-an-agent) and connection IDs from [connection inventory](/api/workflows/connections). + +For updates, omitted `skills` or `connectors` remain unchanged. `null` removes the selection restriction. An empty list or map explicitly selects none. A connector's `allow` policy still respects account consent. + +To add knowledge, upload a file or copy a workspace file through the knowledge endpoints. Save the returned reference in `knowledge_files` with the current `base_config_id`. Storage alone does not attach it to the Agent. + +Knowledge is a copy. Editing the original workspace file does not change that copy. Each Agent accepts up to 25 knowledge files, with a 20 MiB limit per file. Send `knowledge_files: []` to remove all selected knowledge. + + + Supported effort values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`. An input's explicit effort overrides the Agent default. The runtime chooses the nearest supported effort, preferring a higher level before a lower one. + + Steering without an effort preserves the active turn's setting. An explicit change takes effect at the next execution attempt. It does not restart an in-flight model call. + + Public clients can open main and manager conversations. They cannot create Agent work sessions directly. For independent ordinary conversations, omit `agent_id`. + + All conversations share workspace files. Use distinct output paths for concurrent jobs that must not overwrite each other. + + +## Copies and sharing + +Duplication creates an independent Agent. Keep the same idempotency key when retrying duplication. The retry returns the original copy. + +Public sharing is Cloud-only. A shared Agent snapshot excludes credentials, conversations, and schedules. After importing, inspect `setup_requirements` and complete setup in its manager conversation. The recipient supplies credentials and consent. + +## Delete an Agent + +`DELETE /v1/agents/{agent_id}` deletes the Agent and its owned conversations and revokes its shared Agent link. Save the results you need first. + +For exact fields and sharing operations, use the [workspace API reference](/api#choose-your-workspace). + + + | Method and path | Use | + | --- | --- | + | `GET /v1/agents` | Search or list Agents. Use numbered pages or `all=true`. | + | `POST /v1/agents` | Create saved setup without starting a turn. | + | `GET /v1/agents/{agent_id}` | Read setup and `current_config_id`. | + | `PATCH /v1/agents/{agent_id}` | Save changes with `base_config_id`. | + | `DELETE /v1/agents/{agent_id}` | Delete setup and conversations, retire schedules, and revoke its Agent link. | + | `POST /v1/agents/{agent_id}/avatar` | Save appearance. `avatar: null` resets it. | + | `POST /v1/agents/{agent_id}/star` | Set `starred` to true or false. | + | `POST /v1/agents/{agent_id}/duplicate` | Copy setup and knowledge. Use an idempotency key for retries. | + | `POST /v1/agents/{agent_id}/knowledge-files` | Store multipart `file` and return its reference. | + | `POST /v1/agents/{agent_id}/knowledge-files/from-workspace` | Copy `path` into knowledge and return its reference. | + + Appearance and starring do not change instructions or configuration bindings. Duplication excludes conversations, schedules, and public links. + + Creation and duplication receipts remain with the saved Agent. Their keys do not use the input receipt's 24-hour window. Older `creation_id` is accepted for creation. Do not supply conflicting identifiers. + diff --git a/content/docs/api/workflows/connections.mdx b/content/docs/api/workflows/connections.mdx new file mode 100644 index 0000000..f161543 --- /dev/null +++ b/content/docs/api/workflows/connections.mdx @@ -0,0 +1,88 @@ +--- +title: Connections and approvals +icon: plug +description: Connect custom MCP servers and handle the actions that pause a run. +--- + +A connection lets Fluso use another service's tools. An approval decides whether a requested action may proceed. Connecting a service and approving a tool are separate steps. + +Cloud offers [managed app sign-in](/api/cloud/apps) as well as custom MCP servers. A daemon uses its own custom connections. + +## Connect a custom MCP server + +Start with a public HTTPS server URL. `POST /v1/mcp-oauth/probe` discovers its transport and authentication method without saving a connection. + +| Server authentication | Start with | +| --- | --- | +| No authentication, bearer token, or API key | `POST /v1/mcp-oauth/connections/static` | +| OAuth with automatic client registration | `POST /v1/mcp-oauth/sessions` | +| OAuth with your own client registration | `POST /v1/mcp-oauth/sessions/byo-oauth` | + +For OAuth, open the returned `authorization_url`. An `already_connected` response needs no redirect. + +On Cloud, finish with the callback's `completion_proof` under the initiating account. On a daemon, `/daemon/v1/oauth/callback` completes the connection directly. Poll the returned session ID until it is `connected` or reports an error. + +Read connection inventory with `GET /v1/mcp-oauth/connections`. `needs_reauth` requires the user to reconnect. Inventory alone does not prove that the external server is reachable. + +## Handle a waiting run + +Read `state.pending_actions` from `GET /v1/threads/{thread_id}/state`. + +- For `mcp_connect`, guide the user through the requested connection flow. +- For a pending `mcp_tool`, read the approval and show its actual tool arguments for review. + +After the user approves that tool call, send this body to its approval decision endpoint: + +```json +{ + "decision": "allow", + "scope": "once" +} +``` + +Use `decision: "deny"` to refuse it. A developer write token can make this one-time decision. Permanent permissions require a Cloud app session or a paired daemon device. + +An acknowledgement saves the decision. Follow thread state or events to see work resume. Do not send another input to approve the existing request. + +If the decision returns 409 or 410, read the action again. It may have changed or expired. Do not approve a replacement without reviewing it. + + + | Method and path | Use | + | --- | --- | + | `POST /v1/mcp-oauth/probe` | Discover transport and authentication. | + | `POST /v1/mcp-oauth/connections/static` | Save a key-based or unauthenticated connection. | + | `POST /v1/mcp-oauth/sessions` | Start custom OAuth authorization. | + | `POST /v1/mcp-oauth/sessions/byo-oauth` | Start OAuth with your client registration. | + | `POST /v1/mcp-oauth/sessions/complete` | Cloud only. Save the connection from its completion proof. | + | `GET /v1/mcp-oauth/sessions/{session_id}/status` | Read authorization progress. | + | `GET /daemon/v1/oauth/callback` | Daemon only. Receive the provider's OAuth callback. | + | `GET /v1/mcp-oauth/connections` | List connections without exposing secrets. | + | `POST /v1/mcp-oauth/connections/refresh` | Refresh Cloud registrations or daemon tool discovery. | + | `DELETE /v1/mcp-oauth/connections/{server_id}` | Disconnect and invalidate its pending authority. | + | `GET /v1/mcp-oauth/connections/{server_id}/tools` | Read tool descriptions, input schemas, and read-only classifications. | + | `POST /v1/mcp-oauth/connections/{server_id}/tools` | Refresh or repair the connection before tool discovery. | + + Reusing a static connection's `server_id` updates that connection. Its `auth_method` is `bearer`, `api_key`, or `no_auth`. Only the first two accept a token. API keys use `header_name`, which defaults to `X-Api-Key`. + + Tool discovery can return saved tool information with an `error`. A 409 `CONNECTION_CHANGING` includes `Retry-After: 1`. Check the connection again after that delay. + + OAuth `pending` and `processing` are unfinished states. Handle `error`, `expired`, or `superseded` before a new sign-in attempt. + + + + | Method and path | Use | + | --- | --- | + | `GET /v1/mcp-oauth/permissions/approvals/{approval_id}` | Read the action, status, and expiry. | + | `POST /v1/mcp-oauth/permissions/approvals/{approval_id}/decision` | Allow or deny a tool request. | + | `POST /v1/mcp-oauth/permissions/approvals/{approval_id}/cancel` | Dismiss the pending action without granting access. | + | `GET /v1/mcp-oauth/permissions/mode` | Read a thread's `auto_approve` setting and `version`. Supply `thread_id`. | + | `PUT /v1/mcp-oauth/permissions/mode` | Change it with `expected_version` and the same `thread_id`. | + | `GET /v1/mcp-oauth/permissions/default` | Read the account default. | + | `PUT /v1/mcp-oauth/permissions/default` | Change the default with `expected_version`. | + | `GET /v1/mcp-oauth/connections/{server_id}/permissions` | Read saved and effective tool policies. | + | `PUT /v1/mcp-oauth/connections/{server_id}/permissions` | Change persistent connection or tool policies. | + + Cloud developer tokens cannot use these PUT routes or decision `scope: "always"`. Use a signed-in app session for permanent changes. Paired daemon devices can make these changes. + + A stale permission version returns 409. Enabling thread auto-approval can resolve an eligible pending request. An explicitly denied tool remains denied. Changing the account default does not decide an existing pending approval. + diff --git a/content/docs/api/workflows/conversations.mdx b/content/docs/api/workflows/conversations.mdx new file mode 100644 index 0000000..df41120 --- /dev/null +++ b/content/docs/api/workflows/conversations.mdx @@ -0,0 +1,86 @@ +--- +title: Conversations and results +sidebarTitle: Conversations and results +icon: messages-square +description: Submit work, continue a conversation, and read the complete saved answer. +--- + +A **thread** is a conversation. An **input** is a message you submit. A **run** tracks the resulting work. + +Start with the runnable [API quickstart](/api/first-request). These are the operations behind it. + +## Create a conversation + +Send `POST /v1/threads` with an `Idempotency-Key` and an input body: + +```json +{ + "input": "Explain three ways to check whether a source is reliable." +} +``` + +Save the key and body before sending. The receipt returns `thread_id` and `run_id`. Use those values rather than deriving one from the other. + +To open a conversation without starting work, omit `input` and `attachments`. To use a saved Agent, include `agent_id`. Its main conversation is reused. `purpose: "manager"` opens its setup conversation. + +## Send a message + +Send `POST /v1/threads/{thread_id}/inputs` with the next `input` and a new key. If the conversation is busy, use `mode: "queue"` to wait or `mode: "steer"` to guide active work. + +A retry must keep the original key and unchanged body. Reusing a key with different content returns 409. A new key means new work. + +The **follow-up** example in your [workspace's reference](/api#choose-your-workspace) continues the quickstart conversation in the same shell. + +## Manage queued messages + +Read thread state before changing queued work. An active or completed input cannot be edited as a queued message. + +Editing an input requires a new `Idempotency-Key` for that edit. Keep it when retrying the edit. If the input started meanwhile, a 409 preserves the conflict. Keep the user's draft instead of silently submitting replacement work. + +A thread stop holds follow-up inputs. Resume a held input explicitly after reviewing state. + +## Read message history + +Read `GET /v1/threads/{thread_id}/items?run_id={run_id}` for messages, tool results, and attachments belonging to a run. Omit the filter to read the conversation. + +Entries are in `data`. Read every page: pass `next_cursor` as `after` with the unchanged `revision`, until `next_cursor` is null. Keep filters unchanged between pages. + +If a snapshot edit or deletion causes a pagination conflict, discard the partial read and restart without `after` or `revision`. New messages alone do not invalidate the snapshot. + +Read the saved [run status](/api/workflows/runs) as well as the messages. An empty page does not prove the run finished or failed. + +## Follow live progress + +`GET /v1/threads/{thread_id}/events` streams Server-Sent Events. Submitting with `stream: true` starts with an `input.accepted` receipt. Save it before processing later events. + +Streams do not replay missed events. After reconnecting, read saved state and items. Ignore unknown event types and keepalive comments. Replace temporary text with its saved item to avoid showing it twice. + +See [live updates](/api/workflows/streaming) for a complete reconnect sequence and the runnable `watch.mjs` example. + +## Delete a conversation + +`DELETE /v1/threads/{thread_id}` deletes an ordinary conversation and stops its execution. Save needed results first. Agent main and manager conversations require deleting their Agent instead. + +Conversation deletion does not remove a separately published chat snapshot or all workspace files. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/threads` | List by Agent, project, or purpose. Follow numbered `pagination.page` and `pages`. | + | `POST /v1/threads` | Create an ordinary chat or open an Agent's main or manager conversation. | + | `GET /v1/threads/{thread_id}` | Read details and configuration binding. | + | `PATCH /v1/threads/{thread_id}` | Change title, archive, pin, labels, or application metadata. | + | `DELETE /v1/threads/{thread_id}` | Stop and remove an ordinary conversation. | + | `POST /v1/threads/{thread_id}/inputs` | Submit new work or steer active work. | + | `PATCH /v1/threads/{thread_id}/inputs/{input_id}` | Replace queued content with an edit key. | + | `DELETE /v1/threads/{thread_id}/inputs/{input_id}` | Cancel that queued input. | + | `POST /v1/threads/{thread_id}/inputs/{input_id}/resume` | Resume a held input. | + | `GET /v1/threads/{thread_id}/items` | Read saved items with snapshot pagination. | + | `GET /v1/threads/{thread_id}/state` | Read active work, queues, partial output, and pending actions. | + | `GET /v1/threads/{thread_id}/preview` | Read the latest short message preview. | + | `GET /v1/threads/{thread_id}/files` | List files associated with this conversation. | + | `POST /v1/threads/{thread_id}/read` | Update unread tracking. Does not approve tools. | + | `PATCH /v1/threads/{thread_id}/reorder` | Set position within its current project. Cross-project moves are rejected. | + + Agent binding and provenance belong to the server. Custom metadata cannot change them. See [live events](/api/workflows/streaming) and [cancellation](/api/workflows/runs#stop-work) for execution controls. + diff --git a/content/docs/api/workflows/files.mdx b/content/docs/api/workflows/files.mdx new file mode 100644 index 0000000..6b0b019 --- /dev/null +++ b/content/docs/api/workflows/files.mdx @@ -0,0 +1,132 @@ +--- +title: Files and projects +sidebarTitle: Files and projects +icon: folder-kanban +description: Upload inputs, read saved output, and protect files from conflicting edits. +--- + +Files and projects belong to the authenticated workspace. You do not need a runtime ID. Use the paths returned by the API, including saved attachments. + +## Projects + +Use `/v1/projects` to list or create projects. Read, update, or delete a project at `/v1/projects/{project_id}`. Project IDs are also storage names. Keep the exact returned value when building paths. + +Deleting a project deletes its conversations and files, then retires their schedules. Save needed results before deletion. See your [workspace's endpoint reference](/api#choose-your-workspace) for export and deletion behavior. + +## Read a text file + +After [setting your API URL and token](/api/first-request), use a path returned by `GET /v1/files`. Replace the example path below: + +```bash +curl --fail-with-body --silent --show-error --get \ + "$FLUSO_API_URL/files/read" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" \ + --data-urlencode 'path=/projects/Home/files/notes.md' +``` + +Use `GET /v1/files` to browse and `GET /v1/files/download` for file bytes. A text read includes a `revision` for safe replacement. + +## Upload a file + +1. Create an upload with `POST /v1/uploads`, its filename, MIME type, size, and a saved `Idempotency-Key`. +2. Send the bytes to `upload_url` in a multipart POST. Include all returned `upload_fields` unchanged, with the file last. +3. Confirm with `POST /v1/uploads/confirm` and `{"id":"UPLOAD_ID"}`. +4. Read `GET /v1/uploads/{upload_id}` until it finishes. +5. Attach its returned `target_path` to a conversation input. + +Do not send your Fluso bearer token to `upload_url`. + +A `confirmed` upload is ready to attach. `history_unavailable` also means the file was saved, but its new version was not recorded. Show that warning. For `failed`, `cancelled`, or `conflict`, inspect the saved error before starting another transfer. + +If a request times out, read the existing upload. Starting another upload can duplicate work. Confirmed receipts do not guarantee that a file remains unchanged after later edits. + +The full reference includes runnable **upload**, **attach**, and **download** examples. It also covers expired transfer credentials and recovery without another transfer. + +## Edit without losing another change + +Use `PUT /v1/files/text` with `path`, `content`, and the latest `revision` to replace text. Use `overwrite: false` when creating a new file that must not replace an existing path. Add `recursive: true` if its parent directories do not exist yet. + +If another writer changed the file, read the new content and merge before retrying. Do not drop the revision check to force an overwrite. + +Text writes allow 1 MiB of UTF-8 content. Text reads default to 5 MiB. Use uploads and downloads for larger or binary files. + + + Versions and comments apply to ordinary files under `/files/` and `/projects/{project_id}/files/`, excluding hidden directories. + + Replacement refuses to lose existing history unless you explicitly permit it with `allow_history_loss: true`. Treat that option as a data-loss decision. For uploads, changing a failed request requires a new key. Retrying the old key returns its saved failure. + + A directory copy can partly succeed. Inspect the destination before retrying it. An attachment with `available: false` records historical output whose file is no longer at that path. + + +## Restore or discuss a file + +Read `/v1/files/versions` to inspect saved versions before restoring one. Download a selected version through `/versions/content` with its `path` and `commit`. A missing version returns 404 instead of the current file. + +Restoring one file preserves the replaced bytes as a version first. Other files remain unchanged. If preservation fails, review the history warning before allowing any loss. + +File comments can attach a note to a quoted passage, slide, sheet range, or image region. Creating a comment does not start an Agent run. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/projects` | List projects. Follow `next_cursor` as `after`. | + | `POST /v1/projects` | Create a project. Its name becomes its ID. | + | `GET /v1/projects/{project_id}` | Read details and conversation counts. | + | `PATCH /v1/projects/{project_id}` | Change description, icon, labels, archive, or pin settings. Does not rename it. | + | `DELETE /v1/projects/{project_id}` | Delete its conversations and files, then retire schedules. | + | `GET /v1/projects/{project_id}/export` | Stream an export download, not JSON. | + | `GET /v1/projects/{project_id}/search` | Search project files with `q`. | + | `GET /v1/projects/{project_id}/files/origins` | Read recorded file provenance. Missing entries mean unknown origin. | + | `PATCH /v1/projects/{project_id}/files/pins` | Add or remove relative paths such as `files/report.pdf`. | + + Names use 1 to 64 ASCII letters, digits, single spaces, hyphens, or underscores, starting with a letter or digit. URL-encode names in paths. + + Deleting Home also removes its work. An empty Home is recreated on first use. Retry a failed deletion to finish schedule cleanup. If you reuse the project name, deletion addresses the new project. + + + + | Method and path | Use | + | --- | --- | + | `POST /v1/uploads` | Prepare transfer with `file_name`, `mime_type`, and byte `file_size`. | + | `POST /v1/uploads/confirm` | Confirm transfer with `id`. Repeating confirmation is safe. | + | `GET /v1/uploads/{upload_id}` | Read import progress and the final `target_path`. | + | `GET /v1/uploads` | List receipts, following `next_cursor` as `after`. | + | `DELETE /v1/uploads/{file_id}` | Cancel a pending transfer or dismiss a saved-file history warning. | + + Save an `Idempotency-Key` when preparing a transfer. Retrying the same settings returns its receipt. Changed settings return 409. At most four uploads can be pending. + + A retry after transfer credentials expire can return a receipt without `upload_url`. Read or confirm that upload before starting another. A processing or completed receipt needs no repeat transfer. + + Confirmation can return 200 while import is still processing. Poll until a final status. Receipts remain for seven days after completion. Receipt expiry does not delete the saved file. + + Cancelling a pending upload removes temporary bytes. Dismissing `history_unavailable` keeps the saved file. This endpoint does not delete workspace content. + + + + | Method and path | Use | + | --- | --- | + | `GET /v1/files` | Browse a directory, optionally recursively. Keep filters while following `next_cursor`. | + | `GET /v1/files/stat` | Read metadata for a workspace `path`. | + | `GET /v1/files/read` | Read UTF-8 content and its revision. | + | `GET /v1/files/download` | Stream raw bytes and download headers. | + | `PUT /v1/files/text` | Write text, optionally with a revision or `overwrite: false`. | + | `POST /v1/files/edit` | Replace matching text. `replace_all: true` changes every occurrence. | + | `POST /v1/files/directories` | Create a directory. `recursive: true` creates missing parents. | + | `POST /v1/files/move` | Move or rename. Existing destinations require explicit overwrite. | + | `POST /v1/files/copy` | Copy. Directory copies require `recursive: true`. | + | `DELETE /v1/files` | Supply `path` in the query. Nonempty directories also need `recursive=true`. | + | `POST /v1/files/batch-delete` | Delete selected paths and report each result. | + | `GET /v1/files/versions` | List history and the current file fingerprint. | + | `PATCH /v1/files/versions` | Rename a recorded version. | + | `GET /v1/files/versions/{commit}` | Read version metadata without its bytes. | + | `GET /v1/files/versions/content` | Download the selected version's exact bytes. | + | `POST /v1/files/versions/restore` | Restore one file, preserving the replaced version first. | + | `GET /v1/files/comments` | Read file comments across conversations. | + | `POST /v1/files/comments` | Add a note with a passage or object anchor. | + | `PATCH /v1/files/comments` | Edit a note, anchor, or status. | + | `DELETE /v1/files/comments` | Remove a comment by ID. | + + Script-created files may have no history until delivered. `versionable: false` means an overwrite cannot preserve the current bytes. Do not treat version history as a complete workspace backup. + + File deletion revokes public links before removing content. If deletion fails, links remain revoked. Retry deletion to remove the remaining content. + diff --git a/content/docs/api/workflows/meta.json b/content/docs/api/workflows/meta.json new file mode 100644 index 0000000..8e0971e --- /dev/null +++ b/content/docs/api/workflows/meta.json @@ -0,0 +1,16 @@ +{ + "title": "Shared workflows", + "icon": "workflow", + "defaultOpen": false, + "pages": [ + "conversations", + "runs", + "streaming", + "agents", + "files", + "connections", + "schedules", + "workspace", + "usage" + ] +} diff --git a/content/docs/api/workflows/runs.mdx b/content/docs/api/workflows/runs.mdx new file mode 100644 index 0000000..b3aa018 --- /dev/null +++ b/content/docs/api/workflows/runs.mdx @@ -0,0 +1,91 @@ +--- +title: Runs and recovery +sidebarTitle: Runs and recovery +icon: activity +description: Follow accepted work, recover after a disconnection, and stop a run deliberately. +--- + +Submitting an input returns a receipt with `thread_id`, `input_id`, and `run_id`. Save all three. **202 means accepted, not completed.** + +Read `GET /v1/threads/{thread_id}/runs/{run_id}` to check progress. Read `GET /v1/threads/{thread_id}/state` for queued work and pending actions. + +## Understand the status + +| Status | What to do | +| --- | --- | +| `queued` or `running` | Wait, then read again. | +| `waiting_action` | Read the pending approval or connection request in thread state. | +| `held` | Inspect the state and queued inputs before resuming. | +| `completed` | Read all saved result pages and check the answer. | +| `failed` or `cancelled` | Read the error and any saved results before submitting more work. | + +A completed run can contain a failed tool call. A failed run can still have changed an external service. Check the results against the requested task. + +## Recover after a timeout + +- If the submission response was lost, retry the same body with the same `Idempotency-Key`. +- If you received a receipt, read that run and its [saved items](/api/workflows/conversations#read-message-history). +- If a stream closes, reconnect for new events and read saved state for missed results. + +A client timeout does not cancel work. Creating a new key can start another execution. + +Input retry receipts remain until 24 hours after completion. After that, inspect the saved run before deciding whether to submit new work. Runs remain readable until their conversation is deleted. + + + An error from `POST /v1/threads` can include `error.details.thread_id`. Save that ID and inspect the thread state. Its presence does not prove input acceptance. + + After resolving the cause, send the original input and key to that thread's `/inputs` endpoint. This can recover an uncertain acceptance within the retry window. + + +## Stop work + +Use `POST /v1/threads/{thread_id}/runs/{run_id}/cancel` for one run. Use `POST /v1/threads/{thread_id}/stop` for the conversation. + +Read the state until execution stops. A cancellation acknowledgement does not prove that a tool has stopped, and cancellation cannot undo a completed external action. + +Use the **Recovery and cancellation** examples in your [workspace's full reference](/api#choose-your-workspace) for retries, held inputs, and cancellation. + +## Decide whether to retry + +A failed run does not become a new execution when you resend its old key. Read its results before choosing a new message and key. + +For temporary read failures, delay retries with backoff and a maximum wait. Honor `Retry-After`. Do not retry when `error.retryable` is explicitly false. + +For an uncertain write, use that operation's documented retry mechanism. Input keys, upload keys, schedule IDs, and configuration revisions solve different problems. They are not interchangeable. + + + | Response | Action | + | --- | --- | + | 400 or 422 | Correct the request fields. | + | 401 | Replace the credential or sign in again. | + | 402 | Check the account allowance and any `resets_at` value. | + | 403 | Use the required access level or signed-in app session. | + | 404 | Check the workspace and resource ID. The resource may no longer exist. | + | 409 | Read the resource before resolving the conflict. Keep unsent edits. | + | 410 | Follow the error's instructions for the expired resource or approval. | + | 413 or 507 | Reduce the transfer or free workspace storage. | + | 408, 429, 500, 502, 503, or 504 | Retry transient reads with a delay. Preserve keys for uncertain writes. | + + Errors include `error.code` and `error.message`, with details and retry guidance where available. Keep `request_id` or `X-Request-ID` for support. A request ID is not an idempotency key. + + Use UUIDs for new input keys. Accepted characters are ASCII letters, digits, `_`, `-`, `.`, `:`, and `@`, with a maximum of 128 characters. A single dot, consecutive dots, and the `agent-turn:` and `agent-result:` prefixes are reserved. + + For historical run IDs containing `/`, use `GET /v1/runs` with URL-encoded `thread_id` and `run_id` query parameters. Read output through the items endpoint's `run_id` filter. + + + + | Method and path | Use | + | --- | --- | + | `GET /v1/runs` | List saved runs or find one through query filters. | + | `GET /v1/threads/{thread_id}/runs` | List a conversation's runs. Follow `next_cursor` as `after`. | + | `GET /v1/threads/{thread_id}/runs/{run_id}` | Read status, error, and recorded execution settings. | + | `GET /v1/threads/{thread_id}/runs/{run_id}/usage` | Read recorded usage or `null` when unreported. | + | `POST /v1/threads/{thread_id}/runs/{run_id}/cancel` | Request cancellation of this run. | + | `POST /v1/threads/{thread_id}/stop` | Stop active work and hold follow-ups. | + + An input accepts up to 200,000 text characters and 25 attachments. A thread holds up to 100 queued inputs and 50 steering inputs per active turn. + + Each thread retains up to 1,000 recent input receipts. At capacity, admission refuses new work while preserving existing retry receipts. Waiting for a local timeout does not clear accepted work. + + Merge `state.runs` by ID when reading multiple item pages. An old item without a saved run has an unknown outcome. Its text alone does not prove success. + diff --git a/content/docs/developers/api-ref/schedules.mdx b/content/docs/api/workflows/schedules.mdx similarity index 71% rename from content/docs/developers/api-ref/schedules.mdx rename to content/docs/api/workflows/schedules.mdx index 7b96da3..a68ac9e 100644 --- a/content/docs/developers/api-ref/schedules.mdx +++ b/content/docs/api/workflows/schedules.mdx @@ -36,8 +36,10 @@ Use an IANA timezone. At daylight-saving changes, skipped local times move forwa | Edit or pause | `PATCH /v1/schedules/{schedule_id}` | | Delete | `DELETE /v1/schedules/{schedule_id}` | -List responses include last-run information. Use the fields in your [workspace's full reference](/developers/api-ref/authentication#choose-your-workspace) to edit the message, timing, or status. +List responses include last-run information. Follow numbered `pagination.page` and `pages`. Use `attention=true` on `/v1/schedules` to find schedules needing review. Pause with `{"status":"paused"}` or resume with `{"status":"active"}` through PATCH. Read the schedule again to confirm its saved state. -Deleting a schedule prevents future submissions. It does not cancel already accepted work. [Cancel that run separately](/developers/api-ref/runs#stop-work) if needed. +Keep the schedule ID and unchanged settings when retrying creation. A new ID creates another schedule. To change between recurring and one-time timing, supply exactly one of `cron` or `at`. + +Deleting a schedule prevents future submissions. It does not cancel already accepted work. [Cancel that run separately](/api/workflows/runs#stop-work) if needed. On a daemon, the host must be awake for work to run. After downtime, missed recurring runs are combined into one catch-up run. Failed scheduled work can pause the schedule for attention. diff --git a/content/docs/api/workflows/streaming.mdx b/content/docs/api/workflows/streaming.mdx new file mode 100644 index 0000000..12c8c2a --- /dev/null +++ b/content/docs/api/workflows/streaming.mdx @@ -0,0 +1,49 @@ +--- +title: Live updates +icon: radio +description: Show progress while retaining a complete, recoverable conversation. +--- + +Use live events for a responsive interface. Use saved state and items to recover the full result after a disconnection. + +After the [quickstart](/api/first-request), listen to its conversation: + +```bash +curl --no-buffer --fail-with-body --silent --show-error \ + "$FLUSO_API_URL/threads/$thread_id/events" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" +``` + +The response uses Server-Sent Events (SSE). Each `data` field contains a `ThreadEvent` JSON object. A blank line ends the frame. + +## Connect without missing saved work + +1. Open the event stream. +2. Read the thread's saved state and all item pages. +3. Match updates by input, run, and item IDs. +4. Replace temporary text with its saved item when that item arrives. +5. Reconcile saved state periodically while work remains active. + +Repeat this sequence after reconnecting. `Last-Event-ID` does not replay missed events. A closed stream does not establish whether the run finished. + +When submitting an input with `stream: true`, save the initial `input.accepted` receipt before processing progress. It contains the IDs needed for recovery. + +## Render progress accurately + +Tool progress includes `progress.tool_start` and `progress.tool_end`. These describe a tool's execution. They are not instructions for your client to execute that tool. + +A tool preview can omit arguments or results. `args_truncated: true` means the arguments are incomplete. If the flag is absent, completeness is unknown. Use the pending approval's actual arguments when asking a user to approve work. + +For a daemon behind a proxy, disable response buffering for the event stream. + +Ignore unknown event types and keepalive comments. Buffer incomplete frames across network chunks. Use a streaming client that supports an Authorization header. Do not put tokens in the URL. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/threads/{thread_id}/events` | Stream new events. Optional `input_id` follows one input. | + | `GET /v1/threads/{thread_id}/state` | Recover active work, queues, and pending actions. | + | `GET /v1/threads/{thread_id}/items` | Recover saved messages and tool results. Follow all pages. | + + Your workspace's `/v1/examples/watch.mjs` combines these operations. It requires Node 22 or later. Run it with the quickstart's `thread_id` and optional `run_id`. + diff --git a/content/docs/developers/api-ref/usage.mdx b/content/docs/api/workflows/usage.mdx similarity index 72% rename from content/docs/developers/api-ref/usage.mdx rename to content/docs/api/workflows/usage.mdx index ff2591b..6d046b0 100644 --- a/content/docs/developers/api-ref/usage.mdx +++ b/content/docs/api/workflows/usage.mdx @@ -25,6 +25,6 @@ curl --fail-with-body --silent --show-error --get \ --data-urlencode "run_id=$run_id" ``` -Set the API URL, token, and IDs through the [quickstart](/developers/api-ref/authentication#try-a-complete-request) first. Read access is sufficient for usage queries. They do not start workspace compute. +Set the API URL, token, and IDs through the [quickstart](/api/first-request) first. Read access is sufficient for usage queries. They do not start workspace compute. -Use your [workspace's endpoint reference](/developers/api-ref/authentication#choose-your-workspace) for all fields and filters. For daemon inference charges, check the model provider's billing as well. +Use your [workspace's endpoint reference](/api#choose-your-workspace) for all fields and filters. For daemon inference charges, check the model provider's billing as well. diff --git a/content/docs/api/workflows/workspace.mdx b/content/docs/api/workflows/workspace.mdx new file mode 100644 index 0000000..2872402 --- /dev/null +++ b/content/docs/api/workflows/workspace.mdx @@ -0,0 +1,64 @@ +--- +title: Workspace tools +icon: folder-cog +description: Inspect skills, organize labels, and manage runtime readiness. +--- + +These operations support your conversations and Agents. They use the same Cloud account or daemon workspace as your other requests. + +## Choose skills for an Agent + +Read `GET /v1/agent/skills` to find available skill names. For example, inspect a document skill before selecting it for a meeting assistant. + +Built-in skills are already available. For a marketplace skill, read its detail and install it first. Then save its name in the [Agent's configuration](/api/workflows/agents). + +Selecting a missing skill does not install it. Removing a skill does not remove its name from existing Agent configurations. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/agent/skills` | List built-in and workspace skills. | + | `GET /v1/agent/skills/marketplace` | Browse the installable catalog. | + | `GET /v1/agent/skills/marketplace/{skill_id}` | Read catalog and installation metadata. | + | `GET /v1/agent/skills/marketplace/{skill_id}/detail` | Review a skill's instructions before installation. | + | `GET /v1/agent/skills/repo/{skill_id}/detail` | Read a built-in skill's instructions. | + | `GET /v1/agent/skills/workspace/{skill_id}/detail` | Read an installed skill's instructions. | + | `POST /v1/agent/skills/marketplace/{skill_id}/install` | Install the selected catalog skill. | + | `DELETE /v1/agent/skills/marketplace/{skill_id}` | Remove its installed marketplace copy. | + | `DELETE /v1/agent/skills/workspace/{skill_id}` | Remove the named workspace skill directory. | + + Reading a skill document does not execute it. After removal, update Agents that selected the skill or reinstall it. + + +## Organize with labels + +Create a label, then use its returned ID in a project's or thread's `label_ids`. For example, one **Customer work** label can group related conversations and projects. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/agent/labels` | Read the label registry. | + | `POST /v1/agent/labels` | Create with `name` and optional hex `color`. | + | `PATCH /v1/agent/labels` | Send `id` with a changed `name`, `color`, or `order`. | + | `DELETE /v1/agent/labels` | Remove the query's `id`. Keep its conversations and files. | + + +## Check runtime readiness + +Input submission starts the runtime when needed. Your integration usually needs no separate wake loop. + +| Method and path | Use | +| --- | --- | +| `GET /v1/agent/runtime/status` | Read execution and storage readiness. | +| `POST /v1/agent/runtime/wake` | Start if needed, then wait for readiness without sending a chat message. | +| `POST /v1/agent/runtime/stop` | Release compute while keeping saved workspace data. | + +A busy runtime can refuse a stop. Stop active work through its thread or run first. Respect `Retry-After` on temporary readiness errors. + + + `POST /v1/agent/workspace/reset` is destructive. It clears conversations, projects, workspace files, and installed or custom workspace skills. It also retires schedules. + + Saved Agents, their knowledge files, and account settings remain. Home is recreated automatically. Reassign Agents from removed projects before reopening their conversations. Reinstall the workspace skills they need. + + Use individual file, thread, or project deletion for targeted cleanup. + diff --git a/content/docs/deployment-models.mdx b/content/docs/deployment-models.mdx index 7b81954..d011922 100644 --- a/content/docs/deployment-models.mdx +++ b/content/docs/deployment-models.mdx @@ -71,4 +71,4 @@ Traces can include prompt and response text. For a dedicated or on-premises depl - Treat Azure VNet as future availability, not a current option. - Scope on-premises only after Fluso validates the infrastructure, residency, provider, telemetry, and support requirements. -For a custom deployment, [contact Fluso](mailto:sales@premai.io). For the component boundaries behind these models, read [Architecture](/developers/api-ref/architecture). +For a custom deployment, [contact Fluso](mailto:sales@premai.io). For the component boundaries behind these models, read [Architecture](/api/architecture). diff --git a/content/docs/developers/api-ref/agents-and-versions.mdx b/content/docs/developers/api-ref/agents-and-versions.mdx deleted file mode 100644 index c371695..0000000 --- a/content/docs/developers/api-ref/agents-and-versions.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Agents and configuration -sidebarTitle: Agents and configuration -icon: bot -description: Create a reusable Agent, choose its conversation, and update its instructions safely. ---- - -An Agent adds saved instructions and capabilities to a conversation. Start with [API access](/developers/api-ref/authentication) for credentials and your workspace's complete endpoint reference. - -## Create an Agent - -Send `POST /v1/agents` with a saved `Idempotency-Key` and a body such as: - -```json -{ - "name": "Meeting assistant", - "instructions": "Turn my notes into a short meeting brief. List decisions needed and questions still open." -} -``` - -Keep the returned `agent_id`. To use the Agent, send `POST /v1/threads` with that ID and an `input` message. To change its setup through conversation, also set `purpose: "manager"`. - -Opening the same Agent and purpose returns its existing conversation. Two callers share that conversation and its queue. For independent ordinary chats, omit `agent_id`. - -## Read the current configuration - -`GET /v1/agents/{agent_id}` returns the Agent and its `current_config_id`. Keep that ID before editing. - -## Publish an update - -Send only the changed fields to `PATCH /v1/agents/{agent_id}`, together with the ID you read: - -```json -{ - "base_config_id": "CURRENT_CONFIG_ID", - "instructions": "Turn my notes into a short meeting brief. Start with the decisions I need to make." -} -``` - -An old `base_config_id` returns 409 instead of overwriting another edit. Read the latest configuration, review the difference, then retry your intended change. - -The main conversation uses saved changes on its next turn. Existing work sessions retain their configuration binding. `run.execution` records the configuration, provider, model, and reasoning effort used when execution starts. - -## Copies and sharing - -Duplication creates an independent Agent. Keep the same idempotency key when retrying duplication. The retry returns the original copy. - -Public sharing is Cloud-only. A shared Agent snapshot excludes credentials, conversations, and schedules. After importing, inspect `setup_requirements` and complete setup in its manager conversation. The recipient supplies credentials and consent. - -## Delete an Agent - -`DELETE /v1/agents/{agent_id}` deletes the Agent and its owned conversations and revokes its shared Agent link. Save the results you need first. - -For exact fields and sharing operations, use the [workspace API reference](/developers/api-ref/authentication#choose-your-workspace). diff --git a/content/docs/developers/api-ref/authentication.mdx b/content/docs/developers/api-ref/authentication.mdx deleted file mode 100644 index b5ff495..0000000 --- a/content/docs/developers/api-ref/authentication.mdx +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: API access -sidebarTitle: API access -icon: key-round -description: Connect a script to your Cloud or daemon workspace, then send a first request. ---- - -The account API lets an integration use the same conversations, Agents, and files as Fluso. Cloud and the daemon share these operations. You do not need a separate API project or a runtime ID. - -## Choose your workspace - -| Workspace | API base URL | Full guide and endpoint reference | -| --- | --- | --- | -| Fluso Cloud | `https://api.fluso.ai/v1` | **Settings → Developer tokens → API reference** in your app | -| Fluso Dev | `https://api.dev.khichdi.cc/v1` | [Development API reference](https://api.dev.khichdi.cc/docs) | -| Daemon, default local port | `http://127.0.0.1:4111/v1` | Open `http://127.0.0.1:4111/docs` on its host or through your tunnel | -| Daemon, HTTPS | Your workspace URL followed by `/v1` | Your workspace URL followed by `/docs` | - -The reference includes request bodies, response fields, and runnable examples. Its OpenAPI schema is at `/v1/openapi.json`. A daemon lists the endpoints it supports. Cloud account administration and public sharing do not transfer to it. - - - These examples need the current account API. If **Developer tokens** is missing from your app, contact [support](mailto:support@premai.io) to confirm availability for your release. - - -## Get a token - -### Fluso Cloud - -In **Fluso → Settings → Developer tokens**, create a token. Choose **Read only** to inspect existing data, or **Read and write** to submit work and edit it. Use **Read and write** for the quickstart below. Copy the secret when shown. It cannot be retrieved later. - -A write token can also connect services and approve individual tool calls. Managing credentials and permanent tool permissions requires a signed-in app session. - -### Your daemon - -Configure the [daemon](/workspaces) and its model provider first. On its host, run `./fluso-daemon pair` from the installed package folder. Exchange the one-use code at the daemon's address: - -```bash -curl --fail-with-body --silent --show-error \ - 'http://127.0.0.1:4111/daemon/v1/pair' \ - -H 'Content-Type: application/json' \ - --data-raw '{"code":"PAIRING_CODE","name":"My integration"}' -``` - -Replace `PAIRING_CODE` before running. Save the returned `token` and client `id` privately. Codes expire after ten minutes. A device token grants read/write access to the owner's workspace, including tool approvals. - -Use `pair --manage-host` only when the client also needs provider settings, host-file access, or permission to invite devices. Remote connections require [HTTPS or an SSH tunnel](/workspaces/setup#connect-from-another-mac). - -## Try a complete request - -Open Bash in a new example directory. You need cURL 7.87 or later, `jq`, and `uuidgen` for the API examples. Check your version with `curl --version`. The example below uses a daemon. For Cloud, use your environment's URL from the table above. Set the token you saved: - -```bash -export FLUSO_API_URL='http://127.0.0.1:4111/v1' -export FLUSO_API_TOKEN='YOUR_TOKEN' -``` - -Keep real tokens out of source control, shared terminal recordings, and URLs. Use a secret store for deployed integrations. - -Download the example from that workspace. Read it before running: - -```bash -curl --fail --silent --show-error \ - "$FLUSO_API_URL/examples/quickstart.sh" -o quickstart.sh -cat quickstart.sh -``` - -Then run it in the same Bash shell: - -```bash -source quickstart.sh -``` - -The script creates a conversation, requests a short answer, waits for the run, and reads every result page. It keeps `thread_id`, `run_id`, `input_key`, and `input_body` for follow-ups. - -A timeout does not cancel accepted work. Do not rerun the quickstart to recover it: that creates a new request. Read the [saved run and results](/developers/api-ref/runs) instead. - -## Keep access under control - -Cloud developer tokens expire after 90 days by default. To rotate one, create a replacement in Settings, update your integration, verify it, then revoke the old token. Revocation can take up to 30 seconds to reach cached authentication and open streams. - -Daemon device tokens expire after 30 idle days. Authenticated use renews them. Revoke a device with `DELETE /daemon/v1/clients/{clientId}`. A device can revoke itself. Host-management access also permits revoking other devices. - -Native applications can use the Cloud browser sign-in flow instead of developer tokens. Follow **auth** in your app's **API reference**, including its code-verifier and refresh-token requirements. Do not reuse an old refresh token after rotation. - -Next: [conversations and results](/developers/api-ref/threads-and-messages). diff --git a/content/docs/developers/api-ref/files-and-projects.mdx b/content/docs/developers/api-ref/files-and-projects.mdx deleted file mode 100644 index 9f2b687..0000000 --- a/content/docs/developers/api-ref/files-and-projects.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: Files and projects -sidebarTitle: Files and projects -icon: folder-kanban -description: Upload inputs, read saved output, and protect files from conflicting edits. ---- - -Files and projects belong to the authenticated workspace. You do not need a runtime ID. Use the paths returned by the API, including saved attachments. - -## Projects - -Use `/v1/projects` to list or create projects. Read, update, or delete a project at `/v1/projects/{project_id}`. Project IDs are also storage names. Keep the exact returned value when building paths. - -Deleting a project deletes its conversations and files, then retires their schedules. Save needed results before deletion. See your [workspace's endpoint reference](/developers/api-ref/authentication#choose-your-workspace) for export and deletion behavior. - -## Read a text file - -After [setting your API URL and token](/developers/api-ref/authentication), run: - -```bash -curl --fail-with-body --silent --show-error --get \ - "$FLUSO_API_URL/files/read" \ - -H "Authorization: Bearer $FLUSO_API_TOKEN" \ - --data-urlencode 'path=/projects/Home/files/notes.md' -``` - -Use `GET /v1/files` to browse and `GET /v1/files/download` for file bytes. A text read includes a `revision` for safe replacement. - -## Upload a file - -1. Create an upload with `POST /v1/uploads`, its filename, MIME type, size, and a saved `Idempotency-Key`. -2. Send the bytes to `upload_url` in a multipart POST. Include all returned `upload_fields` unchanged, with the file last. -3. Confirm with `POST /v1/uploads/confirm` and `{"id":"UPLOAD_ID"}`. -4. Read `GET /v1/uploads/{upload_id}` until it finishes. -5. Attach its returned `target_path` to a conversation input. - -Do not send your Fluso bearer token to `upload_url`. - -A `confirmed` upload is ready to attach. `history_unavailable` also means the file was saved, but its new version was not recorded. Show that warning. For `failed`, `cancelled`, or `conflict`, inspect the saved error before starting another transfer. - -If a request times out, read the existing upload. Starting another upload can duplicate work. Confirmed receipts do not guarantee that a file remains unchanged after later edits. - -The full reference includes runnable **upload**, **attach**, and **download** examples. It also covers expired transfer credentials and recovery without another transfer. - -## Edit without losing another change - -Use `PUT /v1/files/text` with `path`, `content`, and the latest `revision` to replace text. Use `overwrite: false` when creating a new file that must not replace an existing path. - -If another writer changed the file, read the new content and merge before retrying. Do not drop the revision check to force an overwrite. - -Text writes allow 1 MiB of UTF-8 content. Text reads default to 5 MiB. Use uploads and downloads for larger or binary files. - - - Versions and comments apply to ordinary files under `/files/` and `/projects/{project_id}/files/`, excluding hidden directories. - - Replacement refuses to lose existing history unless you explicitly permit it with `allow_history_loss: true`. Treat that option as a data-loss decision. For uploads, changing a failed request requires a new key. Retrying the old key returns its saved failure. - - A directory copy can partly succeed. Inspect the destination before retrying it. An attachment with `available: false` records historical output whose file is no longer at that path. - diff --git a/content/docs/developers/api-ref/meta.json b/content/docs/developers/api-ref/meta.json deleted file mode 100644 index cb5d332..0000000 --- a/content/docs/developers/api-ref/meta.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "title": "For developers", - "icon": "code-xml", - "defaultOpen": false, - "pages": [ - "authentication", - "threads-and-messages", - "agents-and-versions", - "files-and-projects", - "schedules", - "runs", - "sharing", - "usage", - "architecture" - ] -} diff --git a/content/docs/developers/api-ref/runs.mdx b/content/docs/developers/api-ref/runs.mdx deleted file mode 100644 index b3b05f8..0000000 --- a/content/docs/developers/api-ref/runs.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Runs and recovery -sidebarTitle: Runs and recovery -icon: activity -description: Follow accepted work, recover after a disconnection, and stop a run deliberately. ---- - -Submitting an input returns a receipt with `thread_id`, `input_id`, and `run_id`. Save all three. **202 means accepted, not completed.** - -Read `GET /v1/threads/{thread_id}/runs/{run_id}` to check progress. Read `GET /v1/threads/{thread_id}/state` for queued work and pending actions. - -## Understand the status - -| Status | What to do | -| --- | --- | -| `queued` or `running` | Wait, then read again. | -| `waiting_action` | Read the pending approval or connection request in thread state. | -| `held` | Inspect the state and queued inputs before resuming. | -| `completed` | Read all saved result pages and check the answer. | -| `failed` or `cancelled` | Read the error and any saved results before submitting more work. | - -A completed run can contain a failed tool call. A failed run can still have changed an external service. Check the results against the requested task. - -## Recover after a timeout - -- If the submission response was lost, retry the same body with the same `Idempotency-Key`. -- If you received a receipt, read that run and its [saved items](/developers/api-ref/threads-and-messages#read-message-history). -- If a stream closes, reconnect for new events and read saved state for missed results. - -A client timeout does not cancel work. Creating a new key can start another execution. - -Input retry receipts remain until 24 hours after completion. After that, inspect the saved run before deciding whether to submit new work. Runs remain readable until their conversation is deleted. - - - An error from `POST /v1/threads` can include `error.details.thread_id`. Save that ID and inspect the thread state. Its presence does not prove input acceptance. - - After resolving the cause, send the original input and key to that thread's `/inputs` endpoint. This can recover an uncertain acceptance within the retry window. - - -## Stop work - -Use `POST /v1/threads/{thread_id}/runs/{run_id}/cancel` for one run. Use `POST /v1/threads/{thread_id}/stop` for the conversation. - -Read the state until execution stops. A cancellation acknowledgement does not prove that a tool has stopped, and cancellation cannot undo a completed external action. - -Use the **Recovery and cancellation** examples in your [workspace's full reference](/developers/api-ref/authentication#choose-your-workspace) for retries, held inputs, and cancellation. diff --git a/content/docs/developers/api-ref/sharing.mdx b/content/docs/developers/api-ref/sharing.mdx deleted file mode 100644 index 47c6309..0000000 --- a/content/docs/developers/api-ref/sharing.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Sharing -sidebarTitle: Sharing -icon: share-2 -description: Publish a revocable Agent snapshot and import an independent copy. ---- - -Public sharing is Cloud-only. Sharing an Agent stores a public snapshot. It does not expose a live view of the owner's workspace. Owner routes require [authentication](/developers/api-ref/authentication). A public snapshot read does not. - -## Publish and revoke - -| Method and path | Behavior | -| --- | --- | -| `GET /v1/agents/{agent_id}/share-options` | Available custom skills, knowledge, and custom connections | -| `GET /v1/agents/{agent_id}/share` | Current link record or `null` | -| `POST /v1/agents/{agent_id}/share` | Publish a snapshot | -| `POST /v1/agent-shares/{share_id}/refresh` | Replace the snapshot at the same link | -| `DELETE /v1/agent-shares/{share_id}` | Revoke the link | - -Publishing and refreshing accept a selection: - -```json -{ - "skills": [], - "knowledge_files": [], - "connectors": [] -} -``` - -Use IDs from share options for skills, knowledge names for `knowledge_files`, and server IDs for `connectors`. Instructions and portable settings are included even with empty selections. Selected skill folders and knowledge contents share limits of 20 MiB and 512 regular files. Symbolic links are rejected. Custom connection URLs with query parameters are excluded from share options. - -Review all selected contents and connection URLs before publishing. Stored authentication, conversations, routines, and project files are not bundled. See [Sharing and copies](/developers/guides/sharing) for the recipient experience. - -## Preview and import - -| Method and path | Behavior | -| --- | --- | -| `GET /v1/agent-templates` | Fluso starting-point snapshots | -| `GET /v1/shared/agents/{share_id}` | Public setup, resource manifest, required filenames, and `published_at` | -| `POST /v1/shared/agents/{share_id}/import` | Authenticated import into the recipient's workspace | - -```json -{ - "name": "My release reviewer", - "project_id": "Home", - "published_at": "" -} -``` - -Generate and save a new `Idempotency-Key` for each import. Reuse it and the unchanged body to recover an uncertain import without creating another copy. A changed snapshot returns a conflict rather than silently importing something different from the preview. - -The recipient gets independent setup and selected resource copies. Imported custom skills receive distinct names. App credentials are never inherited. Read the imported Agent's `setup_requirements`, then complete missing connections and consent in its manager conversation. - -A revoked link becomes unavailable. Unsupported older snapshots may require republication by their owner. Revocation does not delete copies already imported. diff --git a/content/docs/developers/api-ref/threads-and-messages.mdx b/content/docs/developers/api-ref/threads-and-messages.mdx deleted file mode 100644 index 89a4d3c..0000000 --- a/content/docs/developers/api-ref/threads-and-messages.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Conversations and results -sidebarTitle: Conversations and results -icon: messages-square -description: Submit work, continue a conversation, and read the complete saved answer. ---- - -A **thread** is a conversation. An **input** is a message you submit. A **run** tracks the resulting work. - -Start with the runnable [API quickstart](/developers/api-ref/authentication#try-a-complete-request). These are the operations behind it. - -## Create a conversation - -Send `POST /v1/threads` with an `Idempotency-Key` and an input body: - -```json -{ - "input": "Explain three ways to check whether a source is reliable." -} -``` - -Save the key and body before sending. The receipt returns `thread_id` and `run_id`. Use those values rather than deriving one from the other. - -To open a conversation without starting work, omit `input` and `attachments`. To use a saved Agent, include `agent_id`. Its main conversation is reused. `purpose: "manager"` opens its setup conversation. - -## Send a message - -Send `POST /v1/threads/{thread_id}/inputs` with the next `input` and a new key. If the conversation is busy, use `mode: "queue"` to wait or `mode: "steer"` to guide active work. - -A retry must keep the original key and unchanged body. Reusing a key with different content returns 409. A new key means new work. - -The **follow-up** example in your [workspace's reference](/developers/api-ref/authentication#choose-your-workspace) continues the quickstart conversation in the same shell. - -## Read message history - -Read `GET /v1/threads/{thread_id}/items?run_id={run_id}` for messages, tool results, and attachments belonging to a run. Omit the filter to read the conversation. - -Entries are in `data`. Read every page: pass `next_cursor` as `after` with the unchanged `revision`, until `next_cursor` is null. Keep filters unchanged between pages. - -If a snapshot edit or deletion causes a pagination conflict, discard the partial read and restart without `after` or `revision`. New messages alone do not invalidate the snapshot. - -Read the saved [run status](/developers/api-ref/runs) as well as the messages. An empty page does not prove the run finished or failed. - -## Follow live progress - -`GET /v1/threads/{thread_id}/events` streams Server-Sent Events. Submitting with `stream: true` starts with an `input.accepted` receipt. Save it before processing later events. - -Streams do not replay missed events. After reconnecting, read saved state and items. Ignore unknown event types and keepalive comments. Replace temporary text with its saved item to avoid showing it twice. - -Use the reference's **watch.mjs** example for a client that combines live updates, result pages, and recovery. It requires Node 22 or later. - -## Delete a conversation - -`DELETE /v1/threads/{thread_id}` deletes an ordinary conversation and stops its execution. Save needed results first. Agent main and manager conversations require deleting their Agent instead. - -Conversation deletion does not remove a separately published chat snapshot or all workspace files. diff --git a/content/docs/developers/meta.json b/content/docs/developers/meta.json index fb75c1d..550ecd7 100644 --- a/content/docs/developers/meta.json +++ b/content/docs/developers/meta.json @@ -6,7 +6,6 @@ "improve", "schedules", "examples", - "guides", - "api-ref" + "guides" ] } diff --git a/content/docs/features/open-architecture.mdx b/content/docs/features/open-architecture.mdx index c930355..76637dd 100644 --- a/content/docs/features/open-architecture.mdx +++ b/content/docs/features/open-architecture.mdx @@ -22,7 +22,7 @@ Different exports serve different purposes. A project ZIP is a file backup; a sh | **Audit logs** | In Fluso Admin, filter **Audit log**, then select **Export _N_ rows** | CSV containing the currently filtered rows from the latest 100 recorded identity and access events | | **Usage records** | In Fluso Admin, open **Observability → Export CSV** | CSV of the recorded daily gateway cost and token totals shown in the 14-day view | -Project ZIPs skip dotted runtime entries and symbolic links. They do not serve as a complete conversation archive. Use the [Threads and messages API](/developers/api-ref/threads-and-messages#read-message-history) when you need persisted chat records in a system you control. +Project ZIPs skip dotted runtime entries and symbolic links. They do not serve as a complete conversation archive. Use the [Threads and messages API](/api/workflows/conversations#read-message-history) when you need persisted chat records in a system you control. ## Skills stay readable @@ -52,4 +52,4 @@ These exports are independent snapshots. Later Fluso changes do not silently rew ## APIs for continuous access -Use [API access](/developers/api-ref/authentication) when a one-time download is not enough. The account API covers Agents, conversations, files, schedules, runs, and usage on Cloud and [daemon workspaces](/workspaces). Authenticate with a Cloud developer token or a paired daemon token. Store only the data your integration needs. +Use [API access](/api/first-request) when a one-time download is not enough. The account API covers Agents, conversations, files, schedules, runs, and usage on Cloud and [daemon workspaces](/workspaces). Authenticate with a Cloud developer token or a paired daemon token. Store only the data your integration needs. diff --git a/content/docs/features/projects.mdx b/content/docs/features/projects.mdx index ab9e140..b685c95 100644 --- a/content/docs/features/projects.mdx +++ b/content/docs/features/projects.mdx @@ -41,7 +41,7 @@ Files created in project chats stay in the same workspace. That makes the projec On **Projects**, open the project's **•••** menu and select **Export as zip**. You can also open the project and select **Export**. -The ZIP contains the project's regular files, their folder paths, `context.md`, and project metadata. It omits empty folders, dotted runtime entries, and symbolic links. Conversation history is separate; use **Share chat** for a reviewable snapshot or the [Threads API](/developers/api-ref/threads-and-messages#read-message-history) for persisted messages. +The ZIP contains the project's regular files, their folder paths, `context.md`, and project metadata. It omits empty folders, dotted runtime entries, and symbolic links. Conversation history is separate; use **Share chat** for a reviewable snapshot or the [Threads API](/api/workflows/conversations#read-message-history) for persisted messages. ## Keep context in the right place @@ -66,4 +66,4 @@ If you already have useful context in ChatGPT, Claude, or another agent, use [Im ## Next -Read [Memory](/features/memory) for the context carried between chats, or [Files and projects](/developers/api-ref/files-and-projects) for the API. +Read [Memory](/features/memory) for the context carried between chats, or [Files and projects](/api/workflows/files) for the API. diff --git a/content/docs/index.mdx b/content/docs/index.mdx index 5a95a8a..abc44c4 100644 --- a/content/docs/index.mdx +++ b/content/docs/index.mdx @@ -60,8 +60,8 @@ Pick a workflow that matches a problem you have right now. Each one is a story, Create an Agent, refine its setup, and return to its conversations, routines, and Inbox. - - Request flow, durable data boundaries, and replaceable components. + + Connect a script to Cloud or Fluso Local, submit work, and read the result. Plans, limits, billing. diff --git a/content/docs/meta.json b/content/docs/meta.json index 5319754..3d27ac8 100644 --- a/content/docs/meta.json +++ b/content/docs/meta.json @@ -12,6 +12,8 @@ "[server-cog][Overview](/deployment-models)", "---Features---", "...features", + "---Fluso API---", + "...api", "---Fluso Local---", "...workspaces", "---Get started---", diff --git a/content/docs/workspaces/index.mdx b/content/docs/workspaces/index.mdx index 8aa99bd..26fe754 100644 --- a/content/docs/workspaces/index.mdx +++ b/content/docs/workspaces/index.mdx @@ -33,7 +33,7 @@ You install a background program called the **daemon** on the machine that will [Contact us](mailto:support@premai.io?subject=Fluso%20Local%20download) for the daemon download links. Include your operating system, processor type, and whether you use Fluso or Fluso Dev. -The [setup guide](/workspaces/setup) checks your machine before installation. You do not need Docker or a source checkout. For use without the desktop app, follow [API access](/developers/api-ref/authentication) after setup. +The [setup guide](/workspaces/setup) checks your machine before installation. You do not need Docker or a source checkout. For use without the desktop app, follow [API access](/api/first-request) after setup. ## Where your work stays diff --git a/public/_redirects b/public/_redirects index 538f7ab..f4c401b 100644 --- a/public/_redirects +++ b/public/_redirects @@ -37,12 +37,39 @@ /developers/export-import-and-share-agents /developers/guides/sharing 301 /developers/export-import-and-share-agents/ /developers/guides/sharing 301 /llms.mdx/docs/developers/export-import-and-share-agents/content.md /llms.mdx/docs/developers/guides/sharing/content.md 301 -/developers/architecture /developers/api-ref/architecture 301 -/developers/architecture/ /developers/api-ref/architecture 301 -/llms.mdx/docs/developers/architecture/content.md /llms.mdx/docs/developers/api-ref/architecture/content.md 301 +/developers/architecture /api/architecture 301 +/developers/architecture/ /api/architecture 301 +/llms.mdx/docs/developers/architecture/content.md /llms.mdx/docs/api/architecture/content.md 301 /workspaces/macos /workspaces/setup 301 /workspaces/macos/ /workspaces/setup 301 /workspaces/linux /workspaces/setup 301 /workspaces/linux/ /workspaces/setup 301 /llms.mdx/docs/workspaces/macos/content.md /llms.mdx/docs/workspaces/setup/content.md 301 /llms.mdx/docs/workspaces/linux/content.md /llms.mdx/docs/workspaces/setup/content.md 301 +/developers/api-ref/authentication /api/first-request 301 +/developers/api-ref/authentication/ /api/first-request 301 +/llms.mdx/docs/developers/api-ref/authentication/content.md /llms.mdx/docs/api/first-request/content.md 301 +/developers/api-ref/threads-and-messages /api/workflows/conversations 301 +/developers/api-ref/threads-and-messages/ /api/workflows/conversations 301 +/llms.mdx/docs/developers/api-ref/threads-and-messages/content.md /llms.mdx/docs/api/workflows/conversations/content.md 301 +/developers/api-ref/runs /api/workflows/runs 301 +/developers/api-ref/runs/ /api/workflows/runs 301 +/llms.mdx/docs/developers/api-ref/runs/content.md /llms.mdx/docs/api/workflows/runs/content.md 301 +/developers/api-ref/agents-and-versions /api/workflows/agents 301 +/developers/api-ref/agents-and-versions/ /api/workflows/agents 301 +/llms.mdx/docs/developers/api-ref/agents-and-versions/content.md /llms.mdx/docs/api/workflows/agents/content.md 301 +/developers/api-ref/files-and-projects /api/workflows/files 301 +/developers/api-ref/files-and-projects/ /api/workflows/files 301 +/llms.mdx/docs/developers/api-ref/files-and-projects/content.md /llms.mdx/docs/api/workflows/files/content.md 301 +/developers/api-ref/schedules /api/workflows/schedules 301 +/developers/api-ref/schedules/ /api/workflows/schedules 301 +/llms.mdx/docs/developers/api-ref/schedules/content.md /llms.mdx/docs/api/workflows/schedules/content.md 301 +/developers/api-ref/usage /api/workflows/usage 301 +/developers/api-ref/usage/ /api/workflows/usage 301 +/llms.mdx/docs/developers/api-ref/usage/content.md /llms.mdx/docs/api/workflows/usage/content.md 301 +/developers/api-ref/sharing /api/cloud/sharing 301 +/developers/api-ref/sharing/ /api/cloud/sharing 301 +/llms.mdx/docs/developers/api-ref/sharing/content.md /llms.mdx/docs/api/cloud/sharing/content.md 301 +/developers/api-ref/architecture /api/architecture 301 +/developers/api-ref/architecture/ /api/architecture 301 +/llms.mdx/docs/developers/api-ref/architecture/content.md /llms.mdx/docs/api/architecture/content.md 301 diff --git a/public/images/api/developer-token-form.png b/public/images/api/developer-token-form.png new file mode 100644 index 0000000000000000000000000000000000000000..832d687d42ed1c7ff7b96565463f807dff7fa110 GIT binary patch literal 71289 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
-

- Scroll horizontally to view the full diagram. -

-
- - - Fluso request and data boundaries - - - A Fluso client sends an authenticated request through the edge, REST - API, Agent gateway, and a user runtime. The API owns platform - records. The gateway and runtime use durable workspace storage. The - runtime queries knowledge and calls governed MCP servers. - - - - - - - - - - Request path - - - - - - - - - - - - - {architectureServices.map((node) => ( - - ))} - - - - Data and context - - - - - - {architectureData.map(({ label, detail, owner, x, icon: Icon }) => ( - - - - {label} - - - {detail} - - - {owner} - - - ))} - - - - - Runtime context - - - - - Knowledge service - - - - - - MCP servers - - - - -
- -
- Compute, platform state, workspace data, knowledge, and external sources - meet through explicit service boundaries. -
-
- ); -} diff --git a/content/docs/api/architecture.mdx b/content/docs/api/architecture.mdx deleted file mode 100644 index 3d7ffa6..0000000 --- a/content/docs/api/architecture.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Architecture -sidebarTitle: Architecture -icon: network -description: Follow a message through the platform and understand what survives a runtime restart. ---- - -Fluso separates the client, platform records, runtime compute, and durable user files. This explains why you can leave a chat, return to queued work, and reuse an Agent without rebuilding its setup. - - - -## From a message to a result - -1. The client sends an authenticated message with its conversation and request IDs. -2. The backend checks identity and usage, then forwards work through the gateway to the user's runtime. -3. The runtime admits the request into the conversation's durable queue. A session worker runs the turn with its accepted Agent setup. -4. Tools read or change workspace files and call connected apps through MCP, subject to access and approval rules. -5. Progress streams to the client. History and request state let it recover after a connection drops. - -A main Agent conversation and its Manager have separate histories. The Manager edits one Agent's setup. The main conversation performs its work and coordinates delegated sessions. - -## What is stored where - -| Boundary | Durable state | -| --- | --- | -| Platform database | User and access records, app connections, routine schedules, public share records | -| User storage | Agent definitions, knowledge files, projects, skills, conversations, request ledgers, and queues | -| Project working memory | Bounded summaries and evidence-backed observations within that project | -| External apps | Their own data and actions. Selected results may be copied into Fluso by the requested work | - -Runtime compute can be suspended or replaced without deliberately deleting those stored records. Current deployment paths include local Docker, AWS ECS, and MicroVM runtimes with ZeroFS storage. Availability depends on the deployment. - -## Configuration and context are different - -A saved Agent edit creates a new configuration. The main conversation uses it on its next turn. Active turns and existing delegated sessions keep the setup they accepted. Skill files and project files remain live shared resources. - -Separate conversations do not imply separate private filesystems. Agents within one user's workspace can use its shared files. Project context and knowledge selection guide the model. They are not a filesystem security boundary. - -## Routine and result delivery - -The scheduler sends a due routine through normal request admission using a stable ID for that occurrence. A busy conversation queues it. The scheduler avoids another overlapping occurrence of the same routine, retries temporary delivery failures, and records problems for Inbox. - -Delegated sessions return requested results to their parent. The main conversation must still reconcile those results with the user's latest direction and actual files. A returned report does not grant approval for a new action. - -## Recovery is not rollback - -A dropped stream does not prove a run failed. The client reconciles with saved history and status. Runtime recovery can resume interrupted work, but an external action that already succeeded is not automatically undone. - -Use conversation history, routine state, and [run status](/api/workflows/runs) to distinguish queued work, approval waits, failures, and completed output. Avoid resubmitting an uncertain operation under a new ID without checking it. - -A [daemon workspace](/workspaces) runs the gateway and runtime on one owner's host. It stores its own account state and files. The desktop connects to it through authenticated native transport. Switching to Cloud does not move that data. - -See [API access](/api/first-request) for developer tokens, daemon pairing, and the account API reference. diff --git a/content/docs/api/cloud/client-sign-in.mdx b/content/docs/api/cloud/client-sign-in.mdx index d609a9e..30950c2 100644 --- a/content/docs/api/cloud/client-sign-in.mdx +++ b/content/docs/api/cloud/client-sign-in.mdx @@ -1,49 +1,120 @@ --- title: Client sign-in icon: log-in -description: Understand browser sign-in, access tokens, and refresh for Cloud clients. +description: Let users sign in to a configured Cloud client and keep their session active. --- -Use a [developer token](/api/cloud/tokens) for your own script. Native clients can use a browser sign-in flow and a renewable client session. +For your own script or service, use a [developer token](/api/cloud/tokens). It needs no browser callback or refresh flow. -The endpoints below support Fluso's client integrations. They are not an open redirect-registration service. The current browser callback supports configured Fluso handoffs, including Telegram. Contact [support](mailto:support@premai.io) before building a new client sign-in handoff. +Use client sign-in when each person must connect their own Fluso account to a supported client. Fluso handles sign-in in the browser. Your client receives a one-use code and exchanges it for tokens. -## Exchange browser sign-in for a session + + A new client needs a configured Fluso callback. [Contact support](mailto:support@premai.io) before implementing that integration. These endpoints do not register arbitrary callback URLs. + -1. Generate a random PKCE `code_verifier` with 43 to 128 allowed characters. -2. Compute its SHA-256 digest as unpadded base64url for `code_challenge`. -3. Start sign-in with `client`, `client_user_id`, and that challenge. -4. Open the returned sign-in URL in the user's browser. -5. Complete the browser step under the user's Clerk app session. -6. Exchange the returned code with the original verifier and client identifiers. +## Sign in once -The verifier can contain ASCII letters, digits, and `-._~`. PKCE binds the returned code to the client that started sign-in. Keep the verifier private until the exchange finishes. + + + Your client requests a sign-in URL. Open it in the user's browser. + + + Fluso handles sign-in and returns a one-use code through your configured callback. + + + Your client sends the code with its private proof. Fluso returns tokens for API access and session renewal. + + -The state expires after ten minutes. The returned code expires after five minutes and works once. If the exchange response is lost, restart sign-in. +The proof, called **PKCE**, keeps another client from using the returned code. Generate it once for each sign-in attempt. -The exchange returns `auth.access_token`, `auth.refresh_token`, `auth.expires_in`, `auth.session_id`, and the user. Use the access token in the `Authorization: Bearer` header. Store the refresh token privately. +Use `auth.access_token` as `Authorization: Bearer TOKEN` for API requests. Save `auth.refresh_token` privately for renewal. `auth.expires_in` tells you when the access token expires. -## Refresh without ending the session + + Use Node 22 or later. Set `baseUrl` to your Cloud API URL, including `/v1`. Use your configured `client` name. Set `clientUserId` to the user's stable ID within your client. -Send the current `refresh_token` to `POST /v1/auth/client/refresh`. Save both replacement tokens together. Allow only one refresh at a time per client session. + Generate the proof and request the browser URL: -The previous refresh token can recover the same token pair for 30 seconds. Reuse after that window revokes the session. Refresh extends the session's 30-day expiry. After a rejected refresh, sign in again. + ```javascript + import { randomBytes, createHash } from 'node:crypto'; -To sign out, send the refresh token to `/v1/auth/client/revoke`. Then remove the local credentials. + const baseUrl = 'https://api.fluso.ai/v1'; + const client = 'YOUR_CONFIGURED_CLIENT'; + const clientUserId = 'YOUR_CLIENT_USER_ID'; - + const verifier = randomBytes(32).toString('base64url'); + const challenge = createHash('sha256').update(verifier).digest('base64url'); + const start = await fetch(`${baseUrl}/auth/client/start`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + client, client_user_id: clientUserId, code_challenge: challenge, + }), + }); + if (!start.ok) throw new Error(`Sign-in could not start (${start.status})`); + const { url } = await start.json(); + ``` + + Open `url` in the user's browser. Keep `verifier` on the initiating client. Do not log it or send it to the callback URL. + + After the configured callback returns `code`, exchange it using the same client identity and verifier: + + ```javascript + const exchange = await fetch(`${baseUrl}/auth/client/exchange`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + client, client_user_id: clientUserId, code, code_verifier: verifier, + }), + }); + if (!exchange.ok) throw new Error(`Sign-in exchange failed (${exchange.status})`); + const { auth } = await exchange.json(); + ``` + + Store `auth` in the client's credential store. It includes `access_token`, `refresh_token`, `expires_in`, `session_id`, and `user`. + + The browser step calls `POST /v1/auth/client/complete` with the returned state under the user's Clerk app session. Current browser callbacks support configured Fluso handoffs, including Telegram. A developer token cannot perform that browser step. + + Sign-in state expires after ten minutes. The returned code expires after five minutes and works once. If the exchange response is lost, start sign-in again. + + For another language, generate a verifier with 43 to 128 ASCII letters, digits, or `-._~`. Its challenge is SHA-256 encoded as unpadded base64url. + + +## Keep the session active + +Before the access token expires, send the saved refresh token to `POST /v1/auth/client/refresh`: + +```json +{ + "refresh_token": "YOUR_SAVED_REFRESH_TOKEN" +} +``` + +The response contains replacement `access_token` and `refresh_token` values. Save both together before sending further requests. Allow only one refresh at a time for that session. + + + Retry the same refresh token within 30 seconds to recover the same replacement pair. Reusing it after that window revokes the session. + + Refresh extends the session's 30-day expiry. If refresh is rejected, ask the user to sign in again. Do not create a new session for each API request. + + +## Sign out + +Send the refresh-token body above to `POST /v1/auth/client/revoke`. Remove the local credentials after revocation. + + | Method and path | Required credential and use | | --- | --- | - | `POST /v1/auth/client/start` | PKCE challenge. Begin the browser handoff. | - | `POST /v1/auth/client/complete` | Clerk session and returned state. Issue the one-use code. | - | `POST /v1/auth/client/exchange` | Code, client identifiers, and original verifier. Issue tokens. | - | `POST /v1/auth/client/sessions` | Clerk session. Create a client session for an already signed-in app. | - | `GET /v1/auth/client/sessions` | Clerk session. List up to 100 active sessions, newest first. | + | `POST /v1/auth/client/start` | PKCE challenge. Request the browser URL. | + | `POST /v1/auth/client/complete` | Clerk app session and returned state. Issue the one-use code. | + | `POST /v1/auth/client/exchange` | Code, client identity, and original verifier. Issue tokens. | + | `POST /v1/auth/client/sessions` | Clerk app session. Create a client session for an already signed-in app. | + | `GET /v1/auth/client/sessions` | Clerk app session. List up to 100 active sessions, newest first. | | `DELETE /v1/auth/client/sessions/{session_id}` | Clerk session for any owned session, or a client token for itself. | - | `POST /v1/auth/client/refresh` | Current refresh token. Rotate the token pair. | + | `POST /v1/auth/client/refresh` | Refresh token. Rotate the token pair. | | `POST /v1/auth/client/revoke` | Refresh token. Revoke its session. Returns 204 even if already invalid. | - A client token does not grant account administration. In particular, developer-token management requires Clerk sign-in. + Client tokens cannot manage developer tokens. That operation requires Clerk sign-in. - Use your [deployment's schema](/api#find-the-exact-fields) for identifier constraints and response fields. Do not mint a new session for each API request. + Your [deployment's schema](/api#find-the-exact-fields) lists exact identifier constraints and response fields. diff --git a/content/docs/api/first-request.mdx b/content/docs/api/first-request.mdx index f1933ac..442489d 100644 --- a/content/docs/api/first-request.mdx +++ b/content/docs/api/first-request.mdx @@ -1,26 +1,28 @@ --- title: Your first request icon: terminal -description: Send a short task and read the complete answer from Cloud or your daemon. +description: Send a message, get an answer, and keep the conversation open for follow-ups. --- -This example creates a conversation, sends one message, and waits for its answer. It works with either workspace. +This example asks Fluso to explain three ways to check a source. You get a complete answer and a conversation you can continue. ## 1. Get access -Create a Cloud [developer token](/api/cloud/tokens) with **Read and write** access. For Fluso Local, get a [paired device token](/api/daemon/pairing) after configuring your model provider. +For Cloud, create a [developer token](/api/cloud/tokens) with **Read and write** access. For Fluso Local, [pair a client](/api/daemon/pairing) after configuring its model provider. -Open the **API reference** for your workspace to confirm API availability in that release. +Open your workspace's [API reference](/api#find-the-exact-fields) to check availability in that release. -Open Bash in a new example directory. The examples need cURL 7.87 or later, `jq`, and `uuidgen`. +Start a Bash shell in a new, empty directory. You need cURL 7.87 or later, `jq`, and `uuidgen`. -Set your [workspace's base URL](/api#choose-your-workspace): +Set the URL for your workspace: ```bash export FLUSO_API_URL='https://api.fluso.ai/v1' ``` -Read the token without displaying it or placing it in your shell history: +For Fluso Dev, use `https://api.dev.khichdi.cc/v1`. For a local daemon, use `http://127.0.0.1:4111/v1`. The token must belong to that workspace. + +Read your token without showing it or saving it in shell history: ```bash read -r -s -p 'Fluso token: ' FLUSO_API_TOKEN @@ -28,53 +30,65 @@ echo export FLUSO_API_TOKEN ``` -Use a secret store in a deployed application. Keep tokens out of URLs, source control, logs, and screenshots. - -## 2. Send a message and read the answer +## 2. Get an answer -Download the example from your workspace: +Download the example from that workspace: ```bash curl --fail --silent --show-error \ - "$FLUSO_API_URL/examples/quickstart.sh" -o quickstart.sh + "$FLUSO_API_URL/examples/quickstart.sh" -o quickstart.sh && cat quickstart.sh ``` -Read the script before running it. Then run it in the same Bash shell: +Read the script, then run it in the same Bash shell: ```bash source quickstart.sh ``` -The script submits a short request, waits for its run, and reads every result page. It retains the IDs for your next message. +It prints Fluso's answer after reading every result page. If work needs attention, it prints the run's status instead. Stop on an error and use [recovery](/api/workflows/runs) before submitting again. -Here is the request pattern behind it: +Keep this shell open. The other guides use the values and functions it provides: -| Step | Request | Keep from the response | -| --- | --- | --- | -| Send work | `POST /v1/threads` with `input` and an `Idempotency-Key` | `thread_id`, `input_id`, `run_id` | -| Check progress | `GET /v1/threads/{thread_id}/runs/{run_id}` | Status and any error | -| Read the answer | `GET /v1/threads/{thread_id}/items?run_id={run_id}` | All result pages, including attachments | +| Name | What it gives you | +| --- | --- | +| `thread_id` | The conversation to continue | +| `run_id` | The work started by your message | +| `input_key`, `input_body` | The exact request to reuse after a lost response | +| `api` | An authenticated request to your chosen workspace | +| `wait_for_run`, `read_run_items` | Wait for completion, then read all saved output | -An **idempotency key** identifies one submitted message. Save it and the request body before sending. Reuse both if that request's response is lost. - -**202 means Fluso accepted the work.** Read the run status to learn whether it completed or needs your action. +For example, `api "/threads/$thread_id/state"` reads the conversation's state. `api` adds the base URL and bearer token. It uses ordinary cURL options. A timeout does not cancel work. Do not rerun the quickstart to recover: it creates a new request. Read the [saved run](/api/workflows/runs) instead. -## 3. Continue the conversation +## 3. Continue from here -Send the next message to `POST /v1/threads/{thread_id}/inputs` with a new key: +[Send a follow-up](/api/workflows/conversations) to turn the answer into a checklist. You can also [attach a document](/api/workflows/files) or [create a reusable Agent](/api/workflows/agents). -```json -{ - "input": "Give me one practical example for each suggestion.", - "mode": "queue" -} -``` + + It sends `POST /v1/threads` with an `input` message and a new `Idempotency-Key`. The response identifies the accepted work: + + ```json + { + "thread_id": "thread_example", + "input_id": "message-1", + "run_id": "message-1", + "disposition": "running" + } + ``` + + Save the returned IDs. Do not assume `input_id` and `run_id` are equal. + + **202 means accepted.** The example reads `GET /v1/threads/{thread_id}/runs/{run_id}` until completion, then reads the items for that run. + + An idempotency key identifies one submitted message. Save it and the body before sending. Reuse both after a lost response. A new key starts new work. + -The reference's `follow-up.sh` runs this step using the IDs from the quickstart. Its `cleanup.sh` removes the example resources when you finish. + + Save any output you need. Download `$FLUSO_API_URL/examples/cleanup.sh`, read it, then run `source cleanup.sh` in the same shell. It deletes the example conversation and any files created by the file examples. -Next, learn to [read conversations](/api/workflows/conversations), [attach a file](/api/workflows/files), or [use an Agent](/api/workflows/agents). + The scripts are examples, not an SDK. In your application, store tokens in a secret store and save request IDs durably. Keep credentials out of URLs, source control, logs, and screenshots. + diff --git a/content/docs/api/index.mdx b/content/docs/api/index.mdx index a63f8e8..db76408 100644 --- a/content/docs/api/index.mdx +++ b/content/docs/api/index.mdx @@ -44,10 +44,10 @@ The guides below explain how to combine those operations. Endpoint tables sit in Pair a client with your own workspace. - + Continue work and retrieve the complete answer. - + Handle a timeout without starting the same work twice.

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 Date: Sun, 11 Oct 2026 21:10:15 +0200 Subject: [PATCH 5/7] docs: clarify schedule and device recovery --- content/docs/api/daemon/pairing.mdx | 4 ++-- content/docs/api/workflows/schedules.mdx | 10 +++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/content/docs/api/daemon/pairing.mdx b/content/docs/api/daemon/pairing.mdx index f7ca039..1d06321 100644 --- a/content/docs/api/daemon/pairing.mdx +++ b/content/docs/api/daemon/pairing.mdx @@ -29,7 +29,7 @@ Replace the address for a remote daemon. Replace `PAIRING_CODE` with the code fr Save `token` privately. Keep the returned client `id` and `daemonId` with your connection settings. If that daemon identity changes later, pair again. -Codes expire after ten minutes. A failed or lost exchange requires a new code. Device tokens expire after 30 idle days. Authenticated use renews them. +Codes expire after ten minutes and work once. If the exchange response is lost, generate a new code. Device tokens expire after 30 idle days. Authenticated use renews them. A normal device token grants read/write workspace access, including tool approvals. Add `--manage-host` only for clients that also need machine administration. @@ -49,7 +49,7 @@ Use the token as `Authorization: Bearer YOUR_TOKEN`. Browser requests must come Send `DELETE /daemon/v1/clients/{clientId}` to revoke a token. A device can revoke itself. Host-management access also permits listing and revoking other devices. -Revocation closes that device's streams and prevents further access. It does not delete the workspace. +Revocation closes that device's streams and prevents further access. The workspace, accepted work, and schedules remain. [Stop work](/api/workflows/runs#stop-work) or [pause schedules](/api/workflows/schedules) separately. | Method and path | Access and use | diff --git a/content/docs/api/workflows/schedules.mdx b/content/docs/api/workflows/schedules.mdx index a68ac9e..87d9493 100644 --- a/content/docs/api/workflows/schedules.mdx +++ b/content/docs/api/workflows/schedules.mdx @@ -36,10 +36,14 @@ Use an IANA timezone. At daylight-saving changes, skipped local times move forwa | Edit or pause | `PATCH /v1/schedules/{schedule_id}` | | Delete | `DELETE /v1/schedules/{schedule_id}` | -List responses include last-run information. Follow numbered `pagination.page` and `pages`. Use `attention=true` on `/v1/schedules` to find schedules needing review. Pause with `{"status":"paused"}` or resume with `{"status":"active"}` through PATCH. Read the schedule again to confirm its saved state. +List responses include last-run information. Follow numbered `pagination.page` and `pages`. Use `attention=true` on `/v1/schedules` to find schedules needing review. Pause with `{"status":"paused"}` or resume with `{"status":"active"}` through PATCH. Check its entry in the schedule list after saving. -Keep the schedule ID and unchanged settings when retrying creation. A new ID creates another schedule. To change between recurring and one-time timing, supply exactly one of `cron` or `at`. +Keep the schedule ID and unchanged settings when retrying creation. A new ID creates another schedule. + +To switch between recurring and one-time timing, send only the new `cron` or `at` field. It replaces the previous timing. Omitted fields stay unchanged. Do not send `null` to clear fields. Deleting a schedule prevents future submissions. It does not cancel already accepted work. [Cancel that run separately](/api/workflows/runs#stop-work) if needed. -On a daemon, the host must be awake for work to run. After downtime, missed recurring runs are combined into one catch-up run. Failed scheduled work can pause the schedule for attention. +On a daemon, the host must be awake for work to run. After downtime, missed recurring runs are combined into one catch-up run. + +Delivery failures can pause a schedule. A failed accepted run does not automatically pause recurring work. A one-time schedule stops submitting after acceptance, even if its run later fails. Check `last_run.status` or the saved run for the outcome. From 28e6f77dd74ed415d728bfa218907fa9b7160765 Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 22:59:29 +0200 Subject: [PATCH 6/7] docs: make API guides task-focused and remove outdated architecture --- AGENTS.md | 2 +- components/diagrams.tsx | 319 ------------------- content/docs/api/architecture.mdx | 53 --- content/docs/api/cloud/client-sign-in.mdx | 123 +++++-- content/docs/api/first-request.mdx | 80 +++-- content/docs/api/index.mdx | 4 +- content/docs/api/meta.json | 3 +- content/docs/api/workflows/agents.mdx | 112 ++++--- content/docs/api/workflows/connections.mdx | 76 +++-- content/docs/api/workflows/conversations.mdx | 77 +++-- content/docs/api/workflows/files.mdx | 100 ++++-- content/docs/api/workflows/meta.json | 2 +- content/docs/api/workflows/runs.mdx | 70 ++-- content/docs/api/workflows/schedules.mdx | 86 +++-- content/docs/api/workflows/streaming.mdx | 69 ++-- content/docs/api/workflows/usage.mdx | 45 ++- content/docs/api/workflows/workspace.mdx | 46 ++- content/docs/deployment-models.mdx | 2 +- public/_redirects | 15 +- 19 files changed, 608 insertions(+), 676 deletions(-) delete mode 100644 content/docs/api/architecture.mdx diff --git a/AGENTS.md b/AGENTS.md index 1b2d00a..13d7b61 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ The navigation keeps Home first and sorts the top-level sections alphabetically. - **App setup** - `/integrations/{github, gmail, google-calendar, slack}`. Per-app permissions and prompts, not feature pages. All of them connect through the Apps tab of the Add MCP dialog. - **Deployment models** - `/deployment-models`. Current availability and operating boundaries for Fluso Cloud, dedicated AWS VPC, planned Azure VNet, and scoped on-premises deployments. - **Features** - `/features/*`. Approvals and permissions, Apps and MCP servers, Chat, Confidential mode, Imports, Memory, Projects, Skills, and Tasks. -- **Fluso API** - `/api/*`. Start with access and a first request. Keep Cloud and daemon credentials and administration separate. Document shared workflows once. Each deployment's `/docs` and `/v1/openapi.json` own exact request and response schemas. Keep endpoint tables collapsed after the workflow. Preserve old API URLs with redirects. +- **Fluso API** - `/api/*`. Start with access and a first request. Keep Cloud and daemon credentials and administration separate. Document shared workflows once under Use the API. Lead with a useful task, runnable requests, and the expected result. Reuse the deployment's example helpers. Put protocol details and recovery rules beside the step that needs them. Each deployment's `/docs` and `/v1/openapi.json` own exact request and response schemas. Keep endpoint tables collapsed after the workflow. Preserve old API URLs with redirects. - **Fluso Local** - `/workspaces/*`. Setup and everyday use on your own machine, followed by settings and maintenance. Keep macOS and Linux as choices inside one setup guide. Request daemon download links from support; do not publish unlisted release URLs. API access is documented under Fluso API. - **Get started** - `/going-deeper`, `/introduction`, `/quickstart`. These pages explain the path from first setup to daily use. - **Reference** - `/resources/{faq, legal, pricing}`. Legal links to the terms of service at `https://fluso.ai/terms` and privacy policy at `https://fluso.ai/privacy`; do not duplicate the policies in the docs. diff --git a/components/diagrams.tsx b/components/diagrams.tsx index 7bc4168..705f13f 100644 --- a/components/diagrams.tsx +++ b/components/diagrams.tsx @@ -1,14 +1,8 @@ import { - Bot, Clock3, - Database, FileText, FolderClosed, MessageSquareText, - Network, - PlugZap, - Server, - ShieldCheck, Users, } from 'lucide-react'; @@ -208,316 +202,3 @@ export function KnowledgeGraphDiagram() { ); } - -const architectureServices = [ - { - label: 'Fluso client', - detail: 'Desktop or remote', - x: 10, - icon: MessageSquareText, - }, - { - label: 'Edge', - detail: 'TLS + routing', - x: 162, - icon: ShieldCheck, - }, - { - label: 'REST API', - detail: 'Identity + records', - x: 314, - icon: Server, - }, - { - label: 'Agent gateway', - detail: 'Admission + routing', - x: 466, - icon: Network, - emphasis: true, - }, - { - label: 'User runtime', - detail: 'Thread worker + tools', - x: 618, - icon: Bot, - }, -] as const; - -const architectureData = [ - { - label: 'PostgreSQL', - detail: 'Platform records', - owner: 'REST API', - x: 380, - icon: Database, - }, - { - label: 'Isolated user storage', - detail: 'Projects, sessions, Agents', - owner: 'Gateway + runtime', - x: 532, - icon: FolderClosed, - }, -] as const; - -const ARCHITECTURE_NODE_WIDTH = 132; - -function ArchitectureNode({ - label, - detail, - x, - icon: Icon, - emphasis = false, -}: { - label: string; - detail: string; - x: number; - icon: typeof FolderClosed; - emphasis?: boolean; -}) { - return ( - - - - ); -} - -export function ArchitectureDiagram() { - return ( -

diff --git a/content/docs/api/meta.json b/content/docs/api/meta.json index 79bec20..894994d 100644 --- a/content/docs/api/meta.json +++ b/content/docs/api/meta.json @@ -5,7 +5,6 @@ "first-request", "cloud", "daemon", - "workflows", - "architecture" + "workflows" ] } diff --git a/content/docs/api/workflows/agents.mdx b/content/docs/api/workflows/agents.mdx index 7a8af91..a3ec4ac 100644 --- a/content/docs/api/workflows/agents.mdx +++ b/content/docs/api/workflows/agents.mdx @@ -1,77 +1,109 @@ --- -title: Agents and configuration -sidebarTitle: Agents and configuration +title: Use Agents icon: bot -description: Create a reusable Agent, choose its conversation, and update its instructions safely. +description: Create a meeting assistant, give it work, and improve its saved instructions. --- -An Agent adds saved instructions and capabilities to a conversation. Start with [API access](/api/first-request) for credentials and your workspace's complete endpoint reference. +An Agent keeps instructions for work you repeat. This example creates a meeting assistant that turns notes into decisions and open questions. -## Create an Agent +Use the Bash shell from [your first request](/api/first-request). It provides authenticated requests through `api`. -Send `POST /v1/agents` with a saved `Idempotency-Key` and a body such as: +## Create the assistant -```json -{ +```bash +agent_key=$(uuidgen) && +agent_body='{ "name": "Meeting assistant", - "instructions": "Turn my notes into a short meeting brief. List decisions needed and questions still open." -} + "instructions": "Turn my notes into a meeting brief. List decisions needed and open questions." +}' && +agent=$(api /agents \ + -H "Idempotency-Key: $agent_key" \ + -H 'Content-Type: application/json' -d "$agent_body") && +agent_id=$(printf '%s' "$agent" | jq -er '.agent_id') ``` -Keep the returned `agent_id`. To use the Agent, send `POST /v1/threads` with that ID and an `input` message. To change its setup through conversation, also set `purpose: "manager"`. +This saves the Agent without starting a conversation. Keep `agent_id` for later use. If the response is lost, repeat the request with the same `agent_key` and `agent_body`. -Opening the same Agent and purpose returns its existing conversation. Two callers share that conversation and its queue. For independent ordinary chats, omit `agent_id`. +## Give it work -## Read the current configuration +Send a message with that Agent's ID: -`GET /v1/agents/{agent_id}` returns the Agent and its `current_config_id`. Keep that ID before editing. - -## Publish an update +```bash +agent_input_key=$(uuidgen) && +agent_input_body=$(jq -nc --arg agent_id "$agent_id" \ + '{agent_id: $agent_id, + input: ("Notes: Maya will send the revised budget on Thursday. " + + "We still need to choose a launch date. Prepare my meeting brief.")}') && +agent_receipt=$(api /threads \ + -H "Idempotency-Key: $agent_input_key" \ + -H 'Content-Type: application/json' -d "$agent_input_body") && +agent_thread_id=$(printf '%s' "$agent_receipt" | jq -er '.thread_id') && +agent_run_id=$(printf '%s' "$agent_receipt" | jq -er '.run_id') +``` -Send only the changed fields to `PATCH /v1/agents/{agent_id}`, together with the ID you read: +Read the answer with the quickstart's helpers. The parentheses keep your original conversation IDs unchanged: -```json -{ - "base_config_id": "CURRENT_CONFIG_ID", - "instructions": "Turn my notes into a short meeting brief. Start with the decisions I need to make." -} +```bash +( + thread_id=$agent_thread_id + run_id=$agent_run_id + wait_for_run && read_run_items | jq '.data' +) ``` -An old `base_config_id` returns 409 instead of overwriting another edit. Read the latest configuration, review the difference, then retry your intended change. +Opening this Agent again returns the same main conversation. Other callers share its history and queue. To open its setup conversation, include `purpose: "manager"` in the thread request. -The main conversation uses saved changes on its next turn. Existing work sessions retain their configuration binding. `run.execution` records the configuration, provider, model, and reasoning effort used when execution starts. +## Improve its instructions -## Select capabilities and knowledge +First read the current setup. Then send your edit with its `current_config_id`: -Use skill names from [skill inventory](/api/workflows/workspace#choose-skills-for-an-agent) and connection IDs from [connection inventory](/api/workflows/connections). +```bash +agent=$(api "/agents/$agent_id") && +config_id=$(printf '%s' "$agent" | jq -er '.current_config_id') && +edit_body=$(jq -nc --arg base "$config_id" \ + '{base_config_id: $base, + instructions: ("Turn my notes into a short meeting brief. " + + "Start with decisions I need to make, then open questions. " + + "Do not invent owners or dates.")}') && +api "/agents/$agent_id" -X PATCH \ + -H 'Content-Type: application/json' -d "$edit_body" +``` -For updates, omitted `skills` or `connectors` remain unchanged. `null` removes the selection restriction. An empty list or map explicitly selects none. A connector's `allow` policy still respects account consent. +A 409 means another edit arrived first. Read the newer setup and review it before retrying. The main conversation uses saved changes on its next turn. Existing work sessions keep their saved configuration. -To add knowledge, upload a file or copy a workspace file through the knowledge endpoints. Save the returned reference in `knowledge_files` with the current `base_config_id`. Storage alone does not attach it to the Agent. +Next, [schedule this assistant](/api/workflows/schedules) using `agent_id` and `agent_thread_id`. -Knowledge is a copy. Editing the original workspace file does not change that copy. Each Agent accepts up to 25 knowledge files, with a 20 MiB limit per file. Send `knowledge_files: []` to remove all selected knowledge. + + Use skill names from [skill inventory](/api/workflows/workspace#choose-skills-for-an-agent) and server IDs from [connection inventory](/api/workflows/connections). - - Supported effort values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`. An input's explicit effort overrides the Agent default. The runtime chooses the nearest supported effort, preferring a higher level before a lower one. + Omitted `skills` or `connectors` stay unchanged during an update. `null` removes the selection restriction. An empty list or map selects none. Connector `allow` still respects account consent. - Steering without an effort preserves the active turn's setting. An explicit change takes effect at the next execution attempt. It does not restart an in-flight model call. + Upload knowledge or copy a workspace file through the knowledge endpoints below. Add the returned reference to `knowledge_files` in an update with the current `base_config_id`. - Public clients can open main and manager conversations. They cannot create Agent work sessions directly. For independent ordinary conversations, omit `agent_id`. + Uploading alone does not attach knowledge to the Agent. Knowledge is a copy, so later edits to the original file do not change it. - All conversations share workspace files. Use distinct output paths for concurrent jobs that must not overwrite each other. + Each Agent accepts up to 25 knowledge files, each no larger than 20 MiB. Use `knowledge_files: []` to remove the selection. -## Copies and sharing + + Supported effort values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`. An input's explicit effort overrides the Agent default. The runtime chooses the nearest supported effort, preferring a higher level before a lower one. + + Steering without an effort keeps the active setting. An explicit change applies at the next execution attempt, without restarting an in-flight model call. -Duplication creates an independent Agent. Keep the same idempotency key when retrying duplication. The retry returns the original copy. + `run.execution` records the configuration, provider, model, and effort used when execution starts. -Public sharing is Cloud-only. A shared Agent snapshot excludes credentials, conversations, and schedules. After importing, inspect `setup_requirements` and complete setup in its manager conversation. The recipient supplies credentials and consent. + Public clients open main and manager conversations, not Agent work sessions. Omit `agent_id` for independent ordinary conversations. -## Delete an Agent + Conversations share workspace files. Use distinct output paths for concurrent jobs that must not overwrite each other. + -`DELETE /v1/agents/{agent_id}` deletes the Agent and its owned conversations and revokes its shared Agent link. Save the results you need first. + + Duplication creates independent setup. Retry it with the same idempotency key to recover the original copy. -For exact fields and sharing operations, use the [workspace API reference](/api#choose-your-workspace). + [Public sharing](/api/cloud/sharing) is Cloud-only. Its snapshot excludes credentials, conversations, and schedules. After import, read `setup_requirements` and complete setup in the Manager conversation. + + `DELETE /v1/agents/{agent_id}` removes the Agent, its owned conversations, and its schedules. It also revokes the Agent's share link. Save needed results before deletion. + | Method and path | Use | diff --git a/content/docs/api/workflows/connections.mdx b/content/docs/api/workflows/connections.mdx index f161543..f344585 100644 --- a/content/docs/api/workflows/connections.mdx +++ b/content/docs/api/workflows/connections.mdx @@ -1,50 +1,76 @@ --- title: Connections and approvals icon: plug -description: Connect custom MCP servers and handle the actions that pause a run. +description: Connect a service once, then help users decide when Fluso needs permission. --- -A connection lets Fluso use another service's tools. An approval decides whether a requested action may proceed. Connecting a service and approving a tool are separate steps. +Your integration can use the services already connected to the same Fluso workspace. A connection gives access to tools. The user's permission settings decide which actions can proceed. -Cloud offers [managed app sign-in](/api/cloud/apps) as well as custom MCP servers. A daemon uses its own custom connections. +For an initial setup, connect the service in **Plugins → MCP** in Fluso. Cloud offers managed apps such as Google Workspace. Fluso Local uses custom MCP servers. -## Connect a custom MCP server +The examples below use `api`, `thread_id`, and `run_id` from [your first request](/api/first-request). + +## Handle a waiting run + +When a run reports `waiting_action`, read its pending action: + +```bash +state=$(api "/threads/$thread_id/state") && +approval_url=$(printf '%s' "$state" | jq -er --arg run "$run_id" \ + 'first(.pending_actions[] | select(.run_id == $run)) | .url') && +approval_route=${approval_url#/v1} && +approval=$(api "$approval_route") && +printf '%s' "$approval" | jq '{kind, status, app_id, server_id, tool_name, arguments, expires_at}' +``` -Start with a public HTTPS server URL. `POST /v1/mcp-oauth/probe` discovers its transport and authentication method without saving a connection. +The action URL already starts with `/v1`. The example removes that prefix because `api` includes it. If there is no matching action, check the run again. It may have resumed or finished. -| Server authentication | Start with | +| Action kind | What to show the user | | --- | --- | -| No authentication, bearer token, or API key | `POST /v1/mcp-oauth/connections/static` | -| OAuth with automatic client registration | `POST /v1/mcp-oauth/sessions` | -| OAuth with your own client registration | `POST /v1/mcp-oauth/sessions/byo-oauth` | +| `mcp_connect` | The service they need to connect. Follow [managed app sign-in](/api/cloud/apps) or custom setup below. | +| `mcp_tool` | The requested tool and its actual arguments, with a choice to allow or deny. | -For OAuth, open the returned `authorization_url`. An `already_connected` response needs no redirect. +For a **pending `mcp_tool` action**, send this only after the user approves the displayed call: -On Cloud, finish with the callback's `completion_proof` under the initiating account. On a daemon, `/daemon/v1/oauth/callback` completes the connection directly. Poll the returned session ID until it is `connected` or reports an error. +```bash +api "$approval_route/decision" \ + -H 'Content-Type: application/json' \ + -d '{"decision":"allow","scope":"once"}' +``` -Read connection inventory with `GET /v1/mcp-oauth/connections`. `needs_reauth` requires the user to reconnect. Inventory alone does not prove that the external server is reachable. +Use `decision: "deny"` to refuse it. After either decision, follow the same run. Do not resend its input. -## Handle a waiting run +A 409 or 410 means the action needs another read. Do not approve a replacement without reviewing it. A successful decision saves consent, but does not mean the run completed. -Read `state.pending_actions` from `GET /v1/threads/{thread_id}/state`. +## Connect a custom MCP server -- For `mcp_connect`, guide the user through the requested connection flow. -- For a pending `mcp_tool`, read the approval and show its actual tool arguments for review. +MCP is the protocol that lets a service expose tools to Fluso. Start with the service's public HTTPS endpoint. -After the user approves that tool call, send this body to its approval decision endpoint: +Probe the endpoint before saving it. Replace the example URL with your service's URL: -```json -{ - "decision": "allow", - "scope": "once" -} +```bash +api /mcp-oauth/probe \ + -H 'Content-Type: application/json' \ + -d '{"server_url":"https://your-service.example/mcp"}' ``` -Use `decision: "deny"` to refuse it. A developer write token can make this one-time decision. Permanent permissions require a Cloud app session or a paired daemon device. +The response identifies its transport and authentication method. Choose the matching connection flow: + +| The service uses | Start with | +| --- | --- | +| No authentication, bearer token, or API key | `POST /v1/mcp-oauth/connections/static` | +| OAuth with automatic client registration | `POST /v1/mcp-oauth/sessions` | +| OAuth with your own client registration | `POST /v1/mcp-oauth/sessions/byo-oauth` | + +Open the returned `authorization_url` for OAuth. `already_connected` needs no sign-in. Wait for the session status to become `connected` before treating setup as complete. -An acknowledgement saves the decision. Follow thread state or events to see work resume. Do not send another input to approve the existing request. + + On Cloud, the callback submits `completion_proof` under the initiating account. On a daemon, `/daemon/v1/oauth/callback` completes it directly. Use the returned authorization URL unchanged. -If the decision returns 409 or 410, read the action again. It may have changed or expired. Do not approve a replacement without reviewing it. + Read `GET /v1/mcp-oauth/connections` to inspect saved connections. `needs_reauth` means the user must reconnect. Saved inventory alone does not prove the service is reachable. + + A developer write token can decide one-time tool approvals. Permanent rules require a Cloud app session or a paired daemon device. + | Method and path | Use | diff --git a/content/docs/api/workflows/conversations.mdx b/content/docs/api/workflows/conversations.mdx index df41120..5816cdb 100644 --- a/content/docs/api/workflows/conversations.mdx +++ b/content/docs/api/workflows/conversations.mdx @@ -1,67 +1,76 @@ --- -title: Conversations and results -sidebarTitle: Conversations and results +title: Send messages icon: messages-square -description: Submit work, continue a conversation, and read the complete saved answer. +description: Continue a conversation and read its complete answer. --- -A **thread** is a conversation. An **input** is a message you submit. A **run** tracks the resulting work. +Turn the answer from [your first request](/api/first-request) into a checklist. Use the same Bash shell, which already has your token, `api` helper, and conversation IDs. -Start with the runnable [API quickstart](/api/first-request). These are the operations behind it. +A **thread** is the conversation. Each message you submit is an **input**. A **run** tracks the work that follows. -## Create a conversation +## Send a follow-up -Send `POST /v1/threads` with an `Idempotency-Key` and an input body: +Save a new key and message, then send them to the existing conversation: -```json -{ - "input": "Explain three ways to check whether a source is reliable." -} +```bash +input_key=$(uuidgen) && +input_body='{ + "input": "Turn those checks into a three-item checklist.", + "mode": "queue" +}' && +receipt=$(api "/threads/$thread_id/inputs" \ + -H "Idempotency-Key: $input_key" \ + -H 'Content-Type: application/json' -d "$input_body") && +run_id=$(printf '%s' "$receipt" | jq -er '.run_id') ``` -Save the key and body before sending. The receipt returns `thread_id` and `run_id`. Use those values rather than deriving one from the other. +The receipt identifies this new run. If the conversation is busy, `queue` waits for the current work to finish. Use `steer` to guide active work instead. -To open a conversation without starting work, omit `input` and `attachments`. To use a saved Agent, include `agent_id`. Its main conversation is reused. `purpose: "manager"` opens its setup conversation. +Keep `input_key` and `input_body` if the response is lost. [Retry those same values](/api/workflows/runs), without generating another key. -## Send a message +## Read the answer -Send `POST /v1/threads/{thread_id}/inputs` with the next `input` and a new key. If the conversation is busy, use `mode: "queue"` to wait or `mode: "steer"` to guide active work. +The quickstart's helpers wait for the run and read every output page: -A retry must keep the original key and unchanged body. Reusing a key with different content returns 409. A new key means new work. +```bash +wait_for_run && +items=$(read_run_items) && +printf '%s' "$items" | jq '.data[] | select(.type == "message" and .role == "assistant")' +``` -The **follow-up** example in your [workspace's reference](/api#choose-your-workspace) continues the quickstart conversation in the same shell. +You receive the saved assistant messages, including any attachments. A message can contain a file without text. Use each attachment's returned path to [download its content](/api/workflows/files). -## Manage queued messages +If the run needs approval or fails, `wait_for_run` stops and prints its status. Follow [Runs and retries](/api/workflows/runs) before sending more work. -Read thread state before changing queued work. An active or completed input cannot be edited as a queued message. +## Start a separate conversation -Editing an input requires a new `Idempotency-Key` for that edit. Keep it when retrying the edit. If the input started meanwhile, a 409 preserves the conflict. Keep the user's draft instead of silently submitting replacement work. +Send `POST /v1/threads` with an `input` and a new key, as the quickstart does. Omit `input` and `attachments` to open an empty conversation. -A thread stop holds follow-up inputs. Resume a held input explicitly after reviewing state. +Include `agent_id` to use a [saved Agent](/api/workflows/agents). Its main conversation is reused. Add `purpose: "manager"` to open that Agent's setup conversation. ## Read message history -Read `GET /v1/threads/{thread_id}/items?run_id={run_id}` for messages, tool results, and attachments belonging to a run. Omit the filter to read the conversation. - -Entries are in `data`. Read every page: pass `next_cursor` as `after` with the unchanged `revision`, until `next_cursor` is null. Keep filters unchanged between pages. - -If a snapshot edit or deletion causes a pagination conflict, discard the partial read and restart without `after` or `revision`. New messages alone do not invalidate the snapshot. +For your own client, use `GET /v1/threads/{thread_id}/items?run_id={run_id}`. Omit `run_id` to read the whole conversation. -Read the saved [run status](/api/workflows/runs) as well as the messages. An empty page does not prove the run finished or failed. +Entries are in `data`. Pass `next_cursor` as `after`, with the unchanged `revision`, until `next_cursor` is null. Keep filters unchanged. The `read_run_items` helper performs these steps for you. -## Follow live progress +An empty page does not prove completion. Check the [run status](/api/workflows/runs). To display progress while waiting, add [live updates](/api/workflows/streaming). -`GET /v1/threads/{thread_id}/events` streams Server-Sent Events. Submitting with `stream: true` starts with an `input.accepted` receipt. Save it before processing later events. + + Edits or deletions can invalidate an item snapshot. On a pagination conflict, discard the partial read and restart without `after` or `revision`. New messages alone do not invalidate it. -Streams do not replay missed events. After reconnecting, read saved state and items. Ignore unknown event types and keepalive comments. Replace temporary text with its saved item to avoid showing it twice. + Read thread state before editing queued work. Active or completed inputs cannot be edited as queued messages. -See [live updates](/api/workflows/streaming) for a complete reconnect sequence and the runnable `watch.mjs` example. + A queued edit needs its own new `Idempotency-Key`. Keep that edit key on retries. If the input started meanwhile, keep the user's draft when the API returns 409. -## Delete a conversation + Stopping a thread holds follow-up inputs. Review state before resuming a held input. + -`DELETE /v1/threads/{thread_id}` deletes an ordinary conversation and stops its execution. Save needed results first. Agent main and manager conversations require deleting their Agent instead. + + `DELETE /v1/threads/{thread_id}` stops and deletes an ordinary conversation. Save needed results first. Agent main and manager conversations require deleting their Agent instead. -Conversation deletion does not remove a separately published chat snapshot or all workspace files. + Conversation deletion does not remove a separately published chat snapshot or all workspace files. + | Method and path | Use | diff --git a/content/docs/api/workflows/files.mdx b/content/docs/api/workflows/files.mdx index 6b0b019..507084e 100644 --- a/content/docs/api/workflows/files.mdx +++ b/content/docs/api/workflows/files.mdx @@ -1,70 +1,100 @@ --- -title: Files and projects -sidebarTitle: Files and projects +title: Work with files icon: folder-kanban -description: Upload inputs, read saved output, and protect files from conflicting edits. +description: Attach a report, get a summary file, and edit saved content without losing another change. --- -Files and projects belong to the authenticated workspace. You do not need a runtime ID. Use the paths returned by the API, including saved attachments. +Give Fluso a short report and get a summary file back. This example covers upload, attachment, execution, and download. -## Projects +Use the Bash shell from [your first request](/api/first-request), plus Node 22 or later. Keep the example in its own directory. -Use `/v1/projects` to list or create projects. Read, update, or delete a project at `/v1/projects/{project_id}`. Project IDs are also storage names. Keep the exact returned value when building paths. +## Upload a report and get a summary -Deleting a project deletes its conversations and files, then retires their schedules. Save needed results before deletion. See your [workspace's endpoint reference](/api#choose-your-workspace) for export and deletion behavior. +Download the two examples from your workspace: -## Read a text file +```bash +curl --fail --silent --show-error \ + "$FLUSO_API_URL/examples/upload.mjs" -o upload.mjs && +curl --fail --silent --show-error \ + "$FLUSO_API_URL/examples/attach.sh" -o attach.sh +``` -After [setting your API URL and token](/api/first-request), use a path returned by `GET /v1/files`. Replace the example path below: +Read both files before running them. The example writes `report.txt` and `summary.md` locally, so use a directory where those names are unused. ```bash -curl --fail-with-body --silent --show-error --get \ - "$FLUSO_API_URL/files/read" \ - -H "Authorization: Bearer $FLUSO_API_TOKEN" \ - --data-urlencode 'path=/projects/Home/files/notes.md' +source attach.sh ``` -Use `GET /v1/files` to browse and `GET /v1/files/download` for file bytes. A text read includes a `revision` for safe replacement. +It uploads a sample report, sends it to your conversation, waits for completion, and downloads the returned attachment as `summary.md`. -## Upload a file +Keep `input_path` for the uploaded report and `output_path` for the summary in your workspace. Use returned paths for future requests. Do not guess them from the prompt. -1. Create an upload with `POST /v1/uploads`, its filename, MIME type, size, and a saved `Idempotency-Key`. -2. Send the bytes to `upload_url` in a multipart POST. Include all returned `upload_fields` unchanged, with the file last. -3. Confirm with `POST /v1/uploads/confirm` and `{"id":"UPLOAD_ID"}`. -4. Read `GET /v1/uploads/{upload_id}` until it finishes. -5. Attach its returned `target_path` to a conversation input. +If the example stops on an error, retain its upload key and receipt. Use the recovery details below before repeating a transfer. -Do not send your Fluso bearer token to `upload_url`. +## Download an attachment -A `confirmed` upload is ready to attach. `history_unavailable` also means the file was saved, but its new version was not recorded. Show that warning. For `failed`, `cancelled`, or `conflict`, inspect the saved error before starting another transfer. +A saved message can contain attachments without text. Read all items for its run, then use each attachment's path: -If a request times out, read the existing upload. Starting another upload can duplicate work. Confirmed receipts do not guarantee that a file remains unchanged after later edits. +```bash +api /files/download --get \ + --data-urlencode "path=$output_path" --output downloaded-summary.md +``` -The full reference includes runnable **upload**, **attach**, and **download** examples. It also covers expired transfer credentials and recovery without another transfer. +Choose a local output name you can replace. `available: false` on an attachment means its file is no longer at that path. ## Edit without losing another change -Use `PUT /v1/files/text` with `path`, `content`, and the latest `revision` to replace text. Use `overwrite: false` when creating a new file that must not replace an existing path. Add `recursive: true` if its parent directories do not exist yet. +Read the report and its current revision: -If another writer changed the file, read the new content and merge before retrying. Do not drop the revision check to force an overwrite. +```bash +saved=$(api /files/read --get --data-urlencode "path=$input_path") && +printf '%s' "$saved" | jq '{content, revision}' +``` + +A revision identifies the content you read. Include it when saving your edit: -Text writes allow 1 MiB of UTF-8 content. Text reads default to 5 MiB. Use uploads and downloads for larger or binary files. +```bash +edit_body=$(printf '%s' "$saved" | jq --arg path "$input_path" \ + '{path: $path, revision, content: (.content + "\nReviewed by me.\n")}') && +api /files/text -X PUT \ + -H 'Content-Type: application/json' -d "$edit_body" +``` - - Versions and comments apply to ordinary files under `/files/` and `/projects/{project_id}/files/`, excluding hidden directories. +If another writer changed the file, the save fails with 409. Read the new content and merge your edit. Keep the revision check. - Replacement refuses to lose existing history unless you explicitly permit it with `allow_history_loss: true`. Treat that option as a data-loss decision. For uploads, changing a failed request requires a new key. Retrying the old key returns its saved failure. +For a new file, use `overwrite: false`. Add `recursive: true` when its parent directories do not exist. Text writes allow 1 MiB of UTF-8 content. Reads default to 5 MiB. - A directory copy can partly succeed. Inspect the destination before retrying it. An attachment with `available: false` records historical output whose file is no longer at that path. +## Projects + +Use `GET /v1/projects` to choose a project and `GET /v1/files` to browse its files. Project IDs are storage names. Keep their exact returned values when building paths. + +Deleting a project removes its conversations and files, then retires their schedules. Save or export needed results first. + + + 1. Send `POST /v1/uploads` with the filename, MIME type, byte size, and a saved `Idempotency-Key`. + 2. POST the bytes to `upload_url` as multipart data, with returned `upload_fields` unchanged and the file last. + 3. Send `POST /v1/uploads/confirm` with `{"id":"UPLOAD_ID"}`. + 4. Read `GET /v1/uploads/{upload_id}` until it finishes. + 5. Attach the returned `target_path` to your next input. + + Do not send your Fluso bearer token to `upload_url`. The downloadable example uses text files. Supply the correct MIME type for other files, such as PDFs. + + `confirmed` means the file is ready. `history_unavailable` means it was saved without a new recorded version. Show that warning. For `failed`, `cancelled`, or `conflict`, inspect the saved error before another transfer. + + A timeout does not prove import failure. Read the existing upload. A confirmed receipt also does not promise that later edits left its bytes unchanged. -## Restore or discuss a file + + Versions and comments apply under `/files/` and `/projects/{project_id}/files/`, excluding hidden directories. -Read `/v1/files/versions` to inspect saved versions before restoring one. Download a selected version through `/versions/content` with its `path` and `commit`. A missing version returns 404 instead of the current file. + Read `/v1/files/versions` before restoring a file. Download a version through `/v1/files/versions/content` with its `path` and `commit`. A missing version returns 404, never the current bytes. -Restoring one file preserves the replaced bytes as a version first. Other files remain unchanged. If preservation fails, review the history warning before allowing any loss. + Restore preserves the replaced bytes as a version first. If preservation fails, review the warning before permitting `allow_history_loss: true`. Other files remain unchanged. -File comments can attach a note to a quoted passage, slide, sheet range, or image region. Creating a comment does not start an Agent run. + The same history protection applies to writes, uploads, copies, and moves. Directory copies can partly succeed. Inspect the destination before retrying. + + File comments can refer to a passage, slide, sheet range, or image region. A comment does not start an Agent run. + | Method and path | Use | diff --git a/content/docs/api/workflows/meta.json b/content/docs/api/workflows/meta.json index 8e0971e..1201821 100644 --- a/content/docs/api/workflows/meta.json +++ b/content/docs/api/workflows/meta.json @@ -1,5 +1,5 @@ { - "title": "Shared workflows", + "title": "Use the API", "icon": "workflow", "defaultOpen": false, "pages": [ diff --git a/content/docs/api/workflows/runs.mdx b/content/docs/api/workflows/runs.mdx index b3aa018..43b619b 100644 --- a/content/docs/api/workflows/runs.mdx +++ b/content/docs/api/workflows/runs.mdx @@ -1,57 +1,75 @@ --- -title: Runs and recovery -sidebarTitle: Runs and recovery +title: Runs and retries icon: activity -description: Follow accepted work, recover after a disconnection, and stop a run deliberately. +description: Find out what happened, recover a result, or stop work without submitting it twice. --- -Submitting an input returns a receipt with `thread_id`, `input_id`, and `run_id`. Save all three. **202 means accepted, not completed.** +A request timed out, or the connection closed before the answer arrived. Start by checking the work Fluso already accepted. -Read `GET /v1/threads/{thread_id}/runs/{run_id}` to check progress. Read `GET /v1/threads/{thread_id}/state` for queued work and pending actions. +Use the Bash shell from [your first request](/api/first-request). It contains the `api` helper and saved IDs. -## Understand the status +## Check the run -| Status | What to do | +```bash +api "/threads/$thread_id/runs/$run_id" | jq '{status, error}' +``` + +| Status | Your next step | | --- | --- | -| `queued` or `running` | Wait, then read again. | -| `waiting_action` | Read the pending approval or connection request in thread state. | -| `held` | Inspect the state and queued inputs before resuming. | -| `completed` | Read all saved result pages and check the answer. | -| `failed` or `cancelled` | Read the error and any saved results before submitting more work. | +| `queued` or `running` | Wait, then check again. | +| `waiting_action` | Read thread state and [handle the pending action](/api/workflows/connections#handle-a-waiting-run). | +| `held` | Review the queue in thread state before resuming an input. | +| `completed` | [Read every result page](/api/workflows/conversations#read-message-history). | +| `failed` or `cancelled` | Read the error and partial results before deciding what to do next. | -A completed run can contain a failed tool call. A failed run can still have changed an external service. Check the results against the requested task. +A completed run can contain a failed tool call. A failed run can still have changed an external service. Check the answer and tool results before repeating work. ## Recover after a timeout -- If the submission response was lost, retry the same body with the same `Idempotency-Key`. -- If you received a receipt, read that run and its [saved items](/api/workflows/conversations#read-message-history). -- If a stream closes, reconnect for new events and read saved state for missed results. +| What you have | What to do | +| --- | --- | +| A receipt with `thread_id` and `run_id` | Read that run and its saved output. | +| A saved key and body, but no receipt | Repeat the original submission with both unchanged. | +| A disconnected stream | Reconnect for new events, then read saved state and items. | + +For a lost [follow-up response](/api/workflows/conversations#send-a-follow-up), resend the saved request: -A client timeout does not cancel work. Creating a new key can start another execution. +```bash +receipt=$(api "/threads/$thread_id/inputs" \ + -H "Idempotency-Key: $input_key" \ + -H 'Content-Type: application/json' -d "$input_body") && +run_id=$(printf '%s' "$receipt" | jq -er '.run_id') +``` -Input retry receipts remain until 24 hours after completion. After that, inspect the saved run before deciding whether to submit new work. Runs remain readable until their conversation is deleted. +Do not generate another key for this retry. A new key can start duplicate work. For the quickstart's initial submission, retry `/threads` instead of `/threads/$thread_id/inputs`. + +Input receipts remain until 24 hours after completion. After that, inspect the saved run before submitting again. Saved runs remain readable until their conversation is deleted. - An error from `POST /v1/threads` can include `error.details.thread_id`. Save that ID and inspect the thread state. Its presence does not prove input acceptance. + An error from `POST /v1/threads` can include `error.details.thread_id`. Keep that ID and inspect its state. Thread creation alone does not prove that Fluso accepted the input. - After resolving the cause, send the original input and key to that thread's `/inputs` endpoint. This can recover an uncertain acceptance within the retry window. + After resolving the error, send the original input and key to that thread's `/inputs` endpoint. This can recover an uncertain acceptance within the retry window. ## Stop work -Use `POST /v1/threads/{thread_id}/runs/{run_id}/cancel` for one run. Use `POST /v1/threads/{thread_id}/stop` for the conversation. +To cancel only the saved run: + +```bash +api "/threads/$thread_id/runs/$run_id/cancel" -X POST +``` -Read the state until execution stops. A cancellation acknowledgement does not prove that a tool has stopped, and cancellation cannot undo a completed external action. +Then check its status until execution stops. Cancellation cannot undo an external action that already completed. -Use the **Recovery and cancellation** examples in your [workspace's full reference](/api#choose-your-workspace) for retries, held inputs, and cancellation. +To stop the conversation and hold its follow-up messages, use `POST /v1/threads/{thread_id}/stop` instead. Review the queue before resuming held inputs. ## Decide whether to retry -A failed run does not become a new execution when you resend its old key. Read its results before choosing a new message and key. +For temporary read failures, wait before retrying. Increase the delay between attempts, set a maximum wait, and honor `Retry-After`. Stop when `error.retryable` is explicitly false. The quickstart's read helpers already do this. -For temporary read failures, delay retries with backoff and a maximum wait. Honor `Retry-After`. Do not retry when `error.retryable` is explicitly false. +For writes, use the operation's own recovery rule. Inputs use saved keys and bodies. Uploads use their receipts. Schedules use their IDs. Configuration edits use revisions. -For an uncertain write, use that operation's documented retry mechanism. Input keys, upload keys, schedule IDs, and configuration revisions solve different problems. They are not interchangeable. +A failed run stays failed when you resend its old key. Review partial results before choosing a new message and key. | Response | Action | diff --git a/content/docs/api/workflows/schedules.mdx b/content/docs/api/workflows/schedules.mdx index 87d9493..c72dfdc 100644 --- a/content/docs/api/workflows/schedules.mdx +++ b/content/docs/api/workflows/schedules.mdx @@ -1,49 +1,77 @@ --- -title: Schedules -sidebarTitle: Schedules +title: Schedule work icon: calendar-clock -description: Schedule Agent work once or repeatedly, and manage future runs. +description: Ask an Agent to review its notes every weekday, then inspect or pause the schedule. --- -A schedule sends a message to an Agent's conversation. The app calls these schedules **routines**. +A schedule sends a message to an Agent at a chosen time. In the app, schedules are called **routines**. -## Create a schedule +First [create the meeting assistant](/api/workflows/agents) and give it some notes. Keep that example's `agent_id` and `agent_thread_id` in the same Bash shell. -Send `POST /v1/agents/{agent_id}/schedules` with a new UUID as `id`. Keep that ID when retrying creation. +## Create a weekday review -For a weekday morning brief: +This schedule asks the assistant to review its conversation every weekday at 9 AM in Zurich. Change the message and timezone for your work. -```json -{ - "id": "REPLACE_WITH_NEW_UUID", - "thread_id": "AGENT_THREAD_ID", - "message": "Prepare a short morning brief. Start with what needs my attention today.", - "cron": "0 9 * * 1-5", - "timezone": "Europe/Zurich" -} +```bash +schedule_id=$(uuidgen) && +schedule_body=$(jq -nc --arg id "$schedule_id" --arg thread "$agent_thread_id" \ + '{id: $id, thread_id: $thread, + message: ("Review the meeting notes in this conversation. " + + "Summarize decisions still open and actions due next."), + cron: "0 9 * * 1-5", timezone: "Europe/Zurich"}') && +api "/agents/$agent_id/schedules" \ + -H 'Content-Type: application/json' -d "$schedule_body" ``` -The conversation must belong to that Agent. Supply exactly one of `cron` or `at`. For a one-time run, use a future `at` timestamp with a UTC offset instead of `cron`. +The response includes `next_run_at`. Check that time before leaving the schedule active. Keep `schedule_id` and `schedule_body` for retries. A new ID creates another schedule. -Use an IANA timezone. At daylight-saving changes, skipped local times move forward by the gap. Repeated times run at their first occurrence. +For a one-time task, replace `cron` with `at`, using a future timestamp with a UTC offset. For example, the format is `2030-01-02T09:00:00+01:00`. ## Review and change future work -| Action | Endpoint | -| --- | --- | -| List all schedules | `GET /v1/schedules` | -| List one Agent's schedules | `GET /v1/agents/{agent_id}/schedules` | -| Edit or pause | `PATCH /v1/schedules/{schedule_id}` | -| Delete | `DELETE /v1/schedules/{schedule_id}` | +Read this Agent's schedules: -List responses include last-run information. Follow numbered `pagination.page` and `pages`. Use `attention=true` on `/v1/schedules` to find schedules needing review. Pause with `{"status":"paused"}` or resume with `{"status":"active"}` through PATCH. Check its entry in the schedule list after saving. +```bash +api "/agents/$agent_id/schedules" | jq '.data' +``` + +Look at `last_run.status` for the previous result. Use its `run_id` and the schedule's `thread_id` to [read the output](/api/workflows/conversations#read-message-history). + +Pause future submissions when you no longer need them: + +```bash +api "/schedules/$schedule_id" -X PATCH \ + -H 'Content-Type: application/json' -d '{"status":"paused"}' +``` + +Check the schedule list after saving. Use `status: "active"` to resume, or `DELETE /v1/schedules/{schedule_id}` to remove it. + +Pausing or deleting a schedule does not cancel work already accepted. [Cancel that run separately](/api/workflows/runs#stop-work). + + + Send changed fields with `PATCH /v1/schedules/{schedule_id}`. Omitted fields stay unchanged. Do not send `null` to clear fields. + + To switch between recurring and one-time timing, send only the new `cron` or `at` field. It replaces the previous timing. + + Use an IANA timezone such as `Europe/Zurich`. At daylight-saving changes, skipped local times move forward by the gap. Repeated times run at their first occurrence. + -Keep the schedule ID and unchanged settings when retrying creation. A new ID creates another schedule. + + The daemon host must be awake for work to run. After downtime, missed recurring runs combine into one catch-up run. -To switch between recurring and one-time timing, send only the new `cron` or `at` field. It replaces the previous timing. Omitted fields stay unchanged. Do not send `null` to clear fields. + Delivery failures can pause a schedule. A failed accepted run does not automatically pause recurring work. -Deleting a schedule prevents future submissions. It does not cancel already accepted work. [Cancel that run separately](/api/workflows/runs#stop-work) if needed. + A one-time schedule stops submitting after acceptance, even if its run later fails. Inspect the saved run before deciding whether to request that work again. + -On a daemon, the host must be awake for work to run. After downtime, missed recurring runs are combined into one catch-up run. + + | Method and path | Use | + | --- | --- | + | `POST /v1/agents/{agent_id}/schedules` | Create with a caller-generated UUID and exactly one of `cron` or `at`. | + | `GET /v1/agents/{agent_id}/schedules` | List schedules for one Agent. | + | `GET /v1/schedules` | List all schedules. Use `attention=true` for delivery problems. | + | `PATCH /v1/schedules/{schedule_id}` | Edit, pause, or resume. | + | `DELETE /v1/schedules/{schedule_id}` | Prevent future submissions. | -Delivery failures can pause a schedule. A failed accepted run does not automatically pause recurring work. A one-time schedule stops submitting after acceptance, even if its run later fails. Check `last_run.status` or the saved run for the outcome. + Follow numbered `pagination.page` and `pages` in list responses. The target conversation must belong to the Agent. + diff --git a/content/docs/api/workflows/streaming.mdx b/content/docs/api/workflows/streaming.mdx index 12c8c2a..1b340fb 100644 --- a/content/docs/api/workflows/streaming.mdx +++ b/content/docs/api/workflows/streaming.mdx @@ -1,49 +1,72 @@ --- title: Live updates icon: radio -description: Show progress while retaining a complete, recoverable conversation. +description: Show progress as Fluso works, and recover the answer after a dropped connection. --- -Use live events for a responsive interface. Use saved state and items to recover the full result after a disconnection. +Add live updates when your interface needs to show progress before the answer is ready. Keep saved results as the source for the completed conversation. -After the [quickstart](/api/first-request), listen to its conversation: +## Try the watcher + +Use the [quickstart's shell](/api/first-request) and Node 22 or later. Download the watcher from your workspace: + +```bash +curl --fail --silent --show-error \ + "$FLUSO_API_URL/examples/watch.mjs" -o watch.mjs && +cat watch.mjs +``` + +After reading it, watch the saved run: ```bash -curl --no-buffer --fail-with-body --silent --show-error \ - "$FLUSO_API_URL/threads/$thread_id/events" \ - -H "Authorization: Bearer $FLUSO_API_TOKEN" +node watch.mjs "$thread_id" "$run_id" ``` -The response uses Server-Sent Events (SSE). Each `data` field contains a `ThreadEvent` JSON object. A blank line ends the frame. +The watcher prints saved conversation updates. It reads every page and reconnects after temporary failures without resubmitting input. Press **Ctrl+C** to stop watching. That does not cancel the run. ## Connect without missing saved work -1. Open the event stream. -2. Read the thread's saved state and all item pages. -3. Match updates by input, run, and item IDs. -4. Replace temporary text with its saved item when that item arrives. -5. Reconcile saved state periodically while work remains active. +Your client needs two sources: + +| Source | Use it for | +| --- | --- | +| Event stream | Show text and tool progress as it arrives. | +| Saved state and items | Recover missed updates and read the final answer. | -Repeat this sequence after reconnecting. `Last-Event-ID` does not replay missed events. A closed stream does not establish whether the run finished. +Open the stream first, then read state and every item page. Match updates by input, run, and item IDs. Replace temporary text with its saved item to avoid duplicates. -When submitting an input with `stream: true`, save the initial `input.accepted` receipt before processing progress. It contains the IDs needed for recovery. +After a disconnection, repeat that sequence. Continue checking saved state periodically while work is active. `Last-Event-ID` does not replay missed events. -## Render progress accurately + + ```bash + curl --no-buffer --fail-with-body --silent --show-error \ + "$FLUSO_API_URL/threads/$thread_id/events" \ + -H "Authorization: Bearer $FLUSO_API_TOKEN" + ``` -Tool progress includes `progress.tool_start` and `progress.tool_end`. These describe a tool's execution. They are not instructions for your client to execute that tool. + The response uses Server-Sent Events (SSE). Each `data` field contains a `ThreadEvent` JSON object. A blank line ends a frame. -A tool preview can omit arguments or results. `args_truncated: true` means the arguments are incomplete. If the flag is absent, completeness is unknown. Use the pending approval's actual arguments when asking a user to approve work. + Buffer incomplete frames across network chunks. Ignore unknown event types and keepalive comments. Use a client that supports an Authorization header. Keep tokens out of URLs. -For a daemon behind a proxy, disable response buffering for the event stream. + If you use `stream: true` during input submission, save the first `input.accepted` receipt. It contains the IDs for recovery if the stream fails. -Ignore unknown event types and keepalive comments. Buffer incomplete frames across network chunks. Use a streaming client that supports an Authorization header. Do not put tokens in the URL. + For a daemon behind a proxy, disable response buffering for this stream. + + + + `progress.tool_start` and `progress.tool_end` describe tool execution. Your client displays these events. It does not execute the tool itself. + + A preview can omit arguments or results. `args_truncated: true` means the arguments are incomplete. If that flag is absent, completeness is unknown. + + For [tool approval](/api/workflows/connections#handle-a-waiting-run), fetch the pending action's actual arguments before asking the user to decide. + - + | Method and path | Use | | --- | --- | | `GET /v1/threads/{thread_id}/events` | Stream new events. Optional `input_id` follows one input. | - | `GET /v1/threads/{thread_id}/state` | Recover active work, queues, and pending actions. | - | `GET /v1/threads/{thread_id}/items` | Recover saved messages and tool results. Follow all pages. | + | `GET /v1/threads/{thread_id}/state` | Recover active work, queues, partial output, and pending actions. | + | `GET /v1/threads/{thread_id}/items` | Recover saved messages and tool results. Follow every page. | - Your workspace's `/v1/examples/watch.mjs` combines these operations. It requires Node 22 or later. Run it with the quickstart's `thread_id` and optional `run_id`. + The watcher's run ID is optional. Supply it to check the run even when it has no saved messages. A closed stream does not establish whether work finished. diff --git a/content/docs/api/workflows/usage.mdx b/content/docs/api/workflows/usage.mdx index 6d046b0..794829f 100644 --- a/content/docs/api/workflows/usage.mdx +++ b/content/docs/api/workflows/usage.mdx @@ -1,30 +1,45 @@ --- -title: Usage -sidebarTitle: Usage +title: Check usage icon: chart-no-axes-combined -description: Read recorded model usage for a workspace or one run. +description: See what a run used, then inspect the model calls behind it. --- -Usage reports describe recorded activity. They can arrive after execution and are not a final bill. A missing report or unknown price does not mean zero cost. +Use the saved conversation and run IDs to inspect the cost of a task. The examples use the Bash shell from [your first request](/api/first-request). -## Read workspace totals +## Check one run -Use `GET /v1/token-usage` for account totals and its session summaries. Public response fields use snake_case, such as `last_updated` and `entry_count`. +```bash +api "/threads/$thread_id/runs/$run_id/usage" | jq . +``` -## Inspect a run +The response contains `tokens`, estimated `cost`, and `entry_count`. A response of `null` means no usage was reported yet. A null `cost` means pricing is unknown, not zero. -`GET /v1/threads/{thread_id}/runs/{run_id}/usage` returns the run's recorded tokens and estimated cost. It returns `null` when no usage is reported. Cost can also be null when a recorded call has no price. +## See which calls contributed -For individual records, use `GET /v1/token-usage/entries` with `thread_id` and `run_id`. Other filters include `provider`, `model`, `since`, and `limit`. +Filter individual usage records to that run: ```bash -curl --fail-with-body --silent --show-error --get \ - "$FLUSO_API_URL/token-usage/entries" \ - -H "Authorization: Bearer $FLUSO_API_TOKEN" \ +api /token-usage/entries --get \ --data-urlencode "thread_id=$thread_id" \ - --data-urlencode "run_id=$run_id" + --data-urlencode "run_id=$run_id" | jq . +``` + +Use the records to compare model calls within the task. Further filters include `provider`, `model`, `since`, and `limit`. + +## Read workspace totals + +```bash +api /token-usage | jq '{totals, last_updated}' ``` -Set the API URL, token, and IDs through the [quickstart](/api/first-request) first. Read access is sufficient for usage queries. They do not start workspace compute. +Reports can arrive after execution. They are recorded estimates, not a final bill. For a daemon using your provider key, check that provider's billing too. + + + | Method and path | Use | + | --- | --- | + | `GET /v1/token-usage` | Workspace totals and session summaries. | + | `GET /v1/token-usage/entries` | Individual records with optional filters. | + | `GET /v1/threads/{thread_id}/runs/{run_id}/usage` | One run's recorded usage, or `null`. | -Use your [workspace's endpoint reference](/api#choose-your-workspace) for all fields and filters. For daemon inference charges, check the model provider's billing as well. + Read access is sufficient. These queries do not start workspace compute. Responses use snake_case fields, including `last_updated` and `entry_count`. + diff --git a/content/docs/api/workflows/workspace.mdx b/content/docs/api/workflows/workspace.mdx index 2872402..a9157cd 100644 --- a/content/docs/api/workflows/workspace.mdx +++ b/content/docs/api/workflows/workspace.mdx @@ -4,11 +4,19 @@ icon: folder-cog description: Inspect skills, organize labels, and manage runtime readiness. --- -These operations support your conversations and Agents. They use the same Cloud account or daemon workspace as your other requests. +Use these operations to choose skills, organize conversations, and check whether the workspace can run work. + +The examples use the authenticated `api` helper from [your first request](/api/first-request). ## Choose skills for an Agent -Read `GET /v1/agent/skills` to find available skill names. For example, inspect a document skill before selecting it for a meeting assistant. +List the skills already available in this workspace: + +```bash +api /agent/skills | jq '.data' +``` + +For a meeting assistant, look for a skill that works with your document format. Use its exact name when updating the [Agent's configuration](/api/workflows/agents#improve-its-instructions). Built-in skills are already available. For a marketplace skill, read its detail and install it first. Then save its name in the [Agent's configuration](/api/workflows/agents). @@ -32,7 +40,27 @@ Selecting a missing skill does not install it. Removing a skill does not remove ## Organize with labels -Create a label, then use its returned ID in a project's or thread's `label_ids`. For example, one **Customer work** label can group related conversations and projects. +For example, mark the quickstart conversation for review. Create a label first: + +```bash +label=$(api /agent/labels \ + -H 'Content-Type: application/json' -d '{"name":"API review"}') && +label_id=$(printf '%s' "$label" | jq -er '.id') +``` + +If that label already exists, reuse its ID from `GET /v1/agent/labels`. After a lost creation response, check that list before creating another. + +Add the label to the conversation without removing its existing labels: + +```bash +thread=$(api "/threads/$thread_id") && +label_body=$(printf '%s' "$thread" | jq --arg id "$label_id" \ + '{label_ids: ((.label_ids // []) + [$id] | unique)}') && +api "/threads/$thread_id" -X PATCH \ + -H 'Content-Type: application/json' -d "$label_body" +``` + +The updated conversation now includes this label's ID. Projects use the same `label_ids` field. Read the latest object before changing its labels. | Method and path | Use | @@ -45,7 +73,15 @@ Create a label, then use its returned ID in a project's or thread's `label_ids`. ## Check runtime readiness -Input submission starts the runtime when needed. Your integration usually needs no separate wake loop. +Input submission starts the runtime when needed. To check readiness without sending a message: + +```bash +api /agent/runtime/status | jq '{status, busy, can_wake, can_stop}' +``` + +Use `can_wake` and `can_stop` when offering those controls. A busy runtime can refuse a stop. Stop active work through its thread or run first. Respect `Retry-After` on temporary readiness errors. + + | Method and path | Use | | --- | --- | @@ -53,7 +89,7 @@ Input submission starts the runtime when needed. Your integration usually needs | `POST /v1/agent/runtime/wake` | Start if needed, then wait for readiness without sending a chat message. | | `POST /v1/agent/runtime/stop` | Release compute while keeping saved workspace data. | -A busy runtime can refuse a stop. Stop active work through its thread or run first. Respect `Retry-After` on temporary readiness errors. + `POST /v1/agent/workspace/reset` is destructive. It clears conversations, projects, workspace files, and installed or custom workspace skills. It also retires schedules. diff --git a/content/docs/deployment-models.mdx b/content/docs/deployment-models.mdx index d011922..0992015 100644 --- a/content/docs/deployment-models.mdx +++ b/content/docs/deployment-models.mdx @@ -71,4 +71,4 @@ Traces can include prompt and response text. For a dedicated or on-premises depl - Treat Azure VNet as future availability, not a current option. - Scope on-premises only after Fluso validates the infrastructure, residency, provider, telemetry, and support requirements. -For a custom deployment, [contact Fluso](mailto:sales@premai.io). For the component boundaries behind these models, read [Architecture](/api/architecture). +For a custom deployment, [contact Fluso](mailto:sales@premai.io). diff --git a/public/_redirects b/public/_redirects index f4c401b..e8391c6 100644 --- a/public/_redirects +++ b/public/_redirects @@ -37,9 +37,9 @@ /developers/export-import-and-share-agents /developers/guides/sharing 301 /developers/export-import-and-share-agents/ /developers/guides/sharing 301 /llms.mdx/docs/developers/export-import-and-share-agents/content.md /llms.mdx/docs/developers/guides/sharing/content.md 301 -/developers/architecture /api/architecture 301 -/developers/architecture/ /api/architecture 301 -/llms.mdx/docs/developers/architecture/content.md /llms.mdx/docs/api/architecture/content.md 301 +/developers/architecture /api 301 +/developers/architecture/ /api 301 +/llms.mdx/docs/developers/architecture/content.md /llms.mdx/docs/api/content.md 301 /workspaces/macos /workspaces/setup 301 /workspaces/macos/ /workspaces/setup 301 /workspaces/linux /workspaces/setup 301 @@ -70,6 +70,9 @@ /developers/api-ref/sharing /api/cloud/sharing 301 /developers/api-ref/sharing/ /api/cloud/sharing 301 /llms.mdx/docs/developers/api-ref/sharing/content.md /llms.mdx/docs/api/cloud/sharing/content.md 301 -/developers/api-ref/architecture /api/architecture 301 -/developers/api-ref/architecture/ /api/architecture 301 -/llms.mdx/docs/developers/api-ref/architecture/content.md /llms.mdx/docs/api/architecture/content.md 301 +/developers/api-ref/architecture /api 301 +/developers/api-ref/architecture/ /api 301 +/llms.mdx/docs/developers/api-ref/architecture/content.md /llms.mdx/docs/api/content.md 301 +/api/architecture /api 301 +/api/architecture/ /api 301 +/llms.mdx/docs/api/architecture/content.md /llms.mdx/docs/api/content.md 301 From 04ceca31c6c1bcbd499a5fd45aafc7806fabfd86 Mon Sep 17 00:00:00 2001 From: biswaroop1547 Date: Sun, 11 Oct 2026 23:02:58 +0200 Subject: [PATCH 7/7] docs: clarify API availability across releases --- content/docs/api/index.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/docs/api/index.mdx b/content/docs/api/index.mdx index db76408..8d3a848 100644 --- a/content/docs/api/index.mdx +++ b/content/docs/api/index.mdx @@ -33,7 +33,7 @@ For a remote daemon, use its HTTPS address or an [SSH tunnel](/workspaces/setup# Open **Settings → Developer tokens → API reference** in Cloud. On a daemon's host, open `http://127.0.0.1:4111/docs`. -Each workspace serves its full endpoint reference at `/docs` and its OpenAPI schema at `/v1/openapi.json`. These describe the fields and operations available in that deployment. The reference also supplies runnable examples under `/v1/examples/`. The daemon serves its schema and examples locally. Its interactive reference needs internet access to load the viewer. +Releases with this API serve the endpoint reference at `/docs` and the OpenAPI schema at `/v1/openapi.json`. These describe the fields and operations available in that deployment. The reference also supplies runnable examples under `/v1/examples/`. The daemon serves its schema and examples locally. Its interactive reference needs internet access to load the viewer. The guides below explain how to combine those operations. Endpoint tables sit inside each guide, after the main workflow. @@ -45,7 +45,7 @@ The guides below explain how to combine those operations. Endpoint tables sit in Pair a client with your own workspace. - Continue work and retrieve the complete answer. + Continue work and read the complete answer. Handle a timeout without starting the same work twice. @@ -53,5 +53,5 @@ The guides below explain how to combine those operations. Endpoint tables sit in
- These guides cover the current public `/v1` API. If your app lacks **Developer tokens**, contact [support](mailto:support@premai.io) about availability for your release. + If your app lacks **Developer tokens** or its API reference returns 404, contact [support](mailto:support@premai.io) about availability for your release.