fix: show the settings card, and make an edit actually take effect
`mode` and `extraRoots` carried no `volatile()` marker, so the framework's
form projection dropped the whole entry from `settings.describe` and the plugin
had no card under Plugins at all. The marker is what creates the card.
It is also what turns each field into a reference rather than a value: the
resolved config hands `apply` a `{get(), [write]}` per volatile field, which is
how a settings write lands in a running plugin without re-applying it. So the
policy and the guard now read both fields at the point of use instead of
snapshotting them at mount, and `sectionOrder`/`contextOrder` stay unmarked
because a prompt section cannot be re-ordered without a remount.
A section is also registered once as a plain string, so the rule could not
follow a live `mode` without freezing at load. The mode-specific sentence moved
into the binding, which is a callback.
This commit is contained in:
1 parent
c41c555400
commit
28f20a8aac
8 files changed
+283
-59
No files matched your search
+30
-2
@@ -18,6 +18,15 @@ import { after, before, test } from 'node:test'
|
||||
|
||||
import { apply as guard } from '../lib/guard.js'
|
||||
import { createFacetPolicy } from '../lib/policy.js'
|
||||
import { Config } from '../index.js'
|
||||
|
||||
/**
|
||||
* The symbol cosmokit marks a volatile config reference with. A settings write
|
||||
* reaches a running plugin as `target[write](value)` — that is literally what
|
||||
* `updateVolatile` does — so a test can commit an edit the way the framework
|
||||
* does, without a settings service.
|
||||
*/
|
||||
const VOLATILE_WRITE = Symbol.for('cosmokit.volatile.write')
|
||||
|
||||
/** A filesystem provider with `dsh-fs-local`'s two relevant behaviours. */
|
||||
const fsLikeLocal = {
|
||||
@@ -50,8 +59,8 @@ const fsLikeLocal = {
|
||||
}
|
||||
|
||||
/** Capture what `apply` registered, so the test can drive it. */
|
||||
function harness({ mode = 'ask', extraRoots = [], tool = true } = {}) {
|
||||
const policy = createFacetPolicy({ mode, extraRoots })
|
||||
function harness({ mode = 'ask', extraRoots = [], tool = true, config } = {}) {
|
||||
const policy = createFacetPolicy(config ?? { mode, extraRoots })
|
||||
const captured = { section: undefined, context: undefined, tool: undefined, preExecute: undefined }
|
||||
const ctx = {
|
||||
facetPolicy: policy,
|
||||
@@ -217,6 +226,25 @@ test('off mode registers nothing', () => {
|
||||
assert.equal(captured.tool, undefined)
|
||||
})
|
||||
|
||||
test('a live mode edit decides the next call, without the guard being remounted', async () => {
|
||||
// The loader hands the guard the schema-resolved config, so `mode` arrives as
|
||||
// a volatile reference rather than a string. This drives that exact shape.
|
||||
const config = Config({ mode: 'ask' })
|
||||
const { captured } = harness({ config })
|
||||
const outside = { name: 'read', args: { file_path: '../../shared/types.ts' }, agent: agent() }
|
||||
|
||||
assert.equal((await dispatch(captured, outside)).kind, 'ask')
|
||||
|
||||
// What a settings write does: commit into the reference in place, and
|
||||
// deliberately do not re-apply the plugin. One mount, three answers.
|
||||
config.mode[VOLATILE_WRITE]('deny')
|
||||
assert.equal((await dispatch(captured, outside)).kind, 'deny')
|
||||
|
||||
// `off` silences the guard entirely, which is the whole meaning of the mode.
|
||||
config.mode[VOLATILE_WRITE]('off')
|
||||
assert.equal((await dispatch(captured, outside)).kind, 'allow')
|
||||
})
|
||||
|
||||
test('the guard adds to a decision, never overrides one', async () => {
|
||||
const { captured } = harness()
|
||||
const denied = await dispatch(captured, {
|
||||
|
||||
Reference in new issue
Block a user