Compare commits

...
Author SHA1 Message Date
dev60-review 88a210b195 fix: withhold an unsafe receiving-agent description; say where unrouted Slack goes with no receiver; qualify the default person
The settings view's default-receiver row withholds its description when agents.operator_session or the resolved session holds unsafe text, since the description names the session and can quote the setting. With no receiving agent, Slack's unchosen-destination line and the docs say unrouted messages go to operator-agent@kernel for compatibility. The rigs skill says requests and asks that name nobody go to the default person.
2026-11-11 02:29:44 +00:00
5 changed files with 32 additions and 6 deletions

No files matched your search

+2 -1
View File
@@ -91,7 +91,8 @@ never picks whichever seat happens to be running. The TUI shows the effective ag
under Settings > Agents & runtime ("Default receiving agent"). It never replaces the default person.
For Slack, `rig slack setup --inbound-destination default-receiver` (the default for a new setup) sends unrouted
messages to the default receiving agent, read when the connector starts. A destination already saved in the connector
messages to the default receiving agent, read when the connector starts. With no receiving agent on the host, they go to
`operator-agent@kernel` for compatibility, as before. A destination already saved in the connector
config, `operator-agent@kernel` included, is kept as chosen; pass `default-receiver` to switch it.
## In the TUI: whose queue, and who acts
+2 -1
View File
@@ -146,7 +146,8 @@ person needs in their project or home folder and name that path.
A declared name is a claim, not a seat's own identity: for a team that acts only on seat identity, the attached
shell above is the route.
- **Who answers OpenRig's questions:** read `rig gateway participants` (its `default:` line) before the handoff.
- A default person is there: say requests and asks go to them, and don't offer again.
- A default person is there: say requests and asks that name nobody go to them (a pairing request or an ask can
name someone else), and don't offer again.
- Several people with no default: ask which should get requests nobody is named for (`rig gateway default <id>`),
or leave it. Never pick one yourself.
- Nobody is registered: offer it in a sentence or two. OpenRig's questions, such as another machine asking to pair,
@@ -4,7 +4,7 @@ import { join, isAbsolute } from "node:path";
import { getOpenRigHome } from "../../openrig-compat.js";
import { SETTINGS_VALID_KEYS, type SettingsStore, type ResolvedSetting } from "./settings-store.js";
import { connectionsProjection, readConnectionConfiguration } from "../gateway/connections-projection.js";
import { DEFAULT_CONFIG, INHERIT_INBOUND_DESTINATION } from "../gateway/slack/config.js";
import { DEFAULT_CONFIG, INHERIT_INBOUND_DESTINATION, LEGACY_INBOUND_DESTINATION } from "../gateway/slack/config.js";
import { projectionPath } from "../gateway/human-registry.js";
import { loadHostRegistry } from "../hosts/hosts-registry-reader.js";
import { DEFAULT_HEALTH_POLICY, validateHealthPolicy } from "../health-policy.js";
@@ -144,9 +144,14 @@ export function settingsBrowser(store: SettingsStore, gateway: Record<string, un
// Derived, not a setting: the agent that receives answers nothing else reads, from agents.operator_session or the
// kernel operator. Shown with where it came from and its state; changed with rig config set agents.operator_session.
if (receiver) {
// The description names the session and can quote the setting: when either is withheld, so is the description.
const unsafe = [receiver.session, resolved?.["agents.operator_session"]?.value]
.some((v) => typeof v === "string" && v !== "" && safeValue(v, "identity", redact).value === null);
add("agents.default_receiver", "general", receiver.session ?? "", undefined,
receiver.source === "setting" ? resolved?.["agents.operator_session"]?.source ?? "unreported" : "default", "identity",
"Displayed daemon instance", describeDefaultReceiver(receiver));
"Displayed daemon instance", unsafe
? "Withheld: agents.operator_session holds credential-bearing or unsafe text, so the receiving agent isn't described here. Inspect it at its source."
: describeDefaultReceiver(receiver));
}
if (resolved) {
for (const host of store.listFeedHostSubscriptions()) {
@@ -171,7 +176,10 @@ export function settingsBrowser(store: SettingsStore, gateway: Record<string, un
const unchosen = key === "inboundDestination" && (!cfg || cfg[key] === INHERIT_INBOUND_DESTINATION);
add("slack." + key, "slack", cfg ? flatten(cfg[key]) : null, flatten(defs[key]),
cfg ? read.fields.includes(key) ? "file" : "default" : "unavailable", kind, undefined,
unchosen ? "Not chosen: when the connector starts, unrouted messages go to the default receiving agent (Agents & runtime)." : undefined);
unchosen ? receiver?.session
? "Not chosen: when the connector starts, unrouted messages go to the default receiving agent (Agents & runtime)."
: `Not chosen: this host has no receiving agent (Agents & runtime), so when the connector starts, unrouted messages go to ${LEGACY_INBOUND_DESTINATION} for compatibility, as before.`
: undefined);
}
// #192: the channel map as one read-only line, "match: channel" per entry.
add("slack.channelMap", "slack", read.cfg ? c.configuration!.channelMap.map((e) => `${e.match ?? "withheld"}: ${e.channel ?? "withheld"}`).join(", ") : null, "",
@@ -145,4 +145,19 @@ describe("the settings view", () => {
.toBe("Not chosen: when the connector starts, unrouted messages go to the default receiving agent (Agents & runtime).");
expect(settingsBrowser(store, null, home).entries.some((entry) => entry.key === "agents.default_receiver")).toBe(false);
});
it("withholds the receiving agent's description when the setting holds unsafe text; with no receiver, Slack's line says where unrouted messages go", () => {
const home = mkdtempSync(join(tmpdir(), "default-receiver-browser-"));
closes.push(() => rmSync(home, { recursive: true, force: true }));
const store = new SettingsStore(join(home, "config.json"));
const unsafe = "token=synthetic-example-value";
store.set("agents.operator_session", unsafe);
const view = settingsBrowser(store, null, home, { session: null, source: "none", state: "missing",
note: `agents.operator_session is "${unsafe}", which isn't an agent's session, so it's ignored` });
const row = view.entries.find((entry) => entry.key === "agents.default_receiver")!;
expect(row.application).toBe("Withheld: agents.operator_session holds credential-bearing or unsafe text, so the receiving agent isn't described here. Inspect it at its source.");
expect(JSON.stringify(view)).not.toContain("synthetic-example-value");
expect(view.entries.find((entry) => entry.key === "slack.inboundDestination")?.application)
.toBe(`Not chosen: this host has no receiving agent (Agents & runtime), so when the connector starts, unrouted messages go to ${LEGACY_INBOUND_DESTINATION} for compatibility, as before.`);
});
});
+2 -1
View File
@@ -146,7 +146,8 @@ person needs in their project or home folder and name that path.
A declared name is a claim, not a seat's own identity: for a team that acts only on seat identity, the attached
shell above is the route.
- **Who answers OpenRig's questions:** read `rig gateway participants` (its `default:` line) before the handoff.
- A default person is there: say requests and asks go to them, and don't offer again.
- A default person is there: say requests and asks that name nobody go to them (a pairing request or an ask can
name someone else), and don't offer again.
- Several people with no default: ask which should get requests nobody is named for (`rig gateway default <id>`),
or leave it. Never pick one yourself.
- Nobody is registered: offer it in a sentence or two. OpenRig's questions, such as another machine asking to pair,