-
Notifications
You must be signed in to change notification settings - Fork 3
plat 357
| Coordination | Value |
|---|---|
| Assigned agent | Claude Code |
| Ticket state | one call model (functions + per-caller conversations) on main; not deployed; live RTS check pending |
| Last synchronized | 2026-09-24 |
| Priority | P1 product |
| Category | integrations (runner-up: scheduler-runs) |
Crew-to-Crew calling had grown five overlapping ideas: crew_chat vs
isolated run destinations, fresh-per-call isolated trigger chats, free-form
call_target, typed functions and the default ask. They are now one model
(user doc: docs/crew-calls.md):
-
One way to call: functions.
askcovers free-form tasks; typed functions add validated inputs and results.connect_to_target,call_target,get_target_runandsend_to_target_runare removed;ask_function_updatecarries mid-run follow-ups. -
One place calls run: each caller's own continuing conversation with the
target Crew. An internal trigger (caller binding) always runs there
(
productWebhookTrigger.ownConversation), whateverrun_destinationit was saved with, and it is keyed by trigger ID only. The per-runtriggerID:runIDsuffix (isolatedAutomationID) is removed, so a caller's follow-up calls remember earlier ones. The Crew's main chat is for people. - External webhooks and schedules keep the main chat / own conversation choice. A webhook's own conversation now also continues across deliveries instead of starting fresh each time.
- UI: the Triggers tab is now Webhooks (external only), and caller bindings are listed under Functions → Callers with Disconnect. Destination labels read "Main chat" / "Own conversation".
-
MCP/CLI: each AgentWorks user's
ask_crewcalls continue one conversation with that Crew, so an external tool can chat with a Crew.
Migration: none needed. Existing caller bindings saved as crew_chat are
routed to their own conversation at dispatch. Old per-run isolated trigger
chats stay in history. Deferred: an optional one-line summary of each
finished call in the Crew's main chat.
Incident: the SDE Crew asked the PR-review gate workflow to review #149 with a
free-text ask. A free-text call could not set GITHUB_OWNER /
GITHUB_REPO / PR_NUMBER, so the gate used the saved PR_NUMBER=147, found
it already merged, skipped, and reported success.
Fix:
-
Workflows no longer have
ask. They offer only function triggers (kind: "function"): a fixed route and groups plusfunction: {name, description, inputs, allowed_callers}. -
Inputs are declared variables, typed (
string,integer,number,boolean), required or optional, optionally with an enum. They are set as that run's variables (WorkflowWebhookDelivery.VariablesandGroup). -
Checked before anything runs: a missing required input, an unknown or
mistyped input, or a bad group is refused
(
workflowFunctionArgs,dispatchWorkflowFunction). -
Auth: no URL and no secret. The server stamps the caller. A Crew or
workflow chat needs a user with edit access to the workflow; MCP/CLI needs
runs:executeand the token's workflow bound.allowed_callersnarrows this further. - Result: the run's outcome (status, error, steps) returns through the normal function call: directly, as an auto-notification, or by polling.
-
Surfaces:
- Builder:
manage_workflow_webhook kind=function; - Crews and workflows:
list_functions/call_function(and generated tools); - MCP:
list_workflow_functions/call_workflow_function/get_workflow_function_call; - CLI:
agentworks functions; - UI: a Functions section on the workflow's Webhooks tab.
- Builder:
-
define_function/delete_functionon a workflow now refuse and point to its Builder.
Not live-verified yet: needs a real function trigger on a workflow and one real call.
Crews and workflows can already call each other through secretless internal
triggers (connect_to_target / call_target / get_target_run /
send_to_target_run, f7130b4f8). A trigger call is a free-form task string
and the answer is whatever prose the target ends with. The caller cannot see
what a target offers, cannot rely on the shape of the answer, cannot get a
quick answer inside its own turn, and cannot ask a long call how it is going
without interrupting it.
Functions. A Crew or workflow declares named functions in
<target>/functions.json:
{"version":1,"functions":[{
"name":"run_login_flow",
"description":"Run the RTS login flow against a build",
"input_schema":{"type":"object","required":["build"],"properties":{
"build":{"type":"string"},"env":{"type":"string","enum":["staging","prod"]}}},
"result_schema":{"type":"object","required":["passed"],"properties":{
"passed":{"type":"boolean"},"failed_step":{"type":"integer"},"trace_path":{"type":"string"}}},
"instructions":"Run tests/login_flow.py with BUILD=<build> ...",
"created_by":"crew:alpha (Alpha Bot)", "created_at":"...", "updated_at":"..."}]}Schemas are a JSON Schema subset: object (properties, required),
array (items), string, number, integer, boolean, enum. A
function is managed like a trigger (define_function, delete_function,
list_functions); any Crew may define functions on any Crew (Crews are
shared read-write server-wide); workflows need owner/editor access.
Creation records the creator.
Calling. call_function(target, function, args) validates args
against the input schema, then dispatches through the target's standard
internal trigger binding (the same durable run and turn queue as
call_target: idle → now, busy → after the current turn). A Crew target
receives the function's instructions, the validated arguments, a call_id
and a result contract: it must end by calling
return_function_result(call_id, result), which is validated against the
result schema (an invalid result is rejected with the validation error so
the target can fix it; after two rejections the call fails). A Crew run
that ends without a result gets one retry turn, then the call fails with
its final text as the reason. A workflow target runs its plan with the
arguments in the payload; its result is the run's outcome (step outputs),
not schema-validated in this slice.
-
Fast path: if the call completes within 120 s,
call_functionreturns the result directly as its tool result. -
Slow path: it returns
{status:"running", call_id}and, unlessnotify=false, the caller's chat is resumed with an[AUTO-NOTIFICATION]carrying the result, failure or timeout (the same background-execution pipeline astrigger_and_auto_notify/call_target).
Default ask. Every Crew and workflow implicitly offers
ask(message: string) -> {answer: string} over its standard inbound trigger,
so a Crew with no declared functions is still callable (listed by
list_functions, callable via call_function, generated as <crew>__ask).
Its result is the target's final free-text reply; return_function_result
is optional. A declared ask replaces it. Targets are told to suggest a
typed function when the same ask keeps arriving.
Generated tools. For Crews and workflows tagged (#crew: /
#workflow:) or attached to the calling Crew, each function is also
registered as its own tool, e.g. rts_flow_tester__run_login_flow(build, env), with its input schema as parameters. Any other function stays
reachable through list_functions + call_function.
Progress and updates mid-run.
-
report_function_progress(call_id, message, percent?)— the target reports milestones (it is told to in the call instructions); the latest 10 entries are kept on the call record. -
get_function_call(call_id)— status, result/error, latest progress, and a short tail of the target's recent activity (last assistant text, current tool) read from its session events, without interrupting it.get_target_run(plain trigger calls) returns the same activity tail. -
ask_function_update(call_id, question)— Crew targets only: delivers a question into the target's running turn through the existingsend_to_target_runlive-input path; the target answers withreport_function_progress, which the caller sees on its next poll.
Guards. No self-calls. A call chain is propagated through in-flight
calls: a target already in the chain is refused (cycle), depth is capped at
4, and one chain may make at most 20 calls. return_function_result and
report_function_progress are accepted only from the call's own target.
UI. The Crew's Automation panel has a Functions tab next to
Schedules and Triggers (CrewFunctionsView): each function with its
inputs, returns, creator and last update (the built-in ask marked "Built
in"), and Recent calls (caller, status, latest progress; expandable to
the progress log and the result or error). The owner can remove a
function; "Edit in chat" sends a guided request to the Crew chat. Backed by
GET /api/crew-functions and DELETE /api/crew-functions/{name}
(owner-scoped like /api/product-webhooks).
- Call records live in memory (with a JSON copy under
<target>/functions/calls/); the auto-notification watch does not survive a server restart (same ascall_target),get_function_callfalls back to the saved copy. - Workflow results are not schema-validated, and workflows have no Functions tab yet (the tab and endpoint are Crew-only).
- "Recent calls" shows calls made since the server started (in-memory).
Auto-synced from docs/ on main. Edit there, not here.