diff --git a/core/http/endpoints/addons/apps_test.go b/core/http/endpoints/addons/apps_test.go index ecbf1255fc..6189b0ccdf 100644 --- a/core/http/endpoints/addons/apps_test.go +++ b/core/http/endpoints/addons/apps_test.go @@ -24,6 +24,7 @@ import ( "github.com/mudler/LocalAI/core/services/addons/apps" "github.com/mudler/LocalAI/core/services/addons/catalog" "github.com/mudler/LocalAI/core/services/addons/gateway" + "github.com/mudler/LocalAI/core/services/addons/hostops" "github.com/mudler/LocalAI/core/services/addons/packages" "github.com/mudler/LocalAI/pkg/addons/contract" . "github.com/onsi/ginkgo/v2" @@ -388,7 +389,7 @@ var _ = Describe("apps routes outcomes and documentation", Label("apps"), func() Expect(json.Unmarshal(b, &m)).To(Succeed()) return m } - full := apps.App{ID: "a", Name: "a", Title: "t", Titles: map[string]string{"en": "t"}, Description: "d", Descriptions: map[string]string{"en": "d"}, Icon: "i", Version: "1", InstalledAt: "2026-10-30T09:15:00Z", Generation: 1, Enabled: true, State: "running", Update: "update-pending", MainPage: "/m", HasUI: true, LaunchMode: "always", Tools: []string{"x"}, ToolCount: 1, Shadows: []string{"bash"}, ToolDefinitions: []contract.ToolDefinition{{Name: "x", Description: "d", Kind: "read", InputSchema: json.RawMessage(`{"type":"object"}`), UIOnly: true, Shadows: "bash", Execution: &contract.ExecutionDescriptor{Class: "commands", Title: "T", Note: "n", Skills: &contract.ExecutionSkills{ProvidesRuntimeFor: []string{"documents"}}}}}, Skills: []contract.SkillDefinition{{Name: "xlsx", Shadows: "xlsx"}}, SkillShadows: []string{"xlsx"}, UI: &apps.UIView{Entry: "/e", CSS: []string{"/c"}, SDK: contract.VersionRange{Min: 1, Max: 1}, Actions: []apps.ChatActionView{{ID: "a", Label: "A", Labels: map[string]string{"en": "A"}, Icon: "star", PackagedIcon: true, Placement: "composer", Tool: "t", Component: "C"}}, Slots: []apps.SlotView{{ID: "s", Slot: "files.preview", Component: "C", Extensions: []string{"docx"}}}, Renderers: []contract.Renderer{{Kind: "approval-card", Tool: "t", Component: "C"}}, Pages: []string{"p"}, Navigation: []string{"p"}, ChatActions: []string{"a"}, Controls: []string{"network-policy"}}, Admin: &apps.AdminView{CanStart: true, CanStop: true, ManagementURL: "/x"}} + full := apps.App{ID: "a", Name: "a", Credentialed: true, ContextFiles: []contract.ContextFile{{Scope: "user", Path: "Memory/index.md"}}, Network: &apps.NetworkView{Kind: contract.NetworkKindEgressCheck, Title: "N", Presets: []contract.NetworkPreset{{ID: "off", Title: "Closed"}}}, Title: "t", Titles: map[string]string{"en": "t"}, Description: "d", Descriptions: map[string]string{"en": "d"}, Icon: "i", Version: "1", InstalledAt: "2026-10-30T09:15:00Z", Generation: 1, Enabled: true, State: "running", Update: "update-pending", MainPage: "/m", HasUI: true, LaunchMode: "always", Tools: []string{"x"}, ToolCount: 1, Shadows: []string{"bash"}, ToolDefinitions: []contract.ToolDefinition{{Name: "x", Description: "d", Kind: "read", InputSchema: json.RawMessage(`{"type":"object"}`), UIOnly: true, Approval: contract.ApprovalAlways, Shadows: "bash", Execution: &contract.ExecutionDescriptor{Class: "commands", Title: "T", Note: "n", Skills: &contract.ExecutionSkills{ProvidesRuntimeFor: []string{"documents"}}}}}, Skills: []contract.SkillDefinition{{Name: "xlsx", Shadows: "xlsx", Slot: &contract.SlotDefinition{Kind: contract.SlotKindDocuments}}}, SkillShadows: []string{"xlsx"}, UI: &apps.UIView{Entry: "/e", CSS: []string{"/c"}, SDK: contract.VersionRange{Min: 1, Max: 1}, Actions: []apps.ChatActionView{{ID: "a", Label: "A", Labels: map[string]string{"en": "A"}, Icon: "star", PackagedIcon: true, Placement: "composer", Tool: "t", Component: "C"}}, Slots: []apps.SlotView{{ID: "s", Slot: "files.preview", Component: "C", Extensions: []string{"docx"}}}, Renderers: []contract.Renderer{{Kind: "approval-card", Tool: "t", Component: "C"}}, Pages: []string{"p"}, Navigation: []string{"p"}, HomeCommands: []apps.HomeCommandView{{ID: "h", Page: "p", Label: "H", Icon: "star"}}, ModelActions: []contract.ModelAction{{Capability: "FLAG_IMAGE", Page: "p"}}, ChatActions: []string{"a"}, Controls: []string{"network-policy"}}, Admin: &apps.AdminView{CanStart: true, CanStop: true, ManagementURL: "/x", HostOperations: &hostops.RequestSummary{Required: []string{"r"}}}} for name, v := range map[string]any{"apps.App": full, "apps.AdminView": *full.Admin, "apps.UIView": *full.UI, "apps.ChatActionView": full.UI.Actions[0], "apps.SlotView": full.UI.Slots[0], "contract.Renderer": full.UI.Renderers[0], "contract.VersionRange": full.UI.SDK, "contract.ToolDefinition": full.ToolDefinitions[0], "contract.ExecutionDescriptor": *full.ToolDefinitions[0].Execution, "contract.ExecutionSkills": *full.ToolDefinitions[0].Execution.Skills, "contract.SkillDefinition": full.Skills[0], "apps.List": apps.List{Apps: []apps.App{full}}} { def, ok := doc.Definitions[name] Expect(ok).To(BeTrue(), name) diff --git a/docs/content/features/addons.md b/docs/content/features/addons.md index 6aa1a7e190..dc566f5f95 100644 --- a/docs/content/features/addons.md +++ b/docs/content/features/addons.md @@ -140,10 +140,10 @@ and reconciling the operation before further use. These declarations extend the strict manifest contract. They keep `schemaVersion: 1`, host/protocol/data-format level 1 and UI SDK 2. The manifest -`api` range is 1 to 2: the execution descriptor, the resource listing, the network capability, the slot of a skill and the legacy arguments of a tool below need 2. +`api` range is exactly 2 (`"api": {"min": 2, "max": 2}`). A manifest with another range, or with none, is refused at install. The Go contract parser is authoritative; there is no separate JSON Schema. The fragments below merge into an otherwise valid manifest, not a complete -installable package. Existing executable, identity, compatibility and UI +installable package. The executable, identity, compatibility and UI entry/SDK fields remain required. ### Home page commands @@ -288,23 +288,9 @@ list and rejects its pending responses, even before effect cleanup. A new list request can populate the refreshed session. These in-memory scopes contain no credentials. -### Legacy arguments of a tool - -Needs manifest `api` 2. A tool can name arguments that an older version took -and that it refuses now. When a call fails the schema check and carries one of -them, the host answers with the hint instead of a bare refusal. The argument is -never forwarded, and the schema must not declare it. - -```json -{ "name": "create-docx", "legacyArgs": [ { "name": "blocks", "hint": "use the markdown string" } ] } -``` - -The answer is `blocks is no longer supported; use the markdown string`. At most -8 entries; the hint is one plain sentence of at most 200 characters. - ### Execution descriptor of a commands tool -Needs manifest `api` 2. On the definition of a tool that shadows `bash`: +On the definition of a tool that shadows `bash`: ```json { @@ -340,10 +326,8 @@ the chat's commands and its status reports `isolated`. When the status is out and the chat says why. A tool descriptor may leave `providesRuntimeFor` out to opt out of that check, because its skills do not need its commands. -The field is optional and does not change the protocol. A manifest that -declares a descriptor must accept API 2 (`"api": {"min": 2, "max": 2}`). The -host speaks API 2 and still accepts add-ons that declare `{1,1}`. An add-on -whose commands tool declares no descriptor works as before: the host treats it +The field is optional and does not change the protocol. An add-on +whose commands tool declares no descriptor still works: the host treats it as a provider of commands under the add-on's own title, with no note, and keeps the isolation check for its document skills. The host never compares the add-on ID with a fixed name for any of this, so a second add-on that runs commands gets @@ -400,13 +384,10 @@ host settings, not settings of one add-on. The setting `chats_host_exec`, which lets the host run commands when no add-on provides `bash`, is also a host setting and names no add-on. -A manifest that declares no `network` works as before. The host attaches the -control to an add-on that asks for `network-policy` in `ui.controls`, is granted -`egress.check` and declares a tool that replaces `bash`; it names the network -by the title of that tool. This is the shape of the manifests written before -this field existed. The field is optional and does not change the protocol: the -manifest must accept API 2 (`"api": {"min": 2, "max": 2}`), and the host still -accepts add-ons that declare `{1,1}`. +The host attaches the control to an add-on that asks for `network-policy` in +`ui.controls`, declares `network` and is granted `egress.check`. A manifest +that declares no `network` gets no network control, whatever tools it replaces. +The field does not change the protocol. ### Requested host operations are not grants @@ -461,20 +442,12 @@ The shipped deployment policy covers every official required operation. The package tests compare all twelve manifests with this source audit and the host's registered vocabulary. Their package-builder fixtures use temporary executable and UI bytes, not official release binaries or frontend build artifacts. -### Compatibility and release packaging +### Release packaging -New hosts accept old manifests without these fields. Old hosts reject unknown -extension fields during staging. Extension-bearing packages therefore require -the host release introducing these contracts; document that minimum LocalAI -release beside each addon release. An older host must retain its installed -package on refusal. Do not add automatic install retries or promise new error -wording from old binaries. - -Catalog version ranges do not negotiate individual fields and cannot guarantee -eligibility before staging. SDK 2 does not signal support for these manifest -extensions. If older hosts need support, release maintainers must decide whether -and how long to publish separate legacy packages. These declarations alone do -not install packages, grant operations or activate runtime/UI integrations. +Every package declares manifest `api` 2, which is the only version the host +speaks. Catalog version ranges do not negotiate individual fields and cannot +guarantee eligibility before staging. These declarations alone do not install +packages, grant operations or activate runtime/UI integrations. ### Resource listing @@ -1483,7 +1456,6 @@ and `library` is the hint it gives for a script: provider reports isolated execution. At most 8 extensions (lower case, with the dot) and 8 words. A slot may not be the name of a protected core skill. -Existing Office packages with these declarations remain compatible; you do not need to reinstall them to change naming metadata. Unknown unprefixed names remain invalid. Ordinary add-on skills keep the add-on namespace rule. - **In a chat.** `load_skill xlsx` returns the eligible provider's instructions. @@ -1762,7 +1734,7 @@ refresh time and prevents stale cached releases from resolving successfully. ### Catalog document and limits A catalog (the library index) is one YAML document: a block list of releases with -`id` (an index written before this name may still use `addonId`, which is read for one release), `name`, optional `description`, `version`, `artifact` +`id`, `name`, optional `description`, `version`, `artifact` (`oci://host/repository`), `digest` (`sha256:` plus 64 lowercase hex characters), `os` and `arch`. It uses the same format as the model and backend gallery indexes: @@ -2154,8 +2126,8 @@ person to confirm. A verified manifest can request exact operation names with `capabilities.hostOperations.required` and `.optional`. These are requests, **not grants**. The owner compares them with the host operation vocabulary -and the immutable host policy selected at startup. Legacy manifests without -requests retain their configured behavior. +and the immutable host policy selected at startup. A manifest without +requests keeps its configured behavior. Coverage uses the broker's literal, nonempty prefix rule. For example, `workspace.` covers the requested name `workspace.materialize`; @@ -2316,10 +2288,11 @@ with the same bearer credential and incarnation header as every other route and `{"op", "kind", "token", ...}`. The ops are `poll` (`offset`, `timeoutSeconds` up to 25), `send` (`chat`, `thread`, `text`, and optionally `messageId`), `edit` (`chat`, `messageId`, `text`), `typing` (`chat`, `thread`, and optionally `messageId`), -`send_file` (`chat`, `thread`, `file`: `{"name", "mime", "data"}`), `me` (who the bot +`send_file` (`chat`, `thread`, `transfer`: the handle of the file, see +[Moving a file by handle](#moving-a-file-by-handle)), `me` (who the bot is, for the token check on save and the Test button), `media` (`media`: one -reference of a message's `media`; the answer is `{"file": {"name", "mime", "data"}}` -with at most `contract.MaxConnectorFileBytes`, 1 MiB, of data) and, for a kind that declares `drafts`, `draft` +reference of a message's `media`, and a `transfer` handle; the answer is +`{"received": {"name", "mime"}}`) and, for a kind that declares `drafts`, `draft` (`chat`, `thread`, `draftId`, `text`). - **Options.** A `poll` carries `options`, a map from each declared option id to the @@ -2362,25 +2335,25 @@ with at most `contract.MaxConnectorFileBytes`, 1 MiB, of data) and, for a kind t once, as before. - A `typing` with a `messageId` says that the host already shows a working message with that ID. A platform without a typing indicator (Slack) then posts nothing. -- `send_file` carries one file of 1 byte to 1 MiB whose `name` is +- `send_file` carries a `transfer` whose `name` is `contract.SafeFileName` of itself (no path, no control characters, at most 128 - bytes). The route refuses any other file, and a platform that cannot send files. + bytes). The route refuses any other file, a call without a handle, and a platform + that cannot send files. The answer names the message (Telegram) or the file (Slack) in `messageId`. The token is the unsealed bot token of one binding. It is passed on each call and the add-on must not -write it anywhere. The answer is `{"updates", "nextOffset", "messageId", "botId", "botUsername", "file"}` or +write it anywhere. The answer is `{"updates", "nextOffset", "messageId", "botId", "botUsername", "received"}` or `{"error": {"code", "retryAfter"}}` with a code of `unauthorized`, `conflict`, `rate_limited`, `too_large` or `failed`. The host maps `unauthorized` to switching the binding off, repeated `conflict` to the same, and `rate_limited` to a wait. `too_large` is the -answer of a `media` whose file is larger than 1 MiB or than the platform lets a bot +answer of a `media` whose file is larger than the bound of the handle or than the platform lets a bot download; the host tells the sender that the file is too large. A runtime that implements `contract.ConnectorRuntime` has the route; others get a 404. The add-on cuts a long `text` to the limit of its platform. ### File abilities and limits -The bytes of a file travel inside the connector envelope, as base64 in JSON. The -envelope is at most 2 MiB, so the contract caps a file at -`contract.MaxConnectorFileBytes` (1 MiB). That is the *inline cap*. A kind declares +The bytes of a file never travel inside the connector envelope: they move by a +handle (see [Moving a file by handle](#moving-a-file-by-handle)). A kind declares what its platform can move, and the host decides how large a file may be, from the number and the layer that sets it: @@ -2389,34 +2362,31 @@ number and the layer that sets it: "id": "telegram", "files": { "receive": { "maxBytes": 20971520, "types": ["image/*", "application/pdf"] }, - "send": { "maxBytes": 52428800 }, - "transfer": true + "send": { "maxBytes": 52428800 } } }] ``` -Add the field only with manifest `"api": {"min": 2, "max": 2}` (or a range that -reaches 2); a manifest that declares `files` with API 1 is refused. Both directions are optional, but `files` -needs at least one. `maxBytes` is 1 to 1 TiB. `types` is up to 32 distinct media types -such as `application/pdf` or `image/*`; it says what the kind moves. For a kind with `transfer`, the host -refuses an inbound file whose type the add-on states and the list does not allow, and -does not send an outbound file whose type (from its extension) is not in the list. The +A kind that declares no `files` moves no files: the host does not ask it for +one and tells a sender who attaches a file that the platform takes none. Both +directions are optional, but `files` needs at least one. `maxBytes` is 1 to 1 TiB. +`types` is up to 32 distinct media types such as `application/pdf` or `image/*`; +it says what the kind moves. The host refuses an inbound file whose type the add-on +states and the list does not allow, and does not send an outbound file whose type +(from its extension) is not in the list. The host also never sends a file whose first bytes are an HTML page. The host uses a declared size as an upper bound that the owner can lower. It does not prove that a transfer works. -The limit in effect for one direction of one binding is the smallest of four layers: +The limit in effect for one direction of one binding is the smallest of three layers: | Layer | Set by | Named in the text to people as | |---|---|---| | Owner | The owner of the bot, in the **Files** section of the binding. It can only lower the limit of the kind. | "limit set for this bot" | | Add-on | The kind, with `files.receive.maxBytes` or `files.send.maxBytes`. | "limit of" the kind's title | | Host | The owner policy key `connector_files_ceiling_bytes`; zero means the default, 100 MB (100 MiB). | "limit of the platform" | -| Inline | The cap of the inline path, `contract.MaxConnectorFileBytes`. Only a kind without `files.transfer` has it. | "limit of this add-on version" | -An add-on that declares nothing, or declares `files` without `transfer`, gets the inline -cap, whatever the other layers say, and keeps the inline path. A kind that declares -`"transfer": true` has no inline cap. When a layer ties, the text names the one -a person can change first (owner, add-on, host, inline). One function in core +When a layer ties, the text names the one +a person can change first (owner, add-on, host). One function in core (`EffectiveFileLimit`) decides this for every kind, so a connector never carries a size of its own, and the sentences that the sender and the model read are built from its result. An add-on that gets a file it cannot read answers `too_large`; the sender then @@ -2424,9 +2394,8 @@ reads the limit in effect. #### Moving a file by handle -`"transfer": true` says that the add-on moves file bytes by a handle, so a file larger -than the envelope can move, in both directions, with no more than one chunk in memory. -The add-on needs the host operation prefix `connector.` in its host policy, as it does +The add-on moves file bytes by a handle, in both directions, with no more than one +chunk in memory. The add-on needs the host operation prefix `connector.` in its host policy, as it does for `connector.inbound`. The host adds `transfer` to the request of the op `media` or `send_file`, and no bytes: @@ -2438,7 +2407,7 @@ The host adds `transfer` to the request of the op `media` or `send_file`, and no - **Inbound (`media`).** The add-on downloads the file and streams it with `connector.file.put` `{"handle", "offset", "data", "final"}`, at most 256 KiB of data - per call; it answers `{"received": {"name", "mime"}}` and no `file`. An `offset` that is + per call; it answers `{"received": {"name", "mime"}}`. An `offset` that is not the number of bytes the host has gets `{"received": n}` and nothing is written, so the add-on resumes there. The host stops at `maxBytes` (HTTP 413, code `too_large`). The host overwrites the size and the SHA-256 of `received` with its own record, so @@ -2456,8 +2425,8 @@ its partial bytes. It also ends after two minutes with no call. Where the add-on in a cluster the frontend and the add-on node share objects and need no shared disk. Outbound, the frontend checks the access of the chat to the file before it makes the handle and gives the owner the hash and size of that content only. Do not use a -`connector.file.*` call outside a connector call. An add-on that does not declare -`transfer` is never sent a handle. +`connector.file.*` call outside a connector call. An add-on that declares no `files` +is never sent a handle. The SDK of the first-party add-ons (`addons/internal/connector`) has `Put` and `Get`; a platform implements `StreamReader` and `StreamSender` and keeps only its own HTTP @@ -2658,7 +2627,7 @@ An add-on declares its sources in the manifest instead, as `capabilities.context } ``` -The field needs manifest API 2. An add-on declares at most three files, one for each +An add-on declares at most three files, one for each scope. Every path lies under `Memory/`, ends in `.md` and holds no parent segment; an add-on cannot name any other part of the home. `cap` can only lower the 2 KiB cap. A declaration gives the add-on no access: the host reads the files as the user, and the diff --git a/docs/content/features/chats-connectors.md b/docs/content/features/chats-connectors.md index 4365ed8053..60b3f47a99 100644 --- a/docs/content/features/chats-connectors.md +++ b/docs/content/features/chats-connectors.md @@ -716,8 +716,7 @@ restart). The JSON store is for one process. ### For add-on authors: sending files A connector add-on does not choose which files reach a person. The host does, and it -sends each one with the typed `send_file` op: the file is in the request (`name`, `mime` -and at most 1 MiB of `data`; a kind that declares `files.transfer` gets a `transfer` handle and no `data`, see [File abilities and limits](/features/addons/#file-abilities-and-limits)) with the chat and the thread of the message it answers. The +sends each one with the typed `send_file` op: the request carries a `transfer` handle for the bytes (`name`, `mime` and `size` of the file; see [File abilities and limits](/features/addons/#file-abilities-and-limits)) with the chat and the thread of the message it answers. The host chooses the files in two ways: - The files that the turn made or changed go on their own, after the answer. diff --git a/pkg/addons/contract/README.md b/pkg/addons/contract/README.md index b8184883ee..70fed3e3cf 100644 --- a/pkg/addons/contract/README.md +++ b/pkg/addons/contract/README.md @@ -7,16 +7,14 @@ The typed declarations remain additive strict v1 fields, not a new protocol vers - `capabilities.ui.homeCommands` names installed pages, never tools or install actions. - `capabilities.executionProvider` names execution tools and one declared UI-only read status tool. - `capabilities.hostOperations.required` and `.optional` request exact operation names, never grants. -- `capabilities.network` declares the egress policy of the commands an add-on runs (kind `egress-check`, a title, preset titles). It attaches the `network-policy` host control and needs manifest api 2. +- `capabilities.network` declares the egress policy of the commands an add-on runs (kind `egress-check`, a title, preset titles). It attaches the `network-policy` host control. `ValidHostOp` validates syntax only. The host's registered vocabulary determines support. Administrator policy and request admission remain authoritative, including for optional operations. Status is advisory; an isolation report does not prove network enforcement or authorize execution. See [the addon guide](../../../docs/content/features/addons.md#optional-typed-manifest-extensions) -for JSON examples, bounds, collision rules, lifecycle behavior, policy migration, and old-host staging refusal. -Old hosts must retain the old package when an extension-bearing upgrade is refused. -Release maintainers select the minimum supporting release and the duration of any legacy package support. +for JSON examples, bounds, collision rules, lifecycle behavior, and policy migration. Typed owner observations currently support local-owner JSON selections only. Named-user and SQL selections remain unavailable and fail closed; they are not completed scope. @@ -64,7 +62,7 @@ FLAG_UPSCALE FLAG_MOTION FLAG_FORECAST ## Context files -`capabilities.contextFiles` names up to three small index files (one for each scope: user, agent, channel) that the host loads into the prompt of a chat. It needs manifest api 2. +`capabilities.contextFiles` names up to three small index files (one for each scope: user, agent, channel) that the host loads into the prompt of a chat. ```json {"contextFiles":[{"scope":"user","path":"Memory/index.md"},{"scope":"agent","path":"Memory/agents/{agent}/index.md","cap":1024}]} diff --git a/scripts/addons/suites.json b/scripts/addons/suites.json index 2cb7d71262..e06b895c1b 100644 --- a/scripts/addons/suites.json +++ b/scripts/addons/suites.json @@ -177,6 +177,9 @@ "requires the main page to be one of the declared pages", "bounds localized titles and descriptions and rejects markup", "reserves the segments of the navigation routes", + "accepts only the manifest API of the host, exactly", + "accepts a declared listing", + "refuses a listing with a bad name, or that names a declared tool", "bounds model actions to one mapping per supported capability", "declares only owning model actions for existing addon destinations", "round trips declared model destinations without changing protocol or SDK", @@ -211,6 +214,7 @@ "too many segments", "long combined route", "rejects noncanonical modelActions entry keys and duplicate keys", + "is valid when the manifest lists the egress check", "validates every enum pair and normalizes nonavailable isolation", "missing isolation", "missing readiness", @@ -249,7 +253,7 @@ "keeps the file tools off the replaceable list", "accepts known controls and refuses an unknown or repeated one", "reads ui.controls from a manifest and refuses an unknown field in it", - "draws the network policy only for an add-on granted egress that runs bash", + "draws the network policy only for an add-on granted egress that declares a network", "serves a second add-on with the same controls, by its grant and not by its name", "draws the tool providers for an add-on that replaces any built-in tool", "Explanation markup", @@ -376,13 +380,11 @@ "accepts a pull kind with a declared page", "accepts drafts and a preview interval within the bounds", "lets an add-on declare nothing but the connector", - "accepts a thread history with the manifest api 2", - "refuses it under the manifest api 1", "refuses an ability that says nothing and a limit out of bounds", "serves nothing for a kind that declares nothing", "cuts the limit to what the kind declares", "accepts an option with a default among its values", - "refuses options under api 1, a bad default, one value, a repeated id and too many", + "refuses a bad default, one value, a repeated id and too many", "resolves the choices of a binding against the declaration", "no kind", "a bad kind id", @@ -396,49 +398,36 @@ "tells a settled status from a pending one", "keeps the wire version and omits empty fields", "has the limits the owner decided", - "is valid for a tool that shadows bash when the manifest accepts API 2", - "needs the manifest to accept API 2", "is refused on a tool that does not shadow bash, with another class or with bad text", - "keeps a manifest of API 1 valid on a host that speaks API 2, and refuses one that needs API 3", "names the document skills of a provider that declares it supplies their runtime", "fails closed for a commands tool without a descriptor", "names nothing for a descriptor that does not supply the runtime of documents", "names nothing for an add-on without a commands tool", "reads the default slots from data", "lets an add-on declare a slot the contract has never heard of", - "needs the manifest to accept API 2 to declare a slot", "keeps a skill that shadows a default slot and declares nothing working", "refuses a shadow of an unknown slot without a declaration, and bad declarations", - "answers with the hint when a call carries the old argument", - "says nothing without the argument, without a declaration or for a forbidden property", - "is validated: bounded, plain, not in the schema, and API 2", "reports network-policy enforcement only for an available isolated provider", "refuses a non-boolean enforcement claim", - "accepts a listing declared with API 2", - "refuses a listing without API 2, with a bad name, or that names a declared tool", "rejects unknown keys in the declaration", - "is valid when the manifest accepts API 2 and lists the egress check", - "needs a manifest that accepts API 2", "needs the egress check among the host operations", "refuses an unknown kind, a bad title and a bad or repeated preset", "reads capabilities.network from JSON and refuses an unknown field in it", "is valid with one file for each scope", - "needs a manifest that accepts API 2 to declare context files", "refuses a path outside the Memory folder, an unsafe path and a wrong placeholder", "refuses a cap that is negative or above the host's index cap", "refuses two files for one scope and more files than scopes", "reads capabilities.contextFiles from JSON and refuses an unknown field in it", + "is valid with a connector kind and read tools", "is drawn for a declared capability that holds the grant, with no bash tool", "is not drawn for a declared capability without the grant", - "is drawn for a manifest that declares no network and replaces bash, as before", - "is not drawn for a manifest that declares no network and does not replace bash", + "is not drawn for a manifest that declares no network, whether or not it replaces bash", "does not read the add-on ID", "gives the declared title of a preset and the host title for the others", "declares its network and keeps the control it asked for", - "keeps the control through its bash tool when an older manifest declares no network", + "loses the control when it declares no network", "accepts a kind that declares what it receives and sends", "accepts a kind that declares one direction only, or nothing", - "needs a manifest that accepts API 2 to declare files", "a limit of zero", "a negative limit", "a limit above the largest the host knows", @@ -710,8 +699,6 @@ "skips a backend entry on an add-on only reader and says so", "skips an unknown kind without reading its fields, and keeps the rest", "skips the reserved model kind in this phase", - "reports the id of a skipped entry, written as id or as the legacy addonId", - "reads a backend entry that still uses the legacy addonId, and refuses one that has both", "still refuses an unknown field on an entry of a known kind", "refuses a malformed kind", "reads every new field and keeps it", @@ -737,7 +724,6 @@ "is refused on an add-on with a digest, without a digest unless mutable, or on an archive", "reads an index of more than a thousand entries", "encodes and decodes an entry with the new fields unchanged", - "reads the legacy addonId as id, and refuses an entry that has both", "carries the built-in tools an add-on replaces, and refuses one that may not be replaced", "carries the built-in skills an add-on replaces, and refuses one that may not be replaced", "carries toolCount and tools from the index to the listing", @@ -872,7 +858,6 @@ "distinguishes healthy empty connector discovery from loss of runtime ownership", "executes enabled owner intent on startup and stops on disable and close without forwarder execution", "writes the owner and storage only, with no address", - "loads a file written in the old format with an address", "loads when only the address changed", "refuses a changed owner ID", "refuses a changed storage ID", @@ -1259,6 +1244,8 @@ "keeps the cause of an outage and still reports it as unavailable", "tells a definite refusal of the add-on apart from an outage", "keeps a refusal apart from an outage", + "refuses a handle for a kind that declared no files, and without a table", + "refuses a media or send_file call that carries no handle", "serves exactly one routing payload per request" ] }, @@ -2213,6 +2200,8 @@ "keeps the cause of an outage and still reports it as unavailable", "tells a definite refusal of the add-on apart from an outage", "keeps a refusal apart from an outage", + "refuses a handle for a kind that declared no files, and without a table", + "refuses a media or send_file call that carries no handle", "serves exactly one routing payload per request" ] }, @@ -2405,12 +2394,7 @@ "carries the declaration of any add-on, whatever its id", "carries nothing for an add-on that declares no resources", "does not alias the manifest", - "leaves an add-on with a mapping and no declaration without a listing", - "keeps the listing of a manifest written before the declaration existed", - "does not touch an old manifest of another add-on, or one that ships a tool of that name", - "never overrides a declaration", - "is not needed by the shipped add-on, which declares the listing itself", - "goes away when the host stops accepting manifests of API 1" + "leaves an add-on with a mapping and no declaration without a listing" ] }, { @@ -2470,8 +2454,7 @@ "reads the library on demand when the startup refresh has not filled the cache", "reads the library again when the startup refresh failed", "writes the owner and storage only, with no address", - "loads a file written in the old format with an address", - "loads when only the address changed", + "loads when only the address changed", "refuses a changed owner ID", "refuses a changed storage ID", "copies every capability field of the manifest, so a new field cannot be forgotten", diff --git a/swagger/docs.go b/swagger/docs.go index 0040188b23..70c87aa9d9 100644 --- a/swagger/docs.go +++ b/swagger/docs.go @@ -5365,7 +5365,7 @@ const docTemplate = `{ }, "/api/chats/conversion": { "get": { - "description": "At start the server converts the old chat page, agents, tasks, skills and Telegram connectors into chats, saved agents, triggers, skills and connector bindings. Triggers and connectors are made switched off. This lists the items that belong to you, with what was dropped and why; an administrator also gets the totals of all users. It never holds a token or the text of a prompt.", + "description": "At start the server converts the old chat page, agents, tasks, skills and connectors into chats, saved agents, triggers and skills. Triggers are made switched off; connectors are listed as dropped and are made again in the Connectors page. This lists the items that belong to you, with what was dropped and why; an administrator also gets the totals of all users. It never holds a token or the text of a prompt.", "tags": [ "chats" ], @@ -14952,6 +14952,10 @@ const docTemplate = `{ "$ref": "#/definitions/contract.ContextFile" } }, + "credentialed": { + "description": "Credentialed is true when the manifest declares credentials: the tools\nwork with a secret that the person keeps in a connector binding. A\nconnector chat holds its write tools to contract.ToolPermission.", + "type": "boolean" + }, "description": { "type": "string", "example": "Keep notes." @@ -15724,19 +15728,6 @@ const docTemplate = `{ } } }, - "contract.LegacyArgument": { - "type": "object", - "properties": { - "hint": { - "description": "Hint is one plain sentence on what to send instead, such as \"use the\nmarkdown string\".", - "type": "string" - }, - "name": { - "description": "Name is the old argument name. The tool's schema must not declare it.", - "type": "string" - } - } - }, "contract.ModelAction": { "type": "object", "properties": { @@ -15908,7 +15899,7 @@ const docTemplate = `{ "type": "string" }, "slot": { - "description": "Slot describes the slot this skill serves: the files it covers and the\nwords to say about them (see SlotDefinition). It needs Shadows and\nmanifest API 2. A skill that shadows a default slot may leave it out.", + "description": "Slot describes the slot this skill serves: the files it covers and the\nwords to say about them (see SlotDefinition). It needs Shadows. A skill that shadows a default slot may leave it out.", "allOf": [ { "$ref": "#/definitions/contract.SlotDefinition" @@ -15956,7 +15947,7 @@ const docTemplate = `{ "type": "string" }, "execution": { - "description": "Execution declares that this tool runs commands and what the host may\nsay about the provider. Only a tool that shadows bash may declare it, and\nthe manifest must then accept API 2 (see MinExecutionAPI).", + "description": "Execution declares that this tool runs commands and what the host may\nsay about the provider. Only a tool that shadows bash may declare it.", "allOf": [ { "$ref": "#/definitions/contract.ExecutionDescriptor" @@ -15971,13 +15962,6 @@ const docTemplate = `{ "description": "Kind is read, write, schema or create.", "type": "string" }, - "legacyArgs": { - "description": "LegacyArgs name arguments that an older version of the tool took and\nthat it refuses now, with a hint for the model (see LegacyArgument). The\nmanifest must accept API 2 (see MinLegacyArgsAPI).", - "type": "array", - "items": { - "$ref": "#/definitions/contract.LegacyArgument" - } - }, "name": { "description": "Name is one of capabilities.tools.", "type": "string" @@ -19907,7 +19891,7 @@ const docTemplate = `{ "type": "integer" }, "effective_bytes": { - "description": "EffectiveBytes is the largest file that moves now: the smallest of the\nhost ceiling, the declared ability, your limit and the inline cap.", + "description": "EffectiveBytes is the largest file that moves now: the smallest of the\nhost ceiling, the declared ability and your limit.", "type": "integer" }, "layer": { @@ -19923,10 +19907,6 @@ const docTemplate = `{ "description": "HostCeilingBytes is the ceiling of the server for a connector file, set\nby the add-on owner; no limit above it applies.", "type": "integer" }, - "inline_bytes": { - "description": "InlineBytes is the cap of the inline path. Until the add-on can stream\na file, nothing larger moves, whatever the other limits are.", - "type": "integer" - }, "receive": { "description": "Receive is the limit of the files that people send to the bot.", "allOf": [ @@ -20236,7 +20216,7 @@ const docTemplate = `{ } }, "kind": { - "description": "Kind is the platform, such as telegram. It is used to create a binding and\nignored by an update. Empty means telegram.", + "description": "Kind is the platform, as a connector add-on declares it (see the kinds\ncall). It is required to create a binding and ignored by an update.", "type": "string" }, "name": { diff --git a/swagger/swagger.json b/swagger/swagger.json index cda50ec015..d05caccd19 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -5362,7 +5362,7 @@ }, "/api/chats/conversion": { "get": { - "description": "At start the server converts the old chat page, agents, tasks, skills and Telegram connectors into chats, saved agents, triggers, skills and connector bindings. Triggers and connectors are made switched off. This lists the items that belong to you, with what was dropped and why; an administrator also gets the totals of all users. It never holds a token or the text of a prompt.", + "description": "At start the server converts the old chat page, agents, tasks, skills and connectors into chats, saved agents, triggers and skills. Triggers are made switched off; connectors are listed as dropped and are made again in the Connectors page. This lists the items that belong to you, with what was dropped and why; an administrator also gets the totals of all users. It never holds a token or the text of a prompt.", "tags": [ "chats" ], @@ -14949,6 +14949,10 @@ "$ref": "#/definitions/contract.ContextFile" } }, + "credentialed": { + "description": "Credentialed is true when the manifest declares credentials: the tools\nwork with a secret that the person keeps in a connector binding. A\nconnector chat holds its write tools to contract.ToolPermission.", + "type": "boolean" + }, "description": { "type": "string", "example": "Keep notes." @@ -15721,19 +15725,6 @@ } } }, - "contract.LegacyArgument": { - "type": "object", - "properties": { - "hint": { - "description": "Hint is one plain sentence on what to send instead, such as \"use the\nmarkdown string\".", - "type": "string" - }, - "name": { - "description": "Name is the old argument name. The tool's schema must not declare it.", - "type": "string" - } - } - }, "contract.ModelAction": { "type": "object", "properties": { @@ -15905,7 +15896,7 @@ "type": "string" }, "slot": { - "description": "Slot describes the slot this skill serves: the files it covers and the\nwords to say about them (see SlotDefinition). It needs Shadows and\nmanifest API 2. A skill that shadows a default slot may leave it out.", + "description": "Slot describes the slot this skill serves: the files it covers and the\nwords to say about them (see SlotDefinition). It needs Shadows. A skill that shadows a default slot may leave it out.", "allOf": [ { "$ref": "#/definitions/contract.SlotDefinition" @@ -15953,7 +15944,7 @@ "type": "string" }, "execution": { - "description": "Execution declares that this tool runs commands and what the host may\nsay about the provider. Only a tool that shadows bash may declare it, and\nthe manifest must then accept API 2 (see MinExecutionAPI).", + "description": "Execution declares that this tool runs commands and what the host may\nsay about the provider. Only a tool that shadows bash may declare it.", "allOf": [ { "$ref": "#/definitions/contract.ExecutionDescriptor" @@ -15968,13 +15959,6 @@ "description": "Kind is read, write, schema or create.", "type": "string" }, - "legacyArgs": { - "description": "LegacyArgs name arguments that an older version of the tool took and\nthat it refuses now, with a hint for the model (see LegacyArgument). The\nmanifest must accept API 2 (see MinLegacyArgsAPI).", - "type": "array", - "items": { - "$ref": "#/definitions/contract.LegacyArgument" - } - }, "name": { "description": "Name is one of capabilities.tools.", "type": "string" @@ -19904,7 +19888,7 @@ "type": "integer" }, "effective_bytes": { - "description": "EffectiveBytes is the largest file that moves now: the smallest of the\nhost ceiling, the declared ability, your limit and the inline cap.", + "description": "EffectiveBytes is the largest file that moves now: the smallest of the\nhost ceiling, the declared ability and your limit.", "type": "integer" }, "layer": { @@ -19920,10 +19904,6 @@ "description": "HostCeilingBytes is the ceiling of the server for a connector file, set\nby the add-on owner; no limit above it applies.", "type": "integer" }, - "inline_bytes": { - "description": "InlineBytes is the cap of the inline path. Until the add-on can stream\na file, nothing larger moves, whatever the other limits are.", - "type": "integer" - }, "receive": { "description": "Receive is the limit of the files that people send to the bot.", "allOf": [ @@ -20233,7 +20213,7 @@ } }, "kind": { - "description": "Kind is the platform, such as telegram. It is used to create a binding and\nignored by an update. Empty means telegram.", + "description": "Kind is the platform, as a connector add-on declares it (see the kinds\ncall). It is required to create a binding and ignored by an update.", "type": "string" }, "name": { diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 797e07c716..94082cc618 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -187,6 +187,12 @@ definitions: items: $ref: '#/definitions/contract.ContextFile' type: array + credentialed: + description: |- + Credentialed is true when the manifest declares credentials: the tools + work with a secret that the person keeps in a connector binding. A + connector chat holds its write tools to contract.ToolPermission. + type: boolean description: example: Keep notes. type: string @@ -822,18 +828,6 @@ definitions: type: string type: array type: object - contract.LegacyArgument: - properties: - hint: - description: |- - Hint is one plain sentence on what to send instead, such as "use the - markdown string". - type: string - name: - description: Name is the old argument name. The tool's schema must not declare - it. - type: string - type: object contract.ModelAction: properties: capability: @@ -973,8 +967,7 @@ definitions: - $ref: '#/definitions/contract.SlotDefinition' description: |- Slot describes the slot this skill serves: the files it covers and the - words to say about them (see SlotDefinition). It needs Shadows and - manifest API 2. A skill that shadows a default slot may leave it out. + words to say about them (see SlotDefinition). It needs Shadows. A skill that shadows a default slot may leave it out. type: object contract.SlotDefinition: properties: @@ -1017,8 +1010,7 @@ definitions: - $ref: '#/definitions/contract.ExecutionDescriptor' description: |- Execution declares that this tool runs commands and what the host may - say about the provider. Only a tool that shadows bash may declare it, and - the manifest must then accept API 2 (see MinExecutionAPI). + say about the provider. Only a tool that shadows bash may declare it. inputSchema: description: |- InputSchema is the JSON schema of the arguments object. It is an @@ -1028,14 +1020,6 @@ definitions: kind: description: Kind is read, write, schema or create. type: string - legacyArgs: - description: |- - LegacyArgs name arguments that an older version of the tool took and - that it refuses now, with a hint for the model (see LegacyArgument). The - manifest must accept API 2 (see MinLegacyArgsAPI). - items: - $ref: '#/definitions/contract.LegacyArgument' - type: array name: description: Name is one of capabilities.tools. type: string @@ -4070,7 +4054,7 @@ definitions: effective_bytes: description: |- EffectiveBytes is the largest file that moves now: the smallest of the - host ceiling, the declared ability, your limit and the inline cap. + host ceiling, the declared ability and your limit. type: integer layer: description: |- @@ -4086,11 +4070,6 @@ definitions: HostCeilingBytes is the ceiling of the server for a connector file, set by the add-on owner; no limit above it applies. type: integer - inline_bytes: - description: |- - InlineBytes is the cap of the inline path. Until the add-on can stream - a file, nothing larger moves, whatever the other limits are. - type: integer receive: allOf: - $ref: '#/definitions/schema.ChatsConnectorFileLimit' @@ -4327,8 +4306,8 @@ definitions: type: array kind: description: |- - Kind is the platform, such as telegram. It is used to create a binding and - ignored by an update. Empty means telegram. + Kind is the platform, as a connector add-on declares it (see the kinds + call). It is required to create a binding and ignored by an update. type: string name: type: string @@ -15646,11 +15625,11 @@ paths: /api/chats/conversion: get: description: At start the server converts the old chat page, agents, tasks, - skills and Telegram connectors into chats, saved agents, triggers, skills - and connector bindings. Triggers and connectors are made switched off. This - lists the items that belong to you, with what was dropped and why; an administrator - also gets the totals of all users. It never holds a token or the text of a - prompt. + skills and connectors into chats, saved agents, triggers and skills. Triggers + are made switched off; connectors are listed as dropped and are made again + in the Connectors page. This lists the items that belong to you, with what + was dropped and why; an administrator also gets the totals of all users. It + never holds a token or the text of a prompt. responses: "200": description: The report