Files
Void-Homelab/lib/ai/agent/tools/read.js
2026-06-04 20:07:22 +10:00

36 lines
1.4 KiB
JavaScript

import { pool } from '../../../db/pool.js';
const TABLE = { page: 'pages', ref: 'refs', task: 'tasks', conversation: 'conversations' };
export const readTool = {
name: 'read',
description: 'Read a single entity (page, ref, task, conversation) by id for grounding.',
input_schema: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['page', 'ref', 'task', 'conversation'] },
id: { type: 'string', description: 'uuid of the entity' }
},
required: ['kind', 'id']
},
async handler({ kind, id }, ctx = {}) {
const table = TABLE[kind];
if (!table) return { error: `unknown kind "${kind}"` };
const { rows: [row] } = await pool.query(`SELECT * FROM ${table} WHERE id=$1`, [id]);
if (!row) return { error: `${kind} ${id} not found` };
// Space scoping. When a Space is bound (external scoped agents — and Dross,
// which operates within one Space), an entity that carries space_id must
// match it. Kinds without a space_id column (conversations) can't be proven
// in-scope, so they're denied to spaceScoped callers (external agents) only.
// Owner/Dross with no bound Space (ctx.space_id == null) → unrestricted.
if (ctx.space_id != null) {
if (row.space_id !== undefined) {
if (row.space_id !== ctx.space_id) return { error: `${kind} ${id} not found` };
} else if (ctx.spaceScoped) {
return { error: `${kind} ${id} not found` };
}
}
return row;
}
};