36 lines
1.4 KiB
JavaScript
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;
|
|
}
|
|
};
|