feat(orchestration): add proactive collaboration - #2082
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (18)
🚧 Files skipped from review as they are similar to previous changes (18)
📝 WalkthroughWalkthroughThis PR replaces the ChangesDocumentation
Build, Dependencies & Vendor Data
Proactive Orchestration & Workflow Runtime Feature
Estimated code review effort: 5 (Critical) | ~180 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ChatInputBox
participant AgentToolManager
participant LiveDelegationService
participant LiveDelegationRepository
participant ChildSession
User->>ChatInputBox: Submit "deepchat_subagents" spawn request
ChatInputBox->>AgentToolManager: call live delegation tool
AgentToolManager->>LiveDelegationService: spawn(slotId, title, prompt)
LiveDelegationService->>LiveDelegationRepository: create delegation + turn
LiveDelegationService->>ChildSession: create/bind child session
ChildSession-->>LiveDelegationService: runtime updates (running/waiting/complete)
LiveDelegationService->>LiveDelegationRepository: settle turn (result ref, tape receipt)
LiveDelegationService-->>AgentToolManager: delegation summary
AgentToolManager-->>ChatInputBox: tool result
sequenceDiagram
participant Renderer
participant WorkflowClient
participant WorkflowRoutes
participant WorkflowService
participant QuickJSWorkflowRuntime
participant WorkflowRepository
Renderer->>WorkflowClient: prepareLaunch(script, input)
WorkflowClient->>WorkflowRoutes: workflowPrepareLaunchRoute
WorkflowRoutes->>WorkflowService: prepare launch approval
WorkflowService-->>WorkflowRoutes: approval card
WorkflowRoutes-->>Renderer: WorkflowLaunchApproval
Renderer->>WorkflowClient: launch(approvalId)
WorkflowClient->>WorkflowRoutes: workflowLaunchRoute
WorkflowRoutes->>WorkflowService: launch(request)
WorkflowService->>WorkflowRepository: create run
WorkflowService->>QuickJSWorkflowRuntime: start(source, input)
QuickJSWorkflowRuntime-->>WorkflowService: INVOKE_AGENT / PHASE / LOG events
WorkflowService->>WorkflowRepository: persist invocation results
QuickJSWorkflowRuntime-->>WorkflowService: COMPLETE/FAILED
WorkflowService-->>Renderer: workflow.run.changed event
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: 13
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (23)
src/renderer/src/i18n/vi-VN/chat.json-578-652 (1)
578-652: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winTranslate the remaining English workflow strings.
The
workflowkeys in lines 578-652 and thesavedkeys in lines 667-698 hold English copy, whileorchestration(563-577) andworkflow.approval(653-666) are Vietnamese. The Vietnamese UI would mix two languages in the same panel. The zh-CN, zh-HK, and zh-TW files translate the same keys, so this gap looks unintended.Provide Vietnamese copy for
workflow.title,loading,runLabel,empty,status,fields,actions,states,invocations,interactions,effects,effectWarning,duration,budget, and the wholesavedsubtree.As per coding guidelines: "Use vue-i18n for user-facing copy".
Also applies to: 667-698
🤖 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 `@src/renderer/src/i18n/vi-VN/chat.json` around lines 578 - 652, Translate every English user-facing string in the Vietnamese locale’s workflow section—including title, loading, run labels, empty state, statuses, fields, actions, states, invocations, interactions, effects, effect warnings, duration, budget, and the entire saved subtree—into natural Vietnamese while preserving all existing keys and interpolation placeholders. Use the corresponding translated locale entries as semantic references and keep the existing vue-i18n key structure unchanged.Source: Coding guidelines
src/main/workflow/launchApproval.ts-76-81 (1)
76-81: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReport the limit that was actually exceeded.
The condition fails for two different limits. The message always names
request.limits.maxScriptBytes. If the request limit is larger thanWORKFLOW_RUNTIME_MAX_SCRIPT_BYTES, the message reports a limit that the source did not exceed.🔧 Proposed fix
- if ( - sourceBytes > WORKFLOW_RUNTIME_MAX_SCRIPT_BYTES || - sourceBytes > request.limits.maxScriptBytes - ) { - throw new Error(`Workflow source exceeds its ${request.limits.maxScriptBytes}-byte limit.`) - } + const effectiveMaxScriptBytes = Math.min( + WORKFLOW_RUNTIME_MAX_SCRIPT_BYTES, + request.limits.maxScriptBytes + ) + if (sourceBytes > effectiveMaxScriptBytes) { + throw new Error(`Workflow source exceeds its ${effectiveMaxScriptBytes}-byte 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 `@src/main/workflow/launchApproval.ts` around lines 76 - 81, Update the error handling in the source-size validation around WORKFLOW_RUNTIME_MAX_SCRIPT_BYTES and request.limits.maxScriptBytes to report the specific limit that was exceeded. Distinguish the runtime maximum from the request-specific limit, preserving the existing rejection behavior while naming the applicable byte limit in each case.src/renderer/src/pages/NewThreadPage.vue-855-858 (1)
855-858: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winApply the submission guard before consuming the workflow slash command.
onSubmitconsumes the workflow slash command on line 856, before the guard on line 857. If a submission is already in flight, or the ACP workspace is unavailable, the composer still consumes the command and clears its authoring state. Move the consume call after the guard.🐛 Proposed fix
async function onSubmit() { - if (chatInputRef.value?.consumeWorkflowSlashCommand?.()) return if (isAcpWorkdirUnavailable.value || isSubmittingInput.value) return + if (chatInputRef.value?.consumeWorkflowSlashCommand?.()) return🤖 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 `@src/renderer/src/pages/NewThreadPage.vue` around lines 855 - 858, In onSubmit, evaluate the isAcpWorkdirUnavailable and isSubmittingInput guard before calling consumeWorkflowSlashCommand. Keep the existing early return for the workflow command after that guard so unavailable or in-flight submissions do not consume or clear the command state.src/main/workflow/runtime/quickjsWorkflowRuntime.ts-505-563 (1)
505-563: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winSkip terminal emission when the run entry no longer exists.
handleHostEventdrops events onceterminalizingis set, butcancelstill emitsFAILEDfromquickjsWorkflowRuntimeif the cancellation happens after the host dropped the event or after the active entry was removed. Storeterminalizingin the runtime host or addrunFinalizer/exited-aware guard logic there.🤖 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 `@src/main/workflow/runtime/quickjsWorkflowRuntime.ts` around lines 505 - 563, Update cancel in quickjsWorkflowRuntime to skip the terminal FAILED emission when the run is already terminalizing, exited, or its active host entry no longer exists. Reuse the runtime host’s terminal-state/run-entry guard used by handleHostEvent, such as terminalizing or runFinalizer state, while preserving cancellation cleanup and emission for active runs.src/main/workflow/runtime/quickjsWorkflowRuntime.ts-100-111 (1)
100-111: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd
catchandfinallyto the agent thenable.
agent()returns an object with onlythen.awaitworks, but guest code that writesagent(...).catch(handler)or.finally(handler)fails with aTypeError. The guest cannot work around this, becausePromiseandPromise.prototypeare frozen andPromise.prototype.constructoris removed. Route both methods through the same observation path.🐛 Proposed fix inside the bootstrap source
let observed = false - return Object.freeze({ - constructor: undefined, - then: (onFulfilled, onRejected) => { - if (!observed) { - observeAgentHost(callPath) - observed = true - } - return hostPromise.then(onFulfilled, onRejected) - } - }) + const observe = () => { + if (!observed) { + observeAgentHost(callPath) + observed = true + } + } + return Object.freeze({ + constructor: undefined, + then: (onFulfilled, onRejected) => { + observe() + return hostPromise.then(onFulfilled, onRejected) + }, + catch: (onRejected) => { + observe() + return hostPromise.then(undefined, onRejected) + }, + finally: (onFinally) => { + observe() + return hostPromise.then( + (value) => { + onFinally() + return value + }, + (error) => { + onFinally() + return promiseReject(error) + } + ) + } + })🤖 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 `@src/main/workflow/runtime/quickjsWorkflowRuntime.ts` around lines 100 - 111, Extend the frozen thenable returned by the agent runtime around the existing then method to expose catch and finally methods. Route both methods through the same observeAgentHost(callPath) guard before delegating to hostPromise, preserving single observation and native promise chaining behavior for guest calls.src/main/workflow/runtime/workflowSourceValidator.ts-309-309 (1)
309-309: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winLines exceed the 100-column limit.
Lines 309, 369, and 380 are longer than 100 columns. Run the formatter so these lines wrap.
As per coding guidelines: "Follow Oxfmt formatting: single quotes, no semicolons, and a 100-column width."
Also applies to: 369-369, 380-380
🤖 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 `@src/main/workflow/runtime/workflowSourceValidator.ts` at line 309, Run the project formatter on the affected workflow source validator code, including the calls around rejectHelper and the corresponding lines near 369 and 380, so all lines comply with Oxfmt’s 100-column width while preserving behavior.Source: Coding guidelines
src/renderer/src/i18n/da-DK/chat.json-578-652 (1)
578-652: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winTranslate the remaining
workflowstrings in the non-Englishchat.jsonlocale files.Each non-English locale still copies many
workflowkeys fromen-US, including the nestedsavedvalues. Translateworkflow.title,workflow.loading,workflow.runLabel,workflow.empty,workflow.status,workflow.fields,workflow.actions,workflow.states,workflow.invocations,workflow.interactions,workflow.effects,workflow.effectWarning,workflow.duration,workflow.budget,workflow.saved, andworkflow.saved.approvalin all affected locales. Keep interpolation placeholders unchanged.🤖 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 `@src/renderer/src/i18n/da-DK/chat.json` around lines 578 - 652, Translate the complete workflow localization content, including workflow.title, loading, runLabel, empty, status, fields, actions, states, invocations, interactions, effects, effectWarning, duration, budget, saved, and saved.approval, in src/renderer/src/i18n/da-DK/chat.json ranges 578-652 and 667-698 and src/renderer/src/i18n/de-DE/chat.json ranges 578-652 and 667-698. Replace copied en-US text with the appropriate Danish or German translations while preserving every interpolation placeholder exactly.src/renderer/src/i18n/es-ES/chat.json-578-652 (1)
578-652: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winWorkflow copy stays English in five locales. The new
workflowblock was copied from en-US into each locale, and onlyworkflow.approval.*was localized.workflow.title,workflow.loading,workflow.empty.*,workflow.status.*,workflow.fields.*,workflow.actions.*,workflow.states.*,workflow.invocations.*,workflow.interactions.*,workflow.effects.*,workflow.effectWarning.*,workflow.duration.*,workflow.budget.*, andworkflow.saved.*remain English in all five files.
src/renderer/src/i18n/es-ES/chat.json#L578-L652: translate the workflow keys into Spanish, and alsoworkflow.saved.*at L667-L699.src/renderer/src/i18n/fa-IR/chat.json#L578-L652: translate the workflow keys into Persian, and alsoworkflow.saved.*at L667-L699.src/renderer/src/i18n/fr-FR/chat.json#L578-L652: translate the workflow keys into French, and alsoworkflow.saved.*at L667-L699.src/renderer/src/i18n/he-IL/chat.json#L578-L652: translate the workflow keys into Hebrew, and alsoworkflow.saved.*at L667-L699.src/renderer/src/i18n/id-ID/chat.json#L578-L652: translate the workflow keys into Indonesian, and alsoworkflow.saved.*at L667-L699.If a follow-up localization pass owns these keys, state that in the PR description so reviewers do not treat it as an omission.
As per coding guidelines: "Use vue-i18n for user-facing copy".
🤖 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 `@src/renderer/src/i18n/es-ES/chat.json` around lines 578 - 652, Translate all user-facing workflow strings, including workflow.title, loading, empty, status, fields, actions, states, invocations, interactions, effects, effectWarning, duration, budget, and saved, instead of leaving English fallbacks. Apply the appropriate translations in src/renderer/src/i18n/es-ES/chat.json#L578-L652 and `#L667-L699`, fa-IR/chat.json#L578-L652 and `#L667-L699`, fr-FR/chat.json#L578-L652 and `#L667-L699`, he-IL/chat.json#L578-L652 and `#L667-L699`, and id-ID/chat.json#L578-L652 and `#L667-L699`; if deferred to a follow-up localization pass, explicitly document that in the PR description.Source: Coding guidelines
src/renderer/src/i18n/it-IT/chat.json-578-652 (1)
578-652: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winTranslate the new
workflowandworkflow.savedstrings. In all four locales theorchestrationblock and theworkflow.approvalblock are translated, but theworkflowcore keys and theworkflow.savedkeys remain English. Users of these locales see mixed-language workflow panels.
src/renderer/src/i18n/it-IT/chat.json#L578-L652: translateworkflow.titlethroughworkflow.budgetinto Italian, and alsoworkflow.savedat L667-L699.src/renderer/src/i18n/ja-JP/chat.json#L578-L652: translate the sameworkflowkeys into Japanese, and alsoworkflow.savedat L667-L699.src/renderer/src/i18n/ko-KR/chat.json#L578-L652: translate the sameworkflowkeys into Korean, and alsoworkflow.savedat L667-L699.src/renderer/src/i18n/ms-MY/chat.json#L578-L652: translate the sameworkflowkeys into Malay, and alsoworkflow.savedat L667-L699.Keep all placeholders unchanged:
{id},{version},{current},{count},{minutes},{seconds},{hours},{duration},{attempt}.As per coding guidelines "Use vue-i18n for user-facing copy".
🤖 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 `@src/renderer/src/i18n/it-IT/chat.json` around lines 578 - 652, Translate the workflow core keys from workflow.title through workflow.budget, plus workflow.saved, while preserving every existing key and placeholder. Apply the same translations in src/renderer/src/i18n/it-IT/chat.json:578-652 and workflow.saved:667-699 (Italian), src/renderer/src/i18n/ja-JP/chat.json:578-652 and workflow.saved:667-699 (Japanese), src/renderer/src/i18n/ko-KR/chat.json:578-652 and workflow.saved:667-699 (Korean), and src/renderer/src/i18n/ms-MY/chat.json:578-652 and workflow.saved:667-699 (Malay); keep placeholders {id}, {version}, {current}, {count}, {minutes}, {seconds}, {hours}, {duration}, and {attempt} unchanged.Source: Coding guidelines
src/renderer/src/i18n/pl-PL/chat.json-578-652 (1)
578-652: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winTranslate the remaining
workflow.*strings; they are still in English.In all four locale files, the
orchestrationblock andworkflow.approvalblock are translated correctly. The rest of theworkflowobject is not translated. It still contains the English source text, for example"title": "Workflows"and"loading": "Loading workflows...". Theworkflow.savedsubtree has the same problem, for example"title": "Saved workflows"and"workspaceRequired": "Open a project workspace to create and run saved workflows.". Users of these locales see English text mixed with translated text in the same panel.
src/renderer/src/i18n/pl-PL/chat.json#L578-L652: Translateworkflow.title,loading,runLabel,empty,status,fields,actions,states,invocations,interactions,effects,effectWarning,duration, andbudgetinto Polish.src/renderer/src/i18n/pl-PL/chat.json#L667-L699: Translateworkflow.saved(title, workspaceRequired, selectPlaceholder, empty, unsaved, runTitle, agentPlaceholder, fields, actions, approval) into Polish.src/renderer/src/i18n/pt-BR/chat.json#L578-L652: Translate the sameworkflow.*keys into Portuguese.src/renderer/src/i18n/pt-BR/chat.json#L667-L699: Translateworkflow.savedinto Portuguese.src/renderer/src/i18n/ru-RU/chat.json#L578-L652: Translate the sameworkflow.*keys into Russian.src/renderer/src/i18n/ru-RU/chat.json#L667-L699: Translateworkflow.savedinto Russian.src/renderer/src/i18n/tr-TR/chat.json#L578-L652: Translate the sameworkflow.*keys into Turkish.src/renderer/src/i18n/tr-TR/chat.json#L667-L699: Translateworkflow.savedinto Turkish.Check whether other locales outside this review batch (da-DK, de-DE, es-ES, fa-IR, fr-FR, he-IL, id-ID, it-IT, ja-JP, ko-KR, ms-MY, vi-VN, zh-CN, zh-HK, zh-TW) have the same gap, since the pattern is identical across every file in this batch.
🤖 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 `@src/renderer/src/i18n/pl-PL/chat.json` around lines 578 - 652, Translate every remaining workflow object string and workflow.saved subtree into the target locale languages, preserving all keys and interpolation placeholders: Polish in src/renderer/src/i18n/pl-PL/chat.json at lines 578-652 and 667-699, Portuguese in src/renderer/src/i18n/pt-BR/chat.json at lines 578-652 and 667-699, Russian in src/renderer/src/i18n/ru-RU/chat.json at lines 578-652 and 667-699, and Turkish in src/renderer/src/i18n/tr-TR/chat.json at lines 578-652 and 667-699. Also inspect the listed additional locales for the same untranslated workflow and workflow.saved keys and translate them if the gap exists.src/renderer/src/components/chat/ChatStatusBar.vue-1342-1351 (1)
1342-1351: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winAdd translation keys for the unavailable orchestration reasons.
orchestrationCapabilityMessageuses dynamic keys likechat.orchestration.proactive.reasons.${capability.reason}, butcapability.reasoncan be one of several values and thechat.orchestration.proactive.reasons.*keys are missing from the locale files. Add the matching keys in all locales so the UI does not show raw key paths for unavailable orchestration capability states.🤖 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 `@src/renderer/src/components/chat/ChatStatusBar.vue` around lines 1342 - 1351, Add the missing chat.orchestration.proactive.reasons.* translation entries for every possible capability.reason value to every locale file, keeping the keys consistent across locales so orchestrationCapabilityMessage resolves localized text instead of raw paths.src/renderer/src/components/chat/ChatStatusBar.vue-341-377 (1)
341-377: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAssociate the ACP option label with its control.
The
<label>element at Line 341 has noforattribute and does not wrap the control. Screen readers announce only the current value for theSelectTriggerand for the booleanButton. Add an accessible name to each control.♿ Proposed fix
<SelectTrigger :disabled="acpConfigReadOnly || isAcpOptionSaving(option.id)" class="h-8 w-[9rem] text-xs" + :aria-label="option.label" ><Button v-else type="button" variant="outline" size="sm" class="h-8 min-w-[6rem] text-xs" + :aria-label="option.label" :disabled="acpConfigReadOnly || isAcpOptionSaving(option.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 `@src/renderer/src/components/chat/ChatStatusBar.vue` around lines 341 - 377, Add accessible labeling for each ACP option in the option-rendering block: associate the visible label with the corresponding SelectTrigger or boolean Button using a stable option-specific identifier, and ensure both control variants expose that label to assistive technologies. Update the label and control markup around the option label, Select, and Button without changing their existing behavior.src/renderer/src/lib/liveDelegationPresentation.ts-75-79 (1)
75-79: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winReturn a fallback presentation for an unknown status.
LiveDelegationSummarycarriesschemaVersion, so a persisted or IPC-delivered row can hold a status string that this map does not contain. In that case the lookup returnsundefined, and a caller that readslabelKeyordotClassthrows during render. Add a terminal-neutral fallback.🛡️ Proposed fix
+const UNKNOWN_PRESENTATION: LiveDelegationStatusPresentation = { + labelKey: 'chat.toolCall.subagents.status.error', + dotClass: 'bg-muted-foreground', + badgeClass: 'bg-muted text-muted-foreground', + active: false, + actionRequired: false +} + export function getLiveDelegationStatusPresentation( status: LiveDelegationDisplayStatus ): LiveDelegationStatusPresentation { - return STATUS_PRESENTATIONS[status] + return STATUS_PRESENTATIONS[status] ?? UNKNOWN_PRESENTATION }The
Recordtype still keeps the map exhaustive for every knownLiveDelegationStatusmember at compile time.🤖 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 `@src/renderer/src/lib/liveDelegationPresentation.ts` around lines 75 - 79, Update getLiveDelegationStatusPresentation to return a terminal-neutral fallback presentation when STATUS_PRESENTATIONS[status] is undefined, while preserving the exhaustive Record typing for known LiveDelegationStatus members and ensuring callers always receive valid labelKey and dotClass values.src/renderer/src/lib/workflowLaunchApproval.ts-20-20 (1)
20-20: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
z.string().max()counts characters, not bytes.
WORKFLOW_RUNTIME_MAX_SCRIPT_BYTESis a byte limit. The producer insrc/main/workflow/launchApproval.tsenforces it withBuffer.byteLength(request.scriptSource, 'utf8').z.string().max()counts UTF-16 code units, so for non-ASCII sources this renderer bound is looser than the producer bound. The fallback is safe because the approval already passed main-process validation, so this is a clarity issue. Add a comment that the constant is reused as a character ceiling, or derive an explicit character bound.🤖 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 `@src/renderer/src/lib/workflowLaunchApproval.ts` at line 20, Clarify the validation in the workflow launch approval schema around scriptSource by documenting that WORKFLOW_RUNTIME_MAX_SCRIPT_BYTES is reused as a character ceiling, since z.string().max() does not enforce the producer’s UTF-8 byte limit. Keep the existing main-process byte validation and renderer max constraint unchanged.src/shared/workflow/serviceContracts.ts-149-152 (1)
149-152: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winClarify the meaning of an explicit
budget: null.
WorkflowLaunchDraftSchemaacceptsbudget: null, andWorkflowLaunchRequestSchemadeclaresbudgetas nullable. This resolver always produces a non-null budget, because it injectsmaxExecutionMs: WORKFLOW_DEFAULT_EXECUTION_TIMEOUT_MSwhen the caller omits it or passesnull. A caller that passesnullto request no budget receives a two-hour execution cap. Either document that every run carries a default execution budget and dropnullable()from the request schema, or preservenullwhen the caller sets it.♻️ Option: preserve an explicit null
- const budget = WorkflowRunBudgetSchema.parse({ - ...(parsed.budget ?? {}), - maxExecutionMs: parsed.budget?.maxExecutionMs ?? WORKFLOW_DEFAULT_EXECUTION_TIMEOUT_MS - }) + const budget = + parsed.budget === null + ? null + : WorkflowRunBudgetSchema.parse({ + ...(parsed.budget ?? {}), + maxExecutionMs: parsed.budget?.maxExecutionMs ?? WORKFLOW_DEFAULT_EXECUTION_TIMEOUT_MS + })🤖 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 `@src/shared/workflow/serviceContracts.ts` around lines 149 - 152, Update the budget resolution around WorkflowRunBudgetSchema.parse to preserve an explicitly provided budget: null instead of replacing it with the default execution budget. Keep applying WORKFLOW_DEFAULT_EXECUTION_TIMEOUT_MS only when the budget is omitted, and ensure WorkflowLaunchDraftSchema and WorkflowLaunchRequestSchema remain consistent with the resulting nullable behavior.src/shared/lib/deepchatSubagents.ts-10-10 (1)
10-10: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winSurface the title limit in the model guidance.
Line 10 defines an 80-character limit. Line 24 does not state it. If the tool layer rejects longer titles, the model learns the bound only from a failed call. Interpolate the constant into the guidance sentence.
✏️ Proposed change
- 'Name each spawned task with a concise user-language action-and-scope title; keep sibling titles distinct and do not use role-only, ordinal, or person-like names.', + `Name each spawned task with a concise user-language action-and-scope title of at most ${DEEPCHAT_SUBAGENT_TASK_TITLE_LIMIT} characters; keep sibling titles distinct and do not use role-only, ordinal, or person-like names.`,Also applies to: 24-24
🤖 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 `@src/shared/lib/deepchatSubagents.ts` at line 10, Update the model guidance sentence near the tool definition to interpolate DEEPCHAT_SUBAGENT_TASK_TITLE_LIMIT, explicitly stating the maximum title length while preserving the existing guidance.src/main/agent/shared/appSessionService.ts-33-46 (1)
33-46: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winPreserve subagent identity when the stored contexts conflict.
Line 36 returns null for the whole metadata record. The caller then reports
subagentMeta: null, so slotId and displayName are lost and the session stops looking like a subagent.src/main/app/composition.tslines 1604-1613 and 1714-1726 resolve children throughsubagentMeta, so those lookups miss the row. Drop only the conflicting contexts and keep the identity fields.🛡️ Proposed change
const liveDelegation = parseLiveDelegationSubagentContext(parsed.liveDelegation) - if (correlatedWorkflow && liveDelegation) return null + const conflicting = Boolean(correlatedWorkflow && liveDelegation) return { slotId: parsed.slotId, displayName: typeof parsed.displayName === 'string' ? parsed.displayName : parsed.slotId, targetAgentId: parsed.targetAgentId === null || typeof parsed.targetAgentId === 'string' ? parsed.targetAgentId : undefined, - ...(correlatedWorkflow ? { workflow: correlatedWorkflow } : {}), - ...(liveDelegation ? { liveDelegation } : {}) + ...(!conflicting && correlatedWorkflow ? { workflow: correlatedWorkflow } : {}), + ...(!conflicting && liveDelegation ? { liveDelegation } : {}) }🤖 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 `@src/main/agent/shared/appSessionService.ts` around lines 33 - 46, Update the conflict handling in the metadata construction around parseWorkflowSubagentContext and parseLiveDelegationSubagentContext: when both contexts are present, omit both conflicting context fields but still return the record with slotId, displayName, and targetAgentId. Remove the early null return while preserving the existing correlated-context and live-delegation fields when they do not conflict.src/shared/workflow/savedWorkflow.ts-36-46 (1)
36-46: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winEnforce byte budgets in the shared schemas.
WORKFLOW_SAVED_MAX_SOURCE_BYTESandWORKFLOW_SAVED_MAX_ARGS_BYTESname byte limits, but Zod’sstring().max()only checks UTF-16 length. A workflow source or args string using multi-byte characters can pass these schemas while exceeding the intended byte cap. Add a byte-length refinement before.max(), or rename the constants to character limits.♻️ Byte-accurate refinement
- source: z.string().min(1).max(WORKFLOW_SAVED_MAX_SOURCE_BYTES) + source: z + .string() + .min(1) + .refine((value) => Buffer.byteLength(value, 'utf8') <= WORKFLOW_SAVED_MAX_SOURCE_BYTES, { + error: 'Workflow source exceeds the maximum byte size' + })-export const WorkflowSavedArgsTextSchema = z.string().max(WORKFLOW_SAVED_MAX_ARGS_BYTES) +export const WorkflowSavedArgsTextSchema = z + .string() + .refine((value) => Buffer.byteLength(value, 'utf8') <= WORKFLOW_SAVED_MAX_ARGS_BYTES, { + error: 'Workflow args exceed the maximum byte size' + })🤖 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 `@src/shared/workflow/savedWorkflow.ts` around lines 36 - 46, Update WorkflowSavedSourceSchema and WorkflowSavedArgsTextSchema to validate UTF-8 byte length against WORKFLOW_SAVED_MAX_SOURCE_BYTES and WORKFLOW_SAVED_MAX_ARGS_BYTES before retaining their existing character-length max checks. Use a Zod refinement based on encoded byte length, preserving the current non-empty source requirement and schema behavior otherwise.src/main/orchestration/routes.ts-44-63 (1)
44-63: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
session_unavailableis handled only for theproactivepolicy. The guard atsrc/main/orchestration/routes.tsLine 48 requiresinput.policy === 'proactive'. A request forexplicitagainst a deleted session therefore reachesoptions.setPolicy, which throws, and the route rejects with a raw persistence error instead of the stableapplied: falseresult. The test mirrors the same narrow scope, so the gap is not detected.
src/main/orchestration/routes.ts#L44-L63: return{ applied: false, policy: DEFAULT_ORCHESTRATION_POLICY, capability }whenevercapability.reason === 'session_unavailable', before theproactivecheck, and keepawait options.getPolicy(input.sessionId)for the remaining unavailable reasons.test/main/orchestration/orchestrationRoutes.test.ts#L92-L116: add an assertion thatupdatePolicy({ sessionId: 'deleted-session', policy: 'explicit' }, context)also resolves toapplied: falseand does not callsetPolicy.🤖 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 `@src/main/orchestration/routes.ts` around lines 44 - 63, Update the orchestrationSetPolicyRoute handler so session_unavailable returns applied: false with DEFAULT_ORCHESTRATION_POLICY for every requested policy, before checking whether input.policy is proactive; retain options.getPolicy for other unavailable reasons and preserve normal setPolicy behavior otherwise. In test/main/orchestration/orchestrationRoutes.test.ts lines 92-116, add coverage for updatePolicy with deleted-session and explicit, asserting applied: false and that options.setPolicy is not called.src/main/agent/invocationAdmission.ts-78-96 (1)
78-96: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winReturn rejected promises for invalid admission options.
acquire()callsnormalizeOwnerId()andnormalizeOwnerLimit()directly, and both throw plainErrorvalues before returning a promise. The current direct caller is caught viaawait, butWorkflowRunAdmission.acquire()forwards the same promise-returning API, so promise-chain callers can still receive an unhandled synchronous throw. Wrap the option validation intry/catchand reject the promise before checkingclosedErroror queuing.🤖 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 `@src/main/agent/invocationAdmission.ts` around lines 78 - 96, Update acquire in AgentInvocationAdmission to wrap normalizeOwnerId and normalizeOwnerLimit validation in a try/catch, returning a rejected promise for validation errors. Perform this rejection before checking closedError or admission state, while preserving the existing behavior for valid options.src/main/tool/agentTools/workflowTool.ts-96-156 (1)
96-156: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winThe advertised parameter list omits
namedWorkflowPath.
workflowAgentToolSchemaacceptsnamedWorkflowPath, andexecuteforwards it toprepareLaunch. The JSON parameters object does not declare it, so the model cannot discover or use saved workflow paths through this tool. Add the property, or remove it from the schema if the field is set by another caller only.♻️ Proposed schema addition
parentMessageId: { type: ['string', 'null'], description: 'Optional parent message identity used as workflow provenance.' }, + namedWorkflowPath: { + type: ['string', 'null'], + description: 'Optional saved workflow path recorded with the prepared launch.' + },🤖 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 `@src/main/tool/agentTools/workflowTool.ts` around lines 96 - 156, Update workflowAgentToolSchema’s properties to declare the namedWorkflowPath parameter already accepted by the schema and forwarded by execute to prepareLaunch, including its appropriate type and description; preserve the existing prepare_launch behavior and do not remove the field from the execution path.test/main/orchestration/liveDelegationService.test.ts-55-66 (1)
55-66: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winEnable
PRAGMA foreign_keys = ONto match production and the repository test.
liveDelegationRepository.test.tssetsPRAGMA foreign_keys = ONbefore creating the tables. This setup does not. Foreign key enforcement is therefore off, soON DELETE CASCADEand thelive_delegation_turnsandlive_delegation_eventsreferences behave differently than in the application database. A service-level regression that violates a foreign key would pass here.💚 Proposed test setup fix
db.exec(` + PRAGMA foreign_keys = ON; CREATE TABLE new_sessions (🤖 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 `@test/main/orchestration/liveDelegationService.test.ts` around lines 55 - 66, Enable SQLite foreign-key enforcement in the test database setup before creating tables, matching the setup used by liveDelegationRepository.test.ts. Update the initialization around DatabaseCtor and the LiveDelegationsTableCtor, LiveDelegationTurnsTableCtor, and LiveDelegationEventsTableCtor calls so PRAGMA foreign_keys is set to ON before schema creation.src/main/tool/agentTools/workflowTool.ts-28-28 (1)
28-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winEnforce
scriptSourceas UTF-8 bytes before parsing.
WORKFLOW_RUNTIME_MAX_SCRIPT_BYTESis a byte limit, butz.string().max(...)limits UTF-16 code units. A multi-byte script can pass this validation and still fail later with a runtime byte-limit error instead of a clear tool validation error. Use a UTF-8Buffer.byteLength()refinement here, or enforce byte length downstream before parsing.🤖 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 `@src/main/tool/agentTools/workflowTool.ts` at line 28, Update the scriptSource validation schema to enforce WORKFLOW_RUNTIME_MAX_SCRIPT_BYTES using UTF-8 byte length rather than z.string().max’s UTF-16 code-unit count. Add a refinement based on Buffer.byteLength() before parsing, while preserving the existing optional and non-empty constraints.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: dad86131-cfb3-45b5-9c09-627870802aec
⛔ Files ignored due to path filters (3)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/renderer/src/lib/icons/icon-collections.generated.tsis excluded by!**/*.generated.*src/renderer/src/lib/icons/icon-whitelist.generated.tsis excluded by!**/*.generated.*
📒 Files selected for processing (279)
.github/workflows/prcheck.ymldocs/architecture/proactive-multi-agent-orchestration/plan.mddocs/architecture/proactive-multi-agent-orchestration/spec.mddocs/architecture/proactive-multi-agent-orchestration/tasks.mddocs/features/workflow-runtime/plan.mddocs/features/workflow-runtime/spec.mddocs/features/workflow-runtime/tasks.mddocs/issues/live-delegation-deletion-coordination/spec.mddocs/issues/live-delegation-interaction-closure/spec.mddocs/issues/live-delegation-terminal-races/spec.mdelectron-builder.ymlelectron.vite.config.tspackage.jsonresources/acp-registry/registry.jsonresources/model-db/providers.jsonsrc/main/agent/deepchat/harness/createDeepChatAgentHarness.tssrc/main/agent/deepchat/harness/deepChatAgentHarness.tssrc/main/agent/deepchat/memory/memoryRuntimeCoordinator.tssrc/main/agent/deepchat/resources/systemPromptBuilder.tssrc/main/agent/deepchat/runtime/contextBuilder.tssrc/main/agent/deepchat/runtime/dispatch.tssrc/main/agent/deepchat/runtime/generationSettings.tssrc/main/agent/deepchat/runtime/interactionCoordinator.tssrc/main/agent/deepchat/runtime/promptAssemblyService.tssrc/main/agent/deepchat/runtime/providerPermissionCoordinator.tssrc/main/agent/deepchat/runtime/runLifecycleCoordinator.tssrc/main/agent/deepchat/runtime/sessionStatusPublisher.tssrc/main/agent/deepchat/runtime/sessionUpdates.tssrc/main/agent/deepchat/runtime/toolResolver.tssrc/main/agent/deepchat/runtime/turnCoordinator.tssrc/main/agent/invocationAdmission.tssrc/main/agent/promptSettings.tssrc/main/agent/settings.tssrc/main/agent/shared/appSessionService.tssrc/main/app/composition.tssrc/main/data/schemaCatalog.tssrc/main/memory/data/tables/deepchatMemoryIngestionProjection.tssrc/main/orchestration/capability.tssrc/main/orchestration/data/database.tssrc/main/orchestration/data/tables/liveDelegationEvents.tssrc/main/orchestration/data/tables/liveDelegationTurns.tssrc/main/orchestration/data/tables/liveDelegations.tssrc/main/orchestration/liveDelegationRepository.tssrc/main/orchestration/liveDelegationService.tssrc/main/orchestration/routes.tssrc/main/session/assignment.tssrc/main/session/contracts.tssrc/main/session/data/tables/deepchatAssistantBlocks.tssrc/main/session/data/tables/deepchatMessages.tssrc/main/session/data/tables/newSessions.tssrc/main/session/data/transcript.tssrc/main/session/deletion.tssrc/main/session/deletionGate.tssrc/main/session/lifecycle.tssrc/main/session/runtimeEvents.tssrc/main/tool/agentTools/agentToolManager.tssrc/main/tool/agentTools/liveDelegationTool.tssrc/main/tool/agentTools/subagentOrchestratorTool.tssrc/main/tool/agentTools/workflowTool.tssrc/main/tool/effectClassification.tssrc/main/tool/effectObserver.tssrc/main/tool/index.tssrc/main/tool/runtimePorts.tssrc/main/tool/sessionToolProvider.tssrc/main/tool/toolMapper.tssrc/main/workflow/childExecutor.tssrc/main/workflow/childIdentity.tssrc/main/workflow/childRuntimeTracker.tssrc/main/workflow/data/database.tssrc/main/workflow/data/tables/workflowInvocations.tssrc/main/workflow/data/tables/workflowRuns.tssrc/main/workflow/domain/executionSnapshot.tssrc/main/workflow/domain/json.tssrc/main/workflow/effectObserver.tssrc/main/workflow/interactionProjection.tssrc/main/workflow/invocationContextRegistry.tssrc/main/workflow/launchApproval.tssrc/main/workflow/launchScope.tssrc/main/workflow/projection.tssrc/main/workflow/repository.tssrc/main/workflow/resultDelivery.tssrc/main/workflow/routes.tssrc/main/workflow/runAdmission.tssrc/main/workflow/runtime/quickjsWorkflowRuntime.tssrc/main/workflow/runtime/workflowSourceOutline.tssrc/main/workflow/runtime/workflowSourceValidator.tssrc/main/workflow/runtime/workflowUtilityHost.tssrc/main/workflow/runtime/workflowUtilityProcessHost.tssrc/main/workflow/savedWorkflowArgs.tssrc/main/workflow/savedWorkflowStore.tssrc/main/workflow/service.tssrc/main/workflow/structuredOutput/contracts.tssrc/main/workflow/structuredOutput/errors.tssrc/main/workflow/structuredOutput/registry.tssrc/main/workflow/structuredOutput/resultSchema.tssrc/main/workflowUtilityHostEntry.tssrc/renderer/api/OrchestrationClient.tssrc/renderer/api/WorkflowClient.tssrc/renderer/api/index.tssrc/renderer/src/apps/chat-main/ChatTabView.vuesrc/renderer/src/components/chat-input/McpIndicator.vuesrc/renderer/src/components/chat/ChatInputBox.vuesrc/renderer/src/components/chat/ChatStatusBar.vuesrc/renderer/src/components/chat/composables/useChatInputMentions.tssrc/renderer/src/components/chat/mentions/SuggestionList.vuesrc/renderer/src/components/chat/mentions/utils.tssrc/renderer/src/components/message/LiveDelegationToolCallCard.vuesrc/renderer/src/components/message/MessageBlockActivityGroup.vuesrc/renderer/src/components/message/MessageBlockToolCall.vuesrc/renderer/src/components/message/MessageItemAssistant.vuesrc/renderer/src/components/message/WorkflowLaunchApprovalCard.vuesrc/renderer/src/components/message/messageActivityGroups.tssrc/renderer/src/components/settings/ModelConfigDialog.vuesrc/renderer/src/components/sidepanel/ChatSidePanel.vuesrc/renderer/src/components/sidepanel/LiveDelegationPanel.vuesrc/renderer/src/components/sidepanel/SavedWorkflowPanel.vuesrc/renderer/src/components/sidepanel/WorkflowPanel.vuesrc/renderer/src/components/sidepanel/WorkspacePanel.vuesrc/renderer/src/events.tssrc/renderer/src/features/chat-page/ChatPage.vuesrc/renderer/src/features/chat-page/composables/useChatPageEventBridge.tssrc/renderer/src/features/chat-page/composables/useDisplayMessages.tssrc/renderer/src/features/chat-page/composables/useToolInteraction.tssrc/renderer/src/features/chat-page/model/displayMessage.tssrc/renderer/src/i18n/da-DK/chat.jsonsrc/renderer/src/i18n/de-DE/chat.jsonsrc/renderer/src/i18n/en-US/chat.jsonsrc/renderer/src/i18n/es-ES/chat.jsonsrc/renderer/src/i18n/fa-IR/chat.jsonsrc/renderer/src/i18n/fr-FR/chat.jsonsrc/renderer/src/i18n/he-IL/chat.jsonsrc/renderer/src/i18n/id-ID/chat.jsonsrc/renderer/src/i18n/it-IT/chat.jsonsrc/renderer/src/i18n/ja-JP/chat.jsonsrc/renderer/src/i18n/ko-KR/chat.jsonsrc/renderer/src/i18n/ms-MY/chat.jsonsrc/renderer/src/i18n/pl-PL/chat.jsonsrc/renderer/src/i18n/pt-BR/chat.jsonsrc/renderer/src/i18n/ru-RU/chat.jsonsrc/renderer/src/i18n/tr-TR/chat.jsonsrc/renderer/src/i18n/vi-VN/chat.jsonsrc/renderer/src/i18n/zh-CN/chat.jsonsrc/renderer/src/i18n/zh-HK/chat.jsonsrc/renderer/src/i18n/zh-TW/chat.jsonsrc/renderer/src/lib/liveDelegationPresentation.tssrc/renderer/src/lib/liveDelegationToolCall.tssrc/renderer/src/lib/workflowAuthoringDraftStore.tssrc/renderer/src/lib/workflowLaunchApproval.tssrc/renderer/src/lib/workflowOutline.tssrc/renderer/src/lib/workflowSupport.tssrc/renderer/src/pages/NewThreadPage.vuesrc/renderer/src/stores/ui/draft.tssrc/renderer/src/stores/ui/liveDelegation.tssrc/renderer/src/stores/ui/session.tssrc/renderer/src/stores/ui/sidepanel.tssrc/shared/agentTools.tssrc/shared/chat.d.tssrc/shared/contracts/common.tssrc/shared/contracts/events.tssrc/shared/contracts/events/orchestration.events.tssrc/shared/contracts/events/workflow.events.tssrc/shared/contracts/routes.tssrc/shared/contracts/routes/orchestration.routes.tssrc/shared/contracts/routes/sessions.routes.tssrc/shared/contracts/routes/workflow.routes.tssrc/shared/lib/assistantDeliverySegments.tssrc/shared/lib/deepchatSubagents.tssrc/shared/orchestration/liveDelegation.tssrc/shared/orchestration/toolEffect.tssrc/shared/types/agent-interface.d.tssrc/shared/types/core/chat.tssrc/shared/types/workspace.tssrc/shared/workflow/authoringContract.tssrc/shared/workflow/domain.tssrc/shared/workflow/orchestrationPolicy.tssrc/shared/workflow/outline.tssrc/shared/workflow/projection.tssrc/shared/workflow/resultDelivery.tssrc/shared/workflow/runtimeProtocol.tssrc/shared/workflow/savedWorkflow.tssrc/shared/workflow/serviceContracts.tssrc/shared/workflow/subagent.tssrc/types/i18n.d.tstest/main/agent/deepchat/harness/deepChatAgentHarness.test.tstest/main/agent/deepchat/memory/memoryRuntimeCoordinator.test.tstest/main/agent/deepchat/resources/systemPromptBuilder.test.tstest/main/agent/deepchat/runtime/contextBuilder.test.tstest/main/agent/deepchat/runtime/dispatch.test.tstest/main/agent/deepchat/runtime/generationSettings.test.tstest/main/agent/deepchat/runtime/promptAssemblyService.test.tstest/main/agent/deepchat/runtime/sessionStatusPublisher.test.tstest/main/agent/deepchat/runtime/toolResolver.test.tstest/main/agent/invocationAdmission.test.tstest/main/agent/promptSettings.test.tstest/main/agent/settings.test.tstest/main/agent/shared/appSessionService.test.tstest/main/build/electronBuilderConfig.test.tstest/main/data/mainDatabase.migrationSqlSplit.test.tstest/main/memory/deepchatMemoryIngestionProjection.test.tstest/main/memory/memoryNativeMigration.test.tstest/main/orchestration/liveDelegationMigration.test.tstest/main/orchestration/liveDelegationRepository.test.tstest/main/orchestration/liveDelegationService.test.tstest/main/orchestration/orchestrationCapability.test.tstest/main/orchestration/orchestrationRoutes.test.tstest/main/routes/dispatcher.test.tstest/main/scripts/prcheckWorkflow.test.tstest/main/session/assignment.test.tstest/main/session/data/tables/deepchatMessagesTable.test.tstest/main/session/data/tables/newSessionsTable.test.tstest/main/session/data/transcript.test.tstest/main/session/deletion.test.tstest/main/session/deletionGate.test.tstest/main/session/lifecycle.test.tstest/main/session/runtimeEvents.test.tstest/main/session/session.integration.test.tstest/main/session/sessionFixture.tstest/main/shared/orchestrationPolicy.test.tstest/main/tool/agentTools/agentToolDependencies.tstest/main/tool/agentTools/agentToolManagerSettings.test.tstest/main/tool/agentTools/liveDelegationTool.test.tstest/main/tool/agentTools/subagentOrchestratorTool.test.tstest/main/tool/agentTools/workflowTool.test.tstest/main/tool/toolService.test.tstest/main/workflow/quickjsWorkflowRuntime.test.tstest/main/workflow/runtimeProtocol.test.tstest/main/workflow/workflowChildExecutor.test.tstest/main/workflow/workflowEffectObserver.test.tstest/main/workflow/workflowInteractionProjection.test.tstest/main/workflow/workflowInvocationContextRegistry.test.tstest/main/workflow/workflowJson.test.tstest/main/workflow/workflowLaunchApproval.test.tstest/main/workflow/workflowLaunchScope.test.tstest/main/workflow/workflowMigration.test.tstest/main/workflow/workflowPersistence.test.tstest/main/workflow/workflowProjection.test.tstest/main/workflow/workflowResultDelivery.test.tstest/main/workflow/workflowResultSchema.test.tstest/main/workflow/workflowRoutes.test.tstest/main/workflow/workflowRunAdmission.test.tstest/main/workflow/workflowSavedStore.test.tstest/main/workflow/workflowService.test.tstest/main/workflow/workflowServiceContracts.test.tstest/main/workflow/workflowSourceOutline.test.tstest/main/workflow/workflowSourceValidator.test.tstest/main/workflow/workflowStructuredOutputRegistry.test.tstest/main/workflow/workflowTestFixtures.tstest/main/workflow/workflowUtilityHost.test.tstest/main/workflow/workflowUtilityProcessHost.test.tstest/renderer/api/OrchestrationClient.test.tstest/renderer/api/WorkflowClient.test.tstest/renderer/components/ChatInputBox.test.tstest/renderer/components/ChatPage.test.tstest/renderer/components/ChatSidePanel.test.tstest/renderer/components/ChatStatusBar.test.tstest/renderer/components/LiveDelegationPanel.test.tstest/renderer/components/McpIndicator.test.tstest/renderer/components/ModelConfigDialog.test.tstest/renderer/components/NewThreadPage.test.tstest/renderer/components/SavedWorkflowPanel.test.tstest/renderer/components/WorkflowPanel.test.tstest/renderer/components/WorkspacePanel.test.tstest/renderer/components/message/LiveDelegationToolCallCard.test.tstest/renderer/components/message/MessageBlockToolCall.test.tstest/renderer/components/message/MessageItemAssistant.test.tstest/renderer/components/message/WorkflowLaunchApprovalCard.test.tstest/renderer/components/message/messageActivityGroups.test.tstest/renderer/composables/useChatInputMentions.test.tstest/renderer/composables/useChatInputSkillScope.test.tstest/renderer/features/chat-page/composables/useChatPageEventBridge.test.tstest/renderer/features/chat-page/composables/useToolInteraction.test.tstest/renderer/lib/liveDelegationToolCall.test.tstest/renderer/lib/workflowAuthoringDraftStore.test.tstest/renderer/lib/workflowLaunchApproval.test.tstest/renderer/lib/workflowSupport.test.tstest/renderer/stores/draft.test.tstest/renderer/stores/liveDelegationStore.test.tstest/renderer/stores/sessionStore.test.tstest/renderer/stores/sidepanel.test.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/main/workflow/launchApproval.ts (1)
97-105: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winBound the retained approval objects before adding to
pendingBytes.
approvalBytesdoes not include the full retained approval/request objects, so they can exceedmaxPendingBytesfor large inputs, scripts, capabilities, agent allowlists, limits, or budgets. Add the serialized size of the stored entry, or add strict byte/structural limits for all retained fields.🤖 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 `@src/main/workflow/launchApproval.ts` around lines 97 - 105, Update the approval-size calculation in the workflow launch approval path before the pendingBytes check so it accounts for the complete retained approval/request entry, including large inputs, scripts, capabilities, agent allowlists, limits, and budgets. Serialize the stored entry using the existing canonical representation where applicable, add its byte length to approvalBytes, and ensure the maxPendingBytes checks bound the actual object retained in pending state.
🧹 Nitpick comments (1)
src/main/workflow/launchApproval.ts (1)
124-147: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winKeep the stored approval immutable.
preparestoresapprovalinpendingand returns the same mutable object. A caller can changeexpiresAtorsummaryafterprepare; laterprune()andget()then observe the changed state. Store a deep clone and return a separate clone, or deep-freeze the complete approval before storing 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 `@src/main/workflow/launchApproval.ts` around lines 124 - 147, The approval stored by prepare must not share mutable state with the object returned to callers. Update the approval handling around WorkflowLaunchApprovalSchema.parse and this.pending.set to deep-clone before storage and return a separate deep clone, or deep-freeze the complete approval before both storage and return, ensuring prune() and get() always observe immutable approval data.
🤖 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.
Outside diff comments:
In `@src/main/workflow/launchApproval.ts`:
- Around line 97-105: Update the approval-size calculation in the workflow
launch approval path before the pendingBytes check so it accounts for the
complete retained approval/request entry, including large inputs, scripts,
capabilities, agent allowlists, limits, and budgets. Serialize the stored entry
using the existing canonical representation where applicable, add its byte
length to approvalBytes, and ensure the maxPendingBytes checks bound the actual
object retained in pending state.
---
Nitpick comments:
In `@src/main/workflow/launchApproval.ts`:
- Around line 124-147: The approval stored by prepare must not share mutable
state with the object returned to callers. Update the approval handling around
WorkflowLaunchApprovalSchema.parse and this.pending.set to deep-clone before
storage and return a separate deep clone, or deep-freeze the complete approval
before both storage and return, ensuring prune() and get() always observe
immutable approval data.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 512759c9-002f-49fd-bfa8-cc962fa720f5
📒 Files selected for processing (37)
docs/features/workflow-runtime/plan.mddocs/features/workflow-runtime/spec.mddocs/features/workflow-runtime/tasks.mdsrc/main/agent/invocationAdmission.tssrc/main/agent/shared/appSessionService.tssrc/main/app/composition.tssrc/main/orchestration/routes.tssrc/main/session/assignment.tssrc/main/session/data/tables/newSessions.tssrc/main/session/deletionGate.tssrc/main/tool/agentTools/workflowTool.tssrc/main/workflow/launchApproval.tssrc/main/workflow/launchScope.tssrc/main/workflow/runtime/workflowSourceValidator.tssrc/main/workflow/structuredOutput/registry.tssrc/renderer/src/components/chat/ChatStatusBar.vuesrc/renderer/src/lib/liveDelegationPresentation.tssrc/renderer/src/lib/workflowLaunchApproval.tssrc/renderer/src/pages/NewThreadPage.vuesrc/shared/lib/deepchatSubagents.tssrc/shared/workflow/savedWorkflow.tssrc/shared/workflow/serviceContracts.tstest/main/agent/invocationAdmission.test.tstest/main/agent/shared/appSessionService.test.tstest/main/memory/memoryNativeMigration.test.tstest/main/orchestration/liveDelegationService.test.tstest/main/orchestration/orchestrationRoutes.test.tstest/main/session/assignment.test.tstest/main/session/deletionGate.test.tstest/main/tool/agentTools/workflowTool.test.tstest/main/workflow/workflowLaunchScope.test.tstest/main/workflow/workflowServiceContracts.test.tstest/main/workflow/workflowSourceValidator.test.tstest/main/workflow/workflowStructuredOutputRegistry.test.tstest/renderer/components/NewThreadPage.test.tstest/renderer/components/WorkspacePanel.test.tstest/renderer/lib/liveDelegationPresentation.test.ts
🚧 Files skipped from review as they are similar to previous changes (28)
- test/main/agent/shared/appSessionService.test.ts
- test/main/agent/invocationAdmission.test.ts
- test/renderer/components/WorkspacePanel.test.ts
- test/main/orchestration/orchestrationRoutes.test.ts
- src/main/orchestration/routes.ts
- src/shared/lib/deepchatSubagents.ts
- test/main/memory/memoryNativeMigration.test.ts
- src/main/session/assignment.ts
- test/renderer/components/NewThreadPage.test.ts
- src/renderer/src/lib/workflowLaunchApproval.ts
- test/main/tool/agentTools/workflowTool.test.ts
- src/shared/workflow/savedWorkflow.ts
- test/main/workflow/workflowLaunchScope.test.ts
- test/main/workflow/workflowStructuredOutputRegistry.test.ts
- src/main/workflow/launchScope.ts
- src/main/agent/shared/appSessionService.ts
- src/main/workflow/runtime/workflowSourceValidator.ts
- src/main/tool/agentTools/workflowTool.ts
- docs/features/workflow-runtime/plan.md
- src/shared/workflow/serviceContracts.ts
- src/renderer/src/pages/NewThreadPage.vue
- test/main/workflow/workflowSourceValidator.test.ts
- src/main/workflow/structuredOutput/registry.ts
- test/main/orchestration/liveDelegationService.test.ts
- src/main/agent/invocationAdmission.ts
- src/main/app/composition.ts
- src/renderer/src/components/chat/ChatStatusBar.vue
- docs/features/workflow-runtime/spec.md
Summary
Add proactive multi-Agent collaboration to DeepChat while keeping orchestration policy, reasoning effort, and execution strategy independent.
This PR introduces:
explicit | proactivecollaboration policy;The parent Agent can now choose the appropriate execution path per task:
Product behavior
explicit: delegation requires an explicit user, project, or Skill instruction.proactive: the parent may delegate when parallel or isolated work materially improves quality or latency.Before
After
Live delegation
Durable Workflow runtime
agent()parallel()pipeline()mapLimit()phase()log()Reliability and safety
unknownand fail closed during automatic recovery.dev.UX
Summary by CodeRabbit