nolo-cli 0.1.18 → 0.1.20

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.
Files changed (111) hide show
  1. package/README.md +9 -1
  2. package/agent-runtime/agentConfigOptions.ts +12 -0
  3. package/agent-runtime/agentRecordConfig.ts +99 -0
  4. package/agent-runtime/agentRecordKeys.ts +14 -0
  5. package/agent-runtime/dialogMessageRecord.ts +16 -0
  6. package/agent-runtime/dialogWritePlan.ts +130 -0
  7. package/agent-runtime/hostAdapter.ts +14 -0
  8. package/agent-runtime/hybridRecordStore.ts +147 -0
  9. package/agent-runtime/index.ts +69 -0
  10. package/agent-runtime/localLoop.ts +78 -6
  11. package/agent-runtime/localToolPolicy.ts +130 -0
  12. package/agent-runtime/localWorkspaceTools.ts +1532 -0
  13. package/agent-runtime/openAiCompatibleProvider.ts +70 -0
  14. package/agent-runtime/openAiCompatibleProviderConfig.ts +38 -0
  15. package/agent-runtime/platformChatProvider.ts +241 -0
  16. package/agent-runtime/taskWorkspace.ts +193 -0
  17. package/agent-runtime/types.ts +2 -0
  18. package/agent-runtime/workspaceSession.ts +76 -0
  19. package/agentAliases.ts +37 -0
  20. package/agentPullCommand.ts +1 -1
  21. package/agentRunCommand.ts +289 -54
  22. package/agentRuntimeCommands.ts +354 -164
  23. package/agentRuntimeLocal.ts +38 -0
  24. package/ai/agent/agentSlice.ts +10 -0
  25. package/ai/agent/buildEditingContext.ts +5 -0
  26. package/ai/agent/buildSystemPrompt.ts +41 -18
  27. package/ai/agent/canvasEditingContext.ts +49 -0
  28. package/ai/agent/cliExecutor.ts +15 -4
  29. package/ai/agent/createAgentSchema.ts +2 -0
  30. package/ai/agent/executeToolCall.ts +3 -2
  31. package/ai/agent/hooks/usePublicAgents.ts +6 -0
  32. package/ai/agent/pageBuilderHandoffRules.ts +75 -0
  33. package/ai/agent/runAgentClientLoop.ts +4 -1
  34. package/ai/agent/runtimeGuidance.ts +19 -0
  35. package/ai/agent/server/fetchPublicAgents.ts +51 -1
  36. package/ai/agent/streamAgentChatTurn.ts +20 -2
  37. package/ai/agent/streamAgentChatTurnUtils.ts +60 -16
  38. package/ai/chat/accumulateToolCallChunks.ts +40 -9
  39. package/ai/chat/parseApiError.ts +3 -0
  40. package/ai/chat/sendOpenAICompletionsRequest.native.ts +23 -10
  41. package/ai/chat/sendOpenAICompletionsRequest.ts +13 -1
  42. package/ai/chat/updateTotalUsage.ts +26 -9
  43. package/ai/llm/deepinfra.ts +51 -0
  44. package/ai/llm/getPricing.ts +6 -0
  45. package/ai/llm/kimi.ts +2 -0
  46. package/ai/llm/openrouterModels.ts +0 -135
  47. package/ai/llm/providers.ts +1 -0
  48. package/ai/llm/types.ts +8 -0
  49. package/ai/taskRun/taskRunProtocol.ts +823 -0
  50. package/ai/token/calculatePrice.ts +30 -0
  51. package/ai/token/externalToolCost.ts +49 -29
  52. package/ai/token/prepareTokenUsageData.ts +6 -1
  53. package/ai/token/serverTokenWriter.ts +4 -2
  54. package/ai/tools/agent/agentTools.ts +21 -0
  55. package/ai/tools/agent/presets/appBuilderPreset.ts +7 -0
  56. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -1
  57. package/ai/tools/agent/taskRunTool.ts +112 -0
  58. package/ai/tools/applyEditTool.ts +6 -3
  59. package/ai/tools/applyLineEditsTool.ts +6 -3
  60. package/ai/tools/checkEnvTool.ts +14 -9
  61. package/ai/tools/codeSearchTool.ts +17 -5
  62. package/ai/tools/execBashTool.ts +33 -29
  63. package/ai/tools/fetchWebpageSupport.ts +24 -0
  64. package/ai/tools/fetchWebpageTool.ts +18 -5
  65. package/ai/tools/index.ts +158 -0
  66. package/ai/tools/jdProductScraperTool.ts +821 -0
  67. package/ai/tools/listFilesTool.ts +6 -3
  68. package/ai/tools/localFilesTool.ts +200 -0
  69. package/ai/tools/readFileTool.ts +6 -3
  70. package/ai/tools/searchRepoTool.ts +6 -3
  71. package/ai/tools/table/rowTools.ts +6 -1
  72. package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
  73. package/ai/tools/toolApiClient.ts +20 -6
  74. package/ai/tools/wereadGatewayTool.ts +152 -0
  75. package/ai/tools/writeFileTool.ts +6 -3
  76. package/client/agentConfigResolver.test.ts +70 -0
  77. package/client/agentConfigResolver.ts +1 -0
  78. package/client/agentRun.test.ts +361 -7
  79. package/client/agentRun.ts +449 -63
  80. package/client/hybridRecordStore.test.ts +115 -0
  81. package/client/hybridRecordStore.ts +41 -0
  82. package/client/localAgentRecords.test.ts +27 -0
  83. package/client/localAgentRecords.ts +7 -0
  84. package/client/localDialogRecords.test.ts +124 -0
  85. package/client/localDialogRecords.ts +30 -0
  86. package/client/localProviderResolver.test.ts +78 -0
  87. package/client/localProviderResolver.ts +1 -0
  88. package/client/localRuntimeAdapter.test.ts +813 -20
  89. package/client/localRuntimeAdapter.ts +279 -232
  90. package/client/localRuntimeDryRun.test.ts +116 -0
  91. package/client/localToolPolicy.ts +8 -81
  92. package/client/taskRunPrompt.ts +26 -0
  93. package/client/taskWorktree.ts +8 -0
  94. package/client/workspaceSession.test.ts +57 -0
  95. package/client/workspaceSession.ts +11 -0
  96. package/commandRegistry.ts +23 -6
  97. package/connectorRunArtifact.ts +121 -0
  98. package/database/actions/write.ts +16 -2
  99. package/database/hooks/useUserData.ts +9 -3
  100. package/database/server/dataHandlers.ts +18 -20
  101. package/database/server/emailRepository.ts +3 -3
  102. package/database/server/patch.ts +18 -10
  103. package/database/server/query.ts +43 -4
  104. package/database/server/read.ts +24 -38
  105. package/database/server/recordIdentity.ts +100 -0
  106. package/database/server/write.ts +21 -25
  107. package/index.ts +70 -33
  108. package/machineCommands.ts +318 -144
  109. package/package.json +4 -1
  110. package/tableCommands.ts +181 -0
  111. package/taskRunCommand.ts +237 -0
