Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
408748b
feat: add settings-backed generic listen-only mode and invoke gating
deanfluencebot Mar 2, 2026
961dc0a
fix: sync quiet/active commands with runtime listen-only config
deanfluencebot Mar 2, 2026
05be0c1
fix: address listen-only invoke and rich reply review findings
deanfluencebot Mar 2, 2026
5287912
fix: tighten reply error semantics and invoke metadata handling
deanfluencebot Mar 2, 2026
11b2445
fix: polish quiet-mode invoke handling and slack mention check
deanfluencebot Mar 2, 2026
baa3264
fix: align builtin reply logging and discord invoke metadata
deanfluencebot Mar 2, 2026
1a02036
fix: defer batch attachment downloads until invoke pass
deanfluencebot Mar 2, 2026
d311875
fix: finalize quiet-mode fallback send semantics cleanup
deanfluencebot Mar 2, 2026
afc73d5
fix: only advertise /digest in telegram help output
deanfluencebot Mar 2, 2026
54dad94
refactor: route task and digest slash commands through agent flow
deanfluencebot Mar 3, 2026
f44d664
fix: make listen-only config update atomic with rcu
deanfluencebot Mar 3, 2026
b0435d5
Merge remote-tracking branch 'origin/main' into codex/quiet-mode-v022
deanfluencebot Mar 3, 2026
6533378
chore: sync lockfile after merging main
deanfluencebot Mar 3, 2026
2253026
fix: preserve runtime listen-only mode on config reload
deanfluencebot Mar 3, 2026
3cd0822
fix: make channel reload updates atomic and dedupe resolver
deanfluencebot Mar 3, 2026
987ba19
fix: apply resolved channel config atomically on reload
deanfluencebot Mar 3, 2026
cc0a3d0
fix: enforce listen-only precedence resolver on reload
deanfluencebot Mar 3, 2026
dd34329
fix: use rcu current snapshot for listen-only precedence
deanfluencebot Mar 3, 2026
32a5e0e
fix: avoid synthetic agent channel override when unset
deanfluencebot Mar 3, 2026
3aefc42
test: add listen-only precedence regression coverage
deanfluencebot Mar 3, 2026
bd41063
test: cover reload rcu merge precedence contract
deanfluencebot Mar 3, 2026
0f7b3b8
refactor: share channel reload merge helper
deanfluencebot Mar 3, 2026
97d5517
config: future-proof channel merge helper
deanfluencebot Mar 3, 2026
0366b00
test: use shared channel merge helper in rcu regression
deanfluencebot Mar 3, 2026
d48d6f1
config: polish resolver helper naming for review
deanfluencebot Mar 3, 2026
c490484
chore: satisfy clippy and simplify channel config merge
deanfluencebot Mar 4, 2026
0f9c0e9
Merge remote-tracking branch 'origin/main' into codex/quiet-mode-v022
deanfluencebot Mar 4, 2026
7c9fb9a
Merge remote-tracking branch 'origin/main' into codex/quiet-mode-v022
deanfluencebot Mar 4, 2026
cb78cb5
config: restore channel listen-only support after main refactor
deanfluencebot Mar 4, 2026
fad08fa
quiet-mode: persist listen setting and keep fast-path inbound logs
deanfluencebot Mar 4, 2026
7feba63
review: apply Tembo suggestions for mentions and rich payload gating
deanfluencebot Mar 4, 2026
9f3ccca
slack: reuse session handle for thread-root lookup
deanfluencebot Mar 4, 2026
a1c5850
config: honor explicit-vs-persisted listen-only precedence
deanfluencebot Mar 4, 2026
aa246d2
review: address latest coderabbit listen-mode and twitch checks
deanfluencebot Mar 4, 2026
e8e200b
review: fix persistence signal and add slack parent-lookup timeout
deanfluencebot Mar 4, 2026
4fa37ba
review: apply latest coderabbit fixes and nits
deanfluencebot Mar 4, 2026
3c72735
review: harden telegram mentions and include twitch reply invoke
deanfluencebot Mar 4, 2026
7ee0840
review: tighten telegram/twitch invoke semantics
deanfluencebot Mar 4, 2026
63bbc3a
review: tighten telegram reply matching and rich payload limits
deanfluencebot Mar 4, 2026
26aea57
fix: scope listen-only persistence per channel
deanfluencebot Mar 4, 2026
686f269
fix: preserve quiet-mode precedence and session override
deanfluencebot Mar 4, 2026
208645d
Merge remote-tracking branch 'origin/main' into codex/quiet-mode-v022
deanfluencebot Mar 4, 2026
c5508fe
Merge branch 'main' into codex/quiet-mode-v022
jamiepine Mar 4, 2026
d3e4b74
Merge branch 'main' into codex/quiet-mode-v022
jamiepine Mar 4, 2026
f963e73
refactor: move Discord-specific validation from reply tool to adapter
jamiepine Mar 5, 2026
502c7dc
fix: add missing ChannelConfig import in config/load.rs
jamiepine Mar 5, 2026
a8169be
fix: collapse nested if per clippy collapsible_if lint
jamiepine Mar 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions interface/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,10 @@ export interface BrowserSection {
close_policy: "close_browser" | "close_tabs" | "detach";
}

export interface ChannelSection {
listen_only_mode: boolean;
}

