Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
81d1f41
Messenger provisioning for every server the pinned releases cover
hmaximilian Oct 25, 2026
a39896f
Messenger bridge and gateway: the phone's narrow door to the agent se…
hmaximilian Oct 25, 2026
6802c52
Fix messenger bridge provisioning, notifications and live revocation
hmaximilian Oct 25, 2026
ac5a44d
Add SimpleX forum and per-session chats with scoped phone approvals
hmaximilian Oct 25, 2026
51ad607
Add per-session SimpleX controls, safe replies and media exchange
hmaximilian Oct 26, 2026
b12fa47
Test: the messenger bridge never takes its own uid as the gateway's
hmaximilian Oct 26, 2026
b362e24
Messenger bridge: its own uid is never the gateway's peer uid
hmaximilian Oct 26, 2026
9a13bf1
Test: an old phone answer never frees a new action; the first answer …
hmaximilian Oct 26, 2026
7b1a9a8
Test: a voice message does what the same words typed would do
hmaximilian Oct 26, 2026
a5b36e1
Messenger: a voice message does what the same words typed would do
hmaximilian Oct 26, 2026
e44e289
Test: speech recognition runs in an interpreter of its own, isolated
hmaximilian Oct 26, 2026
5e5284e
Test: --whisper installs pinned, verified speech recognition
hmaximilian Oct 26, 2026
0651b27
Messenger: --whisper installs pinned local speech recognition
hmaximilian Oct 26, 2026
9818091
Merge remote-tracking branch 'origin/main' into rev/messenger
hmaximilian Oct 26, 2026
3752f57
Merge remote-tracking branch 'origin/main' into rev/messenger
hmaximilian Oct 26, 2026
9a3ff33
Messenger: the activation store is a home secret, not a runtime mask
hmaximilian Oct 26, 2026
f221029
Merge remote-tracking branch 'origin/main' into rev/messenger
hmaximilian Oct 26, 2026
583969a
Merge remote-tracking branch 'origin/main' into rev/messenger
hmaximilian Oct 26, 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
2 changes: 2 additions & 0 deletions delfin/agent/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8430,6 +8430,8 @@ def _is_grantable_read_dir(path: Path | str | None) -> bool:
# The account key and the pins beside it (trust_store): DELFIN's own
# processes outside agent sessions read them, no agent route does.
"trust_store", "trust_store/**", "**/trust_store", "**/trust_store/**",
"messenger_sessions", "messenger_sessions/**",
"**/messenger_sessions", "**/messenger_sessions/**",
)

# Self-modification protection: paths that are still WRITABLE, but always
Expand Down
2 changes: 2 additions & 0 deletions delfin/agent/boundaries.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,8 @@ def _rules_summary(workspace: Path) -> dict:
".delfin/credentials.json",
".delfin/approval_codes",
".delfin/trust_store",
# Which sessions the phone may reach (delfin.messenger.activation).
".delfin/messenger_sessions",
)


Expand Down
25 changes: 17 additions & 8 deletions delfin/agent/kit_confirm.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,17 +660,11 @@ def _refresh_panel(self) -> None:
except Exception:
pass

def _build_request_row(self, req: _ConfirmRequest, widgets):
# ---- 1. Classify the request and compute what (if anything) can
# be persisted. The "Allow + Permanent" button must be actionable
# when it makes sense, and CLEARLY explain why it is unavailable
# when it isn't. ----------------------------------------------
def persistence_for(self, req):
"""The same permanent option for the widget and the paired phone."""
tool = req.tool_name
cmd = req.args.get("command", "") if tool == "bash" else ""
path_arg = req.args.get("path", "") if tool != "bash" else ""
rationale = (req.args.get("rationale", "")
or req.args.get("description", "")).strip()

persist_pat = ""
persist_kind = "" # 'allow_pattern' / 'extra_dir' / ''
persist_disabled_reason = ""
Expand Down Expand Up @@ -732,6 +726,21 @@ def _build_request_row(self, req: _ConfirmRequest, widgets):
"it."
)

return persist_pat, persist_kind, persist_disabled_reason

def _build_request_row(self, req: _ConfirmRequest, widgets):
# ---- 1. Classify the request and compute what (if anything) can
# be persisted. The "Allow + Permanent" button must be actionable
# when it makes sense, and CLEARLY explain why it is unavailable
# when it isn't. ----------------------------------------------
tool = req.tool_name
cmd = req.args.get("command", "") if tool == "bash" else ""
path_arg = req.args.get("path", "") if tool != "bash" else ""
rationale = (req.args.get("rationale", "")
or req.args.get("description", "")).strip()

persist_pat, persist_kind, persist_disabled_reason = self.persistence_for(req)

# ---- 2. Header: tool name + path/cmd snippet + rationale ----
if tool == "bash":
subtitle = f'$&nbsp;<code>{_html_escape(cmd[:200])}</code>'
Expand Down
7 changes: 7 additions & 0 deletions delfin/agent/repl_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ def expand_at_references(text: str, workspace: Path) -> str:
# The builtin handlers
# ---------------------------------------------------------------------------

def _sync(ctx, args: str) -> CommandResult:
from delfin.messenger.activation import configure
sid = str(ctx.session_id or getattr(ctx.engine, "session_id", "") or "")
return CommandResult(output=configure(sid, args, endpoint=""))


def _status(ctx, _args: str) -> CommandResult:
engine = ctx.engine
try:
Expand Down Expand Up @@ -2018,6 +2024,7 @@ def _isolation(engine) -> str:
_keys),
ReplCommand("/editor", "session", "Write the next prompt in "
"$VISUAL or $EDITOR", _editor, True),
ReplCommand("/sync", "session", "SimpleX für diese Sitzung on|off|status", _sync, True),
ReplCommand("/status", "session", "Model, mode, tokens, cost", _status),
ReplCommand("/cost", "session", "What this session has cost", _cost),
ReplCommand("/usage", "session", "Token and cost detail, with the rate",
Expand Down
7 changes: 7 additions & 0 deletions delfin/agent/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -1013,6 +1013,7 @@ def _unsandboxed_argv(cmd: str, repo_dir: Path, mode: str, *,
# The account key and the MCP pins (trust_store): read by DELFIN's
# own processes, never by a command an agent runs.
".delfin/trust_store",
".delfin/messenger_sessions",
)


Expand All @@ -1037,6 +1038,12 @@ def runtime_masked_dirs() -> list[str]:
out.append(str(d.resolve()))
except Exception:
pass
# Not the messenger's activation records: they live in
# ~/.delfin/messenger_sessions, masked with the other home secrets
# (_HOME_SECRET_DIRS) and refused by the path deny globs, like the trust
# store. As a runtime mask the directory was created on every cage build,
# and a workspace containing it (a workspace at $HOME) lost its write
# root under Landlock, which cannot carve a hole into an allowed tree.
return out


Expand Down
Loading
Loading