Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
b6863ae
fix(asktool): keep card drafts across session switches
Oct 20, 2026
36c1d15
fix(persistence): drop empty assistant rows regardless of status
Oct 20, 2026
1b1032c
fix(chat): open worktree references from the branch copy
zszz3 Oct 20, 2026
549b67b
fix(transcript): let an unpinned bookmark own the pane reveal
zszz3 Oct 20, 2026
31caf50
fix(composer): search past a truncated file index for @ mentions
zszz3 Oct 20, 2026
399dd26
fix(work-panel): keep the active tab's close button inside the strip
muzimu217 Oct 19, 2026
99bd282
fix(mcp): fail loudly when a server has no DCR endpoint
muzimu217 Oct 20, 2026
5aa66e2
fix(settings): keep the last successful market list when a query fails
muzimu217 Oct 16, 2026
a6f4ba1
feat(plugins): offer one line of composer suggestion from a plugin (#…
Tioit-Wang Oct 21, 2026
8f4285f
feat(transcript): unify turn folding and command preferences
Tioit-Wang Oct 21, 2026
434e06f
fix(e2e): escalate to SIGKILL when the certificate probe hangs
muzimu217 Oct 21, 2026
c82490c
fix(runtime): size the output cap from the folded request transcript
spacker-343 Oct 21, 2026
6cae2df
feat(transcript): add display modes and thinking shimmer
Oct 21, 2026
60817a5
test(transcript): align turn-process probe with Thinking defaults
Oct 21, 2026
f5b3502
fix(agent-runtime): skip expired OS trust anchors when merging the CA…
Oct 21, 2026
8576493
test(transcript): mock TextShimmer in session reference probe
Oct 21, 2026
ccc2823
Merge pull request #1798 from vastsa/feat/transcript-display-modes
vastsa Oct 21, 2026
338631e
fix(review): sync PR 1791 with current main
vastsa Oct 21, 2026
af7dd33
fix(review): sync PR 1783 with current main
vastsa Oct 21, 2026
739699e
fix(composer): sync deep search PR with current main
vastsa Oct 21, 2026
ea20433
fix(composer): search past a truncated file index for @ mentions
vastsa Oct 21, 2026
a564ed9
fix(chat): sync worktree reference PR with current main
vastsa Oct 21, 2026
bcce2fa
fix(chat): open worktree references from the branch copy
vastsa Oct 21, 2026
4ae7a05
fix(transcript): sync bookmark reveal PR with current main
vastsa Oct 21, 2026
f6974d6
fix(transcript): let an unpinned bookmark own the pane reveal
vastsa Oct 21, 2026
5c72221
fix(mcp): sync no-DCR OAuth PR with current main
vastsa Oct 21, 2026
cb5add2
fix(e2e): sync certificate probe timeout PR with current main
vastsa Oct 21, 2026
02aa567
fix(e2e): test certificate probe kill escalation
vastsa Oct 21, 2026
a142752
fix(mcp): localize missing DCR recovery
vastsa Oct 21, 2026
58da41a
Merge pull request #1791 from muzimu217/fix/mcp-oauth-no-dcr
vastsa Oct 21, 2026
5a0095d
Merge remote-tracking branch 'origin/main' into codex/review-pr-1792
vastsa Oct 21, 2026
18a416b
fix(transcript): fall back to the bookmarked offset when its row is u…
muzimu217 Oct 20, 2026
b73b6df
Merge pull request #1792 from muzimu217/fix/e2e-runner-kill-escalation
vastsa Oct 21, 2026
ee2f4fd
fix(host-runtime): expect the platform scratch separator in the launc…
SeanLiu326 Oct 21, 2026
caa76f2
fix(agent-runtime): tolerate the Windows symlink privilege in the ses…
SeanLiu326 Oct 21, 2026
d008073
feat(composer): toggle background task output from the whole row
vastsa Oct 21, 2026
533639f
docs(spec): restore the missing rationale cell on D669
vastsa Oct 21, 2026
c6db17b
fix(e2e): bound the client certificate baseline fetch
vastsa Oct 21, 2026
82ef18b
fix(e2e): bound the client certificate baseline probe
vastsa Oct 21, 2026
eb74d68
Merge pull request #1804 from vastsa/fix/taskbar-row-toggle
vastsa Oct 21, 2026
fb81b1f
fix(chat): parse positioned and ambiguous file references safely
SunkenCost Oct 21, 2026
986212f
fix(transcript): keep activity details visible after turns settle
SunkenCost Oct 21, 2026
551b0e9
test(transcript): align search test with group defaults
SunkenCost Oct 21, 2026
24c0894
fix(window): restrict recovery pinning to macOS
xuesecanmeng Oct 22, 2026
8956de0
fix(transcript): keep running marker inline
Oct 22, 2026
09534c5
fix(transcript): center running marker
Oct 22, 2026
7bfe753
Merge pull request #1811 from vastsa/fix/transcript-running-marker
Tioit-Wang Oct 22, 2026
caf8b2d
chore(models.dev): refresh catalog before beta.5
Oct 22, 2026
327d08c
test(provider): align DeepSeek window with catalog
Oct 22, 2026
1d71884
chore(release): v0.21.0-beta.6
Oct 22, 2026
657621d
Merge pull request #1813 from vastsa/chore/release-0.21.0-beta.6
Tioit-Wang Oct 22, 2026
c11f8ab
feat(transcript): flatten edit rows and restore group hierarchy
Oct 22, 2026
8c7cfd0
Merge pull request #1814 from vastsa/feat/edit-tool-two-level
Tioit-Wang Oct 22, 2026
45a73f2
fix(settings): size segmented controls to their content
vastsa Oct 22, 2026
fc8d396
Merge pull request #1816 from vastsa/fix/settings-process-display-width
vastsa Oct 22, 2026
c1f81ee
fix(agent-runtime): preserve executions when providers reuse tool IDs
Seraph-x Oct 22, 2026
4d5bb44
fix(live-voice): repair Gemini work setup and report protocol failures
Seraph-x Oct 22, 2026
f30a994
fix(desktop): keep toasts clear of the work panel
AuhNijGnahz Oct 22, 2026
b3baac3
Merge pull request #1819 from Seraph-x/fix/tool-repeats-and-gemini-live
vastsa Oct 22, 2026
cbed623
merge: incorporate latest main
vastsa Oct 22, 2026
2ec8c55
test(e2e): control toast focus transitions
vastsa Oct 22, 2026
8a95c8f
Merge pull request #1823 from AuhNijGnahz/fix/toast-work-panel-overlap
vastsa Oct 22, 2026
f71381b
fix(window): refresh PR candidate from current main
vastsa Oct 22, 2026
ce9bb4b
Merge pull request #1810 from xuesecanmeng/codex/fix-windows-alt-tab-…
vastsa Oct 22, 2026
92683fb
fix(transcript): refresh PR candidate from current main
vastsa Oct 22, 2026
8b1bc2b
Merge pull request #1808 from SunkenCost/fix/issue-1806-activity-disc…
vastsa Oct 22, 2026
845e765
fix(chat): refresh PR candidate from current main
vastsa Oct 22, 2026
8c3b4ac
Merge pull request #1807 from SunkenCost/fix/issue-1722-file-references
vastsa Oct 22, 2026
f398773
fix(transcript): restore bookmarks outside the mounted window
vastsa Oct 22, 2026
e0cedc3
fix(transcript): wait for history before applying fallback
vastsa Oct 22, 2026
101e8e3
fix(transcript): restore unmounted reading bookmarks
vastsa Oct 22, 2026
8c267ab
fix(agent-runtime): skip expired system trust anchors
vastsa Oct 22, 2026
64be4d7
Merge pull request #1799 from ls2023wsw/fix/system-ca-expired-anchors
vastsa Oct 22, 2026
a887bba
fix(runtime): budget against the folded request transcript
vastsa Oct 22, 2026
9a81252
Merge remote-tracking branch 'origin/main' into codex/review-pr1797
vastsa Oct 22, 2026
e747b35
Merge pull request #1797 from spacker-343/fix/output-cap-collapsed-sy…
vastsa Oct 22, 2026
47addd3
Merge remote-tracking branch 'refs/remotes/origin/pr-1776-head' into …
vastsa Oct 22, 2026
2598dca
Merge pull request #1776 from liubai0106/fix/asktool-draft-survives-s…
vastsa Oct 22, 2026
f3c5ba7
Merge remote-tracking branch 'refs/remotes/pr-review/1803-head' into …
vastsa Oct 22, 2026
d1df8bb
Merge pull request #1803 from SeanLiu326/fix/windows-unprivileged-tes…
vastsa Oct 22, 2026
7adb4e6
Merge remote-tracking branch 'refs/remotes/pr-review/1778-head' into …
vastsa Oct 22, 2026
6b22756
fix(persistence): retain one trusted empty completion row
vastsa Oct 22, 2026
3dd8d84
Merge pull request #1778 from liubai0106/fix/empty-complete-persist-g…
vastsa Oct 22, 2026
196d707
Merge remote-tracking branch 'refs/remotes/pr-review/1751-head' into …
vastsa Oct 22, 2026
8f07fad
fix(mcp-market): retain source badges in stale results
vastsa Oct 22, 2026
8c3c3d8
Merge pull request #1751 from muzimu217/fix/market-keep-last-results
vastsa Oct 22, 2026
f1f0f32
Merge remote-tracking branch 'refs/remotes/pr-review/1769-head' into …
vastsa Oct 22, 2026
55dac23
test(work-panel): cover tab close reachability after resize
vastsa Oct 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ resolver = "2"
[workspace.package]
edition = "2021"
license = "LGPL-3.0-or-later"
version = "0.21.0-beta.4"
version = "0.21.0-beta.6"
5 changes: 5 additions & 0 deletions apps/desktop/electron/main/bootstrap/shutdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { PersistenceOutbox } from "../persistence-outbox";
import type { PluginPanelHost } from "../plugin-panel-host";
import type { PluginRuntime } from "../plugin-runtime";
import type { PluginViewHost } from "../plugin-view-host";
import type { ComposerSuggestionRelay } from "../services/composer-suggestion";
import type { AppUpdaterController } from "../updater";
import type { UserMcpRuntime } from "../user-mcp";
import type { McpControlServer } from "../mcp-control";
Expand Down Expand Up @@ -45,6 +46,8 @@ export type ShutdownDependencies = {
mcpOAuth?: Pick<McpOAuthManager, "disposeAll">;
browserHost: Pick<BrowserHost, "dispose">;
pluginViews: Pick<PluginViewHost, "dispose">;
/** Composer suggestions still waiting for an answer at quit (ADR 0333). */
composerSuggestion?: Pick<ComposerSuggestionRelay, "dispose">;
updater: Pick<AppUpdaterController, "dispose" | "isInstallingUpdate">;
logger: Pick<Logger, "app">;
confirmQuitDialog: () => Promise<boolean>;
Expand All @@ -70,6 +73,7 @@ export function registerShutdownHandlers({
mcpOAuth,
browserHost,
pluginViews,
composerSuggestion,
updater,
logger,
confirmQuitDialog,
Expand Down Expand Up @@ -213,6 +217,7 @@ export function registerShutdownHandlers({
userMcp.disposeAll();
mcpOAuth?.disposeAll();
browserHost.dispose();
composerSuggestion?.dispose();
inflightCheckpointer.dispose();

try {
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/electron/main/bootstrap/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,8 +900,8 @@ export async function createWindow({
// so the reservation replan below is treated as an OS-owned adjustment.
windowState.workPanelUserMovePending = false;
applyWorkPanelReservation();
// Brief pin only when actively recovering from a shelf.
if (shelved || electronTiny) {
// Only macOS runs the watchdog that releases this temporary recovery pin.
if (process.platform === "darwin" && (shelved || electronTiny)) {
window.setAlwaysOnTop(true, "floating");
pinUntil = Date.now() + 4000;
} else {
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/electron/main/fs-index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { FS_INDEX_MAX_ENTRIES, getWorkspaceFileIndex } from "@pi-desktop/host-runtime";
export { FS_INDEX_MAX_ENTRIES, getWorkspaceFileIndex, searchWorkspaceFiles } from "@pi-desktop/host-runtime";
17 changes: 15 additions & 2 deletions apps/desktop/electron/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import { createSessionCoordination } from "./runtime/session-coordination";
import { createScheduledRuntime } from "./runtime/scheduled";
import { createDesktopServices } from "./services/desktop-services";
import { createPluginServices } from "./services/plugin-services";
import type { ComposerSuggestionAnswer } from "./services/composer-suggestion";
import { wirePluginThemeRuntimeServices } from "./plugin-theme-services";
import { createSessionCollaborationService } from "./services/session-collaboration";
import {
Expand Down Expand Up @@ -592,6 +593,8 @@ const {

/** sessionId → open host turn id, for turn bookkeeping across agent events. */
const activeTurns = new Map<string, string>();
/** Host-verified completion turns whose first settled reply may be silent. */
const completionNoticeTurns = new Set<string>();
/** Plan submission turns end without a task-complete notification. */
const planSubmissionTurnIds = new Set<string>();
/** sessionId → host execution id for an approved plan currently dispatched. */
Expand Down Expand Up @@ -638,8 +641,12 @@ const sessionCoordination = createSessionCoordination({
* begins running is reported with the durable turn id instead of being
* inferred from the previous turn's end.
*/
onTurnStarted: (sessionId: string, turnId: string) =>
pluginServices.pluginSession.noteTurnStarted(sessionId, turnId),
onTurnStarted: (sessionId: string, turnId: string) => {
pluginServices.pluginSession.noteTurnStarted(sessionId, turnId);
// A new turn owns the composer now, so a suggestion about the previous one
// stops being shown and stops being showable (ADR 0333).
pluginServices.composerSuggestion.noteTurnStarted(sessionId, turnId);
},
});
const {
activeTurnUsages,
Expand Down Expand Up @@ -725,6 +732,7 @@ const {
const eventPersistence = createEventPersistence({
runtimeState,
steeringReplies,
completionNoticeTurns,
activeTurns,
activeToolCalls,
activeToolCallKey,
Expand Down Expand Up @@ -884,12 +892,16 @@ function registerIpc() {
},
/** The shell reports the session on screen; plugin surfaces read it. */
setActivePluginSession: (next) => pluginServices.pluginSession.setActiveSession(next),
/** The shell answers a composer suggestion through the same invoke list. */
settleComposerSuggestion: (answer: ComposerSuggestionAnswer) =>
pluginServices.composerSuggestion.settle(answer),
getPluginLauncherWindow: () => mainState.pluginLauncherWindow,
togglePluginLauncher,
safeOpenExternal,
updater,
dataDir,
activeTurns,
completionNoticeTurns,
isTurnDispatchable,
sessionProjects,
persistenceOutbox,
Expand Down Expand Up @@ -1075,6 +1087,7 @@ registerShutdownHandlers({
mcpOAuth,
browserHost,
pluginViews,
composerSuggestion: pluginServices.composerSuggestion,
updater,
logger,
confirmQuitDialog,
Expand Down
13 changes: 13 additions & 0 deletions apps/desktop/electron/main/ipc/agent-ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type AgentIpcDependencies = {
persistenceOutbox: PersistenceOutbox;
dataDir: string;
activeTurns: Map<string, string>;
completionNoticeTurns: Set<string>;
recordTurnStart: (sessionId: string, turnId: string) => void;
/** Whether the named turn can still start or steer host work. */
isTurnDispatchable: (sessionId: string, turnId: string | null | undefined) => boolean;
Expand Down Expand Up @@ -87,6 +88,7 @@ export function registerAgentIpc({
persistenceOutbox,
dataDir,
activeTurns,
completionNoticeTurns,
recordTurnStart,
isTurnDispatchable,
activeTurnUsages,
Expand Down Expand Up @@ -515,6 +517,16 @@ export function registerAgentIpc({
} satisfies AgentEventEnvelope);

let result: { accepted: boolean; turnId: string };
const isTrustedCompletionNotice =
sessionMessage?.origin.kind === "completion" &&
sessionMessage.origin.targetSessionId === req.sessionId &&
Boolean(
sessionMessage.origin.messageId.trim() &&
sessionMessage.origin.replyToMessageId?.trim(),
);
// sessionMessage was resolved from the Host ledger above; keep this
// provenance Main-owned and tied to the durable turn, never the prompt text.
if (isTrustedCompletionNotice) completionNoticeTurns.add(durableTurnId);
try {
result = await sidecar.call<{ accepted: boolean; turnId: string }>(
"agent.prompt",
Expand Down Expand Up @@ -558,6 +570,7 @@ export function registerAgentIpc({
},
);
} catch (e) {
completionNoticeTurns.delete(durableTurnId);
await finishTurn(req.sessionId, "error", (e as any)?.errorCode, {
turnId: durableTurnId,
});
Expand Down
42 changes: 42 additions & 0 deletions apps/desktop/electron/main/ipc/plugin-ui-ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ import type { PluginPanelTheme } from "../../shared/plugin-panel-chrome";
import type { IpcRegistrar } from "./types";
import { pluginProviderCatalogEntries } from "../plugin-provider-catalog";
import { createSessionPushGate } from "../plugin-session-push-gate";
import {
PLUGIN_COMPOSER_SUGGESTION_REFUSALS,
type PluginComposerSuggestionRefusal,
} from "@pi-desktop/plugin-sdk";
import type { ComposerSuggestionAnswer } from "../services/composer-suggestion";

export type PluginUiIpcDependencies = {
registrar: IpcRegistrar;
Expand All @@ -27,6 +32,12 @@ export type PluginUiIpcDependencies = {
setActivePluginSession: (
next: { sessionId: string; projectPath: string | null } | null,
) => void;
/**
* The shell's answer to one composer suggestion (`pi.composer.suggest`): the
* main process is waiting on it, because only the renderer knows whether the
* draft could take the line.
*/
settleComposerSuggestion: (answer: ComposerSuggestionAnswer) => boolean;
};

/** Register plugin panels, work-panel views, themes and services. */
Expand All @@ -41,6 +52,7 @@ export function registerPluginUiIpc({
getUpdaterLocale,
getPluginPanelTheme,
setActivePluginSession,
settleComposerSuggestion,
}: PluginUiIpcDependencies): void {
const handle = (channel: string, fn: (...args: any[]) => Promise<any>) => {
registrar.handle(channel, fn);
Expand Down Expand Up @@ -86,6 +98,36 @@ export function registerPluginUiIpc({
return { ok: true };
},
);
/**
* The shell's answer to one composer suggestion (`pi.composer.suggest`).
*
* Nothing is refused here: the offer it answers may already have been
* replaced, dropped, or timed out, and a late answer to a forgotten offer is
* simply not applied. `ok` therefore says the message was well formed, not
* that a suggestion is on screen.
*/
handle(
IPC.invoke.pluginComposerSuggestionResult,
async (
payload:
| { requestId?: string | null; shown?: boolean | null; reason?: string | null }
| null,
) => {
const requestId = typeof payload?.requestId === "string" ? payload.requestId.trim() : "";
if (!requestId) throw new Error("INVALID_ARGUMENT: requestId is required");
const reason =
typeof payload?.reason === "string" &&
(PLUGIN_COMPOSER_SUGGESTION_REFUSALS as readonly string[]).includes(payload.reason)
? (payload.reason as PluginComposerSuggestionRefusal)
: undefined;
settleComposerSuggestion({
requestId,
shown: payload?.shown === true,
...(reason ? { reason } : {}),
});
return { ok: true };
},
);
handle(IPC.invoke.pluginOpenPanel, async (id: string) => {
const loaded = plugins.getLoaded(id);
if (!loaded) throw new Error("plugin not loaded");
Expand Down
9 changes: 8 additions & 1 deletion apps/desktop/electron/main/ipc/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { registerSpeechIpc } from "./speech-ipc";
import { registerVoiceIpc } from "./voice-ipc";
import { registerLiveVoiceIpc } from "./live-voice-ipc";
import type { LiveCallService } from "../live-voice/call-service";
import type { ComposerSuggestionAnswer } from "../services/composer-suggestion";
import type { LiveVoiceWidget } from "../live-voice/widget-window";
import type { IpcRegistrar } from "./types";
import type { createTraySessions } from "../tray-sessions";
Expand Down Expand Up @@ -64,9 +65,12 @@ export type RegisterIpcDependencies = {
setActivePluginSession: (
next: { sessionId: string; projectPath: string | null } | null,
) => void;
/** The shell's answer to a composer suggestion it was offered (ADR 0333). */
settleComposerSuggestion: (answer: ComposerSuggestionAnswer) => boolean;
activeUserSubagentDocuments: (...args: any[]) => Promise<any>;
disabledBuiltinSubagents: () => Promise<string[]>;
disabledBuiltinSkills: () => Promise<string[]>;
completionNoticeTurns: Set<string>;
liveCallService?: LiveCallService;
liveVoiceWidget?: LiveVoiceWidget;
restartForStorage: () => void;
Expand Down Expand Up @@ -99,12 +103,14 @@ export function registerIpcHandlers(dependencies: RegisterIpcDependencies) {
getNotificationViewingSessionId,
setNotificationViewingSessionId,
setActivePluginSession,
settleComposerSuggestion,
getPluginLauncherWindow,
togglePluginLauncher,
safeOpenExternal,
updater,
dataDir,
activeTurns,
completionNoticeTurns,
isTurnDispatchable,
sessionProjects,
persistenceOutbox,
Expand Down Expand Up @@ -425,6 +431,7 @@ export function registerIpcHandlers(dependencies: RegisterIpcDependencies) {
persistenceOutbox,
dataDir,
activeTurns,
completionNoticeTurns,
recordTurnStart,
isTurnDispatchable,
activeTurnUsages,
Expand Down Expand Up @@ -505,8 +512,8 @@ export function registerIpcHandlers(dependencies: RegisterIpcDependencies) {
getUpdaterLocale,
getPluginPanelTheme,
setActivePluginSession,
settleComposerSuggestion,
});

registerSpeechIpc({ registrar, speech });

if (voiceService) {
Expand Down
Loading
Loading