@jcheesepkg/nanobot 0.8.6 → 0.8.8

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.
@@ -483,16 +483,81 @@ var FlexTool = class extends Tool {
483
483
  };
484
484
  }
485
485
  buildCustom(data) {
486
- const contents = data.contents;
487
- if (!Array.isArray(contents) || contents.length === 0) throw new Error("custom template requires a non-empty 'contents' array");
488
- return {
486
+ const rawContents = data.contents;
487
+ if (Array.isArray(rawContents) && rawContents.length > 0) return {
489
488
  type: "bubble",
490
489
  body: {
491
490
  type: "box",
492
491
  layout: "vertical",
493
- contents
492
+ contents: rawContents
494
493
  }
495
494
  };
495
+ const title = data.title ? String(data.title) : null;
496
+ const body = data.body ? String(data.body) : null;
497
+ const buttons = Array.isArray(data.buttons) ? data.buttons : [];
498
+ const headerColor = data.header_color ? String(data.header_color) : null;
499
+ if (!title && !body && buttons.length === 0) throw new Error("custom template requires 'contents' array, or at least one of: title, body, buttons");
500
+ const bodyContents = [];
501
+ if (title) bodyContents.push({
502
+ type: "text",
503
+ text: title,
504
+ weight: "bold",
505
+ size: "lg"
506
+ });
507
+ if (body) {
508
+ if (title) bodyContents.push({
509
+ type: "separator",
510
+ margin: "md"
511
+ });
512
+ bodyContents.push({
513
+ type: "text",
514
+ text: body,
515
+ wrap: true,
516
+ margin: title ? "md" : void 0
517
+ });
518
+ }
519
+ const bubble = {
520
+ type: "bubble",
521
+ body: {
522
+ type: "box",
523
+ layout: "vertical",
524
+ contents: bodyContents.length > 0 ? bodyContents : [{
525
+ type: "text",
526
+ text: " "
527
+ }]
528
+ }
529
+ };
530
+ if (headerColor && title) {
531
+ bubble.header = {
532
+ type: "box",
533
+ layout: "vertical",
534
+ backgroundColor: headerColor,
535
+ paddingAll: "lg",
536
+ contents: [{
537
+ type: "text",
538
+ text: title,
539
+ color: "#ffffff",
540
+ weight: "bold",
541
+ size: "lg"
542
+ }]
543
+ };
544
+ bodyContents.shift();
545
+ }
546
+ if (buttons.length > 0) bubble.footer = {
547
+ type: "box",
548
+ layout: "vertical",
549
+ spacing: "sm",
550
+ contents: buttons.map((btn) => ({
551
+ type: "button",
552
+ style: String(btn.style ?? "primary"),
553
+ action: {
554
+ type: "message",
555
+ label: String(btn.label ?? ""),
556
+ text: String(btn.text ?? btn.label ?? "")
557
+ }
558
+ }))
559
+ };
560
+ return bubble;
496
561
  }
497
562
  };
498
563
 
