mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-05 00:23:10 +00:00
refactor(provider): flow branded ProviderID/ModelID through internal signatures (#17182)
This commit is contained in:
@@ -15,9 +15,13 @@ export namespace Permission {
|
||||
return pattern === undefined ? [type] : Array.isArray(pattern) ? pattern : [pattern]
|
||||
}
|
||||
|
||||
function covered(keys: string[], approved: Record<string, boolean>): boolean {
|
||||
const pats = Object.keys(approved)
|
||||
return keys.every((k) => pats.some((p) => Wildcard.match(k, p)))
|
||||
function covered(keys: string[], approved: Map<string, boolean>): boolean {
|
||||
return keys.every((k) => {
|
||||
for (const p of approved.keys()) {
|
||||
if (Wildcard.match(k, p)) return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
export const Info = z
|
||||
@@ -39,6 +43,12 @@ export namespace Permission {
|
||||
})
|
||||
export type Info = z.infer<typeof Info>
|
||||
|
||||
interface PendingEntry {
|
||||
info: Info
|
||||
resolve: () => void
|
||||
reject: (e: any) => void
|
||||
}
|
||||
|
||||
export const Event = {
|
||||
Updated: BusEvent.define("permission.updated", Info),
|
||||
Replied: BusEvent.define(
|
||||
@@ -52,31 +62,13 @@ export namespace Permission {
|
||||
}
|
||||
|
||||
const state = Instance.state(
|
||||
() => {
|
||||
const pending: {
|
||||
[sessionID: string]: {
|
||||
[permissionID: string]: {
|
||||
info: Info
|
||||
resolve: () => void
|
||||
reject: (e: any) => void
|
||||
}
|
||||
}
|
||||
} = {}
|
||||
|
||||
const approved: {
|
||||
[sessionID: string]: {
|
||||
[permissionID: string]: boolean
|
||||
}
|
||||
} = {}
|
||||
|
||||
return {
|
||||
pending,
|
||||
approved,
|
||||
}
|
||||
},
|
||||
() => ({
|
||||
pending: new Map<SessionID, Map<PermissionID, PendingEntry>>(),
|
||||
approved: new Map<SessionID, Map<string, boolean>>(),
|
||||
}),
|
||||
async (state) => {
|
||||
for (const pending of Object.values(state.pending)) {
|
||||
for (const item of Object.values(pending)) {
|
||||
for (const session of state.pending.values()) {
|
||||
for (const item of session.values()) {
|
||||
item.reject(new RejectedError(item.info.sessionID, item.info.id, item.info.callID, item.info.metadata))
|
||||
}
|
||||
}
|
||||
@@ -90,8 +82,8 @@ export namespace Permission {
|
||||
export function list() {
|
||||
const { pending } = state()
|
||||
const result: Info[] = []
|
||||
for (const items of Object.values(pending)) {
|
||||
for (const item of Object.values(items)) {
|
||||
for (const session of pending.values()) {
|
||||
for (const item of session.values()) {
|
||||
result.push(item.info)
|
||||
}
|
||||
}
|
||||
@@ -114,9 +106,9 @@ export namespace Permission {
|
||||
toolCallID: input.callID,
|
||||
pattern: input.pattern,
|
||||
})
|
||||
const approvedForSession = approved[input.sessionID] || {}
|
||||
const approvedForSession = approved.get(input.sessionID)
|
||||
const keys = toKeys(input.pattern, input.type)
|
||||
if (covered(keys, approvedForSession)) return
|
||||
if (approvedForSession && covered(keys, approvedForSession)) return
|
||||
const info: Info = {
|
||||
id: PermissionID.ascending(),
|
||||
type: input.type,
|
||||
@@ -142,13 +134,13 @@ export namespace Permission {
|
||||
return
|
||||
}
|
||||
|
||||
pending[input.sessionID] = pending[input.sessionID] || {}
|
||||
if (!pending.has(input.sessionID)) pending.set(input.sessionID, new Map())
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
pending[input.sessionID][info.id] = {
|
||||
pending.get(input.sessionID)!.set(info.id, {
|
||||
info,
|
||||
resolve,
|
||||
reject,
|
||||
}
|
||||
})
|
||||
Bus.publish(Event.Updated, info)
|
||||
})
|
||||
}
|
||||
@@ -159,9 +151,11 @@ export namespace Permission {
|
||||
export function respond(input: { sessionID: Info["sessionID"]; permissionID: Info["id"]; response: Response }) {
|
||||
log.info("response", input)
|
||||
const { pending, approved } = state()
|
||||
const match = pending[input.sessionID]?.[input.permissionID]
|
||||
if (!match) return
|
||||
delete pending[input.sessionID][input.permissionID]
|
||||
const session = pending.get(input.sessionID)
|
||||
const match = session?.get(input.permissionID)
|
||||
if (!session || !match) return
|
||||
session.delete(input.permissionID)
|
||||
if (session.size === 0) pending.delete(input.sessionID)
|
||||
Bus.publish(Event.Replied, {
|
||||
sessionID: input.sessionID,
|
||||
permissionID: input.permissionID,
|
||||
@@ -173,30 +167,35 @@ export namespace Permission {
|
||||
}
|
||||
match.resolve()
|
||||
if (input.response === "always") {
|
||||
approved[input.sessionID] = approved[input.sessionID] || {}
|
||||
if (!approved.has(input.sessionID)) approved.set(input.sessionID, new Map())
|
||||
const approvedSession = approved.get(input.sessionID)!
|
||||
const approveKeys = toKeys(match.info.pattern, match.info.type)
|
||||
for (const k of approveKeys) {
|
||||
approved[input.sessionID][k] = true
|
||||
approvedSession.set(k, true)
|
||||
}
|
||||
const items = pending[input.sessionID]
|
||||
const items = pending.get(input.sessionID)
|
||||
if (!items) return
|
||||
for (const item of Object.values(items)) {
|
||||
const toRespond: Info[] = []
|
||||
for (const item of items.values()) {
|
||||
const itemKeys = toKeys(item.info.pattern, item.info.type)
|
||||
if (covered(itemKeys, approved[input.sessionID])) {
|
||||
respond({
|
||||
sessionID: item.info.sessionID,
|
||||
permissionID: item.info.id,
|
||||
response: input.response,
|
||||
})
|
||||
if (covered(itemKeys, approvedSession)) {
|
||||
toRespond.push(item.info)
|
||||
}
|
||||
}
|
||||
for (const item of toRespond) {
|
||||
respond({
|
||||
sessionID: item.sessionID,
|
||||
permissionID: item.id,
|
||||
response: input.response,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class RejectedError extends Error {
|
||||
constructor(
|
||||
public readonly sessionID: string,
|
||||
public readonly permissionID: string,
|
||||
public readonly sessionID: SessionID,
|
||||
public readonly permissionID: PermissionID,
|
||||
public readonly toolCallID?: string,
|
||||
public readonly metadata?: Record<string, any>,
|
||||
public readonly reason?: string,
|
||||
|
||||
@@ -108,6 +108,12 @@ export namespace PermissionNext {
|
||||
),
|
||||
}
|
||||
|
||||
interface PendingEntry {
|
||||
info: Request
|
||||
resolve: () => void
|
||||
reject: (e: any) => void
|
||||
}
|
||||
|
||||
const state = Instance.state(() => {
|
||||
const projectID = Instance.project.id
|
||||
const row = Database.use((db) =>
|
||||
@@ -115,17 +121,8 @@ export namespace PermissionNext {
|
||||
)
|
||||
const stored = row?.data ?? ([] as Ruleset)
|
||||
|
||||
const pending: Record<
|
||||
string,
|
||||
{
|
||||
info: Request
|
||||
resolve: () => void
|
||||
reject: (e: any) => void
|
||||
}
|
||||
> = {}
|
||||
|
||||
return {
|
||||
pending,
|
||||
pending: new Map<PermissionID, PendingEntry>(),
|
||||
approved: stored,
|
||||
}
|
||||
})
|
||||
@@ -149,11 +146,11 @@ export namespace PermissionNext {
|
||||
id,
|
||||
...request,
|
||||
}
|
||||
s.pending[id] = {
|
||||
s.pending.set(id, {
|
||||
info,
|
||||
resolve,
|
||||
reject,
|
||||
}
|
||||
})
|
||||
Bus.publish(Event.Asked, info)
|
||||
})
|
||||
}
|
||||
@@ -170,9 +167,9 @@ export namespace PermissionNext {
|
||||
}),
|
||||
async (input) => {
|
||||
const s = await state()
|
||||
const existing = s.pending[input.requestID]
|
||||
const existing = s.pending.get(input.requestID)
|
||||
if (!existing) return
|
||||
delete s.pending[input.requestID]
|
||||
s.pending.delete(input.requestID)
|
||||
Bus.publish(Event.Replied, {
|
||||
sessionID: existing.info.sessionID,
|
||||
requestID: existing.info.id,
|
||||
@@ -182,9 +179,9 @@ export namespace PermissionNext {
|
||||
existing.reject(input.message ? new CorrectedError(input.message) : new RejectedError())
|
||||
// Reject all other pending permissions for this session
|
||||
const sessionID = existing.info.sessionID
|
||||
for (const [id, pending] of Object.entries(s.pending)) {
|
||||
for (const [id, pending] of s.pending) {
|
||||
if (pending.info.sessionID === sessionID) {
|
||||
delete s.pending[id]
|
||||
s.pending.delete(id)
|
||||
Bus.publish(Event.Replied, {
|
||||
sessionID: pending.info.sessionID,
|
||||
requestID: pending.info.id,
|
||||
@@ -211,13 +208,13 @@ export namespace PermissionNext {
|
||||
existing.resolve()
|
||||
|
||||
const sessionID = existing.info.sessionID
|
||||
for (const [id, pending] of Object.entries(s.pending)) {
|
||||
for (const [id, pending] of s.pending) {
|
||||
if (pending.info.sessionID !== sessionID) continue
|
||||
const ok = pending.info.patterns.every(
|
||||
(pattern) => evaluate(pending.info.permission, pattern, s.approved).action === "allow",
|
||||
)
|
||||
if (!ok) continue
|
||||
delete s.pending[id]
|
||||
s.pending.delete(id)
|
||||
Bus.publish(Event.Replied, {
|
||||
sessionID: pending.info.sessionID,
|
||||
requestID: pending.info.id,
|
||||
@@ -283,6 +280,6 @@ export namespace PermissionNext {
|
||||
|
||||
export async function list() {
|
||||
const s = await state()
|
||||
return Object.values(s.pending).map((x) => x.info)
|
||||
return Array.from(s.pending.values(), (x) => x.info)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user