mirror of
https://github.com/andy-stack/vaultkeeper-ai.git
synced 2026-07-22 06:42:03 +00:00
Add PlanApprovalService and PlanApprovalView to enable user review and approval of AI-generated execution plans before execution begins. Users can approve, reject, or suggest changes to plans through a new input mode and dedicated view.
95 lines
3 KiB
TypeScript
95 lines
3 KiB
TypeScript
import type VaultkeeperAIPlugin from 'main';
|
|
import { Resolve } from './DependencyService';
|
|
import { Services } from './Services';
|
|
import type { EventService } from './EventService';
|
|
import { Event } from 'Enums/Event';
|
|
import { Component } from 'obsidian';
|
|
import { AbortService } from './AbortService';
|
|
import type { ExecutionPlan } from 'Types/ExecutionPlan';
|
|
import { PlanApprovalResponse } from 'Types/PlanApprovalResponse';
|
|
|
|
export class PlanApprovalService extends Component {
|
|
|
|
private readonly plugin: VaultkeeperAIPlugin;
|
|
private readonly eventService: EventService;
|
|
private readonly abortService: AbortService;
|
|
|
|
private planResolve?: (response: PlanApprovalResponse) => void;
|
|
|
|
private ongoingApproval: boolean = false;
|
|
|
|
public constructor() {
|
|
super();
|
|
this.plugin = Resolve<VaultkeeperAIPlugin>(Services.VaultkeeperAIPlugin);
|
|
this.eventService = Resolve<EventService>(Services.EventService);
|
|
this.abortService = Resolve<AbortService>(Services.AbortService);
|
|
|
|
this.registerEvent(this.eventService.on(Event.PlanApprovalClosed, () => {
|
|
this.cancelPendingApproval();
|
|
}));
|
|
}
|
|
|
|
public async requestApproval(plan: ExecutionPlan): Promise<PlanApprovalResponse> {
|
|
this.ongoingApproval = true;
|
|
|
|
const signal = this.abortService.signal();
|
|
return new Promise<PlanApprovalResponse>((resolve, reject) => {
|
|
if (signal.aborted) {
|
|
this.finishApproval();
|
|
reject(this.abortService.reason());
|
|
return;
|
|
}
|
|
|
|
const abortHandler = () => {
|
|
this.finishApproval();
|
|
reject(this.abortService.reason());
|
|
};
|
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
|
|
this.planResolve = (response: PlanApprovalResponse) => {
|
|
signal.removeEventListener("abort", abortHandler);
|
|
resolve(response);
|
|
};
|
|
|
|
void this.plugin.activatePlanApprovalView(plan);
|
|
this.eventService.trigger(Event.PlanApprovalOpened);
|
|
});
|
|
}
|
|
|
|
public onApprove() {
|
|
if (this.planResolve) {
|
|
this.planResolve(new PlanApprovalResponse(true));
|
|
}
|
|
this.finishApproval();
|
|
}
|
|
|
|
public onReject() {
|
|
if (this.planResolve) {
|
|
this.planResolve(new PlanApprovalResponse(false));
|
|
}
|
|
this.finishApproval();
|
|
}
|
|
|
|
public onSuggest(suggestion: string) {
|
|
if (this.planResolve) {
|
|
this.planResolve(new PlanApprovalResponse(false, suggestion));
|
|
}
|
|
this.finishApproval();
|
|
}
|
|
|
|
private cancelPendingApproval() {
|
|
if (this.ongoingApproval) {
|
|
if (this.planResolve) {
|
|
this.planResolve(new PlanApprovalResponse(false));
|
|
}
|
|
this.finishApproval();
|
|
}
|
|
}
|
|
|
|
private finishApproval() {
|
|
this.ongoingApproval = false;
|
|
this.planResolve = undefined;
|
|
this.eventService.trigger(Event.PlanApprovalClosed);
|
|
}
|
|
|
|
}
|