@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.d.mts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +1072 -133
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1115 -177
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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,
|
|
643
|
+
registerExistingTool(key, tool50) {
|
|
644
644
|
const config = {
|
|
645
|
-
name:
|
|
646
|
-
description:
|
|
647
|
-
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:
|
|
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,
|
|
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
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
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:
|
|
7062
|
-
skill_name:
|
|
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
|
|
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:
|
|
7096
|
-
skill_name:
|
|
7097
|
-
resource_path:
|
|
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?.
|
|
7161
|
+
const tenantId = runtime?.context?.runConfig?.tenantId;
|
|
7162
|
+
if (!tenantId) {
|
|
7163
|
+
throw new Error("tenantId is missing");
|
|
7164
|
+
}
|
|
7122
7165
|
if (readAll) {
|
|
7123
|
-
|
|
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
|
|
7126
|
-
|
|
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
|
-
|
|
7137
|
-
|
|
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
|
|
8048
|
-
var questionSchema =
|
|
8049
|
-
question:
|
|
8050
|
-
options:
|
|
8051
|
-
type:
|
|
8052
|
-
required:
|
|
8053
|
-
allowOther:
|
|
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 =
|
|
8056
|
-
questions:
|
|
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
|
|
8260
|
-
return
|
|
8261
|
-
}).filter((
|
|
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
|
|
9224
|
+
import { z as z47 } from "zod/v3";
|
|
8288
9225
|
import {
|
|
8289
|
-
createMiddleware as
|
|
9226
|
+
createMiddleware as createMiddleware9,
|
|
8290
9227
|
createAgent as createAgent2,
|
|
8291
|
-
tool as
|
|
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
|
|
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:
|
|
9011
|
-
description:
|
|
9012
|
-
subagent_type:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
10206
|
-
import { createMiddleware as
|
|
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 =
|
|
10434
|
-
var TodoSchema =
|
|
10435
|
-
content:
|
|
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 =
|
|
11375
|
+
var stateSchema = z48.object({ todos: z48.array(TodoSchema).default([]) });
|
|
10439
11376
|
function todoListMiddleware(options) {
|
|
10440
|
-
const writeTodos =
|
|
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:
|
|
10458
|
-
todos:
|
|
11394
|
+
schema: z48.object({
|
|
11395
|
+
todos: z48.array(TodoSchema).describe("List of todo items to update")
|
|
10459
11396
|
})
|
|
10460
11397
|
}
|
|
10461
11398
|
);
|
|
10462
|
-
return
|
|
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((
|
|
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
|
|
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
|
|
11091
|
-
import { createMiddleware as
|
|
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
|
|
11097
|
-
import { tool as
|
|
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 =
|
|
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:
|
|
11148
|
-
task_id:
|
|
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 =
|
|
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:
|
|
11174
|
-
task_id:
|
|
11175
|
-
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 =
|
|
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:
|
|
11201
|
-
task_id:
|
|
11202
|
-
error:
|
|
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 =
|
|
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:
|
|
11221
|
-
to:
|
|
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:
|
|
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 =
|
|
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:
|
|
12240
|
+
schema: z49.object({})
|
|
11304
12241
|
}
|
|
11305
12242
|
);
|
|
11306
|
-
const checkTasksTool =
|
|
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:
|
|
12251
|
+
schema: z49.object({})
|
|
11315
12252
|
}
|
|
11316
12253
|
);
|
|
11317
|
-
const broadcastMessageTool =
|
|
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:
|
|
11337
|
-
content:
|
|
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 =
|
|
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:
|
|
11727
|
-
tasks:
|
|
11728
|
-
|
|
11729
|
-
id:
|
|
11730
|
-
title:
|
|
11731
|
-
description:
|
|
11732
|
-
dependencies:
|
|
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:
|
|
11736
|
-
|
|
11737
|
-
name:
|
|
11738
|
-
role:
|
|
11739
|
-
description:
|
|
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 =
|
|
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:
|
|
11803
|
-
tasks:
|
|
11804
|
-
|
|
11805
|
-
id:
|
|
11806
|
-
title:
|
|
11807
|
-
description:
|
|
11808
|
-
assignee:
|
|
11809
|
-
dependencies:
|
|
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 =
|
|
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:
|
|
11838
|
-
task_id:
|
|
11839
|
-
assignee:
|
|
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 =
|
|
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:
|
|
11866
|
-
task_id:
|
|
11867
|
-
status:
|
|
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 =
|
|
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:
|
|
11894
|
-
task_id:
|
|
11895
|
-
dependencies:
|
|
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 =
|
|
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:
|
|
11940
|
-
team_id:
|
|
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 =
|
|
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:
|
|
11964
|
-
to:
|
|
11965
|
-
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 =
|
|
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:
|
|
12052
|
-
team_id:
|
|
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 =
|
|
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 =
|
|
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:
|
|
12096
|
-
content:
|
|
13032
|
+
schema: z50.object({
|
|
13033
|
+
content: z50.string().describe("Message content to broadcast to all teammates")
|
|
12097
13034
|
})
|
|
12098
13035
|
}
|
|
12099
13036
|
);
|
|
12100
|
-
return
|
|
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 =
|
|
12129
|
-
name:
|
|
12130
|
-
role:
|
|
12131
|
-
description:
|
|
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 =
|
|
12134
|
-
id:
|
|
12135
|
-
title:
|
|
12136
|
-
description:
|
|
12137
|
-
status:
|
|
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 =
|
|
12140
|
-
id:
|
|
12141
|
-
from:
|
|
12142
|
-
to:
|
|
12143
|
-
content:
|
|
12144
|
-
timestamp:
|
|
12145
|
-
type:
|
|
12146
|
-
read:
|
|
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 =
|
|
12149
|
-
teamId:
|
|
12150
|
-
teamLeadId:
|
|
12151
|
-
teammates:
|
|
12152
|
-
tasks:
|
|
12153
|
-
createdAt:
|
|
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 =
|
|
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:
|
|
12158
|
-
team_mailbox:
|
|
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((
|
|
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
|
|
15169
|
-
const toolKey = prefix ? `${prefix}_${
|
|
15170
|
-
|
|
15171
|
-
toolLatticeManager.registerExistingTool(toolKey,
|
|
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,
|