Q195: a change reloads only what is not already shown, and the snapshot stops carrying records the window never reads
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2f79e4261f
commit
56eac39b1b
31 files changed
+146
-214
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { bumpDataVersion, currentDataVersion } from "../services/data-version";
|
||||
import { createIpcGate } from "./ipc-gate";
|
||||
import { app, dialog, shell, type BrowserWindow } from "electron";
|
||||
import type { DatabaseConnection } from "../database/connection";
|
||||
@@ -69,13 +70,18 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
const integrationKeys = new IntegrationKeyStore(deps.connection.db);
|
||||
const providerOAuth = new ProviderOAuthService(integrationKeys);
|
||||
const skills = new SkillService(deps.connection.db, deps.artifactRoot);
|
||||
const notifyDataChanged = coalesce(() => {
|
||||
const announceChange = coalesce(() => {
|
||||
try {
|
||||
if (!deps.mainWindow.isDestroyed() && !deps.mainWindow.webContents.isDestroyed()) {
|
||||
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
||||
deps.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed", version: currentDataVersion() });
|
||||
}
|
||||
} catch { /* renderer may have been disposed during agent run */ }
|
||||
}, 200);
|
||||
// The version moves at once, so the snapshot an action returns already carries the change its own event announces.
|
||||
const notifyDataChanged = () => {
|
||||
bumpDataVersion();
|
||||
announceChange();
|
||||
};
|
||||
const taskExecution = new TaskExecutionService(deps.connection.db, providers, skills, notifyDataChanged);
|
||||
const social = new SocialService(deps.connection.db);
|
||||
const files = new FileService(deps.connection.db, deps.artifactRoot);
|
||||
@@ -456,11 +462,12 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
handle("disconnectComposioToolkit", async (input) => {
|
||||
try {
|
||||
await composioConnections.disconnectToolkit(input.orgId, input.toolkitSlug, input.label);
|
||||
notifyDataChanged();
|
||||
return ok(snapshot.load());
|
||||
} catch (error) {
|
||||
return fail("composio_disconnect_failed", error instanceof Error ? error.message : String(error));
|
||||
} finally {
|
||||
// A disconnect that failed partway may still have written; the page reloads either way.
|
||||
notifyDataChanged();
|
||||
return fail("composio_disconnect_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
});
|
||||
handle("grantConnector", (input) => mutate(() => new ConnectorGrantsService(deps.connection.db).grantFromBoard(input)));
|
||||
@@ -478,11 +485,11 @@ export function registerIpcHandlers(deps: IpcDeps): void {
|
||||
handle("refreshComposioAccounts", async (input) => {
|
||||
try {
|
||||
await composioConnections.refreshAccounts(input.orgId);
|
||||
notifyDataChanged();
|
||||
return ok(snapshot.load());
|
||||
} catch (error) {
|
||||
return fail("composio_refresh_failed", error instanceof Error ? error.message : String(error));
|
||||
} finally {
|
||||
notifyDataChanged();
|
||||
return fail("composio_refresh_failed", error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
});
|
||||
handle("windowMinimize", () => deps.mainWindow.minimize());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { bumpDataVersion } from "../services/data-version";
|
||||
import type { BrowserWindow } from "electron";
|
||||
import type { DatabaseSync } from "node:sqlite";
|
||||
import { IPC_CHANNELS } from "../../shared/ipc";
|
||||
@@ -22,7 +23,7 @@ export function startProviderBackgroundWorker(input: ProviderBackgroundWorkerInp
|
||||
try {
|
||||
const changedCount = await service.recoverBackgroundResponses();
|
||||
if (changedCount > 0 && !input.mainWindow.isDestroyed()) {
|
||||
input.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
||||
input.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed", version: bumpDataVersion() });
|
||||
}
|
||||
} finally {
|
||||
running = false;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { bumpDataVersion } from "../services/data-version";
|
||||
import type { BrowserWindow } from "electron";
|
||||
import type { DatabaseConnection } from "../database/connection";
|
||||
import { IPC_CHANNELS } from "../../shared/ipc";
|
||||
@@ -80,7 +81,7 @@ export function claimNextQueuedRun(connection: DatabaseConnection, excludedProvi
|
||||
export function startTaskQueueWorker(input: TaskQueueWorkerInput): () => void {
|
||||
const notifyDataChanged = () => {
|
||||
if (!input.mainWindow.isDestroyed()) {
|
||||
input.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed" });
|
||||
input.mainWindow.webContents.send(IPC_CHANNELS.rendererEvent, { type: "snapshot-changed", version: bumpDataVersion() });
|
||||
}
|
||||
};
|
||||
const taskExecution = input.taskExecution ?? new TaskExecutionService(input.connection.db, undefined, undefined, notifyDataChanged);
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
let version = 0;
|
||||
|
||||
/** Marks a change the window should show; snapshots and change events carry the count so the window never reloads what it already has. */
|
||||
export function bumpDataVersion(): number {
|
||||
version += 1;
|
||||
return version;
|
||||
}
|
||||
|
||||
export function currentDataVersion(): number {
|
||||
return version;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { currentDataVersion } from "./data-version";
|
||||
import { loadViewState } from "./view-state";
|
||||
import { loadOnboardingDraft } from "./onboarding-progress";
|
||||
import type { DatabaseSync, StatementSync, SQLInputValue } from "node:sqlite";
|
||||
@@ -71,32 +72,17 @@ export class SnapshotService {
|
||||
.map((provider) => ({ ...provider, enabled: toBool(provider.enabled), backgroundEnabled: toBool(provider.backgroundEnabled) }));
|
||||
const providerAuthProfiles = list<AppSnapshot["providerAuthProfiles"][number]>(this.db, "provider_auth_profiles", "created_at desc")
|
||||
.map((profile) => ({ ...profile, active: toBool(profile.active) }));
|
||||
const providerAuthSessions = list<AppSnapshot["providerAuthSessions"][number]>(this.db, "provider_auth_sessions", "created_at desc");
|
||||
const providerRateLimitObservations = list<AppSnapshot["providerRateLimitObservations"][number]>(
|
||||
this.db,
|
||||
"provider_rate_limit_observations",
|
||||
"observed_at desc",
|
||||
);
|
||||
const providerUsageWindows = list<ProviderUsageWindowRow>(this.db, "provider_usage_windows", "window_start desc");
|
||||
const providerRateLimits = buildProviderRateLimits(providers, providerUsageWindows);
|
||||
const files = list<AppSnapshot["files"][number]>(this.db, "file_objects", "created_at desc")
|
||||
.map((file) => ({ ...file, pinned: toBool(file.pinned) }));
|
||||
const legalHolds = list<AppSnapshot["legalHolds"][number]>(this.db, "legal_holds", "created_at desc")
|
||||
.map((hold) => ({ ...hold, active: toBool(hold.active) }));
|
||||
const retentionFreezes = list<AppSnapshot["retentionFreezes"][number]>(this.db, "retention_freezes", "created_at desc")
|
||||
.map((freeze) => ({ ...freeze, active: toBool(freeze.active) }));
|
||||
const employeeIdentities = list<AppSnapshot["employeeIdentities"][number]>(this.db, "employee_identities", "created_at desc")
|
||||
.map((identity) => ({ ...identity, active: toBool(identity.active) }));
|
||||
const featureFlags = list<AppSnapshot["featureFlags"][number]>(this.db, "feature_flags", "key asc")
|
||||
.map((flag) => ({ ...flag, enabled: toBool(flag.enabled) }));
|
||||
const operationalEvents = list<AppSnapshot["operationalEvents"][number]>(this.db, "operational_events", "created_at desc")
|
||||
.map((event) => ({ ...event, success: toBool(event.success) }));
|
||||
|
||||
return {
|
||||
activeOrgId,
|
||||
uiPreferences: this.resolveUiPreferences(),
|
||||
onboardingDraft: loadOnboardingDraft(this.db),
|
||||
viewState: loadViewState(this.db),
|
||||
dataVersion: currentDataVersion(),
|
||||
autonomousPaused: this.resolveAutonomousPaused(),
|
||||
integrationKeys: new IntegrationKeyStore(this.db).statuses(),
|
||||
organizations,
|
||||
@@ -127,7 +113,6 @@ export class SnapshotService {
|
||||
chatAttachments: forMessages(this.db, "chat_attachments", "message_id desc", loadedMessageIds),
|
||||
chatMentions: forMessages(this.db, "chat_mentions", "message_id desc, offset_start asc", loadedMessageIds),
|
||||
chatSlashInvocations: forMessages(this.db, "chat_slash_invocations", "created_at desc", loadedMessageIds),
|
||||
boardInterventions: list(this.db, "board_interventions", "created_at asc"),
|
||||
chatPresence: list(this.db, "chat_presence", "last_seen_at desc"),
|
||||
chatReadStates: list(this.db, "chat_read_state", "last_read_at desc"),
|
||||
files,
|
||||
@@ -136,7 +121,6 @@ export class SnapshotService {
|
||||
scheduledPosts: list(this.db, "scheduled_posts", "scheduled_for asc"),
|
||||
providers,
|
||||
providerAuthProfiles,
|
||||
providerAuthSessions,
|
||||
providerCatalog: PROVIDER_CATALOG,
|
||||
// Explicit projection: raw_response_json alone weighs megabytes across
|
||||
// 100 rows and no renderer surface reads it.
|
||||
@@ -150,7 +134,6 @@ export class SnapshotService {
|
||||
`), activeOrgId ?? "").map((row) => camel(row) as unknown as AppSnapshot["providerRequests"][number]),
|
||||
providerRateLimits,
|
||||
usageStats: buildUsageStats(this.db, new Date()),
|
||||
providerRateLimitObservations,
|
||||
skillDefinitions: list(this.db, "skill_definitions", "created_at asc"),
|
||||
socialAccounts: list(this.db, "social_accounts", "created_at asc"),
|
||||
socialAccountGrants: list(this.db, "social_account_grants", "created_at asc"),
|
||||
@@ -164,19 +147,7 @@ export class SnapshotService {
|
||||
policyRules: list(this.db, "policy_rules", "created_at asc"),
|
||||
migrationReports: list(this.db, "migration_reports", "created_at desc"),
|
||||
complianceExports: list(this.db, "compliance_exports", "created_at desc"),
|
||||
legalHolds,
|
||||
retentionFreezes,
|
||||
employeeIdentities,
|
||||
serviceIdentities: list(this.db, "service_identities", "created_at desc"),
|
||||
roleGrants: list(this.db, "role_grants", "created_at desc"),
|
||||
accessDecisions: recentInOrg<AppSnapshot["accessDecisions"][number]>(this.db, "access_decisions", "created_at desc", 200, activeOrgId),
|
||||
orgDepartments: list(this.db, "org_departments", "name asc"),
|
||||
hiringPolicies: list(this.db, "hiring_policies", "department_name asc"),
|
||||
approvalRoutingRules: list(this.db, "approval_routing_rules", "created_at asc"),
|
||||
approvalAssignments: list(this.db, "approval_assignments", "created_at desc"),
|
||||
featureFlags,
|
||||
sloTargets: list(this.db, "slo_targets", "signal_key asc"),
|
||||
operationalEvents,
|
||||
auditEvents: recentInOrg<AppSnapshot["auditEvents"][number]>(this.db, "audit_events", "created_at desc", 200, activeOrgId),
|
||||
documents: list(this.db, "documents", "updated_at desc"),
|
||||
knowledgeEntries: list(this.db, "knowledge_entries", "updated_at desc"),
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/** A snapshot read at an older data version than the one on screen is a late answer; showing it would undo newer data. */
|
||||
export function supersedes(shownVersion: number, incomingVersion: number): boolean {
|
||||
return incomingVersion >= shownVersion;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { supersedes } from "./snapshot-order";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import type { AppSnapshot } from "../../../shared/domain";
|
||||
import type { DesktopResult } from "../../../shared/result";
|
||||
|
||||
@@ -23,13 +24,17 @@ export function useSnapshot(): {
|
||||
const [snapshot, setSnapshot] = useState<AppSnapshot | null>(null);
|
||||
const [syncFailure, setSyncFailure] = useState<SyncFailure | null>(null);
|
||||
const [syncedAt, setSyncedAt] = useState<string | null>(null);
|
||||
// The newest data version on screen; a change event at or below it needs no reload.
|
||||
const shownVersion = useRef(-1);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
try {
|
||||
const result = await window.agentCompany.loadSnapshot();
|
||||
if (!result.ok) throw new Error(result.error.message);
|
||||
setSnapshot(result.data);
|
||||
setSyncFailure(null);
|
||||
if (!supersedes(shownVersion.current, result.data.dataVersion)) return;
|
||||
shownVersion.current = result.data.dataVersion;
|
||||
setSnapshot(result.data);
|
||||
setSyncedAt(new Date().toISOString());
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
@@ -43,7 +48,8 @@ export function useSnapshot(): {
|
||||
): Promise<T | null> => {
|
||||
const result = await operation();
|
||||
if (!result.ok) throw new Error(result.error.message);
|
||||
if (result.data) {
|
||||
if (result.data && supersedes(shownVersion.current, (result.data as AppSnapshot).dataVersion)) {
|
||||
shownVersion.current = (result.data as AppSnapshot).dataVersion;
|
||||
setSnapshot(result.data as AppSnapshot);
|
||||
setSyncFailure(null);
|
||||
setSyncedAt(new Date().toISOString());
|
||||
@@ -84,7 +90,7 @@ export function useSnapshot(): {
|
||||
guardedLoad();
|
||||
});
|
||||
const unsubscribe = window.agentCompany.subscribe((event) => {
|
||||
if (event.type === "snapshot-changed") scheduleReload();
|
||||
if (event.type === "snapshot-changed" && event.version > shownVersion.current) scheduleReload();
|
||||
});
|
||||
return () => {
|
||||
disposed = true;
|
||||
|
||||
@@ -24,18 +24,3 @@ export type ChatThreadBoardState = ValueOf<typeof CHAT_THREAD_BOARD_STATE>;
|
||||
export type BoardInterventionMode = ValueOf<typeof BOARD_INTERVENTION_MODE>;
|
||||
export type BoardInterventionAction = ValueOf<typeof BOARD_INTERVENTION_ACTION>;
|
||||
|
||||
export interface BoardInterventionRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
threadId: string;
|
||||
messageId: string;
|
||||
mode: BoardInterventionMode;
|
||||
action: BoardInterventionAction;
|
||||
details: string;
|
||||
targetType: "employee" | "agent" | "board" | null;
|
||||
targetId: string | null;
|
||||
approvalId: string | null;
|
||||
createdByType: "employee" | "agent" | "board" | "system";
|
||||
createdById: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -28,26 +28,3 @@ export interface ComplianceExportRecord {
|
||||
completedAt: string | null;
|
||||
}
|
||||
|
||||
export interface LegalHoldRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
entityType: LegalHoldEntityType;
|
||||
entityId: string;
|
||||
reason: string;
|
||||
createdByEmployeeId: string | null;
|
||||
active: boolean;
|
||||
createdAt: string;
|
||||
releasedAt: string | null;
|
||||
}
|
||||
|
||||
export interface RetentionFreezeRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
scopeType: RetentionFreezeScopeType;
|
||||
scopeId: string;
|
||||
reason: string;
|
||||
createdByEmployeeId: string | null;
|
||||
active: boolean;
|
||||
createdAt: string;
|
||||
releasedAt: string | null;
|
||||
}
|
||||
@@ -113,7 +113,8 @@ export interface ProviderOAuthEvent {
|
||||
}
|
||||
|
||||
export type RendererEvent =
|
||||
| { type: "snapshot-changed" }
|
||||
/** `version` is the data version the change produced; a snapshot at that version or later already shows it. */
|
||||
| { type: "snapshot-changed"; version: number }
|
||||
| { type: "provider-response-stream"; event: ProviderStreamRendererEvent }
|
||||
| ProviderOAuthEvent;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { ProviderDataPolicy } from "./provider-policy-domain";
|
||||
export { BOARD_INTERVENTION_ACTION, BOARD_INTERVENTION_MODE, CHAT_THREAD_BOARD_STATE, type BoardInterventionAction, type BoardInterventionMode, type BoardInterventionRecord, type ChatThreadBoardState } from "./board-domain";
|
||||
export { BOARD_INTERVENTION_ACTION, BOARD_INTERVENTION_MODE, CHAT_THREAD_BOARD_STATE, type BoardInterventionAction, type BoardInterventionMode, type ChatThreadBoardState } from "./board-domain";
|
||||
export type { ChatReplyStatus, ChatSpaceAnchor, ChatSpaceRecord, ChatMessageRecord, ChatMembershipRecord, ChatThreadRecord, ChatAttachmentRecord, ChatMentionRecord, ChatSlashInvocationRecord, ChatPresenceRecord, ChatReadStateRecord } from "./chat-domain";
|
||||
export { COMPLIANCE_EXPORT_STATE, COMPLIANCE_EXPORT_TYPE, type ComplianceExportRecord, type ComplianceExportState, type ComplianceExportType, type ComplianceSubjectType, type LegalHoldEntityType, type LegalHoldRecord, type RetentionFreezeRecord, type RetentionFreezeScopeType } from "./compliance-domain";
|
||||
export { ACCESS_DECISION, IDENTITY_SOURCE, ROLE_KEY, type AccessDecision, type AccessDecisionRecord, type EmployeeIdentityRecord, type IdentitySource, type PrincipalType, type RoleGrantRecord, type RoleKey, type ServiceIdentityRecord } from "./identity-domain";
|
||||
export { ROUTE_TO_TYPE, type ApprovalAssignmentRecord, type ApprovalRoutingRuleRecord, type HiringPolicyRecord, type OrgDepartmentRecord, type RouteToType } from "./org-governance-domain";
|
||||
export { FEATURE_FLAG_KEY, SLO_SIGNAL_KEY, type FeatureFlagKey, type FeatureFlagRecord, type OperationalEventRecord, type SloSignalKey, type SloTargetRecord } from "./operations-domain";
|
||||
export type { ProviderAuthProfileRecord, ProviderAuthSessionRecord } from "./provider-auth-domain";
|
||||
export { COMPLIANCE_EXPORT_STATE, COMPLIANCE_EXPORT_TYPE, type ComplianceExportRecord, type ComplianceExportState, type ComplianceExportType, type ComplianceSubjectType, type LegalHoldEntityType, type RetentionFreezeScopeType } from "./compliance-domain";
|
||||
export { ACCESS_DECISION, IDENTITY_SOURCE, ROLE_KEY, type AccessDecision, type IdentitySource, type PrincipalType, type RoleKey } from "./identity-domain";
|
||||
export { ROUTE_TO_TYPE, type HiringPolicyRecord, type RouteToType } from "./org-governance-domain";
|
||||
export { FEATURE_FLAG_KEY, SLO_SIGNAL_KEY, type FeatureFlagKey, type SloSignalKey } from "./operations-domain";
|
||||
export type { ProviderAuthProfileRecord } from "./provider-auth-domain";
|
||||
export { PROVIDER_DATA_POLICY, PROVIDER_EXECUTION_MODE, type ProviderDataPolicy, type ProviderExecutionMode } from "./provider-policy-domain";
|
||||
export { PROVIDER_REQUEST_STATE, type ProviderRequestRecord, type ProviderRequestState, type ProviderStreamRendererEvent } from "./provider-request-domain";
|
||||
export type { ProviderRateLimitObservationRecord, ProviderRateLimitStateRecord } from "./provider-runtime-domain";
|
||||
export type { ProviderRateLimitStateRecord } from "./provider-runtime-domain";
|
||||
export { SEARCH_ENTITY_TYPES, type SearchEntityType, type SearchPage, type SearchResultRecord } from "./search-domain";
|
||||
export type { BrowserActionPolicyRecord, BrowserActionRecord, BrowserSessionContainerRecord, SocialAccountGrantRecord, SocialAccountRecord, SocialApprovalRouteRecord, SocialAuditRecord } from "./social-domain";
|
||||
export type { AppSnapshot, UiLanguage, UiPreferences, UiTheme } from "./snapshot-domain";
|
||||
|
||||
@@ -14,21 +14,3 @@ export type RoleKey = ValueOf<typeof ROLE_KEY>;
|
||||
export type AccessDecision = ValueOf<typeof ACCESS_DECISION>;
|
||||
export type PrincipalType = "employee" | "agent" | "service_identity";
|
||||
|
||||
export interface EmployeeIdentityRecord {
|
||||
id: string; orgId: string; employeeId: string; source: IdentitySource; externalSubject: string;
|
||||
email: string | null; active: boolean; createdAt: string; lastSeenAt: string | null;
|
||||
}
|
||||
|
||||
export interface ServiceIdentityRecord {
|
||||
id: string; orgId: string; agentId: string; name: string; scopeJson: string; createdAt: string;
|
||||
}
|
||||
|
||||
export interface RoleGrantRecord {
|
||||
id: string; orgId: string; principalType: PrincipalType; principalId: string; roleKey: RoleKey;
|
||||
scopeType: string; scopeId: string; grantedByEmployeeId: string | null; createdAt: string;
|
||||
}
|
||||
|
||||
export interface AccessDecisionRecord {
|
||||
id: string; orgId: string; principalType: PrincipalType; principalId: string; action: string;
|
||||
resourceType: string; resourceId: string; decision: AccessDecision; reason: string; createdAt: string;
|
||||
}
|
||||
@@ -16,14 +16,3 @@ export const SLO_SIGNAL_KEY = {
|
||||
export type FeatureFlagKey = ValueOf<typeof FEATURE_FLAG_KEY>;
|
||||
export type SloSignalKey = ValueOf<typeof SLO_SIGNAL_KEY>;
|
||||
|
||||
export interface FeatureFlagRecord {
|
||||
id: string; orgId: string; key: FeatureFlagKey; label: string; enabled: boolean; createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
export interface SloTargetRecord {
|
||||
id: string; orgId: string; signalKey: SloSignalKey; label: string; targetBasisPoints: number; windowMinutes: number; createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
export interface OperationalEventRecord {
|
||||
id: string; orgId: string; signalKey: SloSignalKey; source: string; success: boolean; durationMs: number | null; detailJson: string; createdAt: string;
|
||||
}
|
||||
@@ -3,22 +3,8 @@ type ValueOf<T> = T[keyof T];
|
||||
export const ROUTE_TO_TYPE = { EMPLOYEE: "employee", AGENT: "agent", BOARD: "board" } as const;
|
||||
export type RouteToType = ValueOf<typeof ROUTE_TO_TYPE>;
|
||||
|
||||
export interface OrgDepartmentRecord {
|
||||
id: string; orgId: string; name: string; leadAgentId: string | null;
|
||||
headcountLimit: number; monthlyBudgetLimitCents: number; createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
export interface HiringPolicyRecord {
|
||||
id: string; orgId: string; departmentName: string; maxHeadcount: number;
|
||||
monthlyBudgetLimitCents: number; requireApprovalAboveCents: number; createdAt: string; updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ApprovalRoutingRuleRecord {
|
||||
id: string; orgId: string; kind: string; minimumRisk: string;
|
||||
routeToType: RouteToType; routeToId: string; createdAt: string;
|
||||
}
|
||||
|
||||
export interface ApprovalAssignmentRecord {
|
||||
id: string; orgId: string; approvalId: string; assigneeType: RouteToType;
|
||||
assigneeId: string; routingRuleId: string | null; createdAt: string;
|
||||
}
|
||||
@@ -16,19 +16,3 @@ export interface ProviderAuthProfileRecord {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface ProviderAuthSessionRecord {
|
||||
id: string;
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
employeeId: string;
|
||||
state: string;
|
||||
nonce: string;
|
||||
codeVerifierSha256: string;
|
||||
redirectUri: string;
|
||||
signInUrl: string;
|
||||
partition: string;
|
||||
createdAt: string;
|
||||
expiresAt: string;
|
||||
completedAt: string | null;
|
||||
}
|
||||
@@ -26,15 +26,6 @@ export interface ProviderObservedRateLimitState {
|
||||
rawStateJson: string;
|
||||
}
|
||||
|
||||
export interface ProviderRateLimitObservationRecord extends ProviderObservedRateLimitState {
|
||||
id: string;
|
||||
orgId: string;
|
||||
providerId: string;
|
||||
providerKey: string;
|
||||
model: string | null;
|
||||
observedAt: string;
|
||||
}
|
||||
|
||||
/** The message of the typed error a run gets when its provider's monthly request allowance is used up. */
|
||||
export const PROVIDER_MONTHLY_LIMIT_MESSAGE = "Provider monthly request limit has been reached.";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { OnboardingDraft } from "./commands";
|
||||
import type { UsageStatsRecord } from "./usage-stats";
|
||||
import type { IntegrationKeyStatus } from "./commands/integration-keys";
|
||||
import type { BoardInterventionRecord as BoardInterventionRecordModel } from "./board-domain";
|
||||
import type {
|
||||
ChatAttachmentRecord,
|
||||
ChatMembershipRecord,
|
||||
@@ -15,7 +14,7 @@ import type {
|
||||
ChatReplyStatus,
|
||||
ChatSpaceAnchor,
|
||||
} from "./chat-domain";
|
||||
import type { ComplianceExportRecord, LegalHoldRecord, RetentionFreezeRecord } from "./compliance-domain";
|
||||
import type { ComplianceExportRecord } from "./compliance-domain";
|
||||
import type {
|
||||
AgentRecord,
|
||||
ApprovalRecord,
|
||||
@@ -44,12 +43,10 @@ import type {
|
||||
TaskRunRecord,
|
||||
WorkspaceRecord,
|
||||
} from "./domain";
|
||||
import type { AccessDecisionRecord, EmployeeIdentityRecord, RoleGrantRecord, ServiceIdentityRecord } from "./identity-domain";
|
||||
import type { ApprovalAssignmentRecord, ApprovalRoutingRuleRecord, HiringPolicyRecord, OrgDepartmentRecord } from "./org-governance-domain";
|
||||
import type { FeatureFlagRecord, OperationalEventRecord, SloTargetRecord } from "./operations-domain";
|
||||
import type { ProviderAuthProfileRecord, ProviderAuthSessionRecord } from "./provider-auth-domain";
|
||||
import type { HiringPolicyRecord } from "./org-governance-domain";
|
||||
import type { ProviderAuthProfileRecord } from "./provider-auth-domain";
|
||||
import type { ProviderRequestRecord } from "./provider-request-domain";
|
||||
import type { ProviderRateLimitObservationRecord, ProviderRateLimitStateRecord } from "./provider-runtime-domain";
|
||||
import type { ProviderRateLimitStateRecord } from "./provider-runtime-domain";
|
||||
import type {
|
||||
BrowserActionPolicyRecord,
|
||||
BrowserActionRecord,
|
||||
@@ -75,6 +72,8 @@ export interface AppSnapshot {
|
||||
onboardingDraft: OnboardingDraft | null;
|
||||
/** Where the operator was: page, sections, filters and selected records, by key. */
|
||||
viewState: Record<string, unknown>;
|
||||
/** The data version this snapshot was read at. */
|
||||
dataVersion: number;
|
||||
/** True while the operator has paused the autonomous run engine. */
|
||||
autonomousPaused: boolean;
|
||||
integrationKeys: IntegrationKeyStatus[];
|
||||
@@ -96,7 +95,6 @@ export interface AppSnapshot {
|
||||
chatAttachments: ChatAttachmentRecord[];
|
||||
chatMentions: ChatMentionRecord[];
|
||||
chatSlashInvocations: ChatSlashInvocationRecord[];
|
||||
boardInterventions: BoardInterventionRecordModel[];
|
||||
chatPresence: ChatPresenceRecord[];
|
||||
chatReadStates: ChatReadStateRecord[];
|
||||
files: FileObjectRecord[];
|
||||
@@ -105,13 +103,11 @@ export interface AppSnapshot {
|
||||
scheduledPosts: ScheduledPostRecord[];
|
||||
providers: ProviderRecord[];
|
||||
providerAuthProfiles: ProviderAuthProfileRecord[];
|
||||
providerAuthSessions: ProviderAuthSessionRecord[];
|
||||
providerCatalog: ProviderCatalogEntry[];
|
||||
providerRequests: ProviderRequestRecord[];
|
||||
providerRateLimits: ProviderRateLimitStateRecord[];
|
||||
/** Period statistics per org over all records, for charts and billing. */
|
||||
usageStats: UsageStatsRecord[];
|
||||
providerRateLimitObservations: ProviderRateLimitObservationRecord[];
|
||||
// The snapshot is the renderer's working set and rides IPC on every
|
||||
// refresh. Skill internals (revisions, bindings, per-run runtime
|
||||
// snapshots with compiled prompts) are main-process concerns and stay out
|
||||
@@ -127,19 +123,7 @@ export interface AppSnapshot {
|
||||
policyRules: PolicyRuleRecord[];
|
||||
migrationReports: MigrationReportRecord[];
|
||||
complianceExports: ComplianceExportRecord[];
|
||||
legalHolds: LegalHoldRecord[];
|
||||
retentionFreezes: RetentionFreezeRecord[];
|
||||
employeeIdentities: EmployeeIdentityRecord[];
|
||||
serviceIdentities: ServiceIdentityRecord[];
|
||||
roleGrants: RoleGrantRecord[];
|
||||
accessDecisions: AccessDecisionRecord[];
|
||||
orgDepartments: OrgDepartmentRecord[];
|
||||
hiringPolicies: HiringPolicyRecord[];
|
||||
approvalRoutingRules: ApprovalRoutingRuleRecord[];
|
||||
approvalAssignments: ApprovalAssignmentRecord[];
|
||||
featureFlags: FeatureFlagRecord[];
|
||||
sloTargets: SloTargetRecord[];
|
||||
operationalEvents: OperationalEventRecord[];
|
||||
auditEvents: AuditEventRecord[];
|
||||
documents: DocumentRecord[];
|
||||
knowledgeEntries: KnowledgeEntryRecord[];
|
||||
|
||||
Reference in new issue
Block a user