@@ -1 +1 @@
1
- {"version":3,"file":"flex.mjs","names":[],"sources":["../../../src/agent/tools/flex.ts"],"sourcesContent":["import { Tool } from \"./base.js\";\n\n/**\n * Tool for building LINE Flex Messages from structured data.\n *\n * The LLM calls this tool with a template name and data object,\n * and the tool returns valid Flex JSON that line.ts parseMessage()\n * will detect and send as a Flex Message.\n */\nexport class FlexTool extends Tool {\n readonly name = \"flex_message\";\n readonly description =\n \"Build a LINE Flex Message from a template. Returns JSON that will be rendered as a rich card in LINE. Use this instead of outputting raw JSON.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n template: {\n type: \"string\",\n description:\n \"Template name. Available: fortune, info_card, action_buttons, receipt, morning_summary, hydration, custom.\",\n },\n data: {\n type: \"object\",\n description: \"Template-specific data. See each template for fields.\",\n properties: {\n // fortune\n sign: { type: \"string\", description: \"Zodiac sign with emoji (e.g. '♍ 乙女座')\" },\n stars: { type: \"integer\", description: \"1-5 star rating\" },\n message: { type: \"string\", description: \"Fortune message\" },\n lucky_color: { type: \"string\", description: \"Lucky color name\" },\n lucky_item: { type: \"string\", description: \"Lucky item name\" },\n // info_card\n title: { type: \"string\", description: \"Card title\" },\n body: { type: \"string\", description: \"Card body text\" },\n // action_buttons\n prompt: { type: \"string\", description: \"Question or prompt text\" },\n buttons: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n label: { type: \"string\" },\n text: { type: \"string\" },\n style: { type: \"string\", enum: [\"primary\", \"secondary\", \"link\"] },\n },\n },\n description: \"Button definitions\",\n },\n // receipt\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n value: { type: \"string\" },\n },\n },\n description: \"Line items\",\n },\n total: { type: \"string\", description: \"Total amount\" },\n // morning_summary\n greeting: { type: \"string\", description: \"Greeting text (e.g. 'おはよう!')\" },\n date: { type: \"string\", description: \"Date string (e.g. '2月13日 木曜日')\" },\n weather: { type: \"string\", description: \"Weather info (e.g. '東京 12°C 曇り')\" },\n advice: { type: \"string\", description: \"Short advice (e.g. 'コートでOK')\" },\n schedule: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Schedule/reminder lines\",\n },\n header_color: { type: \"string\", description: \"Header background color hex (default: #1DB446)\" },\n // hydration\n current: { type: \"integer\", description: \"Current count (e.g. glasses drunk)\" },\n goal: { type: \"integer\", description: \"Goal count\" },\n unit: { type: \"string\", description: \"Unit label (default: 杯)\" },\n button_label: { type: \"string\", description: \"Button label (default: '飲んだ!')\" },\n button_text: { type: \"string\", description: \"Button message text (default: '水飲んだ')\" },\n // custom\n contents: {\n type: \"array\",\n description: \"Raw Flex body contents array for custom template\",\n },\n },\n },\n },\n required: [\"template\", \"data\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const template = String(args.template);\n const data = (args.data ?? {}) as Record<string, unknown>;\n\n try {\n let flex: Record<string, unknown>;\n\n switch (template) {\n case \"fortune\":\n flex = this.buildFortune(data);\n break;\n case \"info_card\":\n flex = this.buildInfoCard(data);\n break;\n case \"action_buttons\":\n flex = this.buildActionButtons(data);\n break;\n case \"receipt\":\n flex = this.buildReceipt(data);\n break;\n case \"morning_summary\":\n flex = this.buildMorningSummary(data);\n break;\n case \"hydration\":\n flex = this.buildHydration(data);\n break;\n case \"custom\":\n flex = this.buildCustom(data);\n break;\n default:\n return `Error: unknown template '${template}'. Use: fortune, info_card, action_buttons, receipt, morning_summary, hydration, custom.`;\n }\n\n return JSON.stringify(flex);\n } catch (err) {\n return `Error building flex message: ${err instanceof Error ? err.message : err}`;\n }\n }\n\n private buildFortune(data: Record<string, unknown>): Record<string, unknown> {\n const sign = String(data.sign ?? \"♈ 牡羊座\");\n const stars = Math.max(1, Math.min(5, Number(data.stars ?? 3)));\n const message = String(data.message ?? \"\");\n const luckyColor = data.lucky_color ? String(data.lucky_color) : null;\n const luckyItem = data.lucky_item ? String(data.lucky_item) : null;\n\n const starText = \"★\".repeat(stars) + \"☆\".repeat(5 - stars);\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: sign, weight: \"bold\", size: \"xl\" },\n { type: \"text\", text: \"今日の運勢\", size: \"sm\", color: \"#666666\" },\n { type: \"text\", text: starText, size: \"xxl\", margin: \"md\" },\n ];\n\n if (message) {\n contents.push({\n type: \"text\",\n text: message,\n wrap: true,\n margin: \"sm\",\n });\n }\n\n if (luckyColor || luckyItem) {\n contents.push({ type: \"separator\", margin: \"md\" });\n }\n\n if (luckyColor) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: \"ラッキーカラー\", size: \"sm\", color: \"#228B22\", flex: 0 },\n { type: \"text\", text: luckyColor, size: \"sm\", align: \"end\" },\n ],\n });\n }\n\n if (luckyItem) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: luckyColor ? \"sm\" : \"md\",\n contents: [\n { type: \"text\", text: \"ラッキーアイテム\", size: \"sm\", color: \"#228B22\", flex: 0 },\n { type: \"text\", text: luckyItem, size: \"sm\", align: \"end\" },\n ],\n });\n }\n\n return {\n type: \"bubble\",\n size: \"kilo\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n paddingAll: \"lg\",\n contents,\n },\n };\n }\n\n private buildInfoCard(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"お知らせ\");\n const body = String(data.body ?? \"\");\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: title, weight: \"bold\", size: \"lg\" },\n { type: \"separator\", margin: \"md\" },\n ];\n\n if (body) {\n contents.push({\n type: \"text\",\n text: body,\n wrap: true,\n margin: \"md\",\n });\n }\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents,\n },\n };\n }\n\n private buildActionButtons(data: Record<string, unknown>): Record<string, unknown> {\n const prompt = String(data.prompt ?? \"選択してください\");\n const buttons = (data.buttons ?? []) as Array<Record<string, unknown>>;\n\n if (buttons.length === 0) {\n // Default yes/no\n buttons.push(\n { label: \"はい\", text: \"はい\", style: \"primary\" },\n { label: \"いいえ\", text: \"いいえ\", style: \"secondary\" },\n );\n }\n\n const buttonContents = buttons.map((btn) => ({\n type: \"button\",\n style: String(btn.style ?? \"primary\"),\n action: {\n type: \"message\",\n label: String(btn.label ?? \"\"),\n text: String(btn.text ?? btn.label ?? \"\"),\n },\n }));\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: [\n { type: \"text\", text: prompt, weight: \"bold\", wrap: true },\n ],\n },\n footer: {\n type: \"box\",\n layout: \"vertical\",\n spacing: \"sm\",\n contents: buttonContents,\n },\n };\n }\n\n private buildReceipt(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"明細\");\n const items = (data.items ?? []) as Array<Record<string, unknown>>;\n const total = data.total ? String(data.total) : null;\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: `💰 ${title}`, weight: \"bold\" },\n { type: \"separator\", margin: \"lg\" },\n ];\n\n for (const item of items) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: String(item.name ?? \"\"), flex: 0 },\n { type: \"text\", text: String(item.value ?? \"\"), align: \"end\" },\n ],\n });\n }\n\n if (total) {\n contents.push(\n { type: \"separator\", margin: \"lg\" },\n {\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: \"合計\", weight: \"bold\" },\n { type: \"text\", text: total, weight: \"bold\", align: \"end\" },\n ],\n },\n );\n }\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents,\n },\n };\n }\n\n private buildMorningSummary(data: Record<string, unknown>): Record<string, unknown> {\n const greeting = String(data.greeting ?? \"おはよう!\");\n const date = String(data.date ?? \"\");\n const weather = data.weather ? String(data.weather) : null;\n const advice = data.advice ? String(data.advice) : null;\n const schedule = (data.schedule ?? []) as string[];\n const headerColor = String(data.header_color ?? \"#1DB446\");\n\n const headerContents: Record<string, unknown>[] = [\n { type: \"text\", text: greeting, color: \"#ffffff\", weight: \"bold\", size: \"lg\" },\n ];\n if (date) {\n headerContents.push({ type: \"text\", text: date, color: \"#ffffff\", size: \"sm\" });\n }\n\n const bodyContents: Record<string, unknown>[] = [];\n\n if (weather) {\n bodyContents.push({ type: \"text\", text: weather, weight: \"bold\" });\n }\n if (advice) {\n bodyContents.push({ type: \"text\", text: advice, size: \"sm\", color: \"#666666\" });\n }\n\n if (schedule.length > 0) {\n bodyContents.push({ type: \"separator\", margin: \"md\" });\n for (const line of schedule) {\n bodyContents.push({ type: \"text\", text: line, margin: \"md\", wrap: true });\n }\n }\n\n return {\n type: \"bubble\",\n header: {\n type: \"box\",\n layout: \"vertical\",\n backgroundColor: headerColor,\n paddingAll: \"lg\",\n contents: headerContents,\n },\n body: {\n type: \"box\",\n layout: \"vertical\",\n paddingAll: \"lg\",\n contents: bodyContents.length > 0 ? bodyContents : [{ type: \"text\", text: \"良い一日を!\" }],\n },\n };\n }\n\n private buildHydration(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"水飲んだ?\");\n const current = Number(data.current ?? 0);\n const goal = Number(data.goal ?? 8);\n const unit = String(data.unit ?? \"杯\");\n const buttonLabel = String(data.button_label ?? \"飲んだ!\");\n const buttonText = String(data.button_text ?? \"水飲んだ\");\n\n return {\n type: \"bubble\",\n size: \"kilo\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: [\n { type: \"text\", text: title, size: \"xl\" },\n { type: \"text\", text: `今日: ${current}${unit} / ${goal}${unit}`, color: \"#666666\" },\n ],\n },\n footer: {\n type: \"box\",\n layout: \"horizontal\",\n contents: [\n {\n type: \"button\",\n style: \"primary\",\n color: \"#00B9ED\",\n action: { type: \"message\", label: buttonLabel, text: buttonText },\n },\n ],\n },\n };\n }\n\n private buildCustom(data: Record<string, unknown>): Record<string, unknown> {\n const contents = data.contents as unknown[];\n if (!Array.isArray(contents) || contents.length === 0) {\n throw new Error(\"custom template requires a non-empty 'contents' array\");\n }\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents,\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flex.mjs","names":[],"sources":["../../../src/agent/tools/flex.ts"],"sourcesContent":["import { Tool } from \"./base.js\";\n\n/**\n * Tool for building LINE Flex Messages from structured data.\n *\n * The LLM calls this tool with a template name and data object,\n * and the tool returns valid Flex JSON that line.ts parseMessage()\n * will detect and send as a Flex Message.\n */\nexport class FlexTool extends Tool {\n readonly name = \"flex_message\";\n readonly description =\n \"Build a LINE Flex Message from a template. Returns JSON that will be rendered as a rich card in LINE. Use this instead of outputting raw JSON.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n template: {\n type: \"string\",\n description:\n \"Template name. Available: fortune, info_card, action_buttons, receipt, morning_summary, hydration, custom.\",\n },\n data: {\n type: \"object\",\n description: \"Template-specific data. See each template for fields.\",\n properties: {\n // fortune\n sign: { type: \"string\", description: \"Zodiac sign with emoji (e.g. '♍ 乙女座')\" },\n stars: { type: \"integer\", description: \"1-5 star rating\" },\n message: { type: \"string\", description: \"Fortune message\" },\n lucky_color: { type: \"string\", description: \"Lucky color name\" },\n lucky_item: { type: \"string\", description: \"Lucky item name\" },\n // info_card\n title: { type: \"string\", description: \"Card title\" },\n body: { type: \"string\", description: \"Card body text\" },\n // action_buttons\n prompt: { type: \"string\", description: \"Question or prompt text\" },\n buttons: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n label: { type: \"string\" },\n text: { type: \"string\" },\n style: { type: \"string\", enum: [\"primary\", \"secondary\", \"link\"] },\n },\n },\n description: \"Button definitions\",\n },\n // receipt\n items: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n value: { type: \"string\" },\n },\n },\n description: \"Line items\",\n },\n total: { type: \"string\", description: \"Total amount\" },\n // morning_summary\n greeting: { type: \"string\", description: \"Greeting text (e.g. 'おはよう!')\" },\n date: { type: \"string\", description: \"Date string (e.g. '2月13日 木曜日')\" },\n weather: { type: \"string\", description: \"Weather info (e.g. '東京 12°C 曇り')\" },\n advice: { type: \"string\", description: \"Short advice (e.g. 'コートでOK')\" },\n schedule: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Schedule/reminder lines\",\n },\n header_color: { type: \"string\", description: \"Header background color hex (default: #1DB446)\" },\n // hydration\n current: { type: \"integer\", description: \"Current count (e.g. glasses drunk)\" },\n goal: { type: \"integer\", description: \"Goal count\" },\n unit: { type: \"string\", description: \"Unit label (default: 杯)\" },\n button_label: { type: \"string\", description: \"Button label (default: '飲んだ!')\" },\n button_text: { type: \"string\", description: \"Button message text (default: '水飲んだ')\" },\n // custom\n contents: {\n type: \"array\",\n description: \"Raw Flex body contents array for custom template\",\n },\n },\n },\n },\n required: [\"template\", \"data\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const template = String(args.template);\n const data = (args.data ?? {}) as Record<string, unknown>;\n\n try {\n let flex: Record<string, unknown>;\n\n switch (template) {\n case \"fortune\":\n flex = this.buildFortune(data);\n break;\n case \"info_card\":\n flex = this.buildInfoCard(data);\n break;\n case \"action_buttons\":\n flex = this.buildActionButtons(data);\n break;\n case \"receipt\":\n flex = this.buildReceipt(data);\n break;\n case \"morning_summary\":\n flex = this.buildMorningSummary(data);\n break;\n case \"hydration\":\n flex = this.buildHydration(data);\n break;\n case \"custom\":\n flex = this.buildCustom(data);\n break;\n default:\n return `Error: unknown template '${template}'. Use: fortune, info_card, action_buttons, receipt, morning_summary, hydration, custom.`;\n }\n\n return JSON.stringify(flex);\n } catch (err) {\n return `Error building flex message: ${err instanceof Error ? err.message : err}`;\n }\n }\n\n private buildFortune(data: Record<string, unknown>): Record<string, unknown> {\n const sign = String(data.sign ?? \"♈ 牡羊座\");\n const stars = Math.max(1, Math.min(5, Number(data.stars ?? 3)));\n const message = String(data.message ?? \"\");\n const luckyColor = data.lucky_color ? String(data.lucky_color) : null;\n const luckyItem = data.lucky_item ? String(data.lucky_item) : null;\n\n const starText = \"★\".repeat(stars) + \"☆\".repeat(5 - stars);\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: sign, weight: \"bold\", size: \"xl\" },\n { type: \"text\", text: \"今日の運勢\", size: \"sm\", color: \"#666666\" },\n { type: \"text\", text: starText, size: \"xxl\", margin: \"md\" },\n ];\n\n if (message) {\n contents.push({\n type: \"text\",\n text: message,\n wrap: true,\n margin: \"sm\",\n });\n }\n\n if (luckyColor || luckyItem) {\n contents.push({ type: \"separator\", margin: \"md\" });\n }\n\n if (luckyColor) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: \"ラッキーカラー\", size: \"sm\", color: \"#228B22\", flex: 0 },\n { type: \"text\", text: luckyColor, size: \"sm\", align: \"end\" },\n ],\n });\n }\n\n if (luckyItem) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: luckyColor ? \"sm\" : \"md\",\n contents: [\n { type: \"text\", text: \"ラッキーアイテム\", size: \"sm\", color: \"#228B22\", flex: 0 },\n { type: \"text\", text: luckyItem, size: \"sm\", align: \"end\" },\n ],\n });\n }\n\n return {\n type: \"bubble\",\n size: \"kilo\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n paddingAll: \"lg\",\n contents,\n },\n };\n }\n\n private buildInfoCard(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"お知らせ\");\n const body = String(data.body ?? \"\");\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: title, weight: \"bold\", size: \"lg\" },\n { type: \"separator\", margin: \"md\" },\n ];\n\n if (body) {\n contents.push({\n type: \"text\",\n text: body,\n wrap: true,\n margin: \"md\",\n });\n }\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents,\n },\n };\n }\n\n private buildActionButtons(data: Record<string, unknown>): Record<string, unknown> {\n const prompt = String(data.prompt ?? \"選択してください\");\n const buttons = (data.buttons ?? []) as Array<Record<string, unknown>>;\n\n if (buttons.length === 0) {\n // Default yes/no\n buttons.push(\n { label: \"はい\", text: \"はい\", style: \"primary\" },\n { label: \"いいえ\", text: \"いいえ\", style: \"secondary\" },\n );\n }\n\n const buttonContents = buttons.map((btn) => ({\n type: \"button\",\n style: String(btn.style ?? \"primary\"),\n action: {\n type: \"message\",\n label: String(btn.label ?? \"\"),\n text: String(btn.text ?? btn.label ?? \"\"),\n },\n }));\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: [\n { type: \"text\", text: prompt, weight: \"bold\", wrap: true },\n ],\n },\n footer: {\n type: \"box\",\n layout: \"vertical\",\n spacing: \"sm\",\n contents: buttonContents,\n },\n };\n }\n\n private buildReceipt(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"明細\");\n const items = (data.items ?? []) as Array<Record<string, unknown>>;\n const total = data.total ? String(data.total) : null;\n\n const contents: Record<string, unknown>[] = [\n { type: \"text\", text: `💰 ${title}`, weight: \"bold\" },\n { type: \"separator\", margin: \"lg\" },\n ];\n\n for (const item of items) {\n contents.push({\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: String(item.name ?? \"\"), flex: 0 },\n { type: \"text\", text: String(item.value ?? \"\"), align: \"end\" },\n ],\n });\n }\n\n if (total) {\n contents.push(\n { type: \"separator\", margin: \"lg\" },\n {\n type: \"box\",\n layout: \"horizontal\",\n margin: \"md\",\n contents: [\n { type: \"text\", text: \"合計\", weight: \"bold\" },\n { type: \"text\", text: total, weight: \"bold\", align: \"end\" },\n ],\n },\n );\n }\n\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents,\n },\n };\n }\n\n private buildMorningSummary(data: Record<string, unknown>): Record<string, unknown> {\n const greeting = String(data.greeting ?? \"おはよう!\");\n const date = String(data.date ?? \"\");\n const weather = data.weather ? String(data.weather) : null;\n const advice = data.advice ? String(data.advice) : null;\n const schedule = (data.schedule ?? []) as string[];\n const headerColor = String(data.header_color ?? \"#1DB446\");\n\n const headerContents: Record<string, unknown>[] = [\n { type: \"text\", text: greeting, color: \"#ffffff\", weight: \"bold\", size: \"lg\" },\n ];\n if (date) {\n headerContents.push({ type: \"text\", text: date, color: \"#ffffff\", size: \"sm\" });\n }\n\n const bodyContents: Record<string, unknown>[] = [];\n\n if (weather) {\n bodyContents.push({ type: \"text\", text: weather, weight: \"bold\" });\n }\n if (advice) {\n bodyContents.push({ type: \"text\", text: advice, size: \"sm\", color: \"#666666\" });\n }\n\n if (schedule.length > 0) {\n bodyContents.push({ type: \"separator\", margin: \"md\" });\n for (const line of schedule) {\n bodyContents.push({ type: \"text\", text: line, margin: \"md\", wrap: true });\n }\n }\n\n return {\n type: \"bubble\",\n header: {\n type: \"box\",\n layout: \"vertical\",\n backgroundColor: headerColor,\n paddingAll: \"lg\",\n contents: headerContents,\n },\n body: {\n type: \"box\",\n layout: \"vertical\",\n paddingAll: \"lg\",\n contents: bodyContents.length > 0 ? bodyContents : [{ type: \"text\", text: \"良い一日を!\" }],\n },\n };\n }\n\n private buildHydration(data: Record<string, unknown>): Record<string, unknown> {\n const title = String(data.title ?? \"水飲んだ?\");\n const current = Number(data.current ?? 0);\n const goal = Number(data.goal ?? 8);\n const unit = String(data.unit ?? \"杯\");\n const buttonLabel = String(data.button_label ?? \"飲んだ!\");\n const buttonText = String(data.button_text ?? \"水飲んだ\");\n\n return {\n type: \"bubble\",\n size: \"kilo\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: [\n { type: \"text\", text: title, size: \"xl\" },\n { type: \"text\", text: `今日: ${current}${unit} / ${goal}${unit}`, color: \"#666666\" },\n ],\n },\n footer: {\n type: \"box\",\n layout: \"horizontal\",\n contents: [\n {\n type: \"button\",\n style: \"primary\",\n color: \"#00B9ED\",\n action: { type: \"message\", label: buttonLabel, text: buttonText },\n },\n ],\n },\n };\n }\n\n private buildCustom(data: Record<string, unknown>): Record<string, unknown> {\n // If raw contents array is provided, use it directly\n const rawContents = data.contents as unknown[];\n if (Array.isArray(rawContents) && rawContents.length > 0) {\n return {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: rawContents,\n },\n };\n }\n\n // Fallback: auto-build a card from common fields (title, body, buttons, header_color)\n const title = data.title ? String(data.title) : null;\n const body = data.body ? String(data.body) : null;\n const buttons = Array.isArray(data.buttons) ? data.buttons as Array<Record<string, unknown>> : [];\n const headerColor = data.header_color ? String(data.header_color) : null;\n\n if (!title && !body && buttons.length === 0) {\n throw new Error(\"custom template requires 'contents' array, or at least one of: title, body, buttons\");\n }\n\n const bodyContents: Record<string, unknown>[] = [];\n\n if (title) {\n bodyContents.push({ type: \"text\", text: title, weight: \"bold\", size: \"lg\" });\n }\n if (body) {\n if (title) bodyContents.push({ type: \"separator\", margin: \"md\" });\n bodyContents.push({ type: \"text\", text: body, wrap: true, margin: title ? \"md\" : undefined });\n }\n\n const bubble: Record<string, unknown> = {\n type: \"bubble\",\n body: {\n type: \"box\",\n layout: \"vertical\",\n contents: bodyContents.length > 0 ? bodyContents : [{ type: \"text\", text: \" \" }],\n },\n };\n\n // Add colored header if specified\n if (headerColor && title) {\n bubble.header = {\n type: \"box\",\n layout: \"vertical\",\n backgroundColor: headerColor,\n paddingAll: \"lg\",\n contents: [\n { type: \"text\", text: title, color: \"#ffffff\", weight: \"bold\", size: \"lg\" },\n ],\n };\n // Remove title from body since it's in the header now\n bodyContents.shift();\n }\n\n // Add buttons as footer\n if (buttons.length > 0) {\n bubble.footer = {\n type: \"box\",\n layout: \"vertical\",\n spacing: \"sm\",\n contents: buttons.map((btn) => ({\n type: \"button\",\n style: String(btn.style ?? \"primary\"),\n action: {\n type: \"message\",\n label: String(btn.label ?? \"\"),\n text: String(btn.text ?? btn.label ?? \"\"),\n },\n })),\n };\n }\n\n return bubble;\n }\n}\n"],"mappings":"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"}
@@ -70,31 +70,31 @@ declare const ChannelsConfigSchema: z.ZodObject<{
70
70
  channelAccessToken?: string | undefined;
71
71
  }>>;
72
72
  }, "strip", z.ZodTypeAny, {
73
- telegram: {
74
- enabled: boolean;
75
- token: string;
76
- allowFrom: string[];
77
- proxy?: string | null | undefined;
78
- };
79
73
  line: {
80
74
  enabled: boolean;
81
75
  allowFrom: string[];
82
76
  channelSecret: string;
83
77
  channelAccessToken: string;
84
78
  };
85
- }, {
86
- telegram?: {
87
- enabled?: boolean | undefined;
88
- token?: string | undefined;
89
- allowFrom?: string[] | undefined;
79
+ telegram: {
80
+ enabled: boolean;
81
+ token: string;
82
+ allowFrom: string[];
90
83
  proxy?: string | null | undefined;
91
- } | undefined;
84
+ };
85
+ }, {
92
86
  line?: {
93
87
  enabled?: boolean | undefined;
94
88
  allowFrom?: string[] | undefined;
95
89
  channelSecret?: string | undefined;
96
90
  channelAccessToken?: string | undefined;
97
91
  } | undefined;
92
+ telegram?: {
93
+ enabled?: boolean | undefined;
94
+ token?: string | undefined;
95
+ allowFrom?: string[] | undefined;
96
+ proxy?: string | null | undefined;
97
+ } | undefined;
98
98
  }>;
