File size: 3,159 Bytes
4e23b01 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | import { Disposable } from '#/_base/di/lifecycle';
import { LifecycleScope } from '#/app/scopes';
import { ScopeActivation, registerScopedService } from '#/_base/di/scope';
import { AsyncEmitter, type Event } from '#/_base/event';
import { defineState } from '#/state/state';
import { IAtomicDocumentStore } from '#/persistence/interface/atomicDocumentStore';
import { ISessionContext } from '#/session/sessionContext/sessionContext';
import { ISessionStateService } from '#/session/state/sessionState';
import {
ISessionToolPolicy,
type SessionToolPolicyChangedEvent,
} from './sessionToolPolicy';
interface SessionToolPolicyState {
readonly disabledTools: readonly string[];
}
export const sessionToolPolicyStateKey = defineState<SessionToolPolicyState>('sessionToolPolicy.state', () => ({
disabledTools: [],
}));
const STATE_KEY = 'state.json';
export class SessionToolPolicyService extends Disposable implements ISessionToolPolicy {
declare readonly _serviceBrand: undefined;
readonly ready: Promise<void>;
readonly onDidChange: Event<SessionToolPolicyChangedEvent>;
private readonly changeEmitter = this._register(
new AsyncEmitter<SessionToolPolicyChangedEvent>(),
);
private readonly scope: string;
private updateQueue: Promise<void> = Promise.resolve();
constructor(
@ISessionStateService private readonly states: ISessionStateService,
@ISessionContext sessionContext: ISessionContext,
@IAtomicDocumentStore private readonly store: IAtomicDocumentStore,
) {
super();
this.states.contributeState(sessionToolPolicyStateKey);
this.scope = sessionContext.scope('tool-policy');
this.onDidChange = this.changeEmitter.event;
this.ready = this.load();
}
private get state(): SessionToolPolicyState {
return this.states.get(sessionToolPolicyStateKey);
}
private set state(value: SessionToolPolicyState) {
this.states.set(sessionToolPolicyStateKey, value);
}
disabledTools(): readonly string[] {
return this.state.disabledTools;
}
setDisabledTools(names: readonly string[]): Promise<void> {
const run = this.updateQueue.then(() => this.replace(names));
this.updateQueue = run.catch(() => {});
return run;
}
private async load(): Promise<void> {
const stored = await this.store.get<SessionToolPolicyState>(this.scope, STATE_KEY);
if (stored !== undefined) {
this.state = { disabledTools: [...new Set(stored.disabledTools)] };
}
}
private async replace(names: readonly string[]): Promise<void> {
await this.ready;
const disabledTools = [...new Set(names)];
if (
disabledTools.length === this.state.disabledTools.length &&
disabledTools.every((name, index) => name === this.state.disabledTools[index])
) {
return;
}
const nextState = { disabledTools };
await this.store.set(this.scope, STATE_KEY, nextState);
this.state = nextState;
await this.changeEmitter.fireAsync({}, new AbortController().signal);
}
}
registerScopedService(
LifecycleScope.Session,
ISessionToolPolicy,
SessionToolPolicyService,
ScopeActivation.OnScopeCreated,
'sessionToolPolicy',
);
|