diff --git a/.agentworkforce/agents/relay-feature-guardian/agent.test.ts b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts index af09878e6..8a93bae75 100644 --- a/.agentworkforce/agents/relay-feature-guardian/agent.test.ts +++ b/.agentworkforce/agents/relay-feature-guardian/agent.test.ts @@ -23,7 +23,12 @@ import guardian, { } from './agent.ts'; const persona = JSON.parse(readFileSync(new URL('./persona.json', import.meta.url), 'utf8')) as { + integrations: Record< + string, + { relayfileMount?: { requiredReadPaths?: unknown; writeOnlyPaths?: unknown } } + >; inputs: { SLACK_CHANNEL: { default: string } }; + memory: { enabled: boolean; scopes: string[]; ttlDays: number }; }; const manifestFeatures = [ @@ -318,6 +323,22 @@ describe('relay-feature-guardian runtime paths', () => { expect(persona.inputs.SLACK_CHANNEL.default).toBe('C0AEKNLDNKW'); }); + it('declares bounded manifest and memory reads plus configured Slack output', () => { + expect(persona.integrations.github?.relayfileMount).toEqual({ + requiredReadPaths: ['/github/repos/AgentWorkforce/relay/.agentworkforce/features/**'], + writeOnlyPaths: [], + }); + expect(persona.memory).toEqual({ + enabled: true, + scopes: ['workspace'], + ttlDays: 14, + }); + expect(persona.integrations.slack?.relayfileMount).toEqual({ + requiredReadPaths: [], + writeOnlyPaths: ['/slack/channels/${SLACK_CHANNEL}/**'], + }); + }); + it('deduplicates an ambiguous post retry and advances after a saved receipt', async () => { const transport = new IdempotentSlackTransport(); const restore = bindPreviewTransport(transport); diff --git a/.agentworkforce/agents/relay-feature-guardian/persona.json b/.agentworkforce/agents/relay-feature-guardian/persona.json index f5c4b1e8e..ddbe37ccb 100644 --- a/.agentworkforce/agents/relay-feature-guardian/persona.json +++ b/.agentworkforce/agents/relay-feature-guardian/persona.json @@ -13,11 +13,19 @@ "github": { "scope": { "repo": "AgentWorkforce/relay" + }, + "relayfileMount": { + "requiredReadPaths": ["/github/repos/AgentWorkforce/relay/.agentworkforce/features/**"], + "writeOnlyPaths": [] } }, "slack": { "scope": { "paths": "/slack/channels/**" + }, + "relayfileMount": { + "requiredReadPaths": [], + "writeOnlyPaths": ["/slack/channels/${SLACK_CHANNEL}/**"] } } }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b7d2ade6..87f3fb85e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `agent-relay integration` now discovers relayfile control-plane capabilities before sending API v3 headers, fails fast with upgrade and restart guidance for incompatible daemons, and safely replaces stale daemons when a compatible binary is installed. - `AgentRelaySDK` now maps Relaycast lifecycle states onto its existing Swift presence states, so root-package consumers compile with Relaycast 6.1 and later while package-local builds remain compatible with 6.0.5. -- `relay-feature-guardian` now reads the scoped Relay clone, posts to its configured channel, and advances its exact, revision-safe cycle checkpoint only after a bounded wait returns a real Slack receipt, while safely reconciling retired manifest features. +- `relay-feature-guardian` now limits read mirrors to the feature manifest and workspace memory while keeping Slack write-only to its configured output channel, and advances its exact, revision-safe cycle checkpoint only after a bounded wait returns a real Slack receipt while safely reconciling retired manifest features. - `agent-relay-broker` and `@agent-relay/utils` now preserve mise/asdf/rtx-style CLI shims when spawning provider workers, so Codex, Claude, Gemini, and other agent CLIs installed via a version manager receive their own permission flags (e.g. `--dangerously-bypass-approvals-and-sandbox`) instead of the manager binary rejecting them. ## [10.6.3] - 2026-07-17