refactor(addons): remove the shims for older add-on data

No release carried add-ons, so nothing reads older add-on data or
manifests. Delete the code that did.

- Connectors: the kind and the add-on of the Telegram connectors that
  core once carried are gone (LegacyAddon, KindTelegram, the stored
  binding normalization, the platform titles). A binding needs a kind,
  and its add-on is the one that serves the kind. The conversion of the
  old agent pool lists every connector as dropped instead of making a
  Telegram binding, and its notes say so.
- The library index reads only the id field of an entry, not addonId.
- A host control that has a capability block (the network policy) is
  attached by that block alone, not by a tool that replaces bash.
- The Office create tools no longer answer for the argument names of an
  earlier schema, and the identity file note about old addresses is
  gone.
- The known debt of the provider-name guard drops the entries of the
  conversion and of the resource listing shim, and its ceiling goes
  from 13 to 5.

Assisted-by: Claude:claude-sonnet-5-5 go test
This commit is contained in:
Ettore Di Giacinto committed 2026-11-03 16:56:49 +00:00
1 parent 1834d4fd72
commit 2a491cb2cf
38 files changed
+104 -303

No files matched your search

+1 -1
View File
@@ -46,7 +46,7 @@ var _ = Describe("the connector capability", func() {
kind := func() *ConnectorKind { return &m.Capabilities.Connector.Kinds[0] }
BeforeEach(func() { m.API = VersionRange{Min: 2, Max: 2} })
It("accepts a thread history with the manifest api 2", func() {
It("accepts a thread history", func() {
kind().History = &ConnectorHistory{Thread: true, MaxMessages: 50}
Expect(ValidateManifest(m, host())).To(Succeed())
Expect(kind().ServesThreadHistory()).To(BeTrue())
+1 -1
View File
@@ -36,7 +36,7 @@ var _ = Describe("The credential slot of an add-on", func() {
return h
}
It("is valid with API 2, a connector kind and read tools", func() {
It("is valid with a connector kind and read tools", func() {
Expect(ValidateManifest(manifest(nil), host())).To(Succeed())
})
It("needs a connector kind to hold the binding", func() {
+1 -2
View File
@@ -84,8 +84,7 @@ func CommandsTool(defs []ToolDefinition) (ToolDefinition, bool) {
// IsolatedSkills lists the skills of an add-on that need its own commands
// provider to report isolated execution: the document-slot skills of an
// add-on with a commands tool, unless its descriptor says it does not provide
// the runtime for documents. A commands tool without a descriptor (a manifest
// of API 1) counts as providing it, so such an add-on keeps the check.
// the runtime for documents. A commands tool without a descriptor counts as providing it, so such an add-on keeps the check.
func IsolatedSkills(defs []ToolDefinition, skills []SkillDefinition) []string {
tool, ok := CommandsTool(defs)
if !ok {
+10 -7
View File
@@ -39,9 +39,8 @@ type HostControl struct {
// block needs no shadowed tool.
Capability string
// Shadows is a built-in tool the add-on must replace with a valid tool,
// AnyShadow for any, or "" for none. A control with a Capability applies
// it only to an add-on that does not declare the block: a manifest
// written before the block existed keeps its attachment.
// AnyShadow for any, or "" for none. A control with a Capability is not
// attached by a shadowed tool: the add-on must declare the block.
Shadows string
}
@@ -59,7 +58,7 @@ func declares(c Capabilities, name string) bool {
// HostControls is the closed registry of host controls.
var HostControls = []HostControl{
{ID: ControlNetworkPolicy, Grant: "egress.check", Capability: CapabilityNetwork, Shadows: "bash"},
{ID: ControlNetworkPolicy, Grant: "egress.check", Capability: CapabilityNetwork},
{ID: ControlToolProviders, Shadows: AnyShadow},
}
@@ -115,8 +114,9 @@ func grantCovers(grants []string, op string) bool {
// EntitledControls returns the controls of the manifest that the host draws
// for it: those declared in ui.controls whose grant is among grants (the
// operation prefixes the host setting gives this add-on) and that the add-on
// attaches: it declares the control's capability block, or it replaces the
// built-in tool the control names with a valid tool. The order of the manifest is
// attaches: it declares the control's capability block, or, for a control
// without one, it replaces the built-in tool the control names with a valid
// tool. The order of the manifest is
// kept. A control the add-on is not entitled to is dropped, never an error:
// an administrator can remove a grant at any time.
func EntitledControls(m Manifest, grants []string) []string {
@@ -135,7 +135,10 @@ func EntitledControls(m Manifest, grants []string) []string {
continue
}
switch {
case c.Capability != "" && declares(m.Capabilities, c.Capability):
case c.Capability != "":
if !declares(m.Capabilities, c.Capability) {
continue
}
case c.Shadows == "":
case c.Shadows == AnyShadow:
if len(shadows) == 0 {
+12 -5
View File
@@ -4,6 +4,7 @@ package contract
import (
"encoding/json"
"os"
"slices"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -16,11 +17,16 @@ var _ = Describe("Host controls", func() {
// runner is an add-on that runs commands, with the controls it asks for.
// Any ID works: the host never looks at it.
runner := func(id string, controls ...string) Manifest {
return Manifest{SchemaVersion: 1, ID: id, Version: "1.0.0", Executable: "bin/" + id, OS: "linux", Arch: "amd64",
m := Manifest{SchemaVersion: 1, ID: id, Version: "1.0.0", Executable: "bin/" + id, OS: "linux", Arch: "amd64",
Host: VersionRange{Min: 1, Max: 1}, API: VersionRange{Min: 2, Max: 2}, Protocol: VersionRange{Min: 1, Max: 1}, DataFormat: VersionRange{Min: 1, Max: 1},
Capabilities: Capabilities{Actions: true, Tools: []string{"bash"},
ToolDefinitions: []ToolDefinition{{Name: "bash", Description: "Run a command.", Kind: ToolKindAction, InputSchema: json.RawMessage(bashSchema), Shadows: "bash"}},
UI: &UI{Entry: "ui/main.js", SDK: VersionRange{Min: 2, Max: 2}, Controls: controls}}}
if slices.Contains(controls, ControlNetworkPolicy) {
m.Capabilities.Network = &NetworkCapability{Kind: NetworkKindEgressCheck}
m.Capabilities.HostOperations = &HostOperations{Optional: []string{"egress.check"}}
}
return m
}
host := HostCompatibility{Host: 1, API: 2, Protocol: 1, SDK: HostSDKVersion, DataFormat: 1, OS: "linux", Arch: "amd64"}
@@ -48,15 +54,15 @@ var _ = Describe("Host controls", func() {
Expect(m.Capabilities.UI.Controls).To(Equal([]string{ControlToolProviders}))
})
It("draws the network policy only for an add-on granted egress that runs bash", func() {
It("draws the network policy only for an add-on granted egress that declares a network", func() {
Expect(EntitledControls(runner("sandbox", ControlNetworkPolicy), []string{"workspace.", "egress."})).To(Equal([]string{ControlNetworkPolicy}))
// A narrower grant that still covers the check is enough.
Expect(EntitledControls(runner("sandbox", ControlNetworkPolicy), []string{"egress.check"})).To(Equal([]string{ControlNetworkPolicy}))
Expect(EntitledControls(runner("sandbox", ControlNetworkPolicy), []string{"workspace."})).To(BeEmpty())
Expect(EntitledControls(runner("sandbox", ControlNetworkPolicy), nil)).To(BeEmpty())
m := runner("sandbox", ControlNetworkPolicy)
m.Capabilities.ToolDefinitions[0].Shadows = ""
Expect(EntitledControls(m, []string{"egress."})).To(BeEmpty())
m.Capabilities.Network = nil
Expect(EntitledControls(m, []string{"egress."})).To(BeEmpty(), "a bash tool alone does not attach the control")
})
It("serves a second add-on with the same controls, by its grant and not by its name", func() {
@@ -69,10 +75,11 @@ var _ = Describe("Host controls", func() {
It("draws the tool providers for an add-on that replaces any built-in tool", func() {
web := runner("web", ControlToolProviders, ControlNetworkPolicy)
web.Capabilities.Actions = false
web.Capabilities.Network, web.Capabilities.HostOperations = nil, nil
web.Capabilities.Tools = []string{"fetch"}
web.Capabilities.ToolDefinitions = []ToolDefinition{{Name: "fetch", Description: "Read a page.", Kind: ToolKindRead, InputSchema: json.RawMessage(fetchSchema), Shadows: "web_fetch"}}
Expect(ValidateManifest(web, host)).To(Succeed())
// The network policy needs bash, even with the grant.
// The network policy needs a declared network, even with the grant.
Expect(EntitledControls(web, []string{"egress."})).To(Equal([]string{ControlToolProviders}))
web.Capabilities.ToolDefinitions[0].Shadows = ""
Expect(EntitledControls(web, []string{"egress."})).To(BeEmpty())
+2 -3
View File
@@ -128,8 +128,7 @@ type Capabilities struct {
// (see network.go). It attaches the network-policy host control.
Network *NetworkCapability `json:"network,omitempty"`
// ContextFiles name small index files of the user's home that the host
// loads into the prompt of a chat (see contextfiles.go). They need
// manifest API 2.
// loads into the prompt of a chat (see contextfiles.go).
ContextFiles []ContextFile `json:"contextFiles,omitempty"`
// Credentials declare the secret that the host keeps for the person and
@@ -157,7 +156,7 @@ type ResourceTools struct {
// backing data. It is reachable only from the UI-only delete endpoint.
Delete string `json:"delete,omitempty"`
// List asks the host for a read tool that lists the resources the caller
// may use (see ResourceList). It needs manifest API 2.
// may use (see ResourceList).
List *ResourceList `json:"list,omitempty"`
}
+6 -9
View File
@@ -13,7 +13,7 @@ import (
var _ = Describe("The network capability", func() {
const bashSchema = `{"type":"object","properties":{"command":{"type":"string"}},"required":["command"],"additionalProperties":false}`
// withNetwork is a manifest of API 2 that asks for the network-policy
// withNetwork is a manifest that asks for the network-policy
// control. It runs no command tool unless shadowsBash is set, so what is
// drawn can only come from the declared capability.
withNetwork := func(n *NetworkCapability, shadowsBash bool) Manifest {
@@ -42,7 +42,7 @@ var _ = Describe("The network capability", func() {
declared := &NetworkCapability{Kind: NetworkKindEgressCheck, Title: "Runbox network",
Presets: []NetworkPreset{{ID: "off", Title: "Closed"}, {ID: "all", Title: "Open"}}}
It("is valid when the manifest accepts API 2 and lists the egress check", func() {
It("is valid when the manifest lists the egress check", func() {
Expect(ValidateManifest(withNetwork(declared, false), host())).To(Succeed())
})
@@ -85,10 +85,8 @@ var _ = Describe("The network capability", func() {
Expect(EntitledControls(withNetwork(declared, false), []string{"workspace."})).To(BeEmpty())
Expect(EntitledControls(withNetwork(declared, true), nil)).To(BeEmpty())
})
It("is drawn for a manifest that declares no network and replaces bash, as before", func() {
Expect(EntitledControls(withNetwork(nil, true), grants)).To(Equal([]string{ControlNetworkPolicy}))
})
It("is not drawn for a manifest that declares no network and does not replace bash", func() {
It("is not drawn for a manifest that declares no network, whether or not it replaces bash", func() {
Expect(EntitledControls(withNetwork(nil, true), grants)).To(BeEmpty())
Expect(EntitledControls(withNetwork(nil, false), grants)).To(BeEmpty())
})
It("does not read the add-on ID", func() {
@@ -120,11 +118,10 @@ var _ = Describe("The network capability", func() {
Expect(m.Capabilities.Network).To(Equal(&NetworkCapability{Kind: NetworkKindEgressCheck, Title: "Sandbox"}))
Expect(EntitledControls(m, []string{"egress."})).To(ContainElement(ControlNetworkPolicy))
})
It("keeps the control through its bash tool when an older manifest declares no network", func() {
It("loses the control when it declares no network", func() {
m := load()
m.Capabilities.Network = nil
Expect(EntitledControls(m, []string{"egress."})).To(ContainElement(ControlNetworkPolicy))
Expect(EntitledControls(m, nil)).NotTo(ContainElement(ControlNetworkPolicy))
Expect(EntitledControls(m, []string{"egress."})).NotTo(ContainElement(ControlNetworkPolicy))
})
})
})
+1 -2
View File
@@ -32,8 +32,7 @@ type SkillDefinition struct {
// add-on skill.
Shadows string `json:"shadows,omitempty"`
// 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.
Slot *SlotDefinition `json:"slot,omitempty"`
}
+1 -1
View File
@@ -58,7 +58,7 @@ var defaultSlots = sync.OnceValue(func() []Slot {
// DefaultSlots are the slots the host knows without any declaration, read
// from data. A skill that shadows one of them and declares no slot of its own
// (a manifest of API 1) is served by these.
// is served by these.
func DefaultSlots() []Slot { return slices.Clone(defaultSlots()) }
// DefaultSlot returns the default slot called name.