mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(flows): resolve a non-owner Slack DM recipient via a lookup node (#4955)
This commit is contained in:
@@ -400,6 +400,68 @@ mod tests {
|
||||
ask the user for their member id in one question rather than \
|
||||
guessing a channel"
|
||||
);
|
||||
|
||||
// Positive: non-owner DM resolution — the prompt must teach the
|
||||
// builder to resolve a NAMED recipient who is NOT the connected
|
||||
// owner via a lookup node, not just the owner's own
|
||||
// `platform_user_id`. This guidance must be PLATFORM-AGNOSTIC (no
|
||||
// toolkit-specific slug hardcoded) — the same shape applies to
|
||||
// Slack, Discord, Telegram, or any other messaging toolkit.
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("is NOT the connected"),
|
||||
"standing prompt must teach the non-owner DM case explicitly"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("platform-agnostic"),
|
||||
"standing prompt must state the non-owner DM guidance is \
|
||||
platform-agnostic, not tied to one toolkit"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("search_tool_catalog { query, toolkit }"),
|
||||
"standing prompt must teach resolving the lookup action via \
|
||||
search_tool_catalog scoped to the TARGET toolkit, rather than \
|
||||
hardcoding one platform's slug"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("tool_call` node upstream of the send"),
|
||||
"standing prompt must teach wiring the lookup as a tool_call \
|
||||
node upstream of the send node"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("resolves to exactly one match"),
|
||||
"standing prompt must require a name search to resolve to \
|
||||
exactly one match before binding it without asking"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("ask the user to confirm which person"),
|
||||
"standing prompt must preserve the safety rule: never message an \
|
||||
unverified same-name match, ask instead when ambiguous"
|
||||
);
|
||||
assert!(
|
||||
STANDING_PROMPT.contains("Check the send action")
|
||||
&& STANDING_PROMPT.contains("open conversation"),
|
||||
"standing prompt must teach checking the send tool's own contract \
|
||||
for a required open-conversation step, handled generally via the \
|
||||
contract rather than a single-platform special case"
|
||||
);
|
||||
|
||||
// Negative: none of the non-owner DM guidance may hardcode a
|
||||
// toolkit-specific action slug or arg name — the reviewer flagged an
|
||||
// earlier draft of this guidance as Slack-only, which violates the
|
||||
// platform-agnostic rule.
|
||||
for banned in [
|
||||
"SLACK_FIND_USERS",
|
||||
"SLACK_LIST_ALL_USERS",
|
||||
"config.args.email",
|
||||
"exact_match",
|
||||
] {
|
||||
assert!(
|
||||
!STANDING_PROMPT.contains(banned),
|
||||
"standing prompt's non-owner DM guidance must not hardcode \
|
||||
the platform-specific `{banned}` — it must stay \
|
||||
platform-agnostic (any messaging toolkit)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -623,6 +623,48 @@ into exactly one bucket before you write the node:
|
||||
`#channel` name) — no need to ask. Only if `platform_user_id` is null
|
||||
for that connection, ask the user for their member id in ONE concise
|
||||
question rather than guessing a channel.
|
||||
- "DM `<name>`" / "message `<name>`" where `<name>` is NOT the connected
|
||||
owner (no matching `platform_user_id`) → you don't have their platform
|
||||
user id up front, and guessing one is unsafe. This shape is
|
||||
**platform-agnostic** — it applies the same way whether the
|
||||
destination toolkit is Slack, Discord, Telegram, or any other
|
||||
messaging app. Don't ask immediately — resolve it:
|
||||
1. `search_tool_catalog { query, toolkit }` scoped to the TARGET
|
||||
toolkit to find its user-lookup action — a "find user" / "lookup by
|
||||
email" / "list users" style action, whatever that platform exposes
|
||||
(never assume a slug across toolkits; always search for it).
|
||||
2. Wire that lookup as a **`tool_call` node upstream of the send**.
|
||||
3. Prefer an **email / exact lookup** when the platform offers one —
|
||||
that's unambiguous, so bind its result directly with no question.
|
||||
A **name search** can return multiple people: only bind it straight
|
||||
through when it resolves to exactly one match; otherwise this is
|
||||
bucket 3 — **ask the user to confirm which person / their email**
|
||||
rather than messaging an unverified same-name match. If the
|
||||
toolkit's lookup action can't resolve the person by name or email at
|
||||
all, fall back to its "list users" style action plus a downstream
|
||||
`transform`/`code` filter on an identifying field (email/display
|
||||
name/etc).
|
||||
4. Bind the resolved id into the send node's recipient arg with an `=`
|
||||
expression off the lookup node — use `get_tool_contract` to find the
|
||||
exact output field and confirm with `dry_run_workflow` rather than
|
||||
guessing — same as the owner path above.
|
||||
5. **Check the send action's own `get_tool_contract` for a required
|
||||
"open conversation" step first.** Some messaging toolkits require
|
||||
opening/creating a DM conversation for a user id before you can send
|
||||
to it; others accept a user id as the recipient directly and
|
||||
open/reuse the DM automatically. Never assume either way — if the
|
||||
contract names a separate open/create-conversation action as a
|
||||
prerequisite, wire that `tool_call` too, between the lookup and the
|
||||
send.
|
||||
|
||||
Worked example (illustrative, not tied to one platform) — "every
|
||||
Monday at 9am, message alan@acme.com his open tickets": `trigger`
|
||||
(schedule, Mon 09:00) → `tool_call` `find_alan` (the target toolkit's
|
||||
user-lookup action, args grounded via `get_tool_contract`, e.g. an
|
||||
`email` arg) → `tool_call` fetching the tickets → (an
|
||||
open-conversation `tool_call` first, only if that toolkit's contract
|
||||
requires one) → `tool_call` `dm_alan` (the toolkit's send action,
|
||||
recipient arg bound to `=nodes.find_alan.item.json.data.<id_field>`).
|
||||
- Exactly one connected account for the toolkit the step needs → that
|
||||
account (`list_flow_connections` / `composio_list_connections` tell
|
||||
you this; don't ask "which Gmail?" when there's only one).
|
||||
|
||||
Reference in New Issue
Block a user