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; return trimmed.length > 0 ? trimmed : null;
}; };
const collectPendingFromSyncStores = (sessionScope: Set<string>): Array<{ id: string; sessionID: string }> => { const collectPendingFromSyncStores = (): Array<{ id: string; sessionID: string }> => {
try { try {
const stores = getSyncChildStores(); const stores = getSyncChildStores();
const pending: Array<{ id: string; sessionID: string }> = []; const pending: Array<{ id: string; sessionID: string }> = [];
for (const store of stores.children.values()) { for (const store of stores.children.values()) {
const permissionMap = store.getState().permission ?? {}; const permissionMap = store.getState().permission ?? {};
for (const [sessionId, entries] of Object.entries(permissionMap)) { for (const [sessionId, entries] of Object.entries(permissionMap)) {
if (!sessionScope.has(sessionId)) continue;
for (const permission of entries ?? []) { for (const permission of entries ?? []) {
if (!permission?.id) continue; if (!permission?.id) continue;
pending.push({ id: permission.id, sessionID: permission.sessionID || sessionId }); 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 = ( const autoRespondsPermissionBySession = (
autoAccept: PermissionAutoAcceptMap, autoAccept: PermissionAutoAcceptMap,
sessions: Session[], 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 pendingFromApi = await opencodeClient.listPendingPermissions({ directories: Array.from(directories) });
const mergedPending = new Map<string, { id: string; sessionID: string }>(); const mergedPending = new Map<string, { id: string; sessionID: string }>();
for (const permission of pendingFromStores) { 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) { for (const permission of pendingFromApi) {
if (!permission?.id || !permission?.sessionID) { if (!permission?.id || !permission?.sessionID) {
continue; continue;
} }
if (!sessionScope.has(permission.sessionID)) { 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 }); mergedPending.set(permission.id, { id: permission.id, sessionID: permission.sessionID });
} }
+4
View File
@@ -67,6 +67,7 @@ function routeMessage(params: {
providerID: string providerID: string
modelID: string modelID: string
agent?: string agent?: string
agentMentionName?: string
variant?: string variant?: string
inputMode?: "normal" | "shell" inputMode?: "normal" | "shell"
files?: Array<{ type: "file"; mime: string; url: string; filename: string }> files?: Array<{ type: "file"; mime: string; url: string; filename: string }>
@@ -133,6 +134,7 @@ function routeMessage(params: {
modelID: params.modelID, modelID: params.modelID,
text: params.content, text: params.content,
agent: params.agent, agent: params.agent,
agentMentions: params.agentMentionName ? [{ name: params.agentMentionName }] : undefined,
variant: params.variant, variant: params.variant,
files: params.files, files: params.files,
additionalParts: params.additionalParts, additionalParts: params.additionalParts,
@@ -785,6 +787,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
providerID, providerID,
modelID, modelID,
agent: effectiveDraftAgent, agent: effectiveDraftAgent,
agentMentionName,
variant, variant,
inputMode, inputMode,
files, files,
@@ -860,6 +863,7 @@ export const useSessionUIStore = create<SessionUIState>()((set, get) => ({
providerID, providerID,
modelID, modelID,
agent: effectiveAgent, agent: effectiveAgent,
agentMentionName,
variant, variant,
inputMode, inputMode,
files, files,