@77-zhike/app-sdk 0.2.0-d7b1.12 → 0.2.0-d7b1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -253,3 +253,11 @@ Chart 仅接受纯数据 `labels` 与 `series`,不接受脚本、HTML 或任
253
253
  ### 精确数字的表格录入
254
254
 
255
255
  `form.TextInput` 可选 `labelHidden` 和 `inputMode="numeric" | "decimal"`。隐藏标题仍保留与控件关联的读屏标签,默认标题继续显示,字段错误和说明仍显示。`inputMode` 只提示键盘,不转换、过滤或格式化值,不替代业务校验;需要保留十进制字符串或未完成输入时使用,数值型字段继续使用 `NumberInput`。
256
+
257
+ ## App 对话输入部件
258
+
259
+ `Conversation.Root` 内自由组合 Messages、History、NewButton、Status、Composer。History 默认使用与 automation 相同的图标弹出列表;Composer 默认带附件/图片上传和语音回填,转写后仍需用户发送。输入能力由 Host 提供,不把 File/音频字节或凭据交给 App Worker。
260
+
261
+ 附件只读当前 App 会话中用户提交的文件,不扩展自定义 Tool 的权限。新 App 业务 Tool 仍在 agent 目录用 `.tools.ts` 合并定义并由 Integration Runtime 执行。元数据 Schema 必须为静态字面量,execute 可导入项目自己的业务校验函数。
262
+
263
+ 真实表单协作可参考 `built-in-apps/opportunity-quotes`:页面以 SDK 布局并列 Form 与 Conversation,callback 接收建议,用户确认后更新本地草稿;正式保存沿原入口。NativeDOM 内仍只允许原有控件,不直接放 Conversation;可以在 Conversation 内用独立 NativeDOM 展示本地建议确认区。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@77-zhike/app-sdk",
3
- "version": "0.2.0-d7b1.12",
3
+ "version": "0.2.0-d7b1.13",
4
4
  "description": "智客 Integration App 一期 SDK 与本地 Toolchain",
5
5
  "repository": {
6
6
  "type": "git",
@@ -67,5 +67,5 @@
67
67
  "optional": true
68
68
  }
69
69
  },
70
- "gitHead": "371b8a58d0f56deb5d1a330a688bc84c90704231"
70
+ "gitHead": "f2a667ec95a8d1787cf0c0655787266317069b55"
71
71
  }
@@ -0,0 +1,22 @@
1
+ /** 静态信息进入不可变 Manifest;工具集合由同目录 .tools.ts 唯一决定。 */
2
+ export interface AppAgentDefinition {
3
+ key: string;
4
+ name: string;
5
+ /** App 提供的系统提示词,最多 16KiB UTF-8,不交付 Client Worker。 */
6
+ systemPrompt: string;
7
+ }
8
+ export type ToolSchema = Readonly<Record<string, unknown>>;
9
+ export interface AppToolDefinition<Input, Output> {
10
+ key: string;
11
+ name: string;
12
+ description: string;
13
+ /** 有界 JSON Schema;平台在模型调用和结果交付时再次严格验证。 */
14
+ inputSchema: ToolSchema;
15
+ outputSchema: ToolSchema;
16
+ /** 默认 none;output 只允许成功且通过校验的结果进入实时 callback。 */
17
+ clientResult?: 'none' | 'output';
18
+ /** 仅在所属安装的服务端执行环境运行,凭据沿用现有 Server SDK。 */
19
+ execute(input: Input): Output | Promise<Output>;
20
+ }
21
+ export function defineAgent(definition: AppAgentDefinition): Readonly<AppAgentDefinition>;
22
+ export function defineTool<Input = Record<string, unknown>, Output = unknown>(definition: AppToolDefinition<Input, Output>): Readonly<AppToolDefinition<Input, Output>>;
@@ -0,0 +1,30 @@
1
+ const KEY = /^[a-z][a-z0-9-]{0,63}$/;
2
+
3
+ function fields(value, required, optional = []) {
4
+ if (!value || typeof value !== "object" || Array.isArray(value) ||
5
+ required.some(key => !Object.hasOwn(value, key)) ||
6
+ Object.keys(value).some(key => ![...required, ...optional].includes(key))) {
7
+ throw new TypeError("agent_definition_invalid");
8
+ }
9
+ if (typeof value.key !== "string" || value.key.trim() !== value.key || !KEY.test(value.key) || typeof value.name !== "string" ||
10
+ !value.name.trim() || value.name.length > 80) throw new TypeError("agent_definition_invalid");
11
+ }
12
+
13
+ export function defineAgent(definition) {
14
+ fields(definition, ["key", "name", "systemPrompt"]);
15
+ if (typeof definition.systemPrompt !== "string" || !definition.systemPrompt.trim() ||
16
+ new TextEncoder().encode(definition.systemPrompt).length > 16384) {
17
+ throw new TypeError("agent_definition_invalid");
18
+ }
19
+ return Object.freeze({ ...definition });
20
+ }
21
+
22
+ export function defineTool(definition) {
23
+ fields(definition, ["key", "name", "description", "inputSchema", "outputSchema", "execute"], ["clientResult"]);
24
+ if (typeof definition.execute !== "function" || typeof definition.description !== "string" ||
25
+ !definition.description.trim() || definition.description.length > 1200 ||
26
+ ![undefined, "none", "output"].includes(definition.clientResult)) {
27
+ throw new TypeError("agent_tool_definition_invalid");
28
+ }
29
+ return Object.freeze({ ...definition, clientResult: definition.clientResult ?? "none" });
30
+ }
@@ -1,3 +1,4 @@
1
+ export { defineAgent, defineTool, type AppAgentDefinition, type AppToolDefinition, type ToolSchema } from "./agents/index.js";
1
2
  import type { JsonValue } from "../index.js";
2
3
  export { defineWorkflowStep, defineWorkflowStepExecute } from "./steps/index.js";
3
4
  export type { WorkflowObjectSchema, WorkflowScalarSchema, WorkflowStepDefinition, WorkflowStepOutcome, WorkflowStepResult, WorkflowSchemaValue } from "./steps/index.js";
@@ -1,3 +1,4 @@
1
+ export { defineAgent, defineTool } from "./agents/index.js";
1
2
  const SERVER_FUNCTION_MARKER = Symbol.for("zhike.server-function.v1");
2
3
  export { defineWorkflowStep, defineWorkflowStepExecute } from "./steps/index.js";
3
4
  export { defineWorkflowTrigger, defineWorkflowTriggerActivate, defineWorkflowTriggerOnEvent, defineWorkflowTriggerDeactivate } from "./triggers/index.js";
