Projects group the flow list in place: no new page or tab, the existing sidebar
list gains collapsible per-project sections plus a project filter. Everything is
behind PROJECTS_ENABLED in shared/feature-flags, and with no projects the list
renders exactly as it did before - flows-list.spec guards that.
FlowsList stays the single owner of the load, filter, sort and list state; the
grouping is extracted into a pure flow-grouping.ts and a presentational
flows-group component. A wrapper rendering N flows lists would have re-registered
the same list state and re-triggered the load N times. The group chrome mirrors
tasks-executions-list, which already implements collapsible groups, so the two
sidebars read as one product.
toFlowCreateRequest deliberately still carries no project. It builds the
full-replace PUT the editor issues on every save, so a project sent there would
be silently dropped each time; membership changes only through
assignFlowToProject. flow-mapper.spec guards it.
Deleting a project destroys its flows, so it gets its own dialog rather than a
wider ConfirmDialogService: it names the count, lists the flows, says that
finalized flows go too - which the flow list otherwise forbids - and requires the
project name to be typed. Widening the shared confirm service for one destructive
caller would have rippled through every other call site.
Moving a flow between projects is a menu item, not drag-and-drop: the sidebar is
320px with its own scroll, and the whole card is already a click target, so a
drag gesture would fight the open-flow gesture. Flow order inside a project uses
up/down arrows for the same reason.
Shared context is edited in a dialog modelled on the Global Inputs panel, which
is the mental model users already have for ${{global.x}}; the title toolbar shows
the inherited values read-only, because that is where prompts are written.
"Run project" creates and starts a run - creating alone would look like nothing
happened. Runs come back BLOCKED or STOPPED when a step needs inputs or failed,
and the UI says so instead of claiming progress. Project runs surface in /tasks
as sibling groups labelled with a project chip, derived in tasks-executor: no
second nesting level and no backend change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hasCancellableCall() was hardcoded to false (from an earlier commit that
gutted the real async call tracking), which silently blocked
minimizeCreateWithAi() from ever minimizing an in-progress AI flow creation
into the floating banner, and blocked closeCreateWithAi() from prompting to
cancel a running request.
restoreSessionForFlow() also never resumed polling for a call that was still
QUEUED/RUNNING when its snapshot was saved (e.g. after minimizing and
reopening, or a page reload), so a resumed session lost live progress
updates and could get stuck showing a stale phase.
Restores real hasCancellableCall()/cancelActiveCall() backed by the
submitMessage/getCall/cancelCall flow, resumes polling on restore for an
active call, and refreshes the session from the backend otherwise. Adds
getSession() across the assistant call service stack to support this.
Replace the direct draft/refine/fix/explain HTTP calls (which no longer exist
on the backend) with submitMessage + polling on getCall, wiring the
currentCall/progress-phase UI that was already scaffolded but never
connected. Intent (draft vs refine vs fix vs explain) is now inferred
server-side instead of guessed client-side. Updates the fake service and
specs to match.
The backend replaced GET /blocks/types/catalog with
GET /blocks/types/configurations/catalog. Same payload, same contract.
The container catalog endpoint is unchanged and is left alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
An execution cannot start until every requiredAuthorizations entry is
satisfied. A key of the form LLMProvider::<provider>::authorization is
answered with a vault secret id chosen from a picker filtered by that
provider, or from a credential created on the spot; anything else keeps
the literal-value panel it had.
A provider key never falls through to the literal-value panel, whatever
the provider catalog says or fails to say, so the API key itself can no
longer be pasted as the authorization value. The catalog is therefore no
longer part of the gate: an outstanding requirement blocks the start on
its own, and a failed catalog read is a notice with a retry.
The authorizations PUT answers with the recomputed execution, so its
response replaces the execution in the store rather than triggering a
list refresh, and the backend's 400 message reaches the user. A settled
requirement stays on screen with the credential label and a change
action, and a banner above the graph names the missing providers when
the context aside is collapsed.
The gate itself is now pure - buildAuthorizationGate and
isExecutionStartable in execution-viewer.utils.ts - and covered by
tests, since the component cannot be instantiated under the test
environment.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Lets the assistant panel pick the provider and model for a session, and
choose or create the vault credential an external provider needs. Status
messages for the credential endpoints come from the shared
CREDENTIAL_ERROR_MESSAGES map, so the assistant and the vault report
400/401/409 the same way.
Also moves the remaining Italian UI strings to English, matching the
rest of the application.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the services the credential flows need, following the existing
call/fake pattern: a base, an HTTP implementation and a fake for the
vault, the LLM provider catalog and the per-provider credential listing,
registered in all three environments.
The two fakes share an in-memory vault store that reproduces the
server-side rules of the credential listing (owner, active, provider,
soft delete), so a credential created from one panel shows up in the
other exactly as it does against the real backend.
Also teaches extractHttpErrorMessage the RFC 7807 `detail` field, which
is what the execution endpoints answer with.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
question/actionDescription arrive with literal ${{name}} / ${{global.name}}
placeholders since no block type ever persists a "resolved" prompt. Resolves
them client-side in a single pass (not sequential replaces, to avoid
re-resolving a value that itself contains ${{...}}), then renders each
value as its own expandable card or collapsed accordion instead of
concatenating long/array values inline into the surrounding text.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds GET /executions/{id}/node/{stepId}/iterations to the task
executions API and a recursive execution-tree component that lets
users navigate the full iteration history of looping containers
(not just the currently active one), rendered in the left rail
alongside the run list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements the frontend side of the control-flow engine's Fase 4-5
backend extension: presentation-only swimlanes for grouping nodes by
actor/responsibility, and a new IOType.JSON for structured input/
output descriptors. Both are additive/optional fields, backward
compatible with existing flows.
Swimlanes:
- Model: FlowLane (id, name, description, order, color), FlowData.lanes,
and an optional laneId on every block/container.
- Lane management (add/rename/reorder/color/delete) in a new "Lanes"
section of the title toolbar, mirroring the existing Global Inputs
editor. Deleting a lane clears laneId on any node that referenced it,
avoiding an immediate NODE_LANE_NOT_FOUND validation error.
- Real visual swimlanes on the rete.js canvas: horizontal color-coded
bands with labels that pan/zoom together with the nodes, backed by a
transform layer kept in sync with the area's live transform.
- Drag-to-reassign: moving a node updates its laneId based on the drop
Y position, but only for genuine pointer drags — programmatic moves
(initial load, clone, server-side node regeneration) are excluded via
the existing programmatic-translation tracking, so loading a flow
never silently reassigns lanes.
- A small lane badge on the node header confirms the current
assignment after a drag.
- The readonly/execution-view diff and patch logic now accounts for
laneId and lanes so live updates are detected correctly.
Also fixes a data-loss bug found while wiring this up: exportGraph()
rebuilt each node and the top-level FlowData as explicit object
literals that never carried laneId/lanes through, and flowFromApi()
never normalized lanes coming back from the backend — both would have
silently dropped the field on save/reload, the same class of bug fixed
earlier for biasAnnotations.
JSON IO type:
- Global input type picker now offers JSON alongside TEXT/FILE.
- Behavioral probe editor: JSON-typed mock outputs default to {} and
use the JSON textarea editor instead of a plain text input.
- portSelectableKinds (generic-node) includes JSON among the concrete
types offered wherever a port's kind is ANY.
Verified with the full suite (253/253, 21 new tests) and a live
end-to-end browser check: created two lanes, dragged a node from the
unassigned area into a lane (confirmed via the node's lane badge),
saved, and reopened the saved flow with the lane assignment intact.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mirrors the existing block-side bias impact feature onto containers,
reusing the same descriptor, form and error handling as the backend
integration doc specifies, rather than duplicating them:
- FlowContainer now carries biasAnnotations, matching FlowBlock.
- ContainersService/ContainersCallService gained
retrieveBiasCapabilities/retrieveBiasCapabilitiesForInstance,
backed by GET/POST /containers/types/{type}/bias-capabilities.
- bias-annotations and behavioral-probe-editor now accept FlowNode
instead of FlowBlock only; the probe editor dispatches capability
lookups to BlocksService or ContainersService based on nodeFamily,
so container annotations reuse the exact same shared components
and UI instead of a second, parallel implementation.
- container-node gained the bias badge, the bias-annotations panel,
and preserves annotations across recreateContainer() the same way
generic-node already does across block regeneration.
- task-execution-viewer's bias-rerun candidate list now includes
containers with executable probes, not just blocks.
Also fixes a real data-loss bug found while wiring this up:
rete-editor's exportGraph explicitly stripped bias annotations from
containers on every flow save, silently discarding anything entered
through the new panel.
Verified with the full suite (238/238, 6 new tests) and a live
end-to-end browser check: an annotation added to a container only
offered INPUT_TRANSFORMATION/OUTPUT_TRANSFORMATION (per the fake
container capabilities), and survived a save + reopen of the saved
flow, proving the exportGraph fix actually round-trips the data.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bugs:
- blocks.ts/containers.ts: reset toInit on a failed initial catalog load so
the next call retries instead of leaving the catalog permanently empty.
- task-step-node.ts: align isEmptyDisplayValue with generic-node's
isMissingValue so a field renders consistently between the editor and the
execution view for the same schema.
- flows-call.fake.ts/assistant-call.fake.ts: wrap synchronous throws in
defer() so catchError() on the caller side actually intercepts them.
- utilities/rete-editor.ts: remove a no-op ternary and a leftover debug log.
- stores/flow-editor.ts: save() now returns a handled error instead of
crashing on a non-null assertion when no flow is loaded.
- Removed leftover console.log statements (title-toolbar, flow-item,
editor-sidebar).
Dead code removed: the unused admin-users page, editor-sidebar's unused
createNewBlock(), rete-editor's unused flowChanged output, and the broken,
uncalled ListStateViewHolder.create().
Refactors (duplication called out by the same review):
- New session-guard.ts factory backing authGuard/adminGuard.
- tasks-executor's formatDuration now reuses the shared util.
- New services/shared/http-error.util.ts replacing the duplicated
extractHttpErrorMessage/toHttpError in admin-call.ts and authorization-call.ts.
- New services/bias/bias-error.util.ts unifying the three different ad hoc
error-message extractions across the bias-* dialogs.
- New pages/admin/admin-access.util.ts and utilities/temporary-signal.ts
replacing the duplicated redirectOnAdminAccessDenied and auto-dismiss-toast
patterns.
- New shared ModalShellComponent adopted by the three bias-* dialogs (their
backdrop/header/footer CSS was already byte-identical); new
password-form-validators.ts and a shared password-dialog-chrome.css
collapsing admin-reset-password-dialog and change-password-dialog, which
duplicated their entire validation logic and CSS.
- New services/shared/{catalog-store,empty-node-cache,pending-sync-counter,
deep-clone,flow-node-mapping}.ts: BlocksService/ContainersService and their
*-call.ts mappers were near line-for-line duplicates (which is exactly how
the toInit bug ended up in both).
- New shared/nodes/node-focus-modal-controller.ts: generic-node.ts and
container-node.ts had ~150 identical lines of focus-modal/body-scroll-lock
plumbing.
Confirm-dialog, node-settings-dialog, human-interaction-dialog and
subflow-preview-dialog were deliberately left out of the modal-shell
extraction: each has a meaningfully different structure and no way to verify
visually here, so forcing them into a shared shell was judged higher risk
than the cosmetic-only bias-*/password dialogs, whose CSS was already
byte-identical.
61 test files / 230 tests passing; `ng build` and `ng test` green throughout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Finishes the bias impact experiments plan (docs/bias-impact-experiments-plan.md
steps 7-12):
- Side-effect policy selector: reuse the .llm-warning visual language for the
external side-effects banner, add a REQUIRE_CONFIRMATION note.
- Full-flow compare ("Compare with baseline"): new bias-compare-dialog
(service + host) triggering compareBiasExecutions and opening the shared
report viewer; inline errors read from errors[].message/detail.
- Persisted reports: new bias-impact-report-list (list + detail in one view)
wired into a new "Bias impact reports" tab in task-execution-viewer;
404/403 on report detail show the same inline message on purpose.
- Canvas: annotation badge on generic-node (count, executable-probe
indicator, severity from the backend catalog); new
BiasComparisonViewStateService driving bias-active / downstream-changed /
routing-change highlighting on task-step-node and custom-connection, fed by
a highlightOnCanvas event from bias-impact-report-viewer wired in all three
places that render it; legend + "back to normal view" action in the canvas
toolbar.
- Fixed a bug where the bias variant context badge only rendered for
simulated executions.
- Fixed "Measure bias impact" to stay visible-but-disabled with an
explanatory tooltip while the baseline hasn't reached a final state,
instead of being hidden outright, per the §12 checklist.
- Added a Retry action to the compare dialog's error state for parity with
the report list.
- Added the end-to-end facade flow test (annotation -> capability -> isolated
experiment -> report) plus coverage for all new components/services.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Fix memory leak in task-executions polling (DestroyRef)
- Fix race condition in flows service (error recovery)
- Add centralized NotificationService + GlobalNotificationComponent
- Convert main routes to lazy loading (loadComponent)
- Remove static state from auth interceptor
- Write real unit tests (guards, stores, services)
- Strengthen HFNodeData types (explicit properties)
- Extract pure functions from TaskExecutionViewer into utils
- Add console.warn logging to guards on access denied
- Expand runtime config (apiUrl, assistantEnabled, tourModeAlwaysOn, turnstileEnabled)
- Document FormUtility and ListStateViewHolder base classes
- Remove unused lucide-angular dependency
- Replace ngModel with signal bindings in flows-list/blocks-list
- Use :where() for global mat-form-field override
- Document design system tokens in styles.css