@agenticmail/enterprise 0.5.170 → 0.5.172
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-LSANOO23.js +898 -0
- package/dist/chunk-TX2QYPQN.js +2195 -0
- package/dist/chunk-XY3B3MYT.js +17682 -0
- package/dist/cli-agent-2XSLVRCT.js +1007 -0
- package/dist/cli-agent-MFI6ZMPW.js +1007 -0
- package/dist/cli-serve-VMOFSJFI.js +34 -0
- package/dist/cli.js +3 -3
- package/dist/index.js +3 -3
- package/dist/routes-RGIHFP65.js +6992 -0
- package/dist/runtime-OF4ISGPR.js +49 -0
- package/dist/server-UWD5SXCF.js +12 -0
- package/dist/setup-3ASZ4FJ5.js +20 -0
- package/package.json +1 -1
- package/src/agent-tools/tools/google/chat.ts +393 -0
- package/src/agent-tools/tools/google/forms.ts +367 -0
- package/src/agent-tools/tools/google/index.ts +9 -0
- package/src/agent-tools/tools/google/slides.ts +454 -0
- package/src/cli-agent.ts +16 -0
- package/src/engine/agent-routes.ts +6 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Slides API Tools
|
|
3
|
+
*
|
|
4
|
+
* Lets agents create, read, and update Google Slides presentations.
|
|
5
|
+
* Uses Google Slides API v1: https://slides.googleapis.com
|
|
6
|
+
*
|
|
7
|
+
* Required OAuth scope: https://www.googleapis.com/auth/presentations
|
|
8
|
+
* or https://www.googleapis.com/auth/drive (for creation via Drive)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { AnyAgentTool, ToolCreationOptions } from '../../types.js';
|
|
12
|
+
import type { GoogleToolsConfig } from './index.js';
|
|
13
|
+
import { jsonResult, errorResult } from '../../common.js';
|
|
14
|
+
|
|
15
|
+
// ─── Helper ─────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
async function slidesApi(token: string, path: string, opts?: { method?: string; body?: any; query?: Record<string, string> }): Promise<any> {
|
|
18
|
+
const url = new URL(`https://slides.googleapis.com/v1${path}`);
|
|
19
|
+
if (opts?.query) for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v);
|
|
20
|
+
const res = await fetch(url.toString(), {
|
|
21
|
+
method: opts?.method || 'GET',
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${token}`,
|
|
24
|
+
...(opts?.body ? { 'Content-Type': 'application/json' } : {}),
|
|
25
|
+
},
|
|
26
|
+
...(opts?.body ? { body: JSON.stringify(opts.body) } : {}),
|
|
27
|
+
});
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const errText = await res.text();
|
|
30
|
+
throw new Error(`Google Slides API ${res.status}: ${errText}`);
|
|
31
|
+
}
|
|
32
|
+
if (res.status === 204) return {};
|
|
33
|
+
return res.json();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ─── Tool Definitions ───────────────────────────────────
|
|
37
|
+
|
|
38
|
+
export function createGoogleSlidesTools(config: GoogleToolsConfig, _options?: ToolCreationOptions): AnyAgentTool[] {
|
|
39
|
+
const tp = config.tokenProvider;
|
|
40
|
+
|
|
41
|
+
return [
|
|
42
|
+
// ─── Create Presentation ────────────────────────────
|
|
43
|
+
{
|
|
44
|
+
name: 'google_slides_create',
|
|
45
|
+
description: 'Create a new blank Google Slides presentation with a given title.',
|
|
46
|
+
category: 'productivity' as const,
|
|
47
|
+
parameters: {
|
|
48
|
+
type: 'object' as const,
|
|
49
|
+
properties: {
|
|
50
|
+
title: { type: 'string', description: 'Presentation title' },
|
|
51
|
+
},
|
|
52
|
+
required: ['title'],
|
|
53
|
+
},
|
|
54
|
+
async execute(_id: string, input: any) {
|
|
55
|
+
try {
|
|
56
|
+
const token = await tp.getAccessToken();
|
|
57
|
+
const result = await slidesApi(token, '/presentations', {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
body: { title: input.title },
|
|
60
|
+
});
|
|
61
|
+
return jsonResult({
|
|
62
|
+
presentationId: result.presentationId,
|
|
63
|
+
title: result.title,
|
|
64
|
+
url: `https://docs.google.com/presentation/d/${result.presentationId}/edit`,
|
|
65
|
+
slideCount: (result.slides || []).length,
|
|
66
|
+
});
|
|
67
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// ─── Get Presentation ───────────────────────────────
|
|
72
|
+
{
|
|
73
|
+
name: 'google_slides_get',
|
|
74
|
+
description: 'Get metadata and structure of a Google Slides presentation — title, slides, layouts, masters.',
|
|
75
|
+
category: 'productivity' as const,
|
|
76
|
+
parameters: {
|
|
77
|
+
type: 'object' as const,
|
|
78
|
+
properties: {
|
|
79
|
+
presentationId: { type: 'string', description: 'Presentation ID (from URL or create response)' },
|
|
80
|
+
},
|
|
81
|
+
required: ['presentationId'],
|
|
82
|
+
},
|
|
83
|
+
async execute(_id: string, input: any) {
|
|
84
|
+
try {
|
|
85
|
+
const token = await tp.getAccessToken();
|
|
86
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}`);
|
|
87
|
+
const slides = (result.slides || []).map((s: any, i: number) => {
|
|
88
|
+
const textElements: string[] = [];
|
|
89
|
+
for (const el of (s.pageElements || [])) {
|
|
90
|
+
if (el.shape?.text?.textElements) {
|
|
91
|
+
for (const te of el.shape.text.textElements) {
|
|
92
|
+
if (te.textRun?.content?.trim()) textElements.push(te.textRun.content.trim());
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
slideIndex: i,
|
|
98
|
+
objectId: s.objectId,
|
|
99
|
+
layoutId: s.slideProperties?.layoutObjectId,
|
|
100
|
+
textContent: textElements.join(' | ') || '(no text)',
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
return jsonResult({
|
|
104
|
+
presentationId: result.presentationId,
|
|
105
|
+
title: result.title,
|
|
106
|
+
locale: result.locale,
|
|
107
|
+
slideCount: slides.length,
|
|
108
|
+
slides,
|
|
109
|
+
url: `https://docs.google.com/presentation/d/${result.presentationId}/edit`,
|
|
110
|
+
});
|
|
111
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// ─── Get Slide Page ─────────────────────────────────
|
|
116
|
+
{
|
|
117
|
+
name: 'google_slides_get_page',
|
|
118
|
+
description: 'Get details of a specific slide page including all elements, text, images, and shapes.',
|
|
119
|
+
category: 'productivity' as const,
|
|
120
|
+
parameters: {
|
|
121
|
+
type: 'object' as const,
|
|
122
|
+
properties: {
|
|
123
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
124
|
+
pageObjectId: { type: 'string', description: 'Slide page object ID' },
|
|
125
|
+
},
|
|
126
|
+
required: ['presentationId', 'pageObjectId'],
|
|
127
|
+
},
|
|
128
|
+
async execute(_id: string, input: any) {
|
|
129
|
+
try {
|
|
130
|
+
const token = await tp.getAccessToken();
|
|
131
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}/pages/${input.pageObjectId}`);
|
|
132
|
+
const elements = (result.pageElements || []).map((el: any) => ({
|
|
133
|
+
objectId: el.objectId,
|
|
134
|
+
type: el.shape ? 'shape' : el.image ? 'image' : el.table ? 'table' : el.line ? 'line' : el.video ? 'video' : 'other',
|
|
135
|
+
title: el.title,
|
|
136
|
+
description: el.description,
|
|
137
|
+
text: el.shape?.text?.textElements?.filter((t: any) => t.textRun?.content).map((t: any) => t.textRun.content).join('') || undefined,
|
|
138
|
+
transform: el.transform ? { scaleX: el.transform.scaleX, scaleY: el.transform.scaleY, translateX: el.transform.translateX, translateY: el.transform.translateY } : undefined,
|
|
139
|
+
size: el.size,
|
|
140
|
+
}));
|
|
141
|
+
return jsonResult({ pageObjectId: result.objectId, elements });
|
|
142
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
// ─── Get Slide Thumbnail ────────────────────────────
|
|
147
|
+
{
|
|
148
|
+
name: 'google_slides_thumbnail',
|
|
149
|
+
description: 'Get a thumbnail image URL for a specific slide. Useful for previewing slides.',
|
|
150
|
+
category: 'productivity' as const,
|
|
151
|
+
parameters: {
|
|
152
|
+
type: 'object' as const,
|
|
153
|
+
properties: {
|
|
154
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
155
|
+
pageObjectId: { type: 'string', description: 'Slide page object ID' },
|
|
156
|
+
thumbnailSize: { type: 'string', description: 'Size: "SMALL", "MEDIUM", or "LARGE" (default: MEDIUM)' },
|
|
157
|
+
},
|
|
158
|
+
required: ['presentationId', 'pageObjectId'],
|
|
159
|
+
},
|
|
160
|
+
async execute(_id: string, input: any) {
|
|
161
|
+
try {
|
|
162
|
+
const token = await tp.getAccessToken();
|
|
163
|
+
const query: Record<string, string> = {};
|
|
164
|
+
if (input.thumbnailSize) query['thumbnailProperties.thumbnailSize'] = input.thumbnailSize;
|
|
165
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}/pages/${input.pageObjectId}/thumbnail`, { query });
|
|
166
|
+
return jsonResult({ contentUrl: result.contentUrl, width: result.width, height: result.height });
|
|
167
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// ─── Add Slide ──────────────────────────────────────
|
|
172
|
+
{
|
|
173
|
+
name: 'google_slides_add_slide',
|
|
174
|
+
description: 'Add a new slide to a presentation. Can specify layout (BLANK, TITLE, TITLE_AND_BODY, TITLE_AND_TWO_COLUMNS, etc.).',
|
|
175
|
+
category: 'productivity' as const,
|
|
176
|
+
parameters: {
|
|
177
|
+
type: 'object' as const,
|
|
178
|
+
properties: {
|
|
179
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
180
|
+
insertionIndex: { type: 'string', description: 'Position to insert slide (0-based). Omit to append.' },
|
|
181
|
+
layout: { type: 'string', description: 'Predefined layout: BLANK, TITLE, TITLE_AND_BODY, TITLE_AND_TWO_COLUMNS, TITLE_ONLY, SECTION_HEADER, SECTION_TITLE_AND_DESCRIPTION, ONE_COLUMN_TEXT, MAIN_POINT, BIG_NUMBER (default: BLANK)' },
|
|
182
|
+
},
|
|
183
|
+
required: ['presentationId'],
|
|
184
|
+
},
|
|
185
|
+
async execute(_id: string, input: any) {
|
|
186
|
+
try {
|
|
187
|
+
const token = await tp.getAccessToken();
|
|
188
|
+
const req: any = { createSlide: {} };
|
|
189
|
+
if (input.insertionIndex !== undefined) req.createSlide.insertionIndex = parseInt(input.insertionIndex);
|
|
190
|
+
if (input.layout) req.createSlide.slideLayoutReference = { predefinedLayout: input.layout };
|
|
191
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: { requests: [req] },
|
|
194
|
+
});
|
|
195
|
+
const slideId = result.replies?.[0]?.createSlide?.objectId;
|
|
196
|
+
return jsonResult({ created: true, slideObjectId: slideId });
|
|
197
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// ─── Insert Text ────────────────────────────────────
|
|
202
|
+
{
|
|
203
|
+
name: 'google_slides_insert_text',
|
|
204
|
+
description: 'Insert text into a shape or text box on a slide. Requires the objectId of the shape.',
|
|
205
|
+
category: 'productivity' as const,
|
|
206
|
+
parameters: {
|
|
207
|
+
type: 'object' as const,
|
|
208
|
+
properties: {
|
|
209
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
210
|
+
objectId: { type: 'string', description: 'Object ID of the shape/text box to insert into' },
|
|
211
|
+
text: { type: 'string', description: 'Text to insert' },
|
|
212
|
+
insertionIndex: { type: 'string', description: 'Character index to insert at (default: 0 = beginning)' },
|
|
213
|
+
},
|
|
214
|
+
required: ['presentationId', 'objectId', 'text'],
|
|
215
|
+
},
|
|
216
|
+
async execute(_id: string, input: any) {
|
|
217
|
+
try {
|
|
218
|
+
const token = await tp.getAccessToken();
|
|
219
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
body: {
|
|
222
|
+
requests: [{
|
|
223
|
+
insertText: {
|
|
224
|
+
objectId: input.objectId,
|
|
225
|
+
text: input.text,
|
|
226
|
+
insertionIndex: parseInt(input.insertionIndex || '0'),
|
|
227
|
+
},
|
|
228
|
+
}],
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
return jsonResult({ inserted: true });
|
|
232
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// ─── Replace All Text ───────────────────────────────
|
|
237
|
+
{
|
|
238
|
+
name: 'google_slides_replace_text',
|
|
239
|
+
description: 'Replace all occurrences of text in a presentation. Great for template-based slide generation.',
|
|
240
|
+
category: 'productivity' as const,
|
|
241
|
+
parameters: {
|
|
242
|
+
type: 'object' as const,
|
|
243
|
+
properties: {
|
|
244
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
245
|
+
findText: { type: 'string', description: 'Text to find (e.g. "{{title}}")' },
|
|
246
|
+
replaceText: { type: 'string', description: 'Text to replace with' },
|
|
247
|
+
matchCase: { type: 'string', description: '"true" for case-sensitive match (default: false)' },
|
|
248
|
+
},
|
|
249
|
+
required: ['presentationId', 'findText', 'replaceText'],
|
|
250
|
+
},
|
|
251
|
+
async execute(_id: string, input: any) {
|
|
252
|
+
try {
|
|
253
|
+
const token = await tp.getAccessToken();
|
|
254
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
255
|
+
method: 'POST',
|
|
256
|
+
body: {
|
|
257
|
+
requests: [{
|
|
258
|
+
replaceAllText: {
|
|
259
|
+
containsText: { text: input.findText, matchCase: input.matchCase === 'true' },
|
|
260
|
+
replaceText: input.replaceText,
|
|
261
|
+
},
|
|
262
|
+
}],
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
const count = result.replies?.[0]?.replaceAllText?.occurrencesChanged || 0;
|
|
266
|
+
return jsonResult({ replaced: true, occurrencesChanged: count });
|
|
267
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
// ─── Create Text Box ────────────────────────────────
|
|
272
|
+
{
|
|
273
|
+
name: 'google_slides_create_textbox',
|
|
274
|
+
description: 'Create a text box on a slide with specified position and size. Returns the objectId for inserting text.',
|
|
275
|
+
category: 'productivity' as const,
|
|
276
|
+
parameters: {
|
|
277
|
+
type: 'object' as const,
|
|
278
|
+
properties: {
|
|
279
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
280
|
+
pageObjectId: { type: 'string', description: 'Slide page object ID to add the textbox to' },
|
|
281
|
+
text: { type: 'string', description: 'Initial text for the textbox (optional)' },
|
|
282
|
+
x: { type: 'string', description: 'X position in EMU (1 inch = 914400 EMU). Default: 100pt' },
|
|
283
|
+
y: { type: 'string', description: 'Y position in EMU. Default: 100pt' },
|
|
284
|
+
width: { type: 'string', description: 'Width in EMU. Default: 300pt' },
|
|
285
|
+
height: { type: 'string', description: 'Height in EMU. Default: 50pt' },
|
|
286
|
+
},
|
|
287
|
+
required: ['presentationId', 'pageObjectId'],
|
|
288
|
+
},
|
|
289
|
+
async execute(_id: string, input: any) {
|
|
290
|
+
try {
|
|
291
|
+
const token = await tp.getAccessToken();
|
|
292
|
+
const PT = 12700; // 1pt in EMU
|
|
293
|
+
const objectId = 'textbox_' + Date.now();
|
|
294
|
+
const requests: any[] = [{
|
|
295
|
+
createShape: {
|
|
296
|
+
objectId,
|
|
297
|
+
shapeType: 'TEXT_BOX',
|
|
298
|
+
elementProperties: {
|
|
299
|
+
pageObjectId: input.pageObjectId,
|
|
300
|
+
size: {
|
|
301
|
+
width: { magnitude: parseInt(input.width || String(300 * PT)), unit: 'EMU' },
|
|
302
|
+
height: { magnitude: parseInt(input.height || String(50 * PT)), unit: 'EMU' },
|
|
303
|
+
},
|
|
304
|
+
transform: {
|
|
305
|
+
scaleX: 1, scaleY: 1,
|
|
306
|
+
translateX: parseInt(input.x || String(100 * PT)),
|
|
307
|
+
translateY: parseInt(input.y || String(100 * PT)),
|
|
308
|
+
unit: 'EMU',
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}];
|
|
313
|
+
if (input.text) {
|
|
314
|
+
requests.push({ insertText: { objectId, text: input.text, insertionIndex: 0 } });
|
|
315
|
+
}
|
|
316
|
+
await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
317
|
+
method: 'POST',
|
|
318
|
+
body: { requests },
|
|
319
|
+
});
|
|
320
|
+
return jsonResult({ created: true, objectId });
|
|
321
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
// ─── Add Image to Slide ─────────────────────────────
|
|
326
|
+
{
|
|
327
|
+
name: 'google_slides_add_image',
|
|
328
|
+
description: 'Add an image to a slide from a public URL.',
|
|
329
|
+
category: 'productivity' as const,
|
|
330
|
+
parameters: {
|
|
331
|
+
type: 'object' as const,
|
|
332
|
+
properties: {
|
|
333
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
334
|
+
pageObjectId: { type: 'string', description: 'Slide page to add image to' },
|
|
335
|
+
imageUrl: { type: 'string', description: 'Public URL of the image' },
|
|
336
|
+
x: { type: 'string', description: 'X position in EMU (default: 100pt)' },
|
|
337
|
+
y: { type: 'string', description: 'Y position in EMU (default: 100pt)' },
|
|
338
|
+
width: { type: 'string', description: 'Width in EMU (default: 400pt)' },
|
|
339
|
+
height: { type: 'string', description: 'Height in EMU (default: 300pt)' },
|
|
340
|
+
},
|
|
341
|
+
required: ['presentationId', 'pageObjectId', 'imageUrl'],
|
|
342
|
+
},
|
|
343
|
+
async execute(_id: string, input: any) {
|
|
344
|
+
try {
|
|
345
|
+
const token = await tp.getAccessToken();
|
|
346
|
+
const PT = 12700;
|
|
347
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
348
|
+
method: 'POST',
|
|
349
|
+
body: {
|
|
350
|
+
requests: [{
|
|
351
|
+
createImage: {
|
|
352
|
+
url: input.imageUrl,
|
|
353
|
+
elementProperties: {
|
|
354
|
+
pageObjectId: input.pageObjectId,
|
|
355
|
+
size: {
|
|
356
|
+
width: { magnitude: parseInt(input.width || String(400 * PT)), unit: 'EMU' },
|
|
357
|
+
height: { magnitude: parseInt(input.height || String(300 * PT)), unit: 'EMU' },
|
|
358
|
+
},
|
|
359
|
+
transform: {
|
|
360
|
+
scaleX: 1, scaleY: 1,
|
|
361
|
+
translateX: parseInt(input.x || String(100 * PT)),
|
|
362
|
+
translateY: parseInt(input.y || String(100 * PT)),
|
|
363
|
+
unit: 'EMU',
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
}],
|
|
368
|
+
},
|
|
369
|
+
});
|
|
370
|
+
const imageId = result.replies?.[0]?.createImage?.objectId;
|
|
371
|
+
return jsonResult({ created: true, imageObjectId: imageId });
|
|
372
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
// ─── Delete Slide ───────────────────────────────────
|
|
377
|
+
{
|
|
378
|
+
name: 'google_slides_delete_slide',
|
|
379
|
+
description: 'Delete a slide from a presentation by its object ID.',
|
|
380
|
+
category: 'productivity' as const,
|
|
381
|
+
parameters: {
|
|
382
|
+
type: 'object' as const,
|
|
383
|
+
properties: {
|
|
384
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
385
|
+
objectId: { type: 'string', description: 'Object ID of the slide to delete' },
|
|
386
|
+
},
|
|
387
|
+
required: ['presentationId', 'objectId'],
|
|
388
|
+
},
|
|
389
|
+
async execute(_id: string, input: any) {
|
|
390
|
+
try {
|
|
391
|
+
const token = await tp.getAccessToken();
|
|
392
|
+
await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
393
|
+
method: 'POST',
|
|
394
|
+
body: { requests: [{ deleteObject: { objectId: input.objectId } }] },
|
|
395
|
+
});
|
|
396
|
+
return jsonResult({ deleted: true });
|
|
397
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
// ─── Duplicate Slide ────────────────────────────────
|
|
402
|
+
{
|
|
403
|
+
name: 'google_slides_duplicate_slide',
|
|
404
|
+
description: 'Duplicate an existing slide in a presentation.',
|
|
405
|
+
category: 'productivity' as const,
|
|
406
|
+
parameters: {
|
|
407
|
+
type: 'object' as const,
|
|
408
|
+
properties: {
|
|
409
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
410
|
+
objectId: { type: 'string', description: 'Object ID of the slide to duplicate' },
|
|
411
|
+
},
|
|
412
|
+
required: ['presentationId', 'objectId'],
|
|
413
|
+
},
|
|
414
|
+
async execute(_id: string, input: any) {
|
|
415
|
+
try {
|
|
416
|
+
const token = await tp.getAccessToken();
|
|
417
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
418
|
+
method: 'POST',
|
|
419
|
+
body: { requests: [{ duplicateObject: { objectId: input.objectId } }] },
|
|
420
|
+
});
|
|
421
|
+
const newId = result.replies?.[0]?.duplicateObject?.objectId;
|
|
422
|
+
return jsonResult({ duplicated: true, newObjectId: newId });
|
|
423
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
// ─── Batch Update (Raw) ─────────────────────────────
|
|
428
|
+
{
|
|
429
|
+
name: 'google_slides_batch_update',
|
|
430
|
+
description: 'Send raw batchUpdate requests to the Slides API for advanced operations (create shapes, tables, format text, etc.). Accepts an array of request objects per the Slides API spec.',
|
|
431
|
+
category: 'productivity' as const,
|
|
432
|
+
parameters: {
|
|
433
|
+
type: 'object' as const,
|
|
434
|
+
properties: {
|
|
435
|
+
presentationId: { type: 'string', description: 'Presentation ID' },
|
|
436
|
+
requests: { type: 'string', description: 'JSON string of requests array (per Slides API batchUpdate spec)' },
|
|
437
|
+
},
|
|
438
|
+
required: ['presentationId', 'requests'],
|
|
439
|
+
},
|
|
440
|
+
async execute(_id: string, input: any) {
|
|
441
|
+
try {
|
|
442
|
+
const token = await tp.getAccessToken();
|
|
443
|
+
let requests: any[];
|
|
444
|
+
try { requests = JSON.parse(input.requests); } catch { return errorResult('Invalid JSON in requests parameter'); }
|
|
445
|
+
const result = await slidesApi(token, `/presentations/${input.presentationId}:batchUpdate`, {
|
|
446
|
+
method: 'POST',
|
|
447
|
+
body: { requests },
|
|
448
|
+
});
|
|
449
|
+
return jsonResult({ replies: result.replies || [], writeControl: result.writeControl });
|
|
450
|
+
} catch (e: any) { return errorResult(e.message); }
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
];
|
|
454
|
+
}
|
package/src/cli-agent.ts
CHANGED
|
@@ -178,6 +178,22 @@ export async function runAgent(_args: string[]) {
|
|
|
178
178
|
console.warn(` Routes init: failed (${permErr.message}) — some features may not work`);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
+
// 7c. Initialize activity tracker and journal for tool call recording
|
|
182
|
+
try {
|
|
183
|
+
await routes.activity.setDb(engineDb);
|
|
184
|
+
console.log(' Activity tracker: initialized');
|
|
185
|
+
} catch (actErr: any) {
|
|
186
|
+
console.warn(` Activity tracker init: failed (${actErr.message})`);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
if (routes.journal && typeof routes.journal.setDb === 'function') {
|
|
190
|
+
await routes.journal.setDb(engineDb);
|
|
191
|
+
console.log(' Journal: initialized');
|
|
192
|
+
}
|
|
193
|
+
} catch (jErr: any) {
|
|
194
|
+
console.warn(` Journal init: failed (${jErr.message})`);
|
|
195
|
+
}
|
|
196
|
+
|
|
181
197
|
// 8. Start health check HTTP server
|
|
182
198
|
const app = new Hono();
|
|
183
199
|
|
|
@@ -622,6 +622,12 @@ export function createAgentRoutes(opts: {
|
|
|
622
622
|
'https://www.googleapis.com/auth/documents',
|
|
623
623
|
'https://www.googleapis.com/auth/contacts',
|
|
624
624
|
'https://www.googleapis.com/auth/tasks',
|
|
625
|
+
'https://www.googleapis.com/auth/chat.spaces',
|
|
626
|
+
'https://www.googleapis.com/auth/chat.messages',
|
|
627
|
+
'https://www.googleapis.com/auth/chat.memberships',
|
|
628
|
+
'https://www.googleapis.com/auth/presentations',
|
|
629
|
+
'https://www.googleapis.com/auth/forms.body',
|
|
630
|
+
'https://www.googleapis.com/auth/forms.responses.readonly',
|
|
625
631
|
];
|
|
626
632
|
|
|
627
633
|
if (!oauthClientId) return c.json({ error: 'oauthClientId is required for Google OAuth' }, 400);
|