-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Human-in-the-Loop (HITL) system implementation #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add approval_requests database schema and migration - Create approval-gate component with parameters for title, description, timeout - Add Temporal signal definition for approval resolution - Register component in worker TODO: - Modify Temporal workflow to handle approval signals - Create backend API endpoints for approving/rejecting - Create frontend UI for viewing and actioning approvals Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
- Add ApprovalsService with CRUD operations for approval requests - Add ApprovalsController with authenticated endpoints - Add public approve/reject endpoints with secure tokens - Add signalApproval method to TemporalService for signaling workflows - Wire up ApprovalsModule in app.module.ts Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Worker changes: - Add approval-requests schema for worker DB access - Create approval activity for creating requests in DB - Modify workflow to handle approval gates with Temporal signals - Wire up approval activity in dev worker Frontend changes: - Create ApprovalsPage for viewing/actioning approvals - Add approvals route to App router - Add Approvals navigation item to sidebar The approval flow: 1. Approval Gate component detects pending approval 2. Worker creates approval request in database 3. Workflow waits for signal using condition() 4. Human approves/rejects via UI or public link 5. Backend sends signal to Temporal 6. Workflow resumes with approval result Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
- Create approvals.dto.ts with Zod schemas using createZodDto - Update controller to use proper DTOs with Swagger decorators - Regenerate OpenAPI spec with approval endpoints - Regenerate backend-client with approval methods - Update ApprovalsPage to use api.approvals client methods Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…node trace emission Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…omponents Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…n folder and update category to manual_action Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
… context variables Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…s for manual actions
- Introduced 'variable-list', 'form-fields', and 'selection-options' parameter types.
- Created 'FormFieldsEditor' and 'SelectionOptionsEditor' for interactive node design.
- Refactored 'ParameterField' to generically support specialized editors.
- Added dynamic variable interpolation ({{variable}}) support for form fields and labels.
- Upgraded Manual Approval, Selection, and Form nodes with interactive Designers.
- Improved editor robustness to handle legacy JSON/string data safely.
- Updated Action Center and Workflow runner to support dynamic ports and resolved data output.
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…ionView Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
…olutionView Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
9a17283 to
07f6064
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!signalReceived) { | ||
| // Timeout occurred | ||
| console.log(`[Workflow] Human input timeout for ${action.ref}`); | ||
| throw new Error(`Human input request timed out for node ${action.ref}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark timed-out human inputs as expired/cancelled
When the wait times out, the workflow throws immediately but never updates the human_input_requests record, so the request stays pending in the database. That leaves stale items visible in the Action Center and allows users to attempt a resolution that will signal a closed workflow (likely throwing from the backend). Consider calling the cancellation/expiry activity before throwing so the request status is consistent with the timed‑out run.
Useful? React with 👍 / 👎.
| if (request.inputType === 'selection') return selectedOptions.length === 0; | ||
| if (request.inputType === 'form') return parsedInputSchema?.required?.some((k: string) => !formValues[k]); | ||
| return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allow required boolean/zero form values
The submit button disables when any required field is falsy. This blocks valid inputs like false for required booleans or 0 for required numbers (both evaluate falsy), so users cannot submit those legitimate values. The check should only treat undefined/empty string as missing, not false or 0.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| async getById(id: string): Promise<HumanInputResponseDto> { | ||
| const request = await this.db.query.humanInputRequests.findFirst({ | ||
| where: eq(humanInputRequestsTable.id, id), | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scope human-input lookups by organization
This lookup only filters by id, so any authenticated caller who learns a request ID can read (and then resolve, via resolve()) another org’s human-input request. In a multi-tenant deployment, that’s a cross-tenant data leak and authorization bypass because the AuthGuard only attaches org context and there’s no organizationId predicate here. Please require org context (as other services do) and add organizationId filtering to list/get/resolve queries.
Useful? React with 👍 / 👎.
| if (!signalReceived) { | ||
| // Timeout occurred | ||
| console.log(`[Workflow] Human input timeout for ${action.ref}`); | ||
| throw new Error(`Human input request timed out for node ${action.ref}`); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark timed-out human inputs as expired
When the timeout elapses, the workflow throws but does not update the human_input_requests row (or call the existing cancel activity), so the request remains pending indefinitely. This leaves the Action Center showing stale pending tasks and allows late resolutions for a workflow that has already failed due to timeout. Consider updating the request to expired/cancelled (and emitting a trace event) before throwing on timeout.
Useful? React with 👍 / 👎.
|
@codex address that feedback |
1 similar comment
|
@codex address that feedback |
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
cb6d148 to
98bef4c
Compare
|
Summary
Testing
|
|
Summary
Testing
Screenshots
|
…ests Signed-off-by: betterclever <paliwal.pranjal83@gmail.com>
This PR implements the foundational Human-in-the-Loop (HITL) system for ShipSec AI. It enables workflows to pause execution and wait for human intervention—whether for simple approvals, data collection via forms, or making specific selections.
This is a comprehensive implementation spanning the backend (Temporal, Drizzle, NestJS) and the frontend (Action Center, Workflow Designer).
Key Features
1. Centralized Action Center
/actions) that serves as a command center for all manual tasks.2. Manual Action Components (HITL Nodes)
Implemented a set of specialized nodes for the workflow designer:
3. Dynamic Context & Templating
{{steps.scan.output.vulnerabilities}}) to provide humans with the necessary context to make decisions.4. Robust Backend Architecture
respondedBy,respondedAt, and fullresponseDatapayloads.5. Unified Resolution Framework
HumanInputResolutionView, a "smart" component that handles the entire resolution lifecycle.Technical Implementation Details
human_input_requeststable with relational support.This PR establishes the core capability of "Human-in-the-Loop" which is essential for secure and reliable AI-driven security workflows.