fix: restore agent mentions and child permission auto-accept

Sends tagged agents as OpenCode agent parts
Auto-accepts pending child-session permissions from parent sessions
This commit is contained in:
Bohdan Triapitsyn
2026-05-14 15:30:10 +03:00
parent 34fde831eb
commit 5313494e5a
2 changed files with 80 additions and 5 deletions
+76 -5
View File
@@ -107,14 +107,13 @@ const normalizeDirectoryCandidate = (value: unknown): string | null => {
return trimmed.length > 0 ? trimmed : null;
};
const collectPendingFromSyncStores = (sessionScope: Set<string>): Array<{ id: string; sessionID: string }> => {
const collectPendingFromSyncStores = (): Array<{ id: string; sessionID: string }> => {
try {
const stores = getSyncChildStores();
const pending: Array<{ id: string; sessionID: string }> = [];
for (const store of stores.children.values()) {
const permissionMap = store.getState().permission ?? {};
for (const [sessionId, entries] of Object.entries(permissionMap)) {
if (!sessionScope.has(sessionId)) continue;
for (const permission of entries ?? []) {
if (!permission?.id) continue;
pending.push({ id: permission.id, sessionID: permission.sessionID || sessionId });
@@ -127,6 +126,68 @@ const collectPendingFromSyncStores = (sessionScope: Set<string>): Array<{ id: st
}
};
const sessionBelongsToScope = async (
sessionID: string,
rootSessionID: string,
knownSessions: Session[],
directories: string[],
): Promise<boolean> => {
if (sessionID === rootSessionID) {
return true;
}
const knownById = new Map<string, Session>();
for (const session of knownSessions) {
knownById.set(session.id, session);
}
const fetchedById = new Map<string, Session>();
const fetchSession = async (id: string): Promise<Session | null> => {
const known = knownById.get(id) ?? fetchedById.get(id);
if (known) return known;
for (const directory of directories) {
try {
const result = await opencodeClient.getScopedSdkClient(directory).session.get({
sessionID: id,
directory,
});
if (result.data) {
fetchedById.set(id, result.data);
return result.data;
}
} catch {
// Try the next known project directory.
}
}
try {
const result = await opencodeClient.getSdkClient().session.get({ sessionID: id });
if (result.data) {
fetchedById.set(id, result.data);
return result.data;
}
} catch {
// Missing session metadata means we cannot safely inherit the parent setting.
}
return null;
};
const seen = new Set<string>();
let current: string | undefined = sessionID;
while (current && !seen.has(current)) {
if (current === rootSessionID) {
return true;
}
seen.add(current);
const session = await fetchSession(current);
current = session?.parentID ?? undefined;
}
return false;
};
const autoRespondsPermissionBySession = (
autoAccept: PermissionAutoAcceptMap,
sessions: Session[],
@@ -204,19 +265,29 @@ export const usePermissionStore = create<PermissionStore>()(
}
}
const pendingFromStores = collectPendingFromSyncStores(sessionScope);
const directoryList = Array.from(directories);
const pendingFromStores = collectPendingFromSyncStores();
const pendingFromApi = await opencodeClient.listPendingPermissions({ directories: Array.from(directories) });
const mergedPending = new Map<string, { id: string; sessionID: string }>();
for (const permission of pendingFromStores) {
mergedPending.set(permission.id, permission);
if (sessionScope.has(permission.sessionID)) {
mergedPending.set(permission.id, permission);
continue;
}
if (await sessionBelongsToScope(permission.sessionID, sessionId, sessions, directoryList)) {
mergedPending.set(permission.id, permission);
}
}
for (const permission of pendingFromApi) {
if (!permission?.id || !permission?.sessionID) {
continue;
}
if (!sessionScope.has(permission.sessionID)) {
continue;
const belongsToScope = await sessionBelongsToScope(permission.sessionID, sessionId, sessions, directoryList);
if (!belongsToScope) {
continue;
}
}
mergedPending.set(permission.id, { id: permission.id, sessionID: permission.sessionID });
}
+4
View File
@@ -67,6 +67,7 @@ function routeMessage(params: {
providerID: string
modelID: string
agent?: string
agentMentionName?: string
variant?: string
inputMode?: "normal" | "shell"
files?: Array<{ type: "file"; mime: string; url: string; filename: string }>
@@ -133,6 +134,7 @@ function routeMessage(params: {
modelID: params.modelID,
text: params.content,
agent: params.agent,
agentMentions: params.agentMentionName ? [{ name: params.agentMentionName }] : undefined,
variant: params.variant,
files: params.files,
additionalParts: params.additionalParts,
@@ -785,6 +787,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
providerID,
modelID,
agent: effectiveDraftAgent,
agentMentionName,
variant,
inputMode,
files,
@@ -860,6 +863,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
providerID,
modelID,
agent: effectiveAgent,
agentMentionName,
variant,
inputMode,
files,