Repository navigation
v0.9.15: mship improvements, memory improvements, nextjs bump, plane integration - #8748
waleedlatif1 wants to merge 120 commits into
Conversation
…ead of extra E2B round trips (#8621) * perf(sandbox): grant the run_code session lease in the reconnect instead of extra E2B round trips Reused Mothership workbench calls made five E2B control-plane requests: list, connect, getInfo + setTimeout on acquire (the set always fired), and getInfo on release. Connect now asks for max(5 min, remaining, lease), the handle records the deadline it requested, and acquisition/release skip the provider while that lower bound covers the request. Final deadlines are unchanged; unrequested deadlines are still read back. * test(sandbox): model connect as setting the deadline so a dropped preserve is caught
The simple Chat effort picker labeled medium as Low, high as Medium, and xhigh as High. Derive its options from MOTHERSHIP_EFFORT_OPTIONS so each label names the effort it sends: Medium, High, Extra High. Values, the default (high), and the stored preference are unchanged.
…un --stop-after` (#8622) * feat(workflows): stop a manual v2 run after a block, and `workflows run --stop-after` A manual v2 run can now name `run.stopAfterBlockId`; the run stops once that block completes and downstream blocks do not execute. Combined with a block entry on the same block, it re-runs exactly one block against a prior run's persisted upstream outputs, server-side: sim workflows run W --from-block X --source-run R --stop-after X --select-output X.result Agents verifying an edit no longer re-run every upstream block (often a slow LLM or API call) or toggle blocks off to skip them. - Contract: optional `stopAfterBlockId` on the manual run selection. - Application: both manual operations refuse a block missing from the saved workflow or nested in a loop/parallel (the engine would otherwise run to the end or stop after one iteration), before anything runs. - Execute service: threads the trusted value to the sync and stream paths. - CLI: `--stop-after <blockId>` implies --manual and rejects --async. - E2E: test-workflow-stop-after-e2e.ts against a running app; the http-e2e job gains a Redis service because hosted billing admits runs through a Redis usage reservation. * fix(workflows): refuse stop targets a run cannot reach, and run the E2E self-hosted - The manual operations refuse a stop block the run cannot reach from its entry (an upstream block would let the run finish everything after the entry), and look blocks up as own properties. - The executor fails a run whose stop block is absent from the workflow it executes, instead of running everything; this closes the window between validation and the executor's own draft load, for every caller. - The CLI refuses an empty --stop-after rather than dropping it. - CI: the stop-after E2E gets its own self-hosted app step; the SCIM suite asserts PostgreSQL rate-limit storage, so Redis is not added to that app. Fixture cleanup waits for run logs to finalize before deleting. * fix(workflows): refuse a disabled stop block, or one reached only through one The executor omits disabled blocks from its graph, so a disabled stop target, or one whose only path runs through a disabled block, is never reached and the run would finish everything after the entry. * fix(workflows): the executor refuses a disabled stop block too The serialized workflow keeps disabled blocks, but the DAG skips them, so a disabled stop target would never be reached. --------- Co-authored-by: Waleed Latif <waleed@sim.ai>
…pletion (#8620) * fix(executor): stop retaining duplicate copies of loop block outputs * test(executor): guard output sharing in block logs and loop aggregates * fix(logs): size execution data the way JSON.stringify writes it * fix(logs): count JSON string bytes without copying and unbox primitive wrappers * fix(logs): measure execution data iteratively and apply toJSON on functions * fix(executor): drop the full JSON clone of execution state at run completion * fix(executor): normalize only live state for PII masking and walk serializability lazily * fix(executor): snapshot array lengths and unbox wrappers in JSON walks * fix(logs): read boxed boolean and bigint values the way JSON.stringify does
* feat(projects): add project identity and lifecycle foundation * feat(projects): create projects with their initial environment * docs(projects): record project files follow-up * docs(projects): explain project and workspace creation flows * fix(projects): stage activation after compatible writers deploy * refactor(projects): prepare compatible writers for the SQL backfill * fix(projects): clean up automatically created fixture Projects * fix(workflows): guard restore against concurrent workspace archive * fix(projects): close lifecycle races and surface rollout conflicts * fix(workflows): return not found when import loses archive race
* fix(mcp): restrict MCP server destination changes to admins * fix(mcp): compare exact MCP paths and guard concurrent URL changes * fix(mcp): treat setting a URL on a URL-less server as a destination change * fix(mcp): guard re-registration against concurrent URL changes * fix(mcp): narrow re-registration URL before the guarded update * chore(mcp): use absolute import in utils test
…v2 provider discovery (#8632)
…ck (#8635) * fix(executor): fail a stop-after run whose routing skips the stop block A run with stopAfterBlockId only stopped when the stop block completed. When a router, condition, or untaken error path routed the run away from it, the stop never triggered and the run finished every other branch, reporting success as if it had stopped there. A static check before the run cannot see this. - The engine ends the run as soon as every path into the stop block has been deactivated, before any further block starts, and fails it with `Stop block "<name>" (<id>) was not reached: no path this run took leads to it`. - Any run that ends without completing its stop block fails the same way: a stop block missing from the executed graph, or a Response block that ended the run first. - A loop or parallel stop with nothing to run completes at its start sentinel, whose end sentinel never runs, so that exit now counts as reaching it. - The v2 contract and the CLI `--stop-after` help describe the failure. - E2E: a condition fixture checks the stop on the taken branch still stops there, a stop on the skipped branch fails the run before the other branch's slow block finishes, and the CLI exits non-zero. * fix(executor): a skipped stop block fails a run another branch paused, and names a Response ending - A run whose stop block was proven unreachable fails even when another branch paused, instead of returning a paused run that would resume past it. - When a Response block ended the run first, the error says so rather than claiming no path leads to the stop block.
…le (#8631) * fix(projects): restore workspace deletion and tighten Project lifecycle - Archive a Project with its last active environment instead of refusing the workspace delete; account deletion follows the same rule, and the implicit archive is audited - Gate Project APIs on a `projects` AppConfig flag (PROJECT_API_ENABLED fallback) - Run Project reads in a read-only snapshot without locks; list Projects from the caller's grants with batched authorization - Batch workflow archival, Project transfer and owner reassignment; move Project ownership on organization ownership transfer - Reuse shared advisory-lock and text-array helpers; narrow admin-move conflict mapping to ProjectConflictError * improvement(projects): unify environment archive and align with shared patterns - Archive a workspace's workflows atomically with it through one archiveEnvironmentInTransaction shared by workspace delete and Project archive; the workspace row is locked before the sweep so concurrent creates are covered - Scope the Project lock timeout to lock acquisition and map lock timeouts and deadlocks to a retryable conflict; backfill and multi-Project locks use the shared advisory-lock helpers in code-unit order - Shared orchestrationFailureResponse for raw routes; contracts use the ID primitives and export only what is consumed; audit enums and mock in sync - Project restrictions section matches its sibling settings rows - Batch account-deletion Project loads/locks; skip inconsistent Projects in lists - Harden the foundation integration suite (user-keyed cleanup, poll helper, pid-scoped waits, precise assertions) * fix(projects): trim the requested organization id before validating it * fix(projects): address review on Project locking, archive notifications and list policy cost * fix(projects): keep archive retries from re-stamping MCP servers and isolate post-commit notifications
…m, restore Low (#8634) * feat(mothership): keep each chat's reasoning effort, default to medium, restore Low The simple picker offers Low / Medium / High / Extra High again, each sending exactly that effort. New chats and chats never changed run at medium instead of high. An effort the user picks is stored on the chat (copilot_chats.config) through a new PUT /api/mothership/chats/[chatId]/effort and at turn admission, and later turns of that chat keep it. The global last-used effort is no longer persisted. The Sim Chat block defaults to medium. * fix(mothership): keep the latest effort pick through refetches, failed saves and abandoned new chats * fix(mothership): leave a deduplicated send's chat on the pick its first attempt stored * fix(mothership): show a recovered chat's pick while its details load * fix(mothership): hand a withdrawn first send's effort pick back to the new-chat composer * fix(mothership): hand a withdrawn send's pick back only while its new-chat surface is open
… Tools Compared (#8638) Co-authored-by: Sim Pi Agent <pi@sim.ai>
… Integrations (#8639) Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
…8642) Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
…gine-optimization-actually-mean (#8647) Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
Co-authored-by: Sim Pi Agent <pi@sim.ai>
#8643) * fix(mothership): refuse desktop claims for unapproved or stopped calls The desktop authorize route claimed any pending call, so a gated terminal run that was still awaiting approval, or a call on a run the user had stopped, could be claimed and executed. The claim now locks the run row and refuses once tool admission has closed (Stop, a newer turn, or the run's end), and refuses a call held for the user's decision until they allow it. Whether a call is gated depends on the turn, so pre-persist records it on the row (permission_requested_at). Stop now settles the stopped runs' open desktop calls in the transaction that closes admission: unclaimed calls as never started, claimed calls as outcome unknown. A result for an already-settled call (a retry, or one that lost to Stop) is acknowledged with the stored outcome instead of 404/500. * fix(mothership): settle every open desktop call on Stop and answer 410 after it Stop also settles delivered desktop calls and reads of granted local folders. Authorize checks admission before the call's status, so a call Stop already settled answers 410 rather than 404. * test(mothership): assert the acknowledged outcome, not the publish mock * test(mothership): give the hand-built tool call table the new permission column * refactor(mothership): one claim primitive, one desktop-tool classifier, sealed Stop results The desktop claim is now an option of the run-locked tool execution claim (claimSimToolExecution becomes claimToolExecution) instead of a second copy of the admission check. Stop picks the open desktop calls with the shared TS classifier, which moves to lib/mothership/tools/desktop-tools.ts, instead of a SQL restatement of it, and seals each result the way the confirm route does, so a waiter restores what Stop did rather than failing to unseal it. * fix(mothership): a declined call stays unclaimable without its gate marker Calls gated before permission_requested_at existed carry no marker, so a recorded decision that does not allow the call now disqualifies it too. * test(mothership): assert authorize outcomes, not claim mock calls
…#8655) Picks queued behind an in-flight save run onMutate at once, so after low, high, low a failed first save dropped the newest low and the picker fell back to the stale server effort. Each pick now carries a token and a failed save drops only its own pick.
… JSON serializability (#8658) * fix(executor): unwrap boxed primitives by internal slot when checking JSON serializability * fix(executor): check the boxed slot first and convert wrappers with ToNumber/ToString
…ase its session renews (#8742) * fix(desktop): keep a chat-view import alive while it works, by the lease its session renews An import the chat view runs was failed as lost once it ran past the default tool budget (60 s plus the 30 s resume grace), though it was still uploading. Its claim now takes the execution lease under the claiming session, the chat view renews it through the existing lease route while the import runs, and the turn's wait budget runs to the end of that lease. Without renewals the lease lapses with the default budget, so a closed or crashed window still settles within about a lease. * fix(desktop): keep renewing an import's lease through transient failures, and retry a failed lease lookup - The chat view stops renewing only when the server refuses the call (410) - The resume watchdog retries a failed lease lookup for up to one lease instead of treating it as a lapse - The lifecycle tests assert what the agent is resumed with, and when * fix(desktop): cap a renewed import's wait at the client tool limit, renew at once, and report the extended wait - A chat-view import's lease extends its wait only up to the cap every client tool has (CLIENT_TOOL_RESULT_TIMEOUT_MS), so an import that hangs with its page alive still settles - The page renews the lease as soon as the import starts, then every heartbeat - The force-fail log names an extended wait and how long it lasted; the wait span's budget includes the extension - Tests for the cap, the bound on failed lease lookups, and the client heartbeat * fix(desktop): bound lease lookups, never fail a replaced call, and renew from the start of an import - Each lease lookup gets 5 s (and Stop) before it counts as failed, so a stalled read cannot hold the wait past its deadlines - A call replaced while its lease was read is left to its new watchdog - The page renews from the moment it asks for the manifest; a refusal counts only once the claim is confirmed, and renewing stops on every exit - Heartbeat tests check the lease a fake server holds, not request counts * fix(desktop): judge a lease refusal by whether the claim was confirmed when the renewal was sent * fix(desktop): renew an import's lease only after its claim, and give a capped call up without reading its lease The first heartbeat now comes one beat in, after the desktop's bounded claim, so every renewal follows the claim and a refusal always means the call was stopped, settled, or lapsed. A call at its ceiling is given up before its lease is read, and the force-fail log names whether the budget, the cap, a lapsed lease, or failed lease lookups ended the wait. * test(desktop): give the renewal test's lease room for a slow round trip
When an entry saved with an id on its Stop handoff also has its own resumeUserMessageId, the handoff's id is the one that build sent, so it is the one kept. resumeUserMessageId's doc names every path that sets it.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
We detected this is a high-risk PR and are running a free ultrareview. An ultrareview is a deeper, multi-pass review that catches hard-to-find bugs a standard review can miss. We'll post the findings when it completes. This PR appears to change concurrency-sensitive code such as locks, queues, or retries, where a missed race may only surface under production load, so a deeper multi-pass review is worth running. Want an ultrareview on every high-risk PR? Set up automated ultrareviews. |
|
There was a problem hiding this comment.
We detected this is a high-risk PR and ran a free ultrareview. An ultrareview is a deeper, multi-pass review that catches hard-to-find bugs a standard review can miss.
This PR appears to change concurrency-sensitive code such as locks, queues, or retries, where a missed race may only surface under production load, so a deeper multi-pass review is worth running.
Want an ultrareview on every high-risk PR? Set up automated ultrareviews.
17 issues found across 831 files
Confidence score: 2/5
shell.tscan compile placeholders inside arithmetic contexts it should reject: heredoc scans lose enclosing arithmetic state, and[[ ... -eq ... ]]operands aren’t detected. Preserve the enclosing context and detect arithmetic operands in[[ ]].outbox-events.tscan leave Stripe’scancel_at_period_endopposite the final database value when concurrent events finish out of order. Make updates converge on the latest subscription state.e2b.tscan promise a lease beyond E2B’s absolute sandbox lifetime, leaving the workbench unusable before the lease expires. Cap the deadline at the provider limit and recover or create a sandbox if the requested lease won’t fit.fold-table-row-changes/route.tscan start another page query after its deadline; a slow query can then hitmaxDurationand prevent the cron from reporting or folding the changes. Check the deadline before starting each query.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/docs/lib/openapi.ts">
<violation number="1" location="apps/docs/lib/openapi.ts:6">
P2: These top-level imports eagerly load all seven API documents for non-API docs pages as well, adding roughly 2.5 MB of spec data to normal server startup. Keep the API specs behind a lazy server-only module boundary while still bundling them for deployment.</violation>
<violation number="2" location="apps/docs/lib/openapi.ts:74">
P3: This repeats the same seven imports and filename map already maintained in `apps/docs/lib/openapi-download.ts`; future spec additions or renames now require synchronized edits in two modules. Move the shared map into one module and consume it from both callers.</violation>
</file>
<file name="apps/sim/app/api/v1/admin/workflows/import/route.ts">
<violation number="1" location="apps/sim/app/api/v1/admin/workflows/import/route.ts:127">
P2: `createWorkflowWithState` can throw a classified `conflict` after its name-race retries, but this route catches only `not_found`; concurrent imports therefore return a misleading 500. Map `conflict` to `conflictResponse` so callers receive a retryable 409.</violation>
<violation number="2" location="apps/sim/app/api/v1/admin/workflows/import/route.ts:164">
P2: The broad `not_found` branch mislabels a concurrent folder deletion as a missing workspace. Handle the helper's folder-not-found result separately before mapping the workspace archive race, preserving the folder-specific response.</violation>
</file>
<file name="apps/sim/ee/access-control/components/project-issue-restrictions.tsx">
<violation number="1" location="apps/sim/ee/access-control/components/project-issue-restrictions.tsx:28">
P3: This guard hides configured restrictions after their project is archived, leaving admins no way to see or remove the stale ID. Preserve selected project metadata in the UI or clean the ID when archiving the project.</violation>
<violation number="2" location="apps/sim/ee/access-control/components/project-issue-restrictions.tsx:50">
P3: An initial project-list failure leaves this editor without an in-place retry after React Query exhausts its retry. Render `SettingsQueryErrorState` with `projects.refetch` so admins can recover without reloading the settings page.</violation>
</file>
<file name="apps/sim/lib/execution/code-placeholders/shell.ts">
<violation number="1" location="apps/sim/lib/execution/code-placeholders/shell.ts:495">
P1: Each heredoc-body scan resets `arithmeticDepth`, so it loses an enclosing arithmetic substitution. Carry the enclosing arithmetic context into heredoc scans or reject body placeholders whose output can be consumed by outer arithmetic.</violation>
<violation number="2" location="apps/sim/lib/execution/code-placeholders/shell.ts:541">
P1: `[[ ... -eq ... ]]` is an arithmetic context, but this detector only recognizes delimiter-based forms, so placeholders there are compiled instead of rejected. Detect arithmetic operands in `[[ ]]` and the other shell arithmetic contexts before resolving the placeholder; otherwise values containing `$(...)` are re-evaluated by Bash.</violation>
</file>
<file name="apps/sim/lib/execution/remote-sandbox/e2b.ts">
<violation number="1" location="apps/sim/lib/execution/remote-sandbox/e2b.ts:1133">
P1: This reconnect can promise a lease past E2B’s absolute sandbox lifetime. Cap the usable deadline against the sandbox’s provider-limit deadline and create or recover a new workbench when the requested lease cannot fit.</violation>
</file>
<file name="apps/sim/app/api/copilot/tool-permission/route.ts">
<violation number="1" location="apps/sim/app/api/copilot/tool-permission/route.ts:141">
P3: `desktopDeviceId` identifies the whole turn, not the approved call, so this also rings the executor for non-desktop approvals such as `run_workflow`. The inbox filters those rows out, but every answer still causes an unnecessary authenticated inbox read/reconcile; gate the ring on `isDesktopToolCall(claimed.toolName, existing.args)` after normalizing the stored args.</violation>
</file>
<file name="apps/sim/app/_shell/consent/google-analytics-page-view-tracker.tsx">
<violation number="1" location="apps/sim/app/_shell/consent/google-analytics-page-view-tracker.tsx:17">
P2: This context update does not run for same-path query navigations, so Google can retain the previous sanitized URL and referrer after the URL changes. Track the search portion as well, such as by adding `useSearchParams()` (or an equivalent URL key) to the effect dependencies, and cover a query-only navigation.</violation>
</file>
<file name="apps/sim/app/api/cron/fold-table-row-changes/route.ts">
<violation number="1" location="apps/sim/app/api/cron/fold-table-row-changes/route.ts:31">
P2: `foldPendingTableRowChanges` can start another page query after its 45-second deadline. A slow query can consume the route's remaining 15 seconds and hit `maxDuration`, so the cron exits without reporting or folding the remaining tables; check the deadline before fetching each page or pass an absolute deadline into the helper.
(Based on your team's feedback about per-page sweep budgets.)</violation>
</file>
<file name="apps/sim/lib/billing/webhooks/outbox-events.ts">
<violation number="1" location="apps/sim/lib/billing/webhooks/outbox-events.ts:5">
P1: This event does not guarantee convergence when rows for one subscription run concurrently. An older DB read can finish its Stripe request after a newer update, leaving `cancel_at_period_end` opposite the final DB value; serialize or coalesce per-subscription processing, or recheck and repair before completing the event.</violation>
</file>
<file name="apps/sim/app/api/superuser/import-workflow/route.ts">
<violation number="1" location="apps/sim/app/api/superuser/import-workflow/route.ts:215">
P2: `createWorkflowWithState` can throw a classified `conflict` after exhausting its name-race retries, but this catch handles only `not_found` and returns a generic 500. Map `conflict` to 409 (or use the shared orchestration status mapping) so concurrent imports remain retryable.</violation>
</file>
<file name="apps/docs/app/llms-full.txt/route.ts">
<violation number="1" location="apps/docs/app/llms-full.txt/route.ts:7">
P2: `force-dynamic` removes the previous indefinite cache from this public, build-time docs endpoint. Each request now walks all bundled pages again, so crawler traffic repeatedly pays the full generation cost; keep the streamed response cacheable with an explicit public revalidation/CDN policy.</violation>
<violation number="2" location="apps/docs/app/llms-full.txt/route.ts:33">
P2: A `getLLMText` failure after the first chunk now terminates a response that has already been sent with status 200. Clients that do not surface body-stream errors can accept an incomplete documentation file; preserve an atomic error/status contract or expose an explicit terminal failure.</violation>
</file>
<file name="apps/sim/connectors/plane/utils.ts">
<violation number="1" location="apps/sim/connectors/plane/utils.ts:24">
P2: `parsePlanePage` discards the current page and fails the sync when Plane reports more results without an advancing cursor. Return valid results as non-authoritative partial progress with a notice to narrow the source instead of failing the account.</violation>
</file>
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
| @@ -488,6 +492,7 @@ function collectShellOccurrenceContexts( | |||
| { kind: 'root', quote: 'none', parenthesisDepth: 0, literalRoot }, | |||
| ] | |||
| let skippedRangeIndex = 0 | |||
| let arithmeticDepth = 0 | |||
There was a problem hiding this comment.
P1: Each heredoc-body scan resets arithmeticDepth, so it loses an enclosing arithmetic substitution. Carry the enclosing arithmetic context into heredoc scans or reject body placeholders whose output can be consumed by outer arithmetic.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/execution/code-placeholders/shell.ts, line 495:
<comment>Each heredoc-body scan resets `arithmeticDepth`, so it loses an enclosing arithmetic substitution. Carry the enclosing arithmetic context into heredoc scans or reject body placeholders whose output can be consumed by outer arithmetic.</comment>
<file context>
@@ -488,6 +492,7 @@ function collectShellOccurrenceContexts(
{ kind: 'root', quote: 'none', parenthesisDepth: 0, literalRoot },
]
let skippedRangeIndex = 0
+ let arithmeticDepth = 0
for (let index = start; index < end; ) {
</file context>
| @@ -533,6 +538,24 @@ function collectShellOccurrenceContexts( | |||
| } | |||
| continue | |||
| } | |||
| const arithmeticExpansion = | |||
There was a problem hiding this comment.
P1: [[ ... -eq ... ]] is an arithmetic context, but this detector only recognizes delimiter-based forms, so placeholders there are compiled instead of rejected. Detect arithmetic operands in [[ ]] and the other shell arithmetic contexts before resolving the placeholder; otherwise values containing $(...) are re-evaluated by Bash.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/lib/execution/code-placeholders/shell.ts, line 541:
<comment>`[[ ... -eq ... ]]` is an arithmetic context, but this detector only recognizes delimiter-based forms, so placeholders there are compiled instead of rejected. Detect arithmetic operands in `[[ ]]` and the other shell arithmetic contexts before resolving the placeholder; otherwise values containing `$(...)` are re-evaluated by Bash.</comment>
<file context>
@@ -533,6 +538,24 @@ function collectShellOccurrenceContexts(
}
continue
}
+ const arithmeticExpansion =
+ character === '$' &&
+ ((code[index + 1] === '(' && code[index + 2] === '(') || code[index + 1] === '[')
</file context>
| ) : undefined | ||
| } | ||
| > | ||
| {projects.error && ( |
There was a problem hiding this comment.
P3: An initial project-list failure leaves this editor without an in-place retry after React Query exhausts its retry. Render SettingsQueryErrorState with projects.refetch so admins can recover without reloading the settings page.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/ee/access-control/components/project-issue-restrictions.tsx, line 50:
<comment>An initial project-list failure leaves this editor without an in-place retry after React Query exhausts its retry. Render `SettingsQueryErrorState` with `projects.refetch` so admins can recover without reloading the settings page.</comment>
<file context>
@@ -0,0 +1,81 @@
+ ) : undefined
+ }
+ >
+ {projects.error && (
+ <p className='pl-2 text-[var(--text-error)] text-caption'>{projects.error.message}</p>
+ )}
</file context>
| return null | ||
| } | ||
| const choices = projects.data?.pages.flatMap((page) => page.projects) ?? [] | ||
| if (!projects.error && choices.length === 0) return null |
There was a problem hiding this comment.
P3: This guard hides configured restrictions after their project is archived, leaving admins no way to see or remove the stale ID. Preserve selected project metadata in the UI or clean the ID when archiving the project.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/ee/access-control/components/project-issue-restrictions.tsx, line 28:
<comment>This guard hides configured restrictions after their project is archived, leaving admins no way to see or remove the stale ID. Preserve selected project metadata in the UI or clean the ID when archiving the project.</comment>
<file context>
@@ -0,0 +1,81 @@
+ return null
+ }
+ const choices = projects.data?.pages.flatMap((page) => page.projects) ?? []
+ if (!projects.error && choices.length === 0) return null
+ const selected = new Set(value)
+ return (
</file context>
| decidedAt: claimed.permissionDecidedAt?.toISOString(), | ||
| }) | ||
| // A bound device lists the call for approval; the answer turns it into a call or drops it. | ||
| if (run.desktopDeviceId) ringDesktopInbox(run.desktopDeviceId, 'approval') |
There was a problem hiding this comment.
P3: desktopDeviceId identifies the whole turn, not the approved call, so this also rings the executor for non-desktop approvals such as run_workflow. The inbox filters those rows out, but every answer still causes an unnecessary authenticated inbox read/reconcile; gate the ring on isDesktopToolCall(claimed.toolName, existing.args) after normalizing the stored args.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At apps/sim/app/api/copilot/tool-permission/route.ts, line 141:
<comment>`desktopDeviceId` identifies the whole turn, not the approved call, so this also rings the executor for non-desktop approvals such as `run_workflow`. The inbox filters those rows out, but every answer still causes an unnecessary authenticated inbox read/reconcile; gate the ring on `isDesktopToolCall(claimed.toolName, existing.args)` after normalizing the stored args.</comment>
<file context>
@@ -136,6 +137,8 @@ async function applyDecision(
decidedAt: claimed.permissionDecidedAt?.toISOString(),
})
+ // A bound device lists the call for approval; the answer turns it into a call or drops it.
+ if (run.desktopDeviceId) ringDesktopInbox(run.desktopDeviceId, 'approval')
return { toolCallId, decision, applied: true }
</file context>
… a closed session's input marker (#8753)
* fix(desktop): stop a run the model never got the result of * fix(desktop): never stop a run the model may hold * fix(desktop): only a recovered result leaves a duplicate open * fix(desktop): keep a recovered result's mark while it is parked
…fort across a failed first send (#8754) * fix(mothership): close a pre-aborted SSE stream, keep the new-chat effort across a failed first send - createSSEStream closes at once when the request aborted before start(), instead of subscribing until rotation. - The new-chat effort pick is dropped by useChat when its chatless surface is left, not by each composer's unmount, so a failed first send keeps it and the pending chat view shows it. - The chat response's effort is optional, so a new client loads chats from a server that predates it. * fix(mothership): drop the new-chat effort when the surface adopts a chat A first send stopped before admission adopted its chat without moving the pick, so the next new chat on the same Home mount showed and sent it. adoptResolvedChatId now drops the pick when the surface leaves the new chat. The rollback restore is gone: nothing clears the pick while a send is pending, and it overwrote a pick made during the send.
…the none effort (#8752) * improvement(mothership): relabel the Sol pick GPT-6.1 Sol and retire the none effort * improvement(mothership): check the effort table against the outgoing request
* feat(providers): add Nebius Token Factory * fix(nebius): normalize unsupported forced tools
* ci: shard integration and unit tests, run e2e groups in parallel, add a ci gate
The PR critical path was the PostgreSQL integration suite (~15 min), run in full on an
8 vCPU runner once per provisioning path. Its files run one at a time, so the runner sat
mostly idle.
- integration: each provisioning path (push, migrate) is split into 4 Vitest shards on
4 vCPU runners. Both paths keep the full suite: migrations add triggers, checks and
NOT VALID constraints that db:push does not, so the schemas differ.
- e2e: the four next-dev groups (scim, cli, stop-after, desktop-inbox) run as a matrix,
each on its own database. The boot/wait/stop shell lives once in http-e2e.sh.
- lint: lint, audits, type-check and schema sync split off from the tests.
- test: apps/sim unit tests sharded 2 ways; shard 1 also runs root scripts and the other
workspaces. Each shard has its own Turbo cache disk.
- ci: one aggregate job that fails unless every check passed (skipped is allowed), so a
ruleset can require a single stable check.
Deploy gating is unchanged: migrate still requires the whole Test and Build workflow.
* ci: short names, one setup action, aligned pins, explicit secrets
Naming
- Workflow files: test-build -> checks, migrations -> migrate, deploy-trigger-dev ->
trigger-dev, docs-embeddings -> docs, companion-pr-check -> companion, stickydisk-gc
-> disk-gc. ci.yml, helm.yml and codeql.yml keep their paths: they sign or analyze, and
the path is part of the signer identity and code-scanning key.
- Composite actions: setup-workspace -> setup, cache-mount -> cache, docker-build -> image.
- Every workflow and job display name is a short lowercase id, matching the job id, with
any matrix value in parentheses: ci / checks / integration (push, 1/4), image-amd64 (app).
Duplicates
- Nine hand-written Setup Bun + actions/cache + bun install blocks use the setup action.
It gains node-version (empty keeps the runner's Node, as those jobs had) and registry-url
(npm publishing). This also ends restoring node_modules from another lockfile through the
`${runner.os}-bun-` prefix restore key.
- The runner expression is anchored once per file and aliased after.
Consistency
- One SHA per action: checkout v6 (helm was on v4, codeql on v5), setup-node v6 (desktop
on v4), cache v5 (desktop-release on v4), softprops/action-gh-release v3.0.3 (was v1,
Node 16). Redis 8.2 everywhere.
- secrets: inherit replaced by the secrets each callee reads; the checks workflow reads none.
The dev migration gets only DEV_DATABASE_URL, and staging/production never see it.
- Timeouts on the three macOS desktop-e2e jobs (none before, so a hang billed 6 hours),
and a cache for their Electron, electron-builder and Playwright downloads.
- Publish workflows: values moved from ${{ }} in run blocks to env, concurrency on the
npm/PyPI publishers, persist-credentials: false on checkouts that never push.
- Dependabot for github-actions (one grouped weekly PR), and actionlint in the lint job.
Fixes
- PyPI version check matched substrings (0.1.1 "existed" once 0.1.10 did) and treated a
PyPI outage as "not published". It now asks PyPI for the exact version and fails on
anything but 200 or 404.
Job wiring (runner, if, needs, permissions, timeout, outputs) is unchanged: verified by
loading the old and new ci.yml, checks.yml and helm.yml and comparing every job.
* ci: run the ci gate on cancelled runs so a cancelled head never passes
* docs(desktop): point the release notes at the renamed workflow and jobs
Uh oh!
There was an error while loading. Please reload this page.