@axiom-lattice/core 2.1.36 → 2.1.38

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/index.mjs CHANGED
@@ -640,11 +640,11 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
640
640
  * @param key Lattice键名
641
641
  * @param tool 已有的StructuredTool实例
642
642
  */
643
- registerExistingTool(key, tool48) {
643
+ registerExistingTool(key, tool50) {
644
644
  const config = {
645
- name: tool48.name,
646
- description: tool48.description,
647
- schema: tool48.schema,
645
+ name: tool50.name,
646
+ description: tool50.description,
647
+ schema: tool50.schema,
648
648
  // StructuredTool的schema已经是Zod兼容的
649
649
  needUserApprove: false
650
650
  // MCP工具默认不需要用户批准
@@ -652,7 +652,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
652
652
  const toolLattice = {
653
653
  key,
654
654
  config,
655
- client: tool48
655
+ client: tool50
656
656
  };
657
657
  this.register(key, toolLattice);
658
658
  }
@@ -678,7 +678,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
678
678
  };
679
679
  var toolLatticeManager = ToolLatticeManager.getInstance();
680
680
  var registerToolLattice = (key, config, executor) => toolLatticeManager.registerLattice(key, config, executor);
681
- var registerExistingTool = (key, tool48) => toolLatticeManager.registerExistingTool(key, tool48);
681
+ var registerExistingTool = (key, tool50) => toolLatticeManager.registerExistingTool(key, tool50);
682
682
  var getToolLattice = (key) => toolLatticeManager.getToolLattice(key);
683
683
  var getToolDefinition = (key) => toolLatticeManager.getToolDefinition(key);
684
684
  var getToolClient = (key) => toolLatticeManager.getToolClient(key);
@@ -892,6 +892,7 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
892
892
  constructor() {
893
893
  this.databases = /* @__PURE__ */ new Map();
894
894
  this.defaultDatabaseKeys = /* @__PURE__ */ new Map();
895
+ this.configStore = null;
895
896
  }
896
897
  /**
897
898
  * Get the singleton instance
@@ -950,27 +951,53 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
950
951
  }
951
952
  this.defaultDatabaseKeys.set(tenantId, key);
952
953
  }
954
+ /**
955
+ * Set the configuration store for on-demand database loading
956
+ * @param store - The database configuration store
957
+ */
958
+ setConfigStore(store) {
959
+ this.configStore = store;
960
+ }
953
961
  /**
954
962
  * Get a database by key for a specific tenant
963
+ * If database is not registered and configStore is set, will try to load from store
955
964
  * @param tenantId - Tenant identifier (required)
956
965
  * @param key - Database key (optional, uses default if not provided)
957
966
  * @returns ISqlDatabase instance
958
967
  * @throws Error if tenant or database not found
959
968
  */
960
- getDatabase(tenantId, key) {
969
+ async getDatabase(tenantId, key) {
961
970
  const tenantDbs = this.databases.get(tenantId);
962
- if (!tenantDbs) {
963
- throw new Error(`No databases registered for tenant '${tenantId}'`);
964
- }
965
971
  const dbKey = key || this.defaultDatabaseKeys.get(tenantId);
966
972
  if (!dbKey) {
967
973
  throw new Error(`No default database set for tenant '${tenantId}'`);
968
974
  }
969
- const database = tenantDbs.get(dbKey);
970
- if (!database) {
971
- throw new Error(`Database '${dbKey}' not found for tenant '${tenantId}'`);
975
+ if (tenantDbs) {
976
+ const database = tenantDbs.get(dbKey);
977
+ if (database) {
978
+ return database;
979
+ }
980
+ }
981
+ if (this.configStore) {
982
+ const configEntry = await this.configStore.getConfigByKey(tenantId, dbKey);
983
+ if (configEntry) {
984
+ this.registerDatabase(tenantId, dbKey, configEntry.config);
985
+ if (!this.defaultDatabaseKeys.has(tenantId)) {
986
+ this.defaultDatabaseKeys.set(tenantId, dbKey);
987
+ }
988
+ const newTenantDbs = this.databases.get(tenantId);
989
+ if (newTenantDbs) {
990
+ const database = newTenantDbs.get(dbKey);
991
+ if (database) {
992
+ return database;
993
+ }
994
+ }
995
+ }
996
+ }
997
+ if (!tenantDbs) {
998
+ throw new Error(`No databases registered for tenant '${tenantId}'`);
972
999
  }
973
- return database;
1000
+ throw new Error(`Database '${dbKey}' not found for tenant '${tenantId}'`);
974
1001
  }
975
1002
  /**
976
1003
  * Check if a database is registered for a tenant
@@ -1109,7 +1136,7 @@ ${databaseKeys.map(
1109
1136
  if (!databaseKeys.includes(databaseKey)) {
1110
1137
  return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1111
1138
  }
1112
- const database = sqlDatabaseManager.getDatabase(databaseKey, tenantId);
1139
+ const database = await sqlDatabaseManager.getDatabase(tenantId, databaseKey);
1113
1140
  const tables = await database.listTables();
1114
1141
  if (tables.length === 0) {
1115
1142
  return "No tables found in the database.";
@@ -1156,7 +1183,7 @@ ${databaseKeys.map(
1156
1183
  if (!databaseKeys.includes(databaseKey)) {
1157
1184
  return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1158
1185
  }
1159
- const database = sqlDatabaseManager.getDatabase(databaseKey, tenantId);
1186
+ const database = await sqlDatabaseManager.getDatabase(tenantId, databaseKey);
1160
1187
  const tableNames = tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
1161
1188
  if (tableNames.length === 0) {
1162
1189
  return "Error: No table names provided. Please provide a comma-separated list of table names.";
@@ -1313,7 +1340,7 @@ ${trimmedQuery}
1313
1340
  }
1314
1341
  if (databaseKey && databaseKeys.includes(databaseKey)) {
1315
1342
  try {
1316
- const database = sqlDatabaseManager.getDatabase(databaseKey, tenantId);
1343
+ const database = await sqlDatabaseManager.getDatabase(tenantId, databaseKey);
1317
1344
  const dbType = database.getDatabaseType();
1318
1345
  if (dbType === "postgres") {
1319
1346
  try {
@@ -1414,7 +1441,7 @@ ${databaseKeys.map(
1414
1441
  if (!trimmedQuery) {
1415
1442
  return "Error: Empty query provided. Please provide a valid SQL query.";
1416
1443
  }
1417
- const database = sqlDatabaseManager.getDatabase(databaseKey, tenantId);
1444
+ const database = await sqlDatabaseManager.getDatabase(tenantId, databaseKey);
1418
1445
  const result = await database.executeQuery(trimmedQuery);
1419
1446
  return formatQueryResult(result.rows, result.fields);
1420
1447
  } catch (error) {
@@ -4889,11 +4916,20 @@ var SandboxFilesystem = class {
4889
4916
 
4890
4917
  // src/middlewares/codeEvalMiddleware.ts
4891
4918
  import { createMiddleware } from "langchain";
4919
+
4920
+ // src/middlewares/contextSchema.ts
4921
+ import z40 from "zod";
4922
+ var contextSchema = z40.object({
4923
+ runConfig: z40.any()
4924
+ });
4925
+
4926
+ // src/middlewares/codeEvalMiddleware.ts
4892
4927
  function createCodeEvalMiddleware(params = { isolatedLevel: "global" }) {
4893
4928
  const codeEvalTool = createCodeEvalTool({ isolatedLevel: params.isolatedLevel });
4894
4929
  const codeExecuteFileTool = createCodeExecuteFileTool({ isolatedLevel: params.isolatedLevel });
4895
4930
  return createMiddleware({
4896
4931
  name: "codeEvalMiddleware",
4932
+ contextSchema,
4897
4933
  tools: [codeEvalTool, codeExecuteFileTool, getToolClient("convert_to_markdown")]
4898
4934
  });
4899
4935
  }
@@ -4928,6 +4964,7 @@ function createBrowserMiddleware(params = { isolatedLevel: "global" }) {
4928
4964
  ];
4929
4965
  return createMiddleware2({
4930
4966
  name: "browserMiddleware",
4967
+ contextSchema,
4931
4968
  tools
4932
4969
  });
4933
4970
  }
@@ -4948,6 +4985,7 @@ function createSqlMiddleware(params) {
4948
4985
  };
4949
4986
  return createMiddleware3({
4950
4987
  name: "sqlMiddleware",
4988
+ contextSchema,
4951
4989
  tools: [
4952
4990
  createListTablesSqlTool(toolParams),
4953
4991
  createInfoSqlTool(toolParams),
@@ -6967,11 +7005,11 @@ ${body}` : `${frontmatter}
6967
7005
  };
6968
7006
 
6969
7007
  // src/tool_lattice/skill/load_skills.ts
6970
- import z40 from "zod";
7008
+ import z41 from "zod";
6971
7009
  import { tool as tool39 } from "langchain";
6972
7010
 
6973
7011
  // src/tool_lattice/skill/load_skill_content.ts
6974
- import z41 from "zod";
7012
+ import z42 from "zod";
6975
7013
  import { tool as tool40 } from "langchain";
6976
7014
  var LOAD_SKILL_CONTENT_DESCRIPTION = `
6977
7015
  Execute a skill within the main conversation
@@ -7058,15 +7096,15 @@ ${content}`;
7058
7096
  {
7059
7097
  name: "skill",
7060
7098
  description: LOAD_SKILL_CONTENT_DESCRIPTION,
7061
- schema: z41.object({
7062
- skill_name: z41.string().describe("The name of the skill to load")
7099
+ schema: z42.object({
7100
+ skill_name: z42.string().describe("The name of the skill to load")
7063
7101
  })
7064
7102
  }
7065
7103
  );
7066
7104
  };
7067
7105
 
7068
7106
  // src/tool_lattice/skill/load_skill_resource.ts
7069
- import z42 from "zod";
7107
+ import z43 from "zod";
7070
7108
  import { tool as tool41 } from "langchain";
7071
7109
  var LOAD_SKILL_RESOURCE_DESCRIPTION = `Load a specific resource file from a skill's resources directory. Use this tool when you need to access template files, example data, or other resources bundled with a skill. The resource paths are listed in the skill content when using the load_skill_content tool.`;
7072
7110
  var createLoadSkillResourceTool = () => {
@@ -7092,9 +7130,9 @@ var createLoadSkillResourceTool = () => {
7092
7130
  {
7093
7131
  name: "load_skill_resource",
7094
7132
  description: LOAD_SKILL_RESOURCE_DESCRIPTION,
7095
- schema: z42.object({
7096
- skill_name: z42.string().describe("The name of the skill containing the resource"),
7097
- resource_path: z42.string().describe("The path to the resource relative to the skill's resources/ directory")
7133
+ schema: z43.object({
7134
+ skill_name: z43.string().describe("The name of the skill containing the resource"),
7135
+ resource_path: z43.string().describe("The path to the resource relative to the skill's resources/ directory")
7098
7136
  })
7099
7137
  }
7100
7138
  );
@@ -7109,6 +7147,7 @@ function createSkillMiddleware(params = {}) {
7109
7147
  let latestSkills = [];
7110
7148
  return createMiddleware4({
7111
7149
  name: "skillMiddleware",
7150
+ contextSchema,
7112
7151
  tools: [
7113
7152
  // createLoadSkillsTool({ skills: readAll ? undefined : skills }),
7114
7153
  createLoadSkillContentTool(),
@@ -7116,25 +7155,28 @@ function createSkillMiddleware(params = {}) {
7116
7155
  ],
7117
7156
  beforeAgent: async (state, runtime) => {
7118
7157
  try {
7158
+ console.log("latestSkills_before", latestSkills);
7119
7159
  const storeLattice = getStoreLattice("default", "skill");
7120
7160
  const skillStore = storeLattice?.store;
7121
- const tenantId = runtime?.context?.tenantId || state?.tenantId || "default";
7161
+ const tenantId = runtime?.context?.runConfig?.tenantId;
7162
+ if (!tenantId) {
7163
+ throw new Error("tenantId is missing");
7164
+ }
7122
7165
  if (readAll) {
7123
- latestSkills = await skillStore.getAllSkills(tenantId);
7166
+ const allSkills = await skillStore.getAllSkills(tenantId);
7167
+ latestSkills = allSkills.filter((skill) => skill !== void 0 && skill !== null);
7124
7168
  } else if (skills && skills.length > 0) {
7125
- const skillLatticePromises = skills.map(
7126
- (skillId) => skillStore.getSkillById(tenantId, skillId)
7127
- );
7128
- const skillLattices = await Promise.all(skillLatticePromises);
7129
- latestSkills = skillLattices.filter((skill) => skill !== void 0);
7169
+ const allSkills = await skillStore.getAllSkills(tenantId);
7170
+ latestSkills = allSkills.filter((skill) => skills.includes(skill.id));
7130
7171
  }
7131
7172
  } catch (error) {
7132
7173
  console.error("Error fetching skills:", error);
7133
7174
  }
7134
7175
  },
7135
7176
  wrapModelCall: (request, handler) => {
7136
- const skillsPrompt = latestSkills.map((skill) => `## ${skill.name}
7137
- ${skill.description}`).join("\n");
7177
+ console.log("latestSkills_after", latestSkills);
7178
+ const skillsPrompt = latestSkills.filter((skill) => skill !== void 0 && skill !== null && !!skill.name).map((skill) => `## ${skill.name}
7179
+ ${skill.description || ""}`).join("\n");
7138
7180
  const skillsAddendum = `
7139
7181
 
7140
7182
  <available_skills>
@@ -7896,6 +7938,7 @@ function createFilesystemMiddleware(options = {}) {
7896
7938
  ];
7897
7939
  return createMiddleware5({
7898
7940
  name: "FilesystemMiddleware",
7941
+ contextSchema,
7899
7942
  stateSchema: FilesystemStateSchema,
7900
7943
  tools,
7901
7944
  wrapModelCall: systemPrompt ? async (request, handler) => {
@@ -8026,6 +8069,7 @@ function createMetricsMiddleware(params) {
8026
8069
  };
8027
8070
  return createMiddleware6({
8028
8071
  name: "metricsMiddleware",
8072
+ contextSchema,
8029
8073
  tools: [
8030
8074
  createListMetricsDataSourcesTool(toolParams),
8031
8075
  createQueryMetricsListTool(toolParams),
@@ -8044,16 +8088,16 @@ import { GraphInterrupt as GraphInterrupt2, interrupt as interrupt2 } from "@lan
8044
8088
 
8045
8089
  // src/tool_lattice/ask_user_to_clarify/index.ts
8046
8090
  import { tool as tool43 } from "langchain";
8047
- import z43 from "zod";
8048
- var questionSchema = z43.object({
8049
- question: z43.string().describe("The question text to ask the user"),
8050
- options: z43.array(z43.string()).describe("List of EXACT, selectable values. Maximum 3 options allowed. DO NOT include any 'placeholder' options that require the user to type (e.g., do NOT add 'Enter manual value'). If manual input is needed, set allowOther to true instead."),
8051
- type: z43.enum(["single", "multiple"]).describe("Whether the question allows single or multiple selections"),
8052
- required: z43.boolean().optional().default(false).describe("Whether this question must be answered"),
8053
- allowOther: z43.boolean().optional().default(true).describe("Set to true to append an 'Other' option that opens a free-text input field. Use this for open-ended answers or when the 3 options cannot cover all possibilities.")
8091
+ import z44 from "zod";
8092
+ var questionSchema = z44.object({
8093
+ question: z44.string().describe("The question text to ask the user"),
8094
+ options: z44.array(z44.string()).describe("List of EXACT, selectable values. Maximum 3 options allowed. DO NOT include any 'placeholder' options that require the user to type (e.g., do NOT add 'Enter manual value'). If manual input is needed, set allowOther to true instead."),
8095
+ type: z44.enum(["single", "multiple"]).describe("Whether the question allows single or multiple selections"),
8096
+ required: z44.boolean().optional().default(false).describe("Whether this question must be answered"),
8097
+ allowOther: z44.boolean().optional().default(true).describe("Set to true to append an 'Other' option that opens a free-text input field. Use this for open-ended answers or when the 3 options cannot cover all possibilities.")
8054
8098
  });
8055
- var inputSchema = z43.object({
8056
- questions: z43.array(questionSchema).min(1, "At least one question is required").describe("A structured sequence of clarification questions. Use these to gather missing parameters or disambiguate user intent before proceeding.")
8099
+ var inputSchema = z44.object({
8100
+ questions: z44.array(questionSchema).min(1, "At least one question is required").describe("A structured sequence of clarification questions. Use these to gather missing parameters or disambiguate user intent before proceeding.")
8057
8101
  });
8058
8102
  function createAskUserToClarifyTool() {
8059
8103
  return tool43(
@@ -8154,6 +8198,896 @@ function createAskUserClarifyMiddleware() {
8154
8198
  });
8155
8199
  }
8156
8200
 
8201
+ // src/middlewares/widgetMiddleware.ts
8202
+ import { createMiddleware as createMiddleware8 } from "langchain";
8203
+
8204
+ // src/tool_lattice/widget/loadGuidelines.ts
8205
+ import { tool as tool44 } from "langchain";
8206
+ import { z as z45 } from "zod";
8207
+
8208
+ // src/middlewares/guidelines/index.ts
8209
+ var CORE = `# Imagine \u2014 Visual Creation Suite
8210
+
8211
+ ## Modules
8212
+ Call load_guidelines again with the modules parameter to load detailed guidance:
8213
+ - \`diagram\` \u2014 SVG flowcharts, structural diagrams, illustrative diagrams
8214
+ - \`mockup\` \u2014 UI mockups, forms, cards, dashboards
8215
+ - \`interactive\` \u2014 interactive explainers with controls
8216
+ - \`chart\` \u2014 charts and data analysis (includes Chart.js)
8217
+ - \`art\` \u2014 illustration and generative art
8218
+ Pick the closest fit. The module includes all relevant design guidance.
8219
+
8220
+ **Complexity budget \u2014 hard limits:**
8221
+ - Box subtitles: \u22645 words. Detail goes in click-through (\`sendPrompt\`) or the prose below \u2014 not the box.
8222
+ - Colors: \u22642 ramps per diagram. If colors encode meaning (states, tiers), add a 1-line legend. Otherwise use one neutral ramp.
8223
+ - Horizontal tier: \u22644 boxes at full width (~140px each). 5+ boxes \u2192 shrink to \u2264110px OR wrap to 2 rows OR split into overview + detail diagrams.
8224
+
8225
+ If you catch yourself writing "click to learn more" in prose, the diagram itself must ACTUALLY be sparse. Don't promise brevity then front-load everything.
8226
+
8227
+ You create rich visual content \u2014 SVG diagrams/illustrations and HTML interactive widgets \u2014 that renders inline in conversation. The best output feels like a natural extension of the chat.
8228
+
8229
+ ## Core Design System
8230
+
8231
+ These rules apply to ALL use cases.
8232
+
8233
+ ### Philosophy
8234
+ - **Seamless**: Users shouldn't notice where claude.ai ends and your widget begins.
8235
+ - **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces.
8236
+ - **Compact**: Show the essential inline. Explain the rest in text.
8237
+ - **Text goes in your response, visuals go in the tool** \u2014 All explanatory text, descriptions, introductions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element (diagram, chart, interactive widget). Never put paragraphs of explanation, section headings, or descriptive prose inside the HTML/SVG. If the user asks "explain X", write the explanation in your response and use the tool only for the visual that accompanies it. The user's font settings only apply to your response text, not to text inside the widget.
8238
+
8239
+ ### Streaming
8240
+ Output streams token-by-token. Structure code so useful content appears early.
8241
+ - **HTML**: \`<style>\` (short) \u2192 content HTML \u2192 \`<script>\` last.
8242
+ - **SVG**: \`<defs>\` (markers) \u2192 visual elements immediately.
8243
+ - Prefer inline \`style="..."\` over \`<style>\` blocks \u2014 inputs/controls must look correct mid-stream.
8244
+ - Keep \`<style>\` under ~15 lines. Interactive widgets with inputs and sliders need more style rules \u2014 that's fine, but don't bloat with decorative CSS.
8245
+ - Gradients, shadows, and blur flash during streaming DOM diffs. Use solid flat fills instead.
8246
+
8247
+ ### Rules
8248
+ - No \`<!-- comments -->\` or \`/* comments */\` (waste tokens, break streaming)
8249
+ - No font-size below 11px
8250
+ - No emoji \u2014 use CSS shapes or SVG paths
8251
+ - No gradients, drop shadows, blur, glow, or neon effects
8252
+ - No dark/colored backgrounds on outer containers (transparent only \u2014 host provides the bg)
8253
+ - **Typography**: The default font is Anthropic Sans. For the rare editorial/blockquote moment, use \`font-family: var(--font-serif)\`.
8254
+ - **Headings**: h1 = 22px, h2 = 18px, h3 = 16px \u2014 all \`font-weight: 500\`. Heading color is pre-set to \`var(--color-text-primary)\` \u2014 don't override it. Body text = 16px, weight 400, \`line-height: 1.7\`. **Two weights only: 400 regular, 500 bold.** Never use 600 or 700 \u2014 they look heavy against the host UI.
8255
+ - **Sentence case** always. Never Title Case, never ALL CAPS. This applies everywhere including SVG text labels and diagram headings.
8256
+ - **No mid-sentence bolding**, including in your response text around the tool call. Entity names, class names, function names go in \`code style\` not **bold**. Bold is for headings and labels only.
8257
+ - The widget container is \`display: block; width: 100%\`. Your HTML fills it naturally \u2014 no wrapper div needed. Just start with your content directly. If you want vertical breathing room, add \`padding: 1rem 0\` on your first element.
8258
+ - Never use \`position: fixed\` \u2014 the iframe viewport sizes itself to your in-flow content height, so fixed-positioned elements (modals, overlays, tooltips) collapse it to \`min-height: 100px\`. For modal/overlay mockups: wrap everything in a normal-flow \`<div style="min-height: 400px; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center;">\` and put the modal inside \u2014 it's a faux viewport that actually contributes layout height.
8259
+ - No DOCTYPE, \`<html>\`, \`<head>\`, or \`<body>\` \u2014 just content fragments.
8260
+ - When placing text on a colored background (badges, pills, cards, tags), use the darkest shade from that same color family for the text \u2014 never plain black or generic gray.
8261
+ - **Corners**: use \`border-radius: var(--border-radius-md)\` (or \`-lg\` for cards) in HTML. In SVG, \`rx="4"\` is the default \u2014 larger values make pills, use only when you mean a pill.
8262
+ - **No rounded corners on single-sided borders** \u2014 if using \`border-left\` or \`border-top\` accents, set \`border-radius: 0\`. Rounded corners only work with full borders on all sides.
8263
+ - **No titles or prose inside the tool output** \u2014 see Philosophy above.
8264
+ - **Icon sizing**: When using emoji or inline SVG icons, explicitly set \`font-size: 16px\` for emoji or \`width: 16px; height: 16px\` for SVG icons. Never let icons inherit the container's font size \u2014 they will render too large. For larger decorative icons, use 24px max.
8265
+ - No tabs, carousels, or \`display: none\` sections during streaming \u2014 hidden content streams invisibly. Show all content stacked vertically. (Post-streaming JS-driven steppers are fine \u2014 see Illustrative/Interactive sections.)
8266
+ - No nested scrolling \u2014 auto-fit height.
8267
+ - Scripts execute after streaming \u2014 load libraries via \`<script src="https://cdnjs.cloudflare.com/ajax/libs/...">\` (UMD globals), then use the global in a plain \`<script>\` that follows.
8268
+ - **CDN allowlist (CSP-enforced)**: external resources may ONLY load from \`cdnjs.cloudflare.com\`, \`esm.sh\`, \`cdn.jsdelivr.net\`, \`unpkg.com\`. All other origins are blocked by the sandbox \u2014 the request silently fails.
8269
+
8270
+ ### CSS Variables
8271
+ **Backgrounds**: \`--color-background-primary\` (white), \`-secondary\` (surfaces), \`-tertiary\` (page bg), \`-info\`, \`-danger\`, \`-success\`, \`-warning\`
8272
+ **Text**: \`--color-text-primary\` (black), \`-secondary\` (muted), \`-tertiary\` (hints), \`-info\`, \`-danger\`, \`-success\`, \`-warning\`
8273
+ **Borders**: \`--color-border-tertiary\` (0.15\u03B1, default), \`-secondary\` (0.3\u03B1, hover), \`-primary\` (0.4\u03B1), semantic \`-info/-danger/-success/-warning\`
8274
+ **Typography**: \`--font-sans\`, \`--font-serif\`, \`--font-mono\`
8275
+ **Layout**: \`--border-radius-md\` (8px), \`--border-radius-lg\` (12px \u2014 preferred for most components), \`--border-radius-xl\` (16px)
8276
+ All auto-adapt to light/dark mode. For custom colors in HTML, use CSS variables.
8277
+
8278
+ **Dark mode is mandatory** \u2014 every color must work in both modes:
8279
+ - In SVG: use the pre-built color classes (\`c-blue\`, \`c-teal\`, \`c-amber\`, etc.) for colored nodes \u2014 they handle light/dark mode automatically. Never write \`<style>\` blocks for colors.
8280
+ - In SVG: every \`<text>\` element needs a class (\`t\`, \`ts\`, \`th\`) \u2014 never omit fill or use \`fill="inherit"\`. Inside a \`c-{color}\` parent, text classes auto-adjust to the ramp.
8281
+ - In HTML: always use CSS variables (--color-text-primary, --color-text-secondary) for text. Never hardcode colors like color: #333 \u2014 invisible in dark mode.
8282
+ - Mental test: if the background were near-black, would every text element still be readable?
8283
+
8284
+ ### sendPrompt(text)
8285
+ A global function that sends a message to chat as if the user typed it. Use it when the user's next step benefits from Claude thinking. Handle filtering, sorting, toggling, and calculations in JS instead.
8286
+
8287
+ ### Links
8288
+ \`<a href="https://...">\` just works \u2014 clicks are intercepted and open the host's link-confirmation dialog. Or call \`openLink(url)\` directly.
8289
+
8290
+ ## When nothing fits
8291
+ Pick the closest use case below and adapt. When nothing fits cleanly:
8292
+ - Default to editorial layout if the content is explanatory
8293
+ - Default to card layout if the content is a bounded object
8294
+ - All core design system rules still apply
8295
+ - Use \`sendPrompt()\` for any action that benefits from Claude thinking`;
8296
+ var ART_AND_ILLUSTRATION = `## Art and illustration
8297
+ *"Draw me a sunset" / "Create a geometric pattern"*
8298
+
8299
+ Use \`imagine_svg\`. Same technical rules (viewBox, safe area) but the aesthetic is different:
8300
+ - Fill the canvas \u2014 art should feel rich, not sparse
8301
+ - Bold colors: mix \`--color-text-*\` categories for variety (info blue, success green, warning amber)
8302
+ - Art is the one place custom \`<style>\` color blocks are fine \u2014 freestyle colors, \`prefers-color-scheme\` for dark mode variants if you want them
8303
+ - Layer overlapping opaque shapes for depth
8304
+ - Organic forms with \`<path>\` curves, \`<ellipse>\`, \`<circle>\`
8305
+ - Texture via repetition (parallel lines, dots, hatching) not raster effects
8306
+ - Geometric patterns with \`<g transform="rotate()">\` for radial symmetry`;
8307
+ var CHARTS_CHART_JS = `## Charts (Chart.js)
8308
+ \`\`\`html
8309
+ <div style="position: relative; width: 100%; height: 300px;">
8310
+ <canvas id="myChart"></canvas>
8311
+ </div>
8312
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js" onload="initChart()"></script>
8313
+ <script>
8314
+ function initChart() {
8315
+ new Chart(document.getElementById('myChart'), {
8316
+ type: 'bar',
8317
+ data: { labels: ['Q1','Q2','Q3','Q4'], datasets: [{ label: 'Revenue', data: [12,19,8,15] }] },
8318
+ options: { responsive: true, maintainAspectRatio: false }
8319
+ });
8320
+ }
8321
+ if (window.Chart) initChart();
8322
+ </script>
8323
+ \`\`\`
8324
+
8325
+ **Chart.js rules**:
8326
+ - Canvas cannot resolve CSS variables. Use hardcoded hex or Chart.js defaults.
8327
+ - Wrap \`<canvas>\` in \`<div>\` with explicit \`height\` and \`position: relative\`.
8328
+ - **Canvas sizing**: set height ONLY on the wrapper div, never on the canvas element itself. Use position: relative on the wrapper and responsive: true, maintainAspectRatio: false in Chart.js options. Never set CSS height directly on canvas \u2014 this causes wrong dimensions, especially for horizontal bar charts.
8329
+ - For horizontal bar charts: wrapper div height should be at least (number_of_bars * 40) + 80 pixels.
8330
+ - Load UMD build via \`<script src="https://cdnjs.cloudflare.com/ajax/libs/...">\` \u2014 sets \`window.Chart\` global. Follow with plain \`<script>\` (no \`type="module"\`).
8331
+ - **Script load ordering**: CDN scripts may not be loaded when the next \`<script>\` runs (especially during streaming). Always use \`onload="initChart()"\` on the CDN script tag, define your chart init in a named function, and add \`if (window.Chart) initChart();\` as a fallback at the end of your inline script. This guarantees charts render regardless of load order.
8332
+ - Multiple charts: use unique IDs (\`myChart1\`, \`myChart2\`). Each gets its own canvas+div pair.
8333
+ - For bubble and scatter charts: bubble radii extend past their center points, so points near axis boundaries get clipped. Pad the scale range \u2014 set \`scales.y.min\` and \`scales.y.max\` ~10% beyond your data range (same for x). Or use \`layout: { padding: 20 }\` as a blunt fallback.
8334
+ - Chart.js auto-skips x-axis labels when they'd overlap. If you have \u226412 categories and need all labels visible (waterfall, monthly series), set \`scales.x.ticks: { autoSkip: false, maxRotation: 45 }\` \u2014 missing labels make bars unidentifiable.
8335
+
8336
+ **Number formatting**: negative values are \`-$5M\` not \`$-5M\` \u2014 sign before currency symbol. Use a formatter: \`(v) => (v < 0 ? '-' : '') + '$' + Math.abs(v) + 'M'\`.
8337
+
8338
+ **Legends** \u2014 always disable Chart.js default and build custom HTML. The default uses round dots and no values; custom HTML gives small squares, tight spacing, and percentages:
8339
+
8340
+ \`\`\`js
8341
+ plugins: { legend: { display: false } }
8342
+ \`\`\`
8343
+
8344
+ \`\`\`html
8345
+ <div style="display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--color-text-secondary);">
8346
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #3266ad;"></span>Chrome 65%</span>
8347
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #73726c;"></span>Safari 18%</span>
8348
+ </div>
8349
+ \`\`\`
8350
+
8351
+ Include the value/percentage in each label when the data is categorical (pie, donut, single-series bar). Position the legend above the chart (\`margin-bottom\`) or below (\`margin-top\`) \u2014 not inside the canvas.
8352
+
8353
+ **Dashboard layout** \u2014 wrap summary numbers in metric cards (see UI fragment) above the chart. Chart canvas flows below without a card wrapper. Use \`sendPrompt()\` for drill-down: \`sendPrompt('Break down Q4 by region')\`.`;
8354
+ var COLOR_PALETTE = `## Color palette
8355
+
8356
+ 9 color ramps, each with 7 stops from lightest to darkest. 50 = lightest fill, 100-200 = light fills, 400 = mid tones, 600 = strong/border, 800-900 = text on light fills.
8357
+
8358
+ | Class | Ramp | 50 (lightest) | 100 | 200 | 400 | 600 | 800 | 900 (darkest) |
8359
+ |-------|------|------|-----|-----|-----|-----|-----|------|
8360
+ | \`c-purple\` | Purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
8361
+ | \`c-teal\` | Teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
8362
+ | \`c-coral\` | Coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
8363
+ | \`c-pink\` | Pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
8364
+ | \`c-gray\` | Gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
8365
+ | \`c-blue\` | Blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
8366
+ | \`c-green\` | Green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
8367
+ | \`c-amber\` | Amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
8368
+ | \`c-red\` | Red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
8369
+
8370
+ **How to assign colors**: Color should encode meaning, not sequence. Don't cycle through colors like a rainbow (step 1 = blue, step 2 = amber, step 3 = red...). Instead:
8371
+ - Group nodes by **category** \u2014 all nodes of the same type share one color. E.g. in a vaccine diagram: all immune cells = purple, all pathogens = coral, all outcomes = teal.
8372
+ - For illustrative diagrams, map colors to **physical properties** \u2014 warm ramps for heat/energy, cool for cold/calm, green for organic, gray for structural/inert.
8373
+ - Use **gray for neutral/structural** nodes (start, end, generic steps).
8374
+ - Use **2-3 colors per diagram**, not 6+. More colors = more visual noise. A diagram with gray + purple + teal is cleaner than one using every ramp.
8375
+ - **Prefer purple, teal, coral, pink** for general diagram categories. Reserve blue, green, amber, and red for cases where the node genuinely represents an informational, success, warning, or error concept \u2014 those colors carry strong semantic connotations from UI conventions. (Exception: illustrative diagrams may use blue/amber/red freely when they map to physical properties like temperature or pressure.)
8376
+
8377
+ **Text on colored backgrounds:** Always use the 800 or 900 stop from the same ramp as the fill. Never use black, gray, or --color-text-primary on colored fills. **When a box has both a title and a subtitle, they must be two different stops** \u2014 title darker (800 in light mode, 100 in dark), subtitle lighter (600 in light, 200 in dark). Same stop for both reads flat; the weight difference alone isn't enough. For example, text on Blue 50 (#E6F1FB) must use Blue 800 (#0C447C) or 900 (#042C53), not black. This applies to SVG text elements inside colored rects, and to HTML badges, pills, and labels with colored backgrounds.
8378
+
8379
+ **Light/dark mode quick pick** \u2014 use only stops from the table, never off-table hex values:
8380
+ - **Light mode**: 50 fill + 600 stroke + **800 title / 600 subtitle**
8381
+ - **Dark mode**: 800 fill + 200 stroke + **100 title / 200 subtitle**
8382
+ - Apply \`c-{ramp}\` to a \`<g>\` wrapping shape+text, or directly to a \`<rect>\`/\`<circle>\`/\`<ellipse>\`. Never to \`<path>\` \u2014 paths don't get ramp fill. For colored connector strokes use inline \`stroke="#..."\` (any mid-ramp hex works in both modes). Dark mode is automatic for ramp classes. Available: c-gray, c-blue, c-red, c-amber, c-green, c-teal, c-purple, c-coral, c-pink.
8383
+
8384
+ For status/semantic meaning in UI (success, warning, danger) use CSS variables. For categorical coloring in both diagrams and UI, use these ramps.`;
8385
+ var DIAGRAM_TYPES = `## Diagram types
8386
+ *"Explain how compound interest works" / "How does a process scheduler work"*
8387
+
8388
+ **Two rules that cause most diagram failures \u2014 check these before writing each arrow and each box:**
8389
+ 1. **Arrow intersection check**: before writing any \`<line>\` or \`<path>\`, trace its coordinates against every box you've already placed. If the line crosses any rect's interior (not just its source/target), it will visibly slash through that box \u2014 use an L-shaped \`<path>\` detour instead. This applies to arrows crossing labels too.
8390
+ 2. **Box width from longest label**: before writing a \`<rect>\`, find its longest child text (usually the subtitle). \`rect_width = max(title_chars \xD7 8, subtitle_chars \xD7 7) + 24\`. A 100px-wide box holds at most a 10-char subtitle. If your subtitle is "Files, APIs, streams" (20 chars), the box needs 164px minimum \u2014 100px will visibly overflow.
8391
+
8392
+ **Tier packing:** Compute total width BEFORE placing. Example \u2014 4 pub/sub consumer boxes:
8393
+ - WRONG: x=40,160,260,360 w=160 \u2192 40-60px overlaps (4\xD7160=640 > 480 available)
8394
+ - RIGHT: x=50,200,350,500 w=130 gap=20 \u2192 fits (4\xD7130 + 3\xD720 = 580 \u2264 590 safe width; right edge at 630 \u2264 640)
8395
+ Work bottom-up for trees: size leaf tier first, parent width \u2265 sum of children.
8396
+
8397
+ **Diagrams are the hardest use case** \u2014 they have the highest failure rate due to precise coordinate math. Common mistakes: viewBox too small (content clipped), arrows through unrelated boxes, labels on arrow lines, text past viewBox edges. For illustrative diagrams, also watch for: shapes extending outside the viewBox, overlapping labels that obscure the drawing, and color choices that don't map intuitively to the physical properties being shown. Double-check coordinates before finalizing.
8398
+
8399
+ Use \`imagine_svg\` for diagrams. The widget automatically wraps SVG output in a card.
8400
+
8401
+ **Pick the right diagram type.** The decision is about *intent*, not subject matter. Ask: is the user trying to *document* this, or *understand* it?
8402
+
8403
+ **Reference diagrams** \u2014 the user wants a map they can point at. Precision matters more than feeling. Boxes, labels, arrows, containment. These are the diagrams you'd find in documentation.
8404
+ - **Flowchart** \u2014 steps in sequence, decisions branching, data transforming. Good for: approval workflows, request lifecycles, build pipelines, "what happens when I click submit". Trigger phrases: *"walk me through the process"*, *"what are the steps"*, *"what's the flow"*.
8405
+ - **Structural diagram** \u2014 things inside other things. Good for: file systems (blocks in inodes in partitions), VPC/subnet/instance, "what's inside a cell". Trigger phrases: *"what's the architecture"*, *"how is this organised"*, *"where does X live"*.
8406
+
8407
+ **Intuition diagrams** \u2014 the user wants to *feel* how something works. The goal isn't a correct map, it's the right mental model. These should look nothing like a flowchart. The subject doesn't need a physical form \u2014 it needs a *visual metaphor*.
8408
+ - **Illustrative diagram** \u2014 draw the mechanism. Physical things get cross-sections (water heaters, engines, lungs). Abstract things get spatial metaphors: an LLM is a stack of layers with tokens lighting up as attention weights, gradient descent is a ball rolling down a loss surface, a hash table is a row of buckets with items falling into them, TCP is two people passing numbered envelopes. Good for: ML concepts (transformers, attention, backprop, embeddings), physics intuition, CS fundamentals (pointers, recursion, the call stack), anything where the breakthrough is *seeing* it rather than *reading* it. Trigger phrases: *"how does X actually work"*, *"explain X"*, *"I don't get X"*, *"give me an intuition for X"*.
8409
+
8410
+ **Route on the verb, not the noun.** Same subject, different diagram depending on what was asked:
8411
+
8412
+ | User says | Type | What to draw |
8413
+ |---|---|---|
8414
+ | "how do LLMs work" | **Illustrative** | Token row, stacked layer slabs, attention threads glowing warm between tokens. Go interactive if you can. |
8415
+ | "transformer architecture" | Structural | Labelled boxes: embedding, attention heads, FFN, layer norm. |
8416
+ | "how does attention work" | **Illustrative** | One query token, a fan of lines to every key, line opacity = weight. |
8417
+ | "how does gradient descent work" | **Illustrative** | Contour surface, a ball, a trail of steps. Slider for learning rate. |
8418
+ | "what are the training steps" | Flowchart | Forward \u2192 loss \u2192 backward \u2192 update. Boxes and arrows. |
8419
+ | "how does TCP work" | **Illustrative** | Two endpoints, numbered packets in flight, an ACK returning. |
8420
+ | "TCP handshake sequence" | Flowchart | SYN \u2192 SYN-ACK \u2192 ACK. Three boxes. |
8421
+ | "explain the Krebs cycle" / "how does the event loop work" | **HTML stepper** | Click through stages. Never a ring. |
8422
+ | "how does a hash map work" | **Illustrative** | Key falling through a funnel into one of N buckets. |
8423
+ | "draw the database schema" / "show me the ERD" | **mermaid.js** | \`erDiagram\` syntax. Not SVG. |
8424
+
8425
+ The illustrative route is the default for *"how does X work"* with no further qualification. It is the more ambitious choice \u2014 don't chicken out into a flowchart because it feels safer. Claude draws these well.
8426
+
8427
+ Don't mix families in one diagram. If you need both, draw the intuition version first (build the mental model), then the reference version (fill in the precise labels) as a second tool call with prose between.
8428
+
8429
+ **For complex topics, use multiple SVG calls** \u2014 break the explanation into a series of smaller diagrams rather than one dense diagram. Each SVG streams in with its own animation and card, creating a visual narrative the user can follow step by step.
8430
+
8431
+ **Always add prose between diagrams** \u2014 never stack multiple SVG calls back-to-back without text. Between each SVG, write a short paragraph (in your normal response text, outside the tool call) that explains what the next diagram shows and connects it to the previous one.
8432
+
8433
+ **Promise only what you deliver** \u2014 if your response text says "here are three diagrams", you must include all three tool calls. Never promise a follow-up diagram and omit it. If you can only fit one diagram, adjust your text to match. One complete diagram is better than three promised and one delivered.
8434
+
8435
+ #### Flowchart
8436
+
8437
+ For sequential processes, cause-and-effect, decision trees.
8438
+
8439
+ **Planning**: Size boxes to fit their text generously. At 14px sans-serif, each character is ~8px wide \u2014 a label like "Load Balancer" (13 chars) needs a rect at least 140px wide. When in doubt, make boxes wider and leave more space between them. Cramped diagrams are the most common failure mode.
8440
+
8441
+ **Special characters are wider**: Chemical formulas (C\u2086H\u2081\u2082O\u2086), math notation (\u2211, \u222B, \u221A), subscripts/superscripts via <tspan> with dy/baseline-shift, and Unicode symbols all render wider than plain Latin characters. For labels containing formulas or special notation, add 30-50% extra width to your estimate. When in doubt, make the box wider \u2014 overflow looks worse than extra padding.
8442
+
8443
+ **Spacing**: 60px minimum between boxes, 24px padding inside boxes, 12px between text and edges. Leave 10px gap between arrowheads and box edges. Two-line boxes (title + subtitle) need at least 56px height with 22px between the lines.
8444
+
8445
+ **Vertical text placement**: Every \`<text>\` inside a box needs \`dominant-baseline="central"\`, with y set to the *centre* of the slot it sits in. Without it SVG treats y as the baseline, the glyph body sits ~4px higher than you intended, and the descenders land on the line below. Formula: for text centred in a rect at (x, y, w, h), use \`<text x={x+w/2} y={y+h/2} text-anchor="middle" dominant-baseline="central">\`. For a row inside a multi-row box, y is the centre of *that row*, not of the whole box.
8446
+
8447
+ **Layout**: Prefer single-direction flows (all top-down or all left-right). Keep diagrams simple \u2014 max 4-5 nodes per diagram. The widget is narrow (~680px) so complex layouts break.
8448
+
8449
+ **When the prompt itself is over budget**: if the user lists 6+ components ("draw me auth, products, orders, payments, gateway, queue"), don't draw all of them in one pass \u2014 you'll get overlapping boxes and arrows through text, every time. Decompose: (1) a stripped overview with the boxes only and at most one or two arrows showing the main flow \u2014 no fan-outs, no N-to-N meshes; (2) then one diagram per interesting sub-flow ("here's what happens when an order is placed", "here's the auth handshake"), each with 3-4 nodes and room to breathe. Count the nouns before you draw. The user asked for completeness \u2014 give it to them across several diagrams, not crammed into one.
8450
+
8451
+ **Cycles don't get drawn as rings.** If the last stage feeds back into the first (Krebs cycle, event loop, GC mark-and-sweep, TCP retransmit), your instinct is to place the stages around a circle. Don't. Every spacing rule in this spec is Cartesian \u2014 there is no collision check for "input box orbits outside stage box on a ring". You will get satellite boxes overlapping the stages they feed, labels sitting on the dashed circle, and tangential arrows that point nowhere. The ring is decoration; the loop is conveyed by the return arrow.
8452
+
8453
+ Build a stepper in \`imagine_html\`. One panel per stage, dots or pills showing position (\u25CF \u25CB \u25CB), Next wraps from the last stage back to the first \u2014 that's the loop. Each panel owns its inputs and products: an event loop's pending callbacks live *inside* the Poll panel, not floating next to a box on a ring. Nothing collides because nothing shares the canvas. Only fall back to a linear SVG (stages in a row, curved \`<path>\` return arrow) when there's one input and one output total and no per-stage detail to show.
8454
+
8455
+ **Feedback loops in linear flows:** Don't draw a physical arrow traversing the layout (it fights the flow direction and clips edges). Instead:
8456
+ - Small \`\u21BB\` glyph + text near the cycle point: \`<text>\u21BB returns to start</text>\`
8457
+ - Or restructure the whole diagram as a circle if the cycle IS the point
8458
+
8459
+ **Arrows:** A line from A to B must not cross any other box or label. If the direct path crosses something, route around with an L-bend: \`<path d="M x1 y1 L x1 ymid L x2 ymid L x2 y2"/>\`. Place arrow labels in clear space, not on the midpoint.
8460
+
8461
+ Keep all nodes the same height when they have the same content type (e.g. all single-line boxes = 44px, all two-line boxes = 56px).
8462
+
8463
+ **Flowchart components** \u2014 use these patterns consistently:
8464
+
8465
+ *Single-line node* (44px tall): title only. The \`c-blue\` class sets fill, stroke, and text colors for both light and dark mode automatically \u2014 no \`<style>\` block needed.
8466
+ \`\`\`svg
8467
+ <g class="node c-blue" onclick="sendPrompt('Tell me more about T-cells')">
8468
+ <rect x="100" y="20" width="180" height="44" rx="8" stroke-width="0.5"/>
8469
+ <text class="th" x="190" y="42" text-anchor="middle" dominant-baseline="central">T-cells</text>
8470
+ </g>
8471
+ \`\`\`
8472
+
8473
+ *Two-line node* (56px tall): bold title + muted subtitle.
8474
+ \`\`\`svg
8475
+ <g class="node c-blue" onclick="sendPrompt('Tell me more about dendritic cells')">
8476
+ <rect x="100" y="20" width="200" height="56" rx="8" stroke-width="0.5"/>
8477
+ <text class="th" x="200" y="38" text-anchor="middle" dominant-baseline="central">Dendritic cells</text>
8478
+ <text class="ts" x="200" y="56" text-anchor="middle" dominant-baseline="central">Detect foreign antigens</text>
8479
+ </g>
8480
+ \`\`\`
8481
+
8482
+ *Connector* (no label \u2014 meaning is clear from source + target):
8483
+ \`\`\`svg
8484
+ <line x1="200" y1="76" x2="200" y2="120" class="arr" marker-end="url(#arrow)"/>
8485
+ \`\`\`
8486
+
8487
+ *Neutral node* (gray, for start/end/generic steps): use \`class="box"\` for auto-themed fill/stroke, and default text classes.
8488
+
8489
+ Make all nodes clickable by default \u2014 wrap in \`<g class="node" onclick="sendPrompt('...')">\`. The hover effect is built in.
8490
+
8491
+ #### Structural diagram
8492
+
8493
+ For concepts where physical or logical containment matters \u2014 things inside other things.
8494
+
8495
+ **When to use**: The explanation depends on *where* processes happen. Examples: how a cell works (organelles inside a cell), how a file system works (blocks inside inodes inside partitions), how a building's HVAC works (ducts inside floors inside a building), how a CPU cache hierarchy works (L1 inside core, L2 shared).
8496
+
8497
+ **Core idea**: Large rounded rects are containers. Smaller rects inside them are regions or sub-structures. Text labels describe what happens in each region. Arrows show flow between regions or from external inputs/outputs.
8498
+
8499
+ **Container rules**:
8500
+ - Outermost container: large rounded rect, rx=20-24, lightest fill (50 stop), 0.5px stroke (600 stop). Label at top-left inside, 14px bold.
8501
+ - Inner regions: medium rounded rects, rx=8-12, next shade fill (100-200 stop). Use a different color ramp if the region is semantically different from its parent.
8502
+ - 20px minimum padding inside every container \u2014 text and inner regions must not touch the container edges.
8503
+ - Max 2-3 nesting levels. Deeper nesting gets unreadable at 680px width.
8504
+
8505
+ **Layout**:
8506
+ - Place inner regions side by side within the container, with 16px+ gap between them.
8507
+ - External inputs (sunlight, water, data, requests) sit outside the container with arrows pointing in.
8508
+ - External outputs sit outside with arrows pointing out.
8509
+ - Keep external labels short \u2014 one word or a short phrase. Details go in the prose between diagrams.
8510
+
8511
+ **What goes inside regions**: Text only \u2014 the region name (14px bold) and a short description of what happens there (12px). Don't put flowchart-style boxes inside regions. Don't draw illustrations or icons inside.
8512
+
8513
+ **Structural container example** (library branch with two side-by-side regions, an internal labeled arrow, and an external input). ViewBox 700x320, horizontal layout, color classes handle both light and dark mode \u2014 no \`<style>\` block:
8514
+ \`\`\`svg
8515
+ <defs>
8516
+ <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
8517
+ <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
8518
+ </marker>
8519
+ </defs>
8520
+ <!-- Outer container -->
8521
+ <g class="c-green">
8522
+ <rect x="120" y="30" width="560" height="260" rx="20" stroke-width="0.5"/>
8523
+ <text class="th" x="400" y="62" text-anchor="middle">Library branch</text>
8524
+ <text class="ts" x="400" y="80" text-anchor="middle">Main floor</text>
8525
+ </g>
8526
+ <!-- Inner: Circulation desk -->
8527
+ <g class="c-teal">
8528
+ <rect x="150" y="100" width="220" height="160" rx="12" stroke-width="0.5"/>
8529
+ <text class="th" x="260" y="130" text-anchor="middle">Circulation desk</text>
8530
+ <text class="ts" x="260" y="148" text-anchor="middle">Checkouts, returns</text>
8531
+ </g>
8532
+ <!-- Inner: Reading room -->
8533
+ <g class="c-amber">
8534
+ <rect x="450" y="100" width="210" height="160" rx="12" stroke-width="0.5"/>
8535
+ <text class="th" x="555" y="130" text-anchor="middle">Reading room</text>
8536
+ <text class="ts" x="555" y="148" text-anchor="middle">Seating, reference</text>
8537
+ </g>
8538
+ <!-- Arrow between inner boxes with label -->
8539
+ <text class="ts" x="410" y="175" text-anchor="middle">Books</text>
8540
+ <line x1="370" y1="185" x2="448" y2="185" class="arr" marker-end="url(#arrow)"/>
8541
+ <!-- External input: New acq. \u2014 text vertically aligned with arrow -->
8542
+ <text class="ts" x="40" y="185" text-anchor="middle">New acq.</text>
8543
+ <line x1="75" y1="185" x2="118" y2="185" class="arr" marker-end="url(#arrow)"/>
8544
+ \`\`\`
8545
+
8546
+ **Color in structural diagrams**: Nested regions need distinct ramps \u2014 \`c-{ramp}\` classes resolve to fixed fill/stroke stops, so the same class on parent and child gives identical fills and flattens the hierarchy. Pick a *related* ramp for inner structures (e.g. Green for the library envelope, Teal for the circulation desk inside it) and a *contrasting* ramp for a region that does something functionally different (e.g. Amber for the reading room). This keeps the diagram scannable \u2014 you can see at a glance which parts are related.
8547
+
8548
+ **Database schemas / ERDs \u2014 use mermaid.js, not SVG.** A schema table is a header plus N field rows plus typed columns plus crow's-foot connectors. That is a text-layout problem and hand-placing it in SVG fails the same way every time. mermaid.js \`erDiagram\` does layout, cardinality, and connector routing for free. ERDs only; everything else stays in SVG.
8549
+
8550
+ \`\`\`
8551
+ erDiagram
8552
+ USERS ||--o{ POSTS : writes
8553
+ POSTS ||--o{ COMMENTS : has
8554
+ USERS {
8555
+ uuid id PK
8556
+ string email
8557
+ timestamp created_at
8558
+ }
8559
+ POSTS {
8560
+ uuid id PK
8561
+ uuid user_id FK
8562
+ string title
8563
+ }
8564
+ \`\`\`
8565
+
8566
+ Use \`imagine_html\` for ERDs. Import and initialize in a \`<script type="module">\`. The host CSS re-styles mermaid's output to match the design system \u2014 keep the init block exactly as shown (fontFamily + fontSize are used for layout measurement; deviate and text clips). After rendering, replace sharp-cornered entity \`<path>\` elements with rounded \`<rect rx="8">\` to match the design system, and strip borders from attribute rows (only the outer container and header row keep visible borders \u2014 alternating fill colors separate the rows):
8567
+ \`\`\`html
8568
+ <style>
8569
+ #erd svg.erDiagram .divider path { stroke-opacity: 0.5; }
8570
+ #erd svg.erDiagram .row-rect-odd path,
8571
+ #erd svg.erDiagram .row-rect-odd rect,
8572
+ #erd svg.erDiagram .row-rect-even path,
8573
+ #erd svg.erDiagram .row-rect-even rect { stroke: none !important; }
8574
+ </style>
8575
+ <div id="erd"></div>
8576
+ <script type="module">
8577
+ import mermaid from 'https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs';
8578
+ const dark = matchMedia('(prefers-color-scheme: dark)').matches;
8579
+ await document.fonts.ready;
8580
+ mermaid.initialize({
8581
+ startOnLoad: false,
8582
+ theme: 'base',
8583
+ fontFamily: '"Anthropic Sans", sans-serif',
8584
+ themeVariables: {
8585
+ darkMode: dark,
8586
+ fontSize: '13px',
8587
+ fontFamily: '"Anthropic Sans", sans-serif',
8588
+ lineColor: dark ? '#9c9a92' : '#73726c',
8589
+ textColor: dark ? '#c2c0b6' : '#3d3d3a',
8590
+ },
8591
+ });
8592
+ const { svg } = await mermaid.render('erd-svg', \`erDiagram
8593
+ USERS ||--o{ POSTS : writes
8594
+ POSTS ||--o{ COMMENTS : has\`);
8595
+ document.getElementById('erd').innerHTML = svg;
8596
+
8597
+ // Round only the outermost entity box corners (not internal row stripes)
8598
+ document.querySelectorAll('#erd svg.erDiagram .node').forEach(node => {
8599
+ const firstPath = node.querySelector('path[d]');
8600
+ if (!firstPath) return;
8601
+ const d = firstPath.getAttribute('d');
8602
+ const nums = d.match(/-?[\\d.]+/g)?.map(Number);
8603
+ if (!nums || nums.length < 8) return;
8604
+ const xs = [nums[0], nums[2], nums[4], nums[6]];
8605
+ const ys = [nums[1], nums[3], nums[5], nums[7]];
8606
+ const x = Math.min(...xs), y = Math.min(...ys);
8607
+ const w = Math.max(...xs) - x, h = Math.max(...ys) - y;
8608
+ const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
8609
+ rect.setAttribute('x', x); rect.setAttribute('y', y);
8610
+ rect.setAttribute('width', w); rect.setAttribute('height', h);
8611
+ rect.setAttribute('rx', '8');
8612
+ for (const a of ['fill', 'stroke', 'stroke-width', 'class', 'style']) {
8613
+ if (firstPath.hasAttribute(a)) rect.setAttribute(a, firstPath.getAttribute(a));
8614
+ }
8615
+ firstPath.replaceWith(rect);
8616
+ });
8617
+
8618
+ // Strip borders from attribute rows (mermaid v11: .row-rect-odd / .row-rect-even)
8619
+ document.querySelectorAll('#erd svg.erDiagram .row-rect-odd path, #erd svg.erDiagram .row-rect-even path').forEach(p => {
8620
+ p.setAttribute('stroke', 'none');
8621
+ });
8622
+ </script>
8623
+ \`\`\`
8624
+
8625
+ Works identically for \`classDiagram\` \u2014 swap the diagram source; init stays the same.
8626
+
8627
+ #### Illustrative diagram
8628
+
8629
+ For building *intuition*. The subject might be physical (an engine, a lung) or completely abstract (attention, recursion, gradient descent) \u2014 what matters is that a spatial drawing conveys the mechanism better than labelled boxes would. These are the diagrams that make someone go "oh, *that's* what it's doing."
8630
+
8631
+ **Two flavours, same rules:**
8632
+ - **Physical subjects** get drawn as simplified versions of themselves. Cross-sections, cutaways, schematics. A water heater is a tank with a burner underneath. A lung is a branching tree in a cavity. You're drawing *the thing*, stylised.
8633
+ - **Abstract subjects** get drawn as *spatial metaphors*. You're inventing a shape for something that doesn't have one \u2014 but the shape should make the mechanism obvious. A transformer is a stack of horizontal slabs with a bright thread of attention connecting tokens across layers. A hash function is a funnel scattering items into a row of buckets. The call stack is literally a stack of frames growing and shrinking. Embeddings are dots clustering in space. The metaphor *is* the explanation.
8634
+
8635
+ This is the most ambitious diagram type and the one Claude is best at. Lean into it. Use colour for intensity (a hot attention weight glows amber, a cold one stays gray). Use repetition for scale (many small circles = many parameters).
8636
+
8637
+ **Prefer interactive over static.** A static cross-section is a good answer; a cross-section you can *operate* is a great one. The decision rule: if the real-world system has a control, give the diagram that control. A water heater has a thermostat \u2014 so give the user a slider that shifts the hot/cold boundary, a toggle that fires the burner and animates convection currents. An LLM has input tokens \u2014 let the user click one and watch the attention weights re-fan. A cache has a hit rate \u2014 let them drag it and watch latency change. Reach for \`imagine_html\` with inline SVG first; only fall back to static \`imagine_svg\` when there's genuinely nothing to twiddle.
8638
+
8639
+ **When NOT to use**: The user is asking for a *reference*, not an *intuition*. "What are the components of a transformer" wants labelled boxes \u2014 that's a structural diagram. "Walk me through our CI pipeline" wants sequential steps \u2014 that's a flowchart. Also skip this when the metaphor would be arbitrary rather than revealing: drawing "the cloud" as a cloud shape or "microservices" as little houses doesn't teach anything about how they work. If the drawing doesn't make the *mechanism* clearer, don't draw it.
8640
+
8641
+ **Fidelity ceiling**: These are schematics, not illustrations. Every shape should read at a glance. If a \`<path>\` needs more than ~6 segments to draw, simplify it. A tank is a rounded rect, not a B\xE9zier portrait of a tank. A flame is three triangles, not a fire. Recognisable silhouette beats accurate contour every time \u2014 if you find yourself carefully tracing an outline, you're overshooting.
8642
+
8643
+ **Core principle**: Draw the mechanism, not a diagram *about* the mechanism. Spatial arrangement carries the meaning; labels annotate. A good illustrative diagram works with the labels removed.
8644
+
8645
+ **What changes from flowchart/structural rules**:
8646
+
8647
+ - **Shapes are freeform.** Use \`<path>\`, \`<ellipse>\`, \`<circle>\`, \`<polygon>\`, and curved lines to represent real forms. A water tank is a tall rect with rounded bottom. A heart valve is a pair of curved paths. A circuit trace is a thin polyline. You are not limited to rounded rects.
8648
+ - **Layout follows the subject's geometry**, not a grid. If the thing is tall and narrow (a water heater, a thermometer), the diagram is tall and narrow. If it's wide and flat (a PCB, a geological cross-section), the diagram is wide. Let the subject dictate proportions within the 680px viewBox width.
8649
+ - **Color encodes intensity**, not category. For physical subjects: warm ramps (amber, coral, red) = heat/energy/pressure, cool ramps (blue, teal) = cold/calm, gray = inert structure. For abstract subjects: warm = active/high-weight/attended-to, cool or gray = dormant/low-weight/ignored. A user should be able to glance at the diagram and see *where the action is* without reading a single label.
8650
+ - **Layering and overlap are encouraged \u2014 for shapes.** Unlike flowcharts where boxes must never overlap, illustrative diagrams can layer shapes for depth \u2014 a pipe entering a tank, attention lines fanning through layers, insulation wrapping a chamber. Use z-ordering (later in source = on top) deliberately.
8651
+ - **Text is the exception \u2014 never let a stroke cross it.** The overlap permission is for shapes only. Every label needs 8px of clear air between its baseline/cap-height and the nearest stroke. Don't solve this with a background rect \u2014 solve it by *placing the text somewhere else*. Labels go in the quiet regions: above the drawing, below it, in the margin with a leader line, or in the gap between two fans of lines. If there is no quiet region, the drawing is too dense \u2014 remove something or split into two diagrams.
8652
+ - **Small shape-based indicators are allowed** when they communicate physical state. Triangles for flames. Circles for bubbles or particles. Wavy lines for steam or heat radiation. Parallel lines for vibration. These aren't decoration \u2014 they tell the user what's happening physically. Keep them simple: basic SVG primitives, not detailed illustrations.
8653
+ - **One gradient per diagram is permitted** \u2014 the only exception to the global no-gradients rule \u2014 and only to show a *continuous* physical property across a region (temperature stratification in a tank, pressure drop along a pipe, concentration in a solution). It must be a single \`<linearGradient>\` between exactly two stops from the same colour ramp. No radial gradients, no multi-stop fades, no gradient-as-aesthetic. If two stacked flat-fill rects communicate the same thing, do that instead.
8654
+ - **Animation is permitted for interactive HTML versions.** Use CSS \`@keyframes\` animating only \`transform\` and \`opacity\`. Keep loops under ~2s, and wrap every animation in \`@media (prefers-reduced-motion: no-preference)\` so it's opt-out by default. Animations should show how the system *behaves* \u2014 convection current, rotation, flow \u2014 not just move for the sake of moving. No physics engines or heavy libraries.
8655
+
8656
+ All core rules still apply (viewBox 680px, dark mode mandatory, 14/12px text, pre-built classes, arrow marker, clickable nodes).
8657
+
8658
+ **Label placement**:
8659
+ - Place labels *outside* the drawn object when possible, with a thin leader line (0.5px dashed, \`var(--t)\` stroke) pointing to the relevant part. This keeps the illustration uncluttered.
8660
+ - For large internal zones (like temperature regions in a tank), labels can sit inside if there's ample clear space \u2014 minimum 20px from any edge.
8661
+ - External labels sit in the margin area or above/below the object. **Pick one side for labels and put them all there** \u2014 at 680px wide you don't have room for a drawing *and* label columns on both sides. Reserve at least 140px of horizontal margin on the label side. Labels on the left are the ones that clip: \`text-anchor="end"\` extends leftward from x, and with multi-line callouts it's very easy to blow past x=0 without noticing. Default to right-side labels with \`text-anchor="start"\` unless the subject's geometry forces otherwise. Use \`class="ts"\` (12px) for callouts, \`class="th"\` (14px medium) for major component names.
8662
+
8663
+ **Composition approach**:
8664
+ 1. Start with the main object's silhouette \u2014 the largest shape, centered in the viewBox.
8665
+ 2. Add internal structure: chambers, pipes, membranes, mechanical parts.
8666
+ 3. Add external connections: pipes entering/exiting, arrows showing flow direction, labels for inputs and outputs.
8667
+ 4. Add state indicators last: color fills showing temperature/pressure/concentration, small animated elements showing movement or energy.
8668
+ 5. Leave generous whitespace around the object for labels \u2014 don't crowd annotations against the viewBox edges.
8669
+
8670
+ **Static vs interactive**: Static cutaways and cross-sections work best as pure \`imagine_svg\`. If the diagram benefits from controls \u2014 a slider that changes a temperature zone, buttons toggling between operating states, live readouts \u2014 use \`imagine_html\` with inline SVG for the drawing and HTML controls around it.
8671
+
8672
+ **Illustrative diagram example** \u2014 interactive water heater cross-section with vivid physical-realism colors, animated convection currents, and controls. Uses \`imagine_html\` with inline SVG: a thermostat slider shifts the hot/cold gradient boundary, a heating toggle animates flames on/off and transitions convection to paused. viewBox is 680x560; tank occupies x=180..440, leaving 140px+ of right margin for labels. Smooth convection paths use \`stroke-dasharray:5 5\` at ~1.6s for a gentle flow feel. A warm-glow overlay on the hot zone pulses subtly when heating is on. Flame shapes use warm gradient fills and clean opacity transitions. Labels sit along the right margin with leader lines.
8673
+ \`\`\`html
8674
+ <style>
8675
+ @keyframes conv { to { stroke-dashoffset: -20; } }
8676
+ @keyframes flicker { 0%,100%{opacity:1} 50%{opacity:.82} }
8677
+ @keyframes glow { 0%,100%{opacity:.3} 50%{opacity:.6} }
8678
+ .conv { stroke-dasharray:5 5; animation: conv var(--dur,1.6s) linear infinite; transition: opacity .5s; }
8679
+ .conv.off { opacity:0; animation-play-state:paused; }
8680
+ #flames path { transition: opacity .5s; }
8681
+ #flames.off path { opacity:0; animation:none; }
8682
+ #flames path:nth-child(odd) { animation: flicker .6s ease-in-out infinite; }
8683
+ #flames path:nth-child(even) { animation: flicker .8s ease-in-out infinite .15s; }
8684
+ #warm-glow { animation: glow 3s ease-in-out infinite; transition: opacity .5s; }
8685
+ #warm-glow.off { opacity:0; animation:none; }
8686
+ .toggle-track { position:relative;width:32px;height:18px;background:var(--color-border-secondary);border-radius:9px;transition:background .2s;display:inline-block; }
8687
+ .toggle-track:has(input:checked) { background:var(--color-text-info); }
8688
+ #heat-toggle:checked + span { transform:translateX(14px); }
8689
+ </style>
8690
+ <svg width="100%" viewBox="0 0 680 560">
8691
+ <defs>
8692
+ <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker>
8693
+ <linearGradient id="tg" x1="0" y1="0" x2="0" y2="1">
8694
+ <stop id="gh" offset="40%" stop-color="#E8593C" stop-opacity="0.45"/>
8695
+ <stop id="gc" offset="40%" stop-color="#3B8BD4" stop-opacity="0.4"/>
8696
+ </linearGradient>
8697
+ <linearGradient id="fg1" x1="0" y1="1" x2="0" y2="0"><stop offset="0%" stop-color="#E85D24"/><stop offset="60%" stop-color="#F2A623"/><stop offset="100%" stop-color="#FCDE5A"/></linearGradient>
8698
+ <linearGradient id="fg2" x1="0" y1="1" x2="0" y2="0"><stop offset="0%" stop-color="#D14520"/><stop offset="50%" stop-color="#EF8B2C"/><stop offset="100%" stop-color="#F9CB42"/></linearGradient>
8699
+ <linearGradient id="pipe-h" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#D05538" stop-opacity=".25"/><stop offset="100%" stop-color="#D05538" stop-opacity=".08"/></linearGradient>
8700
+ <linearGradient id="pipe-c" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3B8BD4" stop-opacity=".25"/><stop offset="100%" stop-color="#3B8BD4" stop-opacity=".08"/></linearGradient>
8701
+ <clipPath id="tc"><rect x="180" y="55" width="260" height="390" rx="14"/></clipPath>
8702
+ </defs>
8703
+ <!-- Tank fill -->
8704
+ <g clip-path="url(#tc)"><rect x="180" y="55" width="260" height="390" fill="url(#tg)"/></g>
8705
+ <!-- Warm glow overlay (pulses when heating) -->
8706
+ <g clip-path="url(#tc)"><rect id="warm-glow" x="180" y="55" width="260" height="160" fill="#E8593C" opacity=".3"/></g>
8707
+ <!-- Tank shell (double stroke for solidity) -->
8708
+ <rect x="180" y="55" width="260" height="390" rx="14" fill="none" stroke="var(--t)" stroke-width="2.5" opacity=".25"/>
8709
+ <rect x="180" y="55" width="260" height="390" rx="14" fill="none" stroke="var(--t)" stroke-width="1"/>
8710
+ <!-- Hot pipe out (top right) -->
8711
+ <rect x="370" y="14" width="16" height="50" rx="4" fill="url(#pipe-h)"/>
8712
+ <path d="M378 14V55" stroke="var(--t)" stroke-width="3" stroke-linecap="round" fill="none"/>
8713
+ <!-- Cold pipe in + dip tube (top left) -->
8714
+ <rect x="234" y="14" width="16" height="50" rx="4" fill="url(#pipe-c)"/>
8715
+ <path d="M242 14V55" stroke="var(--t)" stroke-width="3" stroke-linecap="round" fill="none"/>
8716
+ <path d="M242 55V395" stroke="var(--t)" stroke-width="2.5" stroke-linecap="round" fill="none" opacity=".5"/>
8717
+ <!-- Convection currents (curved paths at different speeds) -->
8718
+ <path class="conv" style="--dur:1.6s" fill="none" stroke="#D05538" stroke-width="1" opacity=".5" d="M350 380C355 320,365 240,358 140Q355 110,340 100"/>
8719
+ <path class="conv" style="--dur:2.1s" fill="none" stroke="#C04828" stroke-width=".8" opacity=".35" d="M300 390C308 340,320 260,315 170Q312 130,298 115"/>
8720
+ <path class="conv" style="--dur:2.6s" fill="none" stroke="#B05535" stroke-width=".7" opacity=".3" d="M380 370C382 310,388 230,382 150Q378 120,365 110"/>
8721
+ <!-- Burner bar -->
8722
+ <rect x="188" y="454" width="244" height="5" rx="2" fill="var(--t)" opacity=".6"/>
8723
+ <rect x="220" y="462" width="180" height="6" rx="3" fill="var(--t)" opacity=".3"/>
8724
+ <!-- Flames (gradient-filled organic shapes) -->
8725
+ <g id="flames">
8726
+ <path d="M240,454Q248,430 252,438Q256,424 260,454Z" fill="url(#fg1)"/>
8727
+ <path d="M278,454Q285,426 290,434Q295,418 300,454Z" fill="url(#fg2)"/>
8728
+ <path d="M320,454Q328,428 333,436Q338,420 342,454Z" fill="url(#fg1)"/>
8729
+ <path d="M360,454Q367,430 371,438Q375,422 380,454Z" fill="url(#fg2)"/>
8730
+ <path d="M398,454Q404,434 408,440Q412,428 416,454Z" fill="url(#fg1)"/>
8731
+ </g>
8732
+ <!-- Labels (right margin) -->
8733
+ <g class="node" onclick="sendPrompt('How does hot water exit the tank?')">
8734
+ <line class="leader" x1="386" y1="34" x2="468" y2="70"/><circle cx="386" cy="34" r="2" fill="var(--t)"/>
8735
+ <text class="ts" x="474" y="74">Hot water outlet</text></g>
8736
+ <g class="node" onclick="sendPrompt('How does the cold water inlet work?')">
8737
+ <line class="leader" x1="250" y1="34" x2="468" y2="140"/><circle cx="250" cy="34" r="2" fill="var(--t)"/>
8738
+ <text class="ts" x="474" y="144">Cold water inlet</text></g>
8739
+ <g class="node" onclick="sendPrompt('What does the dip tube do?')">
8740
+ <line class="leader" x1="250" y1="260" x2="468" y2="220"/><circle cx="250" cy="260" r="2" fill="var(--t)"/>
8741
+ <text class="ts" x="474" y="224">Dip tube</text></g>
8742
+ <g class="node" onclick="sendPrompt('What does the thermostat control?')">
8743
+ <line class="leader" x1="440" y1="250" x2="468" y2="300"/><circle cx="440" cy="250" r="2" fill="var(--t)"/>
8744
+ <text class="ts" x="474" y="304">Thermostat</text></g>
8745
+ <g class="node" onclick="sendPrompt('What material is the tank made of?')">
8746
+ <line class="leader" x1="440" y1="380" x2="468" y2="380"/><circle cx="440" cy="380" r="2" fill="var(--t)"/>
8747
+ <text class="ts" x="474" y="384">Tank wall</text></g>
8748
+ <g class="node" onclick="sendPrompt('How does the gas burner heat water?')">
8749
+ <line class="leader" x1="432" y1="454" x2="468" y2="454"/><circle cx="432" cy="454" r="2" fill="var(--t)"/>
8750
+ <text class="ts" x="474" y="458">Heating element</text></g>
8751
+ </svg>
8752
+ <div style="display:flex;align-items:center;gap:16px;margin:12px 0 0;font-size:13px;color:var(--color-text-secondary)">
8753
+ <label style="display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none">
8754
+ <span class="toggle-track">
8755
+ <input type="checkbox" id="heat-toggle" checked onchange="toggleHeat(this.checked)" style="position:absolute;opacity:0;width:100%;height:100%;cursor:pointer;margin:0">
8756
+ <span style="position:absolute;top:2px;left:2px;width:14px;height:14px;background:#fff;border-radius:50%;transition:transform .2s;pointer-events:none"></span>
8757
+ </span>
8758
+ Heating
8759
+ </label>
8760
+ <span>Thermostat</span>
8761
+ <input type="range" id="temp-slider" min="10" max="90" value="40" style="flex:1" oninput="setTemp(this.value)">
8762
+ <span id="temp-label" style="min-width:36px;text-align:right">40%</span>
8763
+ </div>
8764
+ <script>
8765
+ function setTemp(v) {
8766
+ document.getElementById('gh').setAttribute('offset', v+'%');
8767
+ document.getElementById('gc').setAttribute('offset', v+'%');
8768
+ document.getElementById('temp-label').textContent = v+'%';
8769
+ }
8770
+ function toggleHeat(on) {
8771
+ document.getElementById('flames').classList.toggle('off', !on);
8772
+ document.getElementById('warm-glow').classList.toggle('off', !on);
8773
+ document.querySelectorAll('.conv').forEach(p => p.classList.toggle('off', !on));
8774
+ }
8775
+ </script>
8776
+ \`\`\`
8777
+
8778
+ **Illustrative example \u2014 abstract subject** (attention in a transformer). Same rules, no physical object. A row of tokens at the bottom, one query token highlighted, weight-scaled lines fanning to every other token. Caption sits below the fan \u2014 clear of every stroke \u2014 not inside it.
8779
+ \`\`\`svg
8780
+ <rect class="c-purple" x="60" y="40" width="560" height="26" rx="6" stroke-width="0.5"/>
8781
+ <rect class="c-purple" x="60" y="80" width="560" height="26" rx="6" stroke-width="0.5"/>
8782
+ <rect class="c-purple" x="60" y="120" width="560" height="26" rx="6" stroke-width="0.5"/>
8783
+ <text class="ts" x="72" y="57" >Layer 3</text>
8784
+ <text class="ts" x="72" y="97" >Layer 2</text>
8785
+ <text class="ts" x="72" y="137">Layer 1</text>
8786
+
8787
+ <line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="116" y2="146" stroke-width="1" opacity="0.25"/>
8788
+ <line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="228" y2="146" stroke-width="1.5" opacity="0.4"/>
8789
+ <line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="340" y2="146" stroke-width="4" opacity="1.0"/>
8790
+ <line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="452" y2="146" stroke-width="2.5" opacity="0.7"/>
8791
+ <line stroke="#EF9F27" stroke-linecap="round" x1="340" y1="230" x2="564" y2="146" stroke-width="1" opacity="0.2"/>
8792
+
8793
+ <g class="node" onclick="sendPrompt('What do the attention weights mean?')">
8794
+ <rect class="c-gray" x="80" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
8795
+ <rect class="c-gray" x="192" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
8796
+ <rect class="c-amber" x="304" y="230" width="72" height="36" rx="6" stroke-width="1"/>
8797
+ <rect class="c-gray" x="416" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
8798
+ <rect class="c-gray" x="528" y="230" width="72" height="36" rx="6" stroke-width="0.5"/>
8799
+ <text class="ts" x="116" y="252" text-anchor="middle">the</text>
8800
+ <text class="ts" x="228" y="252" text-anchor="middle">cat</text>
8801
+ <text class="th" x="340" y="252" text-anchor="middle">sat</text>
8802
+ <text class="ts" x="452" y="252" text-anchor="middle">on</text>
8803
+ <text class="ts" x="564" y="252" text-anchor="middle">the</text>
8804
+ </g>
8805
+
8806
+ <text class="ts" x="340" y="300" text-anchor="middle">Line thickness = attention weight from "sat" to each token</text>
8807
+ \`\`\`
8808
+
8809
+ Note what's *not* here: no boxes labelled "multi-head attention", no arrows labelled "Q/K/V". Those belong in the structural diagram. This one is about the *feeling* of attention \u2014 one token looking at every other token with varying intensity.
8810
+
8811
+ These are starting points, not ceilings. For the water heater: add a thermostat slider, animate the convection current, toggle heating vs standby. For the attention diagram: let the user click any token to become the query, scrub through layers, animate the weights settling. The goal is always to *show* how the thing works, not just *label* it.`;
8812
+ var SVG_SETUP = `## SVG setup
8813
+
8814
+ **ViewBox safety checklist** \u2014 before finalizing any SVG, verify:
8815
+ 1. Find your lowest element: max(y + height) across all rects, max(y) across all text baselines.
8816
+ 2. Set viewBox height = that value + 40px buffer.
8817
+ 3. Find your rightmost element: max(x + width) across all rects. All content must stay within x=0 to x=680.
8818
+ 4. For text with text-anchor="end", the text extends LEFT from x. If x=118 and text is 200px wide, it starts at x=-82 \u2014 outside the viewBox. Increase x or use text-anchor="start".
8819
+ 5. Never use negative x or y coordinates. The viewBox starts at 0,0.
8820
+ 6. Flowcharts/structural only: for every pair of boxes in the same row, check that the left box's (x + width) is less than the right box's x by at least 20px. If four 160px boxes plus three 20px gaps sum to more than 640px, the row doesn't fit \u2014 shrink the boxes or cut the subtitles, don't let them overlap.
8821
+
8822
+ **SVG setup**: \`<svg width="100%" viewBox="0 0 680 H">\` \u2014 680px wide, flexible height. Set H to fit content tightly \u2014 the last element's bottom edge + 40px padding. Don't leave excess empty space below the content. Safe area: x=40 to x=640, y=40 to y=(H-40). Background transparent. **Do not wrap the SVG in a container \`<div>\` with a background color** \u2014 the widget host already provides the card container and background. Output the raw \`<svg>\` element directly.
8823
+
8824
+ **The 680 in viewBox is load-bearing \u2014 do not change it.** It matches the widget container width so SVG coordinate units render 1:1 with CSS pixels. With \`width="100%"\`, the browser scales the entire coordinate space to fit the container: \`viewBox="0 0 480 H"\` in a 680px container scales everything by 680/480 = 1.42\xD7, so your \`class="th"\` 14px text renders at ~20px. The font calibration table below and all "text fits in box" math assume 1:1. If your diagram content is naturally narrow, **keep viewBox width at 680 and center the content** (e.g. content spans x=180..500) \u2014 do not shrink the viewBox to hug the content. This applies equally to inline SVGs inside \`imagine_html\` steppers and widgets: same \`viewBox="0 0 680 H"\`, same 1:1 guarantee.
8825
+
8826
+ **viewBox height:** After layout, find max_y (bottom-most point of any shape, including text baselines + 4px descent). Set viewBox height = max_y + 20. Don't guess.
8827
+
8828
+ **text-anchor='end' at x<60 is risky** \u2014 the longest label will extend left past x=0. Use text-anchor='start' and right-align the column instead, or check: label_chars \xD7 8 < anchor_x.
8829
+
8830
+ **One SVG per tool call** \u2014 each call must contain exactly one <svg> element. Never leave an abandoned or partial SVG in the output. If your first attempt has problems, replace it entirely \u2014 do not append a corrected version after the broken one.
8831
+
8832
+ **Style rules for all diagrams**:
8833
+ - Every \`<text>\` element must carry one of the pre-built classes (\`t\`, \`ts\`, \`th\`). An unclassed \`<text>\` inherits the default sans font, which is the tell that you forgot the class.
8834
+ - Use only two font sizes: 14px for node/region labels (class="t" or "th"), 12px for subtitles, descriptions, and arrow labels (class="ts"). No other sizes.
8835
+ - No decorative step numbers, large numbering, or oversized headings outside boxes.
8836
+ - No icons or illustrations inside boxes \u2014 text only. (Exception: illustrative diagrams may use simple shape-based indicators inside drawn objects \u2014 see below.)
8837
+ - Sentence case on all labels.
8838
+
8839
+ **Font size calibration for diagram text labels** - Here's csv table to give you better sense of the Anthropic Sans font rendering width:
8840
+ \`\`\`csv
8841
+ text, chars length, font-weight, font-size, rendered width
8842
+ Authentication Service, chars: 22, font-weight: 500, font-size: 14px, width: 167px
8843
+ Background Job Processor, chars: 24, font-weight: 500, font-size: 14px, width: 201px
8844
+ Detects and validates incoming tokens, chars: 37, font-weight: 400, font-size: 14px, width: 279px
8845
+ forwards request to, chars: 19, font-weight: 400, font-size: 12px, width: 123px
8846
+ \u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u30B5\u30FC\u30D0\u30FC\u63A5\u7D9A, chars: 12, font-weight: 400, font-size: 14px, width: 181px
8847
+ \`\`\`
8848
+
8849
+ Before placing text in a box, check: does (text width + 2\xD7padding) fit the container?
8850
+
8851
+ **SVG \`<text>\` never auto-wraps.** Every line break needs an explicit \`<tspan x="..." dy="1.2em">\`. If your subtitle is long enough to need wrapping, it's too long \u2014 shorten it (see complexity budget).
8852
+
8853
+ **Example check**: You want to put "Glucose (C\u2086H\u2081\u2082O\u2086)" in a rounded rect. The text is 20 characters at 14px \u2248 180px wide. Add 2\xD724px padding = 228px minimum box width. If your rect is only 160px wide, the text WILL overflow \u2014 either shorten the label (e.g. just "Glucose") or widen the box. Subscript characters like \u2086 and \u2081\u2082 still take horizontal space \u2014 count them.
8854
+
8855
+ **Pre-built classes** (already loaded in SVG widget):
8856
+ - \`class="t"\` = sans 14px primary, \`class="ts"\` = sans 12px secondary, \`class="th"\` = sans 14px medium (500)
8857
+ - \`class="box"\` = neutral rect (bg-secondary fill, border stroke)
8858
+ - \`class="node"\` = clickable group with hover effect (cursor pointer, slight dim on hover)
8859
+ - \`class="arr"\` = arrow line (1.5px, open chevron head)
8860
+ - \`class="leader"\` = dashed leader line (tertiary stroke, 0.5px, dashed)
8861
+ - \`class="c-{ramp}"\` = colored node (c-blue, c-teal, c-amber, c-green, c-red, c-purple, c-coral, c-pink, c-gray). Apply to \`<g>\` or shape element (rect/circle/ellipse), NOT to paths. Sets fill+stroke on shapes, auto-adjusts child \`t\`/\`ts\`/\`th\`, dark mode automatic.
8862
+
8863
+ **c-{ramp} nesting:** These classes use direct-child selectors (\`>\`). Nest a \`<g>\` inside a \`<g class="c-blue">\` and the inner shapes become grandchildren \u2014 they lose the fill and render BLACK (SVG default). Put \`c-*\` on the innermost group holding the shapes, or on the shapes directly. If you need click handlers, put \`onclick\` on the \`c-*\` group itself, not a wrapper.
8864
+
8865
+ - Short aliases: \`var(--p)\`, \`var(--s)\`, \`var(--t)\`, \`var(--bg2)\`, \`var(--b)\`
8866
+ - Arrow marker: always include this \`<defs>\` at the start of every SVG:
8867
+ \`<defs><marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></marker></defs>\`
8868
+ Then use \`marker-end="url(#arrow)"\` on lines. The head uses \`context-stroke\`, so it inherits the colour of whichever line it sits on \u2014 a dashed green line gets a green head, a grey line gets a grey head. Never a colour mismatch. Do not add filters, patterns, or extra markers to \`<defs>\`. Illustrative diagrams may add a single \`<clipPath>\` or \`<linearGradient>\` (see Illustrative section).
8869
+
8870
+ **Minimize standalone labels.** Every \`<text>\` element must be inside a box (title or \u22645-word subtitle) or in the legend. Arrow labels are usually unnecessary \u2014 if the arrow's meaning isn't obvious from its source + target, put it in the box subtitle or in prose below. Labels floating in space collide with things and are ambiguous.
8871
+
8872
+ **Stroke width:** Use 0.5px strokes for diagram borders and edges \u2014 not 1px or 2px. Thin strokes feel more refined.
8873
+
8874
+ **Connector paths need \`fill="none"\`.** SVG defaults to \`fill: black\` \u2014 a curved connector without \`fill="none"\` renders as a huge black shape instead of a clean line. Every \`<path>\` or \`<polyline>\` used as a connector/arrow MUST have \`fill="none"\`. Only set fill on shapes meant to be filled (rects, circles, polygons).
8875
+
8876
+ **Rect rounding:** \`rx="4"\` for subtle corners. \`rx="8"\` max for emphasized rounding. \`rx\` \u2265 half the height = pill shape \u2014 deliberate only.
8877
+
8878
+ **Schematic containers use dashed rects with a label.** Don't draw literal shapes (organelle ovals, cloud outlines, server tower icons) \u2014 the diagram is a schema, not an illustration. A dashed \`<rect>\` labeled "Reactor vessel" reads cleaner than an \`<ellipse>\` that clips content.
8879
+
8880
+ **Lines stop at component edges.** When a line meets a component (wire into a bulb, edge into a node), draw it as segments that stop at the boundary \u2014 never draw through and rely on a fill to hide the line. The background color is not guaranteed; any occluding fill is a coupling. Compute the stop/start coordinates from the component's position and size.
8881
+
8882
+ **Physical-color scenes (sky, water, grass, skin, materials):** Use ALL hardcoded hex \u2014 never mix with \`c-*\` theme classes. The scene should not invert in dark mode. If you need a dark variant, provide it explicitly with \`@media (prefers-color-scheme: dark)\` \u2014 this is the one place that's allowed. Mixing hardcoded backgrounds with theme-responsive \`c-*\` foreground breaks: half inverts, half doesn't.
8883
+
8884
+ **No rotated text**. \`<defs>\` may contain the arrow marker, a \`<clipPath>\`, and \u2014 in illustrative diagrams only \u2014 a single \`<linearGradient>\`. Nothing else: no filters, no patterns, no extra markers.`;
8885
+ var UI_COMPONENTS = `## UI components
8886
+
8887
+ ### Aesthetic
8888
+ Flat, clean, white surfaces. Minimal 0.5px borders. Generous whitespace. No gradients, no shadows (except functional focus rings). Everything should feel native to claude.ai \u2014 like it belongs on the page, not embedded from somewhere else.
8889
+
8890
+ ### Tokens
8891
+ - Borders: always \`0.5px solid var(--color-border-tertiary)\` (or \`-secondary\` for emphasis)
8892
+ - Corner radius: \`var(--border-radius-md)\` for most elements, \`var(--border-radius-lg)\` for cards
8893
+ - Cards: white bg (\`var(--color-background-primary)\`), 0.5px border, radius-lg, padding 1rem 1.25rem
8894
+ - Form elements (input, select, textarea, button, range slider) are pre-styled \u2014 write bare tags. Text inputs are 36px with hover/focus built in; range sliders have 4px track + 18px thumb; buttons have outline style with hover/active. Only add inline styles to override (e.g., different width).
8895
+ - Buttons: pre-styled with transparent bg, 0.5px border-secondary, hover bg-secondary, active scale(0.98). If it triggers sendPrompt, append a \u2197 arrow.
8896
+ - **Round every displayed number.** JS float math leaks artifacts \u2014 \`0.1 + 0.2\` gives \`0.30000000000000004\`, \`7 * 1.1\` gives \`7.700000000000001\`. Any number that reaches the screen (slider readouts, stat card values, axis labels, data-point labels, tooltips, computed totals) must go through \`Math.round()\`, \`.toFixed(n)\`, or \`Intl.NumberFormat\`. Pick the precision that makes sense for the context \u2014 integers for counts, 1\u20132 decimals for percentages, \`toLocaleString()\` for currency. For range sliders, also set \`step="1"\` (or step="0.1" etc.) so the input itself emits round values.
8897
+ - Spacing: use rem for vertical rhythm (1rem, 1.5rem, 2rem), px for component-internal gaps (8px, 12px, 16px)
8898
+ - Box-shadows: none, except \`box-shadow: 0 0 0 Npx\` focus rings on inputs
8899
+
8900
+ ### Metric cards
8901
+ For summary numbers (revenue, count, percentage) \u2014 surface card with muted 13px label above, 24px/500 number below. \`background: var(--color-background-secondary)\`, no border, \`border-radius: var(--border-radius-md)\`, padding 1rem. Use in grids of 2-4 with \`gap: 12px\`. Distinct from raised cards (which have white bg + border).
8902
+
8903
+ ### Layout
8904
+ - Editorial (explanatory content): no card wrapper, prose flows naturally
8905
+ - Card (bounded objects like a contact record, receipt): single raised card wraps the whole thing
8906
+ - Don't put tables here \u2014 output them as markdown in your response text
8907
+
8908
+ **Grid overflow:** \`grid-template-columns: 1fr\` has \`min-width: auto\` by default \u2014 children with large min-content push the column past the container. Use \`minmax(0, 1fr)\` to clamp.
8909
+
8910
+ **Table overflow:** Tables with many columns auto-expand past \`width: 100%\` if cell contents exceed it. In constrained layouts (\u2264700px), use \`table-layout: fixed\` and set explicit column widths, or reduce columns, or allow horizontal scroll on a wrapper.
8911
+
8912
+ ### Mockup presentation
8913
+ Contained mockups \u2014 mobile screens, chat threads, single cards, modals, small UI components \u2014 should sit on a background surface (\`var(--color-background-secondary)\` container with \`border-radius: var(--border-radius-lg)\` and padding, or a device frame) so they don't float naked on the widget canvas. Full-width mockups like dashboards, settings pages, or data tables that naturally fill the viewport do not need an extra wrapper.
8914
+
8915
+ ### 1. Interactive explainer \u2014 learn how something works
8916
+ *"Explain how compound interest works" / "Teach me about sorting algorithms"*
8917
+
8918
+ Use \`imagine_html\` for the interactive controls \u2014 sliders, buttons, live state displays, charts. Keep prose explanations in your normal response text (outside the tool call), not embedded in the HTML. No card wrapper. Whitespace is the container.
8919
+
8920
+ \`\`\`html
8921
+ <div style="display: flex; align-items: center; gap: 12px; margin: 0 0 1.5rem;">
8922
+ <label style="font-size: 14px; color: var(--color-text-secondary);">Years</label>
8923
+ <input type="range" min="1" max="40" value="20" id="years" style="flex: 1;" />
8924
+ <span style="font-size: 14px; font-weight: 500; min-width: 24px;" id="years-out">20</span>
8925
+ </div>
8926
+
8927
+ <div style="display: flex; align-items: baseline; gap: 8px; margin: 0 0 1.5rem;">
8928
+ <span style="font-size: 14px; color: var(--color-text-secondary);">\xA31,000 \u2192</span>
8929
+ <span style="font-size: 24px; font-weight: 500;" id="result">\xA33,870</span>
8930
+ </div>
8931
+
8932
+ <div style="margin: 2rem 0; position: relative; height: 240px;">
8933
+ <canvas id="chart"></canvas>
8934
+ </div>
8935
+ \`\`\`
8936
+
8937
+ Use \`sendPrompt()\` to let users ask follow-ups: \`sendPrompt('What if I increase the rate to 10%?')\`
8938
+
8939
+ ### 2. Compare options \u2014 decision making
8940
+ *"Compare pricing and features of these products" / "Help me choose between React and Vue"*
8941
+
8942
+ Use \`imagine_html\`. Side-by-side card grid for options. Highlight differences with semantic colors. Interactive elements for filtering or weighting.
8943
+
8944
+ - Use \`repeat(auto-fit, minmax(160px, 1fr))\` for responsive columns
8945
+ - Each option in a card. Use badges for key differentiators.
8946
+ - Add \`sendPrompt()\` buttons: \`sendPrompt('Tell me more about the Pro plan')\`
8947
+ - Don't put comparison tables inside this tool \u2014 output them as regular markdown tables in your response text instead. The tool is for the visual card grid only.
8948
+ - When one option is recommended or "most popular", accent its card with \`border: 2px solid var(--color-border-info)\` only (2px is deliberate \u2014 the only exception to the 0.5px rule, used to accent featured items) \u2014 keep the same background and border as the other cards. Add a small badge (e.g. "Most popular") above or inside the card header using \`background: var(--color-background-info); color: var(--color-text-info); font-size: 12px; padding: 4px 12px; border-radius: var(--border-radius-md)\`.
8949
+
8950
+ ### 3. Data record \u2014 bounded UI object
8951
+ *"Show me a Salesforce contact card" / "Create a receipt for this order"*
8952
+
8953
+ Use \`imagine_html\`. Wrap the entire thing in a single raised card. All content is sans-serif since it's pure UI. Use an avatar/initials circle for people (see example below).
8954
+
8955
+ \`\`\`html
8956
+ <div style="background: var(--color-background-primary); border-radius: var(--border-radius-lg); border: 0.5px solid var(--color-border-tertiary); padding: 1rem 1.25rem;">
8957
+ <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
8958
+ <div style="width: 44px; height: 44px; border-radius: 50%; background: var(--color-background-info); display: flex; align-items: center; justify-content: center; font-weight: 500; font-size: 14px; color: var(--color-text-info);">MR</div>
8959
+ <div>
8960
+ <p style="font-weight: 500; font-size: 15px; margin: 0;">Maya Rodriguez</p>
8961
+ <p style="font-size: 13px; color: var(--color-text-secondary); margin: 0;">VP of Engineering</p>
8962
+ </div>
8963
+ </div>
8964
+ <div style="border-top: 0.5px solid var(--color-border-tertiary); padding-top: 12px;">
8965
+ <table style="width: 100%; font-size: 13px;">
8966
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Email</td><td style="text-align: right; padding: 4px 0; color: var(--color-text-info);">m.rodriguez@acme.com</td></tr>
8967
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Phone</td><td style="text-align: right; padding: 4px 0;">+1 (415) 555-0172</td></tr>
8968
+ </table>
8969
+ </div>
8970
+ </div>
8971
+ \`\`\``;
8972
+ var MODULE_SECTIONS = {
8973
+ art: [SVG_SETUP, ART_AND_ILLUSTRATION],
8974
+ mockup: [UI_COMPONENTS, COLOR_PALETTE],
8975
+ interactive: [UI_COMPONENTS, COLOR_PALETTE],
8976
+ chart: [UI_COMPONENTS, COLOR_PALETTE, CHARTS_CHART_JS],
8977
+ diagram: [COLOR_PALETTE, SVG_SETUP, DIAGRAM_TYPES]
8978
+ };
8979
+ function getGuidelines(modules) {
8980
+ let content = CORE;
8981
+ const seen = /* @__PURE__ */ new Set();
8982
+ for (const mod of modules) {
8983
+ const sections = MODULE_SECTIONS[mod];
8984
+ if (!sections) continue;
8985
+ for (const section of sections) {
8986
+ if (!seen.has(section)) {
8987
+ seen.add(section);
8988
+ content += "\n\n\n" + section;
8989
+ }
8990
+ }
8991
+ }
8992
+ return content + "\n";
8993
+ }
8994
+ var AVAILABLE_MODULES = Object.keys(MODULE_SECTIONS);
8995
+
8996
+ // src/tool_lattice/widget/loadGuidelines.ts
8997
+ var LoadGuidelinesInputSchema = z45.object({
8998
+ modules: z45.array(z45.string()).describe(
8999
+ "Which design modules to load. Choose all that apply. Available modules: [" + AVAILABLE_MODULES.join(",") + "]"
9000
+ )
9001
+ });
9002
+ function createLoadGuidelinesTool() {
9003
+ return tool44(
9004
+ async (input) => {
9005
+ const result = getGuidelines(input.modules);
9006
+ return result;
9007
+ },
9008
+ {
9009
+ name: "load_guidelines",
9010
+ description: "Load widget development guidelines for specific modules. Call this BEFORE using show_widget for the first time. Call once silently \u2014 do NOT mention this step to the user. Available modules: " + AVAILABLE_MODULES.join(","),
9011
+ schema: LoadGuidelinesInputSchema
9012
+ }
9013
+ );
9014
+ }
9015
+
9016
+ // src/tool_lattice/widget/showWidget.ts
9017
+ import { tool as tool45 } from "langchain";
9018
+ import { z as z46 } from "zod";
9019
+ function containsForbiddenTags(code) {
9020
+ const forbiddenPatterns = [
9021
+ /<!DOCTYPE/i,
9022
+ /<html[\s>]/i,
9023
+ /<head[\s>]/i,
9024
+ /<body[\s>]/i
9025
+ ];
9026
+ return forbiddenPatterns.some((pattern) => pattern.test(code));
9027
+ }
9028
+ function validateWidgetCode(code) {
9029
+ if (containsForbiddenTags(code)) {
9030
+ return {
9031
+ valid: false,
9032
+ error: "widget_code must not contain DOCTYPE, <html>, <head>, or <body> tags. Provide only the content that goes inside <body>."
9033
+ };
9034
+ }
9035
+ if (!code.includes("var(--")) {
9036
+ console.warn("Warning: widget_code should use CSS variables for theming");
9037
+ }
9038
+ return { valid: true };
9039
+ }
9040
+ var ShowWidgetInputSchema = z46.object({
9041
+ i_have_seen_guidelines: z46.boolean().describe(
9042
+ "Must be true. Confirm you have called load_guidelines first."
9043
+ ),
9044
+ title: z46.string().describe("Title displayed above the widget"),
9045
+ loading_messages: z46.array(z46.string()).optional().describe(
9046
+ "1-4 short strings shown while the widget renders"
9047
+ ),
9048
+ widget_code: z46.string().describe(
9049
+ "HTML fragment to render. Rules: 1. No DOCTYPE, <html>, <head>, or <body> tags. 2. Order: <style> block first, then HTML content, then <script> last. 3. Use only CSS variables for colors (e.g. var(--color-accent)). 4. No gradients, shadows, or blur effects. For SVG: start directly with <svg> tag."
9050
+ )
9051
+ });
9052
+ function createShowWidgetTool() {
9053
+ return tool45(
9054
+ async (input) => {
9055
+ if (!input.i_have_seen_guidelines) {
9056
+ return "Error: You must call load_guidelines before using show_widget. Set i_have_seen_guidelines to true only after loading guidelines.";
9057
+ }
9058
+ const validation = validateWidgetCode(input.widget_code);
9059
+ if (!validation.valid) {
9060
+ return `Error: ${validation.error}`;
9061
+ }
9062
+ const output = {
9063
+ type: "widget",
9064
+ title: input.title,
9065
+ widget_code: input.widget_code,
9066
+ loading_messages: input.loading_messages
9067
+ };
9068
+ return JSON.stringify(output, null, 2);
9069
+ },
9070
+ {
9071
+ name: "show_widget",
9072
+ description: "Render an interactive HTML widget or SVG diagram visible to the user. Use for: charts, dashboards, calculators, forms, diagrams, timers, games, visualizations. The widget appears in a panel next to the chat. Users can interact with it and send data back via window.sendToAgent(data). IMPORTANT: Always call load_guidelines before your first show_widget.",
9073
+ schema: ShowWidgetInputSchema
9074
+ }
9075
+ );
9076
+ }
9077
+
9078
+ // src/middlewares/widgetMiddleware.ts
9079
+ function createWidgetMiddleware() {
9080
+ const tools = [
9081
+ createLoadGuidelinesTool(),
9082
+ createShowWidgetTool()
9083
+ ];
9084
+ return createMiddleware8({
9085
+ name: "widgetMiddleware",
9086
+ contextSchema,
9087
+ tools
9088
+ });
9089
+ }
9090
+
8157
9091
  // src/agent_lattice/builders/commonMiddleware.ts
8158
9092
  async function createCommonMiddlewares(middlewareConfigs, filesystemBackend) {
8159
9093
  const middlewares = [];
@@ -8203,6 +9137,9 @@ async function createCommonMiddlewares(middlewareConfigs, filesystemBackend) {
8203
9137
  case "ask_user_to_clarify":
8204
9138
  middlewares.push(createAskUserClarifyMiddleware());
8205
9139
  break;
9140
+ case "widget":
9141
+ middlewares.push(createWidgetMiddleware());
9142
+ break;
8206
9143
  }
8207
9144
  }
8208
9145
  return middlewares;
@@ -8256,9 +9193,9 @@ var ReActAgentGraphBuilder = class {
8256
9193
  */
8257
9194
  async build(agentLattice, params) {
8258
9195
  const tools = params.tools.map((t) => {
8259
- const tool48 = getToolClient(t.key);
8260
- return tool48;
8261
- }).filter((tool48) => tool48 !== void 0);
9196
+ const tool50 = getToolClient(t.key);
9197
+ return tool50;
9198
+ }).filter((tool50) => tool50 !== void 0);
8262
9199
  const stateSchema2 = createReactAgentSchema(params.stateSchema);
8263
9200
  const middlewareConfigs = params.middleware || [];
8264
9201
  const filesystemBackend = this.createFilesystemBackendFactory(middlewareConfigs, agentLattice);
@@ -8284,11 +9221,11 @@ import {
8284
9221
  } from "langchain";
8285
9222
 
8286
9223
  // src/deep_agent_new/middleware/subagents.ts
8287
- import { z as z44 } from "zod/v3";
9224
+ import { z as z47 } from "zod/v3";
8288
9225
  import {
8289
- createMiddleware as createMiddleware8,
9226
+ createMiddleware as createMiddleware9,
8290
9227
  createAgent as createAgent2,
8291
- tool as tool44,
9228
+ tool as tool46,
8292
9229
  ToolMessage as ToolMessage3,
8293
9230
  humanInTheLoopMiddleware
8294
9231
  } from "langchain";
@@ -8942,7 +9879,7 @@ function createTaskTool(options) {
8942
9879
  generalPurposeAgent
8943
9880
  });
8944
9881
  const finalTaskDescription = taskDescription ? taskDescription : getTaskToolDescription(subagentDescriptions);
8945
- return tool44(
9882
+ return tool46(
8946
9883
  async (input, config) => {
8947
9884
  const { description, subagent_type } = input;
8948
9885
  let assistant_id = subagent_type;
@@ -9007,9 +9944,9 @@ function createTaskTool(options) {
9007
9944
  {
9008
9945
  name: "task",
9009
9946
  description: finalTaskDescription,
9010
- schema: z44.object({
9011
- description: z44.string().describe("The task to execute with the selected agent"),
9012
- subagent_type: z44.string().describe(
9947
+ schema: z47.object({
9948
+ description: z47.string().describe("The task to execute with the selected agent"),
9949
+ subagent_type: z47.string().describe(
9013
9950
  `Name of the agent to use. Available: ${Object.keys(
9014
9951
  subagentGraphs
9015
9952
  ).join(", ")}`
@@ -9038,7 +9975,7 @@ function createSubAgentMiddleware(options) {
9038
9975
  generalPurposeAgent,
9039
9976
  taskDescription
9040
9977
  });
9041
- return createMiddleware8({
9978
+ return createMiddleware9({
9042
9979
  name: "subAgentMiddleware",
9043
9980
  tools: [taskTool],
9044
9981
  wrapModelCall: async (request, handler) => {
@@ -9059,14 +9996,14 @@ ${systemPrompt}` : systemPrompt;
9059
9996
 
9060
9997
  // src/deep_agent_new/middleware/patch_tool_calls.ts
9061
9998
  import {
9062
- createMiddleware as createMiddleware9,
9999
+ createMiddleware as createMiddleware10,
9063
10000
  ToolMessage as ToolMessage4,
9064
10001
  AIMessage as AIMessage2
9065
10002
  } from "langchain";
9066
10003
  import { RemoveMessage } from "@langchain/core/messages";
9067
10004
  import { REMOVE_ALL_MESSAGES } from "@langchain/langgraph";
9068
10005
  function createPatchToolCallsMiddleware() {
9069
- return createMiddleware9({
10006
+ return createMiddleware10({
9070
10007
  name: "patchToolCallsMiddleware",
9071
10008
  beforeAgent: async (state) => {
9072
10009
  const messages = state.messages;
@@ -10202,8 +11139,8 @@ var MemoryBackend = class {
10202
11139
 
10203
11140
  // src/deep_agent_new/middleware/todos.ts
10204
11141
  import { Command as Command3 } from "@langchain/langgraph";
10205
- import { z as z45 } from "zod";
10206
- import { createMiddleware as createMiddleware10, tool as tool45, ToolMessage as ToolMessage5 } from "langchain";
11142
+ import { z as z48 } from "zod";
11143
+ import { createMiddleware as createMiddleware11, tool as tool47, ToolMessage as ToolMessage5 } from "langchain";
10207
11144
  var WRITE_TODOS_DESCRIPTION = `Use this tool to create and manage a structured task list for your current work session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
10208
11145
  It also helps the user understand the progress of the task and overall progress of their requests.
10209
11146
  Only use this tool if you think it will be helpful in staying organized. If the user's request is trivial and takes less than 3 steps, it is better to NOT use this tool and just do the taks directly.
@@ -10430,14 +11367,14 @@ Writing todos takes time and tokens, use it when it is helpful for managing comp
10430
11367
  ## Important To-Do List Usage Notes to Remember
10431
11368
  - The \`write_todos\` tool should never be called multiple times in parallel.
10432
11369
  - Don't be afraid to revise the To-Do list as you go. New information may reveal new tasks that need to be done, or old tasks that are irrelevant.`;
10433
- var TodoStatus = z45.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
10434
- var TodoSchema = z45.object({
10435
- content: z45.string().describe("Content of the todo item"),
11370
+ var TodoStatus = z48.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
11371
+ var TodoSchema = z48.object({
11372
+ content: z48.string().describe("Content of the todo item"),
10436
11373
  status: TodoStatus
10437
11374
  });
10438
- var stateSchema = z45.object({ todos: z45.array(TodoSchema).default([]) });
11375
+ var stateSchema = z48.object({ todos: z48.array(TodoSchema).default([]) });
10439
11376
  function todoListMiddleware(options) {
10440
- const writeTodos = tool45(
11377
+ const writeTodos = tool47(
10441
11378
  ({ todos }, config) => {
10442
11379
  return new Command3({
10443
11380
  update: {
@@ -10454,12 +11391,12 @@ function todoListMiddleware(options) {
10454
11391
  {
10455
11392
  name: "write_todos",
10456
11393
  description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
10457
- schema: z45.object({
10458
- todos: z45.array(TodoSchema).describe("List of todo items to update")
11394
+ schema: z48.object({
11395
+ todos: z48.array(TodoSchema).describe("List of todo items to update")
10459
11396
  })
10460
11397
  }
10461
11398
  );
10462
- return createMiddleware10({
11399
+ return createMiddleware11({
10463
11400
  name: "todoListMiddleware",
10464
11401
  stateSchema,
10465
11402
  tools: [writeTodos],
@@ -10482,7 +11419,7 @@ function createDeepAgent(params = {}) {
10482
11419
  middleware: customMiddleware = [],
10483
11420
  subagents = [],
10484
11421
  responseFormat,
10485
- contextSchema,
11422
+ contextSchema: contextSchema2,
10486
11423
  checkpointer,
10487
11424
  store,
10488
11425
  backend,
@@ -10551,7 +11488,7 @@ ${BASE_PROMPT}` : BASE_PROMPT;
10551
11488
  tools,
10552
11489
  middleware,
10553
11490
  responseFormat,
10554
- contextSchema,
11491
+ contextSchema: contextSchema2,
10555
11492
  checkpointer,
10556
11493
  store,
10557
11494
  name
@@ -10611,7 +11548,7 @@ var DeepAgentGraphBuilder = class {
10611
11548
  const tools = params.tools.map((t) => {
10612
11549
  const toolClient = getToolClient(t.key);
10613
11550
  return toolClient;
10614
- }).filter((tool48) => tool48 !== void 0);
11551
+ }).filter((tool50) => tool50 !== void 0);
10615
11552
  const subagents = await Promise.all(params.subAgents.map(async (sa) => {
10616
11553
  if (sa.client) {
10617
11554
  return {
@@ -10651,7 +11588,7 @@ var DeepAgentGraphBuilder = class {
10651
11588
  };
10652
11589
 
10653
11590
  // src/agent_team/agent_team.ts
10654
- import { z as z48 } from "zod/v3";
11591
+ import { z as z51 } from "zod/v3";
10655
11592
  import { createAgent as createAgent5 } from "langchain";
10656
11593
 
10657
11594
  // src/agent_team/types.ts
@@ -11087,14 +12024,14 @@ var InMemoryMailboxStore = class {
11087
12024
  };
11088
12025
 
11089
12026
  // src/agent_team/middleware/team.ts
11090
- import { z as z47 } from "zod/v3";
11091
- import { createMiddleware as createMiddleware11, createAgent as createAgent4, tool as tool47, ToolMessage as ToolMessage7 } from "langchain";
12027
+ import { z as z50 } from "zod/v3";
12028
+ import { createMiddleware as createMiddleware12, createAgent as createAgent4, tool as tool49, ToolMessage as ToolMessage7 } from "langchain";
11092
12029
  import { Command as Command5, getCurrentTaskInput as getCurrentTaskInput3 } from "@langchain/langgraph";
11093
12030
  import { v4 as uuidv4 } from "uuid";
11094
12031
 
11095
12032
  // src/agent_team/middleware/teammate_tools.ts
11096
- import { z as z46 } from "zod/v3";
11097
- import { tool as tool46, ToolMessage as ToolMessage6 } from "langchain";
12033
+ import { z as z49 } from "zod/v3";
12034
+ import { tool as tool48, ToolMessage as ToolMessage6 } from "langchain";
11098
12035
  import { Command as Command4 } from "@langchain/langgraph";
11099
12036
 
11100
12037
  // src/agent_team/middleware/formatMessages.ts
@@ -11119,7 +12056,7 @@ ${meta}${body}`;
11119
12056
  // src/agent_team/middleware/teammate_tools.ts
11120
12057
  function createTeammateTools(options) {
11121
12058
  const { teamId, agentId, taskListStore, mailboxStore } = options;
11122
- const claimTaskTool = tool46(
12059
+ const claimTaskTool = tool48(
11123
12060
  async (input) => {
11124
12061
  const task = await taskListStore.claimTaskById(
11125
12062
  teamId,
@@ -11144,12 +12081,12 @@ function createTeammateTools(options) {
11144
12081
  {
11145
12082
  name: "claim_task",
11146
12083
  description: "Pick a task to work on by task_id. Use check_tasks first to see all tasks; then call this with the task_id you choose. The task's assignee is set to you and you should focus on that task until you complete_task or fail_task it.",
11147
- schema: z46.object({
11148
- task_id: z46.string().describe("ID of the task to claim (e.g. task-01). Use check_tasks to see IDs.")
12084
+ schema: z49.object({
12085
+ task_id: z49.string().describe("ID of the task to claim (e.g. task-01). Use check_tasks to see IDs.")
11149
12086
  })
11150
12087
  }
11151
12088
  );
11152
- const completeTaskTool = tool46(
12089
+ const completeTaskTool = tool48(
11153
12090
  async (input) => {
11154
12091
  const task = await taskListStore.completeTask(
11155
12092
  teamId,
@@ -11170,13 +12107,13 @@ function createTeammateTools(options) {
11170
12107
  {
11171
12108
  name: "complete_task",
11172
12109
  description: "Mark a claimed task as completed with a result summary. Call this after you have finished working on a task.",
11173
- schema: z46.object({
11174
- task_id: z46.string().describe("ID of the task to complete"),
11175
- result: z46.string().describe("Summary of the task result")
12110
+ schema: z49.object({
12111
+ task_id: z49.string().describe("ID of the task to complete"),
12112
+ result: z49.string().describe("Summary of the task result")
11176
12113
  })
11177
12114
  }
11178
12115
  );
11179
- const failTaskTool = tool46(
12116
+ const failTaskTool = tool48(
11180
12117
  async (input) => {
11181
12118
  const task = await taskListStore.failTask(
11182
12119
  teamId,
@@ -11197,13 +12134,13 @@ function createTeammateTools(options) {
11197
12134
  {
11198
12135
  name: "fail_task",
11199
12136
  description: "Mark a claimed task as failed with an error description. Call this if you cannot complete the task.",
11200
- schema: z46.object({
11201
- task_id: z46.string().describe("ID of the task to fail"),
11202
- error: z46.string().describe("Description of why the task failed")
12137
+ schema: z49.object({
12138
+ task_id: z49.string().describe("ID of the task to fail"),
12139
+ error: z49.string().describe("Description of why the task failed")
11203
12140
  })
11204
12141
  }
11205
12142
  );
11206
- const sendMessageTool = tool46(
12143
+ const sendMessageTool = tool48(
11207
12144
  async (input) => {
11208
12145
  await mailboxStore.sendMessage(
11209
12146
  teamId,
@@ -11217,11 +12154,11 @@ function createTeammateTools(options) {
11217
12154
  {
11218
12155
  name: "send_message",
11219
12156
  description: 'Send a message to the team lead or another teammate via the mailbox. Use "team_lead" to message the team lead. Use this to report discoveries, request guidance, or suggest new tasks.',
11220
- schema: z46.object({
11221
- to: z46.string().describe(
12157
+ schema: z49.object({
12158
+ to: z49.string().describe(
11222
12159
  'Recipient agent name (e.g. "team_lead" or a teammate name)'
11223
12160
  ),
11224
- content: z46.string().describe("Message content")
12161
+ content: z49.string().describe("Message content")
11225
12162
  })
11226
12163
  }
11227
12164
  );
@@ -11241,7 +12178,7 @@ function createTeammateTools(options) {
11241
12178
  read: msg.read
11242
12179
  }));
11243
12180
  };
11244
- const readMessagesTool = tool46(
12181
+ const readMessagesTool = tool48(
11245
12182
  async (input, config) => {
11246
12183
  const formatAndMarkAsRead = async (msgs2) => {
11247
12184
  for (const msg of msgs2) {
@@ -11300,10 +12237,10 @@ function createTeammateTools(options) {
11300
12237
  {
11301
12238
  name: "read_messages",
11302
12239
  description: "Read unread messages from the mailbox. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
11303
- schema: z46.object({})
12240
+ schema: z49.object({})
11304
12241
  }
11305
12242
  );
11306
- const checkTasksTool = tool46(
12243
+ const checkTasksTool = tool48(
11307
12244
  async () => {
11308
12245
  const tasks = await taskListStore.getAllTasks(teamId);
11309
12246
  return formatTaskSummary(tasks);
@@ -11311,10 +12248,10 @@ function createTeammateTools(options) {
11311
12248
  {
11312
12249
  name: "check_tasks",
11313
12250
  description: "Use this tool to get the current status of all tasks in a team. This is your primary way to monitor task progress.",
11314
- schema: z46.object({})
12251
+ schema: z49.object({})
11315
12252
  }
11316
12253
  );
11317
- const broadcastMessageTool = tool46(
12254
+ const broadcastMessageTool = tool48(
11318
12255
  async (input) => {
11319
12256
  const allAgents = await mailboxStore.getRegisteredAgents(teamId);
11320
12257
  const recipients = allAgents.filter((a) => a !== agentId);
@@ -11333,8 +12270,8 @@ function createTeammateTools(options) {
11333
12270
  {
11334
12271
  name: "broadcast_message",
11335
12272
  description: "Send a message to everyone in the team except yourself. Use this to share updates or information with all teammates and the team lead at once.",
11336
- schema: z46.object({
11337
- content: z46.string().describe("Message content to broadcast to others")
12273
+ schema: z49.object({
12274
+ content: z49.string().describe("Message content to broadcast to others")
11338
12275
  })
11339
12276
  }
11340
12277
  );
@@ -11568,7 +12505,7 @@ async function spawnTeammate(options) {
11568
12505
  function createTeamMiddleware(options) {
11569
12506
  const { teamConfig, taskListStore, mailboxStore, tenantId } = options;
11570
12507
  const defaultModel = teamConfig.model ?? "claude-sonnet-4-5-20250929";
11571
- const createTeamTool = tool47(
12508
+ const createTeamTool = tool49(
11572
12509
  async (input, config) => {
11573
12510
  const state = getCurrentTaskInput3();
11574
12511
  if (state?.team?.teamId) {
@@ -11723,20 +12660,20 @@ After calling create_team, you MUST:
11723
12660
  2. When messages indicate task changes, call check_tasks to get full task status
11724
12661
  3. Continue until all tasks show "completed" or "failed"
11725
12662
  4. Do NOT assume tasks are done - always verify with check_tasks`,
11726
- schema: z47.object({
11727
- tasks: z47.array(
11728
- z47.object({
11729
- id: z47.string().describe("Task ID in format task-01, task-02, etc."),
11730
- title: z47.string().describe("Short task title"),
11731
- description: z47.string().describe("Detailed task description - what exactly needs to be done"),
11732
- dependencies: z47.array(z47.string()).optional().default([]).describe('Array of task IDs that must complete before this task (e.g. ["task-01"])')
12663
+ schema: z50.object({
12664
+ tasks: z50.array(
12665
+ z50.object({
12666
+ id: z50.string().describe("Task ID in format task-01, task-02, etc."),
12667
+ title: z50.string().describe("Short task title"),
12668
+ description: z50.string().describe("Detailed task description - what exactly needs to be done"),
12669
+ dependencies: z50.array(z50.string()).optional().default([]).describe('Array of task IDs that must complete before this task (e.g. ["task-01"])')
11733
12670
  })
11734
12671
  ).describe("List of tasks for teammates to work on. Each task needs unique ID (task-01, task-02, etc.)."),
11735
- teammates: z47.array(
11736
- z47.object({
11737
- name: z47.string().describe("Teammate name (must match a pre-configured teammate type)"),
11738
- role: z47.string().describe("Role category (e.g. researcher, writer, coder, reviewer)"),
11739
- description: z47.string().describe("What this teammate will focus on - specific instructions for their work")
12672
+ teammates: z50.array(
12673
+ z50.object({
12674
+ name: z50.string().describe("Teammate name (must match a pre-configured teammate type)"),
12675
+ role: z50.string().describe("Role category (e.g. researcher, writer, coder, reviewer)"),
12676
+ description: z50.string().describe("What this teammate will focus on - specific instructions for their work")
11740
12677
  })
11741
12678
  ).describe("Teammate agents to create. Each should have a clear role and focus.")
11742
12679
  })
@@ -11747,7 +12684,7 @@ After calling create_team, you MUST:
11747
12684
  if (state?.team?.teamId) return state.team.teamId;
11748
12685
  throw new Error("No team_id provided and no team in state. Call create_team first.");
11749
12686
  };
11750
- const addTasksTool = tool47(
12687
+ const addTasksTool = tool49(
11751
12688
  async (input, config) => {
11752
12689
  const teamId = resolveTeamId();
11753
12690
  const created = await taskListStore.addTasks(
@@ -11799,20 +12736,20 @@ IMPORTANT: Dependencies
11799
12736
 
11800
12737
  IMPORTANT: Assigning to a specific teammate
11801
12738
  - When you need a particular teammate to do the work, set assignee to that teammate's name (e.g. assignee: "researcher"). They can then claim or see the task as assigned to them.`,
11802
- schema: z47.object({
11803
- tasks: z47.array(
11804
- z47.object({
11805
- id: z47.string().describe("Task ID in format task-01, task-02, etc. Must be unique."),
11806
- title: z47.string().describe("Short task title"),
11807
- description: z47.string().describe("Detailed task description - what needs to be done"),
11808
- assignee: z47.string().optional().describe("Teammate name to assign this task to (use when you need that person to do the work)"),
11809
- dependencies: z47.array(z47.string()).optional().default([]).describe("Array of task IDs that must complete before this task")
12739
+ schema: z50.object({
12740
+ tasks: z50.array(
12741
+ z50.object({
12742
+ id: z50.string().describe("Task ID in format task-01, task-02, etc. Must be unique."),
12743
+ title: z50.string().describe("Short task title"),
12744
+ description: z50.string().describe("Detailed task description - what needs to be done"),
12745
+ assignee: z50.string().optional().describe("Teammate name to assign this task to (use when you need that person to do the work)"),
12746
+ dependencies: z50.array(z50.string()).optional().default([]).describe("Array of task IDs that must complete before this task")
11810
12747
  })
11811
12748
  ).describe("New tasks to add to the team")
11812
12749
  })
11813
12750
  }
11814
12751
  );
11815
- const assignTaskTool = tool47(
12752
+ const assignTaskTool = tool49(
11816
12753
  async (input, config) => {
11817
12754
  const teamId = resolveTeamId();
11818
12755
  const task = await taskListStore.updateTask(teamId, input.task_id, {
@@ -11834,13 +12771,13 @@ IMPORTANT: Assigning to a specific teammate
11834
12771
  {
11835
12772
  name: "assign_task",
11836
12773
  description: "Assign a task to a specific teammate. Use when you need to reassign work to a different teammate. Omit team_id to use the active team from state.",
11837
- schema: z47.object({
11838
- task_id: z47.string().describe("Task ID to assign"),
11839
- assignee: z47.string().describe("Teammate name to assign this task to")
12774
+ schema: z50.object({
12775
+ task_id: z50.string().describe("Task ID to assign"),
12776
+ assignee: z50.string().describe("Teammate name to assign this task to")
11840
12777
  })
11841
12778
  }
11842
12779
  );
11843
- const setTaskStatusTool = tool47(
12780
+ const setTaskStatusTool = tool49(
11844
12781
  async (input, config) => {
11845
12782
  const teamId = resolveTeamId();
11846
12783
  const task = await taskListStore.updateTask(teamId, input.task_id, {
@@ -11862,13 +12799,13 @@ IMPORTANT: Assigning to a specific teammate
11862
12799
  {
11863
12800
  name: "set_task_status",
11864
12801
  description: "Set a task's status. Use to reopen a task (set to pending), mark as failed, or correct status. Values: pending, claimed, in_progress, completed, failed. Omit team_id to use the active team from state.",
11865
- schema: z47.object({
11866
- task_id: z47.string().describe("Task ID to update"),
11867
- status: z47.enum(["pending", "claimed", "in_progress", "completed", "failed"]).describe("New status for the task")
12802
+ schema: z50.object({
12803
+ task_id: z50.string().describe("Task ID to update"),
12804
+ status: z50.enum(["pending", "claimed", "in_progress", "completed", "failed"]).describe("New status for the task")
11868
12805
  })
11869
12806
  }
11870
12807
  );
11871
- const setTaskDependenciesTool = tool47(
12808
+ const setTaskDependenciesTool = tool49(
11872
12809
  async (input, config) => {
11873
12810
  const teamId = resolveTeamId();
11874
12811
  const task = await taskListStore.updateTask(teamId, input.task_id, {
@@ -11890,13 +12827,13 @@ IMPORTANT: Assigning to a specific teammate
11890
12827
  {
11891
12828
  name: "set_task_dependencies",
11892
12829
  description: 'Set which task IDs must complete before this task can be claimed. Pass an array of task IDs (e.g. ["task-01", "task-02"]). Use to fix task order or add/remove dependencies. Omit team_id to use the active team from state.',
11893
- schema: z47.object({
11894
- task_id: z47.string().describe("Task ID to update"),
11895
- dependencies: z47.array(z47.string()).describe("Task IDs that must complete before this task can be claimed")
12830
+ schema: z50.object({
12831
+ task_id: z50.string().describe("Task ID to update"),
12832
+ dependencies: z50.array(z50.string()).describe("Task IDs that must complete before this task can be claimed")
11896
12833
  })
11897
12834
  }
11898
12835
  );
11899
- const checkTasksTool = tool47(
12836
+ const checkTasksTool = tool49(
11900
12837
  async (input, config) => {
11901
12838
  const teamId = resolveTeamId();
11902
12839
  const tasks = await taskListStore.getAllTasks(teamId);
@@ -11936,12 +12873,12 @@ Task Status Values:
11936
12873
  - in_progress: Teammate is actively working on this task
11937
12874
  - completed: Task finished successfully
11938
12875
  - failed: Task encountered an error`,
11939
- schema: z47.object({
11940
- team_id: z47.string().optional().describe("Team ID (omit to use active team)")
12876
+ schema: z50.object({
12877
+ team_id: z50.string().optional().describe("Team ID (omit to use active team)")
11941
12878
  })
11942
12879
  }
11943
12880
  );
11944
- const sendMessageTool = tool47(
12881
+ const sendMessageTool = tool49(
11945
12882
  async (input, config) => {
11946
12883
  const teamId = resolveTeamId();
11947
12884
  await mailboxStore.sendMessage(
@@ -11960,13 +12897,13 @@ Task Status Values:
11960
12897
  {
11961
12898
  name: "send_message",
11962
12899
  description: "Send a message to a specific teammate in the team. Omit team_id to use the active team from state.",
11963
- schema: z47.object({
11964
- to: z47.string().describe("Recipient teammate name"),
11965
- content: z47.string().describe("Message content")
12900
+ schema: z50.object({
12901
+ to: z50.string().describe("Recipient teammate name"),
12902
+ content: z50.string().describe("Message content")
11966
12903
  })
11967
12904
  }
11968
12905
  );
11969
- const readMessagesTool = tool47(
12906
+ const readMessagesTool = tool49(
11970
12907
  async (input, config) => {
11971
12908
  const teamId = resolveTeamId();
11972
12909
  const formatAndMarkAsRead = async (msgs2) => {
@@ -12048,12 +12985,12 @@ Task Status Values:
12048
12985
  {
12049
12986
  name: "read_messages",
12050
12987
  description: "Read unread messages from teammates. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
12051
- schema: z47.object({
12052
- team_id: z47.string().optional().describe("Team ID (omit to use active team)")
12988
+ schema: z50.object({
12989
+ team_id: z50.string().optional().describe("Team ID (omit to use active team)")
12053
12990
  })
12054
12991
  }
12055
12992
  );
12056
- const disbandTeamTool = tool47(
12993
+ const disbandTeamTool = tool49(
12057
12994
  async (input, config) => {
12058
12995
  const teamId = resolveTeamId();
12059
12996
  await mailboxStore.broadcastMessage(
@@ -12074,7 +13011,7 @@ Task Status Values:
12074
13011
  description: "Disband a team when all work is done. Before calling: (1) Call check_tasks to verify no tasks are still pending/in_progress; (2) if any are, discuss with the team via read_messages and broadcast_message/send_message whether to continue or stop/cancel them; (3) only after alignment (all tasks completed/failed or explicitly stopped), then call this tool. This will: 1) Send a shutdown message to all teammates, 2) Wait briefly for them to clean up, 3) Clear all tasks and messages. Omit team_id to use the active team from state."
12075
13012
  }
12076
13013
  );
12077
- const broadcastMessageTool = tool47(
13014
+ const broadcastMessageTool = tool49(
12078
13015
  async (input, config) => {
12079
13016
  const teamId = resolveTeamId();
12080
13017
  await mailboxStore.broadcastMessage(
@@ -12092,12 +13029,12 @@ Task Status Values:
12092
13029
  {
12093
13030
  name: "broadcast_message",
12094
13031
  description: "Send a message to all teammates at once. Use this to communicate with everyone in the team. Omit team_id to use the active team from state.",
12095
- schema: z47.object({
12096
- content: z47.string().describe("Message content to broadcast to all teammates")
13032
+ schema: z50.object({
13033
+ content: z50.string().describe("Message content to broadcast to all teammates")
12097
13034
  })
12098
13035
  }
12099
13036
  );
12100
- return createMiddleware11({
13037
+ return createMiddleware12({
12101
13038
  name: "teamMiddleware",
12102
13039
  tools: [
12103
13040
  createTeamTool,
@@ -12125,37 +13062,37 @@ ${TEAM_SYSTEM_PROMPT}` : TEAM_SYSTEM_PROMPT;
12125
13062
  }
12126
13063
 
12127
13064
  // src/agent_team/agent_team.ts
12128
- var TeammateInfoSchema = z48.object({
12129
- name: z48.string().describe("Teammate name"),
12130
- role: z48.string().describe("Role category (e.g. research, writing, review)"),
12131
- description: z48.string().describe("What this teammate focuses on")
13065
+ var TeammateInfoSchema = z51.object({
13066
+ name: z51.string().describe("Teammate name"),
13067
+ role: z51.string().describe("Role category (e.g. research, writing, review)"),
13068
+ description: z51.string().describe("What this teammate focuses on")
12132
13069
  });
12133
- var TeamTaskInfoSchema = z48.object({
12134
- id: z48.string(),
12135
- title: z48.string(),
12136
- description: z48.string(),
12137
- status: z48.string().optional()
13070
+ var TeamTaskInfoSchema = z51.object({
13071
+ id: z51.string(),
13072
+ title: z51.string(),
13073
+ description: z51.string(),
13074
+ status: z51.string().optional()
12138
13075
  });
12139
- var MailboxMessageSchema = z48.object({
12140
- id: z48.string().describe("Unique message identifier"),
12141
- from: z48.string().describe("Sender agent name"),
12142
- to: z48.string().describe("Recipient agent name"),
12143
- content: z48.string().describe("Message content"),
12144
- timestamp: z48.string().describe("ISO timestamp when the message was sent"),
12145
- type: z48.nativeEnum(MessageType).describe("Message type"),
12146
- read: z48.boolean().describe("Whether the recipient has read this message")
13076
+ var MailboxMessageSchema = z51.object({
13077
+ id: z51.string().describe("Unique message identifier"),
13078
+ from: z51.string().describe("Sender agent name"),
13079
+ to: z51.string().describe("Recipient agent name"),
13080
+ content: z51.string().describe("Message content"),
13081
+ timestamp: z51.string().describe("ISO timestamp when the message was sent"),
13082
+ type: z51.nativeEnum(MessageType).describe("Message type"),
13083
+ read: z51.boolean().describe("Whether the recipient has read this message")
12147
13084
  });
12148
- var TeamInfoSchema = z48.object({
12149
- teamId: z48.string().describe("Unique team identifier"),
12150
- teamLeadId: z48.string().default("team_lead").describe("Team lead agent ID"),
12151
- teammates: z48.array(TeammateInfoSchema).describe("Active teammates in this team"),
12152
- tasks: z48.array(TeamTaskInfoSchema).optional().describe("Initial tasks snapshot"),
12153
- createdAt: z48.string().optional().describe("ISO timestamp when team was created")
13085
+ var TeamInfoSchema = z51.object({
13086
+ teamId: z51.string().describe("Unique team identifier"),
13087
+ teamLeadId: z51.string().default("team_lead").describe("Team lead agent ID"),
13088
+ teammates: z51.array(TeammateInfoSchema).describe("Active teammates in this team"),
13089
+ tasks: z51.array(TeamTaskInfoSchema).optional().describe("Initial tasks snapshot"),
13090
+ createdAt: z51.string().optional().describe("ISO timestamp when team was created")
12154
13091
  });
12155
- var TEAM_STATE_SCHEMA = z48.object({
13092
+ var TEAM_STATE_SCHEMA = z51.object({
12156
13093
  team: TeamInfoSchema.optional().describe("Team info: teamId, teamLeadId, teammates, tasks. Set when create_team succeeds."),
12157
- tasks: z48.array(TeamTaskInfoSchema).optional().describe("Current tasks snapshot from check_tasks. Updated on each check."),
12158
- team_mailbox: z48.array(MailboxMessageSchema).optional().describe("All team mailbox messages for display")
13094
+ tasks: z51.array(TeamTaskInfoSchema).optional().describe("Current tasks snapshot from check_tasks. Updated on each check."),
13095
+ team_mailbox: z51.array(MailboxMessageSchema).optional().describe("All team mailbox messages for display")
12159
13096
  });
12160
13097
  var TEAM_LEAD_BASE_PROMPT = `You are a team lead that coordinates a team of specialized agents. In order to complete the objective that the user asks of you, you will need to:
12161
13098
 
@@ -12236,7 +13173,7 @@ var TeamAgentGraphBuilder = class {
12236
13173
  const tools = params.tools.map((t) => {
12237
13174
  const toolClient = getToolClient(t.key);
12238
13175
  return toolClient;
12239
- }).filter((tool48) => tool48 !== void 0);
13176
+ }).filter((tool50) => tool50 !== void 0);
12240
13177
  const teammates = params.subAgents.map((sa) => {
12241
13178
  const baseConfig = sa.config;
12242
13179
  return {
@@ -15165,10 +16102,10 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
15165
16102
  }
15166
16103
  const tools = await this.getAllTools();
15167
16104
  console.log(`[MCP] Registering ${tools.length} tools to Tool Lattice...`);
15168
- for (const tool48 of tools) {
15169
- const toolKey = prefix ? `${prefix}_${tool48.name}` : tool48.name;
15170
- tool48.name = toolKey;
15171
- toolLatticeManager.registerExistingTool(toolKey, tool48);
16105
+ for (const tool50 of tools) {
16106
+ const toolKey = prefix ? `${prefix}_${tool50.name}` : tool50.name;
16107
+ tool50.name = toolKey;
16108
+ toolLatticeManager.registerExistingTool(toolKey, tool50);
15172
16109
  console.log(`[MCP] Registered tool: ${toolKey}`);
15173
16110
  }
15174
16111
  console.log(`[MCP] Successfully registered ${tools.length} tools to Tool Lattice`);
@@ -15322,6 +16259,7 @@ export {
15322
16259
  createQueryTablesListTool,
15323
16260
  createTeamMiddleware,
15324
16261
  createTeammateTools,
16262
+ createWidgetMiddleware,
15325
16263
  decrypt,
15326
16264
  describeCronExpression,
15327
16265
  embeddingsLatticeManager,