export interface SandboxSection {
mode: "enabled" | "disabled";
writable_paths: string[];
Expand All @@ -595,6 +599,7 @@ export interface AgentConfigResponse {
coalesce: CoalesceSection;
memory_persistence: MemoryPersistenceSection;
browser: BrowserSection;
channel: ChannelSection;
discord: DiscordSection;
sandbox: SandboxSection;
}
Expand Down Expand Up @@ -661,6 +666,10 @@ export interface BrowserUpdate {
close_policy?: "close_browser" | "close_tabs" | "detach";
}

export interface ChannelUpdate {
listen_only_mode?: boolean;
}

export interface SandboxUpdate {
mode?: "enabled" | "disabled";
writable_paths?: string[];
Expand All @@ -679,6 +688,7 @@ export interface AgentConfigUpdateRequest {
coalesce?: CoalesceUpdate;
memory_persistence?: MemoryPersistenceUpdate;
browser?: BrowserUpdate;
channel?: ChannelUpdate;
discord?: DiscordUpdate;
sandbox?: SandboxUpdate;
}
Expand Down
25 changes: 23 additions & 2 deletions interface/src/routes/AgentConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function supportsAdaptiveThinking(modelId: string): boolean {
|| id.includes("sonnet-4-6") || id.includes("sonnet-4.6");
}

type SectionId = "soul" | "identity" | "user" | "routing" | "tuning" | "compaction" | "cortex" | "coalesce" | "memory" | "browser" | "sandbox";
type SectionId = "soul" | "identity" | "user" | "routing" | "tuning" | "compaction" | "cortex" | "coalesce" | "memory" | "browser" | "channel" | "sandbox";

const SECTIONS: {
id: SectionId;
Expand All @@ -34,6 +34,7 @@ const SECTIONS: {
{ id: "coalesce", label: "Coalesce", group: "config", description: "Message batching", detail: "When multiple messages arrive in quick succession, coalescing batches them into a single LLM turn. This prevents the agent from responding to each message individually in fast-moving conversations." },
{ id: "memory", label: "Memory Persistence", group: "config", description: "Auto-save interval", detail: "Spawns a silent background branch at regular intervals to recall existing memories and save new ones from the recent conversation. Runs without blocking the channel." },
{ id: "browser", label: "Browser", group: "config", description: "Chrome automation", detail: "Controls browser automation tools available to workers. When enabled, workers can navigate web pages, take screenshots, and interact with sites. JavaScript evaluation is a separate permission." },
{ id: "channel", label: "Channel Behavior", group: "config", description: "Reply behavior", detail: "Listen-only mode suppresses unsolicited replies in busy channels. The agent still responds to slash commands, @mentions, and replies to its own messages." },
{ id: "sandbox", label: "Sandbox", group: "config", description: "Process containment", detail: "OS-level filesystem containment for shell and exec tool subprocesses. When enabled, worker processes run inside a kernel-enforced sandbox (bubblewrap on Linux, sandbox-exec on macOS) with an allowlist-only filesystem — only system paths, the workspace, and explicitly configured extra paths are accessible." },
];

Expand Down Expand Up @@ -64,7 +65,7 @@ export function AgentConfig({ agentId }: AgentConfigProps) {
// Sync activeSection with URL search param
useEffect(() => {
if (search.tab) {
const validSections: SectionId[] = ["soul", "identity", "user", "routing", "tuning", "compaction", "cortex", "coalesce", "memory", "browser", "sandbox"];
const validSections = SECTIONS.map((section) => section.id);
if (validSections.includes(search.tab as SectionId)) {
setActiveSection(search.tab as SectionId);
}
Expand Down Expand Up @@ -414,6 +415,7 @@ const SANDBOX_DEFAULTS = { mode: "enabled" as const, writable_paths: [] as strin
function ConfigSectionEditor({ sectionId, label, description, detail, config, onDirtyChange, saveHandlerRef, onSave }: ConfigSectionEditorProps) {
type ConfigValues = Record<string, string | number | boolean | string[]>;
const sandbox = config.sandbox ?? SANDBOX_DEFAULTS;
const channel = config.channel ?? { listen_only_mode: false };
const [localValues, setLocalValues] = useState<ConfigValues>(() => {
// Initialize from config based on section
switch (sectionId) {
Expand All @@ -431,6 +433,8 @@ function ConfigSectionEditor({ sectionId, label, description, detail, config, on
return { ...config.memory_persistence } as ConfigValues;
case "browser":
return { ...config.browser } as ConfigValues;
case "channel":
return { ...channel } as ConfigValues;
case "sandbox":
return { mode: sandbox.mode, writable_paths: sandbox.writable_paths } as ConfigValues;
default:
Expand Down Expand Up @@ -469,6 +473,9 @@ function ConfigSectionEditor({ sectionId, label, description, detail, config, on
case "browser":
setLocalValues({ ...config.browser });
break;
case "channel":
setLocalValues({ ...channel });
break;
case "sandbox":
setLocalValues({ mode: sandbox.mode, writable_paths: sandbox.writable_paths });
break;
Expand Down Expand Up @@ -509,6 +516,9 @@ function ConfigSectionEditor({ sectionId, label, description, detail, config, on
case "browser":
setLocalValues({ ...config.browser });
break;
case "channel":
setLocalValues({ ...channel });
break;
case "sandbox":
setLocalValues({ mode: sandbox.mode, writable_paths: sandbox.writable_paths });
break;
Expand Down Expand Up @@ -875,6 +885,17 @@ function ConfigSectionEditor({ sectionId, label, description, detail, config, on
</div>
</div>
);
case "channel":
return (
<div className="grid gap-4">
<ConfigToggleField
label="Listen-Only Mode"
description="Only respond when explicitly invoked (slash command, @mention, or reply-to-bot)."
value={localValues.listen_only_mode as boolean}
onChange={(v) => handleChange("listen_only_mode", v)}
/>
</div>
);
default:
return null;
}
Expand Down
Loading
Loading