Commit Graph
14 Commits
Author SHA1 Message Date
NotExpectedYet d9caf4eeec feat: scope a session from a declaration the agent cannot widen
The facet root was the session's working directory and nothing else, so the
only way to scope a conversation to a module was to make the module a
workspace — and the only way to reach anything outside it was `extraRoots`,
which is a fact about the deployment rather than about one conversation.

Scope now has two layers, and the split is the point. The **user layer** is
`config.scopes[sessionId]` — a `focus` and a list of `extras`, written only
from the browser context panel through the settings service, which is what
`settings.mutate` requiring a volatile path buys us. The **agent layer** is in
memory: a narrowing `focus` and its grants. Both can only ever reduce what the
user layer allows, so an agent that goes wrong cannot widen its own scope.

`facet action=focus` is therefore the one scope change the agent may make
unasked, bounded by the scope already in force and decided on canonical targets.
`reset` clears the agent's side only, landing on the user's declaration rather
than on the session directory — landing on the directory would have made
`reset` a widening. `extraRoots` demotes to an optional deployment-wide seed.

The binding now names the context panel as the way to widen, because it is: in
a deployment with no approval channel, `facet action=add` cannot be granted at
all.
2026-09-27 06:10:53 +01:00
gitea-actions f36d46efa1 Release v0.1.4 [skip release] [skip actions] v0.1.4 2026-09-26 08:03:19 +00:00
NotExpectedYet a1c8ecb1e8 fix: say so when a session has no approval channel to ask through
Release / release (push) Successful in 6s
`ask` mode promises that an out-of-facet call is put to the user, and that
promise needs a channel. A session can be composed without one: the
`danger-full-access` permission preset sets `approval: never`, and the
approval service's `decide()` then returns "rejected" before consulting any
answerer. The call is refused, `facet action=add` is refused with it — widening
is itself a question — and the scope can never be widened.

The model is told none of that. `serviceAsk` replaces a rejected ask with
`the user rejected tool \"…\"`, discarding the reason this guard wrote, so the
binding is the only channel left that can state what is true. It now reads the
session's effective approval policy and, when there is no channel, says the call
was refused rather than asked and names the mechanism that still works:
`extraRoots` is configuration applied by the profile, not a grant asked for per
conversation.

A missing approval service counts as no channel (the tool pipeline denies an ask
it cannot route); an unreadable policy counts as a channel, so a transient
failure never tells a user to go and edit their configuration.
2026-09-26 09:02:48 +01:00
gitea-actions 6e696f10a2 Release v0.1.3 [skip release] [skip actions] v0.1.3 2026-09-26 06:50:42 +00:00
NotExpectedYet 1be3a67b97 fix: put the settings card on the Plugins page with a browser half
Release / release (push) Successful in 6s
The `volatile()` markers made the Host serve the namespace, and that was as
far as it went: the Plugins page does not build a card from a `Config` schema.
It draws only what browser halves register into the slots it declares, which is
why dsh-slopo, dsh-recall and dsh-context all ship a `client/` — and why this
plugin, with two served fields, still showed nothing.

The card registers into `plugins.bundle.config` while
`configForms.whileServed` reports the namespace served. The two keys are
deliberately different strings and that is the trap in the file: the settings
document is filed under the profile entry id (`facet-policy`) while the card is
keyed by the package name (`dsh-facet`). Every sibling plugin happens to name
them identically. Binding the form to the wrong one yields a card that renders,
saves, and writes nowhere, so `test/client.test.js` pins both, holds the card's
field list to the schema's volatile fields, and refuses to register at all when
the namespace is not served.
2026-09-26 07:39:57 +01:00
gitea-actions e860ac2989 Release v0.1.2 [skip release] [skip actions] v0.1.2 2026-09-26 06:14:27 +00:00
NotExpectedYet 0e9f75cfab Merge branch 'master' of https://git.notexpectedyet.com/Not-Expected-Yet/dsh-facet
Release / release (push) Successful in 6s
2026-09-26 07:14:12 +01:00
NotExpectedYet 28f20a8aac 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.
2026-09-26 07:13:33 +01:00
gitea-actions e7401445d7 Release v0.1.1 [skip release] [skip actions] v0.1.1 2026-09-26 05:54:48 +00:00
NotExpectedYet c41c555400 Merge branch 'master' of https://git.notexpectedyet.com/Not-Expected-Yet/dsh-facet
Release / release (push) Successful in 6s
2026-09-26 06:54:31 +01:00
NotExpectedYet 651093a00c docs: document installing the bundle from the repository
The Install section only showed a local path, which is the one form that
cannot be updated: pnpm records a commit for a bare git URL, so
`dsh plugin update` has no version to move between. Show the
`#semver:^0.1.0` form, and say why the fragment is the difference.
2026-09-26 06:53:13 +01:00
gitea-actions e5867e6a9c Release v0.1.0 [skip release] [skip actions] v0.1.0 2026-09-26 05:47:54 +00:00
NotExpectedYet cd43e87684 feat: tag and publish a Gitea release on every push to master
Release / release (push) Successful in 6s
A plugin installed as a bare git+https URL is pinned to a commit, so with no
tags there is nothing for `dsh plugin update` to move to. The workflow reads
the commit subjects since the last tag, bumps package.json, prepends
CHANGELOG.md, tags vX.Y.Z, and publishes the Gitea release with those subjects
as the body. The decision logic lives in .gitea/scripts/release.sh so it runs
and is tested without a runner (`npm run test:release`).
2026-09-26 06:45:52 +01:00
NotExpectedYet 0858862cda init 2026-09-26 06:45:50 +01:00