Frontend review (Confluence 491028481) asked to drop the {{#…#}} wrapper (looks
like a Dify template variable) and unify the two separators into one. New token:
[§<kind>:<id>[:<label>]§]
- [§…§] wrapper uses the section sign § (U+00A7) — never appears in Dify template
syntax ({{var}} / {{#a.b#}}) nor in normal prompt text, so zero collision (a
cleaner argument than the old colon-form-invisibility one).
- single ':' separates all three fields; label is the trailing remainder and may
itself contain ':'.
- MENTION_PATTERN / residual-marker regex / scrub all updated; legacy {{#context#}}
and {{var}} forms still pass through untouched.
Pure delimiter swap — parse/expand/scrub/validation semantics unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A {{#knowledge:<id>#}} mention id is user-editable prompt text; a non-UUID id
hit the UUID-typed datasets query and 500'd validate/save (found live in E2E).
_dataset_rows now filters non-UUID ids before querying — they can never match a
row, so they read as missing (placeholder semantics).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Hard rule (PRD: human involvement must be slash-referenced or save errors):
every configured human contact must appear as {{#human:<id>#}} in its surface's
prompt (matched via any identity alias: id/contact_id/human_id/email/name;
identity-less contacts are skipped). Error code human_involvement_not_referenced,
enforced on both the agent soul prompt and the workflow job prompt.
Soft findings (never block save), returned by validate and attached to save
responses as `validation`:
- knowledge_retrieval_placeholder: dangling {{#knowledge:<id>#}} mentions (not
in config, or configured-but-deleted in DB) are kept with a placeholder name
(mention label, fallback "Knowledge <id8>") per the 0522 consensus, instead of
being dropped or rejected; knowledge not referenced is fine.
- mention_target_missing: skill/file/tool/cli_tool/human/node_output/output
mentions whose id resolves to no configured item.
- mention_malformed: mention-shaped markers the strict grammar rejects
(degraded to plain text at runtime by the scrub pass).
Response models gain warnings/knowledge_retrieval_placeholder (validate) and
validation (save); validate endpoints now resolve dataset existence via the
tenant-scoped dataset lookup.
Design: Confluence 490012681 §5. Depends on the ENG-616 mention contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The agent-composer candidates endpoints returned hardcoded empty arrays; node_id
and tenant were ignored. Now:
- workflow variant: previous_node_outputs computed from the draft workflow graph
(WorkflowGraphTopology extracted from agent_v2 validators into
core/workflow/graph_topology.py + new upstream_node_ids BFS). Output
enumeration matches the Node Output Inspector: start variables + recorded
sys.* are static, Agent v2 upstream nodes use their binding's effective
declared outputs, all other kinds infer from the latest draft-run variables
(inferred: true). Entries are emitted in the stored
WorkflowPreviousNodeOutputRef shape (selector/node_id/output/name + display
extras) so a selected candidate writes back into the config verbatim.
human_contacts = the binding's configured node_job items (0522: defined only).
- soul variant (both variants): skills_files / cli_tools(enabled) /
knowledge_datasets (enriched via DatasetService, dangling marked missing for
the placeholder UX) / human_contacts from the agent's current soul config;
dify_tools from BuiltinToolManageService.list_builtin_tools (same source as
the tool selector; daemon outage degrades to an empty list, never a 500).
- per-list cap 200 + response-level truncated flag; all queries tenant-scoped;
empty config returns empty lists.
- controllers pass tenant_id/user_id/node_id (with_current_tenant_id +
with_current_user_id).
Design: Confluence 490012681 §3.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slash-menu references are stored inline in the plain-string prompt as
{{#<kind>:<id>[|<label>]#}} tokens (kind: skill/file/tool/cli_tool/knowledge/
human/node_output/output). Mentions are pointers into the Agent config lists;
label is a plain-text fallback only — the backend re-resolves by id. The colon
form is invisible to the legacy template parsers, so no syntax collision.
- services/agent/prompt_mentions.py: parse/expand/scrub + per-surface allowlists
+ soul/node-job resolvers (tool ids accept provider|provider_id|plugin_id
qualified aliases).
- composer_validator: enforce §2.4 allowlists at save/validate
(mention_kind_not_allowed, mention_limit_exceeded as stable message codes).
- runtime: both run-request builders expand mentions to canonical names before
sending to the agent backend; node_output expands to its reference name only
(the value stays in the Workflow context block, per design §4.2); a final
scrub pass guarantees no {{#…#}} marker ever reaches the model, while legacy
{{#context#}} / {{#a.b#}} forms pass through untouched.
Design: Confluence 490012681 §2/§4. No DB changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>