fix(agent-presets,connection): reclaim a deleted default, unpin a fence beside an open gate
Deleting the preset a user default names left the setting pointed at an id nothing will ever supply again, and every session created without an explicit pick then failed to start — the delete dialog called it 'new sessions cannot select it', which understates a hard creation error. `remove` now clears the user layer when it named the preset just deleted, exposing the deployment's own default underneath. Storing a default that does not exist YET stays deliberate: the roster is a live directory, so a name absent now may exist by the time a session asks, and `resolve` still reports that case. `agentPreset.select` also leaves the loopback set. It was pinned as a real escalation — one preset mounts the toolset that edits the live runtime — but `session.create` already takes an `agentPreset`, so pinning only the switch left the same capability one method over. The deeper reason is that the capability is not the preset's to grant: the deployment's own default already carries `bash` and the filesystem tools, so any caller that may start a session at all can already run commands as this process. `read`/`write`/`remove` stay pinned on their own footing — those touch files, not sessions.
This commit is contained in:
@@ -139,9 +139,8 @@ describe('connection node half', () => {
|
||||
'credentials.describe', 'credentials.set', 'credentials.unset',
|
||||
'llm.discoverModels',
|
||||
// A composition names the plugins a session runs: reading one is
|
||||
// reconnaissance, writing one is arbitrary capability, and selecting one
|
||||
// can move a session onto a preset that edits the live runtime.
|
||||
'agentPreset.select', 'agentPreset.read', 'agentPreset.write', 'agentPreset.remove',
|
||||
// reconnaissance and writing one is arbitrary capability.
|
||||
'agentPreset.read', 'agentPreset.write', 'agentPreset.remove',
|
||||
]) {
|
||||
const denied = fakeResponse()
|
||||
await routes[0]!.handler(
|
||||
@@ -230,7 +229,7 @@ describe('connection node half over a real HTTP server', () => {
|
||||
// Carries a draft credential and turns the host into a fetcher for a
|
||||
// URL the caller picked: an anonymous LAN caller must not reach it.
|
||||
'llm.discoverModels',
|
||||
'agentPreset.select', 'agentPreset.read', 'agentPreset.write', 'agentPreset.remove',
|
||||
'agentPreset.read', 'agentPreset.write', 'agentPreset.remove',
|
||||
]) {
|
||||
expect([method, await call(port, method, 'harness.example')]).toEqual([method, 403])
|
||||
}
|
||||
@@ -238,8 +237,11 @@ describe('connection node half over a real HTTP server', () => {
|
||||
// client's model picker needs it, and it carries no key or endpoint
|
||||
// state (404 is the empty proxy's carrier answer — the fence passed).
|
||||
// `agentPreset.list` joins the model catalog for the same reason: ids and
|
||||
// trust only, and a LAN client's preset picker needs it.
|
||||
for (const method of ['llm.providers', 'llm.models', 'agentPreset.list']) {
|
||||
// trust only, and a LAN client's preset picker needs it. `select` is
|
||||
// reachable too: `session.create` already takes an `agentPreset`, and the
|
||||
// deployment's own default already carries bash, so pinning the switch
|
||||
// would be a fence beside an open gate.
|
||||
for (const method of ['llm.providers', 'llm.models', 'agentPreset.list', 'agentPreset.select']) {
|
||||
expect([method, await call(port, method, 'harness.example')]).toEqual([method, 404])
|
||||
}
|
||||
// Loopback reaches everything, configuration included.
|
||||
|
||||
Reference in New Issue
Block a user