Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 41 additions & 8 deletions bun.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"drizzle-orm": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.8.0",
"gray-matter": "4.0.3",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"immer": "11.1.4",
Expand Down
452 changes: 450 additions & 2 deletions packages/core/src/account.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/core/src/account/sql.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core"

import { AccountV2 } from "../account"
import type { AccountV2 } from "../account"
import { Timestamps } from "../database/schema.sql"

export const AccountTable = sqliteTable("account", {
Expand Down
106 changes: 72 additions & 34 deletions packages/core/src/config/plugin/agent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
export * as ConfigAgentPlugin from "./agent"

import { Effect } from "effect"
import path from "path"
import matter from "gray-matter"
import { Effect, Option, Schema } from "effect"
import { AgentV2 } from "../../agent"
import { Config } from "../../config"
import { ConfigAgent } from "../agent"
import { AppFileSystem } from "../../filesystem"
import { ModelV2 } from "../../model"
import { PermissionV2 } from "../../permission"
import { PluginV2 } from "../../plugin"
Expand All @@ -12,49 +16,83 @@ export const Plugin = PluginV2.define({
effect: Effect.gen(function* () {
const agent = yield* AgentV2.Service
const config = yield* Config.Service
const files = yield* config.get()
const fs = yield* AppFileSystem.Service
const documents = yield* config.get()
const loadFile = Effect.fnUntraced(function* (directory: string, filepath: string) {
const text = yield* fs.readFileString(filepath).pipe(Effect.orDie)
const document = yield* Effect.try({ try: () => matter(text), catch: () => undefined }).pipe(
Effect.catch(() => Effect.void),
)
if (!document) return

const info = Option.getOrUndefined(
Schema.decodeUnknownOption(ConfigAgent.Info)(
{ ...document.data, system: document.content.trim() },
{ errors: "all", onExcessProperty: "ignore" },
),
)
if (!info) return

const relative = path.relative(directory, filepath).split(path.sep).slice(1).join("/")
return {
id: AgentV2.ID.make(relative.slice(0, -path.extname(relative).length)),
info,
}
})
const files = (yield* Effect.forEach(yield* config.directories(), (directory) =>
fs.glob("{agent,agents}/**/*.md", { cwd: directory, absolute: true, dot: true, symlink: true }).pipe(
Effect.orDie,
Effect.flatMap((items) => Effect.forEach(items, (item) => loadFile(directory, item))),
Effect.map((items) => items.filter((item): item is NonNullable<typeof item> => item !== undefined)),
),
)).flat()

yield* agent.update((editor) => {
const permissions = new Map<AgentV2.ID, PermissionV2.Ruleset>()

for (const file of files) {
for (const [id, item] of Object.entries(file.info.agents ?? {})) {
const agentID = AgentV2.ID.make(id)
if (item.disabled) {
editor.remove(agentID)
permissions.delete(agentID)
continue
function update(agentID: AgentV2.ID, item: ConfigAgent.Info, disabled: boolean, rules: PermissionV2.Ruleset) {
if (disabled) {
editor.remove(agentID)
permissions.delete(agentID)
return
}

editor.update(agentID, (agent) => {
if (item.model !== undefined) {
const model = ModelV2.parse(item.model)
agent.model = { id: model.modelID, providerID: model.providerID, variant: agent.model?.variant }
}
if (item.variant !== undefined && agent.model !== undefined) {
agent.model.variant = ModelV2.VariantID.make(item.variant)
}
if (item.options !== undefined) {
Object.assign(agent.options.headers, item.options.headers ?? {})
Object.assign(agent.options.body, item.options.body ?? {})
Object.assign(agent.options.aisdk.provider, item.options.aisdk?.provider ?? {})
Object.assign(agent.options.aisdk.request, item.options.aisdk?.request ?? {})
}
if (item.system !== undefined) agent.system = item.system
if (item.description !== undefined) agent.description = item.description
if (item.mode !== undefined) agent.mode = item.mode
if (item.hidden !== undefined) agent.hidden = item.hidden
if (item.color !== undefined) agent.color = item.color
if (item.steps !== undefined) agent.steps = item.steps
})

editor.update(agentID, (agent) => {
if (item.model !== undefined) {
const model = ModelV2.parse(item.model)
agent.model = { id: model.modelID, providerID: model.providerID, variant: agent.model?.variant }
}
if (item.variant !== undefined && agent.model !== undefined) {
agent.model.variant = ModelV2.VariantID.make(item.variant)
}
if (item.options !== undefined) {
Object.assign(agent.options.headers, item.options.headers ?? {})
Object.assign(agent.options.body, item.options.body ?? {})
Object.assign(agent.options.aisdk.provider, item.options.aisdk?.provider ?? {})
Object.assign(agent.options.aisdk.request, item.options.aisdk?.request ?? {})
}
if (item.system !== undefined) agent.system = item.system
if (item.description !== undefined) agent.description = item.description
if (item.mode !== undefined) agent.mode = item.mode
if (item.hidden !== undefined) agent.hidden = item.hidden
if (item.color !== undefined) agent.color = item.color
if (item.steps !== undefined) agent.steps = item.steps
})
if (rules.length) permissions.set(agentID, [...(permissions.get(agentID) ?? []), ...rules])
}

if (item.permissions !== undefined) {
permissions.set(agentID, [...(permissions.get(agentID) ?? []), ...item.permissions])
}
for (const file of documents) {
for (const [id, item] of Object.entries(file.info.agents ?? {})) {
update(AgentV2.ID.make(id), item, item.disabled ?? false, item.permissions ?? [])
}
}

const global = files.flatMap((file) => file.info.permissions ?? [])
for (const file of files) {
update(file.id, file.info, file.info.disabled ?? false, file.info.permissions ?? [])
}

const global = documents.flatMap((file) => file.info.permissions ?? [])
for (const current of editor.list()) {
editor.update(current.id, (agent) => {
agent.permissions.push(...global, ...(permissions.get(current.id) ?? []))
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/plugin/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Catalog } from "../catalog"
import { Config } from "../config"
import { ConfigAgentPlugin } from "../config/plugin/agent"
import { EventV2 } from "../event"
import { AppFileSystem } from "../filesystem"
import { Location } from "../location"
import { ModelsDev } from "../models-dev"
import { Npm } from "../npm"
Expand All @@ -26,6 +27,7 @@ type Plugin = {
| AgentV2.Service
| Npm.Service
| EventV2.Service
| AppFileSystem.Service
| Location.Service
| PluginV2.Service
| Config.Service
Expand All @@ -51,6 +53,7 @@ export const layer = Layer.effect(
const modelsDev = yield* ModelsDev.Service
const npm = yield* Npm.Service
const events = yield* EventV2.Service
const fs = yield* AppFileSystem.Service
const done = yield* Deferred.make<void>()

const add = Effect.fn("PluginBoot.add")(function* (input: Plugin) {
Expand All @@ -65,6 +68,7 @@ export const layer = Layer.effect(
Effect.provideService(ModelsDev.Service, modelsDev),
Effect.provideService(Npm.Service, npm),
Effect.provideService(EventV2.Service, events),
Effect.provideService(AppFileSystem.Service, fs),
Effect.provideService(PluginV2.Service, plugin),
),
})
Expand Down
Loading
Loading