feat(agents): add provider-neutral orchestration - #5632
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR adds native agent profiles, rules, prompt compilation, durable orchestration, provider compatibility checks, MCP and WebSocket APIs, web and mobile settings, chat selection, thread persistence, and supporting documentation. ChangesNative agent platform
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant WebSocket
participant AgentCatalog
participant AgentOrchestration
participant Provider
Client->>WebSocket: Select profile or invoke agent operation
WebSocket->>AgentCatalog: List or load profile and rule documents
WebSocket->>AgentOrchestration: Spawn, inspect, message, cancel, or integrate run
AgentOrchestration->>Provider: Resolve prompt and start provider turn
Provider-->>AgentOrchestration: Runtime events and usage
AgentOrchestration-->>WebSocket: Run state or result
WebSocket-->>Client: Catalog, mutation, or run response
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 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.
Reviewed the new Effect services under apps/server/src/agents/**, their MCP/WS call sites, and the layer wiring. Findings are limited to Effect service conventions in the new code: standalone *Shape interfaces instead of inline Context.Service interfaces, error classes whose only payload is a free-form detail (with the underlying failure discarded rather than kept as cause), and a duplicated agent-services layer in the WebSocket route.
Posted via Macroscope — Effect Service Conventions
86d41ba to
091b57d
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (14)
apps/server/src/orchestration/Layers/ProjectionPipeline.ts-813-816 (1)
813-816: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUpdate the thread projection timestamp.
Line 815 changes
agentProfilebut leavesupdatedAtunchanged. Clients can miss this state transition when they order or reconcile threads byupdatedAt, especially if no later session event is emitted. SetupdatedAt: event.occurredAtin this upsert.🤖 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 `@apps/server/src/orchestration/Layers/ProjectionPipeline.ts` around lines 813 - 816, Update the projectionThreadRepository.upsert call in the projection event handler to set updatedAt to event.occurredAt alongside agentProfile, preserving the existing row fields and ensuring the thread timestamp reflects this state transition.docs/internals/glossary.md-95-97 (1)
95-97: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse standard adverb placement.
Change “applies always” to “always applies.”
🤖 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 `@docs/internals/glossary.md` around lines 95 - 97, Update the glossary’s Rule definition to change the wording from “applies always” to “always applies,” preserving the rest of the definition and its reference unchanged.Source: Linters/SAST tools
apps/web/src/components/settings/AgentsSettings.logic.ts-128-132 (1)
128-132: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReject empty and blank input in
parseInteger.
Number("")andNumber(" ")return0, andNumber.isInteger(0)is true. If the user clears "Maximum runs" or "Maximum concurrency", the document is built with0. The schema then rejects the document with a decode error instead of the field-specific message.🛡️ Proposed fix
function parseInteger(value: string, label: string): number { + if (value.trim().length === 0) throw new Error(`${label} is required.`); const parsed = Number(value); if (!Number.isInteger(parsed)) throw new Error(`${label} must be a whole number.`); return parsed; }🤖 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 `@apps/web/src/components/settings/AgentsSettings.logic.ts` around lines 128 - 132, Update parseInteger to explicitly reject empty or whitespace-only value before converting it with Number, so cleared “Maximum runs” and “Maximum concurrency” fields produce the existing label-specific whole-number error instead of being interpreted as zero.apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx-718-730 (1)
718-730: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winReflect the off state in the "Always apply" toggle.
The toggle keeps
bg-primaryfor both states, so the off state looks active. The profile toggle at lines 597-609 switches the background. Also add anaccessibilityLabel, because the row label is a sibling element.🎨 Proposed fix
<Pressable accessibilityRole="switch" + accessibilityLabel="Always apply" accessibilityState={{ checked: props.draft.alwaysApply }} onPress={() => props.onChange("alwaysApply", !props.draft.alwaysApply)} - className="rounded-full bg-primary px-3 py-1" + className={`rounded-full px-3 py-1 ${props.draft.alwaysApply ? "bg-primary" : "bg-subtle-strong"}`} > - <Text className="text-sm font-t3-bold text-primary-foreground"> + <Text + className={`text-sm font-t3-bold ${props.draft.alwaysApply ? "text-primary-foreground" : "text-foreground-muted"}`} + > {props.draft.alwaysApply ? "On" : "Off"} </Text> </Pressable>🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines 718 - 730, Update the Always apply toggle in the surrounding settings component to use the inactive background styling when props.draft.alwaysApply is false, matching the profile toggle’s state-dependent styling. Add an accessibilityLabel to the Pressable so the control is identified independently of its sibling text label.apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx-457-476 (1)
457-476: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd loading and error states to the rules list.
The profile list handles
catalog.isPendingandcatalog.error. The rules list does not. During the first load the user sees "No rules yet." If the catalog request fails, the rules section shows the same empty message and hides the failure.🩹 Proposed fix
<View className="overflow-hidden rounded-2xl bg-subtle"> - {rules.length === 0 ? ( + {catalog.isPending && catalog.data === null ? ( + <Text className="p-4 text-sm text-foreground-muted">Loading rules…</Text> + ) : null} + {catalog.error ? ( + <Text accessibilityRole="alert" className="p-4 text-sm text-danger"> + {catalog.error} + </Text> + ) : null} + {!catalog.isPending && !catalog.error && rules.length === 0 ? ( <Text className="p-4 text-sm text-foreground-muted"> No rules yet. Create one to apply reusable instructions by path. </Text> ) : null}🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines 457 - 476, Update the rules list rendering around rules.length and rules.map to handle the catalog loading and error states consistently with the profile list: show a loading state while catalog.isPending, show the catalog error when catalog.error is present, and only show “No rules yet” after a successful load with no rules. Preserve the existing RuleRow rendering for loaded rules.packages/contracts/src/agents.ts-445-452 (1)
445-452: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winRule failures report profile error messages.
The rule error aliases point at
AgentProfileError. A missing or conflicting rule therefore producesAgentProfileNotFoundErrorwith the text "Agent profile '/' was not found." That text reaches the Rules settings UI and MCP clients and names the wrong entity. Add rule-specificAgentRuleNotFoundErrorandAgentRuleRevisionConflictErrorvariants, or parameterize the message with the document kind.🤖 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 `@packages/contracts/src/agents.ts` around lines 445 - 452, Update the rule error definitions near AgentRuleGetError, AgentRuleSaveError, AgentRuleArchiveError, and AgentRuleRestoreError so rule failures no longer alias AgentProfileError. Add rule-specific not-found and revision-conflict variants, or parameterize the shared error with the rule document kind, ensuring exposed messages identify a rule rather than an agent profile.packages/client-runtime/src/state/agents.ts-37-72 (1)
37-72: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winAdd command-level caching after agent mutations.
The web and mobile settings screens call
catalog.refresh()inline, so this library does not prevent the same mutation from showing stale catalog/profile/rule data from anotheratomQuery. Addregistry.refresh(...)toonSuccessin these commands so consumers do not have to duplicate the same invalidation.🤖 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 `@packages/client-runtime/src/state/agents.ts` around lines 37 - 72, Update the agent mutation commands in the command registry to invalidate related catalog, profile, and rule queries through registry.refresh(...) in each command’s onSuccess handler. Apply this to saveProfile, archiveProfile, restoreProfile, saveRule, archiveRule, and restoreRule, using the existing query identifiers and preserving their current scheduler and concurrency configuration.apps/server/src/agents/prompt/RuleMatcher.ts-116-116 (1)
116-116: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
escapeRegexdoes not escape*, so wildcards inside{...}become regex quantifiers.Line 153 escapes each alternation branch with
escapeRegex. The escape set at line 116 omits*. A glob such as{*.ts,*.tsx}therefore compiles to(?:\*?\.ts|...)-style output where*acts as a quantifier on the preceding character instead of matching a path segment. The rule then matches the wrong files, silently and with no diagnostic.The single-character path at line 156 is not affected, because
*and?are handled by earlier branches. Only alternation contents reachescapeRegexas multi-character strings.Either reject
*and?inside alternations with a thrownError, which surfaces as aninvalid-globdiagnostic, or compile each alternative through the same character loop.This also relates to the static analysis hint on line 158. Glob text reaches
new RegExpafter this incomplete escaping. Rule globs are author-supplied and the candidate list is bounded to the context files, so the practical backtracking risk is low, but the escaping gap should still be closed.Also applies to: 146-154
🤖 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 `@apps/server/src/agents/prompt/RuleMatcher.ts` at line 116, The escapeRegex function must escape `*` so wildcard characters inside alternation branches cannot become regex quantifiers. Update its character class while preserving the existing alternation compilation flow in the matcher around the branch handling that calls `escapeRegex`.Source: Linters/SAST tools
apps/server/src/agents/prompt/RuleMatcher.ts-238-259 (1)
238-259: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
contentBytesdoes not measurecontent.The loop accumulates only
rule.bodybytes. The emittedcontentalso contains a<!-- t3-agent-rule: scope/id -->\nheader for each non-empty rule and a\n\njoiner between chunks. The returnedcontentBytesis therefore always lower than the byte length ofcontent, and the 64 KiB cap does not bound what the prompt actually carries.The loop also charges bytes for rules whose body is empty, which are then skipped at line 252 and contribute nothing to
content.Measure the chunk that is appended.
🐛 Proposed fix
for (const rule of matched.rules) { - const bodyBytes = textEncoder.encode(rule.body).byteLength; - const nextBytes = contentBytes + bodyBytes; + if (rule.body.length === 0) continue; + const chunk = `<!-- t3-agent-rule: ${rule.scope}/${rule.id} -->\n${rule.body}`; + const separatorBytes = chunks.length === 0 ? 0 : 2; + const nextBytes = contentBytes + separatorBytes + textEncoder.encode(chunk).byteLength; if (nextBytes > maxBytes) { throw new AgentRuleContentOverflowError({ limitBytes: maxBytes, actualBytes: nextBytes, ruleId: rule.id, scope: rule.scope, }); } contentBytes = nextBytes; - if (rule.body.length > 0) { - chunks.push(`<!-- t3-agent-rule: ${rule.scope}/${rule.id} -->\n${rule.body}`); - } + chunks.push(chunk); }Note that the test at
apps/server/src/agents/prompt/prompt.test.tsline 94 usesmaxBytes = 4with a 5-byte body, so it still overflows after this change.🤖 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 `@apps/server/src/agents/prompt/RuleMatcher.ts` around lines 238 - 259, Update the content assembly loop around matched.rules so byte accounting measures each emitted chunk, including its rule header and newline, and skips empty bodies before charging bytes. Use the encoded byte length of the exact chunk appended to chunks, accumulate that value for contentBytes, and retain the overflow check against maxBytes so the existing 5-byte body test still throws.apps/server/src/ws.ts-1111-1127 (1)
1111-1127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReturn catalog diagnostics in
agentsCatalog.
agentCatalog.list()is a success-only effect that collects malformed profile and rule entries inAgentCatalogSnapshot.diagnostics.agentsCatalogcurrently returns onlyprofilesandrules, so a user cannot see why a catalog entry is missing.🤖 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 `@apps/server/src/ws.ts` around lines 1111 - 1127, Update the WS_METHODS.agentsCatalog handler to include catalog.diagnostics in its returned object alongside the filtered profiles and rules, preserving the existing includeArchived filtering behavior.apps/server/src/agents/run/AgentRunReactor.ts-47-53 (1)
47-53: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winLog when the terminal hook is skipped.
If
getProfileSnapshotreturnsNone, or the workspace root cannot be resolved, Line 52 returns without any signal. The configuredafterResultandonErrorhooks then never run, and the operator sees nothing.putProfileSnapshotshould have persisted the snapshot at launch, so a missing snapshot indicates a real defect upstream. Emit a warning withrun.idandrun.profile.revision.🤖 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 `@apps/server/src/agents/run/AgentRunReactor.ts` around lines 47 - 53, Update the early-return guard in the hook execution flow to emit a warning when profile is null or workspaceRoot is null, including run.id and run.profile.revision in the log; preserve the existing return behavior and do not alter hook execution for valid values.apps/server/src/agents/run/AgentRunDeadlineReactor.ts-26-33 (1)
26-33: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winGuard against an unparsable timestamp, and correct the comment.
Two problems exist in this segment.
Date.parsereturnsNaNfor a malformed timestamp.AgentRun.requestedAtandAgentRun.startedAtare plainstringfields, so a corrupt persisted value producesNaN.isDeadlineExpiredthen always returnsfalse, andschedulecomputesMath.max(0, NaN - nowMillis), which isNaN, and passes it toDuration.millis. The run then never reaches its wall-time budget.The comment states the budget starts when a run is requested. The code prefers
run.startedAt, so a run that stays queued receives a later deadline. Align the comment with the code.🐛 Proposed guard
-/** The wall-time budget starts when a run is requested until it finishes. */ -export const deadlineAtMillis = (run: AgentRun): number => { - const origin = Date.parse(run.startedAt ?? run.requestedAt); - return origin + run.budget.maxWallTimeMinutes * 60_000; -}; +/** + * The wall-time budget starts when a run starts, and falls back to the + * request time while the run is still queued. + */ +export const deadlineAtMillis = (run: AgentRun): number => { + const parsed = Date.parse(run.startedAt ?? run.requestedAt); + const origin = Number.isNaN(parsed) ? Date.parse(run.requestedAt) : parsed; + return (Number.isNaN(origin) ? 0 : origin) + run.budget.maxWallTimeMinutes * 60_000; +};🤖 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 `@apps/server/src/agents/run/AgentRunDeadlineReactor.ts` around lines 26 - 33, Update deadlineAtMillis to handle an unparsable run.startedAt or run.requestedAt without returning NaN, preserving a valid deadline calculation for deadline expiration and scheduling; use the existing AgentRun timestamp context and choose an appropriate safe fallback for invalid persisted timestamps. Correct the comment above deadlineAtMillis to state that the wall-time budget starts from the timestamp selected by the implementation, including the startedAt preference.apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts-4-16 (1)
4-16: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAdd a migration test for
agent_profile_json.
ProjectionThreadRepositoryreads and writesagent_profile_jsonthroughagentProfileinupsert,getById, andlistByProjectId, but040_ProjectionThreadsAgentProfilehas no test cover. Add a migration test that runs up to migration 040 and asserts the column exists, following the existing migration test pattern.🤖 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 `@apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts` around lines 4 - 16, Add a migration test following the existing migration test pattern that runs migrations through 040_ProjectionThreadsAgentProfile and verifies projection_threads contains the agent_profile_json column. Cover the schema assertion only; do not alter ProjectionThreadRepository or migration behavior.apps/server/src/orchestration/Layers/ProviderCommandReactor.ts-774-798 (1)
774-798: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winMove
getCapabilitiesinto the profile branch.
resolvedPrompt.profile !== nullis the only consumer ofrequestedCapabilities, so fetching it on every turn start adds an unneeded provider lookup. Calling it beforeensureSessionForThreadalso runs capabilities lookup after unknown-instance failures, which can return provider capabilities errors instead of the descriptive unknown-instance errors fromgetInstanceInfo.♻️ Proposed refactor
- const requestedModelSelection = - input.modelSelection ?? threadModelSelections.get(input.threadId) ?? thread.modelSelection; - const requestedCapabilities = yield* providerService.getCapabilities( - requestedModelSelection.instanceId, - ); - if (resolvedPrompt.profile !== null) { - const profile = resolvedPrompt.profile; + const requestedModelSelection = + input.modelSelection ?? threadModelSelections.get(input.threadId) ?? thread.modelSelection; + if (resolvedPrompt.profile !== null) { + const profile = resolvedPrompt.profile; + const requestedCapabilities = yield* providerService.getCapabilities( + requestedModelSelection.instanceId, + );🤖 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 `@apps/server/src/orchestration/Layers/ProviderCommandReactor.ts` around lines 774 - 798, Move the providerService.getCapabilities call into the resolvedPrompt.profile !== null branch, immediately before resolveAgentRuntimeCompatibility, and keep requestedModelSelection available there. Avoid fetching capabilities when no profile is present, while preserving ensureSessionForThread/getInstanceInfo validation ordering so unknown instances produce their existing descriptive errors first.
🤖 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 `@apps/mobile/src/features/settings/agentProfile.logic.ts`:
- Around line 51-56: Extract a shared numeric parsing helper that rejects blank
or whitespace-only input before conversion, then apply range validation at each
caller. In apps/mobile/src/features/settings/agentProfile.logic.ts lines 51-56,
update integer so maxRuns and maxWallTimeMinutes reject blank values; in
apps/mobile/src/features/settings/agentRule.logic.ts lines 44-50, make
parseInteger reuse the shared helper instead of duplicating conversion and
integer checks. In apps/mobile/src/features/settings/agentProfile.logic.test.ts
lines 11-29, add negative cases asserting blank budget input and blank priority
cause buildAgentProfileDocument and buildAgentRuleDocument to throw.
- Around line 40-43: Update the runtimeMode default in draftFromProfile so a new
profile uses the least-permissive intended default, “auto,” instead of
“full-access”; leave the other profile defaults unchanged and preserve explicit
existing runtimeMode values.
In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 59-65: Update the mapping logic around the scope and id extraction
to split each colon-delimited target only at its first colon, preserving all
remaining text in id. Keep the environment default for targets without a colon,
then validate the preserved remainder with the existing scope and id checks so
malformed multi-colon targets are rejected rather than truncated.
In `@apps/mobile/src/state/use-thread-composer-state.ts`:
- Around line 110-113: Update the draft persistence and empty-draft handling
around updateComposerDraftSettings and isEmptyDraft so an explicit agentProfile:
null is preserved rather than causing the draft to be deleted. Ensure clearing a
thread-locked profile remains effective for the next turn while retaining
existing removal behavior for genuinely empty drafts.
In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 702-715: Update validate to reuse the single discovered source
collection returned by list/discovery instead of calling find, getProfile, or
getRule for each entry. Extract the post-find loading logic into
loadProfile(source) and loadRule(source), have getProfile and getRule call these
helpers after find, and have validate resolve each entry’s source from the
already discovered collection before invoking the corresponding loader.
In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 979-982: Update the follow-up turn construction in send to load
the pinned profile snapshot via runs.getProfileSnapshot(run.profile.revision),
then derive runtimeMode and interactionMode from that profile using the same
logic as spawn rather than hardcoding "full-access" and "default". Preserve the
profile’s approval requirements for every subsequent turn.
In `@apps/server/src/agents/AgentPromptResolver.ts`:
- Around line 24-45: Reject URI schemes in both normalizeCandidate in
apps/server/src/agents/AgentPromptResolver.ts lines 24-45 and
normalizeWorkspaceRelativePath in apps/server/src/agents/prompt/RuleMatcher.ts
lines 74-98 by adding the same scheme-prefix validation after the drive-letter
check; preserve existing rejection behavior and ensure values such as
https://example.com/x are rejected rather than normalized as relative paths.
Consider sharing the validator to prevent the rules from diverging.
In `@apps/server/src/agents/prompt/PromptCompiler.ts`:
- Around line 180-188: Update compileAgentPrompt and the compileAgentRules
integration to handle AgentRuleContentOverflowError without letting it escape as
a defect. Use isAgentRuleContentOverflowError to convert the overflow into the
established AgentPromptDiagnostic/result path, preserving the declared
AgentPromptCompilation and AgentPromptResolutionError flow for user-facing
handling.
In `@apps/server/src/agents/run/AgentRunDeadlineReactor.ts`:
- Around line 56-67: In apps/server/src/agents/run/AgentRunDeadlineReactor.ts
lines 56-67, update the cancellation handling in the deadline reactor to
distinguish Result failure from an empty successful event list: log the failure
details before returning false, while preserving the existing no-op behavior for
empty events. In apps/server/src/agents/run/AgentRunReactor.ts lines 47-53, add
a warning before the early return that includes run.id, run.profile.revision,
and the specific missing value.
In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 162-175: Update the event handling flow around handle and
runTerminalHook so terminal hooks execute on forked fibers or through a bounded
per-thread concurrent consumer instead of blocking Stream.runForEach’s single
consumer fiber. Preserve sequential event processing for each thread while
allowing hooks from different runs or threads to proceed independently, and
retain the existing error logging context.
- Around line 87-100: The budget-exhaustion branch in AgentRunReactor must stop
matching the human-readable detail text. Add a structured discriminator such as
reason to AgentRunCommandInvariantError, set it for budget-exhaustion failures,
and update the completion check to compare that discriminator while preserving
the existing dispatch behavior.
In `@apps/server/src/mcp/McpSessionRegistry.ts`:
- Line 131: Update McpSessionRegistry.issue() so the "agents" capability is
granted only when the thread’s attached target profile declares the
corresponding MCP tools, rather than unconditionally for every credential;
preserve "preview" access and ensure delegation uses the profile-specific
capability set.
In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts`:
- Around line 303-308: Update the thread.turn.start test setup around
resolveAgentPrompt and the dispatch at line 577 to provide a valid agentProfile
with a known profileRef, then assert the resolveAgentPrompt mock receives that
matching profileRef alongside the message. Ensure the test specifically
exercises the pinned-profile path rather than allowing a null profile reference.
- Around line 2917-2926: Replace the local waitFor polling block in the test
with yield* Effect.promise(() => harness.drain()) after the relevant dispatches.
Use harness.drain to wait for both reactor workers before reading the model,
while preserving the subsequent failure-activity assertions.
In `@apps/server/src/persistence/Migrations/039_AgentRuns.ts`:
- Around line 69-72: Update the migration near the existing
idx_projection_agent_runs_lineage definition to add a dedicated index on
projection_agent_runs(root_run_id), using an IF NOT EXISTS guard and a clear
root-run index name. Leave the existing parent_run_id, status, and created_at
index unchanged.
In `@apps/server/src/ws.ts`:
- Around line 2384-2391: Update websocketRpcRouteLayer and the server
route-layer wiring to construct one shared AgentProfileServices.layer per server
instance and provide that same layer to both HTTP routes and WebSocket RPC,
rather than extracting and rebuilding AgentCatalog, AgentProfileStore, and
AgentRuleStore inside websocketRpcRouteLayer. Ensure AgentRuleStore and
AgentProfileStore retain shared mutex and revision state across both transports.
In `@apps/web/src/components/settings/AgentsSettings.tsx`:
- Around line 506-518: Update the scope select in the AgentsSettings profile
form to use the existing-profile rule from RulesSettings: disable it when the
profile is not new by applying the equivalent isNew-based disabled condition,
while preserving the current value and change handling.
In `@apps/web/src/components/settings/RulesSettings.tsx`:
- Around line 149-158: Update the catch block in the rule restore flow to handle
caught values with the same instanceof Error check used near line 132, passing
plain Error instances directly to setError and using failureMessage only for
Cause values. Remove the unsafe Cause.Cause cast while preserving the existing
error-state behavior.
- Around line 108-113: Update save in RulesSettings so buildAgentRuleDocument
receives no baseline when creating a new rule: use the selectedKey/new-rule
state to pass null for new rules, matching the isNew guard in AgentsSettings,
and retain ruleQuery.data?.rule only when editing an existing selected rule.
---
Minor comments:
In `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 718-730: Update the Always apply toggle in the surrounding
settings component to use the inactive background styling when
props.draft.alwaysApply is false, matching the profile toggle’s state-dependent
styling. Add an accessibilityLabel to the Pressable so the control is identified
independently of its sibling text label.
- Around line 457-476: Update the rules list rendering around rules.length and
rules.map to handle the catalog loading and error states consistently with the
profile list: show a loading state while catalog.isPending, show the catalog
error when catalog.error is present, and only show “No rules yet” after a
successful load with no rules. Preserve the existing RuleRow rendering for
loaded rules.
In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Line 116: The escapeRegex function must escape `*` so wildcard characters
inside alternation branches cannot become regex quantifiers. Update its
character class while preserving the existing alternation compilation flow in
the matcher around the branch handling that calls `escapeRegex`.
- Around line 238-259: Update the content assembly loop around matched.rules so
byte accounting measures each emitted chunk, including its rule header and
newline, and skips empty bodies before charging bytes. Use the encoded byte
length of the exact chunk appended to chunks, accumulate that value for
contentBytes, and retain the overflow check against maxBytes so the existing
5-byte body test still throws.
In `@apps/server/src/agents/run/AgentRunDeadlineReactor.ts`:
- Around line 26-33: Update deadlineAtMillis to handle an unparsable
run.startedAt or run.requestedAt without returning NaN, preserving a valid
deadline calculation for deadline expiration and scheduling; use the existing
AgentRun timestamp context and choose an appropriate safe fallback for invalid
persisted timestamps. Correct the comment above deadlineAtMillis to state that
the wall-time budget starts from the timestamp selected by the implementation,
including the startedAt preference.
In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 47-53: Update the early-return guard in the hook execution flow to
emit a warning when profile is null or workspaceRoot is null, including run.id
and run.profile.revision in the log; preserve the existing return behavior and
do not alter hook execution for valid values.
In `@apps/server/src/orchestration/Layers/ProjectionPipeline.ts`:
- Around line 813-816: Update the projectionThreadRepository.upsert call in the
projection event handler to set updatedAt to event.occurredAt alongside
agentProfile, preserving the existing row fields and ensuring the thread
timestamp reflects this state transition.
In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.ts`:
- Around line 774-798: Move the providerService.getCapabilities call into the
resolvedPrompt.profile !== null branch, immediately before
resolveAgentRuntimeCompatibility, and keep requestedModelSelection available
there. Avoid fetching capabilities when no profile is present, while preserving
ensureSessionForThread/getInstanceInfo validation ordering so unknown instances
produce their existing descriptive errors first.
In `@apps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.ts`:
- Around line 4-16: Add a migration test following the existing migration test
pattern that runs migrations through 040_ProjectionThreadsAgentProfile and
verifies projection_threads contains the agent_profile_json column. Cover the
schema assertion only; do not alter ProjectionThreadRepository or migration
behavior.
In `@apps/server/src/ws.ts`:
- Around line 1111-1127: Update the WS_METHODS.agentsCatalog handler to include
catalog.diagnostics in its returned object alongside the filtered profiles and
rules, preserving the existing includeArchived filtering behavior.
In `@apps/web/src/components/settings/AgentsSettings.logic.ts`:
- Around line 128-132: Update parseInteger to explicitly reject empty or
whitespace-only value before converting it with Number, so cleared “Maximum
runs” and “Maximum concurrency” fields produce the existing label-specific
whole-number error instead of being interpreted as zero.
In `@docs/internals/glossary.md`:
- Around line 95-97: Update the glossary’s Rule definition to change the wording
from “applies always” to “always applies,” preserving the rest of the definition
and its reference unchanged.
In `@packages/client-runtime/src/state/agents.ts`:
- Around line 37-72: Update the agent mutation commands in the command registry
to invalidate related catalog, profile, and rule queries through
registry.refresh(...) in each command’s onSuccess handler. Apply this to
saveProfile, archiveProfile, restoreProfile, saveRule, archiveRule, and
restoreRule, using the existing query identifiers and preserving their current
scheduler and concurrency configuration.
In `@packages/contracts/src/agents.ts`:
- Around line 445-452: Update the rule error definitions near AgentRuleGetError,
AgentRuleSaveError, AgentRuleArchiveError, and AgentRuleRestoreError so rule
failures no longer alias AgentProfileError. Add rule-specific not-found and
revision-conflict variants, or parameterize the shared error with the rule
document kind, ensuring exposed messages identify a rule rather than an agent
profile.
---
Nitpick comments:
In `@apps/mobile/src/state/thread-outbox.test.ts`:
- Around line 131-149: Extend the test around resolveQueuedThreadSettings to
cover both remaining agentProfile branches: assert that an explicit
agentProfile: null clears the thread profile, and assert that omitting the
agentProfile property preserves the existing thread profile. Keep the current
queued-profile round-trip assertion unchanged so all three hasOwnProperty-based
behaviors are verified.
In `@apps/mobile/src/state/use-thread-composer-state.test.ts`:
- Around line 1-4: Rename the test file from use-thread-composer-state.test.ts
to agentProfileSelection.test.ts so its filename matches the
resolveAgentProfileSelection module under test.
In `@apps/server/src/agents/AgentCatalog.test.ts`:
- Around line 33-298: Add a focused test in the AgentCatalog suite that calls
the public validate method with one valid profile and one Markdown document
lacking frontmatter, then assert validation aggregates the expected
missing-frontmatter diagnostic alongside the valid document results. Exercise
both discovery/list diagnostics and per-document loading through validate,
without using arbitrary timeouts.
In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 193-200: Introduce a scope-neutral AgentLocator (or
AgentRuleLocator) alias in packages/contracts and use it for rule references,
including getRule and RuleFrontmatter.profiles where applicable, while retaining
AgentProfileLocator for profile references. Rename decodeProfileLocator at the
rule-decoding call sites around lines 493 and 825 to reflect that it decodes
rule identifiers, and update all related imports and usages.
In `@apps/server/src/agents/AgentHookRunner.ts`:
- Around line 113-127: Update the POSIX invocation in the shell-hook runner to
use /bin/sh with only the -c argument, removing the login-shell behavior while
preserving the existing command, working directory, timeout, and output
handling.
- Around line 15-45: Extract the identical field definitions from
AgentHookBlockedError and AgentHookExecutionError into a shared schema field-set
constant, then reuse it when declaring both TaggedErrorClass schemas. Keep both
error tags and their existing message getters distinct and unchanged.
In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 699-709: In the isolated-worktree branch of the prepareThread
setup, capture the already-validated context.thread.branch in a local constant
immediately after its guard, then use that constant for refName and baseRefName
instead of non-null assertions. Preserve the existing branch validation and
worktree creation behavior.
In `@apps/server/src/agents/AgentProfileStore.test.ts`:
- Around line 172-173: Update the test around projectFile in AgentProfileStore
tests to parse t3.json as JSON and inspect its decoded agents array. Assert that
exactly one agent has id "project-reviewer", replacing the raw project-reviewer
substring count while preserving the existing file-read flow.
- Around line 93-104: The compare-and-swap failure paths are untested. In
apps/server/src/agents/AgentProfileStore.test.ts:93-104, add a second save using
the already-consumed saved.revision and assert
AgentProfileStoreRevisionConflictError; in
apps/server/src/agents/AgentRuleStore.test.ts:62-75, add the equivalent
stale-revision save and assert the rule store’s typed revision-conflict error.
Also add, in either test file, coverage for supplying expectedRevision when the
profile or rule does not exist, asserting the appropriate failure.
In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 388-397: Resolve the `documentPath` behavior for new environment
profiles in the surrounding profile creation flow: either honor
`input.profile.sourcePath` consistently, or validate and reject conflicting
values while retaining `defaultPath`. If always using the environment default is
intentional, add a concise comment documenting that rule; preserve
existing-project behavior.
In `@apps/server/src/agents/AgentPromptResolver.test.ts`:
- Around line 87-89: Strengthen the test assertion in the resolver test so it
verifies the compiled prelude appears before the user-supplied marker, using the
ordering of “## T3 runtime” and malicious in resolved.message. Keep the existing
assertions unchanged; do not address duplicate markers here, as that belongs to
PromptCompiler.
In `@apps/server/src/agents/AgentPromptResolver.ts`:
- Around line 183-199: Update the Effect.forEach call that loads snapshot.rules
in resolve to use bounded concurrency greater than one, preserving the existing
resolutionError mapping and result ordering. Do not alter rule lookup behavior;
only configure concurrency to avoid sequential catalog.getRule calls.
- Around line 156-166: Extract the child-run turn command ID construction into a
shared helper, then reuse it in AgentOrchestrationLive when sending
thread.turn.start and in AgentPromptResolver.isCompiledAgentTurn when comparing
commandId. Ensure the helper consistently formats the agent-spawn prefix and run
ID.
In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 137-138: Update the assertion in the t3.json test to decode
projectFile as JSON, inspect its rules array, and assert exactly one entry whose
id is "project-typescript"; remove the substring-count assertion so the test
validates structured data rather than JSON text layout.
In `@apps/server/src/agents/AgentRuleStore.ts`:
- Around line 365-372: Update the documentPath selection in the relevant
AgentRuleStore function to use Result.isSuccess(current), matching the existing
check earlier in the same function, while preserving the current success and
fallback sourcePath behavior.
In `@apps/server/src/agents/prompt/prompt.test.ts`:
- Around line 72-88: The matching tests around matchAgentRules need coverage for
advanced and malformed glob patterns. Add focused cases for alternation and
character-class patterns, including src/**/*.{ts,tsx} matching src/a.tsx and
{*.ts,*.tsx} matching the appropriate files, plus an unclosed src/[ts pattern
that yields an invalid-glob diagnostic and never matches.
In `@apps/server/src/agents/prompt/PromptCompiler.ts`:
- Around line 83-84: Remove the redundant compatibility aliases from
PromptCompiler and RuleMatcher: delete portablePromptEnvelope, compilePrompt,
matchRules, compileRules, and RuleContentOverflowError exports or properties,
retaining only the canonical names and updating index re-exports and internal
references accordingly.
- Around line 144-148: Document in renderPortablePrompt that envelope.task is
intentionally preserved verbatim and may contain duplicate prompt markers or
section headings, so future consumers must not treat them as trust signals;
update the existing AgentPromptResolver test expectation only if needed to
preserve this documented behavior.
In `@apps/server/src/agents/run/AgentRun.test.ts`:
- Around line 165-292: Update the test title for “enforces inherited depth,
run-count, concurrency, and token budgets” to also identify the estimated-cost
budget assertion, so all five covered budget dimensions are visible when the
test fails.
- Around line 294-405: Add tests in the AgentRun transition/decision coverage
for both missing branches: advance the command occurredAt beyond the configured
maxWallTimeMinutes and assert the relevant succeed or follow-up decision is
rejected, then request a child run with maxRuns or maxTotalTokens greater than
its parent and assert budgetDoesNotExpand rejects it. Reuse the existing
request, start, transition, decide, and fixture helpers and preserve current
assertions.
In `@apps/server/src/agents/run/AgentRun.ts`:
- Around line 365-475: Update the second event switch in evolve to add a default
branch that passes the event to a never assertion, making any newly added
AgentRunEvent variant a compile-time error while preserving all existing cases.
In `@apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts`:
- Around line 90-165: Add focused coverage for the make scheduling flow: use a
fake change stream and TestClock to verify layer startup recovers an active run
from listActive and schedules expiration, then emits a completed-status change
and verifies cancelScheduled removes the timer. Exercise the behavior
deterministically without arbitrary timeouts, anchoring the test around make,
listActive recovery, change-event handling, and TestClock.
- Around line 67-85: Replace the trailing `as AgentRunRepository["Service"]`
cast in `repositoryFor` with a `satisfies AgentRunRepository["Service"]` check
on the object literal, preserving the existing inferred object type while
ensuring missing or incompatible service members fail typechecking.
In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 22-26: Import the exported AgentRun type from ./AgentRun.ts and
use it directly for the run parameter in both hookWorkspace and the other helper
around lines 41–45. Remove the duplicated
NonNullable<Effect.Success<ReturnType<typeof repository.get>>…> type derivation
while preserving the existing helper behavior.
In `@apps/server/src/agents/run/AgentRunRepository.test.ts`:
- Around line 66-206: Extend the AgentRunRepository test suite with focused
coverage for putProfileSnapshot and getProfileSnapshot, including the persisted
profile data used by recovery or hook execution. Add assertions for
getByChildThread and listActive, including expected child-thread lookup and
active-run filtering. Keep the tests isolated within
testLayer("AgentRunRepository", ...) and use the existing migration, repository,
and fixture helpers.
In `@apps/server/src/agents/run/AgentRunRepository.ts`:
- Around line 159-163: The unfiltered SQL branch in the event retrieval method
should not remain available. Make the where/filter parameter required and remove
the fallback query that selects all events, updating the method signature and
callers as needed while preserving the existing filtered queries.
- Around line 218-236: Update the AgentRunRepository projection and migration
039 so result_json is populated from the run’s result data rather than a
constant null, and consumedEstimatedCostUsd is stored alongside consumedTokens.
Ensure the INSERT column/value lists and conflict-update clause consistently
include both fields, using the existing serialization pattern for result data
and the AgentRun property for cost.
In `@apps/server/src/mcp/McpHttpServer.ts`:
- Around line 217-227: Rename ToolkitRegistrations to a preview-specific name
that reflects its use by PreviewToolkitRegistrationLive. Wrap
AgentToolkitHandlersLive and AgentToolkit with
McpToolkit.makeMcpToolkitRegistration using the agents capability, then expose
AgentToolkitRegistrationLive from that registration so it follows the same
capability-aware path as the preview toolkits.
In `@apps/server/src/orchestration/agentProfile.test.ts`:
- Around line 17-30: Update the event helper so it is generic over a specific
OrchestrationEvent member, deriving the type and payload parameters from that
member instead of accepting an arbitrary type and unknown payload. Construct the
shared envelope with the generic event type and retain only any necessary cast
for fields whose union narrowing requires it, ensuring each event call validates
its payload against the selected event type.
- Around line 61-72: Add a second turn-start test case alongside the existing
null-agentProfile case, using a non-null agentProfile in
thread.turn-start-requested and asserting that the projected thread preserves
that exact value. Keep the existing null case unchanged so both clearing and
applying event values are verified.
In `@apps/server/src/orchestration/projector.ts`:
- Around line 547-563: Update the "thread.turn-start-requested" handler so that
when payload.agentProfile is undefined it returns nextBase unchanged, avoiding
the updateThread call; retain the existing agentProfile patch through
updateThread when the value is present.
In `@apps/server/src/persistence/Migrations/039_AgentRuns.test.ts`:
- Around line 48-59: Replace the name-based index assertions in the migration
test with behavioral checks: insert duplicate agent_run_events rows sharing the
same agent_run_id and revision, and duplicate projection_agent_runs rows sharing
child_thread_id, asserting both inserts fail. Preserve any setup required by the
schema and ensure the event constraint test covers the duplicate-revision
behavior used by AgentRunRepository.dispatch.
In `@apps/server/src/persistence/Migrations/039_AgentRuns.ts`:
- Around line 74-82: Remove the redundant CREATE INDEX statements for
idx_agent_run_events_run_revision and idx_projection_agent_runs_child_thread
from the migration, relying on the existing UNIQUE constraints. Update the
corresponding assertions in the migration test to no longer expect either index
name.
In `@apps/server/src/provider/AgentRuntimeCompatibility.test.ts`:
- Around line 29-46: Add coverage in AgentRuntimeCompatibility tests for the
delegation path in resolveAgentRuntimeCompatibility, ensuring the portable
fixture’s mcpServerInjection capability does not mask it. Add assertions for
both mcp-server-injection-unsupported and token-accounting-unsupported while
preserving the existing unsupported-issue expectations.
In `@apps/server/src/ws.ts`:
- Around line 470-482: Replace the derived ref type in mapAgentCatalogError with
AgentProfileLocator, and add AgentProfileLocator to the existing
`@t3tools/contracts` import. Preserve the current error mapping behavior and ref
values.
In `@apps/web/src/components/chat/ChatComposer.tsx`:
- Around line 680-682: Update the selectedAgentProfile useState declaration in
ChatComposer to remove the explicit AgentProfileRef | null generic parameter and
rely on the lazy initializer’s inferred type, preserving the existing initial
value and state behavior.
In `@apps/web/src/components/settings/AgentsSettings.logic.test.ts`:
- Around line 24-44: Update the revision assertion in the “preserves a revision
and parses structured policy fields” test to compare document.revision with the
exact baseline revision, "a".repeat(64), instead of only validating its
hexadecimal format. Keep the existing structured-field assertions unchanged.
In `@apps/web/src/components/settings/AgentsSettings.logic.ts`:
- Around line 177-204: In the configuration-building logic, compute each
optional numeric value once before constructing the result object, then reuse it
for both the undefined check and assigned property. Update the handling around
sharedWriteConcurrency, maxTotalTokens, and maxEstimatedCostUsd while preserving
omission of undefined fields and existing parse behavior.
In `@apps/web/src/components/settings/RulesSettings.logic.ts`:
- Around line 74-89: Extract the shared archived/scope/name/id comparator into
one generic catalog sort helper, then re-export that helper under both
sortAgentRules and sortAgentProfiles. Update the existing sortAgentRules
implementation and the corresponding sortAgentProfiles implementation to use the
shared symbol while preserving their current signatures and ordering.
In `@apps/web/src/components/settings/settingsSearch.ts`:
- Around line 171-175: Add a separate search entry titled “Rules” alongside the
existing “Agents” entry in the settings search configuration, using the same
/settings/agents route so RulesSettingsPanel is discoverable when users search
for “rules”.
In `@apps/web/src/routeTree.gen.ts`:
- Around line 100-104: Remove the `as any` cast from the
`SettingsAgentsRouteImport.update` call in the generated route definition by
regenerating it with typed output. If the generator cannot avoid the cast, add a
narrowly scoped lint exception for this generated route file and document the
generated-code justification.
In `@packages/contracts/src/agents.ts`:
- Around line 351-382: Introduce or reuse a dedicated AgentRuleId brand from
agentRefs.ts, then update AgentRuleSummary.id and all rule operation
identifiers, including AgentRuleGetInput.id, AgentRuleArchiveInput.id, and the
id carried by AgentRuleDocument used in AgentRuleSaveInput, to use AgentRuleId
consistently instead of AgentProfileId or AgentSlug.
- Around line 674-746: Remove the compatibility alias export blocks for
AgentMcpStart/Get/Submit, AgentMcpAgent*, and McpAgentRun* so each codec has one
canonical exported name. Update any call sites referencing those aliases to use
the corresponding canonical
AgentMcpList/Spawn/Status/Wait/Result/Send/Cancel/Integrate symbols.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 121fbec2-7b16-4a78-9116-a9cc89a98d48
📒 Files selected for processing (121)
apps/mobile/src/Stack.tsxapps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsxapps/mobile/src/features/settings/SettingsRouteScreen.tsxapps/mobile/src/features/settings/agentProfile.logic.test.tsapps/mobile/src/features/settings/agentProfile.logic.tsapps/mobile/src/features/settings/agentRule.logic.test.tsapps/mobile/src/features/settings/agentRule.logic.tsapps/mobile/src/features/settings/components/settings-sheet-targets.tsapps/mobile/src/features/threads/NewTaskDraftScreen.tsxapps/mobile/src/features/threads/ThreadComposer.tsxapps/mobile/src/features/threads/ThreadDetailScreen.tsxapps/mobile/src/features/threads/ThreadRouteScreen.tsxapps/mobile/src/features/threads/new-task-flow-provider.tsxapps/mobile/src/features/threads/use-project-actions.tsapps/mobile/src/lib/projectThreadStartTurn.tsapps/mobile/src/state/agentProfileSelection.tsapps/mobile/src/state/agents.tsapps/mobile/src/state/thread-outbox-model.tsapps/mobile/src/state/thread-outbox.test.tsapps/mobile/src/state/use-composer-drafts.tsapps/mobile/src/state/use-thread-composer-state.test.tsapps/mobile/src/state/use-thread-composer-state.tsapps/mobile/src/state/use-thread-outbox-drain.tsapps/server/integration/OrchestrationEngineHarness.integration.tsapps/server/src/agents/AgentCatalog.test.tsapps/server/src/agents/AgentCatalog.tsapps/server/src/agents/AgentHookRunner.test.tsapps/server/src/agents/AgentHookRunner.tsapps/server/src/agents/AgentOrchestration.tsapps/server/src/agents/AgentOrchestrationLive.test.tsapps/server/src/agents/AgentOrchestrationLive.tsapps/server/src/agents/AgentProfileServices.tsapps/server/src/agents/AgentProfileStore.test.tsapps/server/src/agents/AgentProfileStore.tsapps/server/src/agents/AgentPromptResolver.test.tsapps/server/src/agents/AgentPromptResolver.tsapps/server/src/agents/AgentRuleStore.test.tsapps/server/src/agents/AgentRuleStore.tsapps/server/src/agents/prompt/PromptCompiler.tsapps/server/src/agents/prompt/RuleMatcher.tsapps/server/src/agents/prompt/index.tsapps/server/src/agents/prompt/prompt.test.tsapps/server/src/agents/run/AgentRun.test.tsapps/server/src/agents/run/AgentRun.tsapps/server/src/agents/run/AgentRunDeadlineReactor.test.tsapps/server/src/agents/run/AgentRunDeadlineReactor.tsapps/server/src/agents/run/AgentRunReactor.tsapps/server/src/agents/run/AgentRunRepository.test.tsapps/server/src/agents/run/AgentRunRepository.tsapps/server/src/auth/RpcAuthorization.tsapps/server/src/mcp/McpHttpServer.test.tsapps/server/src/mcp/McpHttpServer.tsapps/server/src/mcp/McpInvocationContext.test.tsapps/server/src/mcp/McpInvocationContext.tsapps/server/src/mcp/McpSessionRegistry.test.tsapps/server/src/mcp/McpSessionRegistry.tsapps/server/src/mcp/McpToolkit.tsapps/server/src/mcp/toolkits/agents/handlers.tsapps/server/src/mcp/toolkits/agents/tools.tsapps/server/src/orchestration/Layers/ProjectionPipeline.tsapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.test.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.tsapps/server/src/orchestration/agentProfile.test.tsapps/server/src/orchestration/decider.tsapps/server/src/orchestration/projector.tsapps/server/src/persistence/Layers/ProjectionThreads.tsapps/server/src/persistence/Migrations.tsapps/server/src/persistence/Migrations/039_AgentRuns.test.tsapps/server/src/persistence/Migrations/039_AgentRuns.tsapps/server/src/persistence/Migrations/040_ProjectionThreadsAgentProfile.tsapps/server/src/persistence/Services/ProjectionThreads.tsapps/server/src/provider/AgentRuntimeCompatibility.test.tsapps/server/src/provider/AgentRuntimeCompatibility.tsapps/server/src/provider/Layers/ClaudeAdapter.tsapps/server/src/provider/Layers/CodexAdapter.tsapps/server/src/provider/Layers/CursorAdapter.tsapps/server/src/provider/Layers/GrokAdapter.tsapps/server/src/provider/Layers/OpenCodeAdapter.tsapps/server/src/provider/Services/ProviderAdapter.tsapps/server/src/server.test.tsapps/server/src/server.tsapps/server/src/ws.tsapps/web/src/components/ChatView.logic.test.tsapps/web/src/components/ChatView.logic.tsapps/web/src/components/ChatView.tsxapps/web/src/components/chat/AgentProfilePicker.logic.tsapps/web/src/components/chat/AgentProfilePicker.test.tsapps/web/src/components/chat/AgentProfilePicker.tsxapps/web/src/components/chat/ChatComposer.tsxapps/web/src/components/settings/AgentsSettings.logic.test.tsapps/web/src/components/settings/AgentsSettings.logic.tsapps/web/src/components/settings/AgentsSettings.test.tsxapps/web/src/components/settings/AgentsSettings.tsxapps/web/src/components/settings/RulesSettings.logic.test.tsapps/web/src/components/settings/RulesSettings.logic.tsapps/web/src/components/settings/RulesSettings.test.tsxapps/web/src/components/settings/RulesSettings.tsxapps/web/src/components/settings/SettingsSidebarNav.tsxapps/web/src/components/settings/settingsSearch.tsapps/web/src/routeTree.gen.tsapps/web/src/routes/settings.agents.tsxapps/web/src/state/agents.tsdocs/internals/agents.mddocs/internals/glossary.mddocs/user/agents.mdpackages/client-runtime/package.jsonpackages/client-runtime/src/state/agents.tspackages/contracts/src/agentRefs.test.tspackages/contracts/src/agentRefs.tspackages/contracts/src/agents.test.tspackages/contracts/src/agents.tspackages/contracts/src/index.tspackages/contracts/src/orchestration.tspackages/contracts/src/providerRuntime.tspackages/contracts/src/rpc.tspackages/contracts/src/t3ProjectFile.test.tspackages/contracts/src/t3ProjectFile.tspackages/shared/package.jsonpackages/shared/src/agentRuleGlobs.test.tspackages/shared/src/agentRuleGlobs.ts
091b57d to
4195a0b
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
4195a0b to
2ec33ac
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Follow-up on the earlier findings: the standalone *Shape interfaces, the AgentRuleStore message getters, and the duplicated AgentProfileServices.layer in ws.ts are all resolved, and the hook/prompt/orchestration errors now carry structural attributes plus a real cause. One retained issue: several pure validation failures now manufacture an Error (or default one) purely to satisfy a cause field that is required, which the conventions call out explicitly. Three inline notes below.
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
apps/server/src/agents/prompt/RuleMatcher.ts (1)
21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove redundant return type annotations.
TypeScript infers these return types from the implementations. Keep annotations where they define required parameter or public data shapes.
As per coding guidelines, “Prefer inferred types over explicit annotations and do not use
any.”Also applies to: 33-33, 74-74, 101-101, 117-117, 120-120, 163-166, 173-176, 184-184
🤖 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 `@apps/server/src/agents/prompt/RuleMatcher.ts` at line 21, Remove redundant explicit return type annotations from the identified getters and methods in RuleMatcher, including get message and the additional referenced locations, while preserving annotations that define required parameter or public data shapes. Keep the implementations and inferred return behavior unchanged, and do not introduce any.Source: Coding guidelines
apps/server/src/agents/AgentOrchestrationLive.ts (1)
822-831: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winReplace
Option.getOrThrowwith a typed failure.Line 830 converts a missing run into an unhandled defect. Every other failure in
spawnmaps intoAgentProfileInvalidError. An MCP caller receives a typed error in all other paths and an untyped defect here.♻️ Proposed refactor
const run = yield* runs.get(runId).pipe( Effect.mapError((cause) => invalid("Could not reload the Agent run.", { operation: "run-reload", cause, runId, }), ), - Effect.map(Option.getOrThrow), + Effect.flatMap( + Option.match({ + onNone: () => + Effect.fail( + invalid("The Agent run disappeared after it started.", { + operation: "run-reload", + runId, + }), + ), + onSome: Effect.succeed, + }), + ), );🤖 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 `@apps/server/src/agents/AgentOrchestrationLive.ts` around lines 822 - 831, Update the run reload flow in spawn around runs.get and Option.getOrThrow so a missing run produces the same typed AgentProfileInvalidError path as other failures instead of an unhandled defect. Map the empty Option to invalid with the existing run-reload operation context and runId, while preserving the current successful run value and runs.get error mapping.
🤖 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 `@apps/mobile/src/features/settings/agentSettings.logic.ts`:
- Around line 1-5: Update parseRequiredNumber to parse the trimmed input once,
reject results that are not finite—including NaN—alongside the existing
required-value validation, and remove its explicit number return type so
TypeScript infers it.
In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 693-718: Update failSpawn to clean up isolated Git worktrees
before deleting the child thread when target.workspace.mode is
"isolated-worktree". Track successful createWorktree completion in prepareThread
and only invoke the worktree-removal operation from failSpawn when that creation
succeeded, preserving existing failure dispatch behavior.
- Around line 584-607: Update the agent-run.request handling around the visible
compileAgentPrompt flow to apply the same lineage token and estimated-cost
budget checks already used for follow-up handling. Validate the requested child
run before it is spawned, reject over-budget requests through the existing error
path, and preserve the current depth, run-count, and concurrency checks.
- Around line 52-58: Update the AgentProfileInvalidError construction to omit
the cause property when context?.cause is undefined, using the same
conditional-spread pattern as profileId and runId; preserve the existing cause
value whenever one is provided.
In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Around line 241-259: Update the rule-content accumulation in the loop over
matched.rules to measure the serialized output rather than only rule.body:
construct each non-empty rule’s header/body chunk, include its "\n\n" separator
as emitted by chunks.join("\n\n"), and check the resulting byte count against
maxBytes before appending. Preserve empty-rule behavior and report the
serialized size in AgentRuleContentOverflowError.
- Around line 120-160: Replace the backtracking RegExp construction in globRegex
with a linear-time glob-matching implementation, ensuring patterns containing
repeated or overlapping wildcards such as **a cannot cause exponential work when
tested against a non-matching path. Preserve the existing glob semantics for *,
**, ?, character classes, and alternations, and update callers to use the safe
matcher instead of expression.test.
---
Nitpick comments:
In `@apps/server/src/agents/AgentOrchestrationLive.ts`:
- Around line 822-831: Update the run reload flow in spawn around runs.get and
Option.getOrThrow so a missing run produces the same typed
AgentProfileInvalidError path as other failures instead of an unhandled defect.
Map the empty Option to invalid with the existing run-reload operation context
and runId, while preserving the current successful run value and runs.get error
mapping.
In `@apps/server/src/agents/prompt/RuleMatcher.ts`:
- Line 21: Remove redundant explicit return type annotations from the identified
getters and methods in RuleMatcher, including get message and the additional
referenced locations, while preserving annotations that define required
parameter or public data shapes. Keep the implementations and inferred return
behavior unchanged, and do not introduce any.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 5daa31b4-9fa3-4d8e-96b2-d0b2d7a434ce
📒 Files selected for processing (31)
apps/mobile/src/features/settings/agentProfile.logic.test.tsapps/mobile/src/features/settings/agentProfile.logic.tsapps/mobile/src/features/settings/agentRule.logic.test.tsapps/mobile/src/features/settings/agentRule.logic.tsapps/mobile/src/features/settings/agentSettings.logic.tsapps/mobile/src/state/use-composer-drafts.test.tsapps/server/src/agents/AgentCatalog.test.tsapps/server/src/agents/AgentCatalog.tsapps/server/src/agents/AgentOrchestrationLive.test.tsapps/server/src/agents/AgentOrchestrationLive.tsapps/server/src/agents/AgentPromptResolver.test.tsapps/server/src/agents/AgentPromptResolver.tsapps/server/src/agents/prompt/RuleMatcher.tsapps/server/src/agents/prompt/prompt.test.tsapps/server/src/agents/run/AgentRun.test.tsapps/server/src/agents/run/AgentRun.tsapps/server/src/agents/run/AgentRunDeadlineReactor.tsapps/server/src/agents/run/AgentRunReactor.tsapps/server/src/mcp/McpSessionRegistry.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.test.tsapps/server/src/persistence/Migrations/039_AgentRuns.test.tsapps/server/src/persistence/Migrations/039_AgentRuns.tsapps/server/src/ws.tsapps/web/src/components/settings/AgentsSettings.logic.test.tsapps/web/src/components/settings/AgentsSettings.logic.tsapps/web/src/components/settings/AgentsSettings.test.tsxapps/web/src/components/settings/AgentsSettings.tsxapps/web/src/components/settings/RulesSettings.logic.test.tsapps/web/src/components/settings/RulesSettings.logic.tsapps/web/src/components/settings/RulesSettings.tsxdocs/user/agents.md
🚧 Files skipped from review as they are similar to previous changes (24)
- apps/mobile/src/features/settings/agentProfile.logic.test.ts
- apps/web/src/components/settings/AgentsSettings.logic.test.ts
- apps/server/src/persistence/Migrations/039_AgentRuns.test.ts
- apps/web/src/components/settings/RulesSettings.logic.test.ts
- apps/server/src/agents/AgentPromptResolver.test.ts
- apps/server/src/agents/prompt/prompt.test.ts
- apps/mobile/src/features/settings/agentRule.logic.test.ts
- apps/web/src/components/settings/AgentsSettings.logic.ts
- apps/server/src/mcp/McpSessionRegistry.ts
- apps/server/src/agents/run/AgentRunDeadlineReactor.ts
- apps/server/src/ws.ts
- docs/user/agents.md
- apps/web/src/components/settings/RulesSettings.tsx
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
- apps/mobile/src/features/settings/agentProfile.logic.ts
- apps/web/src/components/settings/AgentsSettings.tsx
- apps/mobile/src/features/settings/agentRule.logic.ts
- apps/server/src/agents/AgentCatalog.test.ts
- apps/server/src/persistence/Migrations/039_AgentRuns.ts
- apps/web/src/components/settings/RulesSettings.logic.ts
- apps/server/src/agents/run/AgentRun.test.ts
- apps/server/src/agents/run/AgentRun.ts
- apps/server/src/agents/AgentPromptResolver.ts
- apps/server/src/agents/run/AgentRunReactor.ts
There was a problem hiding this comment.
One finding on error attribute safety in the new Agent orchestration code. Earlier rounds' findings (standalone *Shape interfaces, missing message getters, invented cause values, duplicated AgentProfileServices layer) look addressed.
Posted via Macroscope — Effect Service Conventions
|
@coderabbitai review |
✅ Action performedReview finished.
|
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)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx (1)
257-295: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winAdd failure handling to the archive and restore handlers.
saveRuleDocumentandsavewrap their awaited command intry/catch/finallyand show an error message.archiveRestoreRuleandarchiveRestoreusetry/finallyonly. Ifcommand(...)rejects instead of returning a failure result, the rejection escapes through() => void archiveRestoreRule(), the user sees no message, and an unhandled promise rejection is produced.Add a
catchbranch that sets the corresponding error state.🛠️ Proposed fix for `archiveRestoreRule`
setRuleNotice(selectedRuleSummary.archivedAt ? "Rule restored." : "Rule archived."); catalog.refresh(); + } catch (caught) { + setRuleError(caught instanceof Error ? caught.message : "The rule could not be updated."); } finally { ruleCommandInFlight.current = false; setRuleCommandPending(false); }Also applies to: 349-387
🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines 257 - 295, Update archiveRestoreRule and the corresponding archiveRestore handler to add catch branches around their awaited command calls, setting the appropriate rule error state when a rejection occurs. Preserve the existing early returns, success handling, and finally blocks so command-in-flight state is always cleared.
🧹 Nitpick comments (2)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx (1)
43-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare
diagnosticLabelinstead of copying it.The same helper exists in
apps/web/src/components/settings/AgentsSettings.tsxandapps/web/src/components/settings/RulesSettings.tsx. Three copies of one formatting rule will drift. Move the helper into shared client code and import it in all three screens.As per coding guidelines, "shared logic belongs in
packages/client-runtimewhen appropriate."🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx` around lines 43 - 44, Move the diagnosticLabel helper into shared client code under packages/client-runtime, then import and reuse it in SettingsAgentsRouteScreen, AgentsSettings, and RulesSettings. Remove each local duplicate while preserving the existing diagnostic formatting behavior.Source: Coding guidelines
apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts (1)
672-678: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the user-visible failure for the rejected profile.
The test verifies that no prompt resolution, session start, or turn send occurs. It does not verify what the user sees. Add a read-model assertion after
harness.drain()that the thread records the incompatibility failure. That protects against a silent-drop regression where the turn is rejected without any activity.As per coding guidelines, "Backend behavior changes must include focused tests for that behavior."
🤖 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 `@apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts` around lines 672 - 678, Extend the test after harness.drain() to read the thread’s activity/read model and assert it records the user-visible incompatibility failure for the rejected profile. Keep the existing no-op assertions for resolveAgentPrompt, startSession, and sendTurn, and use the test harness’s established read-model accessors and failure representation.Source: Coding guidelines
🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 257-295: Update archiveRestoreRule and the corresponding
archiveRestore handler to add catch branches around their awaited command calls,
setting the appropriate rule error state when a rejection occurs. Preserve the
existing early returns, success handling, and finally blocks so
command-in-flight state is always cleared.
---
Nitpick comments:
In `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 43-44: Move the diagnosticLabel helper into shared client code
under packages/client-runtime, then import and reuse it in
SettingsAgentsRouteScreen, AgentsSettings, and RulesSettings. Remove each local
duplicate while preserving the existing diagnostic formatting behavior.
In `@apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts`:
- Around line 672-678: Extend the test after harness.drain() to read the
thread’s activity/read model and assert it records the user-visible
incompatibility failure for the rejected profile. Keep the existing no-op
assertions for resolveAgentPrompt, startSession, and sendTurn, and use the test
harness’s established read-model accessors and failure representation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7370eb5b-bfc2-4cbe-9684-93259c1edb44
📒 Files selected for processing (24)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsxapps/server/integration/OrchestrationEngineHarness.integration.tsapps/server/src/agents/AgentCatalog.tsapps/server/src/agents/AgentHookRunner.test.tsapps/server/src/agents/AgentHookRunner.tsapps/server/src/agents/AgentOrchestrationLive.test.tsapps/server/src/agents/AgentOrchestrationLive.tsapps/server/src/agents/AgentPromptResolver.test.tsapps/server/src/agents/AgentPromptResolver.tsapps/server/src/agents/prompt/RuleMatcher.tsapps/server/src/agents/prompt/prompt.test.tsapps/server/src/agents/run/AgentRun.test.tsapps/server/src/agents/run/AgentRun.tsapps/server/src/agents/run/AgentRunDeadlineReactor.test.tsapps/server/src/agents/run/AgentRunDeadlineReactor.tsapps/server/src/agents/run/AgentRunReactor.test.tsapps/server/src/agents/run/AgentRunReactor.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.test.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.tsapps/server/src/ws.tsapps/web/src/components/settings/AgentsSettings.tsxapps/web/src/components/settings/RulesSettings.tsxpackages/contracts/src/agents.test.tspackages/contracts/src/agents.ts
🚧 Files skipped from review as they are similar to previous changes (14)
- apps/server/integration/OrchestrationEngineHarness.integration.ts
- apps/server/src/agents/prompt/prompt.test.ts
- apps/server/src/agents/AgentHookRunner.test.ts
- apps/server/src/ws.ts
- apps/server/src/agents/run/AgentRun.ts
- apps/server/src/agents/AgentHookRunner.ts
- apps/web/src/components/settings/AgentsSettings.tsx
- apps/server/src/agents/run/AgentRunDeadlineReactor.ts
- apps/server/src/agents/prompt/RuleMatcher.ts
- apps/server/src/agents/run/AgentRun.test.ts
- apps/server/src/agents/AgentPromptResolver.ts
- apps/web/src/components/settings/RulesSettings.tsx
- apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
- packages/contracts/src/agents.ts
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx`:
- Around line 569-574: Update the rules catalog rendering near the rules.length
check in SettingsAgentsRouteScreen to handle loading and error states before
showing the empty-catalog message. Mirror the existing profile catalog guards
around lines 509-521, using the rules catalog’s loading and error state symbols,
and preserve “No rules yet” only for a successfully loaded empty result.
- Around line 839-851: Update the Always apply Pressable to use an off-state
background when props.draft.alwaysApply is false, while preserving the primary
background when enabled. Add an accessibilityLabel identifying it as the “Always
apply” switch, matching the accessible labeling used by the nearby Show in chat
Agent picker switch.
- Around line 287-320: Add a catch handler to archiveRestoreRule, and likewise
to archiveRestore, so rejected command promises set an appropriate user-facing
rule error while preserving the existing context-key guard. Keep the finally
blocks responsible for clearing ruleCommandInFlight and ruleCommandPending,
ensuring void archiveRestoreRule() and the corresponding archiveRestore call do
not produce unhandled rejections.
In `@apps/web/src/components/settings/RulesSettings.tsx`:
- Around line 127-182: In the RulesSettings mutation flow, add shared isMutating
state used by both save and archiveRestore, set it before either command starts,
and clear it in a finally block so it resets on success or failure. Pass
isMutating to RuleEditor and disable both Save and Archive/Restore actions while
it is true, preventing concurrent submissions with stale expectedRevision
values.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 778bef4d-4da2-4296-a024-f1fdc2fefd7c
📒 Files selected for processing (52)
apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsxapps/mobile/src/features/settings/agentProfile.logic.test.tsapps/mobile/src/features/settings/agentProfile.logic.tsapps/mobile/src/features/settings/agentRule.logic.test.tsapps/mobile/src/features/settings/agentRule.logic.tsapps/mobile/src/features/settings/agentSettings.logic.test.tsapps/mobile/src/features/settings/agentSettings.logic.tsapps/mobile/src/features/threads/NewTaskDraftScreen.tsxapps/mobile/src/features/threads/ThreadComposer.tsxapps/server/src/agents/AgentCatalog.test.tsapps/server/src/agents/AgentCatalog.tsapps/server/src/agents/AgentOrchestrationLive.test.tsapps/server/src/agents/AgentOrchestrationLive.tsapps/server/src/agents/AgentProfileServices.tsapps/server/src/agents/AgentProfileStore.test.tsapps/server/src/agents/AgentProfileStore.tsapps/server/src/agents/AgentProjectFileCoordinator.tsapps/server/src/agents/AgentRuleStore.test.tsapps/server/src/agents/AgentRuleStore.tsapps/server/src/agents/AgentStoreErrorMapping.test.tsapps/server/src/agents/AgentStoreErrorMapping.tsapps/server/src/agents/AgentWorkspaceRoot.test.tsapps/server/src/agents/AgentWorkspaceRoot.tsapps/server/src/agents/prompt/RuleMatcher.tsapps/server/src/agents/prompt/prompt.test.tsapps/server/src/agents/run/AgentRun.test.tsapps/server/src/agents/run/AgentRun.tsapps/server/src/agents/run/AgentRunDeadlineReactor.test.tsapps/server/src/agents/run/AgentRunDeadlineReactor.tsapps/server/src/agents/run/AgentRunReactor.test.tsapps/server/src/agents/run/AgentRunReactor.tsapps/server/src/provider/Layers/CursorAdapter.test.tsapps/server/src/provider/Layers/CursorAdapter.tsapps/server/src/provider/Layers/GrokAdapter.test.tsapps/server/src/provider/Layers/GrokAdapter.tsapps/server/src/provider/Layers/OpenCodeAdapter.test.tsapps/server/src/provider/Layers/OpenCodeAdapter.tsapps/server/src/ws.tsapps/web/src/components/chat/AgentProfilePicker.logic.tsapps/web/src/components/chat/AgentProfilePicker.test.tsapps/web/src/components/chat/AgentProfilePicker.tsxapps/web/src/components/chat/ChatComposer.logic.test.tsapps/web/src/components/chat/ChatComposer.logic.tsapps/web/src/components/chat/ChatComposer.tsxapps/web/src/components/settings/AgentsSettings.logic.test.tsapps/web/src/components/settings/AgentsSettings.logic.tsapps/web/src/components/settings/AgentsSettings.tsxapps/web/src/components/settings/RulesSettings.logic.test.tsapps/web/src/components/settings/RulesSettings.logic.tsapps/web/src/components/settings/RulesSettings.test.tsxapps/web/src/components/settings/RulesSettings.tsxpackages/contracts/src/agents.ts
🚧 Files skipped from review as they are similar to previous changes (41)
- apps/server/src/agents/AgentWorkspaceRoot.test.ts
- apps/server/src/agents/AgentStoreErrorMapping.test.ts
- apps/server/src/provider/Layers/CursorAdapter.test.ts
- apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
- apps/server/src/provider/Layers/CursorAdapter.ts
- apps/mobile/src/features/settings/agentRule.logic.test.ts
- apps/web/src/components/chat/ChatComposer.logic.test.ts
- apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
- apps/server/src/agents/AgentWorkspaceRoot.ts
- apps/server/src/agents/AgentProfileStore.test.ts
- apps/server/src/provider/Layers/GrokAdapter.test.ts
- apps/server/src/agents/run/AgentRun.test.ts
- apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
- apps/server/src/agents/AgentCatalog.test.ts
- apps/mobile/src/features/settings/agentRule.logic.ts
- apps/server/src/agents/AgentStoreErrorMapping.ts
- apps/web/src/components/settings/RulesSettings.test.tsx
- apps/server/src/provider/Layers/GrokAdapter.ts
- apps/server/src/agents/AgentProjectFileCoordinator.ts
- apps/server/src/agents/prompt/prompt.test.ts
- apps/mobile/src/features/threads/ThreadComposer.tsx
- apps/server/src/provider/Layers/OpenCodeAdapter.ts
- apps/web/src/components/chat/AgentProfilePicker.logic.ts
- apps/server/src/agents/AgentProfileStore.ts
- apps/server/src/ws.ts
- apps/web/src/components/settings/RulesSettings.logic.ts
- apps/server/src/agents/prompt/RuleMatcher.ts
- apps/server/src/agents/AgentRuleStore.ts
- apps/server/src/agents/run/AgentRunDeadlineReactor.ts
- apps/mobile/src/features/settings/agentProfile.logic.ts
- apps/server/src/agents/AgentRuleStore.test.ts
- apps/web/src/components/chat/AgentProfilePicker.test.ts
- apps/web/src/components/chat/ChatComposer.tsx
- apps/server/src/agents/run/AgentRun.ts
- apps/web/src/components/settings/AgentsSettings.tsx
- apps/server/src/agents/AgentProfileServices.ts
- packages/contracts/src/agents.ts
- apps/web/src/components/chat/AgentProfilePicker.tsx
- apps/server/src/agents/AgentOrchestrationLive.test.ts
- apps/server/src/agents/run/AgentRunReactor.ts
- apps/server/src/agents/AgentOrchestrationLive.ts
ApprovabilityVerdict: Needs human review 1 blocking correctness issue found. Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
|
Opened megascan#1 with the Agents panel bridge plus model, profile, and run identity display. It targets the contributor branch directly, so merging it will update this PR. The stacked PR includes before/after UI evidence and focused validation. |
…orchestration # Conflicts: # apps/mobile/src/Stack.tsx # apps/mobile/src/features/settings/components/settings-sheet-targets.ts # apps/mobile/src/features/threads/NewTaskDraftScreen.tsx # apps/mobile/src/features/threads/ThreadComposer.tsx # apps/mobile/src/features/threads/new-task-flow-provider.tsx # apps/server/src/persistence/Migrations.ts # apps/server/src/ws.ts # apps/web/src/routeTree.gen.ts
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (17)
apps/server/src/ws.ts (2)
484-496: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueImport the ref type instead of deriving it from
Parameters<...>.Lines 486-491 rebuild the profile-ref shape through
Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>[0]["ref"]. The contracts package already exports the profile reference type used bygetProfile. A direct import states the intent and survives a signature change togetProfile.🤖 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 `@apps/server/src/ws.ts` around lines 484 - 496, Update mapAgentCatalogError to use the contracts package’s exported profile reference type for ref instead of deriving its id and scope through Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>. Import and apply that existing type while preserving the current error mapping behavior.
456-478: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winPreserve the underlying cause when project lookup fails.
The
Effect.mapErrorat lines 471-477 converts every non-AgentProfileInvalidErrorfailure intoAgentProfileInvalidErrorwith the textCould not resolve project '<id>'.. A projection read failure is an infrastructure error, not invalid input. The original cause is discarded, so the server logs keep no record of why the read failed. Log the cause before mapping.♻️ Proposed change
Effect.mapError((error) => isAgentProfileInvalidError(error) ? error : new AgentProfileInvalidError({ detail: `Could not resolve project '${projectId}'.`, }), ), + Effect.tapError((error) => + isAgentProfileInvalidError(error) + ? Effect.void + : Effect.logWarning("agent workspace root resolution failed", { + projectId, + error, + }), + ),Place the
tapErrorbefore themapErrorso it observes the original failure.🤖 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 `@apps/server/src/ws.ts` around lines 456 - 478, Update agentWorkspaceRoot to add an Effect.tapError before the existing Effect.mapError, logging the original project lookup failure and its cause before non-AgentProfileInvalidError values are wrapped. Preserve the current AgentProfileInvalidError mapping and successful workspaceRoot behavior.apps/server/src/persistence/Migrations/041_AgentRuns.ts (1)
61-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winTwo indexes duplicate implicit indexes created by UNIQUE constraints.
SQLite creates an implicit index for every UNIQUE constraint.
- Line 61 declares
UNIQUE (agent_run_id, revision)onagent_run_events. The index at lines 80-83 uses the same columns in the same order, so it adds no new access path. It doubles index write cost on the event-append path, which runs on every dispatch.- Line 26 declares
child_thread_id TEXT UNIQUE. The index at lines 85-88 duplicates that implicit index.Drop both explicit indexes and update the assertions in
apps/server/src/persistence/Migrations/041_AgentRuns.test.tsat lines 56 and 61.♻️ Proposed removal
- yield* sql` - CREATE INDEX IF NOT EXISTS idx_agent_run_events_run_revision - ON agent_run_events(agent_run_id, revision) - `; - - yield* sql` - CREATE INDEX IF NOT EXISTS idx_projection_agent_runs_child_thread - ON projection_agent_runs(child_thread_id) - `;Also applies to: 80-88
🤖 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 `@apps/server/src/persistence/Migrations/041_AgentRuns.ts` at line 61, Remove the explicit indexes duplicating the UNIQUE constraints: the agent_run_events index on (agent_run_id, revision) and the child_thread_id index. Update the corresponding assertions in AgentRuns migration tests to expect both indexes to be absent, while preserving all other migration indexes and constraints.apps/server/src/persistence/Migrations/041_AgentRuns.test.ts (1)
31-47: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winAssert the full column set used by the repository upsert.
AgentRunRepositoryinserts intoprojection_agent_runswith 28 columns, includingconsumed_tokens,detached,integration_target_thread_id,last_error,workspace_mode, andproject_id. The test asserts only 13 of them. A future migration edit that drops one of the unasserted columns breaks the repository at runtime, and this regression test stays green. Add the remaining columns to the expected list.🤖 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 `@apps/server/src/persistence/Migrations/041_AgentRuns.test.ts` around lines 31 - 47, Expand the expected column list in the migration test’s columnNames assertion to include every column inserted by AgentRunRepository’s projection_agent_runs upsert, including consumed_tokens, detached, integration_target_thread_id, last_error, workspace_mode, project_id, and all other omitted columns, so the test covers the complete 28-column contract.apps/server/src/agents/AgentRuleStore.test.ts (1)
139-146: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider asserting the decoded
t3.jsonreference instead of a substring count.The assertion counts
project-typescriptoccurrences and expects exactly 2. That count depends on the id also appearing inside the default path. A change to the default path layout breaks this test with an unclear failure. Decodet3.jsonand assert onerulesentry with the expectedidandpath.🤖 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 `@apps/server/src/agents/AgentRuleStore.test.ts` around lines 139 - 146, Replace the substring-count assertion on projectFile in the AgentRuleStore test with a decoded t3.json assertion. Parse the JSON and verify that the rules collection contains exactly one entry with id project-typescript and the expected path, while preserving the existing generated markdown content assertion.apps/server/src/agents/AgentRuleStore.ts (1)
404-411: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse
Result.isSuccessfor consistency.Line 409 inspects
current._tagdirectly. Lines 386 and 437 use theResultpredicates. UseResult.isSuccess(current)here so the file uses one style, and so the narrowing stays valid if theResultrepresentation changes.🤖 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 `@apps/server/src/agents/AgentRuleStore.ts` around lines 404 - 411, Update the documentPath selection in the surrounding rule-loading method to use Result.isSuccess(current) instead of checking current._tag directly, while preserving the existing sourcePath fallback behavior for both success and non-success results.apps/server/src/agents/run/AgentRunReactor.ts (1)
235-293: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated failure dispatch.
completeSuccessfulRunbuilds the sameagent-run.failcommand and the same return shape three times, at Lines 240-252, 259-271, and 280-292. Extract one local helper that takes the failure detail and returns thefailedresult. This removes the risk of the three copies drifting apart.♻️ Proposed refactor
+ const failRun = (failure: string) => + retryDurable( + input.repository.dispatch({ + type: "agent-run.fail", + runId: input.run.id, + failure, + ...(input.usage === undefined ? {} : { usage: input.usage }), + occurredAt: input.occurredAt, + }), + ).pipe( + Effect.map((failed) => ({ + status: "failed" as const, + revision: revisionAfterAgentRunTransition(input.run, failed), + })), + );🤖 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 `@apps/server/src/agents/run/AgentRunReactor.ts` around lines 235 - 293, In completeSuccessfulRun, extract the repeated agent-run.fail dispatch and failed-status/revision construction into one local helper accepting the failure detail. Replace the budget-exhausted preflight path, afterResult hook failure path, and completion budget-exhausted path with calls to that helper, preserving usage, occurredAt, retryDurable, and revisionAfterAgentRunTransition behavior.apps/server/src/agents/run/AgentRunReactor.test.ts (1)
235-248: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the redundant
continuedflag.The assertion on
continuedcannot fail. IfappendAgentRunTaskActivitypropagated the interruption, the test fiber would already be interrupted. Drop the flag and let the successful completion of the effect be the assertion, or assert an observable effect such as a follow-up dispatch.🤖 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 `@apps/server/src/agents/run/AgentRunReactor.test.ts` around lines 235 - 248, Remove the redundant continued variable and its assertion from the test around appendAgentRunTaskActivity. Let successful completion of the Effect.gen block verify that the interruption was not propagated, without adding unrelated assertions or behavior.apps/server/src/agents/AgentCatalog.ts (1)
732-832: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the repeated
AgentCatalogDocumentErrorconstruction.The same eight-line
Effect.mapError((cause) => new AgentCatalogDocumentError({...}))block appears eight times acrossprofileSummary,ruleSummary,profileDocument,ruleDocument,revisionOf, andreadSource. Onlykindandcodevary. A small helper reduces the repetition and keeps the diagnostic fields consistent when the error shape changes.♻️ Sketch: shared error mapper
+ const documentError = + (source: Source, code: "invalid-document" | "missing-frontmatter" | "read-failed") => + (cause: unknown) => + new AgentCatalogDocumentError({ + kind: source.kind, + scope: source.ref.scope, + id: source.ref.id, + sourcePath: source.sourcePath, + code, + cause, + });Each site then becomes
Effect.mapError(documentError(source, "invalid-document")).source.kindalready carries"profile"or"rule", so the hardcodedkindliterals are redundant.🤖 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 `@apps/server/src/agents/AgentCatalog.ts` around lines 732 - 832, Extract the repeated AgentCatalogDocumentError construction into a shared documentError helper that accepts the Source and error code, derives kind from source.kind, and preserves scope, id, sourcePath, and cause. Replace the Effect.mapError callbacks in profileSummary, ruleSummary, profileDocument, ruleDocument, revisionOf, and readSource with this helper, passing "invalid-document" or the existing code as appropriate.apps/mobile/src/state/agentProfileSelection.test.ts (1)
6-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the explicit-selection branch of the tri-state resolver.
resolveAgentProfileSelectionaccepts three draft states:null,undefined, and an explicit selection. The tests covernullandundefinedonly. The explicit-selection branch is the path a user takes when picking an agent in the composer, and it is untested.💚 Proposed test
+ it("returns an explicit draft selection over the thread selection", () => { + const draft = { + id: AgentProfileId.make("chosen"), + scope: "environment" as const, + revision: AgentProfileRevision.make("b".repeat(64)), + }; + const fallback = { + id: AgentProfileId.make("fallback"), + scope: "environment" as const, + revision: AgentProfileRevision.make("a".repeat(64)), + }; + expect(resolveAgentProfileSelection(draft, fallback)).toEqual(draft); + expect(resolveAgentProfileSelection(undefined, null)).toBeNull(); + });🤖 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 `@apps/mobile/src/state/agentProfileSelection.test.ts` around lines 6 - 25, Add a test in the “agent profile selection” suite for resolveAgentProfileSelection that passes an explicit agent profile selection and verifies the same selection is returned unchanged, covering the composer’s user-selected branch alongside the existing null and undefined cases.apps/mobile/src/features/settings/agentSettings.logic.ts (1)
14-21: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueThe context key delimiter is not escaped.
agentSettingsContextKeyjoins four values with:.selectionKeyalready contains a colon, because a selection key is built as${scope}:${id}. If any component contains a colon, two different inputs can produce the same key, and a superseded save then passes the context-key guard inSettingsAgentsRouteScreen.Serialize the tuple instead of concatenating. Also drop the explicit
stringreturn type.♻️ Proposed change
export function agentSettingsContextKey(input: { readonly environmentId: string | null; readonly projectId: string | null; readonly selectionKey: string | null; readonly generation: number; -}): string { - return `${input.environmentId ?? ""}:${input.projectId ?? ""}:${input.selectionKey ?? ""}:${input.generation}`; +}) { + return JSON.stringify([ + input.environmentId, + input.projectId, + input.selectionKey, + input.generation, + ]); }As per coding guidelines: "Prefer inferred types over explicit annotations and do not use
any."🤖 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 `@apps/mobile/src/features/settings/agentSettings.logic.ts` around lines 14 - 21, Update agentSettingsContextKey to serialize the four input values as an unambiguous tuple rather than concatenating them with colons, preserving null values distinctly so embedded delimiters cannot collide. Remove the explicit string return type and let TypeScript infer it.Source: Coding guidelines
apps/server/src/agents/AgentProfileStore.ts (1)
374-380: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePreserve the existing entry position in
t3.json.The filter-then-append pattern moves a re-saved profile to the end of the
agentsarray.t3.jsonis a checked-in file, so every save of an existing profile produces an ordering diff.Replace the entry in place when it already exists, and append only for a new id.
♻️ Proposed change
- const agents = [ - ...(current.agents ?? []).filter((entry) => entry.id !== input.ref.id), - { - id: input.ref.id, - path: input.documentPath, - }, - ]; + const existing = current.agents ?? []; + const entry = { id: input.ref.id, path: input.documentPath }; + const agents = existing.some((item) => item.id === input.ref.id) + ? existing.map((item) => (item.id === input.ref.id ? entry : item)) + : [...existing, entry];🤖 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 `@apps/server/src/agents/AgentProfileStore.ts` around lines 374 - 380, Update the agents construction in the profile save flow to preserve an existing profile’s array position: locate the entry matching input.ref.id and replace it with the new id/path object, while appending only when no matching entry exists. Keep the current.agents fallback behavior unchanged.apps/mobile/src/features/settings/agentRule.logic.ts (1)
55-61: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDrop the explicit return type on
parseInteger.
parseIntegerdeclares: number, but the value is fully inferable fromparseRequiredNumber.integerinagentProfile.logic.tshas the same annotation.As per coding guidelines: "Prefer inferred types over explicit annotations and do not use
any."🤖 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 `@apps/mobile/src/features/settings/agentRule.logic.ts` around lines 55 - 61, Remove the explicit : number return annotation from parseInteger in agentRule.logic.ts, allowing its return type to be inferred from parseRequiredNumber while preserving the existing validation and return behavior.Source: Coding guidelines
packages/contracts/src/agents.ts (2)
378-411: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winUse a rule-specific id type for rule operations.
AgentRule.idisAgentSlug, butAgentRuleGetInput.idandAgentRuleArchiveInput.idareAgentProfileId. Callers must brand a rule identifier with the profile brand. If the two identifier rules diverge later, the rule endpoints will silently follow the profile constraints.Add an
AgentRuleIdexport and use it for both the document field and the rule operation inputs.🤖 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 `@packages/contracts/src/agents.ts` around lines 378 - 411, Define and export an AgentRuleId type based on AgentSlug, then update AgentRuleDocument.id, AgentRuleGetInput.id, and AgentRuleArchiveInput.id to use AgentRuleId instead of AgentProfileId. Ensure restore operations inherit the updated archive identifier type through AgentRuleArchiveInput.
703-775: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReduce the number of alias layers for one contract.
Each MCP operation now has up to three exported names: the compact name,
AgentMcpAgent*, andMcpAgentRun*. New code can pick any of them, so the canonical name is not enforced by the type system.Keep one canonical name plus at most one deprecated compatibility alias, and mark the compatibility aliases with
@deprecatedso editors steer callers to the canonical export.🤖 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 `@packages/contracts/src/agents.ts` around lines 703 - 775, Reduce the alias layers around the AgentMcp operation contracts to one canonical export and at most one compatibility alias per operation. Remove the redundant AgentMcpAgent* or McpAgentRun* layer, retain the intended legacy aliases, and annotate every retained compatibility export with `@deprecated` pointing callers to the canonical name. Preserve each alias as a reference to the same underlying codec and type.apps/mobile/src/features/settings/agentRule.logic.test.ts (1)
62-72: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the thrown message for the multi-colon target case.
.toThrow()with no argument passes for any error. This test guards the fix that stoppedparseProfilesfrom truncating"environment:reviewer:truncated"to id"reviewer". The distinguishing evidence is which error is raised: schema rejection of the preserved id"reviewer:truncated".💚 Proposed assertion
- ).toThrow(); + ).toThrow("Rule settings contain an invalid value.");🤖 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 `@apps/mobile/src/features/settings/agentRule.logic.test.ts` around lines 62 - 72, Update the buildAgentRuleDocument test for the multi-colon profiles value to assert the specific schema-rejection message for the preserved id “reviewer:truncated,” rather than accepting any thrown error. Keep the existing input and verify the error distinguishes this case from truncation to “reviewer.”packages/client-runtime/src/state/subagentRuntime.test.ts (1)
99-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the
agentProfileIdtitle fallback.
getOrCreateinsubagentRuntime.tsnow falls back toagentProfileIdfor the title whentitleanddetailare absent. This test suppliesagentProfileIdbut never asserts the resultingtitle, so the new fallback branch is untested.💚 Proposed assertion
expect(agents[0]).toMatchObject({ id: "6a31f0ba-1111-2222-3333-444444444444", model: "gpt-5.6-sol", profileId: "sol-smoke-test", + title: "sol-smoke-test", status: "completed", });🤖 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 `@packages/client-runtime/src/state/subagentRuntime.test.ts` around lines 99 - 121, Extend the “preserves native Agent run model, profile ID, and run ID” test to assert that the folded agent’s title falls back to the supplied agentProfileId when title and detail are absent. Keep the existing identity, model, profileId, and status assertions unchanged.
🤖 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 `@apps/mobile/src/features/settings/agentProfile.logic.ts`:
- Around line 62-67: Update integer to accept minimum and maximum bounds, remove
its explicit number return annotation, and include the field label and limits in
range errors. Import the contract limits and validate maxRuns, maxConcurrency,
maxDepth, and maxWallTimeMinutes with their respective bounds, using a minimum
of 1 for the PositiveInt fields and 0 for maxDepth. Apply the same integer
validation and messages in the web profile editor so decodeAgentProfileDocument
behaves identically across mobile and web.
In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 88-104: In apps/mobile/src/features/settings/agentRule.logic.ts
lines 88-104, pass parseProfiles(draft.profiles) directly to
decodeAgentRuleDocument instead of calling decodeAgentProfileLocators, so
validation occurs inside the guarded path. In
apps/mobile/src/features/settings/agentRule.logic.test.ts lines 62-72, replace
the broad .toThrow() assertion with one requiring "Rule settings contain an
invalid value.".
In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 399-453: Protect the project-scoped compare-and-swap sequence in
saveUnlocked with projectFileCoordinator.withWorkspaceLock, covering the
current-profile read, revision validation, existing-file snapshot, and
writeContained call. Preserve the existing process-local mutex behavior and
avoid locking environment-scoped profiles unless required by the coordinator’s
API.
In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 193-213: Update apps/server/src/agents/AgentRuleStore.test.ts
lines 193-213 by passing expectedRevision: saved.revision to the second
store.save call, then assert the failure has tag AgentRuleStoreError and
operation "write-project-file" so the test reaches and verifies rollback. No
direct code change is required in apps/server/src/agents/AgentRuleStore.ts lines
386-394; confirm its expectedRevision contract remains unchanged.
In `@packages/contracts/src/agents.ts`:
- Around line 436-449: Remove the optional cause field from the
AgentProfileInvalidError transport schema while preserving detail, operation,
profileId, and runId. Keep underlying causes server-side or expose only an
explicitly redacted replacement field, and leave the message getter returning
detail.
---
Nitpick comments:
In `@apps/mobile/src/features/settings/agentRule.logic.test.ts`:
- Around line 62-72: Update the buildAgentRuleDocument test for the multi-colon
profiles value to assert the specific schema-rejection message for the preserved
id “reviewer:truncated,” rather than accepting any thrown error. Keep the
existing input and verify the error distinguishes this case from truncation to
“reviewer.”
In `@apps/mobile/src/features/settings/agentRule.logic.ts`:
- Around line 55-61: Remove the explicit : number return annotation from
parseInteger in agentRule.logic.ts, allowing its return type to be inferred from
parseRequiredNumber while preserving the existing validation and return
behavior.
In `@apps/mobile/src/features/settings/agentSettings.logic.ts`:
- Around line 14-21: Update agentSettingsContextKey to serialize the four input
values as an unambiguous tuple rather than concatenating them with colons,
preserving null values distinctly so embedded delimiters cannot collide. Remove
the explicit string return type and let TypeScript infer it.
In `@apps/mobile/src/state/agentProfileSelection.test.ts`:
- Around line 6-25: Add a test in the “agent profile selection” suite for
resolveAgentProfileSelection that passes an explicit agent profile selection and
verifies the same selection is returned unchanged, covering the composer’s
user-selected branch alongside the existing null and undefined cases.
In `@apps/server/src/agents/AgentCatalog.ts`:
- Around line 732-832: Extract the repeated AgentCatalogDocumentError
construction into a shared documentError helper that accepts the Source and
error code, derives kind from source.kind, and preserves scope, id, sourcePath,
and cause. Replace the Effect.mapError callbacks in profileSummary, ruleSummary,
profileDocument, ruleDocument, revisionOf, and readSource with this helper,
passing "invalid-document" or the existing code as appropriate.
In `@apps/server/src/agents/AgentProfileStore.ts`:
- Around line 374-380: Update the agents construction in the profile save flow
to preserve an existing profile’s array position: locate the entry matching
input.ref.id and replace it with the new id/path object, while appending only
when no matching entry exists. Keep the current.agents fallback behavior
unchanged.
In `@apps/server/src/agents/AgentRuleStore.test.ts`:
- Around line 139-146: Replace the substring-count assertion on projectFile in
the AgentRuleStore test with a decoded t3.json assertion. Parse the JSON and
verify that the rules collection contains exactly one entry with id
project-typescript and the expected path, while preserving the existing
generated markdown content assertion.
In `@apps/server/src/agents/AgentRuleStore.ts`:
- Around line 404-411: Update the documentPath selection in the surrounding
rule-loading method to use Result.isSuccess(current) instead of checking
current._tag directly, while preserving the existing sourcePath fallback
behavior for both success and non-success results.
In `@apps/server/src/agents/run/AgentRunReactor.test.ts`:
- Around line 235-248: Remove the redundant continued variable and its assertion
from the test around appendAgentRunTaskActivity. Let successful completion of
the Effect.gen block verify that the interruption was not propagated, without
adding unrelated assertions or behavior.
In `@apps/server/src/agents/run/AgentRunReactor.ts`:
- Around line 235-293: In completeSuccessfulRun, extract the repeated
agent-run.fail dispatch and failed-status/revision construction into one local
helper accepting the failure detail. Replace the budget-exhausted preflight
path, afterResult hook failure path, and completion budget-exhausted path with
calls to that helper, preserving usage, occurredAt, retryDurable, and
revisionAfterAgentRunTransition behavior.
In `@apps/server/src/persistence/Migrations/041_AgentRuns.test.ts`:
- Around line 31-47: Expand the expected column list in the migration test’s
columnNames assertion to include every column inserted by AgentRunRepository’s
projection_agent_runs upsert, including consumed_tokens, detached,
integration_target_thread_id, last_error, workspace_mode, project_id, and all
other omitted columns, so the test covers the complete 28-column contract.
In `@apps/server/src/persistence/Migrations/041_AgentRuns.ts`:
- Line 61: Remove the explicit indexes duplicating the UNIQUE constraints: the
agent_run_events index on (agent_run_id, revision) and the child_thread_id
index. Update the corresponding assertions in AgentRuns migration tests to
expect both indexes to be absent, while preserving all other migration indexes
and constraints.
In `@apps/server/src/ws.ts`:
- Around line 484-496: Update mapAgentCatalogError to use the contracts
package’s exported profile reference type for ref instead of deriving its id and
scope through Parameters<AgentCatalog.AgentCatalog["Service"]["getProfile"]>.
Import and apply that existing type while preserving the current error mapping
behavior.
- Around line 456-478: Update agentWorkspaceRoot to add an Effect.tapError
before the existing Effect.mapError, logging the original project lookup failure
and its cause before non-AgentProfileInvalidError values are wrapped. Preserve
the current AgentProfileInvalidError mapping and successful workspaceRoot
behavior.
In `@packages/client-runtime/src/state/subagentRuntime.test.ts`:
- Around line 99-121: Extend the “preserves native Agent run model, profile ID,
and run ID” test to assert that the folded agent’s title falls back to the
supplied agentProfileId when title and detail are absent. Keep the existing
identity, model, profileId, and status assertions unchanged.
In `@packages/contracts/src/agents.ts`:
- Around line 378-411: Define and export an AgentRuleId type based on AgentSlug,
then update AgentRuleDocument.id, AgentRuleGetInput.id, and
AgentRuleArchiveInput.id to use AgentRuleId instead of AgentProfileId. Ensure
restore operations inherit the updated archive identifier type through
AgentRuleArchiveInput.
- Around line 703-775: Reduce the alias layers around the AgentMcp operation
contracts to one canonical export and at most one compatibility alias per
operation. Remove the redundant AgentMcpAgent* or McpAgentRun* layer, retain the
intended legacy aliases, and annotate every retained compatibility export with
`@deprecated` pointing callers to the canonical name. Preserve each alias as a
reference to the same underlying codec and type.
🪄 Autofix
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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9538eafe-c167-4a9f-bfb9-0f610a408644
📒 Files selected for processing (139)
apps/mobile/src/Stack.tsxapps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsxapps/mobile/src/features/settings/SettingsRouteScreen.tsxapps/mobile/src/features/settings/agentProfile.logic.test.tsapps/mobile/src/features/settings/agentProfile.logic.tsapps/mobile/src/features/settings/agentRule.logic.test.tsapps/mobile/src/features/settings/agentRule.logic.tsapps/mobile/src/features/settings/agentSettings.logic.test.tsapps/mobile/src/features/settings/agentSettings.logic.tsapps/mobile/src/features/settings/components/settings-sheet-targets.tsapps/mobile/src/features/threads/NewTaskDraftScreen.tsxapps/mobile/src/features/threads/ThreadComposer.tsxapps/mobile/src/features/threads/ThreadDetailScreen.tsxapps/mobile/src/features/threads/ThreadRouteScreen.tsxapps/mobile/src/features/threads/new-task-flow-provider.tsxapps/mobile/src/features/threads/use-project-actions.tsapps/mobile/src/lib/projectThreadStartTurn.tsapps/mobile/src/state/agentProfileSelection.test.tsapps/mobile/src/state/agentProfileSelection.tsapps/mobile/src/state/agents.tsapps/mobile/src/state/thread-outbox-model.tsapps/mobile/src/state/thread-outbox.test.tsapps/mobile/src/state/use-composer-drafts.test.tsapps/mobile/src/state/use-composer-drafts.tsapps/mobile/src/state/use-thread-composer-state.test.tsapps/mobile/src/state/use-thread-composer-state.tsapps/mobile/src/state/use-thread-outbox-drain.tsapps/server/integration/OrchestrationEngineHarness.integration.tsapps/server/src/agents/AgentCatalog.test.tsapps/server/src/agents/AgentCatalog.tsapps/server/src/agents/AgentHookRunner.test.tsapps/server/src/agents/AgentHookRunner.tsapps/server/src/agents/AgentOrchestration.tsapps/server/src/agents/AgentOrchestrationLive.test.tsapps/server/src/agents/AgentOrchestrationLive.tsapps/server/src/agents/AgentProfileServices.tsapps/server/src/agents/AgentProfileStore.test.tsapps/server/src/agents/AgentProfileStore.tsapps/server/src/agents/AgentProjectFileCoordinator.tsapps/server/src/agents/AgentPromptResolver.test.tsapps/server/src/agents/AgentPromptResolver.tsapps/server/src/agents/AgentRuleStore.test.tsapps/server/src/agents/AgentRuleStore.tsapps/server/src/agents/AgentStoreErrorMapping.test.tsapps/server/src/agents/AgentStoreErrorMapping.tsapps/server/src/agents/AgentWorkspaceRoot.test.tsapps/server/src/agents/AgentWorkspaceRoot.tsapps/server/src/agents/prompt/PromptCompiler.tsapps/server/src/agents/prompt/RuleMatcher.tsapps/server/src/agents/prompt/index.tsapps/server/src/agents/prompt/prompt.test.tsapps/server/src/agents/run/AgentRun.test.tsapps/server/src/agents/run/AgentRun.tsapps/server/src/agents/run/AgentRunDeadlineReactor.test.tsapps/server/src/agents/run/AgentRunDeadlineReactor.tsapps/server/src/agents/run/AgentRunReactor.test.tsapps/server/src/agents/run/AgentRunReactor.tsapps/server/src/agents/run/AgentRunRepository.test.tsapps/server/src/agents/run/AgentRunRepository.tsapps/server/src/auth/RpcAuthorization.tsapps/server/src/mcp/McpHttpServer.test.tsapps/server/src/mcp/McpHttpServer.tsapps/server/src/mcp/McpInvocationContext.test.tsapps/server/src/mcp/McpInvocationContext.tsapps/server/src/mcp/McpSessionRegistry.test.tsapps/server/src/mcp/McpSessionRegistry.tsapps/server/src/mcp/McpToolkit.tsapps/server/src/mcp/toolkits/agents/handlers.tsapps/server/src/mcp/toolkits/agents/tools.tsapps/server/src/orchestration/Layers/ProjectionPipeline.tsapps/server/src/orchestration/Layers/ProjectionSnapshotQuery.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.test.tsapps/server/src/orchestration/Layers/ProviderCommandReactor.tsapps/server/src/orchestration/agentProfile.test.tsapps/server/src/orchestration/decider.tsapps/server/src/orchestration/projector.tsapps/server/src/persistence/Layers/ProjectionThreads.tsapps/server/src/persistence/Migrations.tsapps/server/src/persistence/Migrations/041_AgentRuns.test.tsapps/server/src/persistence/Migrations/041_AgentRuns.tsapps/server/src/persistence/Migrations/042_ProjectionThreadsAgentProfile.tsapps/server/src/persistence/Services/ProjectionThreads.tsapps/server/src/provider/AgentRuntimeCompatibility.test.tsapps/server/src/provider/AgentRuntimeCompatibility.tsapps/server/src/provider/Layers/ClaudeAdapter.tsapps/server/src/provider/Layers/CodexAdapter.tsapps/server/src/provider/Layers/CursorAdapter.test.tsapps/server/src/provider/Layers/CursorAdapter.tsapps/server/src/provider/Layers/GrokAdapter.test.tsapps/server/src/provider/Layers/GrokAdapter.tsapps/server/src/provider/Layers/OpenCodeAdapter.test.tsapps/server/src/provider/Layers/OpenCodeAdapter.tsapps/server/src/provider/Services/ProviderAdapter.tsapps/server/src/server.test.tsapps/server/src/server.tsapps/server/src/ws.tsapps/web/src/components/AgentsPanel.tsxapps/web/src/components/ChatView.logic.test.tsapps/web/src/components/ChatView.logic.tsapps/web/src/components/ChatView.tsxapps/web/src/components/chat/AgentProfilePicker.logic.tsapps/web/src/components/chat/AgentProfilePicker.test.tsapps/web/src/components/chat/AgentProfilePicker.tsxapps/web/src/components/chat/ChatComposer.logic.test.tsapps/web/src/components/chat/ChatComposer.logic.tsapps/web/src/components/chat/ChatComposer.tsxapps/web/src/components/settings/AgentsSettings.logic.test.tsapps/web/src/components/settings/AgentsSettings.logic.tsapps/web/src/components/settings/AgentsSettings.test.tsxapps/web/src/components/settings/AgentsSettings.tsxapps/web/src/components/settings/RulesSettings.logic.test.tsapps/web/src/components/settings/RulesSettings.logic.tsapps/web/src/components/settings/RulesSettings.test.tsxapps/web/src/components/settings/RulesSettings.tsxapps/web/src/components/settings/SettingsSidebarNav.tsxapps/web/src/components/settings/settingsSearch.tsapps/web/src/routeTree.gen.tsapps/web/src/routes/settings.agents.tsxapps/web/src/state/agents.tsdocs/internals/agents.mddocs/internals/glossary.mddocs/user/agents.mdpackages/client-runtime/package.jsonpackages/client-runtime/src/state/agents.tspackages/client-runtime/src/state/subagentRuntime.test.tspackages/client-runtime/src/state/subagentRuntime.tspackages/contracts/src/agentRefs.test.tspackages/contracts/src/agentRefs.tspackages/contracts/src/agents.test.tspackages/contracts/src/agents.tspackages/contracts/src/index.tspackages/contracts/src/orchestration.tspackages/contracts/src/providerRuntime.tspackages/contracts/src/rpc.tspackages/contracts/src/t3ProjectFile.test.tspackages/contracts/src/t3ProjectFile.tspackages/shared/package.jsonpackages/shared/src/agentRuleGlobs.test.tspackages/shared/src/agentRuleGlobs.ts
🚧 Files skipped from review as they are similar to previous changes (116)
- apps/server/src/auth/RpcAuthorization.ts
- apps/mobile/src/features/threads/use-project-actions.ts
- apps/server/src/provider/Layers/GrokAdapter.test.ts
- packages/client-runtime/package.json
- apps/mobile/src/state/use-composer-drafts.test.ts
- apps/server/src/server.test.ts
- apps/mobile/src/features/threads/ThreadRouteScreen.tsx
- apps/server/integration/OrchestrationEngineHarness.integration.ts
- apps/server/src/persistence/Migrations.ts
- apps/mobile/src/features/settings/SettingsRouteScreen.tsx
- apps/mobile/src/state/use-thread-outbox-drain.ts
- apps/server/src/agents/prompt/index.ts
- apps/server/src/mcp/McpSessionRegistry.ts
- apps/mobile/src/lib/projectThreadStartTurn.ts
- apps/server/src/agents/AgentStoreErrorMapping.test.ts
- apps/web/src/components/settings/AgentsSettings.test.tsx
- apps/web/src/components/settings/AgentsSettings.logic.test.ts
- apps/server/src/persistence/Layers/ProjectionThreads.ts
- packages/shared/src/agentRuleGlobs.test.ts
- apps/server/src/provider/AgentRuntimeCompatibility.test.ts
- apps/mobile/src/features/settings/components/settings-sheet-targets.ts
- apps/mobile/src/state/use-composer-drafts.ts
- apps/web/src/components/ChatView.logic.ts
- apps/server/src/agents/AgentProfileServices.ts
- apps/server/src/mcp/McpInvocationContext.ts
- apps/mobile/src/features/settings/agentProfile.logic.test.ts
- apps/server/src/mcp/McpSessionRegistry.test.ts
- apps/server/src/provider/AgentRuntimeCompatibility.ts
- apps/web/src/components/ChatView.logic.test.ts
- apps/web/src/components/chat/AgentProfilePicker.logic.ts
- apps/mobile/src/state/use-thread-composer-state.ts
- apps/mobile/src/features/threads/ThreadComposer.tsx
- apps/server/src/agents/AgentOrchestrationLive.test.ts
- apps/server/src/mcp/McpToolkit.ts
- apps/web/src/components/settings/settingsSearch.ts
- apps/server/src/orchestration/agentProfile.test.ts
- apps/server/src/agents/AgentWorkspaceRoot.ts
- apps/mobile/src/features/settings/SettingsAgentsRouteScreen.tsx
- apps/server/src/agents/run/AgentRunDeadlineReactor.test.ts
- apps/server/src/persistence/Services/ProjectionThreads.ts
- apps/web/src/components/chat/ChatComposer.logic.test.ts
- apps/web/src/components/chat/ChatComposer.logic.ts
- apps/server/src/agents/AgentProjectFileCoordinator.ts
- apps/web/src/state/agents.ts
- apps/server/src/agents/AgentWorkspaceRoot.test.ts
- apps/server/src/agents/run/AgentRunRepository.test.ts
- apps/mobile/src/state/thread-outbox.test.ts
- apps/server/src/mcp/toolkits/agents/handlers.ts
- apps/mobile/src/state/thread-outbox-model.ts
- apps/server/src/mcp/McpHttpServer.test.ts
- apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
- packages/contracts/src/providerRuntime.ts
- packages/shared/package.json
- apps/server/src/agents/AgentProfileStore.test.ts
- docs/internals/agents.md
- packages/contracts/src/agentRefs.test.ts
- apps/server/src/provider/Layers/CursorAdapter.test.ts
- apps/server/src/provider/Layers/GrokAdapter.ts
- packages/contracts/src/agentRefs.ts
- apps/server/src/provider/Services/ProviderAdapter.ts
- apps/server/src/provider/Layers/CursorAdapter.ts
- apps/server/src/agents/AgentStoreErrorMapping.ts
- apps/server/src/provider/Layers/ClaudeAdapter.ts
- apps/server/src/server.ts
- apps/server/src/agents/AgentPromptResolver.test.ts
- apps/server/src/mcp/McpInvocationContext.test.ts
- apps/web/src/components/settings/SettingsSidebarNav.tsx
- packages/contracts/src/index.ts
- apps/mobile/src/state/use-thread-composer-state.test.ts
- apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts
- packages/contracts/src/t3ProjectFile.ts
- apps/web/src/components/settings/RulesSettings.logic.test.ts
- apps/server/src/orchestration/decider.ts
- apps/server/src/orchestration/projector.ts
- apps/mobile/src/state/agents.ts
- apps/server/src/mcp/McpHttpServer.ts
- apps/mobile/src/features/threads/ThreadDetailScreen.tsx
- apps/server/src/agents/AgentHookRunner.ts
- apps/server/src/provider/Layers/OpenCodeAdapter.ts
- apps/server/src/agents/prompt/PromptCompiler.ts
- apps/web/src/components/settings/AgentsSettings.logic.ts
- apps/mobile/src/Stack.tsx
- apps/server/src/agents/AgentOrchestration.ts
- apps/server/src/mcp/toolkits/agents/tools.ts
- apps/web/src/components/settings/RulesSettings.logic.ts
- apps/server/src/agents/run/AgentRunRepository.ts
- apps/web/src/components/settings/RulesSettings.test.tsx
- apps/web/src/routes/settings.agents.tsx
- packages/shared/src/agentRuleGlobs.ts
- apps/server/src/provider/Layers/OpenCodeAdapter.test.ts
- apps/web/src/components/chat/AgentProfilePicker.tsx
- apps/server/src/agents/AgentPromptResolver.ts
- apps/web/src/components/settings/RulesSettings.tsx
- apps/web/src/components/chat/AgentProfilePicker.test.ts
- apps/server/src/agents/prompt/RuleMatcher.ts
- packages/contracts/src/orchestration.ts
- apps/server/src/agents/AgentCatalog.test.ts
- apps/mobile/src/features/threads/new-task-flow-provider.tsx
- apps/web/src/components/settings/AgentsSettings.tsx
- packages/contracts/src/agents.test.ts
- apps/server/src/provider/Layers/CodexAdapter.ts
- apps/server/src/agents/AgentOrchestrationLive.ts
- apps/mobile/src/state/agentProfileSelection.ts
- apps/web/src/components/ChatView.tsx
- apps/web/src/routeTree.gen.ts
- apps/web/src/components/chat/ChatComposer.tsx
- apps/server/src/agents/AgentHookRunner.test.ts
- apps/server/src/agents/run/AgentRunDeadlineReactor.ts
- apps/server/src/agents/prompt/prompt.test.ts
- apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
- docs/user/agents.md
- packages/contracts/src/t3ProjectFile.test.ts
- apps/server/src/orchestration/Layers/ProjectionPipeline.ts
- apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
- packages/contracts/src/rpc.ts
- apps/server/src/agents/run/AgentRun.ts
| function integer(value: string, label: string): number { | ||
| const parsed = parseRequiredNumber(value, label); | ||
| if (!Number.isInteger(parsed) || parsed < 0) | ||
| throw new Error(`${label} must be a non-negative whole number.`); | ||
| return parsed; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Budget fields report contract range violations with a generic message.
integer accepts any non-negative whole number. The contract bounds each budget field: maxRuns at most 32, maxConcurrency at most 8, maxDepth at most 4, and maxWallTimeMinutes at most 120. maxRuns, maxConcurrency, and maxWallTimeMinutes are also PositiveInt, so 0 is invalid.
An out-of-range entry therefore reaches decodeAgentProfileDocument and surfaces as "Profile settings contain an invalid value." The user does not learn which field failed or what the limit is. parseInteger in agentRule.logic.ts reports its range precisely, so the two editors behave differently.
Pass the minimum and maximum into integer and report them. Also drop the explicit number return type to follow the inferred-types guideline.
🐛 Proposed fix
-function integer(value: string, label: string): number {
+function integer(value: string, label: string, minimum: number, maximum: number) {
const parsed = parseRequiredNumber(value, label);
- if (!Number.isInteger(parsed) || parsed < 0)
- throw new Error(`${label} must be a non-negative whole number.`);
+ if (!Number.isInteger(parsed) || parsed < minimum || parsed > maximum) {
+ throw new Error(`${label} must be a whole number from ${minimum} to ${maximum}.`);
+ }
return parsed;
}Then import the contract limits and apply them per field:
budgets: {
- maxRuns: integer(draft.maxRuns, "Maximum runs"),
- maxConcurrency: integer(draft.maxConcurrency, "Maximum concurrency"),
- maxDepth: integer(draft.maxDepth, "Maximum delegation depth"),
- maxWallTimeMinutes: integer(draft.maxWallTimeMinutes, "Maximum wall time"),
+ maxRuns: integer(draft.maxRuns, "Maximum runs", 1, AGENT_RUN_MAX_RUNS),
+ maxConcurrency: integer(
+ draft.maxConcurrency,
+ "Maximum concurrency",
+ 1,
+ AGENT_RUN_MAX_CONCURRENCY,
+ ),
+ maxDepth: integer(
+ draft.maxDepth,
+ "Maximum delegation depth",
+ 0,
+ AGENT_RUN_MAX_DELEGATION_DEPTH,
+ ),
+ maxWallTimeMinutes: integer(
+ draft.maxWallTimeMinutes,
+ "Maximum wall time",
+ 1,
+ AGENT_RUN_MAX_WALL_TIME_MINUTES,
+ ),
},Apply the same change to the web profile editor so both surfaces report identical messages.
As per coding guidelines: "Prefer inferred types over explicit annotations" and "Frontend behavior must support all applicable clients: web, desktop/Electron, and mobile/React Native".
Also applies to: 98-109
🤖 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 `@apps/mobile/src/features/settings/agentProfile.logic.ts` around lines 62 -
67, Update integer to accept minimum and maximum bounds, remove its explicit
number return annotation, and include the field label and limits in range
errors. Import the contract limits and validate maxRuns, maxConcurrency,
maxDepth, and maxWallTimeMinutes with their respective bounds, using a minimum
of 1 for the PositiveInt fields and 0 for maxDepth. Apply the same integer
validation and messages in the web profile editor so decodeAgentProfileDocument
behaves identically across mobile and web.
Source: Coding guidelines
| return decodeAgentRuleDocument({ | ||
| id: draft.id.trim(), | ||
| scope: draft.scope, | ||
| revision: baseline?.revision ?? "a".repeat(64), | ||
| name: draft.name.trim(), | ||
| ...(draft.description.trim() ? { description: draft.description.trim() } : {}), | ||
| globs: parseAgentRuleGlobs(draft.globs), | ||
| alwaysApply: draft.alwaysApply, | ||
| priority: parseInteger(draft.priority), | ||
| sourcePath: baseline?.sourcePath ?? null, | ||
| archivedAt: baseline?.archivedAt ?? null, | ||
| updatedAt: now, | ||
| body: draft.body, | ||
| profiles: decodeAgentProfileLocators(parseProfiles(draft.profiles)), | ||
| createdAt: baseline?.createdAt ?? now, | ||
| }); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
An invalid profile target surfaces a raw Effect ParseError to the user, and the test does not detect it. decodeAgentProfileLocators runs while the argument object is built, so it executes before control enters decodeAgentRuleDocument and its try/catch. The escaping error is rendered directly by the settings screen. The existing test uses a bare .toThrow(), which passes for any error and therefore does not distinguish the raw schema dump from the intended message.
apps/mobile/src/features/settings/agentRule.logic.ts#L88-L104: remove the separatedecodeAgentProfileLocatorscall and passparseProfiles(draft.profiles)straight intodecodeAgentRuleDocument, soAgentRuleDocumentSchemavalidates the locators inside the guarded path.apps/mobile/src/features/settings/agentRule.logic.test.ts#L62-L72: change.toThrow()to.toThrow("Rule settings contain an invalid value.")so the test pins the user-visible message.
📍 Affects 2 files
apps/mobile/src/features/settings/agentRule.logic.ts#L88-L104(this comment)apps/mobile/src/features/settings/agentRule.logic.test.ts#L62-L72
🤖 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 `@apps/mobile/src/features/settings/agentRule.logic.ts` around lines 88 - 104,
In apps/mobile/src/features/settings/agentRule.logic.ts lines 88-104, pass
parseProfiles(draft.profiles) directly to decodeAgentRuleDocument instead of
calling decodeAgentProfileLocators, so validation occurs inside the guarded
path. In apps/mobile/src/features/settings/agentRule.logic.test.ts lines 62-72,
replace the broad .toThrow() assertion with one requiring "Rule settings contain
an invalid value.".
| const saveUnlocked = Effect.fn("AgentProfileStore.saveUnlocked")(function* (input: { | ||
| readonly profile: AgentProfileDocument; | ||
| readonly expectedRevision?: AgentProfileRevision | undefined; | ||
| readonly workspaceRoot?: string | undefined; | ||
| }) { | ||
| const ref: AgentProfileLocator = { id: input.profile.id, scope: input.profile.scope }; | ||
| const current = yield* catalog | ||
| .getProfile({ ref, workspaceRoot: input.workspaceRoot }) | ||
| .pipe(Effect.result); | ||
| if (Result.isSuccess(current)) { | ||
| if (input.expectedRevision !== current.success.revision) { | ||
| return yield* new AgentProfileStoreRevisionConflictError({ | ||
| scope: ref.scope, | ||
| id: ref.id, | ||
| ...(input.expectedRevision ? { expectedRevision: input.expectedRevision } : {}), | ||
| actualRevision: current.success.revision, | ||
| }); | ||
| } | ||
| } else if (current.failure._tag !== "AgentCatalogNotFoundError") { | ||
| return yield* storeError("load", ref, "Could not load current profile.", current.failure); | ||
| } else if (input.expectedRevision !== undefined) { | ||
| return yield* new AgentProfileStoreRevisionConflictError({ | ||
| scope: ref.scope, | ||
| id: ref.id, | ||
| expectedRevision: input.expectedRevision, | ||
| }); | ||
| } | ||
|
|
||
| const defaultPath = | ||
| ref.scope === "environment" | ||
| ? path.join("agents", `${ref.id}.md`) | ||
| : `.t3code/agents/${ref.id}.md`; | ||
| const documentPath = | ||
| current._tag === "Success" | ||
| ? (current.success.sourcePath ?? defaultPath) | ||
| : ref.scope === "environment" | ||
| ? defaultPath | ||
| : (input.profile.sourcePath ?? defaultPath); | ||
| const target = yield* resolveWritePath({ | ||
| ref, | ||
| workspaceRoot: input.workspaceRoot, | ||
| documentPath, | ||
| }); | ||
| const previous = yield* existingFile(target.filePath).pipe( | ||
| Effect.mapError((cause) => | ||
| storeError("write-document", ref, "Could not snapshot profile Markdown.", cause), | ||
| ), | ||
| ); | ||
| yield* writeContained({ | ||
| ref, | ||
| root: target.root, | ||
| filePath: target.filePath, | ||
| contents: renderProfile(input.profile), | ||
| operation: "write-document", | ||
| }); |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
The compare-and-swap window is not protected for the Markdown document.
saveUnlocked reads the current revision through catalog.getProfile, then writes the file. Only the process-local mutex serializes this. writeProjectReference takes projectFileCoordinator.withWorkspaceLock, but the profile Markdown write at Line 447 runs outside any workspace lock.
An external editor or a second server process can change the document between the read and the write. The write then overwrites that change without a conflict error.
If a single server process is the only writer by design, record that assumption in the module comment. Otherwise, take the workspace lock around the read-check-write sequence for project-scoped profiles.
🤖 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 `@apps/server/src/agents/AgentProfileStore.ts` around lines 399 - 453, Protect
the project-scoped compare-and-swap sequence in saveUnlocked with
projectFileCoordinator.withWorkspaceLock, covering the current-profile read,
revision validation, existing-file snapshot, and writeContained call. Preserve
the existing process-local mutex behavior and avoid locking environment-scoped
profiles unless required by the coordinator’s API.
| const result = yield* withStore( | ||
| workspace, | ||
| tempDir, | ||
| Effect.service(AgentRuleStore.AgentRuleStore).pipe( | ||
| Effect.flatMap((store) => | ||
| store.save({ | ||
| rule: { ...saved, body: "This write must be rolled back." }, | ||
| workspaceRoot: workspace, | ||
| }), | ||
| ), | ||
| Effect.result, | ||
| ), | ||
| ); | ||
|
|
||
| assert.isTrue(Result.isFailure(result)); | ||
| assert.match( | ||
| yield* fileSystem.readFileString( | ||
| path.join(workspace, ".t3code", "rules", "restore-typescript.md"), | ||
| ), | ||
| /Use strict types\./, | ||
| ); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
The rollback test for an existing project rule does not reach the rollback path. saveUnlocked compares input.expectedRevision with the current revision and fails with AgentRuleStoreRevisionConflictError when they differ. The test saves an existing rule without expectedRevision, so the save fails at the compare-and-swap check before any Markdown write. The assertions still pass, but they prove only that nothing was written.
apps/server/src/agents/AgentRuleStore.test.ts#L193-L213: passexpectedRevision: saved.revisionto the secondstore.savecall so the save reacheswriteContainedand then fails on the invalidt3.json. Also assert the returned failure tag isAgentRuleStoreErrorwithoperation: "write-project-file"so a future compare-and-swap regression cannot pass this test silently.apps/server/src/agents/AgentRuleStore.ts#L386-L394: no code change required; confirm that requiringexpectedRevisionfor every update of an existing rule is the intended contract, because the callers must always supply it.
📍 Affects 2 files
apps/server/src/agents/AgentRuleStore.test.ts#L193-L213(this comment)apps/server/src/agents/AgentRuleStore.ts#L386-L394
🤖 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 `@apps/server/src/agents/AgentRuleStore.test.ts` around lines 193 - 213, Update
apps/server/src/agents/AgentRuleStore.test.ts lines 193-213 by passing
expectedRevision: saved.revision to the second store.save call, then assert the
failure has tag AgentRuleStoreError and operation "write-project-file" so the
test reaches and verifies rollback. No direct code change is required in
apps/server/src/agents/AgentRuleStore.ts lines 386-394; confirm its
expectedRevision contract remains unchanged.
| export class AgentProfileInvalidError extends Schema.TaggedErrorClass<AgentProfileInvalidError>()( | ||
| "AgentProfileInvalidError", | ||
| { | ||
| detail: TrimmedNonEmptyString, | ||
| operation: Schema.optional(Schema.String), | ||
| profileId: Schema.optional(AgentProfileId), | ||
| runId: Schema.optional(AgentRunId), | ||
| cause: Schema.optional(Schema.Defect()), | ||
| }, | ||
| ) { | ||
| override get message(): string { | ||
| return this.detail; | ||
| } | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect how AgentProfileInvalidError and its cause are produced and encoded.
rg -n -C 4 'AgentProfileInvalidError' --type=ts
rg -n -C 3 'Schema\.Defect\(' --type=tsRepository: pingdotgg/t3code
Length of output: 154
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
git ls-files 'packages/contracts/src/agents.ts'
printf '%s\n' '--- error references ---'
rg -n -C 5 'AgentProfileInvalidError|AgentProfileInvalid' .
printf '%s\n' '--- Defect schemas ---'
rg -n -C 5 'Defect\s*\(' packages --glob '*.{ts,tsx,js,jsx}' || true
printf '%s\n' '--- transport encoding and error handling ---'
rg -n -C 4 'encode.*Error|Schema\.encode|TaggedErrorClass|Cause|cause' packages apps --glob '*.{ts,tsx,js,jsx}' 2>/dev/null | head -n 500 || trueRepository: pingdotgg/t3code
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- websocket error flow ---'
sed -n '480,560p' apps/server/src/ws.ts
printf '%s\n' '--- AgentProfileError transport references ---'
rg -n -C 5 'AgentProfileError|AgentProfileInvalidError|observeRpcEffect|Schema\.encode|encode.*error|error.*encode' apps/server/src packages/contracts/src --glob '*.{ts,tsx}'
printf '%s\n' '--- RPC and HTTP error response helpers ---'
rg -n -C 5 'Rpc.*Error|mapError|TaggedError|JSON|response.*error|error:' apps/server/src/ws.ts apps/server/src/mcp --glob '*.{ts,tsx}' | head -n 400
printf '%s\n' '--- Effect dependency metadata ---'
rg -n -C 3 '"effect"|effect' package.json packages/*/package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null | head -n 120Repository: pingdotgg/t3code
Length of output: 50373
🌐 Web query:
Effect Schema Defect serialization Schema.Defect JSON encode official documentation
💡 Result:
In the Effect library, Schema.Defect is a specialized schema designed to handle the serialization of JavaScript Error instances and other unrecoverable defects, which typically do not serialize correctly to JSON because their properties (like message, stack, and name) are non-enumerable [1][2][3]. When used with Schema.encodeSync or other encoding functions, Schema.Defect transforms these Error objects into plain, JSON-serializable objects that retain essential diagnostic information [1][2][3]. Key behaviors of Schema.Defect include: - Encoding: It converts Error instances into objects containing properties such as name and message [2][3]. - Decoding: If the input contains the expected properties (e.g., message and optionally name or stack), it reconstructs an Error instance [2]. - Use Case: It is primarily used for cross-boundary communication (such as RPC calls) or persistence, where standard JSON serialization would otherwise result in an empty object ({}) for Error types [1][2]. Example usage with Schema.encodeSync: ts import { Schema, Exit } from "effect" // Define a schema using Schema.Defect const schema = Schema.Exit({ failure: Schema.String, success: Schema.NumberFromString, defect: Schema.Defect, }) const encode = Schema.encodeSync(schema) // Encoding a die exit (defect) console.log(encode(Exit.die(new Error("Example message")))) /* Output: { _tag: 'Failure', cause: { _tag: 'Die', defect: { name: 'Error', message: 'Example message' } } } */ By default, various Effect internal mechanisms (like @effect/rpc) utilize Schema.Defect to ensure that errors can be safely transmitted across network boundaries without losing their descriptive content [1][4].
Citations:
- 1: fix(rpc): encode defects with Schema.Defect in sendRequestDefect and sendDefect Effect-TS/effect#6055
- 2: https://www.effect.website/docs/v3/schema/effect-data-types
- 3: https://www.effect.solutions/error-handling
- 4: feat(rpc): add defect schema option to Rpc.make Effect-TS/effect#6065
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- RPC group and server construction ---'
rg -n -C 6 'WsRpcGroup|RpcGroup|RpcServer|RpcClient|WebSocket.*Server|WebSocket.*Client|errorSchema|errors:' apps/server/src packages --glob '*.{ts,tsx}' | head -n 500
printf '%s\n' '--- instrumentation implementation ---'
sed -n '1,260p' apps/server/src/observability/RpcInstrumentation.ts
printf '%s\n' '--- package files and lockfile version ---'
rg -n -C 4 '"effect"|"`@effect/rpc`"|"`@effect/platform`"' package.json apps/server/package.json packages/*/package.json pnpm-lock.yaml 2>/dev/null | head -n 180
printf '%s\n' '--- local Effect sources, if present ---'
find . -path '*/node_modules/effect*' -o -path '*/node_modules/@effect/rpc*' | head -n 30Repository: pingdotgg/t3code
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- WsRpcGroup definition ---'
rg -n -C 12 'WsRpcGroup' packages/contracts/src apps/server/src --glob '*.{ts,tsx}'
printf '%s\n' '--- RPC server setup in ws.ts ---'
rg -n -C 12 'RpcServer\.|RpcGroup|WebSocket|wsRpc|WsRpc' apps/server/src/ws.ts
printf '%s\n' '--- effect catalog version ---'
rg -n -C 5 'effect:' pnpm-workspace.yaml package.json pnpm-lock.yaml 2>/dev/null | head -n 100
printf '%s\n' '--- RPC request declarations ---'
rg -n -C 8 'Rpc\.|Request<|RpcGroup\.make|RpcGroup\.of|RpcGroup\.add' packages/contracts/src --glob '*.{ts,tsx}' | head -n 300Repository: pingdotgg/t3code
Length of output: 50372
🌐 Web query:
site:github.com/Effect-TS/effect "Schema.Defect" "stack"
💡 Result:
In the context of the Effect-TS ecosystem, Schema.Defect is a specific schema used to handle the serialization of defects (unexpected errors, such as runtime exceptions) when they cross boundaries, most notably in RPC (Remote Procedure Call) communications [1][2]. Key details regarding Schema.Defect: 1. Purpose: When an Effect application encounters a defect, it typically carries non-enumerable properties like message and stack trace. Standard serialization (such as JSON.stringify) fails to preserve these, causing the diagnostic information to be lost [1]. Schema.Defect provides a structured way to encode these defects so that they can be transmitted and reconstructed accurately [1]. 2. Stack Traces: By default, Schema.Defect is designed to handle defect serialization for external boundaries, which sometimes involves stripping sensitive or internal diagnostic details like stack traces [2]. However, developers can control this behavior. For instance, when defining RPC methods using Rpc.make, one can provide a defect schema option (e.g., Schema.Defect({ includeStack: true })) to explicitly include stack traces and other diagnostic data, which is useful for internal service boundaries where debugging visibility is required [3][2]. 3. Integration: Schema.Defect is integrated into the broader Effect schema system [4][5]. In RPC implementations, it serves as the default mechanism for encoding exits (the result of an effect execution) that have failed due to a defect, ensuring that the error state is communicated across the wire [1][2]. Developers can override this default on a per-RPC basis if they need to pass more complex or custom error structures [2][6].
Citations:
- 1: fix(rpc): encode defects with Schema.Defect in sendRequestDefect and sendDefect Effect-TS/effect#6055
- 2: feat(rpc): add defect schema option to Rpc.make Effect-TS/effect#6065
- 3: https://github.com/Effect-TS/effect/blob/2e9a34ac/packages/platform-node/test/fixtures/rpc-schemas.ts
- 4: Effect-TS/effect@07299a3
- 5: https://github.com/Effect-TS/effect/blob/2e9a34ac/packages/effect/test/schema/representation/toSchema.test.ts
- 6: https://github.com/Effect-TS/effect/blob/3e59443b/packages/rpc/test/Rpc.test.ts
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
tarball="$(jq -r '.dist.tarball' "$tmpdir/metadata.json")"
curl -fsSL "$tarball" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
printf '%s\n' '--- Schema.Defect implementation and nearby helpers ---'
rg -n -C 12 'Defect|defect' "$tmpdir/package/src" "$tmpdir/package/dist" 2>/dev/null | head -n 300Repository: pingdotgg/t3code
Length of output: 26714
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
printf '%s\n' '--- files defining or exporting Defect ---'
rg -l 'Defect' "$tmpdir/package/src" | grep -E '/(Schema|schema|ParseResult|Effect|Rpc)' | head -n 40
printf '%s\n' '--- exact Defect definitions and encoders ---'
rg -n -C 20 'export const Defect|const Defect|function Defect|includeStack|defect.*encode|encode.*defect' "$tmpdir/package/src/Schema.ts" "$tmpdir/package/src/Schema" "$tmpdir/package/src/unstable/rpc" 2>/dev/null | head -n 500Repository: pingdotgg/t3code
Length of output: 33199
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
node --input-type=module - "$tmpdir/package/dist" <<'JS'
import * as Schema from `${process.argv[2]}/Schema.js`;
const CauseSchema = Schema.Defect();
const encodeCause = Schema.encodeUnknownSync(Schema.toCodecJson(CauseSchema));
const samples = {
error: new Error("failed to read /home/user/.t3/userdata/config.json"),
errorWithCause: Object.assign(
new Error("provider failed"),
{ cause: new Error("token at /run/secrets/provider-token") },
),
object: { message: "filesystem path: /srv/private/project", stack: "secret stack" },
};
for (const [label, value] of Object.entries(samples)) {
console.log(label, JSON.stringify(encodeCause(value)));
}
JSRepository: pingdotgg/t3code
Length of output: 972
🏁 Script executed:
#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
curl -fsSL 'https://registry.npmjs.org/effect/4.0.0-beta.103' -o "$tmpdir/metadata.json"
curl -fsSL "$(jq -r '.dist.tarball' "$tmpdir/metadata.json")" -o "$tmpdir/effect.tgz"
tar -xzf "$tmpdir/effect.tgz" -C "$tmpdir"
node --input-type=module - "$tmpdir/package/dist" <<'JS'
const Schema = await import(`file://${process.argv[2]}/Schema.js`);
const CauseSchema = Schema.Defect();
const encodeCause = Schema.encodeUnknownSync(Schema.toCodecJson(CauseSchema));
const samples = {
error: new Error("failed to read /home/user/.t3/userdata/config.json"),
errorWithCause: Object.assign(
new Error("provider failed"),
{ cause: new Error("token at /run/secrets/provider-token") },
),
object: { message: "filesystem path: /srv/private/project", stack: "secret stack" },
};
for (const [label, value] of Object.entries(samples)) {
console.log(label, JSON.stringify(encodeCause(value)));
}
JSRepository: pingdotgg/t3code
Length of output: 1188
Remove cause from the transport schema. Agent WebSocket RPC errors include AgentProfileInvalidError. Schema.Defect() encodes error messages and nested causes, which can disclose filesystem paths or other server details. Keep cause server-side or map it to a redacted field.
🤖 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 `@packages/contracts/src/agents.ts` around lines 436 - 449, Remove the optional
cause field from the AgentProfileInvalidError transport schema while preserving
detail, operation, profileId, and runId. Keep underlying causes server-side or
expose only an explicitly redacted replacement field, and leave the message
getter returning detail.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
There are 6 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 502d128. Configure here.
| revision: 0, | ||
| occurredAt: command.occurredAt, | ||
| wallTimeOriginAt: | ||
| state.runs.get(parent.rootRunId)?.wallTimeOriginAt ?? parent.wallTimeOriginAt, |
There was a problem hiding this comment.
Spawn ignores exhausted wall budget
Medium Severity
Child agent-run.request now inherits wallTimeOriginAt from the root, so a late spawn can already be past maxWallTimeMinutes, but request/start still succeed. Token and cost budgets fail closed before creating work; wall time only blocks later succeed/follow-up, so orchestration can still create threads and worktrees that the deadline reactor immediately cancels.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 502d128. Configure here.
Port the reusable profile and Rule layer from upstream PR pingdotgg#5632 onto the V2 orchestration model. Profiles and matching Rules are stored through authorized environment RPCs, selected on web and mobile, pinned through tri-state V2 thread commands, snapshotted immutably in migration 052, and compiled into every provider turn. Existing fork delegation rows remain available as typed read-only V2 history. This intentionally does not import the donor AgentRun repository, reactors, MCP toolkit, or conflicting migrations. Runtime/workspace policy, tool restrictions, delegation budgets, provider requirements, and non-prompt hooks remain follow-up enforcement work.


What changed
This PR adds a provider-neutral Agents and Rules system to T3 Code. It is one end-to-end product feature spanning contracts, persistence, server orchestration, MCP, all five provider adapters, web/desktop, mobile, migrations, tests, and documentation.
Current size: 141 files, 18,461 additions, and 86 deletions. This exceeds automated approvability limits and requires careful human review; the passing correctness checks should not be mistaken for approval of a diff this large.
Suggested labels:
enhancement,documentation,size:XXL,needs-triage,preview:web, andcodex. The fork author does not have upstream triage permission, so GitHub only applied the labels available to its automation.Agent profiles and Rules
chatSelectabledistinguishes direct-chat Agents from delegation-only specialists. Existing pinned threads retain their Agent even if it is later hidden from new chats.t3.jsonreferences contained to the canonical project root; the catalog does not recursively scan repositories.Durable orchestration
AgentRundomain, append-only events, transactional projections, immutable profile snapshots, lineage queries, revision waits, deadlines, usage, results, follow-ups, cancellation, and integration.T3-owned Agent tools
Providers receive the same portable MCP toolkit:
agent_listagent_spawnagent_statusagent_waitagent_resultagent_sendagent_cancelagent_integrateTools are scoped to the invoking T3 project, thread, lineage, and run ownership. Launch is asynchronous and returns a durable run ID; result reads are bounded and paginated.
Provider-neutral boundary
Shared and isolated workspaces
git apply --check --3way, and then applies it.Web, desktop, mobile, and remote behavior
Why
ACP is a clean provider transport, but an ACP provider does not inherently know that it is running inside T3 and cannot reliably orchestrate another provider. Provider-native subagent systems also differ in naming, policy, lifetime, and availability.
This puts orchestration in the layer that has the required context: T3. A user can chat with an inexpensive coordinator, delegate architecture or implementation to specialized models, and keep every child run inspectable as an ordinary T3 thread without coupling core behavior to OpenGrok, GrokBuild, or any single provider.
UI evidence
Before: no Agent settings surface
After: first-party profile and Rule management
The selected specialist is marked delegation only. The host identifier is redacted from the public evidence image.
After: searchable picker beside the model picker
Search filters direct-chat profiles immediately. Delegation-only specialists remain available through orchestration but are absent from new top-level chat choices.
After: selected Agent shown in the composer
After: native Agent run in the parent Agents panel
After: file-aware Rules
Short picker interaction recording
Evidence is published on a separate fork branch so binary review artifacts do not enter the product diff.
Verification
EPERM.git diff --check: passed.1062149af.1062149af.Integrated browser coverage used an isolated
.t3environment and exercised:src/**/*.{ts,tsx}across save and reload;Honest scope and known limitations
chatSelectablecontrols discovery, not authorization. Delegation policy and provider compatibility remain execution gates..shmock-wrapper limitations on this Windows host. The complete OpenCode file hits the existing privileged-symlinkEPERMlimitation. The new provider assertions pass directly; Linux CI remains authoritative for those complete files.afterResultparticipates in the durable success decision and provider events must stay ordered per thread. A future optimization requires a keyed bounded scheduler with explicit drain and shutdown semantics.t3McpCapabilitiesis compatibility metadata, not an ACL.Checklist
Implemented with GPT-5.6 Sol through the Codex harness in T3 Code.
Note
Add provider-neutral agent orchestration with profile/rule management and MCP tool surface
AgentOrchestrationservice interface,AgentRunRepositoryfor durable event storage,AgentRunReactor/AgentRunDeadlineReactorfor state management and wall-time budget enforcement, andAgentPromptResolverfor profile-aware prompt compilation.AgentProfileStoreandAgentRuleStorefor filesystem-backed, revision-checked persistence of agent profiles and rules in Markdown+YAML frontmatter format, coordinated viaAgentProjectFileCoordinatorto avoid races.agents.catalog,agents.getProfile,agents.saveProfile,agents.archiveProfile,agents.restoreProfile, and rule equivalents) with scope-appropriate auth enforcement.agent_*MCP tools (agent_spawn,agent_wait,agent_result,agent_send,agent_cancel,agent_integrate, etc.) gated on a new'agents'MCP capability granted by default to all sessions./settings/agents) and mobile (SettingsAgents) apps, including catalog browsing, draft editing, save/archive/restore, and environment/project scoping.agent_profile_snapshots,projection_agent_runs,agent_run_eventstables and addingagent_profile_jsontoprojection_threads.'agents'capability by default; existing credential checks for'preview'are unaffected but all sessions gain access to agent tools without explicit opt-in.Macroscope summarized 502d128.
Note
Medium Risk
Changes thread bootstrap and outbox schema (agent profile pinning) and adds large new settings/orchestration surfaces; incorrect revision or selection merging could mis-pin agents on turns, though logic is covered by focused tests.
Overview
Mobile adds an Agents entry under Settings (stack route
agents) with a new screen to pick environment/project context, browse profile and rule catalogs, create and edit drafts, and save/archive/restore via existingagentEnvironmentRPC atoms. Supporting modules (agentProfile.logic,agentRule.logic,agentSettings.logic) handle document building, revision-safe hydration, and optimistic selection after saves.Composer flows now expose an Agent toolbar menu on new-task and thread composers. Choices are limited to chat-selectable profiles (with delegation-only profiles still visible when already pinned). Selecting a profile can apply its default model;
AgentProfileRefis stored on composer drafts, queued outbox messages, andthread.turn.start/ thread-creation bootstrap payloads, with explicit “No agent” preserved viaresolveAgentProfileSelection.Server (partial in this diff) introduces read-only AgentCatalog (environment Markdown dirs + explicit
t3.jsonproject refs, bounded RPC lists), AgentHookRunner for staged context/shell hooks, the AgentOrchestration service boundary, and orchestration lifecycle/integration test coverage; the orchestration integration harness stubs AgentPromptResolver when profiles are unused.Reviewed by Cursor Bugbot for commit 502d128. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit