Commit Graph
5926 Commits
Author SHA1 Message Date
Mohamed Boudra ad4d910aef fix(markdown): retry transient image decode failures 2026-10-15 16:55:15 +02:00
Mohamed Boudra b88b018d5f fix(markdown): preserve literal image fallback labels 2026-10-15 16:54:07 +02:00
Mohamed Boudra f90e160ac6 fix(markdown): normalize HTML following code elements 2026-10-15 16:53:16 +02:00
Mohamed Boudra 65f16f6322 fix(markdown): preserve spacing between HTML elements 2026-10-15 16:52:02 +02:00
Mohamed Boudra e7589cbc86 fix(markdown): classify web links explicitly and complete renderer types 2026-10-15 16:42:14 +02:00
Mohamed Boudra e014113792 fix(markdown): preserve complete assistant messages on native 2026-10-15 16:40:41 +02:00
Mohamed Boudra f829ce6ef3 fix(markdown): keep the first image reference definition 2026-10-15 16:25:58 +02:00
Mohamed Boudra e851a71ded fix(markdown): normalize only parsed HTML nodes 2026-10-15 16:24:52 +02:00
Mohamed Boudra 9e275bf361 fix(markdown): size inline images to the line and collapse failed loads 2026-10-15 16:22:37 +02:00
Mohamed Boudra 58f2fed621 fix(markdown): keep the renderer tree stable while images resolve 2026-10-15 16:17:52 +02:00
Mohamed Boudra 8e1dd51ff0 fix(markdown): create renderer types when applying package patch
The published nightly omits both MarkdownRenderers type files. Represent them as new files so patch-package succeeds on a fresh npm install, matching the already patched development package.
2026-10-15 16:17:32 +02:00
Mohamed Boudra fca12a89d6 fix(markdown): normalize inline HTML before rendering 2026-10-15 16:08:55 +02:00
Mohamed Boudra 648f290687 fix(markdown): restore file link hover tooltips on web 2026-10-15 16:05:50 +02:00
Mohamed Boudra 8057a65f41 fix(markdown): restore selection highlight on web 2026-10-15 16:00:26 +02:00
Mohamed Boudra 134b796859 fix(markdown): derive table and task list colours from the theme 2026-10-15 15:57:03 +02:00
Mohamed Boudra f5fb20cc55 fix(markdown): preserve encoded image paths after rebase
Keep the shared renderer image normalization and file-read regression coverage that originally lived in the main integration merge.
2026-10-15 15:33:33 +02:00
Mohamed Boudra 307f0dee0d test(markdown): exercise find across whole-message history 2026-10-15 15:32:20 +02:00
Mohamed Boudra d2169a061c fix(markdown): preserve authored empty link titles 2026-10-15 15:32:20 +02:00
Mohamed Boudra ee4a5bca7a fix(markdown): avoid empty web history measurements 2026-10-15 15:32:19 +02:00
Mohamed Boudra b782963970 fix(markdown): retain diagram identity and reference scope 2026-10-15 15:32:19 +02:00
Mohamed Boudra 3d06f652b2 fix(markdown): restore diagrams and preserve link origin 2026-10-15 15:32:19 +02:00
Mohamed Boudra 554530a667 fix(markdown): preserve file links and image revisions 2026-10-15 15:32:19 +02:00
Mohamed Boudra 35302f7251 test: verify library image controls and isolate website config 2026-10-15 15:32:19 +02:00
Mohamed Boudra 096508240f feat(markdown): use enriched renderer across all platforms 2026-10-15 15:32:19 +02:00
Mohamed Boudra 3639084530 revert(app): remove shared prose-run renderer changes
Reverts ab4737bb2 and ac47e1063 before replacing the Markdown engine across all platforms.
2026-10-15 15:32:02 +02:00
Mohamed Boudra 2db3488dbd fix(app): preserve Markdown containers before grouping prose 2026-10-15 15:32:02 +02:00
Mohamed Boudra bf100e1752 fix(app): share selectable native prose across Markdown surfaces 2026-10-15 15:32:02 +02:00
Mohamed Boudra ab234d5f12 fix(app): skip prose classification for blocks over the render cap 2026-10-15 15:32:02 +02:00
Mohamed Boudra 633950bb6a fix(app): preserve bracketed IPv6 links in native prose 2026-10-15 15:32:02 +02:00
Mohamed Boudra 1fbdac728f fix(app): keep growing markdown on the streaming block renderer 2026-10-15 15:32:02 +02:00
Mohamed Boudra 9b7a6ba782 fix(app): preserve parser-owned links and streaming tilde tails 2026-10-15 15:32:02 +02:00
Mohamed Boudra aeaee471a6 fix(app): preserve home paths and normalized prose links 2026-10-15 15:32:02 +02:00
Mohamed Boudra 9bbfe29c0e fix(app): bound prose append work and preserve markdown edge cases 2026-10-15 15:32:02 +02:00
Mohamed Boudra d42e0ca00c fix(app): preserve provisional syntax at cached reveal boundaries 2026-10-15 15:32:02 +02:00
Mohamed Boudra eedf916023 fix(app): preserve native prose entities and links while bounding preparation cost 2026-10-15 15:32:02 +02:00
Mohamed Boudra 7023d20a5b fix(app): preserve markdown source and file actions in native prose
Prepare original source blocks with source-position edits from the assistant parser. Keep reference scope and live/history projection consistent, and retain link text when dispatching workspace file actions.
2026-10-15 15:32:02 +02:00
Mohamed Boudra 5bf0e4b588 fix(app): preserve inline image formatting and reference scope 2026-10-15 15:32:02 +02:00
Mohamed Boudra 9ef289e7f4 fix(app): allow additive task lists and admonitions in native prose 2026-10-15 15:32:02 +02:00
Mohamed Boudra ae649cf7ec docs: record native prose compatibility fixes and heading limits 2026-10-15 15:32:02 +02:00
Mohamed Boudra 83061e0312 fix(app): preserve markdown semantics and render limits in prose runs 2026-10-15 15:32:02 +02:00
Mohamed Boudra e831814d73 docs: explain native prose selection boundaries and tradeoffs 2026-10-15 15:32:01 +02:00
Mohamed Boudra 75907b360b fix(app): render selectable native prose with enriched markdown 2026-10-15 15:32:01 +02:00
Mohamed Boudra d8f79925d1 fix(app): group native assistant prose into selectable runs 2026-10-15 15:32:01 +02:00
paseo-bot[bot]andGrant Ammons 80badb3b8f fix(opencode): answer multi-select v2 questions from the question card (#6149)
Closes #6135

Co-authored-by: Grant Ammons <grant@grant.dev>
2026-10-15 15:15:19 +02:00
71f575e16a Keep malformed Claude question answers retryable (#6936)
* fix: reject a question permission that would deliver no answer

Answering an `AskUserQuestion` permission in any shape but
`updatedInput.answers` keyed by one of its questions resolves as `allow` and
delivers nothing. The waiting agent is told "The user did not answer the
questions." — an affirmative falsehood rather than silence, so neither side can
see that anything failed. The responder sees success; the agent believes the
human declined and acts on that.

The field it is most natural to reach for cannot work. `selectedActionId` is
`z.string().optional()` with no membership check, and is read only for `plan`
kinds; a question advertises `actions: undefined`, so there is no action id to
name in the first place.

It is also unrecoverable. `respondToPermission` deletes the request from
`pendingPermissions` on entry, so the malformed answer consumes it and the retry
fails with "No pending permission request". The guard runs before that delete,
and `AgentManager` drops its own copy only once the call resolves, so both maps
survive the throw and the request stays answerable.

The answer contract is per provider, so the guard is in the Claude provider
rather than in `AgentManager`. Claude keeps an answer only when its key is a
question's full text or its header and its value is a non-empty string; Codex's
`mapCodexQuestionResponseByHeader` reads headers only and deliberately selects
each question's first option when nothing maps, which a shared guard would have
rejected; OpenCode reads headers only as well.

`resolveClaudeAskUserQuestionAnswers` is extracted from
`normalizeClaudeAskUserQuestionUpdatedInput` with no change in behaviour, and
both it and the guard now use it. So "would this deliver anything" is answered
by the code that does the delivering, rather than by a second copy of its rule —
which matters more than it looks, because that rule has two details easy to
restate wrongly: `readNonEmptyString` tests `value.trim()` for emptiness but
returns the *original* string, so a question named `" Which colour? "` is keyed
by that padding; and an empty `updatedInput.questions` array is a list that wins
over the request's own questions and maps nothing, not an absent one.

Verified on a live daemon over the MCP `respond_to_permission` path against a
seat whose question text is literally `" Which colour? "`: `selectedActionId`,
the trimmed key, an empty echoed question list and a numeric answer are each
rejected with the request still pending, and
`{"answers":{" Which colour? ":"Ochre"}}` then reaches the agent.

One known limit: the WebSocket path (`session.ts handleAgentPermissionResponse`)
catches the throw and emits an `activity_log` error rather than failing the
call, because a permission response is modelled as a notification. So
`paseo permit allow` still prints `allowed` and exits 0 for a rejected shape.
The destructive half is closed on every path — the request survives and the
agent is never told a falsehood — but closing the reporting half needs a
protocol change.

* test(claude): cover the question-answer-required retry path

A non-deliverable answer to an AskUserQuestion permission is rejected, but
the pending request survives it: a corrected retry against the same request
id is accepted, and the waiting agent receives the answer.

Mutation testing on a fork found that the existing suite does not pin this
ordering — moving the deliverability check to run after the request is
deleted from pendingPermissions still leaves every existing test green. This
test reds against that change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Thomas Van <minhthong@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: paseo-bot <paseo-bot@users.noreply.github.com>
2026-10-15 14:21:03 +02:00
paseo-bot[bot]andplatanus-kr 0708b8295c Preserve explicit Claude session names on import (#6930)
* fix(server): name an imported Claude agent after its /rename

Importing a Claude session named the agent after its first prompt even when the
session had been renamed in the CLI, so the name had to be set again by hand.
#4216 fixed the Import Session listing; the imported agent still kept the
prompt.

Read the last usable `custom-title` from the transcript and pass it as the
imported config title. The record is re-emitted on every save, so searching
backwards from the end of the file finds the current name without walking a
transcript that can reach tens of megabytes.

Only an explicit rename is carried over. Agents are named after their first
prompt line since #1563, so adopting the generated `ai-title` here would put
that LLM summary back.

Refs #2513

* refactor(server): drop unused generality from the Claude title reader

readLastClaudeTitle took content, type and field so it could read both
custom-title and ai-title records, which the listing needed before #4216 took
that over. Only the custom-title read is left, so the two extra parameters are
dead flexibility that also broke the single-object-parameter rule in
packages/server/CLAUDE.md.

Collapse it to readLastClaudeCustomTitle(content) and pull the record type into
a constant so the needle and the type check share one literal.

Hooks skipped: the repo-wide typecheck fails on two pre-existing Electron
typing errors in packages/desktop/src/main.ts that reproduce with this change
stashed. packages/server typecheck, lint and format all pass.

* test(server): drive the real resume path in the import title tests

The tests assigned an inline arrow function to client.resumeSession to keep
Claude Code from spawning. Importing replays the transcript from disk and never
starts a process, so the stub was never needed. Dropping it runs the real
resume path, which is what docs/testing.md asks for, and removes the arrow
assignment that docs/coding-standards.md rules out.

Hooks skipped: the repo-wide typecheck fails on two pre-existing Electron
typing errors in packages/desktop/src/main.ts that reproduce with this change
stashed. packages/server typecheck, lint, format and the 83 claude tests pass.

* fix(server): narrow the transcript read error instead of asserting it

The ENOENT check cast the caught value to NodeJS.ErrnoException to reach
`code`, which is the kind of assertion docs/coding-standards.md rules out:
narrow with `if`, do not silence the compiler. Add the same local type guard
pid-lock.ts already uses and test through it.

Behaviour is unchanged. A value that is not an Error still falls through to
the rethrow, exactly as reading `code` off the assertion did.

Hooks skipped: the repo-wide typecheck fails on two pre-existing Electron
typing errors in packages/desktop/src/main.ts that reproduce with this change
stashed. packages/server typecheck, lint, format and the 83 claude tests pass.

* fix(server): let claudeProjectDirSync resolve the Claude config dir

The import title reader built the config dir itself through os.homedir(),
but os is no longer imported here, so the fallback threw a ReferenceError
whenever CLAUDE_CONFIG_DIR was unset. claudeProjectDirSync already resolves
the same default.

* test(server): give the import title helpers object parameters

* fix(server): preserve imported session renames across history locations

The import title reader must use the same provider environment and transcript
lookup as history replay. Otherwise an imported session has its history but
loses its explicit name when its configuration directory or project changed.

Co-authored-by: platanus-kr <platanus.kr@gmail.com>

---------

Co-authored-by: platanus-kr <platanus.kr@gmail.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-15 13:17:01 +02:00
paseo-bot[bot]andbwestlund17 4d5c3c13d4 fix(acp): preserve slash command argument hints (#6920)
ACP command updates discarded input hints before the autocomplete command list reached consumers. Preserve the advertised hint, retaining the empty hint for commands without input metadata.

Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: bwestlund17 <brian.westlund@proton.me>
2026-10-15 12:13:18 +02:00
Mohamed Boudra d3efddb0b5 test(server): await provider discovery before readiness assertion (#6994)
Repeated snapshot reads supersede pending launch resolution and keep readiness loading. Await the refresh RPC before asserting the discovered state.
2026-10-15 15:35:14 +07:00
paseo-bot[bot]andAlexander Adam 0ed57187a9 Keep fork owners in Gitea and Forgejo checkout names (#6829)
* fix(gitea): prefix fork PR branches with the owner

Two forks with the same branch name (say patch-1) ended up on the same
local branch. The gitea service had no buildPrLocalBranchName or
defaultCheckoutRefs, github has both.

getPullRequestCheckoutTarget also fell back to refs/heads/<name> on
origin for fork PRs. Thats the base repo's branch, not the fork's, so
a failed refs/pull fetch checked out the wrong branch without an error.

fixes #3515

* refactor(forge): share fork branch naming with github

moved the owner normalizer and the branch name builder to
utils/change-request-checkout.ts, gitea and github both use it.

github changes a bit: a fork PR with an unknown or unusable owner (eg
deleted fork) now gets pr-N/<branch> instead of the bare branch name,
which could collide with a local branch the same way #3515 did.

* fix(gitea): PRs from a deleted fork are still cross repo

the API drops head.repo once the fork is gone, we read that as a same
repo PR and fetched refs/heads/<name> from origin

* fix(gitea): handle AGit PRs and deleted head branches

neither has a branch on origin. forgejo reports refs/pull/N/head as
head.ref for both, so skip the refs/heads fallback and dont track
origin.

also try refs/pull/N/head on upstream for clones where origin is your
fork, like github does. that list lives in the shared util now.

* test: resolver harness uses the real branch helper

plus deleted forks and same repo PRs without an origin branch

* fix(gitea): collapse pull-ref-only heads to pr-N in local branch names

buildForkLocalBranchName still used the raw headRef even when it was
just the pull ref (refs/pull/N/head), which agit prs and deleted head
branches report as their headRefName. that gave local branches literal
names like refs/heads/refs/pull/3/head.

* fix(gitea): surface hasHeadBranch on the pr checkout target

resolveForkCheckoutFacts already knew whether a pr had a real head
branch (agit flow, or head.ref == the pull ref both mean no). now that
signal reaches buildPrLocalBranchName so worktree names and local
branches stop being named after the raw pull ref.

covers same-repo, fork with owner known, and deleted fork all with no
real head branch. added an end to end gitea-service test plus resolver
cases since the worktree slug follows localBranchName too.

* refactor(forge): one head ref kind on the checkout target

hasOriginHeadBranch and hasHeadBranch could describe contradictory
states (cross-repo checkout claiming an origin head branch). collapse
both into headRefKind: "branch" | "pull-ref" and derive origin
tracking in the resolver instead of storing it separately.

Co-authored-by: Alexander Adam <alexander.adam@vade.io>

* fix(worktrees): avoid branch namespace conflicts in fork checkouts

Fork owner prefixes can name an existing branch as a parent, which Git rejects even when the full proposed name is unused. Allocate names against parent and child refs, suffixing the conflicting segment while preserving existing branches and fetched commits.

* test(worktrees): make fork fixture line endings deterministic

Windows Git converts the fixture checkout to CRLF when core.autocrlf is enabled. Set the repository fixture to preserve its written LF bytes so the content assertion checks the fetched patch on every platform. Reproduced the failure locally with inherited core.autocrlf=true and verified the corrected setup against that same configuration.

---------

Co-authored-by: Alexander Adam <alexander.adam@vade.io>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
2026-10-15 10:10:54 +02:00
Mohamed Boudra a96c892302 fix(website): start the sponsors band heart at the column edge (#6993) 2026-10-15 15:04:10 +07:00