Files
openchamber/packages/vscode/src/bridge-permission-auto-accept-runtime.ts
T
Bohdan Triapitsyn 85400459e9 perf: overhaul session loading, caching, and runtime isolation (#2360)
Improve OpenChamber responsiveness under large session workloads while fixing
cache, synchronization, and persistence correctness across runtimes, projects,
directories, and worktrees.

- prioritize selected and visible sessions during bootstrap and defer
  non-critical enrichment work
- reduce redundant message loading, event processing, store publication, and
  hidden sidebar work
- prevent stale session and message requests from overwriting newer
  authoritative state
- preserve existing data when authoritative fetches fail instead of treating
  failures as successful empty responses
- scope session materialization, messages, drafts, queues, todos, pins,
  permissions, folders, tabs, Git state, and pull request data by runtime and
  directory identity
- harden runtime switching, reconnect, cleanup, mutation reconciliation, and
  persisted-state ordering
- preserve live subagent Task linkage when metadata arrives after an older
  message request or while streaming parts are suspended
- coalesce overlapping tail refreshes without losing newer refresh demand
- improve cold-session loading by moving deferrable work out of the critical
  bootstrap path
- isolate URL authentication, mobile credentials, native secrets, and other
  runtime-owned state across endpoint changes
- bound long-lived caches and remove avoidable allocations from event and
  rendering hot paths
- limit virtualization to archive collections where it improves rendering
  without disrupting active sidebar layout
- stabilize session folders, pin ordering, expanded state, and persisted
  sidebar behavior
- open skill files through the same secure editor and outside-workspace grant
  flow used by file navigation, including worktree sessions
- expand regression coverage for stale completions, runtime collisions,
  reconnect behavior, persistence races, authoritative empty results, and
  subagent refresh ordering
- document the updated synchronization, cache ownership, performance, and
  runtime-isolation invariants
2026-07-21 20:52:20 +03:00

104 lines
3.9 KiB
TypeScript

const STORAGE_KEY = 'permissionAutoAccept';
type PolicyContext = {
globalState: {
get: (key: string) => unknown;
update: (key: string, value: unknown) => PromiseLike<void>;
};
};
export type PermissionAutoAcceptSnapshot = {
sessions: Record<string, boolean>;
revision: number;
};
const normalizeSnapshot = (value: unknown): PermissionAutoAcceptSnapshot => {
const source = value && typeof value === 'object' && !Array.isArray(value)
? value as { sessions?: unknown }
: {};
const entries = source.sessions && typeof source.sessions === 'object' && !Array.isArray(source.sessions)
? Object.entries(source.sessions)
: [];
const sessions: Record<string, boolean> = {};
for (const [sessionId, enabled] of entries) {
if (sessionId && typeof enabled === 'boolean') sessions[sessionId] = enabled;
}
const revision = Number.isSafeInteger((source as { revision?: unknown }).revision)
&& Number((source as { revision?: unknown }).revision) >= 0
? Number((source as { revision?: unknown }).revision)
: 0;
return { sessions, revision };
};
const readPermissionAutoAcceptPolicy = (context: PolicyContext) =>
normalizeSnapshot(context.globalState.get(STORAGE_KEY));
const operationQueues = new WeakMap<object, Promise<void>>();
const serialize = async <T>(context: PolicyContext, operation: () => Promise<T>): Promise<T> => {
const owner = context.globalState as object;
const previous = operationQueues.get(owner) ?? Promise.resolve();
let release!: () => void;
const current = new Promise<void>((resolve) => { release = resolve; });
const queued = previous.catch(() => undefined).then(() => current);
operationQueues.set(owner, queued);
await previous.catch(() => undefined);
try {
return await operation();
} finally {
release();
if (operationQueues.get(owner) === queued) operationQueues.delete(owner);
}
};
async function setPermissionAutoAcceptPolicy(
context: PolicyContext,
sessionId: string,
enabled: boolean,
broadcast: (snapshot: PermissionAutoAcceptSnapshot) => PromiseLike<unknown>,
) {
return serialize(context, async () => {
const current = readPermissionAutoAcceptPolicy(context);
const snapshot = {
sessions: { ...current.sessions, [sessionId]: enabled },
revision: current.revision + 1,
};
await context.globalState.update(STORAGE_KEY, snapshot);
await broadcast(snapshot);
return snapshot;
});
}
export async function handlePermissionAutoAcceptBridgeMessage(
message: { id: string; type: string; payload?: unknown },
context?: PolicyContext,
dependencies?: { broadcast: (snapshot: PermissionAutoAcceptSnapshot) => PromiseLike<unknown> },
) {
if (message.type !== 'api:permission-auto-accept:get' && message.type !== 'api:permission-auto-accept:set') {
return null;
}
if (!context) return { id: message.id, type: message.type, success: false, error: 'Extension context is unavailable' };
if (message.type === 'api:permission-auto-accept:get') {
const snapshot = await serialize(context, async () => readPermissionAutoAcceptPolicy(context));
return { id: message.id, type: message.type, success: true, data: snapshot };
}
const payload = message.payload && typeof message.payload === 'object'
? message.payload as { sessionId?: unknown; enabled?: unknown }
: {};
const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId.trim() : '';
if (!sessionId) return { id: message.id, type: message.type, success: false, error: 'sessionId is required' };
if (typeof payload.enabled !== 'boolean') {
return { id: message.id, type: message.type, success: false, error: 'enabled must be a boolean' };
}
const snapshot = await setPermissionAutoAcceptPolicy(
context,
sessionId,
payload.enabled,
dependencies?.broadcast ?? (() => Promise.resolve()),
);
return { id: message.id, type: message.type, success: true, data: snapshot };
}