| 1 | import { PERSONA, normalizePersonaConfig } from './persona.ts' |
| 2 | /** Raw DSH roster preparation, over the existing reviewed file/entry receipt. |
| 3 | * Discovery borrows the pinned package's algorithms. Mounts use the one Loader; |
| 4 | * Rust alone selects caller/AgentProfile ancestry and writes session/settings. |
| 5 | */ |
| 6 | import { Service } from '@deepseek-ai/cordis' |
| 7 | import { createHash } from 'node:crypto' |
| 8 | import { resolve, relative, dirname, sep } from 'node:path' |
| 9 | import { fileURLToPath, pathToFileURL } from 'node:url' |
| 10 | import { discoverPresets, type DiscoveryIO } from './upstream/agent-presets/discovery.ts' |
| 11 | import { PRESET_ID, type AgentPreset, type PresetRoot } from './upstream/agent-presets/preset.ts' |
| 12 | import { classifyRowSpecifier } from './upstream/agent-presets/specifier.ts' |
| 13 | import { fileComposition, mountedCompositionRows, type AgentPresetCompositionRow } from './upstream/agent-presets/composition-inventory.ts' |
| 14 | import {isJsExpr} from './upstream/loader/src/index.ts' |
| 15 | import type { ReviewedComposition, ReviewedModule, CompositionMount } from './composition.ts' |
| 16 | |
| 17 | export const AGENT_PRESETS = '@deepseek-ai/dsh-agent-presets' |
| 18 | const MAX_PRESETS = 64 |
| 19 | const hash = (bytes: string) => createHash('sha256').update(bytes).digest('hex') |
| 20 | const SHA = /^[a-f0-9]{64}$/ |
| 21 | function plain(path: string): boolean { |
| 22 | return typeof path === 'string' && path.length > 0 && path.length <= 4096 && !/[\\:\u0000-\u001f\u007f]/u.test(path) |
| 23 | && !path.startsWith('/') && path.split('/').every(p => p !== '' && p !== '.' && p !== '..') |
| 24 | } |
| 25 | function fail(message: string): never { throw new Error(`agent-presets: ${message}`) } |
| 26 | |
| 27 | /** Exact package export lookup inside the admitted inventory, no upward walk. */ |
| 28 | export function containedPackageModule(name: string, documents: ReadonlyMap<string, string>, files: Readonly<Record<string, string>>): ReviewedModule | undefined { |
| 29 | if (!name || /[\\:\u0000-\u0020\u007f]/u.test(name)) return |
| 30 | const parts = name.split('/') |
| 31 | const n = name.startsWith('@') ? 2 : 1 |
| 32 | if (parts.length < n || parts.some(p => !p || p === '.' || p === '..')) return |
| 33 | const pkg = parts.slice(0, n).join('/') |
| 34 | const packageRoot = `node_modules/${pkg}` |
| 35 | const source = documents.get(`${packageRoot}/package.json`) |
| 36 | if (source === undefined) return |
| 37 | let data: any |
| 38 | try { data = JSON.parse(source) } catch { return } |
| 39 | if (data.name !== pkg || data.type !== 'module') return |
| 40 | const subpath = parts.length === n ? '.' : `./${parts.slice(n).join('/')}` |
| 41 | const refused=Symbol('unsupported package target') |
| 42 | function target(value: any): string | undefined | typeof refused { |
| 43 | if (typeof value === 'string') return value |
| 44 | if (!value || Array.isArray(value) || typeof value !== 'object') return refused |
| 45 | for (const [condition, child] of Object.entries(value)) { |
| 46 | if (['node', 'import', 'default'].includes(condition)) { const picked = target(child); if (picked !== undefined) return picked } |
| 47 | } |
| 48 | } |
| 49 | let entry: string | undefined | typeof refused |
| 50 | if (data.exports !== undefined) { |
| 51 | const value = data.exports |
| 52 | entry = value && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).some(k => k.startsWith('.')) |
| 53 | ? target(value[subpath]) : subpath === '.' ? target(value) : undefined |
| 54 | } else if (subpath === '.') entry = typeof data.main === 'string' ? data.main : './index.js' |
| 55 | else entry = subpath |
| 56 | if (typeof entry!=='string' || !entry.startsWith('./') || !plain(entry.slice(2))) return |
| 57 | const path = `${packageRoot}/${entry.slice(2)}` |
| 58 | if (!/\.(mjs|js|mts)$/.test(path) || !SHA.test(files[path] ?? '')) return |
| 59 | return { name, path, sha256: files[path] } |
| 60 | } |
| 61 | |
| 62 | export interface PresetCatalogRow { |
| 63 | id: string |
| 64 | trust: 'system' | 'user' |
| 65 | name?: string |
| 66 | description?: string |
| 67 | order?: number |
| 68 | broken?: string |
| 69 | is_default: boolean |
| 70 | entry?: { path: string; sha256: string } |
| 71 | } |
| 72 | export interface PresetCatalog { |
| 73 | version: 1 |
| 74 | default?: string |
| 75 | mode_selection_enabled: boolean |
| 76 | presets: PresetCatalogRow[] |
| 77 | /** Pure file projection; conditional gates stay conditional until mounted. */ |
| 78 | inventory: Record<string, {path: string; rows: AgentPresetCompositionRow[]} | {path: string; broken: string}> |
| 79 | } |
| 80 | export interface PreparedPreset { |
| 81 | metadata: PresetCatalogRow |
| 82 | composition: ReviewedComposition |
| 83 | /** Original source for read/inventory; never written by the host. */ |
| 84 | source: string |
| 85 | } |
| 86 | export interface PresetReviewInput { |
| 87 | kind: 'agent-presets' |
| 88 | composition: ReviewedComposition |
| 89 | /** Exact documents only; other module bytes remain in composition.files. */ |
| 90 | documents: { path: string; sha256: string; source: string }[] |
| 91 | directories: string[] |
| 92 | } |
| 93 | export interface PresetReview { |
| 94 | catalog: PresetCatalog |
| 95 | presets: PreparedPreset[] |
| 96 | } |
| 97 | |
| 98 | /** Nonexecuting discovery: no FS calls, home expansion or ambient package lookup. */ |
| 99 | export async function reviewAgentPresets(input: PresetReviewInput): Promise<PresetReview> { |
| 100 | const { composition } = input |
| 101 | const files = composition.files |
| 102 | if (!files || Object.keys(files).length > 4096 || !Array.isArray(input.directories) || input.directories.length > 4096) fail('invalid source inventory') |
| 103 | const base = resolve('codewhale-reviewed-agent-presets') |
| 104 | const baseUrl = pathToFileURL(`${base}${sep}`).href |
| 105 | function key(path: string): string { |
| 106 | const inside = relative(base, path).split(sep).join('/') |
| 107 | if (!plain(inside)) fail('path escapes the admitted source root') |
| 108 | return inside |
| 109 | } |
| 110 | const documents = new Map<string, string>() |
| 111 | let bytes = 0 |
| 112 | for (const doc of input.documents) { |
| 113 | if (!plain(doc.path) || !SHA.test(doc.sha256) || files[doc.path] !== doc.sha256 || hash(doc.source) !== doc.sha256 || documents.has(doc.path)) fail('document changed or is outside the source receipt') |
| 114 | bytes += Buffer.byteLength(doc.source) |
| 115 | if (Buffer.byteLength(doc.source) > 1024 * 1024 || bytes > 4 * 1024 * 1024) fail('documents exceed their bound') |
| 116 | documents.set(doc.path, doc.source) |
| 117 | } |
| 118 | const directories = new Set(input.directories) |
| 119 | for (const path of directories) if (!plain(path)) fail('invalid source directory') |
| 120 | for (const path of Object.keys(files)) if (!plain(path) || !SHA.test(files[path])) fail('invalid source file receipt') |
| 121 | const invalid = new Set<string>() |
| 122 | const parsed = new Map<string, any[]>() |
| 123 | const io: DiscoveryIO = { |
| 124 | async readFile(path) { const relative = key(path); if (invalid.has(relative)) throw new Error('composition exceeds its data/shape bound'); const text = documents.get(relative); if (text === undefined) throw new Error('document not admitted'); return text }, |
| 125 | async stat(path) { return { isFile: () => Object.hasOwn(files, key(path)) } }, |
| 126 | async readdir(path) { |
| 127 | const prefix = `${key(path)}/` |
| 128 | if (!directories.has(prefix.slice(0, -1))) { const e = new Error('missing root'); Object.assign(e, { code: 'ENOENT' }); throw e } |
| 129 | return [...directories].filter(p => p.startsWith(prefix) && !p.slice(prefix.length).includes('/')) |
| 130 | .map(p => ({ name: p.slice(prefix.length), isDirectory: () => true })) |
| 131 | }, |
| 132 | } |
| 133 | // Import here avoids an initialization cycle; reviewer never imports a row. |
| 134 | const { reviewComposition, parsePresetComposition, compositionRowProblem, hasReviewedRowBridge } = await import('./composition.ts') |
| 135 | for (const [path, source] of documents) { |
| 136 | if (!path.endsWith('/agent.cordis.yml')) continue |
| 137 | try { parsed.set(path, parsePresetComposition(source)) } catch { invalid.add(path) } |
| 138 | } |
| 139 | const top = reviewComposition(composition) |
| 140 | const rosterRows: any[] = [] |
| 141 | function visit(rows: any[]) { |
| 142 | for (const row of rows) { |
| 143 | if (row.group === true || row.name === 'cordis:group' || row.name === '@deepseek-ai/cordis-plugin-group') visit(row.config ?? []) |
| 144 | else if (row.name === AGENT_PRESETS) rosterRows.push(row) |
| 145 | } |
| 146 | } |
| 147 | visit(top.entries) |
| 148 | if (rosterRows.length !== 1) fail('exactly one raw roster row is required') |
| 149 | const config = rosterRows[0].config ?? {} |
| 150 | if (!config || Array.isArray(config) || typeof config !== 'object' || Object.keys(config).some(k => !['default', 'roots', 'includeShippedRoot', 'includeUserRoot'].includes(k))) fail('roster configuration must be literal upstream fields') |
| 151 | for (const k of ['includeShippedRoot', 'includeUserRoot']) if (config[k] !== undefined && typeof config[k] !== 'boolean') fail('root gates must be literal booleans') |
| 152 | if (config.default !== undefined && (typeof config.default !== 'string' || !PRESET_ID.test(config.default) || config.default.length > 64)) fail('invalid default id') |
| 153 | const roots: PresetRoot[] = [] |
| 154 | if (config.includeShippedRoot !== false) { |
| 155 | const candidates = [...documents.entries()].filter(([p, text]) => { |
| 156 | if (p !== 'package.json' && !p.endsWith('/package.json')) return false |
| 157 | try { return JSON.parse(text).name === AGENT_PRESETS } catch { return false } |
| 158 | }) |
| 159 | if (candidates.length !== 1) fail('shipped root requires exactly one admitted agent-presets package') |
| 160 | const folder = dirname(candidates[0][0]).split(sep).join('/') |
| 161 | const root = folder === '.' ? 'presets' : `${folder}/presets` |
| 162 | if (!directories.has(root)) fail('shipped preset root was not admitted') |
| 163 | roots.push({ path: resolve(base, root), trust: 'system' }) |
| 164 | } |
| 165 | if (config.roots !== undefined && !Array.isArray(config.roots)) fail('roots must be a literal list') |
| 166 | for (const root of config.roots ?? []) { |
| 167 | if (!root || Object.keys(root).some(k => !['path', 'trust'].includes(k)) || !plain(root.path) || !['system', 'user'].includes(root.trust)) fail('configured roots must stay inside the admitted package') |
| 168 | if (!directories.has(root.path)) fail('configured preset root was not admitted') |
| 169 | roots.push({ path: resolve(base, root.path), trust: root.trust }) |
| 170 | } |
| 171 | if (config.includeUserRoot !== false) { |
| 172 | if (!directories.has('.agent-presets')) fail('user root requires an admitted .agent-presets tree; ambient home discovery is refused') |
| 173 | roots.push({ path: resolve(base, '.agent-presets'), trust: 'user' }) |
| 174 | } |
| 175 | if (roots.length > 64) fail('too many roots') |
| 176 | const declared = new Map(composition.modules.map(m => [m.name, m])) |
| 177 | if (declared.size !== composition.modules.length) fail('duplicate module name receipt') |
| 178 | const found = await discoverPresets(roots, baseUrl, name => hasReviewedRowBridge(name) || declared.has(name) || containedPackageModule(name, documents, files) !== undefined, io) |
| 179 | if (found.length > MAX_PRESETS) fail('too many presets') |
| 180 | const prepared: PreparedPreset[] = [] |
| 181 | const rows: PresetCatalogRow[] = [] |
| 182 | const inventory: PresetCatalog['inventory'] = Object.create(null) |
| 183 | for (const preset of found) { |
| 184 | if (preset.id.length > 64 || (preset.name?.length ?? 0) > 1024 || (preset.description?.length ?? 0) > 4096) fail('preset metadata exceeds its bound') |
| 185 | const metadata: PresetCatalogRow = { id: preset.id, trust: preset.trust, is_default: preset.id === config.default, |
| 186 | ...(preset.name === undefined ? {} : { name: preset.name }), ...(preset.description === undefined ? {} : { description: preset.description }), |
| 187 | ...(preset.order === undefined ? {} : { order: preset.order }), ...(preset.broken === undefined ? {} : { broken: preset.broken }) } |
| 188 | rows.push(metadata) |
| 189 | const path = key(preset.path) |
| 190 | inventory[preset.id] = {path, ...(preset.broken !== undefined ? {broken:preset.broken} : await fileComposition(preset.path, () => { throw new Error('conditional until mounted') }, io.readFile))} |
| 191 | if (preset.broken !== undefined) continue |
| 192 | const source = await io.readFile(preset.path, 'utf8') |
| 193 | const entries = parsed.get(key(preset.path))! |
| 194 | const modules = new Map<string, ReviewedModule>() |
| 195 | function resolveRows(children: any[]) { |
| 196 | for (const row of children) { |
| 197 | if (Boolean(row.disabled) && !isJsExpr(row.disabled)) continue |
| 198 | if (row.group === true || row.name === 'cordis:group' || row.name === '@deepseek-ai/cordis-plugin-group') { resolveRows(row.config ?? []); continue } |
| 199 | const specifier = classifyRowSpecifier(row.name) |
| 200 | if (row.name === PERSONA) normalizePersonaConfig(row.config) |
| 201 | if (specifier.kind === 'builtin' || hasReviewedRowBridge(row.name)) continue |
| 202 | if (row.name === AGENT_PRESETS) fail('nested roster cannot create a second selection authority') |
| 203 | const problem=compositionRowProblem(row.name);if(problem) fail(problem) |
| 204 | let receipt: ReviewedModule | undefined |
| 205 | if (specifier.kind === 'package') receipt = declared.get(row.name) ?? containedPackageModule(row.name, documents, files) |
| 206 | else { |
| 207 | const url = specifier.kind === 'file' ? new URL(specifier.specifier) : new URL(specifier.specifier, pathToFileURL(preset.path).href) |
| 208 | const path = key(fileURLToPath(url)) |
| 209 | if (files[path] !== undefined) receipt = { name: row.name, path, sha256: files[path] } |
| 210 | } |
| 211 | if (!receipt || files[receipt.path] !== receipt.sha256) { if (isJsExpr(row.disabled)) continue; fail('row module has no exact admitted receipt') } |
| 212 | modules.set(row.name, receipt) |
| 213 | } |
| 214 | } |
| 215 | try { resolveRows(entries) } catch (error) { metadata.broken = error instanceof Error ? error.message : 'preset preparation refused'; continue } |
| 216 | const layer = JSON.stringify([{ insert: entries }]) |
| 217 | const spec: ReviewedComposition = { version: 1, layers: [{ path: key(preset.path), sha256: hash(layer), source: layer }], modules: [...modules.values()], files } |
| 218 | reviewComposition(spec) |
| 219 | prepared.push({ metadata, composition: spec, source }) |
| 220 | } |
| 221 | if (config.default !== undefined && !prepared.some(p => p.metadata.id === config.default)) fail('default preset is missing or broken') |
| 222 | if (!prepared.length) fail('no admitted usable preset') |
| 223 | if (Buffer.byteLength(JSON.stringify(inventory)) > 4 * 1024 * 1024) fail('composition inventory exceeds its bound') |
| 224 | return { catalog: { version: 1, ...(config.default === undefined ? {} : {default:config.default}), mode_selection_enabled: true, presets: rows, inventory }, presets: prepared } |
| 225 | } |
| 226 | |
| 227 | export interface PresetMountConfig { |
| 228 | catalog: PresetCatalog |
| 229 | selected: PreparedPreset |
| 230 | base_url: string |
| 231 | } |
| 232 | /** The raw roster row is a scope-local view of Core's captured selected entry. |
| 233 | * Its subtree owns the real five contributions. It cannot select/reparent an |
| 234 | * Agent, start a turn, write a session, or mutate roots/settings. |
| 235 | */ |
| 236 | export function defineReviewedAgentPresets(mount: (ctx: any, base: string, spec: ReviewedComposition) => Promise<CompositionMount>) { |
| 237 | return class ReviewedAgentPresets extends Service { |
| 238 | static inject = ['loader'] |
| 239 | private readonly snapshot: PresetMountConfig |
| 240 | private readonly selfCtx: any |
| 241 | constructor(ctx: any, config: { codewhale_reviewed?: PresetMountConfig }) { |
| 242 | super(ctx, 'agentPresets') |
| 243 | this.selfCtx = ctx |
| 244 | const snapshot = config?.codewhale_reviewed |
| 245 | if (!snapshot || snapshot.catalog?.version !== 1 || !snapshot.catalog.presets.some(p => p.id === snapshot.selected?.metadata.id && !p.broken)) fail('raw roster needs an installer-minted preset receipt') |
| 246 | this.snapshot = structuredClone(snapshot) |
| 247 | } |
| 248 | private mounting?: Promise<CompositionMount> |
| 249 | private mounted?: CompositionMount |
| 250 | mountSelected() { return this.mounting ??= mount(this.selfCtx, this.snapshot.base_url, this.snapshot.selected.composition).then(result => this.mounted=result) } |
| 251 | async list() { return this.snapshot.catalog.presets.map(row => ({...structuredClone(row),path:fileURLToPath(new URL(this.snapshot.catalog.inventory[row.id].path,this.snapshot.base_url))})) } |
| 252 | async remoteExportList() { return {presets:this.snapshot.catalog.presets.map(({is_default,entry,...row})=>({...structuredClone(row),isDefault:is_default})),authorable:false,defaultId:this.defaultId,modeSelectionEnabled:this.snapshot.catalog.mode_selection_enabled} } |
| 253 | get defaultId() { return this.snapshot.catalog.default } |
| 254 | get authorable() { return false } |
| 255 | async resolve(id = this.defaultId) { |
| 256 | const row = (await this.list()).find(p => p.id === id) |
| 257 | if (!row) fail('preset not found') |
| 258 | return row |
| 259 | } |
| 260 | composedPreset() { return this.snapshot.selected.metadata.id } |
| 261 | async compositionInventory() { |
| 262 | return this.snapshot.catalog.presets.map(row => { |
| 263 | const identity={id:row.id,trust:row.trust,...(row.name===undefined?{}:{name:row.name}),isDefault:row.id===this.defaultId} |
| 264 | if (row.id===this.composedPreset() && this.mounted) return {...identity,rows:mountedCompositionRows(this.mounted.tree)} |
| 265 | const read=this.snapshot.catalog.inventory[row.id] |
| 266 | if (row.broken || 'broken' in read) return {...identity,broken:row.broken ?? ('broken' in read?read.broken:undefined),rows:[]} |
| 267 | return {...identity,rows:structuredClone(read.rows)} |
| 268 | }) |
| 269 | } |
| 270 | async readDocument(id: string) { |
| 271 | if (id !== this.composedPreset()) fail('read another preset through Core file authority') |
| 272 | return { agentPreset: id, trust: this.snapshot.selected.metadata.trust, content: this.snapshot.selected.source } |
| 273 | } |
| 274 | select() { fail('select through Core AgentProfile/native preset selection before a turn') } |
| 275 | recompose() { fail('Core owns the captured agent selection and blank-session check') } |
| 276 | copy() { fail('copy through Core file/install/review/reload authority') } |
| 277 | remove() { fail('delete through Core file/install/review/reload authority') } |
| 278 | } |
| 279 | } |
| 280 |