Skip to content

Sync upstream/main to d1b705a0c (8 commits) - #43

Merged
totally-tim merged 9 commits into
mainfrom
sync/upstream-main-2026-09-13
Sep 13, 2026
Merged

totally-tim merged 9 commits into
mainfrom
sync/upstream-main-2026-09-13

Conversation

@totally-tim

@totally-tim totally-tim commented Sep 13, 2026

Copy link
Copy Markdown
Owner

What was merged

upstream/main @ d1b705a0c — 8 commits past merge-base f22a37e6:

99 files changed. The v0.8.0 tag was already merged on main, so this sync takes upstream tip rather than a release tag.

Conflict-resolution decisions

Seven conflicts; resolved by ownership (upstream for infrastructure/correctness, fork for deliberate product differences):

  • directory-sync/internal/agent-store.ts — upstream moved transcript cleanup (timeline cursor, stream head/tail, authoritative history) from removeFromDirectory() into remove(). Took upstream's split; the fork's directory cleanup (details, queued messages, init state, permissions) stays in removeFromDirectory().
  • host-runtime.ts / host-runtime.test.ts — upstream deleted the requireAppHost capability gate entirely as part of Keep older daemons usable and let clients choose timeline recovery getpaseo/paseo#4737 (old daemons now connect in legacy mode instead of being rejected). Took upstream's removal, including the fork's Forkeo error text on that gate.
  • workspace-layout-store.ts — the substantive merge. Upstream removed pendingAgentIdsByWorkspace/resolvePendingAgent and made pinned tabs unconditional; the fork keeps continuationPendingIdsByWorkspace (handoff-successor tab protection). Resolution: dropped the obsolete upstream pending-ids mechanism, kept the fork's continuation-pending set, passed it as pinned ids into upstream's new reconcileWorkspaceTabs contract, and re-added knownAgentIds as an optional field on WorkspaceTabSnapshot/WorkspaceAgentVisibility so continuation protection can still tell an archived/known successor from a deleted one. Optional keeps upstream's literal test fixtures type-valid.
  • agent-manager.ts — combined fork's registerOnce/account-aware resume with upstream's runLifecycleMutation lane; kept fork's lease release in closeAgentRuntime; upstream's early session.close() ownership-retention adopted. See "Post-merge fix" below for the race this initially introduced.
  • codex-app-server-agent.ts — upstream's resume/session changes merged with the fork's account-aware Codex provider.

Post-merge fix (race found by tests, not by conflicts)

agent-manager.test.ts → "a stored-only archive closes a shared resume before a later protected load returns" failed deterministically after the merge. Root cause: the fork's await this.cancelContinuation(agentId) sat before runLifecycleMutation(...) in archiveSnapshot/archiveAgent. That one-microtask gap deferred lifecycleMutationTails.set past the point where a concurrent ensureUnarchivedAgentLoadedwaitForAgentClose reads the tail — so the protected load waited only on the in-flight resume, re-read storage before the archive's archivedAt write landed, and resolved instead of rejecting.

Fix: moved cancelContinuation inside the lane mutation in archiveAgent, archiveSnapshot, and cancelAgentRun (the latter uses the foreground lane; same hazard). cancelExisting never re-enters either lane (verified: it touches the continuation store, agentStorage.get, getAgent only), so this is deadlock-free and restores upstream's synchronous lane entry while keeping cancel-before-archive ordering. 196/196 in agent-manager.test.ts now pass.

Fork behaviors kept / deliberately diverged

  • Continuation queue + handoff (continuationPendingIdsByWorkspace, cancelContinuation in archive/cancel paths, registerOnce, account-aware resumeWithAccount, handoff labels/successor checks in agent-loading.ts) — verified via agent-handoff/runtime.test.ts and the continuation service tests.
  • Provider-account management (accounts, accountLeases, accountClients, account-aware requireClient).
  • Spawn-isolation fields on create/resume paths.
  • respondToPermission/AndWait — present upstream too; intact.
  • Forkeo branding retained where upstream rewrote fork-specific text.
  • projectGroups capability-gate wording kept per docs/fork.md (deliberate locale-churn avoidance).

Decisions

  • Old-host gating: follows upstream. Upstream Keep older daemons usable and let clients choose timeline recovery getpaseo/paseo#4737 removed the requireAppHost gate, so a host that predates owned subscriptions connects in legacy mode instead of failing with "Update the host to use this version of Forkeo." That gate was never a fork behavior: upstream added it in Keep plugin subscriptions independent and connections quiet getpaseo/paseo#4596 on 2026-09-11, the rename PR rebranded the string, and upstream deleted it a day later. No fork release tag (v1.3.1 or earlier) contains it. Re-adding it would block a new phone build from reaching a Mac still on v1.3.1 and would conflict on every sync of host-runtime.ts. Per-feature "Update the host to…" strings still nudge users when they hit a gated feature. The separate projectGroups gate wording is unaffected.
  • Nothing else unresolved: all conflicts resolved, all targeted tests green.

