Files
Meldwork/desktop/test/agents/agent-permission-policy.test.cjs
T
Ryder Sun 68145eb319 refactor: extract receipt parsing, watchdog, stream marker filter and permission policy from the invocation
local-workspace-agent-invocation.cjs held four independent pieces of logic
inside one 1500-line method. They now live in small modules with their own
tests, and the invocation only wires them together:

- collaboration/collaboration-receipt.cjs: parse and normalise the receipt
  (unchanged behaviour; the existing receipt tests now import it directly)
- runs/invocation-watchdog.cjs: startup, inactivity and stall windows
- collaboration/answer-marker-filter.cjs: hides control markers while the
  answer streams
- agents/agent-permission-policy.cjs: automatic answers to permission
  requests, read-only turns only approve side-effect-free tools

Two latent defects fixed on the way:
- the watchdog cleared its timer on the first event without re-arming it, so
  an Agent that went silent right after its first event was never stopped
- the marker filter upper-cased the buffer to find markers, which shifts the
  cut position for characters whose upper-case form is longer (e.g. ss)

Checked with: node --test on the new module tests; npm --prefix desktop run
test:workspace (595 pass).
2026-10-16 10:09:38 +08:00

56 lines
2.0 KiB
JavaScript

const test = require('node:test')
const assert = require('node:assert/strict')
const { automaticPermissionResponse } = require('../../src/agents/agent-permission-policy.cjs')
const options = [
{ optionId: 'allow-always', kind: 'allow_always', name: 'Always' },
{ optionId: 'allow', kind: 'allow_once', name: 'Allow' },
{ optionId: 'reject', kind: 'reject_once', name: 'Reject' },
]
test('read-only turns approve only tools without side effects', () => {
for (const kind of ['read', 'search', 'fetch', 'think']) {
assert.deepEqual(
automaticPermissionResponse({ toolCall: { kind }, options }, { allowWrite: false }),
{ status: 'approved', optionId: 'allow' },
kind,
)
}
for (const kind of ['edit', 'delete', 'move', 'execute', 'switch_mode', 'other', undefined]) {
assert.deepEqual(
automaticPermissionResponse({ toolCall: { kind }, options }, { allowWrite: false }),
{ status: 'rejected', optionId: 'reject' },
String(kind),
)
}
})
test('a refused request without a reject option is answered with null instead of the user gate', () => {
assert.equal(automaticPermissionResponse({
toolCall: { kind: 'execute' },
options: [{ optionId: 'allow', kind: 'allow_once' }],
}, { allowWrite: false }), null)
})
test('writable turns approve everything the Agent offers, preferring a one-time allow', () => {
assert.deepEqual(
automaticPermissionResponse({ toolCall: { kind: 'execute' }, options }, { allowWrite: true }),
{ status: 'approved', optionId: 'allow' },
)
assert.deepEqual(
automaticPermissionResponse({
toolCall: { kind: 'edit' },
options: [{ optionId: 'only', kind: 'something_else' }],
}, { allowWrite: true }),
{ status: 'approved', optionId: 'only' },
)
})
test('with nothing to choose the caller must ask the user', () => {
assert.equal(
automaticPermissionResponse({ toolCall: { kind: 'read' }, options: [] }, { allowWrite: false }),
undefined,
)
assert.equal(automaticPermissionResponse(null, { allowWrite: true }), undefined)
})