@@ -0,0 +1,99 @@
1
+ import ts from 'typescript';
2
+ import { posix } from 'node:path';
3
+ import { ZhikeCliError } from '../../errors.js';
4
+ import { assertAgentManifest } from './manifest.js';
5
+
6
+ const AGENT_PATH = /^app\/agents\/([a-z][a-z0-9-]{0,63})\/agent\.ts$/;
7
+ const TOOL_PATH = /^app\/agents\/([a-z][a-z0-9-]{0,63})\/([a-z][a-z0-9-]{0,63})\.tools\.ts$/;
8
+ function invalid(path, message) { throw new ZhikeCliError('zhike_agent_invalid', `${path}: ${message}`); }
9
+ function literal(node, path, depth = 0) {
10
+ if (depth > 24) invalid(path, '静态声明嵌套过深');
11
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;
12
+ if (ts.isNumericLiteral(node)) return Number(node.text);
13
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(node.operand)) return -Number(node.operand.text);
14
+ if (node.kind === ts.SyntaxKind.TrueKeyword) return true;
15
+ if (node.kind === ts.SyntaxKind.FalseKeyword) return false;
16
+ if (node.kind === ts.SyntaxKind.NullKeyword) return null;
17
+ if (ts.isArrayLiteralExpression(node)) return node.elements.map(item => literal(item, path, depth + 1));
18
+ if (ts.isObjectLiteralExpression(node)) {
19
+ const result = Object.create(null);
20
+ for (const property of node.properties) {
21
+ if (!ts.isPropertyAssignment(property) || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) invalid(path, '元数据必须为静态 JSON 字面量');
22
+ const key = property.name.text;
23
+ if (Object.hasOwn(result, key) || ['__proto__', 'prototype', 'constructor'].includes(key)) invalid(path, '重复或保留字段');
24
+ result[key] = literal(property.initializer, path, depth + 1);
25
+ }
26
+ return result;
27
+ }
28
+ invalid(path, '元数据不能读取变量、函数或运行时代码');
29
+ }
30
+
31
+ function definition(file, api) {
32
+ const { sourceFile, relativePath } = file;
33
+ const imported = sourceFile.statements.some(statement => ts.isImportDeclaration(statement) &&
34
+ statement.moduleSpecifier.text === '@77-zhike/app-sdk/server' &&
35
+ statement.importClause?.namedBindings && ts.isNamedImports(statement.importClause.namedBindings) &&
36
+ statement.importClause.namedBindings.elements.some(item => !item.propertyName && item.name.text === api));
37
+ const exports = sourceFile.statements.filter(statement => ts.isExportAssignment(statement) && !statement.isExportEquals);
38
+ const call = exports[0]?.expression;
39
+ if (!imported || exports.length !== 1 || !call || !ts.isCallExpression(call) ||
40
+ !ts.isIdentifier(call.expression) || call.expression.text !== api || call.arguments.length !== 1 ||
41
+ !ts.isObjectLiteralExpression(call.arguments[0])) invalid(relativePath, `必须直接默认导出 ${api}({...})`);
42
+ const value = Object.create(null);
43
+ for (const property of call.arguments[0].properties) {
44
+ if (!property.name || !(ts.isIdentifier(property.name) || ts.isStringLiteral(property.name))) invalid(relativePath, '声明禁止 spread 或动态属性');
45
+ const key = property.name.text;
46
+ if (Object.hasOwn(value, key)) invalid(relativePath, '重复声明字段');
47
+ if (api === 'defineTool' && key === 'execute') {
48
+ const handler = ts.isMethodDeclaration(property) ? property : ts.isPropertyAssignment(property) ? property.initializer : null;
49
+ if (!handler || !(ts.isMethodDeclaration(handler) || ts.isArrowFunction(handler) || ts.isFunctionExpression(handler)) || handler.parameters.length > 1) invalid(relativePath, 'execute 必须是仅接收业务 input 的函数');
50
+ value[key] = true;
51
+ } else {
52
+ if (!ts.isPropertyAssignment(property)) invalid(relativePath, '元数据必须为静态字段');
53
+ value[key] = literal(property.initializer, relativePath);
54
+ }
55
+ }
56
+ return value;
57
+ }
58
+
59
+ export function discoverAgents(files) {
60
+ const agents = new Map();
61
+ for (const file of files) {
62
+ const match = file.relativePath.match(AGENT_PATH);
63
+ if (!match) continue;
64
+ const value = definition(file, 'defineAgent');
65
+ if (value.key !== match[1] || Object.keys(value).some(key => !['key', 'name', 'systemPrompt'].includes(key))) invalid(file.relativePath, 'Agent key 必须等于目录且只接受 key/name/systemPrompt');
66
+ agents.set(value.key, { ...value, relativePath: file.relativePath, tools: [] });
67
+ }
68
+ if (agents.size > 16) invalid("app/agents", "一个 App 最多 16 个 Agent");
69
+ for (const file of files) {
70
+ const match = file.relativePath.match(TOOL_PATH);
71
+ if (file.relativePath.endsWith('.tools.ts') && !match) invalid(file.relativePath, '工具必须位于 Agent 的直接子目录');
72
+ if (/^app\/agents\/.+\/agent\.(?:ts|tsx)$/.test(file.relativePath) && !AGENT_PATH.test(file.relativePath)) invalid(file.relativePath, 'Agent 入口必须是直接子目录 agent.ts');
73
+ for (const statement of file.sourceFile.statements) {
74
+ if (!(ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
75
+ const specifier = statement.moduleSpecifier.text;
76
+ if (!specifier.startsWith('.')) continue;
77
+ const target = posix.normalize(posix.join(posix.dirname(file.relativePath), specifier)).replace(/\.js$/, '.ts');
78
+ const tool = (target.endsWith(".ts") ? target : `${target}.ts`).match(TOOL_PATH);
79
+ if (tool && (!match || match[1] !== tool[1])) invalid(file.relativePath, '不能跨 Agent 导入工具声明,请抽取普通 helper');
80
+ }
81
+ if (!match) continue;
82
+ const owner = agents.get(match[1]);
83
+ if (!owner) invalid(file.relativePath, '工具缺少所属 agent.ts');
84
+ const value = definition(file, 'defineTool');
85
+ if (value.key !== match[2] || value.execute !== true) invalid(file.relativePath, 'Tool key 必须等于文件前缀且提供 execute');
86
+ delete value.execute;
87
+ owner.tools.push({ ...value, clientResult: value.clientResult ?? 'none', artifact: 'server',
88
+ entry: `agent-tool/${match[1]}/${match[2]}`, relativePath: file.relativePath });
89
+ }
90
+ return [...agents.values()].sort((a, b) => a.key.localeCompare(b.key)).map(agent => {
91
+ agent.tools.sort((a, b) => a.key.localeCompare(b.key));
92
+ try { assertAgentManifest(agentManifest(agent)); } catch { invalid(agent.relativePath, 'Agent/Tool 元数据、Schema 或归属无效'); }
93
+ return Object.freeze(agent);
94
+ });
95
+ }
96
+
97
+ export function agentManifest({ relativePath, tools, ...agent }) {
98
+ return { ...agent, tools: tools.map(({ relativePath: path, ...tool }) => tool) };
99
+ }
@@ -0,0 +1,62 @@
1
+ const KEY = /^[a-z][a-z0-9-]{0,63}$/;
2
+ const FIELD = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
3
+ const RESERVED = new Set(['tenant_id', 'tenantId', 'member_id', 'memberId', 'installation_id', 'installationId',
4
+ 'app_id', 'appId', 'version_id', 'versionId', 'run_id', 'executionId', 'tool_call_id', 'constructor', 'prototype']);
5
+ const SCHEMA_KEYS = ['type', 'properties', 'required', 'additionalProperties', 'items', 'enum', 'const',
6
+ 'minLength', 'maxLength', 'pattern', 'minimum', 'maximum', 'minItems', 'maxItems', 'title', 'description'];
7
+ function invalid() { throw new TypeError('app_manifest_agent_invalid'); }
8
+ function record(value) { if (!value || typeof value !== 'object' || Array.isArray(value)) invalid(); }
9
+ function keys(value, allowed) { if (Object.keys(value).some(key => !allowed.includes(key))) invalid(); }
10
+ function text(value, max) { if (typeof value !== 'string' || !value.trim() || value.length > max) invalid(); }
11
+
12
+ export function assertAgentSchema(value) {
13
+ function visit(schema, depth = 0) {
14
+ record(schema); keys(schema, SCHEMA_KEYS);
15
+ if (depth > 6 || !['object', 'array', 'string', 'integer', 'number', 'boolean', 'null'].includes(schema.type)) invalid();
16
+ if (schema.type === 'object') {
17
+ record(schema.properties);
18
+ if (schema.additionalProperties !== false || Object.keys(schema.properties).length > 32) invalid();
19
+ const required = schema.required ?? [];
20
+ if (!Array.isArray(required) || new Set(required).size !== required.length ||
21
+ required.some(key => typeof key !== 'string' || !Object.hasOwn(schema.properties, key))) invalid();
22
+ for (const [key, child] of Object.entries(schema.properties)) {
23
+ if (key.trim() !== key || !FIELD.test(key) || RESERVED.has(key)) invalid();
24
+ visit(child, depth + 1);
25
+ }
26
+ } else if (schema.type === 'array') visit(schema.items, depth + 1);
27
+ else if (schema.properties !== undefined || schema.items !== undefined || schema.required !== undefined ||
28
+ schema.additionalProperties !== undefined) invalid();
29
+ for (const key of ['minLength', 'maxLength', 'minItems', 'maxItems']) {
30
+ if (schema[key] !== undefined && (!Number.isSafeInteger(schema[key]) || schema[key] < 0 || schema[key] > 8192)) invalid();
31
+ }
32
+ for (const [min, max] of [['minLength', 'maxLength'], ['minItems', 'maxItems'], ['minimum', 'maximum']]) {
33
+ if (schema[min] !== undefined && schema[max] !== undefined && schema[min] > schema[max]) invalid();
34
+ }
35
+ for (const key of ['minimum', 'maximum']) if (schema[key] !== undefined &&
36
+ (typeof schema[key] !== 'number' || !Number.isFinite(schema[key]))) invalid();
37
+ if (schema.enum !== undefined && (!Array.isArray(schema.enum) || !schema.enum.length || schema.enum.length > 64 ||
38
+ schema.enum.some(item => item !== null && (!['string', 'boolean', 'number'].includes(typeof item) || typeof item === 'number' && !Number.isFinite(item))))) invalid();
39
+ if (schema.const !== undefined && schema.const !== null && (!['string', 'boolean', 'number'].includes(typeof schema.const) || typeof schema.const === 'number' && !Number.isFinite(schema.const))) invalid();
40
+ for (const key of ['title', 'description']) if (schema[key] !== undefined) text(schema[key], 1200);
41
+ if (schema.pattern !== undefined) { text(schema.pattern, 256); try { new RegExp(schema.pattern); } catch { invalid(); } }
42
+ }
43
+ visit(value);
44
+ if (value.type !== 'object' || new TextEncoder().encode(JSON.stringify(value)).length > 16384) invalid();
45
+ return value;
46
+ }
47
+
48
+ export function assertAgentManifest(value) {
49
+ record(value); keys(value, ['key', 'name', 'systemPrompt', 'tools']);
50
+ if (typeof value.key !== "string" || value.key.trim() !== value.key || !KEY.test(value.key)) invalid();
51
+ text(value.name, 80); text(value.systemPrompt, 16384);
52
+ if (new TextEncoder().encode(value.systemPrompt).length > 16384 || !Array.isArray(value.tools) || value.tools.length > 32) invalid();
53
+ const seen = new Set();
54
+ for (const tool of value.tools) {
55
+ record(tool); keys(tool, ['artifact', 'key', 'entry', 'name', 'description', 'inputSchema', 'outputSchema', 'clientResult']);
56
+ if (typeof tool.key !== "string" || tool.key.trim() !== tool.key || !KEY.test(tool.key) || seen.has(tool.key) || tool.artifact !== 'server' ||
57
+ tool.entry !== `agent-tool/${value.key}/${tool.key}` || !['none', 'output'].includes(tool.clientResult)) invalid();
58
+ seen.add(tool.key); text(tool.name, 80); text(tool.description, 1200);
59
+ assertAgentSchema(tool.inputSchema); assertAgentSchema(tool.outputSchema);
60
+ }
61
+ return value;
62
+ }
@@ -6,6 +6,7 @@ import { dirname, relative, resolve, sep } from "node:path";
6
6
  import { build } from "esbuild";
7
7
  import ts from "typescript";
8
8
 
9
+ import { agentManifest } from "./agents/discovery.js";
9
10
  import { serializeWebhookResponse } from "./webhook-response.js";
10
11
 
11
12
  import { TOOLING_VERSION } from "../config.js";
@@ -42,7 +43,7 @@ export async function buildAppBundle({
42
43
  sources.serverFunctions.length > 0 ||
43
44
  sources.webhookHandlers.length > 0 ||
44
45
  sources.workflowSteps.length > 0 ||
45
- sources.workflowTriggers.length > 0
46
+ sources.workflowTriggers.length > 0 || sources.agents.length > 0
46
47
  ? await buildServerArtifact(projectDirectory, sources) : null;
47
48
  } catch (error) {
48
49
  const policyError = error.errors?.find(item => item.detail instanceof ZhikeCliError)?.detail;
@@ -95,7 +96,7 @@ export async function buildAppBundle({
95
96
  });
96
97
  }
97
98
 
98
- async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFunctions }) {
99
+ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFunctions, agents }) {
99
100
  const paths = {
100
101
  "@77-zhike/app-sdk": [resolve(SDK_SOURCE_ROOT, "index.d.ts")],
101
102
  "@77-zhike/app-sdk/client": [resolve(SDK_SOURCE_ROOT, "client", "index.d.ts")],
@@ -125,11 +126,15 @@ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFu
125
126
  const rootNames = sourceFiles.map((path) => resolve(projectDirectory, "src", path));
126
127
  // 仅供 TypeScript 检查,不落盘、不进 Artifact;复用原包装的参数/结果合同。
127
128
  const contractPath = resolve(projectDirectory, ".zhike/server-function-contracts.ts");
129
+ const handlers = [
130
+ ...serverFunctions.map(item => ({ ...item, member: '' })),
131
+ ...agents.flatMap(agent => agent.tools.map(item => ({ ...item, member: '.execute' }))),
132
+ ];
128
133
  const contractSource = [
129
134
  'import type { ServerFunction } from "@77-zhike/app-sdk/server";',
130
- ...serverFunctions.flatMap(({ relativePath }, index) => [
135
+ ...handlers.flatMap(({ relativePath, member }, index) => [
131
136
  `import handler${index} from ${JSON.stringify(`../src/${relativePath.slice(0, -3)}`)};`,
132
- `type Contract${index} = ServerFunction<Parameters<typeof handler${index}>, Awaited<ReturnType<typeof handler${index}>>>;`,
137
+ `type Contract${index} = ServerFunction<Parameters<typeof handler${index}${member}>, Awaited<ReturnType<typeof handler${index}${member}>>>;`,
133
138
  ]),
134
139
  ].join("\n");
135
140
  const host = ts.createCompilerHost(options);
@@ -138,14 +143,14 @@ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFu
138
143
  resolve(fileName) === contractPath
139
144
  ? ts.createSourceFile(fileName, contractSource, languageVersion, true)
140
145
  : getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
141
- if (serverFunctions.length) rootNames.push(contractPath);
146
+ if (handlers.length) rootNames.push(contractPath);
142
147
  const program = ts.createProgram({ rootNames, options, host });
143
148
  const diagnostics = ts.getPreEmitDiagnostics(program);
144
149
  if (diagnostics.length === 0) return;
145
150
  const diagnostic = diagnostics[0];
146
151
  const contractLine = diagnostic.file?.fileName === contractPath
147
152
  ? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start || 0).line : -1;
148
- const contractOwner = contractLine > 0 ? serverFunctions[Math.floor((contractLine - 1) / 2)] : null;
153
+ const contractOwner = contractLine > 0 ? handlers[Math.floor((contractLine - 1) / 2)] : null;
149
154
  const location = contractOwner ? `src/${contractOwner.relativePath}(参数/返回类型)` : diagnostic.file
150
155
  ? `${relative(projectDirectory, diagnostic.file.fileName).split(sep).join("/")}:${
151
156
  diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start || 0).line + 1
@@ -189,7 +194,12 @@ async function buildServerArtifact(projectDirectory, sources) {
189
194
  const hasConnectionLifecycle = sources.lifecycleEvents.some(
190
195
  ({ event }) => event === "connection-added" || event === "connection-removed",
191
196
  );
197
+ const tools = sources.agents.flatMap(agent => agent.tools);
198
+ const toolImports = tools.map((tool, index) => `import agentTool${index} from "./src/${tool.relativePath}";`);
199
+ const agentToolDeclarations = `const agentTools = new Map([${tools.map((tool, index) => `[${JSON.stringify(tool.entry)}, agentTool${index}]`).join(",")}]);`;
200
+ const agentToolDispatch = ` if (request.kind === "agent_tool") { const tool = agentTools.get(request.entryKey); if (!tool) throw new Error("runtime_agent_tool_not_found"); return tool.execute(Object.freeze({ ...request.input })); }\n`;
192
201
  const imports = [
202
+ ...toolImports,
193
203
  ...(hasConnectionLifecycle
194
204
  ? ['import { getWorkspaceConnection as getLifecycleConnection } from "@77-zhike/app-sdk/server";']
195
205
  : []),
@@ -244,7 +254,7 @@ async function buildServerArtifact(projectDirectory, sources) {
244
254
  const triggerDispatch = sources.workflowTriggers.length > 0
245
255
  ? ` if (request.kind === "workflow_trigger") {\n const handler = workflowTriggers.get(request.entryKey);\n if (!handler) throw new Error("runtime_workflow_trigger_not_found");\n const result = await handler(Object.freeze({ ...request.params }));\n return request.entryKey.endsWith("/deactivate") ? null : result;\n }\n`
246
256
  : "";
247
- const stdin = `${imports}\n${serializeWebhookResponse.toString()}\nconst lifecycleEvents = new Map([${lifecycleEntries}]);\nconst serverFunctions = new Map([${serverEntries}]);\nconst webhookHandlers = new Map([${webhookEntries}]);\nconst workflowSteps = new Map([${workflowEntries}]);\n${triggerDeclarations}export async function dispatchServerInvocation(request) {\n if (!request || typeof request !== "object") throw new Error("runtime_invocation_invalid");\n if (request.kind === "lifecycle_event") {\n const definition = lifecycleEvents.get(request.entryKey);\n if (!definition) throw new Error("runtime_lifecycle_event_not_found");\n const params = definition.event === "app-installed" || definition.event === "app-uninstalled" ? request.context : Object.freeze({ connection: getLifecycleConnection() });\n await definition.handler(params);\n return null;\n }\n if (request.kind === "server_function") {\n const handler = serverFunctions.get(request.entryKey);\n if (!handler) throw new Error("runtime_server_function_not_found");\n return handler(...request.args);\n }\n if (request.kind === "workflow_step") {\n const handler = workflowSteps.get(request.entryKey);\n if (!handler) throw new Error("runtime_workflow_step_not_found");\n return handler(Object.freeze({ config: request.config }));\n }\n${triggerDispatch} if (request.kind === "webhook_handler") {\n const handler = webhookHandlers.get(request.entryKey);\n if (!handler) throw new Error("runtime_webhook_handler_not_found");\n const input = request.webRequest;\n const webRequest = new Request(input.url, { method: input.method, headers: input.headers, body: ["GET", "HEAD"].includes(input.method) ? undefined : input.body });\n const response = await handler(webRequest, Object.freeze({ ...request.context }));\n if (!(response instanceof Response)) throw new Error("runtime_webhook_response_invalid");\n return serializeWebhookResponse(response);\n }\n throw new Error("runtime_invocation_kind_invalid");\n}\n`;
257
+ const stdin = `${imports}\n${serializeWebhookResponse.toString()}\nconst lifecycleEvents = new Map([${lifecycleEntries}]);\nconst serverFunctions = new Map([${serverEntries}]);\nconst webhookHandlers = new Map([${webhookEntries}]);\nconst workflowSteps = new Map([${workflowEntries}]);\n${triggerDeclarations}${agentToolDeclarations}\nexport async function dispatchServerInvocation(request) {\n if (!request || typeof request !== "object") throw new Error("runtime_invocation_invalid");\n if (request.kind === "lifecycle_event") {\n const definition = lifecycleEvents.get(request.entryKey);\n if (!definition) throw new Error("runtime_lifecycle_event_not_found");\n const params = definition.event === "app-installed" || definition.event === "app-uninstalled" ? request.context : Object.freeze({ connection: getLifecycleConnection() });\n await definition.handler(params);\n return null;\n }\n if (request.kind === "server_function") {\n const handler = serverFunctions.get(request.entryKey);\n if (!handler) throw new Error("runtime_server_function_not_found");\n return handler(...request.args);\n }\n if (request.kind === "workflow_step") {\n const handler = workflowSteps.get(request.entryKey);\n if (!handler) throw new Error("runtime_workflow_step_not_found");\n return handler(Object.freeze({ config: request.config }));\n }\n${triggerDispatch}${agentToolDispatch} if (request.kind === "webhook_handler") {\n const handler = webhookHandlers.get(request.entryKey);\n if (!handler) throw new Error("runtime_webhook_handler_not_found");\n const input = request.webRequest;\n const webRequest = new Request(input.url, { method: input.method, headers: input.headers, body: ["GET", "HEAD"].includes(input.method) ? undefined : input.body });\n const response = await handler(webRequest, Object.freeze({ ...request.context }));\n if (!(response instanceof Response)) throw new Error("runtime_webhook_response_invalid");\n return serializeWebhookResponse(response);\n }\n throw new Error("runtime_invocation_kind_invalid");\n}\n`;
248
258
  const result = await build({
249
259
  absWorkingDir: projectDirectory,
250
260
  bundle: true,
@@ -400,6 +410,7 @@ function createManifest({ clientBytes, serverBytes, sources }) {
400
410
  event,
401
411
  key,
402
412
  })),
413
+ ...(sources.agents.length ? { agents: sources.agents.map(agentManifest) } : {}),
403
414
  serverFunctions: sources.serverFunctions.map(({ key }) => ({
404
415
  artifact: "server",
405
416
  key,
@@ -430,6 +441,9 @@ function createServerDefinitionsDigest(sources) {
430
441
  return sha256(
431
442
  canonicalJsonBytes({
432
443
  lifecycleEvents: sources.lifecycleEvents.map(({ event, key }) => ({ event, key })),
444
+ ...(sources.agents.length ? { agents: sources.agents.map(({ key, tools }) => ({
445
+ key, tools: tools.map(({ key: toolKey, entry }) => ({ key: toolKey, entry })),
446
+ })) } : {}),
433
447
  serverFunctions: sources.serverFunctions.map(({ key }) => ({ key })),
434
448
  webhookHandlers: sources.webhookHandlers.map(({ key, methods }) => ({ key, methods })),
435
449
  ...(sources.workflowSteps.length > 0 ? { workflowSteps: sources.workflowSteps.map(({ key, entry }) => ({ key, entry })) } : {}),
@@ -5,6 +5,7 @@ import { posix, relative, resolve, sep } from "node:path";
5
5
  import ts from "typescript";
6
6
 
7
7
  import { ZhikeCliError } from "../errors.js";
8
+ import { discoverAgents } from "./agents/discovery.js";
8
9
  import { discoverWorkflowBlock } from "./workflow-blocks/discovery.js";
9
10
  import { assertAllowedPackageImport } from "./dependency-policy.js";
10
11
 
@@ -50,6 +51,7 @@ export async function discoverAppSources(projectDirectory) {
50
51
  }
51
52
  const sourceFilesByPath = new Map(parsedFiles.map(file => [file.relativePath, file.sourceFile]));
52
53
 
54
+ const agents = discoverAgents(parsedFiles);
53
55
  const frontendExtensions = [];
54
56
  const lifecycleEvents = [];
55
57
  const serverFunctions = [];
@@ -138,7 +140,8 @@ export async function discoverAppSources(projectDirectory) {
138
140
  serverFunctions.length === 0 &&
139
141
  webhookHandlers.length === 0 &&
140
142
  workflowSteps.length === 0 &&
141
- workflowTriggers.length === 0
143
+ workflowTriggers.length === 0 &&
144
+ agents.length === 0
142
145
  ) {
143
146
  throw toolchainError(
144
147
  "zhike_source_entry_missing",
@@ -149,6 +152,7 @@ export async function discoverAppSources(projectDirectory) {
149
152
  throw toolchainError("zhike_settings_duplicate", "一个 App 至多声明一个 Settings");
150
153
  }
151
154
  return Object.freeze({
155
+ agents: Object.freeze(agents),
152
156
  frontendExtensions: Object.freeze(sortByKey(frontendExtensions)),
153
157
  lifecycleEvents: Object.freeze(sortByKey(lifecycleEvents)),
154
158
  serverFunctions: Object.freeze(sortByKey(serverFunctions)),
@@ -572,7 +576,7 @@ export function validateImports(sourceFile, relativePath, options = {}) {
572
576
  const allowsStandardFetch =
573
577
  options.target === "server" || (!options.target && (
574
578
  relativePath.startsWith("server/") ||
575
- relativePath.startsWith("app/blocks/") ||
579
+ relativePath.startsWith("app/blocks/") || relativePath.startsWith("app/agents/") ||
576
580
  /^app\/events\/.+\.event\.ts$/.test(relativePath) ||
577
581
  /^app\/webhooks\/.+\.webhook\.ts$/.test(relativePath)));
578
582
  const target = allowsStandardFetch ? "server" : "client";
@@ -1,5 +1,5 @@
1
1
  // 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
2
- export const componentTypes = Object.freeze(["NativeDOM","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
3
- export const publicComponentNames = Object.freeze(["NativeDOM","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
4
- export const legacyComponentTypes = Object.freeze(["NativeDOM","Text","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
2
+ export const componentTypes = Object.freeze(["Conversation","ConversationRoot","ConversationMessages","ConversationComposer","ConversationHistory","ConversationNewButton","ConversationStopButton","ConversationStatus","NativeDOM","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
3
+ export const publicComponentNames = Object.freeze(["Conversation","ConversationRoot","ConversationMessages","ConversationComposer","ConversationHistory","ConversationNewButton","ConversationStopButton","ConversationStatus","NativeDOM","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
4
+ export const legacyComponentTypes = Object.freeze(["Conversation","ConversationRoot","ConversationMessages","ConversationComposer","ConversationHistory","ConversationNewButton","ConversationStopButton","ConversationStatus","NativeDOM","Text","Icon","Stack","SplitLayout","SplitLayoutMain","SplitLayoutAside","SplitLayoutActions","Section","Banner","Badge","LoadingState","Skeleton","Card","Grid","EmptyState","ZhikeLink","Button","SearchInput","FilterSelect","Switch","LookupCell","Tabs","TabsList","TabsTrigger","TabsContent","Table","TableHeader","TableBody","TableRow","TableCell","Avatar","DescriptionList","DescriptionListItem","DescriptionListMenu","DescriptionListMenuItem","Divider","Chart","DashboardSurface","DashboardLayout","DashboardPanel","MetricReadout","AnalysisView","Json","Link","StatusBadge","TextBlock","TypographyTitle","TypographyBody","TypographyCaption","TableHeaderCell","WidgetTextWidget","WidgetTitle","WidgetTextPrimary","WidgetTextSecondary","WidgetLoading","WidgetDecoration"]);
5
5
  export const badgeColors = Object.freeze(["amber","blue","cyan","green","grey","lavender","lime","orange","pink","purple","red","yellow"]);
@@ -0,0 +1,33 @@
1
+ /** 实时事件身份;executionId 是服务端实际运行 ID。 */
2
+ export interface ConversationEventIdentity {
3
+ eventId: string;
4
+ conversationId: string;
5
+ turnId: string;
6
+ executionId: string;
7
+ }
8
+ export interface ConversationCreatedEvent { eventId: string; conversationId: string; }
9
+ export type TurnStartedEvent = ConversationEventIdentity;
10
+ export type TurnFinishedEvent = ConversationEventIdentity & {
11
+ status: 'completed' | 'cancelled' | 'failed';
12
+ error?: { code: string; message: string };
13
+ };
14
+ export type ToolCallEvent = ConversationEventIdentity & {
15
+ toolCallId: string;
16
+ tool: { kind: 'app'; key: string; name: string };
17
+ phase: 'started' | 'succeeded' | 'failed' | 'cancelled';
18
+ /** 仅显式 clientResult=output 且经过平台校验时存在。 */
19
+ result?: import('./index.js').JsonValue;
20
+ error?: { code: string; message: string };
21
+ };
22
+ export interface ConversationErrorEvent {
23
+ eventId: string;
24
+ phase: 'request' | 'transport' | 'execution';
25
+ code: string;
26
+ message: string;
27
+ /** 必须先重新读取状态,再由用户决定是否重试。 */
28
+ retryable: boolean;
29
+ requestId?: string;
30
+ conversationId?: string;
31
+ turnId?: string;
32
+ executionId?: string;
33
+ }
package/src/ui/factory.js CHANGED
@@ -53,6 +53,10 @@ export function createUiComponents(React, { legacy = false, useOwner = () => nul
53
53
  }
54
54
  return React.createElement(type, props);
55
55
  }]));
56
+ if (components.Conversation) Object.assign(components.Conversation, { Root: components.ConversationRoot,
57
+ Messages: components.ConversationMessages, Composer: components.ConversationComposer,
58
+ History: components.ConversationHistory, NewButton: components.ConversationNewButton,
59
+ StopButton: components.ConversationStopButton, Status: components.ConversationStatus });
56
60
  Object.assign(components.DescriptionList, { Item: components.DescriptionListItem, Menu: components.DescriptionListMenu });
57
61
  components.DescriptionListMenu.Item = components.DescriptionListMenuItem;
58
62
  Object.assign(components.Table, { Header: components.TableHeader, HeaderCell: components.TableHeaderCell, Body: components.TableBody, Row: components.TableRow, Cell: components.TableCell });
package/src/ui/index.d.ts CHANGED
@@ -8,6 +8,63 @@ export interface ChartData { labels: string[]; series: { id: string; label: stri
8
8
  export type AppIconName = "a-arrow-down" | "a-arrow-up" | "a-large-small" | "accessibility" | "activity" | "activity-square" | "ad" | "air-vent" | "airplay" | "alarm-check" | "alarm-clock" | "alarm-clock-check" | "alarm-clock-minus" | "alarm-clock-off" | "alarm-clock-plus" | "alarm-minus" | "alarm-plus" | "alarm-smoke" | "album" | "alert-circle" | "alert-octagon" | "alert-triangle" | "align-center" | "align-center-horizontal" | "align-center-vertical" | "align-end-horizontal" | "align-end-vertical" | "align-horizontal-distribute-center" | "align-horizontal-distribute-end" | "align-horizontal-distribute-start" | "align-horizontal-justify-center" | "align-horizontal-justify-end" | "align-horizontal-justify-start" | "align-horizontal-space-around" | "align-horizontal-space-between" | "align-justify" | "align-left" | "align-right" | "align-start-horizontal" | "align-start-vertical" | "align-vertical-distribute-center" | "align-vertical-distribute-end" | "align-vertical-distribute-start" | "align-vertical-justify-center" | "align-vertical-justify-end" | "align-vertical-justify-start" | "align-vertical-space-around" | "align-vertical-space-between" | "ambulance" | "ampersand" | "ampersands" | "amphora" | "anchor" | "angle" | "angry" | "annoyed" | "antenna" | "anvil" | "aperture" | "app-window" | "app-window-mac" | "apple" | "archive" | "archive-restore" | "archive-x" | "area-chart" | "armchair" | "arrow-big-down" | "arrow-big-down-dash" | "arrow-big-left" | "arrow-big-left-dash" | "arrow-big-right" | "arrow-big-right-dash" | "arrow-big-up" | "arrow-big-up-dash" | "arrow-down" | "arrow-down-0-1" | "arrow-down-01" | "arrow-down-1-0" | "arrow-down-10" | "arrow-down-a-z" | "arrow-down-az" | "arrow-down-circle" | "arrow-down-from-line" | "arrow-down-left" | "arrow-down-left-from-circle" | "arrow-down-left-from-square" | "arrow-down-left-square" | "arrow-down-narrow-wide" | "arrow-down-right" | "arrow-down-right-from-circle" | "arrow-down-right-from-square" | "arrow-down-right-square" | "arrow-down-square" | "arrow-down-to-dot" | "arrow-down-to-line" | "arrow-down-up" | "arrow-down-wide-narrow" | "arrow-down-z-a" | "arrow-down-za" | "arrow-left" | "arrow-left-circle" | "arrow-left-from-line" | "arrow-left-right" | "arrow-left-square" | "arrow-left-to-line" | "arrow-right" | "arrow-right-circle" | "arrow-right-from-line" | "arrow-right-left" | "arrow-right-square" | "arrow-right-to-line" | "arrow-up" | "arrow-up-0-1" | "arrow-up-01" | "arrow-up-1-0" | "arrow-up-10" | "arrow-up-a-z" | "arrow-up-az" | "arrow-up-circle" | "arrow-up-down" | "arrow-up-from-dot" | "arrow-up-from-line" | "arrow-up-left" | "arrow-up-left-from-circle" | "arrow-up-left-from-square" | "arrow-up-left-square" | "arrow-up-narrow-wide" | "arrow-up-right" | "arrow-up-right-from-circle" | "arrow-up-right-from-square" | "arrow-up-right-square" | "arrow-up-square" | "arrow-up-to-line" | "arrow-up-wide-narrow" | "arrow-up-z-a" | "arrow-up-za" | "arrows-up-from-line" | "asterisk" | "asterisk-square" | "astroid" | "at-sign" | "atom" | "audio-lines" | "audio-lines-off" | "audio-lines-x" | "audio-waveform" | "award" | "axe" | "axis-3-d" | "axis-3d" | "baby" | "backpack" | "badge" | "badge-alert" | "badge-cent" | "badge-check" | "badge-dollar-sign" | "badge-euro" | "badge-help" | "badge-indian-rupee" | "badge-info" | "badge-japanese-yen" | "badge-minus" | "badge-percent" | "badge-plus" | "badge-pound-sterling" | "badge-question-mark" | "badge-russian-ruble" | "badge-swiss-franc" | "badge-turkish-lira" | "badge-x" | "baggage-claim" | "balloon" | "ban" | "banana" | "bandage" | "banknote" | "banknote-arrow-down" | "banknote-arrow-up" | "banknote-check" | "banknote-x" | "bar-chart" | "bar-chart-2" | "bar-chart-3" | "bar-chart-4" | "bar-chart-big" | "bar-chart-horizontal" | "bar-chart-horizontal-big" | "barcode" | "barrel" | "baseline" | "bath" | "battery" | "battery-charging" | "battery-full" | "battery-low" | "battery-medium" | "battery-plus" | "battery-warning" | "beaker" | "bean" | "bean-off" | "bed" | "bed-double" | "bed-single" | "beef" | "beef-off" | "beer" | "beer-off" | "bell" | "bell-check" | "bell-dot" | "bell-electric" | "bell-minus" | "bell-off" | "bell-plus" | "bell-ring" | "between-horizonal-end" | "between-horizonal-start" | "between-horizontal-end" | "between-horizontal-start" | "between-vertical-end" | "between-vertical-start" | "biceps-flexed" | "bike" | "binary" | "binoculars" | "biohazard" | "bird" | "birdhouse" | "bitcoin" | "blend" | "blender" | "blinds" | "blocks" | "bluetooth" | "bluetooth-connected" | "bluetooth-off" | "bluetooth-searching" | "bold" | "bolt" | "bomb" | "bone" | "bone-fracture" | "book" | "book-a" | "book-alert" | "book-audio" | "book-check" | "book-copy" | "book-dashed" | "book-down" | "book-headphones" | "book-heart" | "book-image" | "book-key" | "book-lock" | "book-marked" | "book-minus" | "book-open" | "book-open-check" | "book-open-text" | "book-plus" | "book-search" | "book-template" | "book-text" | "book-type" | "book-up" | "book-up-2" | "book-user" | "book-x" | "bookmark" | "bookmark-check" | "bookmark-minus" | "bookmark-off" | "bookmark-plus" | "bookmark-x" | "boom-box" | "bot" | "bot-message-square" | "bot-off" | "bottle-wine" | "bow-arrow" | "box" | "box-select" | "boxes" | "braces" | "brackets" | "brain" | "brain-circuit" | "brain-cog" | "brick-wall" | "brick-wall-fire" | "brick-wall-shield" | "briefcase" | "briefcase-business" | "briefcase-conveyor-belt" | "briefcase-medical" | "bring-to-front" | "broccoli" | "broom" | "broom-sparkles" | "brush" | "brush-cleaning" | "bubbles" | "bug" | "bug-off" | "bug-play" | "building" | "building-2" | "bus" | "bus-front" | "cable" | "cable-car" | "cake" | "cake-slice" | "calculator" | "calendar" | "calendar-1" | "calendar-arrow-down" | "calendar-arrow-up" | "calendar-check" | "calendar-check-2" | "calendar-clock" | "calendar-cog" | "calendar-days" | "calendar-fold" | "calendar-heart" | "calendar-minus" | "calendar-minus-2" | "calendar-off" | "calendar-plus" | "calendar-plus-2" | "calendar-range" | "calendar-search" | "calendar-sync" | "calendar-x" | "calendar-x-2" | "calendars" | "camera" | "camera-off" | "candlestick-chart" | "candy" | "candy-cane" | "candy-off" | "cannabis" | "cannabis-off" | "captions" | "captions-off" | "car" | "car-battery" | "car-front" | "car-taxi-front" | "caravan" | "card-sim" | "carrot" | "case-lower" | "case-sensitive" | "case-upper" | "cassette-tape" | "cast" | "castle" | "cat" | "cctv" | "cctv-off" | "chart-area" | "chart-bar" | "chart-bar-big" | "chart-bar-decreasing" | "chart-bar-increasing" | "chart-bar-stacked" | "chart-candlestick" | "chart-column" | "chart-column-big" | "chart-column-decreasing" | "chart-column-increasing" | "chart-column-stacked" | "chart-gantt" | "chart-line" | "chart-network" | "chart-no-axes-column" | "chart-no-axes-column-decreasing" | "chart-no-axes-column-increasing" | "chart-no-axes-combined" | "chart-no-axes-gantt" | "chart-pie" | "chart-scatter" | "chart-spline" | "check" | "check-check" | "check-circle" | "check-circle-2" | "check-line" | "check-square" | "check-square-2" | "chef-hat" | "cherry" | "chess-bishop" | "chess-king" | "chess-knight" | "chess-pawn" | "chess-queen" | "chess-rook" | "chevron-down" | "chevron-down-circle" | "chevron-down-square" | "chevron-first" | "chevron-last" | "chevron-left" | "chevron-left-circle" | "chevron-left-square" | "chevron-right" | "chevron-right-circle" | "chevron-right-square" | "chevron-up" | "chevron-up-circle" | "chevron-up-square" | "chevrons-down" | "chevrons-down-up" | "chevrons-left" | "chevrons-left-right" | "chevrons-left-right-ellipsis" | "chevrons-right" | "chevrons-right-left" | "chevrons-up" | "chevrons-up-down" | "church" | "cigarette" | "cigarette-off" | "circle" | "circle-alert" | "circle-arrow-down" | "circle-arrow-left" | "circle-arrow-out-down-left" | "circle-arrow-out-down-right" | "circle-arrow-out-up-left" | "circle-arrow-out-up-right" | "circle-arrow-right" | "circle-arrow-up" | "circle-check" | "circle-check-big" | "circle-chevron-down" | "circle-chevron-left" | "circle-chevron-right" | "circle-chevron-up" | "circle-dashed" | "circle-divide" | "circle-dollar-sign" | "circle-dot" | "circle-dot-dashed" | "circle-ellipsis" | "circle-equal" | "circle-euro" | "circle-fading-arrow-up" | "circle-fading-plus" | "circle-gauge" | "circle-help" | "circle-minus" | "circle-off" | "circle-parking" | "circle-parking-off" | "circle-pause" | "circle-percent" | "circle-pile" | "circle-play" | "circle-plus" | "circle-pound-sterling" | "circle-power" | "circle-question-mark" | "circle-slash" | "circle-slash-2" | "circle-slashed" | "circle-small" | "circle-star" | "circle-stop" | "circle-user" | "circle-user-round" | "circle-x" | "circuit-board" | "citrus" | "clapperboard" | "clipboard" | "clipboard-check" | "clipboard-clock" | "clipboard-copy" | "clipboard-edit" | "clipboard-list" | "clipboard-minus" | "clipboard-paste" | "clipboard-pen" | "clipboard-pen-line" | "clipboard-plus" | "clipboard-signature" | "clipboard-type" | "clipboard-x" | "clock" | "clock-1" | "clock-10" | "clock-11" | "clock-12" | "clock-2" | "clock-3" | "clock-4" | "clock-5" | "clock-6" | "clock-7" | "clock-8" | "clock-9" | "clock-alert" | "clock-arrow-down" | "clock-arrow-left" | "clock-arrow-right" | "clock-arrow-up" | "clock-check" | "clock-fading" | "clock-plus" | "closed-caption" | "cloud" | "cloud-alert" | "cloud-backup" | "cloud-check" | "cloud-cog" | "cloud-download" | "cloud-drizzle" | "cloud-fog" | "cloud-hail" | "cloud-lightning" | "cloud-moon" | "cloud-moon-rain" | "cloud-off" | "cloud-rain" | "cloud-rain-wind" | "cloud-snow" | "cloud-sun" | "cloud-sun-rain" | "cloud-sync" | "cloud-upload" | "cloudy" | "clover" | "club" | "code" | "code-2" | "code-square" | "code-xml" | "coffee" | "cog" | "coins" | "columns" | "columns-2" | "columns-3" | "columns-3-cog" | "columns-4" | "columns-settings" | "combine" | "command" | "compass" | "component" | "computer" | "concierge-bell" | "cone" | "construction" | "contact" | "contact-2" | "contact-round" | "container" | "contrast" | "cookie" | "cooking-pot" | "copy" | "copy-check" | "copy-minus" | "copy-plus" | "copy-slash" | "copy-x" | "copyleft" | "copyright" | "corner-down-left" | "corner-down-right" | "corner-left-down" | "corner-left-up" | "corner-right-down" | "corner-right-up" | "corner-up-left" | "corner-up-right" | "cpu" | "creative-commons" | "credit-card" | "croissant" | "crop" | "cross" | "crosshair" | "crown" | "cuboid" | "cup-soda" | "curly-braces" | "currency" | "cylinder" | "dam" | "database" | "database-arrow-down" | "database-arrow-up" | "database-backup" | "database-check" | "database-minus" | "database-plus" | "database-search" | "database-x" | "database-zap" | "decimals-arrow-left" | "decimals-arrow-right" | "delete" | "dessert" | "diameter" | "diamond" | "diamond-minus" | "diamond-percent" | "diamond-plus" | "dice-1" | "dice-2" | "dice-3" | "dice-4" | "dice-5" | "dice-6" | "dices" | "diff" | "disc" | "disc-2" | "disc-3" | "disc-album" | "divide" | "divide-circle" | "divide-square" | "dna" | "dna-off" | "dock" | "dog" | "dollar-sign" | "donut" | "door-closed" | "door-closed-locked" | "door-open" | "dot" | "dot-square" | "download" | "download-cloud" | "drafting-compass" | "drama" | "drill" | "drone" | "droplet" | "droplet-off" | "droplets" | "drum" | "drumstick" | "dumbbell" | "ear" | "ear-off" | "earth" | "earth-lock" | "eclipse" | "edit" | "edit-2" | "edit-3" | "egg" | "egg-fried" | "egg-off" | "eject" | "ellipse" | "ellipsis" | "ellipsis-vertical" | "equal" | "equal-approximately" | "equal-not" | "equal-square" | "eraser" | "ethernet-port" | "euro" | "ev-charger" | "expand" | "external-link" | "eye" | "eye-closed" | "eye-dashed" | "eye-off" | "face-angry" | "face-expressionless" | "face-grinning" | "face-neutral" | "face-slightly-frowning" | "face-slightly-smiling" | "face-slightly-smiling-plus" | "factory" | "fan" | "fast-forward" | "feather" | "fence" | "ferris-wheel" | "file" | "file-archive" | "file-audio" | "file-audio-2" | "file-axis-3-d" | "file-axis-3d" | "file-badge" | "file-badge-2" | "file-bar-chart" | "file-bar-chart-2" | "file-box" | "file-braces" | "file-braces-corner" | "file-chart-column" | "file-chart-column-increasing" | "file-chart-line" | "file-chart-pie" | "file-check" | "file-check-2" | "file-check-corner" | "file-clock" | "file-code" | "file-code-2" | "file-code-corner" | "file-cog" | "file-cog-2" | "file-diff" | "file-digit" | "file-down" | "file-edit" | "file-exclamation-point" | "file-headphone" | "file-heart" | "file-image" | "file-input" | "file-json" | "file-json-2" | "file-key" | "file-key-2" | "file-line-chart" | "file-lock" | "file-lock-2" | "file-minus" | "file-minus-2" | "file-minus-corner" | "file-music" | "file-output" | "file-pen" | "file-pen-line" | "file-pie-chart" | "file-play" | "file-plus" | "file-plus-2" | "file-plus-corner" | "file-question" | "file-question-mark" | "file-scan" | "file-search" | "file-search-2" | "file-search-corner" | "file-signal" | "file-signature" | "file-sliders" | "file-spreadsheet" | "file-stack" | "file-symlink" | "file-terminal" | "file-text" | "file-type" | "file-type-2" | "file-type-corner" | "file-up" | "file-user" | "file-video" | "file-video-2" | "file-video-camera" | "file-volume" | "file-volume-2" | "file-warning" | "file-x" | "file-x-2" | "file-x-corner" | "files" | "film" | "filter" | "filter-x" | "fingerprint" | "fingerprint-pattern" | "fire-extinguisher" | "fish" | "fish-off" | "fish-symbol" | "fishing-hook" | "fishing-rod" | "flag" | "flag-off" | "flag-triangle-left" | "flag-triangle-right" | "flame" | "flame-kindling" | "flashlight" | "flashlight-off" | "flask-conical" | "flask-conical-off" | "flask-round" | "flip-horizontal" | "flip-horizontal-2" | "flip-vertical" | "flip-vertical-2" | "flower" | "flower-2" | "focus" | "fold-horizontal" | "fold-vertical" | "folder" | "folder-archive" | "folder-bookmark" | "folder-check" | "folder-clock" | "folder-closed" | "folder-code" | "folder-cog" | "folder-cog-2" | "folder-dot" | "folder-down" | "folder-edit" | "folder-git" | "folder-git-2" | "folder-heart" | "folder-input" | "folder-kanban" | "folder-key" | "folder-lock" | "folder-minus" | "folder-open" | "folder-open-dot" | "folder-output" | "folder-pen" | "folder-plus" | "folder-root" | "folder-search" | "folder-search-2" | "folder-symlink" | "folder-sync" | "folder-tree" | "folder-up" | "folder-x" | "folders" | "footprints" | "fork-knife" | "fork-knife-crossed" | "forklift" | "form" | "form-input" | "forward" | "frame" | "frown" | "fuel" | "fullscreen" | "function-square" | "funnel" | "funnel-plus" | "funnel-x" | "gallery-horizontal" | "gallery-horizontal-end" | "gallery-thumbnails" | "gallery-vertical" | "gallery-vertical-end" | "gamepad" | "gamepad-2" | "gamepad-directional" | "gantt-chart" | "gantt-chart-square" | "gauge" | "gauge-circle" | "gavel" | "gem" | "georgian-lari" | "ghost" | "gift" | "git-branch" | "git-branch-minus" | "git-branch-plus" | "git-commit" | "git-commit-horizontal" | "git-commit-vertical" | "git-compare" | "git-compare-arrows" | "git-fork" | "git-graph" | "git-merge" | "git-merge-conflict" | "git-pull-request" | "git-pull-request-arrow" | "git-pull-request-closed" | "git-pull-request-create" | "git-pull-request-create-arrow" | "git-pull-request-draft" | "glass-water" | "glasses" | "globe" | "globe-2" | "globe-check" | "globe-lock" | "globe-off" | "globe-x" | "goal" | "gpu" | "grab" | "graduation-cap" | "grape" | "grid" | "grid-2-x-2" | "grid-2-x-2-check" | "grid-2-x-2-plus" | "grid-2-x-2-x" | "grid-2x2" | "grid-2x2-check" | "grid-2x2-plus" | "grid-2x2-x" | "grid-3-x-3" | "grid-3x2" | "grid-3x3" | "grip" | "grip-horizontal" | "grip-vertical" | "group" | "guitar" | "ham" | "hamburger" | "hammer" | "hand" | "hand-coins" | "hand-fist" | "hand-grab" | "hand-heart" | "hand-helping" | "hand-metal" | "hand-platter" | "handbag" | "handshake" | "hard-drive" | "hard-drive-download" | "hard-drive-upload" | "hard-hat" | "hash" | "hat-glasses" | "haze" | "hd" | "hdmi-port" | "heading" | "heading-1" | "heading-2" | "heading-3" | "heading-4" | "heading-5" | "heading-6" | "headphone-off" | "headphones" | "headset" | "heart" | "heart-crack" | "heart-handshake" | "heart-minus" | "heart-off" | "heart-plus" | "heart-pulse" | "heart-x" | "heater" | "helicopter" | "help-circle" | "helping-hand" | "hexagon" | "highlighter" | "history" | "home" | "hop" | "hop-off" | "hospital" | "hotel" | "hourglass" | "house" | "house-heart" | "house-plug" | "house-plus" | "house-wifi" | "ice-cream" | "ice-cream-2" | "ice-cream-bowl" | "ice-cream-cone" | "id-card" | "id-card-lanyard" | "image" | "image-down" | "image-minus" | "image-off" | "image-play" | "image-plus" | "image-up" | "image-upscale" | "images" | "import" | "inbox" | "indent" | "indent-decrease" | "indent-increase" | "indian-rupee" | "infinity" | "info" | "inspect" | "inspection-panel" | "italic" | "iteration-ccw" | "iteration-cw" | "japanese-yen" | "joystick" | "kanban" | "kanban-square" | "kanban-square-dashed" | "kayak" | "key" | "key-round" | "key-square" | "keyboard" | "keyboard-music" | "keyboard-off" | "lamp" | "lamp-ceiling" | "lamp-desk" | "lamp-floor" | "lamp-wall-down" | "lamp-wall-up" | "land-plot" | "landmark" | "languages" | "laptop" | "laptop-2" | "laptop-minimal" | "laptop-minimal-check" | "lasso" | "lasso-select" | "laugh" | "layer-arrow-down" | "layer-arrow-up" | "layers" | "layers-2" | "layers-3" | "layers-arrow-down" | "layers-arrow-up" | "layers-minus" | "layers-plus" | "layout" | "layout-dashboard" | "layout-freeform" | "layout-grid" | "layout-list" | "layout-panel-left" | "layout-panel-top" | "layout-template" | "leaf" | "leafy-green" | "lectern" | "lens-concave" | "lens-convex" | "letter-text" | "library" | "library-big" | "library-square" | "life-buoy" | "ligature" | "lightbulb" | "lightbulb-off" | "line-chart" | "line-dot-right-horizontal" | "line-squiggle" | "line-style" | "link" | "link-2" | "link-2-off" | "list" | "list-check" | "list-checks" | "list-chevrons-down-up" | "list-chevrons-up-down" | "list-clock" | "list-collapse" | "list-end" | "list-filter" | "list-filter-plus" | "list-indent-decrease" | "list-indent-increase" | "list-minus" | "list-music" | "list-ordered" | "list-plus" | "list-restart" | "list-sort-ascending" | "list-sort-descending" | "list-start" | "list-todo" | "list-tree" | "list-video" | "list-x" | "loader" | "loader-2" | "loader-circle" | "loader-pinwheel" | "locate" | "locate-fixed" | "locate-off" | "location-edit" | "lock" | "lock-keyhole" | "lock-keyhole-open" | "lock-open" | "log-in" | "log-out" | "logs" | "lollipop" | "luggage" | "m-square" | "magnet" | "mail" | "mail-badge" | "mail-check" | "mail-minus" | "mail-open" | "mail-plus" | "mail-question" | "mail-question-mark" | "mail-search" | "mail-warning" | "mail-x" | "mailbox" | "mails" | "map" | "map-minus" | "map-pin" | "map-pin-check" | "map-pin-check-inside" | "map-pin-house" | "map-pin-minus" | "map-pin-minus-inside" | "map-pin-off" | "map-pin-pen" | "map-pin-plus" | "map-pin-plus-inside" | "map-pin-search" | "map-pin-x" | "map-pin-x-inside" | "map-pinned" | "map-plus" | "mars" | "mars-stroke" | "martini" | "maximize" | "maximize-2" | "medal" | "megaphone" | "megaphone-off" | "meh" | "memory-stick" | "menu" | "menu-square" | "merge" | "message-circle" | "message-circle-check" | "message-circle-code" | "message-circle-dashed" | "message-circle-heart" | "message-circle-more" | "message-circle-off" | "message-circle-plus" | "message-circle-question" | "message-circle-question-mark" | "message-circle-reply" | "message-circle-warning" | "message-circle-x" | "message-square" | "message-square-check" | "message-square-code" | "message-square-dashed" | "message-square-diff" | "message-square-dot" | "message-square-heart" | "message-square-lock" | "message-square-more" | "message-square-off" | "message-square-plus" | "message-square-quote" | "message-square-reply" | "message-square-share" | "message-square-text" | "message-square-warning" | "message-square-x" | "messages-square" | "metronome" | "mic" | "mic-2" | "mic-audio-lines" | "mic-off" | "mic-signal" | "mic-vocal" | "microchip" | "microscope" | "microwave" | "midi-port" | "milestone" | "milk" | "milk-off" | "minimize" | "minimize-2" | "minus" | "minus-circle" | "minus-square" | "mirror-rectangular" | "mirror-round" | "monitor" | "monitor-check" | "monitor-cloud" | "monitor-cog" | "monitor-dot" | "monitor-down" | "monitor-off" | "monitor-pause" | "monitor-play" | "monitor-smartphone" | "monitor-speaker" | "monitor-stop" | "monitor-up" | "monitor-x" | "moon" | "moon-star" | "mop" | "mop-sparkles" | "more-horizontal" | "more-vertical" | "mosque" | "motorbike" | "mountain" | "mountain-snow" | "mouse" | "mouse-left" | "mouse-off" | "mouse-pointer" | "mouse-pointer-2" | "mouse-pointer-2-off" | "mouse-pointer-ban" | "mouse-pointer-click" | "mouse-pointer-square-dashed" | "mouse-right" | "move" | "move-3-d" | "move-3d" | "move-diagonal" | "move-diagonal-2" | "move-down" | "move-down-left" | "move-down-right" | "move-horizontal" | "move-left" | "move-right" | "move-up" | "move-up-left" | "move-up-right" | "move-vertical" | "music" | "music-2" | "music-3" | "music-4" | "navigation" | "navigation-2" | "navigation-2-off" | "navigation-off" | "network" | "newspaper" | "nfc" | "non-binary" | "notebook" | "notebook-pen" | "notebook-tabs" | "notebook-text" | "notepad-text" | "notepad-text-dashed" | "nut" | "nut-off" | "octagon" | "octagon-alert" | "octagon-minus" | "octagon-pause" | "octagon-x" | "omega" | "option" | "orbit" | "origami" | "outdent" | "package" | "package-2" | "package-check" | "package-minus" | "package-open" | "package-plus" | "package-search" | "package-x" | "paint-bucket" | "paint-roller" | "paintbrush" | "paintbrush-2" | "paintbrush-vertical" | "palette" | "palmtree" | "panda" | "panel-bottom" | "panel-bottom-close" | "panel-bottom-dashed" | "panel-bottom-inactive" | "panel-bottom-open" | "panel-left" | "panel-left-close" | "panel-left-dashed" | "panel-left-inactive" | "panel-left-open" | "panel-left-right-dashed" | "panel-right" | "panel-right-close" | "panel-right-dashed" | "panel-right-inactive" | "panel-right-open" | "panel-top" | "panel-top-bottom-dashed" | "panel-top-close" | "panel-top-dashed" | "panel-top-inactive" | "panel-top-open" | "panels-left-bottom" | "panels-left-right" | "panels-right-bottom" | "panels-top-bottom" | "panels-top-left" | "paper-bag" | "paperclip" | "parasol" | "parentheses" | "parking-circle" | "parking-circle-off" | "parking-meter" | "parking-square" | "parking-square-off" | "party-popper" | "pause" | "pause-circle" | "pause-octagon" | "paw-print" | "pc-case" | "pen" | "pen-box" | "pen-line" | "pen-off" | "pen-square" | "pen-tool" | "pencil" | "pencil-line" | "pencil-off" | "pencil-ruler" | "pencil-sparkles" | "pentagon" | "percent" | "percent-circle" | "percent-diamond" | "percent-square" | "person-standing" | "phi" | "philippine-peso" | "phone" | "phone-call" | "phone-forwarded" | "phone-incoming" | "phone-missed" | "phone-off" | "phone-outgoing" | "pi" | "pi-square" | "piano" | "pickaxe" | "picture-in-picture" | "picture-in-picture-2" | "pie-chart" | "piggy-bank" | "pilcrow" | "pilcrow-left" | "pilcrow-right" | "pilcrow-square" | "pill" | "pill-bottle" | "pin" | "pin-off" | "pipette" | "pizza" | "plane" | "plane-landing" | "plane-takeoff" | "play" | "play-circle" | "play-off" | "play-square" | "plug" | "plug-2" | "plug-zap" | "plug-zap-2" | "plus" | "plus-circle" | "plus-square" | "pocket-knife" | "podcast" | "podium" | "pointer" | "pointer-off" | "popcorn" | "popsicle" | "pound-sterling" | "power" | "power-circle" | "power-off" | "power-square" | "presentation" | "printer" | "printer-check" | "printer-x" | "projector" | "proportions" | "puzzle" | "pyramid" | "qr-code" | "quote" | "rabbit" | "radar" | "radiation" | "radical" | "radio" | "radio-off" | "radio-receiver" | "radio-tower" | "radius" | "rainbow" | "rat" | "ratio" | "receipt" | "receipt-cent" | "receipt-euro" | "receipt-indian-rupee" | "receipt-japanese-yen" | "receipt-pound-sterling" | "receipt-russian-ruble" | "receipt-swiss-franc" | "receipt-text" | "receipt-turkish-lira" | "rectangle-circle" | "rectangle-ellipsis" | "rectangle-goggles" | "rectangle-horizontal" | "rectangle-vertical" | "recycle" | "redo" | "redo-2" | "redo-dot" | "refresh-ccw" | "refresh-ccw-dot" | "refresh-cw" | "refresh-cw-off" | "refrigerator" | "regex" | "remove-formatting" | "repeat" | "repeat-1" | "repeat-2" | "repeat-off" | "replace" | "replace-all" | "reply" | "reply-all" | "rewind" | "ribbon" | "road" | "rocket" | "rocking-chair" | "roller-coaster" | "rose" | "rotate-3-d" | "rotate-3d" | "rotate-ccw" | "rotate-ccw-clock" | "rotate-ccw-key" | "rotate-ccw-square" | "rotate-cw" | "rotate-cw-fading-clock" | "rotate-cw-square" | "route" | "route-off" | "router" | "rows" | "rows-2" | "rows-3" | "rows-4" | "rss" | "ruler" | "ruler-dimension-line" | "russian-ruble" | "sailboat" | "salad" | "sandwich" | "satellite" | "satellite-dish" | "saudi-riyal" | "save" | "save-all" | "save-check" | "save-off" | "save-pen" | "save-plus" | "scale" | "scale-3-d" | "scale-3d" | "scaling" | "scan" | "scan-barcode" | "scan-box" | "scan-eye" | "scan-face" | "scan-heart" | "scan-line" | "scan-qr-code" | "scan-search" | "scan-square" | "scan-text" | "scatter-chart" | "school" | "school-2" | "scissors" | "scissors-line-dashed" | "scissors-square" | "scissors-square-dashed-bottom" | "scooter" | "screen-share" | "screen-share-off" | "scroll" | "scroll-text" | "search" | "search-alert" | "search-check" | "search-code" | "search-slash" | "search-x" | "section" | "send" | "send-horizonal" | "send-horizontal" | "send-to-back" | "separator-horizontal" | "separator-vertical" | "server" | "server-cog" | "server-crash" | "server-off" | "server-plus" | "settings" | "settings-2" | "shapes" | "share" | "share-2" | "sheet" | "shell" | "shelving-unit" | "shield" | "shield-alert" | "shield-ban" | "shield-check" | "shield-close" | "shield-cog" | "shield-cog-corner" | "shield-ellipsis" | "shield-half" | "shield-keyhole" | "shield-lock" | "shield-minus" | "shield-off" | "shield-plus" | "shield-question" | "shield-question-mark" | "shield-user" | "shield-x" | "ship" | "ship-wheel" | "shirt" | "shopping-bag" | "shopping-basket" | "shopping-cart" | "shovel" | "shower-head" | "shredder" | "shrimp" | "shrink" | "shrub" | "shuffle" | "sidebar" | "sidebar-close" | "sidebar-open" | "sigma" | "sigma-square" | "signal" | "signal-high" | "signal-low" | "signal-medium" | "signal-zero" | "signature" | "signpost" | "signpost-big" | "siren" | "skip-back" | "skip-forward" | "skull" | "slash" | "slash-square" | "slice" | "sliders" | "sliders-horizontal" | "sliders-vertical" | "smartphone" | "smartphone-charging" | "smartphone-nfc" | "smile" | "smile-plus" | "snail" | "snowflake" | "soap-dispenser-droplet" | "sofa" | "solar-panel" | "sort-asc" | "sort-desc" | "soup" | "space" | "spade" | "sparkle" | "sparkles" | "speaker" | "speech" | "spell-check" | "spell-check-2" | "spline" | "spline-pointer" | "split" | "split-square-horizontal" | "split-square-vertical" | "spool" | "sport-shoe" | "spotlight" | "spray-can" | "sprout" | "square" | "square-activity" | "square-arrow-down" | "square-arrow-down-left" | "square-arrow-down-right" | "square-arrow-left" | "square-arrow-out-down-left" | "square-arrow-out-down-right" | "square-arrow-out-up-left" | "square-arrow-out-up-right" | "square-arrow-right" | "square-arrow-right-enter" | "square-arrow-right-exit" | "square-arrow-up" | "square-arrow-up-left" | "square-arrow-up-right" | "square-asterisk" | "square-bottom-dashed-scissors" | "square-centerline-dashed-horizontal" | "square-centerline-dashed-vertical" | "square-chart-gantt" | "square-check" | "square-check-big" | "square-chevron-down" | "square-chevron-left" | "square-chevron-right" | "square-chevron-up" | "square-code" | "square-dashed" | "square-dashed-bottom" | "square-dashed-bottom-code" | "square-dashed-kanban" | "square-dashed-mouse-pointer" | "square-dashed-text" | "square-dashed-top-solid" | "square-dimensions" | "square-divide" | "square-dot" | "square-equal" | "square-function" | "square-gantt-chart" | "square-kanban" | "square-library" | "square-m" | "square-menu" | "square-minus" | "square-mouse-pointer" | "square-off" | "square-parking" | "square-parking-off" | "square-pause" | "square-pen" | "square-percent" | "square-pi" | "square-pilcrow" | "square-play" | "square-plus" | "square-power" | "square-radical" | "square-round-corner" | "square-scissors" | "square-sigma" | "square-slash" | "square-split-horizontal" | "square-split-vertical" | "square-square" | "square-stack" | "square-star" | "square-stop" | "square-terminal" | "square-text" | "square-user" | "square-user-round" | "square-x" | "squares-exclude" | "squares-intersect" | "squares-subtract" | "squares-unite" | "squircle" | "squircle-dashed" | "squirrel" | "stamp" | "star" | "star-check" | "star-half" | "star-minus" | "star-off" | "star-plus" | "star-x" | "stars" | "step-back" | "step-forward" | "stethoscope" | "sticker" | "sticky-note" | "sticky-note-check" | "sticky-note-minus" | "sticky-note-off" | "sticky-note-plus" | "sticky-note-x" | "sticky-notes" | "stone" | "stop-circle" | "store" | "stretch-horizontal" | "stretch-vertical" | "strikethrough" | "subscript" | "subtitles" | "summary" | "sun" | "sun-dim" | "sun-medium" | "sun-moon" | "sun-snow" | "sunrise" | "sunset" | "superscript" | "swatch-book" | "swiss-franc" | "switch-camera" | "sword" | "swords" | "syringe" | "table" | "table-2" | "table-cells-merge" | "table-cells-split" | "table-columns-split" | "table-config" | "table-of-contents" | "table-properties" | "table-rows-split" | "tablet" | "tablet-smartphone" | "tablets" | "tag" | "tag-plus" | "tag-x" | "tags" | "tally-1" | "tally-2" | "tally-3" | "tally-4" | "tally-5" | "tangent" | "target" | "telescope" | "tent" | "tent-tree" | "terminal" | "terminal-square" | "test-tube" | "test-tube-2" | "test-tube-diagonal" | "test-tubes" | "text" | "text-align-center" | "text-align-end" | "text-align-justify" | "text-align-start" | "text-cursor" | "text-cursor-input" | "text-initial" | "text-quote" | "text-search" | "text-select" | "text-selection" | "text-wrap" | "theater" | "thermometer" | "thermometer-snowflake" | "thermometer-sun" | "thumbs-down" | "thumbs-up" | "ticket" | "ticket-check" | "ticket-minus" | "ticket-percent" | "ticket-plus" | "ticket-slash" | "ticket-x" | "tickets" | "tickets-plane" | "timeline" | "timer" | "timer-off" | "timer-reset" | "toggle-left" | "toggle-right" | "toilet" | "tool-case" | "toolbox" | "tornado" | "torus" | "touchpad" | "touchpad-off" | "towel-rack" | "tower-control" | "toy-brick" | "tractor" | "traffic-cone" | "train" | "train-front" | "train-front-tunnel" | "train-track" | "tram-front" | "transgender" | "trash" | "trash-2" | "tree-deciduous" | "tree-palm" | "tree-pine" | "trees" | "trending-down" | "trending-up" | "trending-up-down" | "triangle" | "triangle-alert" | "triangle-dashed" | "triangle-right" | "trophy" | "truck" | "truck-electric" | "turkish-lira" | "turntable" | "turtle" | "tv" | "tv-2" | "tv-minimal" | "tv-minimal-play" | "type" | "type-outline" | "umbrella" | "umbrella-off" | "underline" | "undo" | "undo-2" | "undo-dot" | "unfold-horizontal" | "unfold-vertical" | "ungroup" | "university" | "unlink" | "unlink-2" | "unlock" | "unlock-keyhole" | "unplug" | "upload" | "upload-cloud" | "usb" | "usb-c-port" | "user" | "user-2" | "user-check" | "user-check-2" | "user-circle" | "user-circle-2" | "user-cog" | "user-cog-2" | "user-key" | "user-lock" | "user-minus" | "user-minus-2" | "user-pen" | "user-plus" | "user-plus-2" | "user-round" | "user-round-arrow-left" | "user-round-check" | "user-round-cog" | "user-round-key" | "user-round-minus" | "user-round-pen" | "user-round-plus" | "user-round-search" | "user-round-x" | "user-search" | "user-shield" | "user-square" | "user-square-2" | "user-star" | "user-x" | "user-x-2" | "users" | "users-2" | "users-round" | "utensils" | "utensils-crossed" | "utility-pole" | "van" | "variable" | "vault" | "vector-square" | "vegan" | "venetian-mask" | "venus" | "venus-and-mars" | "verified" | "vibrate" | "vibrate-off" | "video" | "video-off" | "videotape" | "view" | "voicemail" | "volleyball" | "volume" | "volume-1" | "volume-2" | "volume-off" | "volume-x" | "vote" | "wallet" | "wallet-2" | "wallet-cards" | "wallet-minimal" | "wallpaper" | "wand" | "wand-2" | "wand-sparkles" | "warehouse" | "washing-machine" | "watch" | "waves" | "waves-arrow-down" | "waves-arrow-up" | "waves-horizontal" | "waves-ladder" | "waves-vertical" | "waypoints" | "webcam" | "webcam-off" | "webhook" | "webhook-off" | "weight" | "weight-tilde" | "wheat" | "wheat-off" | "whole-word" | "wifi" | "wifi-cog" | "wifi-high" | "wifi-low" | "wifi-off" | "wifi-pen" | "wifi-sync" | "wifi-zero" | "wind" | "wind-arrow-down" | "wine" | "wine-off" | "workflow" | "worm" | "wrap-text" | "wrench" | "wrench-off" | "x" | "x-circle" | "x-line-top" | "x-octagon" | "x-square" | "zap" | "zap-off" | "zodiac-aquarius" | "zodiac-aries" | "zodiac-cancer" | "zodiac-capricorn" | "zodiac-gemini" | "zodiac-leo" | "zodiac-libra" | "zodiac-ophiuchus" | "zodiac-pisces" | "zodiac-sagittarius" | "zodiac-scorpio" | "zodiac-taurus" | "zodiac-virgo" | "zoom-in" | "zoom-out";
9
9
  export type JsonValue = null | boolean | number | string | JsonValue[] | { [key: string]: JsonValue };
10
10
  export interface BannerAction { label: string; variant: "primary" | "secondary"; onTrigger: () => void | Promise<void>; }
11
+ export interface ConversationProps {
12
+ agentId: string;
13
+ scenario: string;
14
+ context: string;
15
+ title?: string;
16
+ placeholder?: string;
17
+ onConversationCreated?: (event: import("./conversation.js").ConversationCreatedEvent) => void | Promise<void>;
18
+ onTurnStarted?: (event: import("./conversation.js").TurnStartedEvent) => void | Promise<void>;
19
+ onTurnFinished?: (event: import("./conversation.js").TurnFinishedEvent) => void | Promise<void>;
20
+ onToolCall?: (event: import("./conversation.js").ToolCallEvent) => void | Promise<void>;
21
+ onError?: (event: import("./conversation.js").ConversationErrorEvent) => void | Promise<void>;
22
+ style?: AppStyle;
23
+ }
24
+ export const Conversation: ComponentType<ConversationProps> & { Root: typeof ConversationRoot; Messages: typeof ConversationMessages; Composer: typeof ConversationComposer; History: typeof ConversationHistory; NewButton: typeof ConversationNewButton; StopButton: typeof ConversationStopButton; Status: typeof ConversationStatus };
25
+ export interface ConversationRootProps {
26
+ agentId: string;
27
+ scenario: string;
28
+ context: string;
29
+ title?: string;
30
+ placeholder?: string;
31
+ onConversationCreated?: (event: import("./conversation.js").ConversationCreatedEvent) => void | Promise<void>;
32
+ onTurnStarted?: (event: import("./conversation.js").TurnStartedEvent) => void | Promise<void>;
33
+ onTurnFinished?: (event: import("./conversation.js").TurnFinishedEvent) => void | Promise<void>;
34
+ onToolCall?: (event: import("./conversation.js").ToolCallEvent) => void | Promise<void>;
35
+ onError?: (event: import("./conversation.js").ConversationErrorEvent) => void | Promise<void>;
36
+ style?: AppStyle;
37
+ children?: ReactNode;
38
+ }
39
+ export const ConversationRoot: ComponentType<ConversationRootProps>;
40
+ export interface ConversationMessagesProps {
41
+ fill?: boolean;
42
+ emptyTitle?: string;
43
+ emptyDescription?: string;
44
+ }
45
+ export const ConversationMessages: ComponentType<ConversationMessagesProps>;
46
+ export interface ConversationComposerProps {
47
+ label?: string;
48
+ placeholder?: string;
49
+ }
50
+ export const ConversationComposer: ComponentType<ConversationComposerProps>;
51
+ export interface ConversationHistoryProps {
52
+ label?: string;
53
+ }
54
+ export const ConversationHistory: ComponentType<ConversationHistoryProps>;
55
+ export interface ConversationNewButtonProps {
56
+ label?: string;
57
+ iconOnly?: boolean;
58
+ }
59
+ export const ConversationNewButton: ComponentType<ConversationNewButtonProps>;
60
+ export interface ConversationStopButtonProps {
61
+ label?: string;
62
+ }
63
+ export const ConversationStopButton: ComponentType<ConversationStopButtonProps>;
64
+ export interface ConversationStatusProps {
65
+
66
+ }
67
+ export const ConversationStatus: ComponentType<ConversationStatusProps>;
11
68
  export interface NativeDOMProps {
12
69
  label: string;
13
70
  styles?: string;
@@ -31,6 +88,10 @@ export interface StackProps {
31
88
  }
32
89
  export const Stack: ComponentType<StackProps>;
33
90
  export interface SplitLayoutProps {
91
+ resizable?: boolean;
92
+ fillHeight?: boolean;
93
+ asideHidden?: boolean;
94
+ persistenceKey?: string;
34
95
  children?: ReactNode;
35
96
  }
36
97
  export const SplitLayout: ComponentType<SplitLayoutProps> & { Main: typeof SplitLayoutMain; Aside: typeof SplitLayoutAside; Actions: typeof SplitLayoutActions };
@@ -339,5 +400,6 @@ export interface WidgetDecorationProps {
339
400
  children: ReactNode;
340
401
  }
341
402
  export const WidgetDecoration: ComponentType<WidgetDecorationProps>;
403
+ export type { ConversationCreatedEvent, TurnStartedEvent, TurnFinishedEvent, ToolCallEvent, ConversationErrorEvent } from "./conversation.js";
342
404
  export const Typography: { Title: typeof TypographyTitle; Body: typeof TypographyBody; Caption: typeof TypographyCaption };
343
405
  export const Widget: { TextWidget: typeof WidgetTextWidget; Title: typeof WidgetTitle; Text: { Primary: typeof WidgetTextPrimary; Secondary: typeof WidgetTextSecondary }; Loading: typeof WidgetLoading; Decoration: typeof WidgetDecoration };
package/src/ui/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // 此文件由 scripts/generate-components.mjs 生成,请修改产品能力声明。
2
2
  import * as React from "react";
3
3
  import { createUiComponents } from "./factory.js";
4
- export const { NativeDOM, Icon, Stack, SplitLayout, SplitLayoutMain, SplitLayoutAside, SplitLayoutActions, Section, Banner, Badge, LoadingState, Skeleton, Card, Grid, EmptyState, ZhikeLink, Button, SearchInput, FilterSelect, Switch, LookupCell, Tabs, TabsList, TabsTrigger, TabsContent, Table, TableHeader, TableBody, TableRow, TableCell, Avatar, DescriptionList, DescriptionListItem, DescriptionListMenu, DescriptionListMenuItem, Divider, Chart, DashboardSurface, DashboardLayout, DashboardPanel, MetricReadout, AnalysisView, Json, Link, StatusBadge, TextBlock, TypographyTitle, TypographyBody, TypographyCaption, TableHeaderCell, WidgetTextWidget, WidgetTitle, WidgetTextPrimary, WidgetTextSecondary, WidgetLoading, WidgetDecoration, Typography, Widget } = createUiComponents(React);
4
+ export const { Conversation, ConversationRoot, ConversationMessages, ConversationComposer, ConversationHistory, ConversationNewButton, ConversationStopButton, ConversationStatus, NativeDOM, Icon, Stack, SplitLayout, SplitLayoutMain, SplitLayoutAside, SplitLayoutActions, Section, Banner, Badge, LoadingState, Skeleton, Card, Grid, EmptyState, ZhikeLink, Button, SearchInput, FilterSelect, Switch, LookupCell, Tabs, TabsList, TabsTrigger, TabsContent, Table, TableHeader, TableBody, TableRow, TableCell, Avatar, DescriptionList, DescriptionListItem, DescriptionListMenu, DescriptionListMenuItem, Divider, Chart, DashboardSurface, DashboardLayout, DashboardPanel, MetricReadout, AnalysisView, Json, Link, StatusBadge, TextBlock, TypographyTitle, TypographyBody, TypographyCaption, TableHeaderCell, WidgetTextWidget, WidgetTitle, WidgetTextPrimary, WidgetTextSecondary, WidgetLoading, WidgetDecoration, Typography, Widget } = createUiComponents(React);