@@ -1,7 +1,45 @@
1
+ export {
2
+ buildOpenAiCompatibleChatCompletionRequest,
3
+ buildPlatformChatCompletionRequest,
4
+ canUsePlatformChatProvider,
5
+ hasDirectOpenAiCompatibleProvider,
6
+ createHybridRecordStore,
7
+ executeLocalToolWithPolicy,
8
+ activateWorkspaceSession,
9
+ createWorkspaceSession,
10
+ buildAgentRuntimeAgentLookupKeys,
11
+ buildAgentRuntimeDialogWritePlan,
12
+ dialogMessageRecordToAgentRuntimeMessage,
13
+ formatWorkspaceSessionActivation,
14
+ pickAgentRuntimeInferenceOptions,
15
+ parseOpenAiCompatibleChatCompletionResponse,
16
+ parsePlatformChatCompletionData,
17
+ parsePlatformChatCompletionResponse,
18
+ resolveAgentRuntimeConfigFromRecord,
19
+ resolvePlatformChatProviderConfig,
20
+ resolveLocalToolPolicy,
21
+ resolveOpenAiCompatibleProviderConfig,
22
+ shouldUsePlatformChatProvider,
23
+ shouldCacheHybridRemoteRecord,
24
+ shouldFetchAgentRuntimeRecordRemotely,
25
+ } from "./agent-runtime";
1
26
  export { runLocalAgentTurn } from "./agent-runtime/localLoop";
27
+ export {
28
+ buildLocalWorkspaceOpenAiTools,
29
+ buildLocalWorkspacePolicyToolNames,
30
+ buildLocalWorkspaceToolset,
31
+ createLocalWorkspaceToolExecutors,
32
+ } from "./agent-runtime/localWorkspaceTools";
2
33
  export type {
3
34
  AgentRuntimeAgentConfig,
4
35
  AgentRuntimeHostAdapter,
5
36
  AgentRuntimeRequestedMode,
6
37
  AgentRuntimeSaveTurnInput,
38
+ HybridRecordKvDb,
39
+ HybridRecordStore,
40
+ LocalToolPolicyDecision,
41
+ PreparedTaskWorkspace,
42
+ PrepareTaskWorkspace,
43
+ WorkspaceSession,
44
+ WorkspaceSessionMode,
7
45
  } from "./agent-runtime";
@@ -193,6 +193,16 @@ const processAgentUpdateChanges = (
193
193
  if ("useServerProxy" in data && data.useServerProxy !== undefined) {
194
194
  changes.useServerProxy = !!data.useServerProxy;
195
195
  }
196
+ if ("sharingLevel" in data) {
197
+ const sharingLevel = (data as any).sharingLevel;
198
+ if (
199
+ sharingLevel === "default" ||
200
+ sharingLevel === "split" ||
201
+ sharingLevel === "full"
202
+ ) {
203
+ changes.sharingLevel = sharingLevel;
204
+ }
205
+ }
196
206
 
197
207
  // greeting / tools 直接透传
198
208
  if ("greeting" in data) {
@@ -2,6 +2,7 @@
2
2
 
3
3
  import type { RootState } from "app/store";
4
4
  import type { AgentRuntimeOptions } from "./types";
5
+ import { buildCanvasNodeEditingContextSummary } from "./canvasEditingContext";
5
6
  import { selectCurrentTable, selectTableRows } from "render/table/tableSlice";
6
7
  import { selectDoc } from "render/page/docSlice";
7
8
 
@@ -368,6 +369,10 @@ export const buildEditingContextSummary = (
368
369
  ].join("\n");
369
370
  }
370
371
 
372
+ if (targetKind === "canvas_node") {
373
+ return buildCanvasNodeEditingContextSummary(runtimeOptions);
374
+ }
375
+
371
376
  // 其它 kind 以后按需扩展
372
377
  return null;
373
378
  };
