Merge branch 'codex/invariant-service-seam' into codex/invariant-package-registration-gate
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# @deepseek-ai/dsh-permission
|
||||
|
||||
User-facing permission presets through `ctx.permission` ([`PermissionService`](src/index.ts)). Each configured name bundles `bash/sandbox-mode` with `approval/policy`; the defaults are `workspace-write` (`workspace-write` + `ask`) and `danger-full-access` (`danger-full-access` + `never`). The ACP bridge exposes them as one `Permissions` select, while sandbox execution and approval continue to consume their own knobs.
|
||||
User-facing permission presets through `ctx.permission` ([`PermissionService`](src/index.ts)). Each configured name bundles `sandbox/mode` with `approval/policy`; the defaults are `workspace-write` (`workspace-write` + `ask`) and `danger-full-access` (`danger-full-access` + `never`). The ACP bridge exposes them as one `Permissions` select, while sandbox execution and approval continue to consume their own knobs.
|
||||
|
||||
`set(session, name)` records a changed selection in a log-only `permission/preset` event, then calls each knob's setter only when its effective value changes. The selection event precedes the knob events and preserves user intent when presets share a bundle; a net-zero selection appends nothing. `current(events)` prefers a still-matching recorded selection, then the first matching table entry, and otherwise returns `custom`. Clients may display `custom` as the current value, but cannot select it.
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"@deepseek-ai/dsh-bash": "^0.0.1",
|
||||
"@deepseek-ai/dsh-invariants": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
"@deepseek-ai/dsh-user-approval": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
@@ -41,6 +42,7 @@
|
||||
"@deepseek-ai/dsh-bash": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-approval": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
|
||||
@@ -12,7 +12,10 @@ import { Context, Service } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import { SANDBOX_MODES, effectiveSandboxMode, setSandboxMode } from '@deepseek-ai/dsh-bash'
|
||||
import { SANDBOX_MODES, effectiveSandboxMode, setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
// Side-effect type import: declaration-merges `ctx.bash` (the capability fact
|
||||
// `sandboxMode` this service reads), without a value dependency on the seam.
|
||||
import type {} from '@deepseek-ai/dsh-bash'
|
||||
import type { ApprovalPolicy } from '@deepseek-ai/dsh-user-approval'
|
||||
import { APPROVAL_POLICIES, effectiveApprovalPolicy, setApprovalPolicy } from '@deepseek-ai/dsh-user-approval'
|
||||
|
||||
@@ -36,7 +39,7 @@ declare module '@deepseek-ai/dsh-session' {
|
||||
|
||||
/** One preset's sandbox/approval bundle and optional client presentation. */
|
||||
export interface PresetSpec {
|
||||
/** The `bash/sandbox-mode` value the preset writes through. */
|
||||
/** The `sandbox/mode` value the preset writes through. */
|
||||
sandbox: SandboxMode
|
||||
/** The `approval/policy` value the preset writes through. */
|
||||
approval: ApprovalPolicy
|
||||
|
||||
@@ -51,7 +51,7 @@ describe('PermissionService', () => {
|
||||
it('a knob state matching no table entry derives custom — a state, not an error', async () => {
|
||||
const ctx = await mounted()
|
||||
const session = freshSession('sess-custom')
|
||||
session.append('bash/sandbox-mode', { mode: 'read-only' })
|
||||
session.append('sandbox/mode', { mode: 'read-only' })
|
||||
expect(ctx.permission.current(session.events)).toBe(CUSTOM_PRESET)
|
||||
ctx.permission.set(session, 'danger-full-access')
|
||||
expect(ctx.permission.current(session.events)).toBe('danger-full-access')
|
||||
@@ -74,7 +74,7 @@ describe('PermissionService', () => {
|
||||
ctx.permission.set(session, 'agentish')
|
||||
expect(ctx.permission.current(session.events)).toBe('agentish')
|
||||
session.append('approval/policy', { policy: 'never' })
|
||||
session.append('bash/sandbox-mode', { mode: 'danger-full-access' })
|
||||
session.append('sandbox/mode', { mode: 'danger-full-access' })
|
||||
expect(ctx.permission.current(session.events)).toBe('danger-full-access')
|
||||
})
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('PermissionService', () => {
|
||||
ctx.permission.set(session, 'danger-full-access')
|
||||
expect(session.events.map(e => [e.type, e.data])).toEqual([
|
||||
['permission/preset', { preset: 'danger-full-access' }],
|
||||
['bash/sandbox-mode', { mode: 'danger-full-access' }],
|
||||
['sandbox/mode', { mode: 'danger-full-access' }],
|
||||
['approval/policy', { policy: 'never' }],
|
||||
])
|
||||
})
|
||||
@@ -102,12 +102,12 @@ describe('PermissionService', () => {
|
||||
ctx.permission.set(session, 'danger-full-access')
|
||||
// Re-selecting from a drifted state records the choice and repairs only
|
||||
// the changed knob.
|
||||
session.append('bash/sandbox-mode', { mode: 'read-only' })
|
||||
session.append('sandbox/mode', { mode: 'read-only' })
|
||||
ctx.permission.set(session, 'danger-full-access')
|
||||
const tail = session.events.slice(4)
|
||||
expect(tail.map(e => [e.type, e.data])).toEqual([
|
||||
['permission/preset', { preset: 'danger-full-access' }],
|
||||
['bash/sandbox-mode', { mode: 'danger-full-access' }],
|
||||
['sandbox/mode', { mode: 'danger-full-access' }],
|
||||
])
|
||||
})
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
{
|
||||
"path": "../../sandbox/sandbox"
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox-policy"
|
||||
},
|
||||
{
|
||||
"path": "../../bash/bash"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user