feat(wework): add personal task supervisor - #2392
Conversation
📝 WalkthroughWalkthroughChangesPersonal task supervision
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant Workbench
participant RuntimeWork
participant Codex
participant Task
User->>Workbench: configure supervisor
Workbench->>RuntimeWork: set supervisor state
RuntimeWork->>Codex: run read-only evaluation
Codex-->>RuntimeWork: return structured result
RuntimeWork->>Task: create suggestion or send correction
Task-->>Workbench: emit supervisor update and generated message
Workbench-->>User: display status or correction
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
wework/src/features/workbench/runtimePaneMessages.ts (1)
149-180: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winDispatch the supervisor-generated user message after identity validation.
onChatStartdispatchesuser_addedforpayload.runtimeGeneratedUserMessagebefore callingruntimeStreamTaskSubtaskIdentity(payload). If identity resolution fails, the handler warns and returns without emittingassistant_started,onAssistantStart, oronRefreshWorkLists— but theuser_addedaction already ran. This leaves an orphaned supervisor correction message in the local cache with no associated assistant turn.Move the
runtimeGeneratedUserMessageblock after the identity check so both actions are emitted together, or neither is.🐛 Proposed fix
onChatStart: payload => { if (!isRuntimeTaskStreamPayload(address, payload)) return - if (payload.runtimeGeneratedUserMessage) { - handlers.onMessageAction({ - type: 'user_added', - message: { - id: payload.runtimeGeneratedUserMessage.id, - taskId: address.taskId, - role: 'user', - content: payload.runtimeGeneratedUserMessage.message, - status: 'done', - source: payload.runtimeGeneratedUserMessage.source as MessageSource, - createdAt: new Date(payload.runtimeGeneratedUserMessage.createdAt).toISOString(), - }, - }) - } const identity = runtimeStreamTaskSubtaskIdentity(payload) if (!identity) { warnAndDropRuntimeStreamEvent('chat:start', address, payload) return } + if (payload.runtimeGeneratedUserMessage) { + handlers.onMessageAction({ + type: 'user_added', + message: { + id: payload.runtimeGeneratedUserMessage.id, + taskId: address.taskId, + role: 'user', + content: payload.runtimeGeneratedUserMessage.message, + status: 'done', + source: payload.runtimeGeneratedUserMessage.source as MessageSource, + createdAt: new Date(payload.runtimeGeneratedUserMessage.createdAt).toISOString(), + }, + }) + } debugRuntimeStreamEvent('chat:start', address, payload, true)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/features/workbench/runtimePaneMessages.ts` around lines 149 - 180, Move the runtimeGeneratedUserMessage user_added dispatch in onChatStart to after runtimeStreamTaskSubtaskIdentity(payload) succeeds and the invalid-identity early return. Keep the existing message construction unchanged so the supervisor message is emitted only alongside the validated assistant_started flow.
🧹 Nitpick comments (7)
wework/src/components/layout/TaskSupervisorControl.test.tsx (1)
111-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the rejection path.
The current test only covers a resolved
onAccept. Add a case whereonAcceptrejects, and assert that the card shows an error message. This locks in the error handling requested inTaskSupervisorControl.tsxlines 285-295. Add a matching case for a rejectedonSetinTaskSupervisorControl, which already renderserror.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/layout/TaskSupervisorControl.test.tsx` around lines 111 - 127, Extend the tests around SupervisorSuggestionCards to cover a rejected onAccept promise and assert that the card displays the resulting error message. Add a corresponding TaskSupervisorControl test with a rejected onSet promise, verifying its existing error rendering behavior; keep the current successful action coverage unchanged.wework/src/components/layout/TaskSupervisorControl.tsx (1)
94-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a dismissal path for the configuration panel.
The panel opens on click and stays open until the user clicks the toggle again. Keyboard users cannot close it with
Escape, and a click outside the panel does not close it. Add anEscapekey handler and an outside-click handler so the popover behaves like the other workbench popovers.If a shared popover primitive already exists in
wework/src/components/ui, reuse it instead of adding local handlers.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/layout/TaskSupervisorControl.tsx` around lines 94 - 138, The configuration panel controlled by the toggle’s open state lacks dismissal behavior. Reuse the existing popover primitive from the UI components, if available, around the toggle and panel so Escape closes it and clicks outside dismiss it; otherwise add handlers tied to the panel’s open state and preserve the existing toggle behavior and panel content.executor/src/runtime_work/handler/supervisor.rs (4)
564-566: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSplit the system prompt across lines.
The prompt is a single ~1400-character line.
SUPERVISOR_PROMPT_VERSIONshows the prompt will be revised, and every revision will produce a one-line diff that reviewers cannot read.Use a
concat!of per-sentence literals or an indented raw string so future prompt edits produce readable diffs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@executor/src/runtime_work/handler/supervisor.rs` around lines 564 - 566, Update supervisor_system_prompt() to format the long prompt across readable lines using concat! with per-sentence string literals or an indented raw string, while preserving the exact prompt content and returned String behavior. Keep SUPERVISOR_PROMPT_VERSION compatibility unchanged.
36-49: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winBound
instructionslength.
modeandinterval_secondsare validated, butinstructionsis accepted at any length and is interpolated verbatim into every evaluation prompt bysupervisor_prompt. Every other prompt input in this file is bounded, bySUPERVISOR_LATEST_CONTENT_CHARSandSUPERVISOR_CONTEXT_CONTENT_CHARS. An oversized value inflates the token cost of every scheduled evaluation, at the configured interval, for the lifetime of the task.Reject or truncate
instructionsabove an explicit character limit.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@executor/src/runtime_work/handler/supervisor.rs` around lines 36 - 49, Bound the instructions value parsed in the supervisor handler before it is stored or passed to supervisor_prompt. Add an explicit character limit consistent with the existing prompt-input limits, and reject or truncate values exceeding it while preserving normal processing for valid instructions.
751-791: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for the interval path of an active task.
The two scheduling tests cover inactive tasks only: completion between ticks, and a stopped task with no AI content. The primary path, an active task that becomes due after
interval_seconds, has no test. That path also contains theinterval_secondsarithmetic flagged at Line 627.Add two cases with
active = true: one wherenow - last_evaluated_atis just below the interval and the check returnsfalse, and one where it is at or above the interval and the check returnstrue.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@executor/src/runtime_work/handler/supervisor.rs` around lines 751 - 791, Add tests for the active-task interval path in supervisor_needs_scheduled_check: with active=true and a last_evaluated_at timestamp, verify a now value just below interval_seconds returns false, and a value at or beyond the interval returns true. Keep the existing completion and stopped-task cases unchanged.
165-189: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid scanning all tasks every supervisor tick.
start_supervisor_schedulercallslocal_task_links(false)every 5 seconds, andlocal_task_linksdelegates toRuntimeWorkStore::list_task_summaries, which refreshes the task index at runtime. Devices with supervisor disabled still incur this store refresh and sort each tick because supervisor filtering happens only after listing. Skip the listing when no supervision state exists, or use a small tracked set of active supervisor task IDs updated by supervisor enable/clear operations.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@executor/src/runtime_work/handler/supervisor.rs` around lines 165 - 189, Update start_supervisor_scheduler to avoid calling local_task_links(false) on every tick when no supervisor state exists; add a lightweight guard or tracked active supervisor-task ID set maintained by supervisor enable/clear operations, and only evaluate those tasks while preserving supervisor_needs_scheduled_check behavior.wework/src/stream/responseApiStream.ts (1)
781-787: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winSkip
last_content_hashfrom the supervisor contract.
RuntimeSupervisorStateandRuntimeSupervisorSuggestionalready serialize withcamelCase, so the field names match TypeScript.last_content_hashis only used for internal deduplication and has no declaration inRuntimeSupervisorState; add#[serde(skip_serializing)]or remove it so the emitted payload matches the declared contract.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/stream/responseApiStream.ts` around lines 781 - 787, Update the serialization definition for the supervisor state or suggestion type used by the runtime supervisor update flow to exclude last_content_hash from emitted payloads, using the existing serde skip attribute or removing the field. Preserve the camelCase serialization of all declared RuntimeSupervisorState and RuntimeSupervisorSuggestion fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@executor/src/runtime_work/handler/supervisor.rs`:
- Around line 551-562: Update parse_supervisor_evaluation to bind the result of
prefix stripping and use that intermediate value as the fallback when removing
the closing fence, preserving the stripped content for unterminated fences. Add
a test covering truncated ```json input that still parses successfully.
- Around line 508-519: Update record_supervisor_error and
supervisor_needs_scheduled_check to track consecutive failures and a next-retry
timestamp on supervisor state, applying exponential backoff between retries and
stopping scheduling after a bounded failure limit. Preserve the failure status
and error details for UI visibility, and reset the failure/backoff state when a
supervisor evaluation succeeds or is re-enabled.
- Around line 622-631: Update the interval threshold calculation in the
supervisor activity check to use saturating multiplication after the i64
conversion, preventing overflow when an out-of-range persisted interval becomes
i64::MAX. Preserve the existing fallback and comparison behavior for valid
intervals.
- Around line 232-243: Update the unchanged-content branch in the supervisor
evaluation flow to call emit_supervisor_updated after store.update_task
refreshes last_evaluated_at, last_error, and status. Preserve the existing early
return while ensuring the emitted event carries the refreshed supervisor state.
- Around line 109-126: Update the suggestion-status handler around update_task
so it verifies that suggestion_id matched an entry and returns a not_found error
when no suggestion is found. Preserve the existing status and resolved_at
updates for matches, and only emit supervisor_updated and return the accepted
supervisor_response after a successful modification.
In `@wework/e2e/desktop/task-flow.e2e.mjs`:
- Around line 6662-6671: Remove the redundant withTimeout wrappers around the
two awaitScenarioRequestCount calls in the supervisor flow, since that method
already applies the default timeout. Await each request count directly and
preserve the specific diagnostic messages through a follow-up assertion or
otherwise ensure failures retain equivalent context; do not keep competing
timeouts with the same duration.
- Around line 9042-9055: Update the evaluator-request assertions in the visible
request-checking branch to track whether this is the first evaluation. Require
SUPERVISOR_COMPLETION_TEXT only for the first evaluator request; for subsequent
requests, accept either SUPERVISOR_COMPLETION_TEXT or
SUPERVISOR_CORRECTION_COMPLETION_TEXT while preserving the schema and
original-transcript assertions.
- Line 6640: Update verifyTaskSupervisorLifecycle around
control.setScenario('supervisor') to save the prior scenario, wrap the lifecycle
steps in try/finally, restore control.scenario in finally, and disable
supervision before returning by clicking the toggle button and
task-supervisor-disable-button.
In `@wework/src/api/runtimeWork.ts`:
- Around line 158-173: Update getRuntimeSupervisor, setRuntimeSupervisor,
clearRuntimeSupervisor, and resolveRuntimeSupervisor to reject immediately
before calling client.post, matching compactRuntimeTask’s unsupported cloud-task
handling. Preserve the existing request signatures and response types while
ensuring none of the /runtime-work/supervisor/* HTTP calls are issued.
In `@wework/src/components/layout/TaskSupervisorControl.tsx`:
- Around line 285-295: Update TaskSupervisorControl’s resolve function to catch
rejected action calls, store a user-facing error message in dedicated
resolve-error state, and clear that state when starting a new resolution or
after successful completion. Render the stored message in the suggestion card
near the rationale only when no resolution is active, while preserving the
existing resolvingId cleanup and handling both onAccept and onDismiss failures.
In `@wework/src/components/settings/ContextSettingsPage.tsx`:
- Around line 121-128: Prevent in-flight edits from being overwritten in
handleSaveSupervisorPrinciples and the corresponding save flow around lines
197-203: disable the supervisor-principles textarea while saving, or track and
preserve edits made after the request begins instead of unconditionally applying
nextPreferences.supervisorPrinciples. Ensure the user’s latest input remains
intact when the request completes.
In `@wework/src/features/workbench/runtimeConversationCache.ts`:
- Around line 403-417: Validate that payload.appliedAtMs is a finite number
before the fallback guidance object is constructed in the guidance-message flow.
Ensure the non-queued executor-originated path never calls
Date(...).toISOString() with an invalid timestamp, while preserving the existing
queued guidance behavior and valid-timestamp fallback.
---
Outside diff comments:
In `@wework/src/features/workbench/runtimePaneMessages.ts`:
- Around line 149-180: Move the runtimeGeneratedUserMessage user_added dispatch
in onChatStart to after runtimeStreamTaskSubtaskIdentity(payload) succeeds and
the invalid-identity early return. Keep the existing message construction
unchanged so the supervisor message is emitted only alongside the validated
assistant_started flow.
---
Nitpick comments:
In `@executor/src/runtime_work/handler/supervisor.rs`:
- Around line 564-566: Update supervisor_system_prompt() to format the long
prompt across readable lines using concat! with per-sentence string literals or
an indented raw string, while preserving the exact prompt content and returned
String behavior. Keep SUPERVISOR_PROMPT_VERSION compatibility unchanged.
- Around line 36-49: Bound the instructions value parsed in the supervisor
handler before it is stored or passed to supervisor_prompt. Add an explicit
character limit consistent with the existing prompt-input limits, and reject or
truncate values exceeding it while preserving normal processing for valid
instructions.
- Around line 751-791: Add tests for the active-task interval path in
supervisor_needs_scheduled_check: with active=true and a last_evaluated_at
timestamp, verify a now value just below interval_seconds returns false, and a
value at or beyond the interval returns true. Keep the existing completion and
stopped-task cases unchanged.
- Around line 165-189: Update start_supervisor_scheduler to avoid calling
local_task_links(false) on every tick when no supervisor state exists; add a
lightweight guard or tracked active supervisor-task ID set maintained by
supervisor enable/clear operations, and only evaluate those tasks while
preserving supervisor_needs_scheduled_check behavior.
In `@wework/src/components/layout/TaskSupervisorControl.test.tsx`:
- Around line 111-127: Extend the tests around SupervisorSuggestionCards to
cover a rejected onAccept promise and assert that the card displays the
resulting error message. Add a corresponding TaskSupervisorControl test with a
rejected onSet promise, verifying its existing error rendering behavior; keep
the current successful action coverage unchanged.
In `@wework/src/components/layout/TaskSupervisorControl.tsx`:
- Around line 94-138: The configuration panel controlled by the toggle’s open
state lacks dismissal behavior. Reuse the existing popover primitive from the UI
components, if available, around the toggle and panel so Escape closes it and
clicks outside dismiss it; otherwise add handlers tied to the panel’s open state
and preserve the existing toggle behavior and panel content.
In `@wework/src/stream/responseApiStream.ts`:
- Around line 781-787: Update the serialization definition for the supervisor
state or suggestion type used by the runtime supervisor update flow to exclude
last_content_hash from emitted payloads, using the existing serde skip attribute
or removing the field. Preserve the camelCase serialization of all declared
RuntimeSupervisorState and RuntimeSupervisorSuggestion fields.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 4b4591d1-4570-48a5-8937-0624806a443a
📒 Files selected for processing (41)
docs/en/wework/settings.mddocs/en/wework/tasks.mddocs/zh/wework/settings.mddocs/zh/wework/tasks.mdexecutor/src/agents/codex.rsexecutor/src/agents/codex/tests.rsexecutor/src/runtime_work/events.rsexecutor/src/runtime_work/handler.rsexecutor/src/runtime_work/handler/helpers/transcript.rsexecutor/src/runtime_work/handler/supervisor.rsexecutor/src/runtime_work/handler/tests.rsexecutor/src/runtime_work/response.rsexecutor/src/runtime_work/worktrees.rswework/e2e/desktop/checkpoints.mjswework/e2e/desktop/task-flow.e2e.mjswework/src-tauri/src/lib.rswework/src/App.tsxwework/src/api/executorAccess.tswework/src/api/hybrid/hybridServices.tswework/src/api/local/localServices.test.tswework/src/api/local/localServices.tswework/src/api/runtime/runtimeChatStream.tswework/src/api/runtimeWork.tswework/src/components/layout/DesktopWorkbenchMain.tsxwework/src/components/layout/TaskSupervisorControl.test.tsxwework/src/components/layout/TaskSupervisorControl.tsxwework/src/components/settings/ContextSettingsPage.test.tsxwework/src/components/settings/ContextSettingsPage.tsxwework/src/features/workbench/WorkbenchProvider.tsxwework/src/features/workbench/runtimeConversationCache.test.tswework/src/features/workbench/runtimeConversationCache.tswework/src/features/workbench/runtimePaneMessages.test.tswework/src/features/workbench/runtimePaneMessages.tswework/src/i18n/locales/en/common.jsonwework/src/i18n/locales/zh-CN/common.jsonwework/src/stream/chatStream.tswework/src/stream/responseApiStream.test.tswework/src/stream/responseApiStream.tswework/src/tauri/appPreferences.test.tswework/src/tauri/appPreferences.tswework/src/types/api.ts
| self.store.update_task(&link.local_task_id, |task| { | ||
| let Some(supervisor) = task.supervisor.as_mut() else { | ||
| return; | ||
| }; | ||
| if let Some(suggestion) = supervisor | ||
| .suggestions | ||
| .iter_mut() | ||
| .find(|suggestion| suggestion.id == suggestion_id) | ||
| { | ||
| suggestion.status = status.clone(); | ||
| suggestion.resolved_at = Some(now_ms()); | ||
| } | ||
| task.updated_at = now_ms(); | ||
| }); | ||
| self.emit_supervisor_updated(&link.local_task_id); | ||
| Ok(supervisor_response( | ||
| &self.local_task_link(&link.local_task_id).unwrap_or(link), | ||
| )) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm update_task invokes the closure synchronously.
ast-grep run --pattern 'pub fn update_task($$$) { $$$ }' --lang rust executor/src
rg -nP --type=rust -C8 '\bfn update_task\s*[<(]' executor/srcRepository: wecode-ai/Wegent
Length of output: 3769
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a 'supervisor\.rs|store\.rs|error|ipc' . | sed 's#^\./##' | head -200
echo "== supervisor handler snippet =="
sed -n '80,140p' executor/src/runtime_work/handler/supervisor.rs
echo "== runtime work store update functions =="
sed -n '120,190p' executor/src/runtime_work/store.rs
echo "== runtime work supervisor definitions/usages in supervisor.rs =="
sed -n '1,80p' executor/src/runtime_work/handler/supervisor.rs
sed -n '140,220p' executor/src/runtime_work/handler/supervisor.rs
echo "== AppIpcError definitions/usages =="
rg -n "AppIpcError|new\\(" executor/src -g '*.rs' | head -80
echo "== tests mentioning resolve_task_supervisor or supervisor suggestion =="
rg -n "resolve_task_supervisor|suggestions|supervisor_response|suggestionId|accepted" . -g '*.rs' | head -200Repository: wecode-ai/Wegent
Length of output: 38144
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== response definitions =="
sed -n '1,90p' executor/src/runtime_work/response.rs
sed -n '560,615p' executor/src/runtime_work/handler/supervisor.rs
echo "== AppIpcError definition =="
sed -n '1,110p' executor/src/local/app_ipc.rs
echo "== resolve endpoint contract/route context =="
sed -n '30,50p' executor/src/runtime_work/handler.rs
echo "== frontend/backend references to supervisor.resolve =="
rg -n "supervisor\.resolve|runtime\.tasks\.supervisor\.resolve|suggestionId|suggestion_id|resolved_at|accepted:\s*false|accepted: true" . --glob '!node_modules' --glob '!dist' --glob '!build' | head -240Repository: wecode-ai/Wegent
Length of output: 25639
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== AppIpcError implementation =="
sed -n '1,120p' executor/src/local/app_ipc.rs | sed -n '5,35p'
echo "== resolve task call site =="
sed -n '640,675p' wework/src/components/layout/DesktopWorkbenchMain.tsx
echo "== runtimeWork implementation around resolve ==
sed -n '2234,2254p' wework/src/features/workbench/WorkbenchProvider.tsx
echo "== focused frontend try-catch patterns for runtimeWork calls =="
rg -n "RuntimeWork|runtimeWork|accepted:\s*false|accepted: false|RuntimeWorkError|catch|try {|resolved_at|suggestionId" wework/src/components/layout/DesktopWorkbenchMain.tsx wework/src -g '*.ts' -g '*.tsx' | head -220Repository: wecode-ai/Wegent
Length of output: 2522
Return not_found when suggestionId has no match.
update_task returns a value instead of the closure result, so this always emits accepted: true even when no suggestion is modified. For a stale/wrong suggestionId, return an error instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@executor/src/runtime_work/handler/supervisor.rs` around lines 109 - 126,
Update the suggestion-status handler around update_task so it verifies that
suggestion_id matched an entry and returns a not_found error when no suggestion
is found. Preserve the existing status and resolved_at updates for matches, and
only emit supervisor_updated and return the accepted supervisor_response after a
successful modification.
| let content_hash = content_hash(&visible_ai_progress); | ||
| if supervisor.last_content_hash.as_deref() == Some(content_hash.as_str()) { | ||
| self.store.update_task(local_task_id, |task| { | ||
| let Some(current) = task.supervisor.as_mut() else { | ||
| return; | ||
| }; | ||
| current.last_evaluated_at = Some(now_ms()); | ||
| current.last_error = None; | ||
| current.status = "active".to_owned(); | ||
| }); | ||
| return Ok(()); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The unchanged-content path updates last_evaluated_at without emitting an update.
Every other write path in this file calls emit_supervisor_updated after update_task. This branch does not. The UI therefore keeps showing the previous lastEvaluatedAt for as long as the AI output does not change, which makes active supervision look stalled.
🐛 Proposed fix to emit the refreshed state
current.last_evaluated_at = Some(now_ms());
current.last_error = None;
current.status = "active".to_owned();
});
+ self.emit_supervisor_updated(local_task_id);
return Ok(());
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| let content_hash = content_hash(&visible_ai_progress); | |
| if supervisor.last_content_hash.as_deref() == Some(content_hash.as_str()) { | |
| self.store.update_task(local_task_id, |task| { | |
| let Some(current) = task.supervisor.as_mut() else { | |
| return; | |
| }; | |
| current.last_evaluated_at = Some(now_ms()); | |
| current.last_error = None; | |
| current.status = "active".to_owned(); | |
| }); | |
| return Ok(()); | |
| } | |
| let content_hash = content_hash(&visible_ai_progress); | |
| if supervisor.last_content_hash.as_deref() == Some(content_hash.as_str()) { | |
| self.store.update_task(local_task_id, |task| { | |
| let Some(current) = task.supervisor.as_mut() else { | |
| return; | |
| }; | |
| current.last_evaluated_at = Some(now_ms()); | |
| current.last_error = None; | |
| current.status = "active".to_owned(); | |
| }); | |
| self.emit_supervisor_updated(local_task_id); | |
| return Ok(()); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@executor/src/runtime_work/handler/supervisor.rs` around lines 232 - 243,
Update the unchanged-content branch in the supervisor evaluation flow to call
emit_supervisor_updated after store.update_task refreshes last_evaluated_at,
last_error, and status. Preserve the existing early return while ensuring the
emitted event carries the refreshed supervisor state.
| fn record_supervisor_error(&self, local_task_id: &str, error: String) { | ||
| self.store.update_task(local_task_id, |task| { | ||
| let Some(supervisor) = task.supervisor.as_mut() else { | ||
| return; | ||
| }; | ||
| supervisor.status = "error".to_owned(); | ||
| supervisor.last_error = Some(error.clone()); | ||
| supervisor.last_evaluated_at = Some(now_ms()); | ||
| supervisor.last_content_hash = None; | ||
| }); | ||
| self.emit_supervisor_updated(local_task_id); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
A failing evaluation retries every 5 seconds forever, with no backoff.
record_supervisor_error sets last_content_hash = None. supervisor_needs_scheduled_check returns true whenever last_content_hash is None (Line 619). The scheduler ticks every 5 seconds. A supervisor that fails for a persistent reason therefore re-enters evaluate_task_supervisor on every tick for the lifetime of the task.
Two failure shapes make this expensive:
- Fast failures, for example
"runtime task session is not ready"at Line 204, produce one attempt per 5 seconds indefinitely. - Model failures produce a new read-only Codex turn per attempt, each with a 60-second timeout, so a broken model configuration bills a turn roughly every minute per supervised task.
Record consecutive failure count and next-retry time on the supervisor state, apply exponential backoff, and stop scheduling after a bounded number of consecutive failures while leaving the state visible to the UI so the user can fix the configuration and re-enable it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@executor/src/runtime_work/handler/supervisor.rs` around lines 508 - 519,
Update record_supervisor_error and supervisor_needs_scheduled_check to track
consecutive failures and a next-retry timestamp on supervisor state, applying
exponential backoff between retries and stopping scheduling after a bounded
failure limit. Preserve the failure status and error details for UI visibility,
and reset the failure/backoff state when a supervisor evaluation succeeds or is
re-enabled.
| fn parse_supervisor_evaluation(content: &str) -> Result<SupervisorEvaluation, String> { | ||
| let trimmed = content.trim(); | ||
| let json_text = trimmed | ||
| .strip_prefix("```json") | ||
| .or_else(|| trimmed.strip_prefix("```")) | ||
| .unwrap_or(trimmed) | ||
| .strip_suffix("```") | ||
| .unwrap_or(trimmed) | ||
| .trim(); | ||
| serde_json::from_str(json_text) | ||
| .map_err(|error| format!("invalid supervisor evaluation: {error}")) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
The suffix fallback discards the prefix stripping.
.strip_suffix("```").unwrap_or(trimmed) falls back to the original trimmed, not to the value produced by the prefix strip. For output that opens a fence but does not close it, for example a truncated "```json\n{...}", the opening fence is re-added and serde_json::from_str fails.
Bind the intermediate value so each step falls back to the previous step.
🐛 Proposed fix for the fence stripping
fn parse_supervisor_evaluation(content: &str) -> Result<SupervisorEvaluation, String> {
let trimmed = content.trim();
- let json_text = trimmed
- .strip_prefix("```json")
- .or_else(|| trimmed.strip_prefix("```"))
- .unwrap_or(trimmed)
- .strip_suffix("```")
- .unwrap_or(trimmed)
- .trim();
+ let without_prefix = trimmed
+ .strip_prefix("```json")
+ .or_else(|| trimmed.strip_prefix("```"))
+ .unwrap_or(trimmed);
+ let json_text = without_prefix
+ .strip_suffix("```")
+ .unwrap_or(without_prefix)
+ .trim();
serde_json::from_str(json_text)
.map_err(|error| format!("invalid supervisor evaluation: {error}"))
}Please also add a test for the unterminated-fence input.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@executor/src/runtime_work/handler/supervisor.rs` around lines 551 - 562,
Update parse_supervisor_evaluation to bind the result of prefix stripping and
use that intermediate value as the fallback when removing the closing fence,
preserving the stripped content for unterminated fences. Add a test covering
truncated ```json input that still parses successfully.
| if active | ||
| && supervisor | ||
| .last_evaluated_at | ||
| .is_some_and(|last_evaluated_at| { | ||
| now.saturating_sub(last_evaluated_at) | ||
| >= i64::try_from(supervisor.interval_seconds).unwrap_or(i64::MAX) * 1_000 | ||
| }) | ||
| { | ||
| return true; | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
i64::MAX * 1_000 can overflow.
i64::try_from(supervisor.interval_seconds).unwrap_or(i64::MAX) yields i64::MAX for an out-of-range value, and the following * 1_000 then overflows. set_task_supervisor validates the interval on the write path, but this value is also read back from the persisted store, so a stale or hand-edited record reaches this multiplication. In a debug build the overflow panics inside the scheduler task.
Use saturating_mul.
🐛 Proposed fix for the multiplication
.is_some_and(|last_evaluated_at| {
now.saturating_sub(last_evaluated_at)
- >= i64::try_from(supervisor.interval_seconds).unwrap_or(i64::MAX) * 1_000
+ >= i64::try_from(supervisor.interval_seconds)
+ .unwrap_or(i64::MAX)
+ .saturating_mul(1_000)
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if active | |
| && supervisor | |
| .last_evaluated_at | |
| .is_some_and(|last_evaluated_at| { | |
| now.saturating_sub(last_evaluated_at) | |
| >= i64::try_from(supervisor.interval_seconds).unwrap_or(i64::MAX) * 1_000 | |
| }) | |
| { | |
| return true; | |
| } | |
| if active | |
| && supervisor | |
| .last_evaluated_at | |
| .is_some_and(|last_evaluated_at| { | |
| now.saturating_sub(last_evaluated_at) | |
| >= i64::try_from(supervisor.interval_seconds) | |
| .unwrap_or(i64::MAX) | |
| .saturating_mul(1_000) | |
| }) | |
| { | |
| return true; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@executor/src/runtime_work/handler/supervisor.rs` around lines 622 - 631,
Update the interval threshold calculation in the supervisor activity check to
use saturating multiplication after the i64 conversion, preventing overflow when
an out-of-range persisted interval becomes i64::MAX. Preserve the existing
fallback and comparison behavior for valid intervals.
| if (requestText.includes('Current visible progress snapshot (JSON):')) { | ||
| assert.ok( | ||
| requestText.includes('correction'), | ||
| 'The supervisor evaluator request did not include its structured output schema' | ||
| ) | ||
| assert.ok( | ||
| requestText.includes(SUPERVISOR_COMPLETION_TEXT), | ||
| 'The supervisor evaluator did not receive the latest assistant progress' | ||
| ) | ||
| assert.equal( | ||
| requestText.includes(SUPERVISOR_PROMPT), | ||
| false, | ||
| 'The supervisor evaluator received the original user transcript instead of recent AI content' | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
The evaluator branch assumes only one evaluation happens.
The branch asserts that every evaluator request contains SUPERVISOR_COMPLETION_TEXT. The evaluator sends a bounded slice of recent AI content. After the correction turn completes, a further evaluation carries SUPERVISOR_CORRECTION_COMPLETION_TEXT and may no longer carry SUPERVISOR_COMPLETION_TEXT. That request would then fail this assertion and report a misleading cause.
Scope the strict assertion to the first evaluator request, and accept either completion marker for later ones.
🛡️ Proposed guard
if (requestText.includes('Current visible progress snapshot (JSON):')) {
+ const evaluatorRequests = this.scenarioRequests
+ .get('supervisor')
+ .filter(item =>
+ JSON.stringify(item.body).includes('Current visible progress snapshot (JSON):')
+ ).length
assert.ok(
requestText.includes('correction'),
'The supervisor evaluator request did not include its structured output schema'
)
assert.ok(
- requestText.includes(SUPERVISOR_COMPLETION_TEXT),
+ evaluatorRequests > 1 ||
+ requestText.includes(SUPERVISOR_COMPLETION_TEXT),
'The supervisor evaluator did not receive the latest assistant progress'
)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/e2e/desktop/task-flow.e2e.mjs` around lines 9042 - 9055, Update the
evaluator-request assertions in the visible request-checking branch to track
whether this is the first evaluation. Require SUPERVISOR_COMPLETION_TEXT only
for the first evaluator request; for subsequent requests, accept either
SUPERVISOR_COMPLETION_TEXT or SUPERVISOR_CORRECTION_COMPLETION_TEXT while
preserving the schema and original-transcript assertions.
| getRuntimeSupervisor(data: RuntimeSupervisorGetRequest): Promise<RuntimeSupervisorResponse> { | ||
| return client.post('/runtime-work/supervisor/get', data) | ||
| }, | ||
| setRuntimeSupervisor(data: RuntimeSupervisorSetRequest): Promise<RuntimeSupervisorResponse> { | ||
| return client.post('/runtime-work/supervisor/set', data) | ||
| }, | ||
| clearRuntimeSupervisor( | ||
| data: RuntimeSupervisorClearRequest | ||
| ): Promise<RuntimeSupervisorResponse> { | ||
| return client.post('/runtime-work/supervisor/clear', data) | ||
| }, | ||
| resolveRuntimeSupervisor( | ||
| data: RuntimeSupervisorResolveRequest | ||
| ): Promise<RuntimeSupervisorResponse> { | ||
| return client.post('/runtime-work/supervisor/resolve', data) | ||
| }, |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Look for backend routes that serve the supervisor endpoints.
rg -n --iglob '!**/node_modules/**' -C3 'runtime-work/supervisor|supervisor/(get|set|clear|resolve)'
rg -n -C3 'runtime-work/goal/set' --iglob '!**/node_modules/**' --iglob '!wework/src/**'Repository: wecode-ai/Wegent
Length of output: 154
🏁 Script executed:
#!/bin/bash
set -u
echo "== repo files matching runtimeWork =="
fd -a 'runtimeWork|hybridServices|common.*api|api' . | sed 's#^\./##' | head -200
echo
echo "== runtime-work/supervisor references in repo =="
rg -n --iglob '!**/node_modules/**' --iglob '!**/.git/**' 'runtime-work/supervisor|runtime_work/supervisor|/supervisor/(get|set|clear|resolve)|supervisor' . | head -300
echo
echo "== related API endpoint references excluding wework/src =="
rg -n --iglob '!**/node_modules/**' --iglob '!**/.git/**' --iglob '!wework/src/**' 'runtime-work/(goal|supervisor)|runtime_work/(goal|supervisor)' . | head -300
echo
echo "== inspect candidate backend route patterns =="
rg -n --iglob '!**/node_modules/**' --iglob '!**/.git/**' -C2 "post\(\s*/['\"]runtime-work/(goal|supervisor)|runtime-work/(goal|supervisor)|runtime.work|runtimeWork" . | head -400Repository: wecode-ai/Wegent
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -u
echo "== inspect wework runtimeWork.ts relevant section =="
sed -n '1,220p' wework/src/api/runtimeWork.ts
echo
echo "== inspect hybridServices.ts routing relevant section =="
sed -n '1,260p' wework/src/api/hybrid/hybridServices.ts
echo
echo "== inspect backend runtime-work endpoint tests mentioning supervisor =="
rg -n -C3 'supervisor|"/runtime-work/supervisor|/runtime-work/supervisor' backend/backend/app backend tests || true
echo
echo "== inspect executor supervisor router registration =="
sed -n '380,438p' executor/src/runtime_work/handler.rs
sed -n '1,140p' executor/src/runtime_work/handler/supervisor.rs
echo
echo "== inspect localServices supervisor implementations =="
sed -n '2210,2250p' wework/src/api/local/localServices.tsRepository: wecode-ai/Wegent
Length of output: 28812
🏁 Script executed:
#!/bin/bash
set -u
echo "== exact supervisor route string references excluding tests/docs =="
rg -n --glob '!**/*.md' --glob '!**/wework/src/*.rs' --glob '!executor/tests/**' --glob '!wework/e2e/**' --glob '!**/package-lock.json' '"/runtime-work/supervisor|runtime-work/supervisor|/supervisor/get|/supervisor/set|/supervisor/clear|/supervisor/resolve' .
echo
echo "== backend api.py top imports/router registration =="
sed -n '1,260p' backend/app/api/api.py
echo
echo "== backend endpoints containing runtime-work =="
rg -n --iglob '!**/node_modules/**' --iglob '!**/.git/**' -C3 '"runtime-work|runtime-work/' backend/app/backend app backend/wework --glob 'backend/**/*.py' --glob 'app/**/*.py' --glob '!**/tests/**' | head -300Repository: wecode-ai/Wegent
Length of output: 9582
🏁 Script executed:
#!/bin/bash
set -u
echo "== backend api.py remainder around supervisor routes =="
rg -n -C4 'runtime-work|supervisor|router|prefix=' backend/app/api/api.py | sed -n '240,360p'
echo
echo "== backend runtime_work endpoint file =="
sed -n '1,260p' backend/app/api/endpoints/runtime_work.py
echo
echo "== backend runtime_work supervisor references =="
rg -n -C3 'supervisor|RuntimeSupervisor|/supervisor|supervisor' backend/app/api/endpoints/runtime_work.py backend/app backend/app/api/endpoints/runtime_work.py tests backend/tests 2>/dev/null | sed -n '1,240p'Repository: wecode-ai/Wegent
Length of output: 9998
Reject supervisor calls outside local Codex tasks.
These routes are only registered for the local executor IPC path (runtime.tasks.supervisor.*), while /runtime-work/supervisor/* is not served by the backend runtime-work API. Reject getRuntimeSupervisor, setRuntimeSupervisor, clearRuntimeSupervisor, and resolveRuntimeSupervisor before issuing these HTTP calls, like compactRuntimeTask does for unsupported cloud-task APIs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/api/runtimeWork.ts` around lines 158 - 173, Update
getRuntimeSupervisor, setRuntimeSupervisor, clearRuntimeSupervisor, and
resolveRuntimeSupervisor to reject immediately before calling client.post,
matching compactRuntimeTask’s unsupported cloud-task handling. Preserve the
existing request signatures and response types while ensuring none of the
/runtime-work/supervisor/* HTTP calls are issued.
| const resolve = async ( | ||
| suggestion: RuntimeSupervisorSuggestion, | ||
| action: (suggestion: RuntimeSupervisorSuggestion) => Promise<void> | ||
| ) => { | ||
| setResolvingId(suggestion.id) | ||
| try { | ||
| await action(suggestion) | ||
| } finally { | ||
| setResolvingId(null) | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Handle rejections from onAccept and onDismiss.
resolve awaits action(suggestion) without a catch. The callers in DesktopWorkbenchMain.tsx (resolveTaskSupervisorSuggestion, lines 656-672) throw a localized Error when the API response is not accepted, and submitPaneInput can also reject. Both button handlers call void resolve(...), so a rejection becomes an unhandled promise rejection and the user sees no feedback. The suggestion stays pending with no explanation.
Capture the error and render it in the card, in the same way TaskSupervisorControl renders its error state.
🛡️ Proposed fix to surface resolve failures
export function SupervisorSuggestionCards({
suggestions,
onAccept,
onDismiss,
}: SupervisorSuggestionCardsProps) {
const { t } = useTranslation('common')
const [resolvingId, setResolvingId] = useState<string | null>(null)
+ const [resolveError, setResolveError] = useState<string | null>(null)
const visible = suggestions.filter(suggestion => suggestion.status === 'pending').slice(-2)
if (visible.length === 0) return null
const resolve = async (
suggestion: RuntimeSupervisorSuggestion,
action: (suggestion: RuntimeSupervisorSuggestion) => Promise<void>
) => {
setResolvingId(suggestion.id)
+ setResolveError(null)
try {
await action(suggestion)
+ } catch (error) {
+ setResolveError(error instanceof Error ? error.message : String(error))
} finally {
setResolvingId(null)
}
}Then render the message inside the card, for example after the rationale paragraph:
{resolveError && resolvingId === null && (
<p className="mt-1 text-xs text-red-600">{resolveError}</p>
)}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/layout/TaskSupervisorControl.tsx` around lines 285 -
295, Update TaskSupervisorControl’s resolve function to catch rejected action
calls, store a user-facing error message in dedicated resolve-error state, and
clear that state when starting a new resolution or after successful completion.
Render the stored message in the suggestion card near the rationale only when no
resolution is active, while preserving the existing resolvingId cleanup and
handling both onAccept and onDismiss failures.
| const handleSaveSupervisorPrinciples = async () => { | ||
| setSaving(true) | ||
| setError(null) | ||
| try { | ||
| const nextPreferences = await updateAppPreferences({ supervisorPrinciples }) | ||
| setPreferences(nextPreferences) | ||
| setSupervisorPrinciples(nextPreferences.supervisorPrinciples) | ||
| setSavedSupervisorPrinciples(nextPreferences.supervisorPrinciples) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Prevent in-flight edits from being overwritten.
The textarea accepts edits while saving is true. The request at Line 125 contains the earlier value. When it completes, Lines 127-128 replace any later input. Disable the textarea during the save, or preserve edits made after the request starts.
Proposed fix
<textarea
data-testid="context-supervisor-principles-textarea"
value={supervisorPrinciples}
+ disabled={saving}
onChange={event => setSupervisorPrinciples(event.target.value)}Also applies to: 197-203
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/settings/ContextSettingsPage.tsx` around lines 121 -
128, Prevent in-flight edits from being overwritten in
handleSaveSupervisorPrinciples and the corresponding save flow around lines
197-203: disable the supervisor-principles textarea while saving, or track and
preserve edits made after the request begins instead of unconditionally applying
nextPreferences.supervisorPrinciples. Ensure the user’s latest input remains
intact when the request completes.
| const queuedGuidance = takeAppliedRuntimeConversationGuidance(address, payload) | ||
| const guidanceMessage = | ||
| queuedGuidance ?? | ||
| (payload.clientGuidanceId && payload.message | ||
| ? { | ||
| id: payload.clientGuidanceId, | ||
| content: payload.message, | ||
| status: 'sending', | ||
| deliveryMode: 'guidance', | ||
| createdAt: new Date(payload.appliedAtMs).toISOString(), | ||
| } | ||
| : null) | ||
| if (!guidanceMessage) return null | ||
|
|
||
| if (takeInterruptedRuntimeConversationGuidance(address, guidanceMessage.id)) { | ||
| if (queuedGuidance && takeInterruptedRuntimeConversationGuidance(address, guidanceMessage.id)) { |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Guard against a non-finite appliedAtMs in the new fallback branch.
When no queued guidance exists, this branch builds a message using new Date(payload.appliedAtMs).toISOString(). If appliedAtMs is not a finite number, toISOString() throws RangeError: Invalid time value.
The queued path already guards this exact scenario elsewhere (see the existing test "uses a valid timestamp when the runtime guidance timestamp is non-finite"), but that test does not exercise this new branch, because it pre-populates a queued message. This new branch is precisely the path used for executor-originated corrections that never touch the local queue, so it is the most likely to encounter an unexpected timestamp value in practice.
Validate appliedAtMs before constructing the Date.
🐛 Proposed fix
const queuedGuidance = takeAppliedRuntimeConversationGuidance(address, payload)
const guidanceMessage =
queuedGuidance ??
(payload.clientGuidanceId && payload.message
? {
id: payload.clientGuidanceId,
content: payload.message,
status: 'sending',
deliveryMode: 'guidance',
- createdAt: new Date(payload.appliedAtMs).toISOString(),
+ createdAt: new Date(
+ Number.isFinite(payload.appliedAtMs) ? payload.appliedAtMs : Date.now()
+ ).toISOString(),
}
: null)Based on coding guidelines, "fix the primary flow rather than hiding defects behind fallbacks" supports validating the timestamp at its source instead of letting an unhandled exception propagate.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const queuedGuidance = takeAppliedRuntimeConversationGuidance(address, payload) | |
| const guidanceMessage = | |
| queuedGuidance ?? | |
| (payload.clientGuidanceId && payload.message | |
| ? { | |
| id: payload.clientGuidanceId, | |
| content: payload.message, | |
| status: 'sending', | |
| deliveryMode: 'guidance', | |
| createdAt: new Date(payload.appliedAtMs).toISOString(), | |
| } | |
| : null) | |
| if (!guidanceMessage) return null | |
| if (takeInterruptedRuntimeConversationGuidance(address, guidanceMessage.id)) { | |
| if (queuedGuidance && takeInterruptedRuntimeConversationGuidance(address, guidanceMessage.id)) { | |
| const queuedGuidance = takeAppliedRuntimeConversationGuidance(address, payload) | |
| const guidanceMessage = | |
| queuedGuidance ?? | |
| (payload.clientGuidanceId && payload.message | |
| ? { | |
| id: payload.clientGuidanceId, | |
| content: payload.message, | |
| status: 'sending', | |
| deliveryMode: 'guidance', | |
| createdAt: new Date( | |
| Number.isFinite(payload.appliedAtMs) ? payload.appliedAtMs : Date.now() | |
| ).toISOString(), | |
| } | |
| : null) | |
| if (!guidanceMessage) return null | |
| if (queuedGuidance && takeInterruptedRuntimeConversationGuidance(address, guidanceMessage.id)) { |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/features/workbench/runtimeConversationCache.ts` around lines 403 -
417, Validate that payload.appliedAtMs is a finite number before the fallback
guidance object is constructed in the guidance-message flow. Ensure the
non-queued executor-originated path never calls Date(...).toISOString() with an
invalid timestamp, while preserving the existing queued guidance behavior and
valid-timestamp fallback.
Source: Coding guidelines
What changed
Why
Users need a proxy that can watch ongoing AI work and intervene with the same effect as a message typed into the task composer. The supervision loop must keep working when the task UI is closed, react while work is still in progress, and avoid creating a separate task or conversation.
User impact
After enabling Experimental Features, users can configure supervision from a task, choose suggestion or automatic correction, select a model and frequency, and leave the workbench UI while the executor continues inspecting active work.
Supervision stops scheduling checks once a task has finished and its final content has been inspected. The configuration remains available for the next turn until the user closes supervision.
Validation
cargo fmt --checkcargo test --all-features --libcargo clippy --all-features --lib -- -D warningssupervisor-lifecycleSummary by CodeRabbit