@@ -7,6 +7,7 @@ import { Agent } from "app/types";
7
7
  import { buildSkillGuidancePromptBlock } from "ai/skills/referenceRuntime";
8
8
  import { buildRuntimeGuidanceBlocks } from "ai/agent/runtimeGuidance";
9
9
  import { canonicalizeToolNames } from "ai/tools/toolNameAliases";
10
+ import { PAGE_BUILDER_HANDOFF_INSTRUCTIONS } from "./pageBuilderHandoffRules";
10
11
  import { compileContextLayers, type CompiledContext } from "./contextCompiler";
11
12
  import { Contexts } from "../types";
12
13
 
@@ -31,20 +32,18 @@ const MENU_USAGE_INSTRUCTIONS = `--- 交互说明(菜单与快捷入口) ---
31
32
 
32
33
  对你来说:
33
34
  - 不要假设或讨论用户点击了哪些按钮、使用了什么菜单,也不要在回复中提到"按钮""菜单""界面""第几个选项"等字样。
34
- - 把所有收到的消息都当作用户刚刚输入的一句话,根据这句话的内容直接理解和回答即可。
35
- - 当你希望用户在几个互斥选项之间做选择时,可以调用 ui_ask_choice 工具;用户的选择会以一条新的自然语言消息的形式出现在后续对话中。`;
35
+ - 把所有收到的消息都当作用户刚刚输入的一句话,根据这句话的内容直接理解和回答即可。`;
36
36
 
37
37
  // ============================================================================
38
38
  // 网页访问(有 exa_search 工具时注入)
39
39
  // ============================================================================
40
40
 
41
41
  const WEBPAGE_ACCESS_INSTRUCTIONS = `--- 网页访问能力 (Web Access) ---
42
- 你拥有访问互联网的强大能力。在需要获取外部信息时,请遵循以下由简入繁的策略:
42
+ 在需要获取外部信息时,请遵循以下由简入繁的策略:
43
43
 
44
44
  0. **如果用户已经给了明确 URL,先直接抓这些 URL**
45
45
  - 不要先搜索,不要先猜备用网址,不要忽略用户给的链接。
46
46
  - 用户明确给出的 URL,默认就是本次任务的最高优先级网页真值来源。
47
- - 如果 URL 是 X/Twitter status 链接(例如 \`x.com/.../status/...\` 或 \`twitter.com/.../status/...\`),并且你拥有 \`read_x_post\` 工具,应优先调用 \`read_x_post\`,不要先用搜索、镜像站或通用网页抓取替代。
48
47
  - 如果任务是“根据这些网页更新代码 / 文档 / 配置”,先逐个 fetch,再基于抓到的内容提取字段。
49
48
  - 只要这些 URL 能正常抓取,就不要再调用 exa_search 搜索“更权威”的来源,也不要自行切换到其他站点或镜像页。
50
49
  - 只有在用户给的 URL 抓取失败、页面明显缺字段、或页面内容与任务不匹配时,才允许额外搜索;如果发生这种降级,最终回复里要明确说明原因。
@@ -71,6 +70,20 @@ const WEBPAGE_ACCESS_INSTRUCTIONS = `--- 网页访问能力 (Web Access) ---
71
70
  3. **精准数据抓取 (Scrapers)**
72
71
  - 如果用户明确需要 YouTube 视频信息、亚马逊商品数据、Google 搜索结果,请直接使用对应的专用 Scraper 工具 (youtubeScraper, amazonProductScraper 等),效果比自己去爬网页更好。`;
73
72
 
73
+ // ============================================================================
74
+ // 本地文件整理(有 local desktop file tools 时注入)
75
+ // ============================================================================
76
+
77
+ const LOCAL_FILE_ORGANIZATION_INSTRUCTIONS = `--- 本地文件整理能力 ---
78
+ 你可以帮助用户整理 Windows 桌面客户端里已授权的本地文件夹。必须遵守:
79
+
80
+ - 只能访问用户已授权的本地文件夹,所有路径都必须是授权根目录内的相对路径。
81
+ - 开始整理前,先列目录和读取必要文件,理解现状后再提出计划。
82
+ - 对移动、重命名、写入、删除等改动,不要直接修改文件;先调用 proposeLocalFileChanges 创建待确认计划。
83
+ - 用户确认后,才执行已确认的本地文件整理计划。
84
+ - 删除类操作要保守;能通过移动到整理目录解决时,不要优先删除。
85
+ - 回复用户时说明将要改哪些文件、为什么改、以及是否可撤销。`;
86
+
74
87
  // ============================================================================
75
88
  // Agent 编排协作(有 callAgent / runStreamingAgent 工具时注入)
76
89
  // ============================================================================
@@ -79,9 +92,6 @@ const AGENT_ORCHESTRATION_INSTRUCTIONS = `--- Agent 编排与协作 ---
79
92
  你所在的系统支持多个子 Agent 和工作流工具,请把自己视为"总协调者":
80
93
 
81
94
  1)子 Agent 协作
82
- - 使用 callAgent 工具,可以让其它 Agent 作为"专家顾问"分别给出意见或完成子任务。
83
- - 使用 runStreamingAgent 工具,可以把后续对话完全交给某个专用 Agent(例如代码助手、翻译助手)。
84
- - 使用 streamParallelAgents 工具,可以并行询问多个 Agent,再由你继续综合共识和分歧。
85
95
  - 如果目标 Agent 记录已经声明 delegation.serverBase / runtimeServerBase,工具会自动路由到对应 nolo server;你不需要重复填写 serverBase。
86
96
  - 如果用户明确给了另一个可访问的 server origin(例如 Windows 机器的 Cloudflare 域名),可以在工具参数里传 serverBase 覆盖自动路由;不要臆造地址,也不要把普通 localhost 当成远端机器。
87
97
  - 当用户需要多视角分析或辩论时,你可以:
@@ -105,22 +115,17 @@ const AGENT_ORCHESTRATION_INSTRUCTIONS = `--- Agent 编排与协作 ---
105
115
  // ============================================================================
106
116
 
107
117
  const KNOWLEDGE_MANAGEMENT_INSTRUCTIONS = `--- 知识管理 ---
108
- 你拥有 read / createDoc / updateDoc 能力,应主动管理知识,而不是每次重新推理。
109
118
 
110
119
  ## 三层知识决策
111
120
 
112
- **1. 直接写进 prompt**(需要 updateAgent 权限)
113
- - 极短、每次必用的规则(角色定义、回复风格、固定限制)
114
- - 标准:去掉它你就不知道自己是谁
115
-
116
- **2. references 挂载知识(每次对话自动加载)**
121
+ **1. references 挂载知识(每次对话自动加载)**
117
122
  - Agent 配置里的 references 会在每次对话开始时自动展开注入到 system prompt
118
123
  - type=instruction:高优先级,注入到 system prompt 顶部,适合行为规则、操作指南
119
124
  - type=knowledge:作为参考资料注入,适合领域知识、背景资料
120
125
  - 支持挂载:page / dialog / table(内容完整展开)
121
126
  - **page 里的 @mention 只展开元信息(标题+dbKey),不递归展开内容本身**
122
127
 
123
- **3. createDoc 创建文档(按需 read 加载)**
128
+ **2. createDoc 创建文档(按需 read 加载)**
124
129
  - 总知识页(索引):跨会话的决策规则、路由表,用 @[page:PAGE-xxx|标题] mention 指向细分页
125
130
  - 细分知识页(内容):具体领域内容、任务结果,通过总知识索引
126
131
  - mention 只是指针,要获取细分页内容必须显式调用 read({ dbKey })
@@ -164,7 +169,6 @@ const MEMORY_CAPTURE_INSTRUCTIONS = `--- 长期记忆 ---
164
169
  // ============================================================================
165
170
 
166
171
  const SELF_UPDATE_INSTRUCTIONS = `--- Agent 自我更新能力 ---
167
- 你拥有 updateSelf 权限,可以修改自己的配置。请谨慎、精准地使用。
168
172
 
169
173
  ## 何时更新自己
170
174
  - 重要决策/进度变化 → updateDoc 写回状态页
@@ -210,8 +214,6 @@ const CODE_EDITING_FIDELITY_INSTRUCTIONS = `--- 代码编辑真值规则 ---
210
214
  - 编辑完成后,最终回复只做简短结果汇报:说清改了哪些条目、依据了哪些 URL、是否还有缺失字段。不要长篇复述网页正文。`;
211
215
 
212
216
  const TABLE_SHARE_INSTRUCTIONS = `--- 表格创建与分享 ---
213
- 当用户要你整理结构化信息时,可以先用 createTable 建表,再用 addTableRow / addTableRows 填充内容。
214
-
215
217
  如果用户明确要求“社区分享 / 公开分享 / 分享链接”,并且你有 shareTable 工具:
216
218
  - 优先调用 shareTable;
217
219
  - 传入表的 dbKey,或 tenantId + tableId;
@@ -419,7 +421,10 @@ export const buildSystemPromptContext = (options: {
419
421
  const agentOrchestrationSection = agentTools.some((t) =>
420
422
  ["callAgent", "runStreamingAgent", "streamParallelAgents"].includes(t)
421
423
  )
422
- ? AGENT_ORCHESTRATION_INSTRUCTIONS
424
+ ? [
425
+ AGENT_ORCHESTRATION_INSTRUCTIONS,
426
+ agentTools.includes("runStreamingAgent") ? PAGE_BUILDER_HANDOFF_INSTRUCTIONS : "",
427
+ ].filter(Boolean).join("\n\n")
423
428
  : "";
424
429
 
425
430
  const menuUsageSection = agentTools.includes("ui_ask_choice")
@@ -431,6 +436,17 @@ export const buildSystemPromptContext = (options: {
431
436
  )
432
437
  ? WEBPAGE_ACCESS_INSTRUCTIONS
433
438
  : "";
439
+ const localFileOrganizationSection = agentTools.some((t) =>
440
+ [
441
+ "listLocalFiles",
442
+ "readLocalFile",
443
+ "proposeLocalFileChanges",
444
+ "executeApprovedLocalFileChanges",
445
+ "undoLocalFileChangeBatch",
446
+ ].includes(t)
447
+ )
448
+ ? LOCAL_FILE_ORGANIZATION_INSTRUCTIONS
449
+ : "";
434
450
  const tableShareSection = agentTools.some((t) =>
435
451
  ["createTable", "shareTable", "addTableRow", "addTableRows"].includes(t)
436
452
  )
@@ -466,6 +482,7 @@ export const buildSystemPromptContext = (options: {
466
482
  startupProtocol,
467
483
  contextLayerContract,
468
484
  emailRegistrationWorkflow,
485
+ webResearchToolPolicy,
469
486
  } =
470
487
  buildRuntimeGuidanceBlocks(agentTools);
471
488
 
@@ -500,6 +517,7 @@ export const buildSystemPromptContext = (options: {
500
517
  { id: "core-persona", owner: "agent", content: corePersonaSection },
501
518
  { id: "agent-orchestration", owner: "platform", content: agentOrchestrationSection },
502
519
  { id: "web-access", owner: "platform", content: webAccessSection },
520
+ { id: "local-file-organization", owner: "platform", content: localFileOrganizationSection },
503
521
  { id: "table-share", owner: "platform", content: tableShareSection },
504
522
  { id: "menu-usage", owner: "platform", content: menuUsageSection },
505
523
  { id: "clarification-mode", owner: "platform", content: clarifyingSection },
@@ -515,6 +533,11 @@ export const buildSystemPromptContext = (options: {
515
533
  owner: "platform",
516
534
  content: emailRegistrationWorkflow,
517
535
  },
536
+ {
537
+ id: "web-research-tool-policy",
538
+ owner: "platform",
539
+ content: webResearchToolPolicy,
540
+ },
518
541
  { id: "user-global-prompt", owner: "user", content: userGlobalPromptSection },
519
542
  { id: "response-guidelines", owner: "platform", content: responseGuidelinesSection },
520
543
  { id: "skill-guidance", owner: "runtime", content: skillGuidanceSection },
@@ -0,0 +1,49 @@
1
+ import type { AgentRuntimeOptions } from "./types";
2
+
3
+ export function buildCanvasNodeEditingContextSummary(
4
+ runtimeOptions?: AgentRuntimeOptions
5
+ ): string | null {
6
+ if (runtimeOptions?.editingTarget?.kind !== "canvas_node") return null;
7
+
8
+ const editingTarget = runtimeOptions.editingTarget;
9
+ const metadata = editingTarget.metadata ?? {};
10
+ const selectedNodeId =
11
+ typeof metadata.selectedNodeId === "string"
12
+ ? metadata.selectedNodeId
13
+ : editingTarget.key ?? "(未知节点)";
14
+ const part =
15
+ typeof metadata.part === "string"
16
+ ? metadata.part
17
+ : editingTarget.title ?? selectedNodeId;
18
+ const nodeType =
19
+ typeof metadata.type === "string" ? metadata.type : "(未知类型)";
20
+ const path = Array.isArray(metadata.path)
21
+ ? metadata.path.filter((item): item is string => typeof item === "string")
22
+ : [];
23
+ const props = metadata.props && typeof metadata.props === "object"
24
+ ? metadata.props
25
+ : {};
26
+ const style = metadata.style && typeof metadata.style === "object"
27
+ ? metadata.style
28
+ : {};
29
+
30
+ return [
31
+ "当前编辑目标:Canvas Tree 中的一个选中节点。",
32
+ `- 节点 ID: ${selectedNodeId}`,
33
+ `- part: ${part}`,
34
+ `- 节点类型: ${nodeType}`,
35
+ ...(path.length ? [`- 节点路径: ${path.join(" > ")}`] : []),
36
+ "",
37
+ "当前节点 props:",
38
+ JSON.stringify(props, null, 2),
39
+ "",
40
+ "当前节点 style:",
41
+ JSON.stringify(style, null, 2),
42
+ "",
43
+ "【给 AI 的操作指南 / 非用户原话】",
44
+ `1. 如果用户要求修改当前模块,只输出 updateNode,目标 id 必须是 ${selectedNodeId}。`,
45
+ "2. 不要重建 root/shell,不要重新 append 已存在的大块内容。",
46
+ "3. 除非用户明确要求新增子模块,否则不要 appendNode。",
47
+ "4. 回复仍必须遵守 Canvas Tree MVP 协议:只输出 canvas_snapshot NDJSON,不要输出解释或源码。",
48
+ ].join("\n");
49
+ }
@@ -29,6 +29,7 @@ export interface CliExecuteOptions {
29
29
  model?: string;
30
30
  timeout?: number; // ms,默认 120_000
31
31
  cwd?: string;
32
+ env?: Record<string, string | undefined>;
32
33
  yolo?: boolean; // 允许所有工具,默认 true(后台任务常用)
33
34
  }
34
35
 
@@ -62,6 +63,16 @@ export interface CliSessionTurnResult extends CliExecuteResult {
62
63
 
63
64
  const cliSessions = new Map<string, CliSessionState>();
64
65
 
66
+ function buildCliProcessEnv(extraEnv?: Record<string, string | undefined>) {
67
+ return Object.fromEntries(
68
+ Object.entries({
69
+ ...process.env,
70
+ ...extraEnv,
71
+ NO_COLOR: "1",
72
+ }).filter((entry): entry is [string, string] => typeof entry[1] === "string")
73
+ );
74
+ }
75
+
65
76
  // ── 各 provider 实现 ─────────────────────────────────────────────────────────
66
77
 
67
78
  /**
@@ -97,7 +108,7 @@ function executeCopilot(
97
108
 
98
109
  exec(
99
110
  cmd,
100
- { timeout, cwd, env: { ...process.env } },
111
+ { timeout, cwd, env: buildCliProcessEnv(options.env) },
101
112
  (error, stdout, stderr) => {
102
113
  if (error) {
103
114
  if (error.killed && (error as any).signal === "SIGTERM") {
@@ -198,7 +209,7 @@ function executeGemini(
198
209
 
199
210
  exec(
200
211
  cmd,
201
- { timeout, cwd, env: { ...process.env } },
212
+ { timeout, cwd, env: buildCliProcessEnv(options.env) },
202
213
  (error, stdout, stderr) => {
203
214
  if (error) {
204
215
  if (error.killed && (error as any).signal === "SIGTERM") {
@@ -255,7 +266,7 @@ function executeCodex(
255
266
  const start = Date.now();
256
267
  const proc = spawn("codex", [...args, prompt], {
257
268
  cwd,
258
- env: { ...process.env, NO_COLOR: "1" },
269
+ env: buildCliProcessEnv(options.env),
259
270
  stdio: ["ignore", "pipe", "pipe"],
260
271
  });
261
272
  let stdout = "";
@@ -335,7 +346,7 @@ function executeClaude(
335
346
  const start = Date.now();
336
347
  const proc = spawn("claude", args, {
337
348
  cwd,
338
- env: { ...process.env, NO_COLOR: "1" },
349
+ env: buildCliProcessEnv(options.env),
339
350
  stdio: ["ignore", "pipe", "pipe"],
340
351
  });
341
352
  let stdout = "";
@@ -112,6 +112,8 @@ export const getCreateAgentSchema = (t: TFunction) =>
112
112
 
113
113
  outputPrice: z.number().min(0, t("validation.priceMin")).default(0),
114
114
 
115
+ sharingLevel: z.enum(["default", "split", "full"]).optional(),
116
+
115
117
  avatarFileId: z.string().optional().or(z.string().length(0)),
116
118
 
117
119
  tags: z.string().trim().optional().or(z.string().length(0)),
@@ -24,7 +24,7 @@ interface ToolCall {
24
24
  export async function executeToolCall(
25
25
  tc: ToolCall,
26
26
  thunkApi: any,
27
- parentMessageId?: string
27
+ context?: { parentMessageId?: string; agentKey?: string }
28
28
  ): Promise<string> {
29
29
  const toolName: string = tc.function?.name ?? "";
30
30
  const toolArgs = (() => {
@@ -41,7 +41,8 @@ export async function executeToolCall(
41
41
  return `[工具 "${toolName}" 未找到]`;
42
42
  }
43
43
  const result = await executor(toolArgs, thunkApi, {
44
- parentMessageId: parentMessageId ?? "",
44
+ parentMessageId: context?.parentMessageId ?? "",
45
+ agentKey: context?.agentKey,
45
46
  });
46
47
  // executor 返回 { rawData, displayData? } 或直接返回字符串
47
48
  const raw = result?.rawData ?? result;
@@ -43,6 +43,8 @@ export interface UsePublicAgentsOptions {
43
43
  imageOutputOnly?: boolean;
44
44
  /** Filter agents that include this tool in their tools array */
45
45
  toolName?: string;
46
+ /** Return list-card metadata only from remote public-agent APIs. */
47
+ summary?: boolean;
46
48
  initialData?: Agent[];
47
49
  reloadMode?: "preview" | "catalog";
48
50
  }
@@ -163,6 +165,7 @@ export function usePublicAgents({
163
165
  userId,
164
166
  imageOutputOnly = false,
165
167
  toolName,
168
+ summary = false,
166
169
  initialData = [],
167
170
  reloadMode = "catalog",
168
171
  }: UsePublicAgentsOptions = {}) {
@@ -194,6 +197,7 @@ export function usePublicAgents({
194
197
  userId,
195
198
  imageOutputOnly,
196
199
  toolName,
200
+ summary,
197
201
  };
198
202
  const myReqId = ++requestIdRef.current;
199
203
 
@@ -413,6 +417,7 @@ export function usePublicAgents({
413
417
  userId,
414
418
  imageOutputOnly,
415
419
  toolName,
420
+ summary,
416
421
  currentServer,
417
422
  syncServers,
418
423
  currentUserId,
@@ -432,6 +437,7 @@ export function usePublicAgents({
432
437
  searchName,
433
438
  sortBy,
434
439
  toolName,
440
+ summary,
435
441
  userId,
436
442
  ]);
437
443
 
@@ -0,0 +1,75 @@
1
+ export const PAGE_BUILDER_AGENT_PUBLIC_KEY =
2
+ "agent-pub-01PAGEBUILDR00000000FT7R9G";
3
+
4
+ export type PageBuilderScenario = {
5
+ id: string;
6
+ label: string;
7
+ userIntent: string;
8
+ prompt: string;
9
+ };
10
+
11
+ export const PAGE_BUILDER_SCENARIOS: PageBuilderScenario[] = [
12
+ {
13
+ id: "information-display",
14
+ label: "信息展示",
15
+ userIntent: "主页、产品介绍、服务介绍、作品集、活动页、报价页",
16
+ prompt:
17
+ "帮我做一个独立 AI 产品顾问的个人主页。要看起来专业,包含个人定位、能提供的服务卡片、代表成果卡片、可信背书和联系合作按钮。",
18
+ },
19
+ {
20
+ id: "data-analysis",
21
+ label: "数据分析",
22
+ userIntent: "日报、周报、经营看板、质检报告、销售漏斗、反馈分析",
23
+ prompt:
24
+ "帮我做一个 AI 客服质检日报,包含核心指标、近 7 天风险趋势、异常会话明细表,以及明天需要跟进的建议。",
25
+ },
26
+ {
27
+ id: "process-guide",
28
+ label: "流程说明",
29
+ userIntent: "教程、SOP、onboarding、操作指南、课程步骤、申请流程",
30
+ prompt:
31
+ "帮我做一个新员工使用 CRM 的交互教程页面,包含学习目标、操作步骤、示例练习、检查清单和下一步按钮。",
32
+ },
33
+ {
34
+ id: "decision-comparison",
35
+ label: "决策比较",
36
+ userIntent: "方案对比、产品选型、候选人对比、竞品分析、采购建议",
37
+ prompt:
38
+ "帮我做一个三款 CRM 方案对比页,包含对比维度表格、每个方案的优缺点卡片、评分、适合团队类型和最终推荐。",
39
+ },
40
+ {
41
+ id: "plan-roadmap",
42
+ label: "计划排布",
43
+ userIntent: "项目计划、学习计划、健身计划、内容日历、发布节奏",
44
+ prompt:
45
+ "帮我做一个 30 天内容发布计划页面,包含阶段目标、每周任务、优先级、里程碑卡片和风险提醒。",
46
+ },
47
+ {
48
+ id: "mixed-pitch",
49
+ label: "混合长尾",
50
+ userIntent: "汇报页、融资材料、复杂项目概览,混合展示、数据、计划、对比",
51
+ prompt:
52
+ "给一个早期 AI 客服创业项目做一页融资汇报页,包含市场机会、关键数据指标卡、增长趋势图、产品路线、竞品对比表、团队介绍和下一步计划。",
53
+ },
54
+ ];
55
+
56
+ const scenarioLines = PAGE_BUILDER_SCENARIOS.map(
57
+ (scenario) => `- ${scenario.label}:${scenario.userIntent}`
58
+ ).join("\n");
59
+
60
+ export const PAGE_BUILDER_HANDOFF_INSTRUCTIONS = `--- 页面生成助手 handoff ---
61
+ 当用户需要把内容变成“可看的页面/报告/看板/教程/对比页/计划页”时,可以把本轮交给页面生成助手。
62
+
63
+ 目标 Agent:
64
+ - agentKey: ${PAGE_BUILDER_AGENT_PUBLIC_KEY}
65
+ - 推荐工具: runStreamingAgent
66
+
67
+ 适合 handoff 的视觉意图大类:
68
+ ${scenarioLines}
69
+
70
+ 调用边界:
71
+ - 用户明确要求“做成页面 / 看板 / 报告 / 教程 / 主页 / 对比页 / 计划表 / 可视化”时,可以直接 runStreamingAgent。
72
+ - 如果用户只是讨论内容,但你判断“文字回答不如可视化页面”,先询问用户:“这个更适合做成一页可视化页面,要我生成一个可交互版本吗?”
73
+ - 不要把普通问答、闲聊、代码解释、纯文本总结强行交给页面生成助手。
74
+ - handoff 时,把用户原始需求和你已确认的业务上下文一起放进 userInput,不要要求用户理解 React、HTML、代码块或运行时。
75
+ - 页面生成助手只负责生成和修改可交互页面;你仍负责判断、解释、澄清和后续协调。`;
@@ -125,7 +125,10 @@ export async function runAgentClientLoop(
125
125
  // 5. 执行工具调用
126
126
  for (const tc of assistantMsg.tool_calls) {
127
127
  toolCallCount++;
128
- const toolResultContent = await executeToolCall(tc, thunkApi, parentMessageId);
128
+ const toolResultContent = await executeToolCall(tc, thunkApi, {
129
+ parentMessageId,
130
+ agentKey,
131
+ });
129
132
  messages.push({
130
133
  role: "tool",
131
134
  tool_call_id: tc.id,
@@ -45,6 +45,20 @@ const buildEmailRegistrationWorkflowBlock = (
45
45
  ].join("\n");
46
46
  };
47
47
 
48
+ const buildWebResearchToolPolicyBlock = (
49
+ hasExecShellTool: boolean,
50
+ hasFetchWebpageTool: boolean
51
+ ): string => {
52
+ if (!hasExecShellTool || !hasFetchWebpageTool) return "";
53
+
54
+ return [
55
+ "--- 生产环境网页研究工具策略 ---",
56
+ "生产环境网页研究优先使用 fetchWebpage、站点 Markdown / llms.txt、或专用浏览/搜索工具。",
57
+ "不要用 execShell/execBash 调 curl、grep、sed 等命令抓网页或截取网页段落;生产环境通常会禁用 dev shell,反复尝试只会浪费回合。",
58
+ "如果网页内容过长或锚点段落没有被单独提取,先寻找该文档站提供的 Markdown 版本、独立页面、llms.txt 索引或更具体 URL,再继续回答。",
59
+ ].join("\n");
60
+ };
61
+
48
62
  export const resolveRuntimeGuidanceToolOptions = (
49
63
  tools: string[] = []
50
64
  ): RuntimeGuidanceToolOptions => {
@@ -80,6 +94,7 @@ export const resolveRuntimeGuidanceToolOptions = (
80
94
 
81
95
  export const buildRuntimeGuidanceBlocks = (tools: string[] = []) => {
82
96
  const options = resolveRuntimeGuidanceToolOptions(tools);
97
+ const normalizedTools = canonicalizeToolNames(tools);
83
98
 
84
99
  return {
85
100
  startupProtocol: buildStartupProtocolBlock({
@@ -93,5 +108,9 @@ export const buildRuntimeGuidanceBlocks = (tools: string[] = []) => {
93
108
  emailRegistrationWorkflow: buildEmailRegistrationWorkflowBlock(
94
109
  options.hasEmailRegistrationWorkflow
95
110
  ),
111
+ webResearchToolPolicy: buildWebResearchToolPolicyBlock(
112
+ options.hasExecShellTool,
113
+ normalizedTools.includes("fetchWebpage")
114
+ ),
96
115
  };
97
116
  };
@@ -20,6 +20,8 @@ export interface FetchPublicAgentsOptions {
20
20
  imageOutputOnly?: boolean;
21
21
  /** Filter agents that include this tool in their tools array */
22
22
  toolName?: string;
23
+ /** Return list-card metadata only. Full agent config is still available by key. */
24
+ summary?: boolean;
23
25
  }
24
26
 
25
27
  export interface FetchPublicAgentsResult {
@@ -80,6 +82,51 @@ async function getPublicAgents(): Promise<Agent[]> {
80
82
  const DEFAULT_LIMIT = 20;
81
83
  const MAX_LIMIT = 100;
82
84
 
85
+ const SUMMARY_FIELDS = new Set([
86
+ "dbKey",
87
+ "id",
88
+ "type",
89
+ "userId",
90
+ "name",
91
+ "title",
92
+ "displayName",
93
+ "introduction",
94
+ "tags",
95
+ "tools",
96
+ "provider",
97
+ "model",
98
+ "cliProvider",
99
+ "apiSource",
100
+ "hasVision",
101
+ "cover",
102
+ "avatarFileId",
103
+ "inputPrice",
104
+ "outputPrice",
105
+ "imageModel",
106
+ "imageConfig",
107
+ "imageWorkflow",
108
+ "pricing",
109
+ "metrics",
110
+ "runtimeBinding",
111
+ "spaceId",
112
+ "isPublic",
113
+ "createdAt",
114
+ "updatedAt",
115
+ "created",
116
+ "updated_at",
117
+ "deletedAt",
118
+ ]);
119
+
120
+ const toPublicAgentSummary = (agent: Agent): Agent => {
121
+ const summary: Record<string, unknown> = {};
122
+ for (const [key, value] of Object.entries(agent as Record<string, unknown>)) {
123
+ if (SUMMARY_FIELDS.has(key)) {
124
+ summary[key] = value;
125
+ }
126
+ }
127
+ return summary as Agent;
128
+ };
129
+
83
130
  export async function fetchPublicAgents(
84
131
  options: FetchPublicAgentsOptions = {}
85
132
  ): Promise<FetchPublicAgentsResult> {
@@ -90,6 +137,7 @@ export async function fetchPublicAgents(
90
137
  userId,
91
138
  imageOutputOnly = false,
92
139
  toolName,
140
+ summary = false,
93
141
  } = options;
94
142
 
95
143
  const limit = Math.min(Math.max(rawLimit, 1), MAX_LIMIT);
@@ -123,6 +171,8 @@ export async function fetchPublicAgents(
123
171
  // 使用统一的排序逻辑
124
172
  const sorted = sortAgents(list, sortBy);
125
173
 
126
- const data = sorted.slice(0, limit);
174
+ const data = sorted.slice(0, limit).map((agent) =>
175
+ summary ? toPublicAgentSummary(agent) : agent
176
+ );
127
177
  return { data, total: list.length, hasMore: list.length > limit };
128
178
  }