The external action itself is a visible run. A single square node fully represents it; no extra participant node is needed.
External Program Run Nodes
Defines DAG views, completed summary handoffs, and chat history for a user directly running a function from Programs, fn-form, or another external entry point. This page covers top-level runs, terminal delivery, and projections of existing execution events, not internal workflow stages.
An external invocation appears in the conversation layer as a rounded-square Program/Function node, branching directly from ROOT without synthetic User or LLM nodes. Substantive workflow results are written to files by default; successful Conclusion handoffs are generated by a separate summary function, not the workflow result body. For other terminal states, the frontend deterministically generates Conclusion from status and error.
End-to-end flow: execution, handoff, and chat projection
Node vocabulary
Normal chat remains circular User → triangular LLM. An external Program is another explicit action under ROOT, not a simulated chat turn.
A dedicated avatar distinguishes a top-level Program from ordinary assistant messages. The title reuses the existing Thinking/Functions strip’s typography, color, and chevron with more complete information. Expanding directly shows the current RuntimeBlock, without redesigning internal function calls.
Chat UI visual contract
RuntimeBlock and TreeStep, with the message footer at its own chat-message level.Conclusion and action footer remain outside. Conclusion uses ordinary chat-body typography, without blockquotes, left borders, or quotation indentation; the footer always stays at the bottom of the complete message and never collapses with execution.Four compact-title states
This workflow read the materials, checked their contents, and organized the results. It retained complete execution records as requested and did not repeat the report body in chat.
- Read and checked input materials.
- Organized content and checked consistency.
- Saved task artifacts and checked execution status.
The task is complete; no further user action is needed.
Workflow failed: Permission denied.
Workflow cancelled; 3 calls were recorded before cancellation.
Expansion, collapse, and click behavior
| User action or state change | Chat UI behavior |
|---|---|
| Run just created | Immediately show the dedicated avatar and compact title. Unless the user intervenes, retain existing ExecutionStrip behavior: expand by default while Running.... |
| User clicks the title row | Toggle only arguments, execution tree, and usage. Terminal Conclusion and message footer do not collapse with execution. |
| Expanded state | Fully reuse current function-call rendering; this design does not change the root function row, nested calls, Details, Copy, Retry, Edit & re-run, or version navigation. |
| Run ends without user intervention | Retain existing ExecutionStrip logic to auto-collapse and update the terminal title. On successful completion with workflow_handoff_v1, Conclusion shows the summary handoff. Capped, cancelled, interrupted, or failed states do not call the LLM summary; the frontend creates a deterministic status or error Conclusion. The footer always follows Conclusion. |
| User has manually expanded or collapsed | Run-state and event updates do not override the user’s choice. |
| Title exceeds narrow-screen width | Keep one line with end truncation and a visible chevron; retain the full title as the button’s accessible name. |
Run state changes the square, not the node
Data semantics and visual projection
| Layer | Current semantics | Required design |
|---|---|---|
| Persistence | A manual top-level function is one role=code node; caller is empty, and predecessor is the current HEAD, or ROOT in an empty session. | Do not change storage fields or add synthetic User/LLM nodes. |
| Ownership after history reload | caller represents call ownership; predecessor records only conversation-layer chronology. | caller always renders as a top-level Program when empty or ROOT, even if predecessor points to an assistant. Only a nonempty caller pointing to an assistant places the call inside that message’s runtimeChildren; never infer call ownership from predecessor. |
| DAG conversation layer | Manual top-level functions belong to the conversation layer and are visible by default; persisted predecessor still points to HEAD at call creation. | The session overview uses derived ROOT → Program projection edges to lay out rounded squares as direct ROOT children. This overview edge is not persisted and does not remove or rewrite predecessor. Detailed views may still read the original predecessor when chronology is needed. |
| Node label | Code nodes already have function name, status, arguments, and output. | Show at least the function name and existing English status in the graph; arguments and output belong in Details. |
| Status updates | Run start and end update the same code node. | Position and shape remain unchanged; only outline and status text change. |
| Chat title | Top-level Runtime already provides function name, status, timestamps, and contextTree. | Generate a one-line title as “function name · status · duration · summary.” Keep existing English status terms; derive duration from message time and current time or root-node duration. |
| Title summary | While running, count current execution-tree nodes; after completion, the root node has output or error. | Show the currently observable execution-tree node count while running; a one-line output preview on completion; a one-line error preview on failure; and the current node count on cancellation. Only normalize whitespace and truncate; do not call an LLM. |
| Terminal conclusion | The old summary actually equals the complete return value of workflow(), incorrectly treating report content as a chat summary. | After successful workflow completion, call an independent tool-free summary function. It reads the original task, function names and states, and up to 240 characters of operational handoff preview for each function=agent step in the execution record. It reads no call arguments, complete artifact bodies, or result summaries from non-agent functions, preventing intermediate results from entering through later call arguments. Output follows natural chat structure: usually 2–3 overview sentences, optionally a short numbered process for multi-stage tasks, then a completion assessment. Single-stage tasks may omit the list. Include citations, paths, and warnings only when useful for handoff, not as fixed fields. Previews must not restate substantive conclusions. Complete artifacts are written to the current session working directory by default and retained only in files and workflow state. |
| Summary call | The frontend makes no additional model call. | The additional LLM summary call occurs only in the backend on successful workflow completion, reusing the current session’s selected provider/model with tools disabled and a JSON-object response requirement. Call failure or malformed output uses a conservative fallback that does not claim artifact completion, does not change the completed state, and never falls back to showing complete artifacts. Capped, cancelled, interrupted, and failed states skip LLM summary, while the frontend still generates deterministic status or error Conclusions. |
| Public handoff schema | Complete raw result, call arguments, error bodies, and per-step results remain in workflow state. | summary_kind is fixed to workflow_handoff_v1; summary is short conversational Markdown, with no fixed item count or fields; return_result is computed only by a deterministic guard over the original task, with explicit negation taking precedence over direct-return wording; result defaults to null, reading raw result from state only when the guard is true. Public items uses a safe field allowlist retaining only step identity, status, hashes, and timestamps; public revisions retains only version and time. Arguments, results, and error bodies do not enter the public payload. |
| Legacy artifact delivery | Historical code.py/SINGLE-run planner examples allow return findings or direct return agent(...). | These rules serve explicit legacy resume only; new creation is prohibited. Current create/revise projects use the pre-publication author contract. Substantive artifacts default to the current working directory and enter chat results only when the task explicitly requests direct body return. |
| Expanded content | Internal function calls form the execution layer through caller; chat already has RuntimeBlock, TreeStep and Details. | Fully reuse existing components and interactions without a Planner-, Agent-, or workflow-specific visual layer. |
| Runtime selection | Direct runs still belong to the current session; the session picker’s provider_override/model_override selects that session’s model, falling back to the session agent configuration when no explicit override exists. | Before creating the function subprocess, the parent resolves the same selection and explicitly passes a provider/model snapshot. The subprocess must not override it with global auto-detection. |
| footer | The top-level footer already has time, Copy, Retry, Edit & re-run, and version navigation. | The footer is outside execution and always at the message bottom: after Conclusion when present, or after the collapsed title otherwise. Its top spacing reuses the 12px assistant-body-to-footer paragraph gap; visibility conditions, button order, and behavior remain unchanged. |
Current state, references, and decisions
| Reference | Existing capability | Gap and decision |
|---|---|---|
layout.html | ROOT=diamond, User=circle, LLM=triangle, Code=square; scenario 5 already defines manual functions. | Adopt the existing square directly; add no new node shape. |
dag/overview.md | Distinguishes predecessor from caller; a manual top-level invocation has only one code node. | Preserve predecessor chronology in storage; derive ROOT → Program layout edges separately for the session overview without fabricating caller messages. |
dag/rendering.md | Manual top-level functions belong to the conversation layer and are visible by default. | Currently invisible nodes are an implementation gap, not grounds to change the data model. |
Current RuntimeBlock | shows the execution tree when present, or a running StepRow otherwise; a message footer already exists at top level. | The top-level container reuses ExecutionStrip expansion-state logic; only execution content and usage enter the collapsible area, with the existing footer rendered unchanged outside it. Nested calls retain their existing rendering. |
Current ExecutionStrip | Expand by default while running; collapse on completion if untouched; user choices take precedence. Use small title text and a chevron. | Adopt its state rules and visual density directly; do not write a Program-specific collapse state machine. |
| New graph framework | No missing capability requires one; the current SVG renderer already supports code squares and state outlines. | Do not add React Flow, Cytoscape, or other dependencies. |
Adopt: Existing code squares, branch colors, English status terms, ExecutionStrip collapse rules, and RuntimeBlock expanded content.
Adapt: Add a dedicated top-level Program avatar; make the compact title more detailed than Thinking/Functions with function name, status, duration, and result preview.
Reject: Fabricated User/LLM nodes, a new lane, large cards, percentage progress, a workflow-specific internal timeline, and complete artifact bodies presented as work summaries.
Conclusion. Root-function, nested-function, argument, output, and error rendering after expansion all follow the current RuntimeBlock; no dedicated internal process design is added for auto_workflow.Implemented acceptance criteria: summary and chat UI
| Entry point | Observable result |
|---|---|
| Direct function run after refreshing existing chat | Even when the run’s predecessor is the previous assistant, an empty caller preserves a top-level Program. Successful runs with workflow_handoff_v1 keep showing Conclusion, and the message footer remains visible. Internal runs with nonempty caller pointing to an assistant remain inside that assistant message. |
| Chat record just created | Show the dedicated Program avatar and “function name · Running... · duration · step count” title; while running, expand by default the existing RuntimeBlock. |
| User collapses the top-level run | Keep the avatar, one-line title, existing Conclusion, optional direct result, and message footer visible; only arguments, execution tree, and usage become invisible and occupy no layout space. |
| Chat receives execution events | The expanded area continues updating through existing TreeStep; the title step count updates, without overriding manual expansion/collapse. |
| Workflow completes successfully | Auto-collapse if untouched and update the title to Completed. The backend summary call produces a short conversational handoff: overview, optional numbered main steps for multi-stage work, and final completion assessment. No mandatory citations, paths, fixed item count, or report-body repetition. Time and action footer follow Conclusion with the same 12px paragraph gap as assistant messages. |
| Workflow capped, cancelled, interrupted, or failed | Show the matching terminal title without an LLM summary call. The frontend deterministically generates status or error Conclusion from terminal state, call count, and existing error. The footer follows Conclusion; neither enters the collapsible area. |
| Task requests a report, code, or table without direct return | Write substantive artifacts to the current working directory. Conclusion summarizes completed steps and completion status, mentioning paths only when they help the user continue. The payload carries no complete result for chat rendering. |
| Task explicitly requests a direct answer or body return | The summary function still produces a work summary; a separate task-only authorization decision sets return_result=true. Conclusion displays the complete direct result after the work summary. |
| Task explicitly forbids returning the body in chat | Negation takes precedence. The summary model, agent previews, and wording such as “do not write files” cannot set return_result to true; public payload result remains null. |
| Summary call fails or returns malformed output | Show a conservative fallback without claiming files were generated. Workflow remains Completed, complete raw result stays out of the public payload, and the error remains only in state. |
| Narrow screens and keyboard | Keep the title on one truncated line with visible chevron. The button supports keyboard access and exposes aria-expanded, with the complete summary readable by assistive technology. |
| Direct run after selecting a custom session provider/model | The function subprocess uses the same provider/model, does not access another session or the global picker, and does not fail because the provider is absent from the built-in PROVIDERS table. |
| Compatibility | Ordinary internal tools invoked by LLMs remain grouped in the execution layer; existing User, LLM, merge, and spawn shapes stay unchanged. |
Future DAG projection acceptance criteria
| Entry point | Observable result |
|---|---|
From Programs in an empty session, run auto_workflow | Immediately show exactly one rounded-square ROOT child in the DAG, labeled auto_workflow from Running.... |
| Run from Programs in a session with existing chat | Preserve the existing User → LLM structure; add a Program node directly under ROOT in the same conversation layer. |
| Overview projection handles existing predecessor | Persisted predecessor remains the current HEAD; the overview renderer derives ROOT → Program layout edges from empty caller, without incorrectly treating the Program as an assistant’s internal call. |
| Run completes, fails, or is cancelled | Update the same Program node’s outline and status without moving it or creating a second result node. |
This implementation task
| Scope | Contract |
|---|---|
| Production files | workflow/__init__.py run-summary logic produces a structured handoff; runtime-summary.ts treats only version-marked handoffs as Conclusion and exposes raw results only when return_result=true; runtime-block.tsx separately renders the work summary and explicitly requested direct result within the same Conclusion. |
| Public entry point | Directly run a user-level auto_workflow from Programs or fn-form in a session; refreshing history must yield the same conclusion. |
| Compatibility and failure | Non-workflow Function rows, running workflows, and nested LLM tool calls remain unchanged. Incomplete JSON, failure, and cancellation must not cause rendering exceptions or fabricated success counts. |
| Explicit exclusions | No new dependencies, DAG changes, button content/order/behavior changes, or changes to ordinary assistant messages and nested functions. The summary function must not use tools or execute the task again. |
| RED / GREEN | Python public-entry tests verify that the current package Workflow’s independent summary receives no complete results, call arguments, or non-agent step results, only short agent operational handoffs. Intermediate results remain internal state even when referenced by later call arguments. Explicit legacy-resume tests separately preserve SINGLE/code.py file-delivery compatibility and must not create new legacy runs.npm run check:chat-ui covers new handoffs, explicit results, and suppression of complete bodies from old payloads; then run Web typecheck, full Web checks, documentation links, and diff checks. |
Implementation evidence
runtime-block.tsx adds only a dedicated avatar and ExecutionStrip for top-level Programs; execution tree and usage share one collapsible region. The footer follows both that region and Conclusion, with execution-strip.css reusing the assistant message’s 12px body-to-footer gap. Nested LLM tool calls retain their original structure. Successful Conclusions treat only content marked workflow_handoff_v1 as a work summary and show the complete result separately afterward only when return_result=true; for capped, cancelled, interrupted, and failed states, runtime-summary.ts deterministically generates status or error Conclusions without a model call. The old payload’s raw summary is no longer treated as a work summary.Backend implementation:
workflow/__init__.py calls one independent tool-free summary function after the current package Workflow succeeds. Inputs are the task, executed function names, statuses, raw-result length, and only agent-step operational handoffs capped at 240 characters. No call arguments, complete raw results, or other function result summaries are included. A deterministic guard reading only the original task authorizes body disclosure. Malformed or failed summaries produce a safe handoff with errors retained only in state. Historical planner and SINGLE file-delivery rules remain only for explicit legacy resume.Automated checks:
pytest -q tests/component/agent/test_agentic_workflow.py covers current package Workflow and explicit legacy resume, whose creation paths must not mix; full Web checks, Web typecheck, documentation links, and git diff --check are also verified.Real acceptance: Historical workflow payloads lack the
workflow_handoff_v1 marker, so the old 12198-character task result is no longer displayed as Conclusion. Real 18100 acceptance of new handoffs must use a newly created workflow run and only /Applications/OpenProgram.app, the default ~/.openprogram from 18100; do not start 18200. DAG node projection was unchanged by this implementation.