Validation

  • npm run build:server — clean (protocol/relay/highlight/plugin/server/cli declarations rebuilt before typecheck).
  • npm run typecheck — clean across all workspaces.
  • npm run lint — 0 warnings, 0 errors (4347 files).
  • npm run format — clean.
  • App batch: npx vitest run on 12 touched files (agent-directory-sync, workspace-subagents-integration, assistant-markdown-parser, local-file-attachment-store, workspace-recovery model, provider-account tests, agent-handoff runtime, others) — 382 passed.
  • Server batch: agent-manager.test.ts (196 passed, incl. the previously failing archive/resume race test), plus agent-loading.test.ts, agent-continuation/service.test.ts, agent-continuation/daemon.test.ts, workspace-archive-service.test.ts, snapshot-mutation-ownership.test.ts, lifecycle-command.test.ts, handoff-agent.test.ts88 passed.
  • Never ran a full workspace suite; no daemon restarts.

paseo-bot Bot and others added 9 commits September 11, 2026 18:53
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
…etpaseo#4736)

* fix(agents): restore archived Codex workspaces without losing focus

History selection must survive without cached agent records, and reading an archived Codex thread must not acquire an interactive writer. Keep workspace restore explicit, serialize native lifecycle transitions, and leave transcript lifetime with its owner.

* test(app): express Codex restore specs as user journeys

* test(app): finish restore fixture cleanup after teardown errors

* test(app): align restore coverage and await fixture readiness

Workspace restore leaves agents archived until explicit Unarchive. Keep that distinction covered for multiple agents. Compile the recovery fixture before its interaction test starts, and wait for sidebar agent hydration before asserting tab presence.
…o#4742)

* fix(app): keep incomplete Markdown formatted while streaming

Treat unfinished formatting at the growing assistant-message tail as provisional so delimiters and link destinations do not flash while tokens arrive. Completed messages retain ordinary Markdown parsing.

* test(app): express streaming Markdown checks as user actions
…etpaseo#4180)

* fix(server): probe Cursor thinking options per model

Cursor ACP only reports thought_level for the current model, so the catalog was copying Haiku's Off/On onto Grok 4.6 until the next probe happened to land on Grok.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(server): drop inherited thinking options when an ACP catalog probe fails

A rejected model switch was leaving the probe session's thinking list on the failed model, so Grok could still advertise Haiku's Off/On.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(server): probe per-model thinking when session/new has no thought_level

Composer/Auto as the Cursor ACP default omits thought_level, so the catalog helper skipped every model switch and Grok kept an empty thinking list.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(server): discover model options without changing preferences

Use the provider model catalog extension instead of probing model switches that persist user preferences. Keep provider-specific discovery in each shim and validate extension responses at the boundary.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
…etpaseo#4737)

* fix(subscriptions): preserve older-host workflows and explicit timeline recovery

Keep established app and SDK workflows available on older daemons without emulating independent directory filters. Restore live timeline membership without choosing the consumer's history recovery policy. Preserve daemon filesystem paths separately from legacy workspace IDs.

* fix(client): keep legacy workspace replies scoped to the requested page
…paseo#4756)

Keep plan proposals at their original timeline position through approval, rejection, and cancellation. Make plan cards collapsible while retaining readable history after follow-up prompts and daemon restart.
Decode clipboard image data URLs locally because native fetch rejects them. Add native-runtime and byte-integrity regression coverage, and clarify empty-clipboard guidance.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T07:50:59.617317Z e3f7689 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3f7689f88

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

read(entries: AgentEntry[], reset: boolean): Workspace[] {
if (reset) this.agents.clear();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid resetting live workspace state during one-shot reads

When an old daemon has an active workspace observation, any later first-page fetchWorkspaces() call also reaches this shared LegacyWorkspaces instance with reset = true, clearing agents accumulated for the live observation. A subsequent removal for an agent omitted from that one-shot page then produces no workspace_update, so the subscriber can retain a stale workspace indefinitely. Keep one-shot/page state separate from the aggregate backing the active legacy subscription, or only reset that aggregate when its subscribing query is replaced.

AGENTS.md reference: AGENTS.md:L139-L140

Useful? React with 👍 / 👎.

await this.drainSessionEvents(agentId);
// Retain ownership until shutdown succeeds. A failed close may still own a
// native writer, so publishing a resumable closed snapshot would orphan it.
await agent.session.close();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Roll back archive state when provider shutdown fails

When archiveAgent() reaches this call, markRecordArchived() has already persisted archivedAt and fired the archive lifecycle callback. If the provider's close() rejects, the new early throw retains the live runtime and reports archive failure while leaving the durable record archived; after a daemon restart the agent therefore disappears into History despite the failed operation, and retrying can fire the archive callback again. Either close before committing the archive transition or restore the prior record/callback state on this failure path.

Useful? React with 👍 / 👎.

Comment on lines +123 to +128
case "session.events.set_subscription.request":
if (!this.info.features?.explicitEventSubscriptions) return null;
return {
...query,
events: this.members(query.type, "events").filter(isLegacyEvent),
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Aggregate notification demand across legacy event observers

On a daemon that supports explicit events but not owned subscriptions, every new event observation replaces the connection-wide server slot. This request merges the surviving event names but retains only the newest observer's notifications value, so a later ordinary observation—such as directory or provider updates—can set notifications false even while the session's { notifications: true } attention feed remains active; release has the same problem because it rebuilds membership from the released query. Aggregate notification demand across all surviving event interests (true if any requests it) along with the event names.

AGENTS.md reference: AGENTS.md:L139-L140

Useful? React with 👍 / 👎.

@totally-tim
totally-tim merged commit fa1fbfe into main Sep 13, 2026
8 of 9 checks passed
@totally-tim totally-tim mentioned this pull request Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants