@unboundai/tb-ppt-mgagent 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -49,13 +49,22 @@ Every tool call writes one JSON-lines entry to `TB_PPT_LOG_FILE` (default `logs/
49
49
  | `started` | either | call received, with its key arguments (outline preview/length, or sessionId) |
50
50
  | `success` | `generate_tb_powerpoint` | session created; includes `sessionId` |
51
51
  | `not_ready` | `get_tb_powerpoint_result` | session isn't `idle` yet; includes `sessionStatus` |
52
+ | `agent_message` | `get_tb_powerpoint_result` | the agent's own most recent `agent.message` text, up to 500 chars, in `agentMessagePreview` — this is what the agent actually said, not our derived status. Logged once the session is idle, before the stop-reason check, so it's there even on `not_end_turn` |
52
53
  | `not_end_turn` | `get_tb_powerpoint_result` | idle, but `stop_reason` wasn't `end_turn`; includes `stopReason` |
54
+ | `listing_files` | `get_tb_powerpoint_result` | about to call Files API `list` for this session |
55
+ | `files_listed` | `get_tb_powerpoint_result` | includes `fileCount`, every `.pptx` candidate found (`pptxCandidates`, each with `id`/`filename`/`sizeBytes`), and which one was `chosenFileId` |
53
56
  | `no_output_file` | `get_tb_powerpoint_result` | finished cleanly but no `.pptx` found; includes `fileCount` |
54
- | `success` | `get_tb_powerpoint_result` | file downloaded; includes `filename`, `downloadUrl` |
57
+ | `download_started` | `get_tb_powerpoint_result` | about to call Files API `download`; includes `fileId`, `filename`, `expectedSizeBytes` |
58
+ | `download_completed` | `get_tb_powerpoint_result` | bytes received; includes `downloadedBytes` |
59
+ | `saving_file` | `get_tb_powerpoint_result` | about to write to `destPath` inside `outputDir` |
60
+ | `file_saved` | `get_tb_powerpoint_result` | write confirmed via `fs.statSync`; includes `savedBytes` and `bytesMatch` (should be `true`) |
61
+ | `success` | `get_tb_powerpoint_result` | fully done; includes `filename`, `destPath`, `downloadUrl` |
55
62
  | `invalid_arguments` | either | zod validation failed; includes `details` |
56
63
  | `error` | either | anything else threw; includes `message` |
57
64
 
58
- All entries include `durationMs` except the initial `started` line. To see what's been run:
65
+ The agent's actual final message is also returned to the caller as `agentMessage` on every `get_tb_powerpoint_result` response (not just logged), so a not-quite-right run can be diagnosed from the tool's own output, not just the log file.
66
+
67
+ All entries include `durationMs` except the intermediate progress lines (`agent_message`, `listing_files`, `files_listed`, `download_started`, `download_completed`, `saving_file`) — those are pure checkpoints, timed by their surrounding `started`/`success`/`error` lines instead. To see what's been run:
59
68
 
60
69
  ```bash
61
70
  tail -f logs/tasks.log
package/build/index.js CHANGED
@@ -268,15 +268,35 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
268
268
  }]
269
269
  };
270
270
  }
271
- // Find the most recent session.status_idle event to read its stop_reason.
271
+ // Find the most recent session.status_idle event to read its stop_reason, and
272
+ // the most recent agent.message event so we can log/return what the agent
273
+ // actually said (not just our own derived status).
272
274
  let latestIdleEvent = null;
275
+ let latestAgentMessage = null;
273
276
  for await (const event of client.beta.sessions.events.list(sessionId)) {
274
277
  if (event.type === "session.status_idle") {
275
278
  if (!latestIdleEvent || event.processed_at > latestIdleEvent.processed_at) {
276
279
  latestIdleEvent = event;
277
280
  }
278
281
  }
282
+ else if (event.type === "agent.message") {
283
+ const text = event.content
284
+ .filter((block) => block.type === "text")
285
+ .map((block) => block.text)
286
+ .join("\n")
287
+ .trim();
288
+ if (text && (!latestAgentMessage || event.processed_at > latestAgentMessage.processed_at)) {
289
+ latestAgentMessage = { processed_at: event.processed_at, text };
290
+ }
291
+ }
279
292
  }
293
+ const agentMessagePreview = latestAgentMessage ? latestAgentMessage.text.slice(0, 500) : null;
294
+ logTask({
295
+ tool: name,
296
+ status: "agent_message",
297
+ sessionId,
298
+ agentMessagePreview
299
+ });
280
300
  const stopReasonType = latestIdleEvent?.stop_reason.type;
281
301
  if (stopReasonType !== "end_turn") {
282
302
  logTask({
@@ -293,12 +313,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
293
313
  ready: false,
294
314
  status: session.status,
295
315
  stopReason: stopReasonType ?? null,
316
+ agentMessage: agentMessagePreview,
296
317
  message: `Session is idle but its stop reason is "${stopReasonType ?? "unknown"}", not "end_turn" - not fetching files yet.`
297
318
  }, null, 2)
298
319
  }]
299
320
  };
300
321
  }
301
322
  // Session finished cleanly - look for a .pptx output file.
323
+ logTask({ tool: name, status: "listing_files", sessionId });
302
324
  const files = await client.beta.files.list({
303
325
  scope_id: sessionId,
304
326
  betas: [MANAGED_AGENTS_BETA]
@@ -307,6 +329,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
307
329
  const pptxFiles = fileList.filter((f) => /\.pptx$/i.test(f.filename));
308
330
  const outputPptxFiles = pptxFiles.filter((f) => /outputs?[\\/]/i.test(f.filename));
309
331
  const chosen = outputPptxFiles[0] ?? pptxFiles[0];
332
+ logTask({
333
+ tool: name,
334
+ status: "files_listed",
335
+ sessionId,
336
+ fileCount: fileList.length,
337
+ pptxCandidates: pptxFiles.map((f) => ({ id: f.id, filename: f.filename, sizeBytes: f.size_bytes })),
338
+ chosenFileId: chosen?.id ?? null
339
+ });
310
340
  if (!chosen) {
311
341
  logTask({
312
342
  tool: name,
@@ -322,6 +352,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
322
352
  ready: true,
323
353
  status: "idle",
324
354
  stopReason: "end_turn",
355
+ agentMessage: agentMessagePreview,
325
356
  message: "Session finished but no .pptx output file was found.",
326
357
  sessionFiles: fileList.map((f) => ({ id: f.id, filename: f.filename }))
327
358
  }, null, 2)
@@ -331,20 +362,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
331
362
  const { outputDir, publicUrlBase } = requireOutputConfig();
332
363
  const safeName = path.basename(chosen.filename || `${sessionId}.pptx`);
333
364
  const destPath = path.join(outputDir, safeName);
365
+ logTask({
366
+ tool: name,
367
+ status: "download_started",
368
+ sessionId,
369
+ fileId: chosen.id,
370
+ filename: safeName,
371
+ expectedSizeBytes: chosen.size_bytes
372
+ });
334
373
  // files.download() resolves to a plain Response on this SDK version - no
335
374
  // writeToFile() helper (see file header). Read the bytes and write them ourselves.
336
375
  const response = await client.beta.files.download(chosen.id, {
337
376
  betas: [MANAGED_AGENTS_BETA]
338
377
  });
339
378
  const arrayBuffer = await response.arrayBuffer();
379
+ const downloadedBytes = arrayBuffer.byteLength;
380
+ logTask({
381
+ tool: name,
382
+ status: "download_completed",
383
+ sessionId,
384
+ fileId: chosen.id,
385
+ downloadedBytes
386
+ });
387
+ logTask({ tool: name, status: "saving_file", sessionId, destPath, outputDir });
340
388
  fs.mkdirSync(outputDir, { recursive: true });
341
389
  fs.writeFileSync(destPath, Buffer.from(arrayBuffer));
390
+ const savedBytes = fs.statSync(destPath).size;
391
+ logTask({
392
+ tool: name,
393
+ status: "file_saved",
394
+ sessionId,
395
+ destPath,
396
+ savedBytes,
397
+ bytesMatch: savedBytes === downloadedBytes
398
+ });
342
399
  const downloadUrl = `${publicUrlBase.replace(/\/$/, "")}/${encodeURIComponent(safeName)}`;
343
400
  logTask({
344
401
  tool: name,
345
402
  status: "success",
346
403
  sessionId,
347
404
  filename: safeName,
405
+ destPath,
348
406
  downloadUrl,
349
407
  durationMs: Date.now() - startedAt
350
408
  });
@@ -355,6 +413,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
355
413
  ready: true,
356
414
  status: "idle",
357
415
  stopReason: "end_turn",
416
+ agentMessage: agentMessagePreview,
358
417
  filename: safeName,
359
418
  savedTo: destPath,
360
419
  downloadUrl
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundai/tb-ppt-mgagent",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server that hands a presentation outline to the Traffic Builders PowerPoint Managed Agent, then retrieves the finished .pptx",
5
5
  "main": "build/index.js",
6
6
  "bin": {