99
99
  type ChannelsConfig = z.infer<typeof ChannelsConfigSchema>;
100
100
  declare const AgentDefaultsSchema: z.ZodObject<{
@@ -675,31 +675,31 @@ declare const ConfigSchema: z.ZodObject<{
675
675
  channelAccessToken?: string | undefined;
676
676
  }>>;
677
677
  }, "strip", z.ZodTypeAny, {
678
- telegram: {
679
- enabled: boolean;
680
- token: string;
681
- allowFrom: string[];
682
- proxy?: string | null | undefined;
683
- };
684
678
  line: {
685
679
  enabled: boolean;
686
680
  allowFrom: string[];
687
681
  channelSecret: string;
688
682
  channelAccessToken: string;
689
683
  };
690
- }, {
691
- telegram?: {
692
- enabled?: boolean | undefined;
693
- token?: string | undefined;
694
- allowFrom?: string[] | undefined;
684
+ telegram: {
685
+ enabled: boolean;
686
+ token: string;
687
+ allowFrom: string[];
695
688
  proxy?: string | null | undefined;
696
- } | undefined;
689
+ };
690
+ }, {
697
691
  line?: {
698
692
  enabled?: boolean | undefined;
699
693
  allowFrom?: string[] | undefined;
700
694
  channelSecret?: string | undefined;
701
695
  channelAccessToken?: string | undefined;
702
696
  } | undefined;
697
+ telegram?: {
698
+ enabled?: boolean | undefined;
699
+ token?: string | undefined;
700
+ allowFrom?: string[] | undefined;
701
+ proxy?: string | null | undefined;
702
+ } | undefined;
703
703
  }>>;
