sync: update from private repo (dfadcd5f)
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync
2026-06-23 06:38:48 +00:00
parent 6a2f2cc736
commit 29ccaf1e92
377 changed files with 31028 additions and 8994 deletions
+173
View File
@@ -0,0 +1,173 @@
import type { ToolDef } from '../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from './core.js';
import type { Repository } from '../../db/repository.js';
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
const MAX_TITLE_LEN = 200;
const MAX_DESC_LEN = 4000;
let _repo: Repository | null = null;
export function setCalendarRepo(repo: Repository | null): void {
_repo = repo;
}
const ADD_CALENDAR_EVENT_DEF: ToolDef = {
type: 'function',
function: {
name: 'AddCalendarEvent',
description:
'実行中タスクのスペースのカレンダーに予定を1件追加する(created_by=agent)。詳細は ReadToolDoc({ name: "AddCalendarEvent" }) で取得可能。',
parameters: {
type: 'object',
properties: {
title: { type: 'string', description: '予定のタイトル(必須)' },
date: { type: 'string', description: '開始日 YYYY-MM-DD(ローカル日付、必須)' },
end_date: { type: 'string', description: '終了日 YYYY-MM-DD。複数日にまたがる予定のとき指定。省略すると単日' },
time: { type: 'string', description: 'HH:MM。省略すると終日扱い' },
description: { type: 'string', description: '予定の補足説明(任意)' },
},
required: ['title', 'date'],
},
},
};
const LIST_CALENDAR_EVENTS_DEF: ToolDef = {
type: 'function',
function: {
name: 'ListCalendarEvents',
description:
'実行中タスクのスペースのカレンダー予定を日付範囲で一覧する。詳細は ReadToolDoc({ name: "AddCalendarEvent" }) で取得可能。',
parameters: {
type: 'object',
properties: {
from: { type: 'string', description: '範囲開始 YYYY-MM-DD(任意)' },
to: { type: 'string', description: '範囲終了 YYYY-MM-DD(任意)' },
},
required: [],
},
},
};
export const TOOL_DEFS: Record<string, ToolDef> = {
AddCalendarEvent: ADD_CALENDAR_EVENT_DEF,
ListCalendarEvents: LIST_CALENDAR_EVENTS_DEF,
};
export async function executeTool(
name: string,
input: Record<string, unknown>,
ctx: ToolContext,
): Promise<ToolResult | null> {
if (name === 'AddCalendarEvent') return executeAddCalendarEvent(input, ctx);
if (name === 'ListCalendarEvents') return executeListCalendarEvents(input, ctx);
return null;
}
async function executeAddCalendarEvent(
input: Record<string, unknown>,
ctx: ToolContext,
): Promise<ToolResult> {
if (!_repo) {
return { output: 'Calendar repo is not initialized', isError: true };
}
const spaceId = ctx.spaceId;
if (!spaceId) {
return {
output:
'このタスクはスペースに属していないため予定を追加できません(旧 local タスクには spaceId がありません)',
isError: true,
};
}
const title = input['title'];
const date = input['date'];
const endDateInput = input['end_date'];
const time = input['time'];
const description = input['description'];
if (typeof title !== 'string' || !title.trim()) {
return { output: 'title は必須です', isError: true };
}
if (title.length > MAX_TITLE_LEN) {
return { output: `title が長すぎます(最大 ${MAX_TITLE_LEN} 文字)`, isError: true };
}
if (typeof date !== 'string' || !DATE_PATTERN.test(date)) {
return { output: 'date は YYYY-MM-DD 形式で指定してください', isError: true };
}
let endDate: string | null = null;
if (endDateInput !== undefined && endDateInput !== null && endDateInput !== '') {
if (typeof endDateInput !== 'string' || !DATE_PATTERN.test(endDateInput)) {
return { output: 'end_date は YYYY-MM-DD 形式で指定してください', isError: true };
}
if (endDateInput < date) {
return { output: 'end_date は date 以降の日付にしてください', isError: true };
}
endDate = endDateInput;
}
if (time !== undefined && time !== null && time !== '') {
if (typeof time !== 'string' || !TIME_PATTERN.test(time)) {
return { output: 'time は HH:MM 形式で指定してください', isError: true };
}
}
if (description !== undefined && description !== null) {
if (typeof description !== 'string') {
return { output: 'description は文字列で指定してください', isError: true };
}
if (description.length > MAX_DESC_LEN) {
return { output: `description が長すぎます(最大 ${MAX_DESC_LEN} 文字)`, isError: true };
}
}
const sourceTaskId = ctx.taskId ? Number(ctx.taskId) : null;
const ev = await _repo.createCalendarEvent({
spaceId,
ownerId: ctx.ownerId ?? null,
date,
endDate,
time: typeof time === 'string' && time !== '' ? time : null,
title: title.trim(),
description: typeof description === 'string' ? description : null,
createdBy: 'agent',
sourceTaskId: Number.isFinite(sourceTaskId) ? sourceTaskId : null,
});
return {
output: `予定を追加しました: ${ev.date}${ev.time ? ` ${ev.time}` : '(終日)'} 「${ev.title}」(id=${ev.id})`,
isError: false,
};
}
async function executeListCalendarEvents(
input: Record<string, unknown>,
ctx: ToolContext,
): Promise<ToolResult> {
if (!_repo) {
return { output: 'Calendar repo is not initialized', isError: true };
}
const spaceId = ctx.spaceId;
if (!spaceId) {
return {
output: 'このタスクはスペースに属していないため予定を一覧できません',
isError: true,
};
}
const from = input['from'];
const to = input['to'];
if (from !== undefined && (typeof from !== 'string' || !DATE_PATTERN.test(from))) {
return { output: 'from は YYYY-MM-DD 形式で指定してください', isError: true };
}
if (to !== undefined && (typeof to !== 'string' || !DATE_PATTERN.test(to))) {
return { output: 'to は YYYY-MM-DD 形式で指定してください', isError: true };
}
const events = await _repo.listCalendarEvents(spaceId, {
from: typeof from === 'string' ? from : undefined,
to: typeof to === 'string' ? to : undefined,
});
if (events.length === 0) {
return { output: '指定範囲に予定はありません', isError: false };
}
const lines = events.map(
(e) => `- ${e.date}${e.time ? ` ${e.time}` : '(終日)'} 「${e.title}」(id=${e.id}, ${e.createdBy})`,
);
return { output: lines.join('\n'), isError: false };
}