704
704
  providers: z.ZodDefault<z.ZodObject<{
705
705
  anthropic: z.ZodDefault<z.ZodObject<{
@@ -1064,18 +1064,18 @@ declare const ConfigSchema: z.ZodObject<{
1064
1064
  };
1065
1065
  };
1066
1066
  channels: {
1067
- telegram: {
1068
- enabled: boolean;
1069
- token: string;
1070
- allowFrom: string[];
1071
- proxy?: string | null | undefined;
1072
- };
1073
1067
  line: {
1074
1068
  enabled: boolean;
1075
1069
  allowFrom: string[];
1076
1070
  channelSecret: string;
1077
1071
  channelAccessToken: string;
1078
1072
  };
1073
+ telegram: {
1074
+ enabled: boolean;
1075
+ token: string;
1076
+ allowFrom: string[];
1077
+ proxy?: string | null | undefined;
1078
+ };
1079
1079
  };
1080
1080
  providers: {
1081
1081
  anthropic: {
@@ -1170,18 +1170,18 @@ declare const ConfigSchema: z.ZodObject<{
1170
1170
  } | undefined;
1171
1171
  } | undefined;
1172
1172
  channels?: {
1173
- telegram?: {
1174
- enabled?: boolean | undefined;
1175
- token?: string | undefined;
1176
- allowFrom?: string[] | undefined;
1177
- proxy?: string | null | undefined;
1178
- } | undefined;
1179
1173
  line?: {
1180
1174
  enabled?: boolean | undefined;
1181
1175
  allowFrom?: string[] | undefined;
1182
1176
  channelSecret?: string | undefined;
1183
1177
  channelAccessToken?: string | undefined;
1184
1178
  } | undefined;
1179
+ telegram?: {
1180
+ enabled?: boolean | undefined;
1181
+ token?: string | undefined;
1182
+ allowFrom?: string[] | undefined;
1183
+ proxy?: string | null | undefined;
1184
+ } | undefined;
1185
1185
  } | undefined;
1186
1186
  providers?: {
1187
1187
  anthropic?: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jcheesepkg/nanobot",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "description": "Lightweight AI assistant - TypeScript port",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -71,3 +71,32 @@ Skills use three-level loading:
71
71
  - `name`: The skill name
72
72
  - `description`: What the skill does AND when to use it. Include trigger contexts.
73
73
  All "when to use" info goes here since the body only loads after triggering.
74
+
75
+ ### Flex Message Integration
76
+
77
+ If the skill produces output that would benefit from rich visual display (cards, progress trackers, summaries, confirmations), include `flex_message` tool usage in the SKILL.md.
78
+
79
+ Available templates: `fortune`, `info_card`, `action_buttons`, `receipt`, `morning_summary`, `hydration`.
80
+
81
+ For anything else, use `custom` with common fields:
82
+ ```
83
+ flex_message(template="custom", data={
84
+ title: "カードタイトル",
85
+ body: "本文テキスト",
86
+ header_color: "#FF6B6B",
87
+ buttons: [
88
+ { label: "ボタン", text: "送信テキスト", style: "primary" }
89
+ ]
90
+ })
91
+ ```
92
+
93
+ Include this in the skill's Output section:
94
+ ```markdown
95
+ ## Output
96
+
97
+ **ALWAYS use the `flex_message` tool** to display results as a rich card.
98
+ After the tool returns, output ONLY the raw JSON string returned by the tool.
99
+ ```
100
+
101
+ Good candidates for flex: trackers, reminders, summaries, confirmations, selections.
102
+ Not needed for: conversational replies, simple text answers, file operations.