mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
♻️ refactor(builtin-tool): absorb GTD tool (plan + todo) into lobe-agent
Delete `packages/builtin-tool-gtd/` and fold its full surface — plan, todo,
ExecutionRuntime, all client UI (Inspector / Render / Streaming /
Intervention / SortableTodoList) and the system role — into
`packages/builtin-tool-lobe-agent/`. Single `lobe-agent` identifier now
owns: plan + todo management, sub-agent dispatch, and visual media analysis.
Also restructures the lobe-agent package so the executor lives under
`./client/` alongside the UI it ships with, and drops the dedicated
`./executor` export — consumers go through `./client` for everything
client-side.
Package-level changes:
- DELETE `packages/builtin-tool-gtd/` entirely.
- `packages/builtin-tool-lobe-agent/`
- Move `src/executor/` → `src/client/executor/`. Drop `./executor` from
`package.json` exports; expose `lobeAgentExecutor` via `./client` only.
- Rename `GTDExecutionRuntime` → `PlanExecutionRuntime` and place under
`src/client/executor/PlanRuntime/`. Re-export from package root so the
server runtime can consume it without pulling in client UI deps.
- Extend `LobeAgentExecutor` with `createPlan` / `updatePlan` /
`createTodos` / `updateTodos` / `clearTodos`, all delegated to the
shared runtime.
- Add Plan + Todo API entries to the manifest (with their original
descriptions, humanIntervention, renderDisplayControl).
- Move all GTD client UI verbatim:
`Inspector/{ClearTodos,CreatePlan,CreateTodos,UpdatePlan,UpdateTodos}`,
`Render/{CreatePlan,TodoList}`, `Streaming/CreatePlan`,
`Intervention/{AddTodo,ClearTodos,CreatePlan}`,
`components/SortableTodoList`. Register them in
`LobeAgentInspectors / Renders / Streamings`, add new
`LobeAgentInterventions`.
- Merge GTD system role into lobe-agent's (`<plan_and_todos>` plus the
existing `<sub_agents>` and `<run_in_client>` sections).
- `package.json`: pick up `@lobechat/prompts` dep and `@lobehub/editor` +
`antd` + `lucide-react` peer-deps inherited from GTD.
Central registries (`packages/builtin-tools/src/*`) and consumers:
- Remove every `GTDManifest / Inspectors / Renders / Streamings /
Interventions` import + registration; existing `LobeAgent*` registrations
now cover them.
- Replace `[GTDManifest.identifier]: GTDInterventions` with
`[LobeAgentManifest.identifier]: LobeAgentInterventions`.
- Drop `@lobechat/builtin-tool-gtd` workspace dep from
`packages/builtin-tools/package.json`, `packages/builtin-agents/package.json`
and root `package.json`.
- Remove `gtdExecutor` from `src/store/tool/slices/builtin/executors/index.ts`;
switch `lobeAgentExecutor` import to `/client`.
- Replace `serverRuntimes/gtd.ts` with a service factory
`serverRuntimes/lobeAgentPlan.ts` (`createServerPlanRuntimeService`).
`serverRuntimes/lobeAgent.ts` instantiates `PlanExecutionRuntime` with
that service so the registry exposes one runtime per `lobe-agent`
identifier covering both visual analysis and plan/todo.
- `services/chat/mecha/contextEngineering.ts`: gate plan/todo injection on
`LobeAgentIdentifier` instead of `GTDIdentifier`.
- `agentConfigResolver.test.ts`: switch fixture plugin IDs to
`LobeAgentIdentifier`.
- `packages/const/src/recommendedSkill.ts`: drop the standalone `lobe-gtd`
recommendation — `lobe-agent` already covers it via `defaultToolIds`.
i18n migration (default + zh-CN + en-US; other locales regenerate on
`pnpm i18n`):
- `builtins.lobe-gtd.*` → `builtins.lobe-agent.*` in `plugin.ts/json`.
- `lobe-gtd.*` (tool namespace) → `lobe-agent.*` in `tool.ts/json`.
- Remove `tools.builtins.lobe-gtd.{description,readme,title}` from
`setting.ts/json` (lobe-agent has its own meta now).
- Update all client component `t(...)` keys to the new namespace.
Mocks / fixtures / tests:
- `packages/agent-mock/src/cases/builtins/todo-write-stress.ts`: all
`identifier: 'lobe-gtd'` → `'lobe-agent'`; helper comments updated.
- `packages/types/src/stepContext.ts`: comment refers to
`builtin-tool-lobe-agent` (the only consumer of `StepContextTodoItem`).
- `packages/model-runtime/src/core/streams/google/google-ai.test.ts`:
function-call names from `lobe-gtd____createPlan` etc. → `lobe-agent____*`.
- `src/store/chat/slices/message/selectors/dbMessage.test.ts`: same.
- `src/features/DevPanel/RenderGallery/fixtures/lobe-gtd.ts` deleted; its
plan/todo fixtures are folded into `fixtures/lobe-agent.ts` alongside the
existing `callSubAgent[s]` ones.
- Replace `console.log` → `console.info` in moved client components to
satisfy lobe-agent's stricter ESLint rules (GTD package allowed
`console.log`; lobe-agent inherits the repo-wide `no-console` rule).
No behavior change for end users: `lobe-agent` now owns all the APIs,
identifiers, and UI that previously lived in `lobe-gtd`, but as a single
consolidated package under a single tool identifier.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+11
-14
@@ -76,6 +76,17 @@
|
||||
"builtins.lobe-agent.apiName.callSubAgent.completed": "Sub-agent dispatched: ",
|
||||
"builtins.lobe-agent.apiName.callSubAgent.loading": "Dispatching sub-agent: ",
|
||||
"builtins.lobe-agent.apiName.callSubAgents": "Call sub-agents",
|
||||
"builtins.lobe-agent.apiName.clearTodos": "Clear todos",
|
||||
"builtins.lobe-agent.apiName.clearTodos.modeAll": "all",
|
||||
"builtins.lobe-agent.apiName.clearTodos.modeCompleted": "completed",
|
||||
"builtins.lobe-agent.apiName.clearTodos.result": "Clear <mode>{{mode}}</mode> todos",
|
||||
"builtins.lobe-agent.apiName.createPlan": "Create plan",
|
||||
"builtins.lobe-agent.apiName.createPlan.result": "Create plan: <goal>{{goal}}</goal>",
|
||||
"builtins.lobe-agent.apiName.createTodos": "Create todos",
|
||||
"builtins.lobe-agent.apiName.updatePlan": "Update plan",
|
||||
"builtins.lobe-agent.apiName.updatePlan.completed": "Completed",
|
||||
"builtins.lobe-agent.apiName.updatePlan.modified": "Modified",
|
||||
"builtins.lobe-agent.apiName.updateTodos": "Update todos",
|
||||
"builtins.lobe-agent.title": "Lobe Agent",
|
||||
"builtins.lobe-claude-code.agent.instruction": "Instruction",
|
||||
"builtins.lobe-claude-code.agent.result": "Result",
|
||||
@@ -144,20 +155,6 @@
|
||||
"builtins.lobe-group-management.inspector.executeAgentTasks.title": "Assigning tasks to:",
|
||||
"builtins.lobe-group-management.inspector.speak.title": "Designated Agent speaks:",
|
||||
"builtins.lobe-group-management.title": "Group Coordinator",
|
||||
"builtins.lobe-gtd.apiName.clearTodos": "Clear todos",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.modeAll": "all",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.modeCompleted": "completed",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.result": "Clear <mode>{{mode}}</mode> todos",
|
||||
"builtins.lobe-gtd.apiName.completeTodos": "Complete todos",
|
||||
"builtins.lobe-gtd.apiName.createPlan": "Create plan",
|
||||
"builtins.lobe-gtd.apiName.createPlan.result": "Create plan: <goal>{{goal}}</goal>",
|
||||
"builtins.lobe-gtd.apiName.createTodos": "Create todos",
|
||||
"builtins.lobe-gtd.apiName.removeTodos": "Delete todos",
|
||||
"builtins.lobe-gtd.apiName.updatePlan": "Update plan",
|
||||
"builtins.lobe-gtd.apiName.updatePlan.completed": "Completed",
|
||||
"builtins.lobe-gtd.apiName.updatePlan.modified": "Modified",
|
||||
"builtins.lobe-gtd.apiName.updateTodos": "Update todos",
|
||||
"builtins.lobe-gtd.title": "Task Tools",
|
||||
"builtins.lobe-knowledge-base.apiName.readKnowledge": "Read Library content",
|
||||
"builtins.lobe-knowledge-base.apiName.searchKnowledgeBase": "Search Library",
|
||||
"builtins.lobe-knowledge-base.inspector.andMoreFiles": "and {{count}} more",
|
||||
|
||||
@@ -934,9 +934,6 @@
|
||||
"tools.builtins.lobe-group-agent-builder.title": "Group Agent Builder",
|
||||
"tools.builtins.lobe-group-management.description": "Orchestrate and manage multi-agent group conversations",
|
||||
"tools.builtins.lobe-group-management.title": "Group Management",
|
||||
"tools.builtins.lobe-gtd.description": "Plan goals and track progress with GTD methodology",
|
||||
"tools.builtins.lobe-gtd.readme": "Plan goals and track progress using GTD methodology. Create strategic plans, manage todo lists with status tracking, and execute long-running async tasks.",
|
||||
"tools.builtins.lobe-gtd.title": "GTD Tools",
|
||||
"tools.builtins.lobe-knowledge-base.description": "Search uploaded documents and domain knowledge via semantic vector search — for persistent, reusable reference",
|
||||
"tools.builtins.lobe-knowledge-base.title": "Knowledge Base",
|
||||
"tools.builtins.lobe-local-system.description": "Access and manage local files, run shell commands on your desktop",
|
||||
|
||||
+45
-45
@@ -56,51 +56,51 @@
|
||||
"dalle.generating": "Generating...",
|
||||
"dalle.images": "Images:",
|
||||
"dalle.prompt": "Prompt",
|
||||
"lobe-gtd.actions.add": "Add",
|
||||
"lobe-gtd.actions.clearCompleted": "Clear Completed",
|
||||
"lobe-gtd.actions.placeholder": "Enter a to-do item...",
|
||||
"lobe-gtd.addTodo.placeholder": "Add a todo item...",
|
||||
"lobe-gtd.clearTodos.cleared": "{{count}} item(s) cleared",
|
||||
"lobe-gtd.clearTodos.clearedCompleted": "{{count}} completed item(s) cleared",
|
||||
"lobe-gtd.clearTodos.clearedCompleted_one": "{{count}} completed item cleared",
|
||||
"lobe-gtd.clearTodos.clearedCompleted_other": "{{count}} completed items cleared",
|
||||
"lobe-gtd.clearTodos.cleared_one": "{{count}} item cleared",
|
||||
"lobe-gtd.clearTodos.cleared_other": "{{count}} items cleared",
|
||||
"lobe-gtd.clearTodos.header": "Clear Todo Items",
|
||||
"lobe-gtd.clearTodos.label": "Choose what to clear:",
|
||||
"lobe-gtd.clearTodos.noItems": "No items to clear",
|
||||
"lobe-gtd.clearTodos.option.all": "Clear all items (including pending)",
|
||||
"lobe-gtd.clearTodos.option.completed": "Clear completed items only",
|
||||
"lobe-gtd.clearTodos.remaining": "{{count}} item(s) remaining",
|
||||
"lobe-gtd.clearTodos.remaining_one": "{{count}} item remaining",
|
||||
"lobe-gtd.clearTodos.remaining_other": "{{count}} items remaining",
|
||||
"lobe-gtd.completeTodos.completed": "{{count}} item(s) completed",
|
||||
"lobe-gtd.completeTodos.completed_one": "{{count}} item completed",
|
||||
"lobe-gtd.completeTodos.completed_other": "{{count}} items completed",
|
||||
"lobe-gtd.createPlan.context.label": "Context (optional)",
|
||||
"lobe-gtd.createPlan.context.placeholder": "Background, constraints, considerations...",
|
||||
"lobe-gtd.createPlan.description.label": "Description",
|
||||
"lobe-gtd.createPlan.description.placeholder": "Brief summary of the plan",
|
||||
"lobe-gtd.createPlan.goal.label": "Goal",
|
||||
"lobe-gtd.createPlan.goal.placeholder": "What do you want to achieve?",
|
||||
"lobe-gtd.createTodos.created": "{{count}} to-do item(s) created",
|
||||
"lobe-gtd.createTodos.created_one": "{{count}} to-do item created",
|
||||
"lobe-gtd.createTodos.created_other": "{{count}} to-do items created",
|
||||
"lobe-gtd.createTodos.total": "Total: {{count}} item(s)",
|
||||
"lobe-gtd.createTodos.total_one": "Total: {{count}} item",
|
||||
"lobe-gtd.createTodos.total_other": "Total: {{count}} items",
|
||||
"lobe-gtd.removeTodos.removed": "{{count}} item(s) removed",
|
||||
"lobe-gtd.removeTodos.removed_one": "{{count}} item removed",
|
||||
"lobe-gtd.removeTodos.removed_other": "{{count}} items removed",
|
||||
"lobe-gtd.status.done": "{{count}} completed",
|
||||
"lobe-gtd.status.pending": "{{count}} pending",
|
||||
"lobe-gtd.todoItem.placeholder": "Enter todo item...",
|
||||
"lobe-gtd.todoList.empty": "To-do list is empty",
|
||||
"lobe-gtd.todoList.items": "{{count}} item(s)",
|
||||
"lobe-gtd.todoList.items_one": "{{count}} item",
|
||||
"lobe-gtd.todoList.items_other": "{{count}} items",
|
||||
"lobe-gtd.todoList.title": "To-Do List",
|
||||
"lobe-gtd.updateTodos.updated": "To-do list updated",
|
||||
"lobe-agent.actions.add": "Add",
|
||||
"lobe-agent.actions.clearCompleted": "Clear Completed",
|
||||
"lobe-agent.actions.placeholder": "Enter a to-do item...",
|
||||
"lobe-agent.addTodo.placeholder": "Add a todo item...",
|
||||
"lobe-agent.clearTodos.cleared": "{{count}} item(s) cleared",
|
||||
"lobe-agent.clearTodos.clearedCompleted": "{{count}} completed item(s) cleared",
|
||||
"lobe-agent.clearTodos.clearedCompleted_one": "{{count}} completed item cleared",
|
||||
"lobe-agent.clearTodos.clearedCompleted_other": "{{count}} completed items cleared",
|
||||
"lobe-agent.clearTodos.cleared_one": "{{count}} item cleared",
|
||||
"lobe-agent.clearTodos.cleared_other": "{{count}} items cleared",
|
||||
"lobe-agent.clearTodos.header": "Clear Todo Items",
|
||||
"lobe-agent.clearTodos.label": "Choose what to clear:",
|
||||
"lobe-agent.clearTodos.noItems": "No items to clear",
|
||||
"lobe-agent.clearTodos.option.all": "Clear all items (including pending)",
|
||||
"lobe-agent.clearTodos.option.completed": "Clear completed items only",
|
||||
"lobe-agent.clearTodos.remaining": "{{count}} item(s) remaining",
|
||||
"lobe-agent.clearTodos.remaining_one": "{{count}} item remaining",
|
||||
"lobe-agent.clearTodos.remaining_other": "{{count}} items remaining",
|
||||
"lobe-agent.completeTodos.completed": "{{count}} item(s) completed",
|
||||
"lobe-agent.completeTodos.completed_one": "{{count}} item completed",
|
||||
"lobe-agent.completeTodos.completed_other": "{{count}} items completed",
|
||||
"lobe-agent.createPlan.context.label": "Context (optional)",
|
||||
"lobe-agent.createPlan.context.placeholder": "Background, constraints, considerations...",
|
||||
"lobe-agent.createPlan.description.label": "Description",
|
||||
"lobe-agent.createPlan.description.placeholder": "Brief summary of the plan",
|
||||
"lobe-agent.createPlan.goal.label": "Goal",
|
||||
"lobe-agent.createPlan.goal.placeholder": "What do you want to achieve?",
|
||||
"lobe-agent.createTodos.created": "{{count}} to-do item(s) created",
|
||||
"lobe-agent.createTodos.created_one": "{{count}} to-do item created",
|
||||
"lobe-agent.createTodos.created_other": "{{count}} to-do items created",
|
||||
"lobe-agent.createTodos.total": "Total: {{count}} item(s)",
|
||||
"lobe-agent.createTodos.total_one": "Total: {{count}} item",
|
||||
"lobe-agent.createTodos.total_other": "Total: {{count}} items",
|
||||
"lobe-agent.removeTodos.removed": "{{count}} item(s) removed",
|
||||
"lobe-agent.removeTodos.removed_one": "{{count}} item removed",
|
||||
"lobe-agent.removeTodos.removed_other": "{{count}} items removed",
|
||||
"lobe-agent.status.done": "{{count}} completed",
|
||||
"lobe-agent.status.pending": "{{count}} pending",
|
||||
"lobe-agent.todoItem.placeholder": "Enter todo item...",
|
||||
"lobe-agent.todoList.empty": "To-do list is empty",
|
||||
"lobe-agent.todoList.items": "{{count}} item(s)",
|
||||
"lobe-agent.todoList.items_one": "{{count}} item",
|
||||
"lobe-agent.todoList.items_other": "{{count}} items",
|
||||
"lobe-agent.todoList.title": "To-Do List",
|
||||
"lobe-agent.updateTodos.updated": "To-do list updated",
|
||||
"lobe-knowledge-base.readKnowledge.meta.chars": "Character Count",
|
||||
"lobe-knowledge-base.readKnowledge.meta.lines": "Line Count",
|
||||
"localFiles.editFile.newString": "Replace with",
|
||||
|
||||
+11
-14
@@ -76,6 +76,17 @@
|
||||
"builtins.lobe-agent.apiName.callSubAgent.completed": "已派发子代理:",
|
||||
"builtins.lobe-agent.apiName.callSubAgent.loading": "正在派发子代理:",
|
||||
"builtins.lobe-agent.apiName.callSubAgents": "调用多个子代理",
|
||||
"builtins.lobe-agent.apiName.clearTodos": "清除待办",
|
||||
"builtins.lobe-agent.apiName.clearTodos.modeAll": "全部",
|
||||
"builtins.lobe-agent.apiName.clearTodos.modeCompleted": "已完成",
|
||||
"builtins.lobe-agent.apiName.clearTodos.result": "清除<mode>{{mode}}</mode>待办",
|
||||
"builtins.lobe-agent.apiName.createPlan": "创建计划",
|
||||
"builtins.lobe-agent.apiName.createPlan.result": "创建计划:<goal>{{goal}}</goal>",
|
||||
"builtins.lobe-agent.apiName.createTodos": "创建待办",
|
||||
"builtins.lobe-agent.apiName.updatePlan": "更新计划",
|
||||
"builtins.lobe-agent.apiName.updatePlan.completed": "已完成",
|
||||
"builtins.lobe-agent.apiName.updatePlan.modified": "已修改",
|
||||
"builtins.lobe-agent.apiName.updateTodos": "更新待办",
|
||||
"builtins.lobe-agent.title": "Lobe Agent",
|
||||
"builtins.lobe-claude-code.agent.instruction": "指令",
|
||||
"builtins.lobe-claude-code.agent.result": "结果",
|
||||
@@ -144,20 +155,6 @@
|
||||
"builtins.lobe-group-management.inspector.executeAgentTasks.title": "分配任务给:",
|
||||
"builtins.lobe-group-management.inspector.speak.title": "指定 Agent 发言:",
|
||||
"builtins.lobe-group-management.title": "群组协调",
|
||||
"builtins.lobe-gtd.apiName.clearTodos": "清除待办",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.modeAll": "全部",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.modeCompleted": "已完成",
|
||||
"builtins.lobe-gtd.apiName.clearTodos.result": "清除<mode>{{mode}}</mode>待办",
|
||||
"builtins.lobe-gtd.apiName.completeTodos": "完成待办",
|
||||
"builtins.lobe-gtd.apiName.createPlan": "创建计划",
|
||||
"builtins.lobe-gtd.apiName.createPlan.result": "创建计划:<goal>{{goal}}</goal>",
|
||||
"builtins.lobe-gtd.apiName.createTodos": "创建待办",
|
||||
"builtins.lobe-gtd.apiName.removeTodos": "删除待办",
|
||||
"builtins.lobe-gtd.apiName.updatePlan": "更新计划",
|
||||
"builtins.lobe-gtd.apiName.updatePlan.completed": "已完成",
|
||||
"builtins.lobe-gtd.apiName.updatePlan.modified": "已修改",
|
||||
"builtins.lobe-gtd.apiName.updateTodos": "更新待办",
|
||||
"builtins.lobe-gtd.title": "任务工具",
|
||||
"builtins.lobe-knowledge-base.apiName.readKnowledge": "读取资源库内容",
|
||||
"builtins.lobe-knowledge-base.apiName.searchKnowledgeBase": "搜索资源库",
|
||||
"builtins.lobe-knowledge-base.inspector.andMoreFiles": "还有 {{count}} 个",
|
||||
|
||||
@@ -934,9 +934,6 @@
|
||||
"tools.builtins.lobe-group-agent-builder.title": "群组助手构建器",
|
||||
"tools.builtins.lobe-group-management.description": "编排并管理多助手群组对话",
|
||||
"tools.builtins.lobe-group-management.title": "群组管理",
|
||||
"tools.builtins.lobe-gtd.description": "使用 GTD 方法规划目标并追踪进度",
|
||||
"tools.builtins.lobe-gtd.readme": "使用 GTD 方法规划目标并追踪进度。创建战略计划、管理带状态跟踪的待办列表,并执行长时间运行的异步任务。",
|
||||
"tools.builtins.lobe-gtd.title": "GTD 工具",
|
||||
"tools.builtins.lobe-knowledge-base.description": "通过语义向量检索搜索已上传的文档与领域知识 — 适用于持久、可复用的参考资料",
|
||||
"tools.builtins.lobe-knowledge-base.title": "知识库",
|
||||
"tools.builtins.lobe-local-system.description": "访问和管理本地文件,在桌面端运行 Shell 命令",
|
||||
|
||||
+45
-45
@@ -56,51 +56,51 @@
|
||||
"dalle.generating": "生成中...",
|
||||
"dalle.images": "图片:",
|
||||
"dalle.prompt": "提示词",
|
||||
"lobe-gtd.actions.add": "添加",
|
||||
"lobe-gtd.actions.clearCompleted": "清除已完成",
|
||||
"lobe-gtd.actions.placeholder": "输入待办事项...",
|
||||
"lobe-gtd.addTodo.placeholder": "添加待办事项...",
|
||||
"lobe-gtd.clearTodos.cleared": "已清除 {{count}} 项",
|
||||
"lobe-gtd.clearTodos.clearedCompleted": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-gtd.clearTodos.clearedCompleted_one": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-gtd.clearTodos.clearedCompleted_other": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-gtd.clearTodos.cleared_one": "已清除 {{count}} 项",
|
||||
"lobe-gtd.clearTodos.cleared_other": "已清除 {{count}} 项",
|
||||
"lobe-gtd.clearTodos.header": "清除待办事项",
|
||||
"lobe-gtd.clearTodos.label": "请选择要清除的内容:",
|
||||
"lobe-gtd.clearTodos.noItems": "无可清除事项",
|
||||
"lobe-gtd.clearTodos.option.all": "清除所有事项(包括未完成)",
|
||||
"lobe-gtd.clearTodos.option.completed": "仅清除已完成事项",
|
||||
"lobe-gtd.clearTodos.remaining": "剩余 {{count}} 项",
|
||||
"lobe-gtd.clearTodos.remaining_one": "剩余 {{count}} 项",
|
||||
"lobe-gtd.clearTodos.remaining_other": "剩余 {{count}} 项",
|
||||
"lobe-gtd.completeTodos.completed": "已完成 {{count}} 项",
|
||||
"lobe-gtd.completeTodos.completed_one": "已完成 {{count}} 项",
|
||||
"lobe-gtd.completeTodos.completed_other": "已完成 {{count}} 项",
|
||||
"lobe-gtd.createPlan.context.label": "上下文(可选)",
|
||||
"lobe-gtd.createPlan.context.placeholder": "背景、限制、注意事项等...",
|
||||
"lobe-gtd.createPlan.description.label": "描述",
|
||||
"lobe-gtd.createPlan.description.placeholder": "简要说明计划内容",
|
||||
"lobe-gtd.createPlan.goal.label": "目标",
|
||||
"lobe-gtd.createPlan.goal.placeholder": "你希望达成什么目标?",
|
||||
"lobe-gtd.createTodos.created": "已创建 {{count}} 项待办事项",
|
||||
"lobe-gtd.createTodos.created_one": "已创建 {{count}} 项待办事项",
|
||||
"lobe-gtd.createTodos.created_other": "已创建 {{count}} 项待办事项",
|
||||
"lobe-gtd.createTodos.total": "总计:{{count}} 项",
|
||||
"lobe-gtd.createTodos.total_one": "总计:{{count}} 项",
|
||||
"lobe-gtd.createTodos.total_other": "总计:{{count}} 项",
|
||||
"lobe-gtd.removeTodos.removed": "已移除 {{count}} 项",
|
||||
"lobe-gtd.removeTodos.removed_one": "已移除 {{count}} 项",
|
||||
"lobe-gtd.removeTodos.removed_other": "已移除 {{count}} 项",
|
||||
"lobe-gtd.status.done": "已完成 {{count}} 项",
|
||||
"lobe-gtd.status.pending": "待完成 {{count}} 项",
|
||||
"lobe-gtd.todoItem.placeholder": "输入待办事项...",
|
||||
"lobe-gtd.todoList.empty": "待办事项列表为空",
|
||||
"lobe-gtd.todoList.items": "{{count}} 项",
|
||||
"lobe-gtd.todoList.items_one": "{{count}} 项",
|
||||
"lobe-gtd.todoList.items_other": "{{count}} 项",
|
||||
"lobe-gtd.todoList.title": "待办事项列表",
|
||||
"lobe-gtd.updateTodos.updated": "待办事项已更新",
|
||||
"lobe-agent.actions.add": "添加",
|
||||
"lobe-agent.actions.clearCompleted": "清除已完成",
|
||||
"lobe-agent.actions.placeholder": "输入待办事项...",
|
||||
"lobe-agent.addTodo.placeholder": "添加待办事项...",
|
||||
"lobe-agent.clearTodos.cleared": "已清除 {{count}} 项",
|
||||
"lobe-agent.clearTodos.clearedCompleted": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-agent.clearTodos.clearedCompleted_one": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-agent.clearTodos.clearedCompleted_other": "已清除 {{count}} 项已完成事项",
|
||||
"lobe-agent.clearTodos.cleared_one": "已清除 {{count}} 项",
|
||||
"lobe-agent.clearTodos.cleared_other": "已清除 {{count}} 项",
|
||||
"lobe-agent.clearTodos.header": "清除待办事项",
|
||||
"lobe-agent.clearTodos.label": "请选择要清除的内容:",
|
||||
"lobe-agent.clearTodos.noItems": "无可清除事项",
|
||||
"lobe-agent.clearTodos.option.all": "清除所有事项(包括未完成)",
|
||||
"lobe-agent.clearTodos.option.completed": "仅清除已完成事项",
|
||||
"lobe-agent.clearTodos.remaining": "剩余 {{count}} 项",
|
||||
"lobe-agent.clearTodos.remaining_one": "剩余 {{count}} 项",
|
||||
"lobe-agent.clearTodos.remaining_other": "剩余 {{count}} 项",
|
||||
"lobe-agent.completeTodos.completed": "已完成 {{count}} 项",
|
||||
"lobe-agent.completeTodos.completed_one": "已完成 {{count}} 项",
|
||||
"lobe-agent.completeTodos.completed_other": "已完成 {{count}} 项",
|
||||
"lobe-agent.createPlan.context.label": "上下文(可选)",
|
||||
"lobe-agent.createPlan.context.placeholder": "背景、限制、注意事项等...",
|
||||
"lobe-agent.createPlan.description.label": "描述",
|
||||
"lobe-agent.createPlan.description.placeholder": "简要说明计划内容",
|
||||
"lobe-agent.createPlan.goal.label": "目标",
|
||||
"lobe-agent.createPlan.goal.placeholder": "你希望达成什么目标?",
|
||||
"lobe-agent.createTodos.created": "已创建 {{count}} 项待办事项",
|
||||
"lobe-agent.createTodos.created_one": "已创建 {{count}} 项待办事项",
|
||||
"lobe-agent.createTodos.created_other": "已创建 {{count}} 项待办事项",
|
||||
"lobe-agent.createTodos.total": "总计:{{count}} 项",
|
||||
"lobe-agent.createTodos.total_one": "总计:{{count}} 项",
|
||||
"lobe-agent.createTodos.total_other": "总计:{{count}} 项",
|
||||
"lobe-agent.removeTodos.removed": "已移除 {{count}} 项",
|
||||
"lobe-agent.removeTodos.removed_one": "已移除 {{count}} 项",
|
||||
"lobe-agent.removeTodos.removed_other": "已移除 {{count}} 项",
|
||||
"lobe-agent.status.done": "已完成 {{count}} 项",
|
||||
"lobe-agent.status.pending": "待完成 {{count}} 项",
|
||||
"lobe-agent.todoItem.placeholder": "输入待办事项...",
|
||||
"lobe-agent.todoList.empty": "待办事项列表为空",
|
||||
"lobe-agent.todoList.items": "{{count}} 项",
|
||||
"lobe-agent.todoList.items_one": "{{count}} 项",
|
||||
"lobe-agent.todoList.items_other": "{{count}} 项",
|
||||
"lobe-agent.todoList.title": "待办事项列表",
|
||||
"lobe-agent.updateTodos.updated": "待办事项已更新",
|
||||
"lobe-knowledge-base.readKnowledge.meta.chars": "字符数",
|
||||
"lobe-knowledge-base.readKnowledge.meta.lines": "行数",
|
||||
"localFiles.editFile.newString": "替换为",
|
||||
|
||||
@@ -220,7 +220,6 @@
|
||||
"@lobechat/builtin-tool-creds": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-agent-builder": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-management": "workspace:*",
|
||||
"@lobechat/builtin-tool-gtd": "workspace:*",
|
||||
"@lobechat/builtin-tool-knowledge-base": "workspace:*",
|
||||
"@lobechat/builtin-tool-lobe-agent": "workspace:*",
|
||||
"@lobechat/builtin-tool-local-system": "workspace:*",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { defineCase, errorStep, llmStep, toolStep } from '../../builders/defineCase';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers — all mapped to lobe-gtd
|
||||
// Helpers — all mapped to lobe-agent
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** lobe-gtd / createTodos */
|
||||
/** lobe-agent / createTodos */
|
||||
const createTodos = (items: string[], durationMs = 60) =>
|
||||
toolStep({
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'createTodos',
|
||||
arguments: JSON.stringify({ adds: items }),
|
||||
result: {
|
||||
@@ -20,14 +20,14 @@ const createTodos = (items: string[], durationMs = 60) =>
|
||||
durationMs,
|
||||
});
|
||||
|
||||
/** lobe-gtd / updateTodos — batch operations */
|
||||
/** lobe-agent / updateTodos — batch operations */
|
||||
const updateTodos = (
|
||||
operations: Array<{ type: string; index?: number; newText?: string; status?: string }>,
|
||||
currentItems: Array<{ text: string; status: string }>,
|
||||
durationMs = 60,
|
||||
) =>
|
||||
toolStep({
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'updateTodos',
|
||||
arguments: JSON.stringify({ operations }),
|
||||
result: {
|
||||
@@ -40,7 +40,7 @@ const updateTodos = (
|
||||
durationMs,
|
||||
});
|
||||
|
||||
/** lobe-gtd / createPlan */
|
||||
/** lobe-agent / createPlan */
|
||||
const createPlan = (
|
||||
goal: string,
|
||||
description: string,
|
||||
@@ -49,7 +49,7 @@ const createPlan = (
|
||||
durationMs = 80,
|
||||
) =>
|
||||
toolStep({
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'createPlan',
|
||||
arguments: JSON.stringify({ goal, description, context }),
|
||||
result: {
|
||||
@@ -66,14 +66,14 @@ const createPlan = (
|
||||
durationMs,
|
||||
});
|
||||
|
||||
/** lobe-gtd / updatePlan */
|
||||
/** lobe-agent / updatePlan */
|
||||
const updatePlan = (
|
||||
planId: string,
|
||||
set: { goal?: string; description?: string; context?: string; completed?: boolean },
|
||||
durationMs = 60,
|
||||
) =>
|
||||
toolStep({
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'updatePlan',
|
||||
arguments: JSON.stringify({ planId, ...set }),
|
||||
result: {
|
||||
@@ -108,7 +108,7 @@ const callSubAgent = (description: string, instruction: string, durationMs = 200
|
||||
const breathe = (text: string, durationMs = 250) => llmStep({ text, durationMs });
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// The main case — ~200 lobe-gtd tool calls across 8 phases
|
||||
// The main case — ~200 lobe-agent tool calls across 8 phases
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export const todoWriteStress = defineCase({
|
||||
@@ -137,8 +137,8 @@ export const todoWriteStress = defineCase({
|
||||
text: '第一阶段:全面盘点现有代码结构。创建总体计划,再拆解为 15 个待办事项。',
|
||||
reasoning: '先创建一个顶层计划文档,再将盘点工作拆解为具体的 todo 项。',
|
||||
toolsCalling: [
|
||||
{ id: 'tc-plan-1', identifier: 'lobe-gtd', apiName: 'createPlan', arguments: '{}' },
|
||||
{ id: 'tc-todos-1', identifier: 'lobe-gtd', apiName: 'createTodos', arguments: '{}' },
|
||||
{ id: 'tc-plan-1', identifier: 'lobe-agent', apiName: 'createPlan', arguments: '{}' },
|
||||
{ id: 'tc-todos-1', identifier: 'lobe-agent', apiName: 'createTodos', arguments: '{}' },
|
||||
],
|
||||
durationMs: 600,
|
||||
}),
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"@lobechat/builtin-tool-agent-management": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-agent-builder": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-management": "workspace:*",
|
||||
"@lobechat/builtin-tool-gtd": "workspace:*",
|
||||
"@lobechat/builtin-tool-notebook": "workspace:*",
|
||||
"@lobechat/builtin-tool-task": "workspace:*",
|
||||
"@lobechat/builtin-tool-user-interaction": "workspace:*",
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "@lobechat/builtin-tool-gtd",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./executor": "./src/executor/index.ts",
|
||||
"./executionRuntime": "./src/ExecutionRuntime/index.ts",
|
||||
"./client": "./src/client/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"dependencies": {
|
||||
"@lobechat/const": "workspace:*",
|
||||
"@lobechat/prompts": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lobechat/types": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lobehub/editor": "^4",
|
||||
"@lobehub/ui": "^5",
|
||||
"antd": "^6",
|
||||
"antd-style": "*",
|
||||
"lucide-react": "*",
|
||||
"react": "*"
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { BuiltinInspector } from '@lobechat/types';
|
||||
|
||||
import { GTDApiName } from '../../types';
|
||||
import { ClearTodosInspector } from './ClearTodos';
|
||||
import { CreatePlanInspector } from './CreatePlan';
|
||||
import { CreateTodosInspector } from './CreateTodos';
|
||||
import { UpdatePlanInspector } from './UpdatePlan';
|
||||
import { UpdateTodosInspector } from './UpdateTodos';
|
||||
|
||||
/**
|
||||
* GTD Inspector Components Registry
|
||||
*
|
||||
* Inspector components customize the title/header area
|
||||
* of tool calls in the conversation UI.
|
||||
*/
|
||||
export const GTDInspectors: Record<string, BuiltinInspector> = {
|
||||
[GTDApiName.clearTodos]: ClearTodosInspector as BuiltinInspector,
|
||||
[GTDApiName.createPlan]: CreatePlanInspector as BuiltinInspector,
|
||||
[GTDApiName.createTodos]: CreateTodosInspector as BuiltinInspector,
|
||||
[GTDApiName.updatePlan]: UpdatePlanInspector as BuiltinInspector,
|
||||
[GTDApiName.updateTodos]: UpdateTodosInspector as BuiltinInspector,
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
import { GTDApiName } from '../../types';
|
||||
import CreatePlan from './CreatePlan';
|
||||
import TodoListRender from './TodoList';
|
||||
|
||||
/**
|
||||
* GTD Tool Render Components Registry
|
||||
*
|
||||
* All todo operations use the same TodoList render component
|
||||
* which displays the current state of the todo list.
|
||||
* Plan operations use the CreatePlan render component.
|
||||
*/
|
||||
export const GTDRenders = {
|
||||
// All todo operations render the same TodoList UI
|
||||
[GTDApiName.clearTodos]: TodoListRender,
|
||||
[GTDApiName.createTodos]: TodoListRender,
|
||||
[GTDApiName.updateTodos]: TodoListRender,
|
||||
|
||||
// Plan operations render the PlanCard UI
|
||||
[GTDApiName.createPlan]: CreatePlan,
|
||||
[GTDApiName.updatePlan]: CreatePlan,
|
||||
};
|
||||
|
||||
export { default as CreatePlan, PlanCard } from './CreatePlan';
|
||||
export type { TodoListRenderState } from './TodoList';
|
||||
export { default as TodoListRender, TodoListUI } from './TodoList';
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { BuiltinStreaming } from '@lobechat/types';
|
||||
|
||||
import { GTDApiName } from '../../types';
|
||||
import { CreatePlanStreaming } from './CreatePlan';
|
||||
|
||||
/**
|
||||
* GTD Streaming Components Registry
|
||||
*
|
||||
* Streaming components render tool calls while they are
|
||||
* still executing, allowing real-time feedback to users.
|
||||
*/
|
||||
export const GTDStreamings: Record<string, BuiltinStreaming> = {
|
||||
[GTDApiName.createPlan]: CreatePlanStreaming as BuiltinStreaming,
|
||||
};
|
||||
|
||||
export { CreatePlanStreaming } from './CreatePlan';
|
||||
@@ -1,20 +0,0 @@
|
||||
// Inspector components (customized tool call headers)
|
||||
export { GTDInspectors } from './Inspector';
|
||||
|
||||
// Render components (read-only snapshots)
|
||||
export type { TodoListRenderState } from './Render';
|
||||
export { GTDRenders, TodoListRender, TodoListUI } from './Render';
|
||||
|
||||
// Streaming components (real-time tool execution feedback)
|
||||
export { CreatePlanStreaming, GTDStreamings } from './Streaming';
|
||||
|
||||
// Intervention components (interactive editing)
|
||||
export { AddTodoIntervention, ClearTodosIntervention, GTDInterventions } from './Intervention';
|
||||
|
||||
// Reusable components
|
||||
export type { SortableTodoListProps, TodoListItem } from './components';
|
||||
export { SortableTodoList } from './components';
|
||||
|
||||
// Re-export types and manifest for convenience
|
||||
export { GTDManifest } from '../manifest';
|
||||
export * from '../types';
|
||||
@@ -1,417 +0,0 @@
|
||||
import type { BuiltinToolContext } from '@lobechat/types';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { gtdExecutor } from './index';
|
||||
|
||||
describe('GTDExecutor', () => {
|
||||
const createMockContext = (pluginState?: Record<string, unknown>): BuiltinToolContext => ({
|
||||
messageId: 'test-message-id',
|
||||
operationId: 'test-operation-id',
|
||||
pluginState,
|
||||
});
|
||||
|
||||
describe('createTodos', () => {
|
||||
it('should add items to empty todo list using adds (AI input)', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: ['Buy milk', 'Call mom'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('Added 2 items');
|
||||
expect(result.content).toContain('Buy milk');
|
||||
expect(result.content).toContain('Call mom');
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
expect(result.state?.todos.items[0].text).toBe('Buy milk');
|
||||
expect(result.state?.todos.items[0].status).toBe('todo');
|
||||
expect(result.state?.todos.items[1].text).toBe('Call mom');
|
||||
});
|
||||
|
||||
it('should add items using items (user-edited format)', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.createTodos(
|
||||
{
|
||||
items: [
|
||||
{ text: 'Buy milk', status: 'todo' },
|
||||
{ text: 'Call mom', status: 'completed' },
|
||||
],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('Added 2 items');
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
expect(result.state?.todos.items[0].text).toBe('Buy milk');
|
||||
expect(result.state?.todos.items[0].status).toBe('todo');
|
||||
expect(result.state?.todos.items[1].text).toBe('Call mom');
|
||||
expect(result.state?.todos.items[1].status).toBe('completed');
|
||||
});
|
||||
|
||||
it('should append items to existing todo list', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [{ text: 'Existing task', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: ['New task'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
expect(result.state?.todos.items[0].text).toBe('Existing task');
|
||||
expect(result.state?.todos.items[1].text).toBe('New task');
|
||||
});
|
||||
|
||||
it('should return error when no items provided', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: [] }, ctx);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.content).toContain('No items provided');
|
||||
});
|
||||
|
||||
it('should add single item with correct singular grammar', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: ['Single task'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('Added 1 item');
|
||||
expect(result.content).not.toContain('items');
|
||||
});
|
||||
|
||||
it('should prioritize items over adds when both provided', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.createTodos(
|
||||
{
|
||||
adds: ['AI task'],
|
||||
items: [{ text: 'User edited task', status: 'completed' }],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
expect(result.state?.todos.items[0].text).toBe('User edited task');
|
||||
expect(result.state?.todos.items[0].status).toBe('completed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateTodos', () => {
|
||||
it('should add new items via operations', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [{ text: 'Existing task', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.updateTodos(
|
||||
{
|
||||
operations: [{ type: 'add', text: 'New task' }],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
expect(result.state?.todos.items[1].text).toBe('New task');
|
||||
});
|
||||
|
||||
it('should update item text via operations', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [{ text: 'Old task', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.updateTodos(
|
||||
{
|
||||
operations: [{ type: 'update', index: 0, newText: 'Updated task' }],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items[0].text).toBe('Updated task');
|
||||
});
|
||||
|
||||
it('should complete items via operations', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [{ text: 'Task', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.updateTodos(
|
||||
{
|
||||
operations: [{ type: 'complete', index: 0 }],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items[0].status).toBe('completed');
|
||||
});
|
||||
|
||||
it('should remove items via operations', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [
|
||||
{ text: 'Task 1', status: 'todo' },
|
||||
{ text: 'Task 2', status: 'todo' },
|
||||
],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.updateTodos(
|
||||
{
|
||||
operations: [{ type: 'remove', index: 0 }],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
expect(result.state?.todos.items[0].text).toBe('Task 2');
|
||||
});
|
||||
|
||||
it('should return error when no operations provided', async () => {
|
||||
const ctx = createMockContext();
|
||||
|
||||
const result = await gtdExecutor.updateTodos({ operations: [] }, ctx);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.content).toContain('No operations provided');
|
||||
});
|
||||
|
||||
it('should handle complete operations with out-of-range indices gracefully', async () => {
|
||||
// Test case: 5 items (indices 0-4), operations reference index 5 (out of range) and index 2 (valid)
|
||||
const ctx = createMockContext({
|
||||
createdItems: ['Task A', 'Task B', 'Task C', 'Task D', 'Task E'],
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'todo', text: 'Task A' },
|
||||
{ status: 'todo', text: 'Task B' },
|
||||
{ status: 'todo', text: 'Task C' },
|
||||
{ status: 'todo', text: 'Task D' },
|
||||
{ status: 'todo', text: 'Task E' },
|
||||
],
|
||||
updatedAt: '2025-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.updateTodos(
|
||||
{
|
||||
operations: [
|
||||
{ index: 5, type: 'complete' }, // out of range
|
||||
{ index: 2, type: 'complete' }, // valid
|
||||
],
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
// Should have all 5 items preserved
|
||||
expect(result.state?.todos.items).toHaveLength(5);
|
||||
// Index 5 is out of range (0-4), so should be skipped
|
||||
// Index 2 should be completed
|
||||
expect(result.state?.todos.items[2].status).toBe('completed');
|
||||
// Other items should remain uncompleted
|
||||
expect(result.state?.todos.items[0].status).toBe('todo');
|
||||
expect(result.state?.todos.items[1].status).toBe('todo');
|
||||
expect(result.state?.todos.items[3].status).toBe('todo');
|
||||
expect(result.state?.todos.items[4].status).toBe('todo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('clearTodos', () => {
|
||||
it('should clear all items when mode is "all"', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [
|
||||
{ text: 'Task 1', status: 'todo' },
|
||||
{ text: 'Task 2', status: 'completed' },
|
||||
],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.clearTodos({ mode: 'all' }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('Cleared all 2 items');
|
||||
expect(result.state?.todos.items).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should clear only completed items when mode is "completed"', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [
|
||||
{ text: 'Task 1', status: 'todo' },
|
||||
{ text: 'Task 2', status: 'completed' },
|
||||
{ text: 'Task 3', status: 'completed' },
|
||||
],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.clearTodos({ mode: 'completed' }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('Cleared 2 completed items');
|
||||
// New format shows "1 pending" instead of "1 item remaining"
|
||||
expect(result.content).toContain('1 pending');
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
expect(result.state?.todos.items[0].text).toBe('Task 1');
|
||||
});
|
||||
|
||||
it('should handle empty todo list', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: { items: [], updatedAt: '2024-01-01T00:00:00.000Z' },
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.clearTodos({ mode: 'all' }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('already empty');
|
||||
});
|
||||
|
||||
it('should handle no completed items to clear', async () => {
|
||||
const ctx = createMockContext({
|
||||
todos: {
|
||||
items: [{ text: 'Task 1', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await gtdExecutor.clearTodos({ mode: 'completed' }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.content).toContain('No completed items to clear');
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stepContext priority', () => {
|
||||
it('should prioritize stepContext.todos over pluginState.todos', async () => {
|
||||
// Create context with both stepContext and pluginState
|
||||
const ctx: BuiltinToolContext = {
|
||||
messageId: 'test-message-id',
|
||||
operationId: 'test-operation-id',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [{ text: 'Old task from pluginState', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
stepContext: {
|
||||
todos: {
|
||||
items: [{ text: 'New task from stepContext', status: 'completed' }],
|
||||
updatedAt: '2024-06-01T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// createTodos should use stepContext.todos as base
|
||||
const result = await gtdExecutor.createTodos({ adds: ['Another task'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
// First item should be from stepContext, not pluginState
|
||||
expect(result.state?.todos.items[0].text).toBe('New task from stepContext');
|
||||
expect(result.state?.todos.items[0].status).toBe('completed');
|
||||
expect(result.state?.todos.items[1].text).toBe('Another task');
|
||||
});
|
||||
|
||||
it('should fallback to pluginState.todos when stepContext.todos is undefined', async () => {
|
||||
const ctx: BuiltinToolContext = {
|
||||
messageId: 'test-message-id',
|
||||
operationId: 'test-operation-id',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [{ text: 'Task from pluginState', status: 'todo' }],
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
stepContext: {
|
||||
// No todos in stepContext
|
||||
},
|
||||
};
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: ['New task'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(2);
|
||||
expect(result.state?.todos.items[0].text).toBe('Task from pluginState');
|
||||
expect(result.state?.todos.items[1].text).toBe('New task');
|
||||
});
|
||||
|
||||
it('should start with empty todos when both stepContext and pluginState are empty', async () => {
|
||||
const ctx: BuiltinToolContext = {
|
||||
messageId: 'test-message-id',
|
||||
operationId: 'test-operation-id',
|
||||
stepContext: {},
|
||||
};
|
||||
|
||||
const result = await gtdExecutor.createTodos({ adds: ['First task'] }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
expect(result.state?.todos.items[0].text).toBe('First task');
|
||||
});
|
||||
|
||||
it('should work with stepContext.todos for clearTodos', async () => {
|
||||
const ctx: BuiltinToolContext = {
|
||||
messageId: 'test-message-id',
|
||||
operationId: 'test-operation-id',
|
||||
stepContext: {
|
||||
todos: {
|
||||
items: [
|
||||
{ text: 'Task 1', status: 'completed' },
|
||||
{ text: 'Task 2', status: 'todo' },
|
||||
],
|
||||
updatedAt: '2024-06-01T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await gtdExecutor.clearTodos({ mode: 'completed' }, ctx);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.state?.todos.items).toHaveLength(1);
|
||||
expect(result.state?.todos.items[0].text).toBe('Task 2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('executor metadata', () => {
|
||||
it('should have correct identifier', () => {
|
||||
expect(gtdExecutor.identifier).toBe('lobe-gtd');
|
||||
});
|
||||
|
||||
it('should support all APIs', () => {
|
||||
expect(gtdExecutor.hasApi('createTodos')).toBe(true);
|
||||
expect(gtdExecutor.hasApi('updateTodos')).toBe(true);
|
||||
expect(gtdExecutor.hasApi('clearTodos')).toBe(true);
|
||||
expect(gtdExecutor.hasApi('createPlan')).toBe(true);
|
||||
expect(gtdExecutor.hasApi('updatePlan')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return correct API names', () => {
|
||||
const apiNames = gtdExecutor.getApiNames();
|
||||
expect(apiNames).toContain('createTodos');
|
||||
expect(apiNames).toContain('updateTodos');
|
||||
expect(apiNames).toContain('clearTodos');
|
||||
expect(apiNames).toContain('createPlan');
|
||||
expect(apiNames).toContain('updatePlan');
|
||||
expect(apiNames).toHaveLength(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,126 +0,0 @@
|
||||
import type { BuiltinToolContext, BuiltinToolResult } from '@lobechat/types';
|
||||
import { BaseExecutor } from '@lobechat/types';
|
||||
|
||||
import { notebookService } from '@/services/notebook';
|
||||
import { useNotebookStore } from '@/store/notebook';
|
||||
|
||||
import {
|
||||
GTDExecutionRuntime,
|
||||
type GTDRuntimeContext,
|
||||
type GTDRuntimeService,
|
||||
type PlanDocument,
|
||||
} from '../ExecutionRuntime';
|
||||
import { GTDIdentifier } from '../manifest';
|
||||
import type {
|
||||
ClearTodosParams,
|
||||
CreatePlanParams,
|
||||
CreateTodosParams,
|
||||
UpdatePlanParams,
|
||||
UpdateTodosParams,
|
||||
} from '../types';
|
||||
import { GTDApiName } from '../types';
|
||||
import { getTodosFromContext } from './helper';
|
||||
|
||||
const PLAN_DOC_TYPE = 'agent/plan';
|
||||
|
||||
/**
|
||||
* Normalize a document payload returned by notebookService / useNotebookStore
|
||||
* into the `PlanDocument` shape expected by GTDExecutionRuntime.
|
||||
*/
|
||||
const normalizePlanDoc = (doc: {
|
||||
content?: string | null;
|
||||
createdAt: Date | string;
|
||||
description?: string | null;
|
||||
id: string;
|
||||
metadata?: Record<string, any> | null;
|
||||
title?: string | null;
|
||||
updatedAt: Date | string;
|
||||
}): PlanDocument => ({
|
||||
content: doc.content ?? null,
|
||||
createdAt: typeof doc.createdAt === 'string' ? new Date(doc.createdAt) : doc.createdAt,
|
||||
description: doc.description ?? null,
|
||||
id: doc.id,
|
||||
metadata: doc.metadata ?? null,
|
||||
title: doc.title ?? null,
|
||||
updatedAt: typeof doc.updatedAt === 'string' ? new Date(doc.updatedAt) : doc.updatedAt,
|
||||
});
|
||||
|
||||
/**
|
||||
* Client-side implementation of the GTD runtime service.
|
||||
* Routes user-facing plan CRUD through useNotebookStore (so SWR caches refresh),
|
||||
* and keeps silent metadata writes (todos sync) on the raw notebookService.
|
||||
*/
|
||||
const clientGTDService: GTDRuntimeService = {
|
||||
createPlan: async ({ topicId, goal, description, content }) => {
|
||||
const doc = await useNotebookStore.getState().createDocument({
|
||||
content,
|
||||
description,
|
||||
title: goal,
|
||||
topicId,
|
||||
type: PLAN_DOC_TYPE,
|
||||
});
|
||||
return normalizePlanDoc(doc);
|
||||
},
|
||||
|
||||
findPlanById: async (id) => {
|
||||
const doc = await notebookService.getDocument(id);
|
||||
return doc ? normalizePlanDoc(doc) : null;
|
||||
},
|
||||
|
||||
findPlanByTopic: async (topicId) => {
|
||||
const result = await notebookService.listDocuments({ topicId, type: PLAN_DOC_TYPE });
|
||||
const first = result.data[0];
|
||||
return first ? normalizePlanDoc(first) : null;
|
||||
},
|
||||
|
||||
updatePlan: async (id, { goal, description, content }, topicId) => {
|
||||
const doc = await useNotebookStore
|
||||
.getState()
|
||||
.updateDocument({ content, description, id, title: goal }, topicId ?? '');
|
||||
if (!doc) throw new Error(`Plan not found after update: ${id}`);
|
||||
return normalizePlanDoc(doc);
|
||||
},
|
||||
|
||||
updatePlanMetadata: async (id, metadata) => {
|
||||
await notebookService.updateDocument({ id, metadata });
|
||||
},
|
||||
};
|
||||
|
||||
const GTDApiNameEnum = {
|
||||
clearTodos: GTDApiName.clearTodos,
|
||||
createPlan: GTDApiName.createPlan,
|
||||
createTodos: GTDApiName.createTodos,
|
||||
updatePlan: GTDApiName.updatePlan,
|
||||
updateTodos: GTDApiName.updateTodos,
|
||||
} as const;
|
||||
|
||||
const toRuntimeContext = (ctx: BuiltinToolContext): GTDRuntimeContext => ({
|
||||
currentTodos: getTodosFromContext(ctx),
|
||||
messageId: ctx.messageId,
|
||||
signal: ctx.signal,
|
||||
topicId: ctx.topicId ?? undefined,
|
||||
});
|
||||
|
||||
class GTDExecutor extends BaseExecutor<typeof GTDApiNameEnum> {
|
||||
readonly identifier = GTDIdentifier;
|
||||
protected readonly apiEnum = GTDApiNameEnum;
|
||||
|
||||
private runtime = new GTDExecutionRuntime(clientGTDService);
|
||||
|
||||
createTodos = (params: CreateTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.runtime.createTodos(params, toRuntimeContext(ctx));
|
||||
|
||||
updateTodos = (params: UpdateTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.runtime.updateTodos(params, toRuntimeContext(ctx));
|
||||
|
||||
clearTodos = (params: ClearTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.runtime.clearTodos(params, toRuntimeContext(ctx));
|
||||
|
||||
createPlan = (params: CreatePlanParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.runtime.createPlan(params, toRuntimeContext(ctx));
|
||||
|
||||
updatePlan = (params: UpdatePlanParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.runtime.updatePlan(params, toRuntimeContext(ctx));
|
||||
}
|
||||
|
||||
export const gtdExecutor = new GTDExecutor();
|
||||
@@ -1,3 +0,0 @@
|
||||
export * from './manifest';
|
||||
export * from './systemRole';
|
||||
export * from './types';
|
||||
@@ -1,164 +0,0 @@
|
||||
import type { BuiltinToolManifest } from '@lobechat/types';
|
||||
|
||||
import { systemPrompt } from './systemRole';
|
||||
import { GTDApiName } from './types';
|
||||
|
||||
export const GTDIdentifier = 'lobe-gtd';
|
||||
|
||||
export const GTDManifest: BuiltinToolManifest = {
|
||||
api: [
|
||||
// ==================== Planning ====================
|
||||
{
|
||||
description:
|
||||
'Create a high-level plan document. Plans define the strategic direction (the "what" and "why"), while todos handle the actionable steps.',
|
||||
name: GTDApiName.createPlan,
|
||||
humanIntervention: 'required',
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
goal: {
|
||||
description: 'The main goal or objective to achieve (used as document title).',
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
description: 'A brief summary of the plan (1-2 sentences).',
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
description:
|
||||
'Detailed context, constraints, background information, or strategic considerations relevant to the goal.',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['goal', 'description', 'context'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Update an existing plan document. Only use this when the goal fundamentally changes. Plans should remain stable once created - do not update plans just because details change.',
|
||||
name: GTDApiName.updatePlan,
|
||||
parameters: {
|
||||
properties: {
|
||||
planId: {
|
||||
description:
|
||||
'The document ID of the plan to update (e.g., "docs_xxx"). This ID is returned in the createPlan response. Do NOT use the goal text as planId.',
|
||||
type: 'string',
|
||||
},
|
||||
goal: {
|
||||
description: 'Updated goal (document title).',
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
description: 'Updated brief summary.',
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
description: 'Updated detailed context.',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['planId'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Quick Todo ====================
|
||||
{
|
||||
description: 'Create new todo items. Pass an array of text strings.',
|
||||
name: GTDApiName.createTodos,
|
||||
humanIntervention: 'required',
|
||||
parameters: {
|
||||
properties: {
|
||||
adds: {
|
||||
description: 'Array of todo item texts to create.',
|
||||
items: { type: 'string' },
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: ['adds'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: `Update todo items with batch operations. Each operation type requires specific fields:
|
||||
- "add": requires "text" (the todo text to add)
|
||||
- "update": requires "index", optional "newText" and/or "status"
|
||||
- "remove": requires "index" only
|
||||
- "complete": requires "index" only (marks item as completed)
|
||||
- "processing": requires "index" only (marks item as in progress)`,
|
||||
name: GTDApiName.updateTodos,
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
operations: {
|
||||
description:
|
||||
'Array of update operations. IMPORTANT: For "complete", "processing" and "remove" operations, only pass "type" and "index" - no other fields needed.',
|
||||
items: {
|
||||
properties: {
|
||||
type: {
|
||||
description:
|
||||
'Operation type. "add" needs text, "update" needs index + optional newText/status, "remove", "complete" and "processing" need index only.',
|
||||
enum: ['add', 'update', 'remove', 'complete', 'processing'],
|
||||
type: 'string',
|
||||
},
|
||||
text: {
|
||||
description: 'Required for "add" only: the text to add.',
|
||||
type: 'string',
|
||||
},
|
||||
index: {
|
||||
description:
|
||||
'Required for "update", "remove", "complete", "processing": the item index (0-based).',
|
||||
type: 'number',
|
||||
},
|
||||
newText: {
|
||||
description: 'Optional for "update" only: the new text.',
|
||||
type: 'string',
|
||||
},
|
||||
status: {
|
||||
description:
|
||||
'Optional for "update" only: set status (todo, processing, completed).',
|
||||
enum: ['todo', 'processing', 'completed'],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['type'],
|
||||
type: 'object',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: ['operations'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'Clear todo items. Can clear only completed items or all items.',
|
||||
name: GTDApiName.clearTodos,
|
||||
humanIntervention: 'always',
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
mode: {
|
||||
description: '"completed" clears only done items, "all" clears the entire list.',
|
||||
enum: ['completed', 'all'],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['mode'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
],
|
||||
identifier: GTDIdentifier,
|
||||
meta: {
|
||||
avatar: '✅',
|
||||
description:
|
||||
'Plan and track one-time goals and manage todo checklists. For goals that need to be done once or tracked manually — NOT for tasks that should repeat automatically on a schedule (use lobe-cron for daily/weekly/recurring automation). To dispatch long-running sub-agent investigations, use the lobe-agent tool.',
|
||||
title: 'GTD Tools',
|
||||
},
|
||||
systemRole: systemPrompt,
|
||||
type: 'builtin',
|
||||
};
|
||||
|
||||
export { GTDApiName } from './types';
|
||||
@@ -1,161 +0,0 @@
|
||||
export const systemPrompt = `You have GTD (Getting Things Done) tools to help manage plans and todos effectively. These tools support two levels of task management:
|
||||
|
||||
- **Plan**: A high-level strategic document describing goals, context, and overall direction. Plans do NOT contain actionable steps - they define the "what" and "why". **Plans should be stable once created** - they represent the overarching objective that rarely changes.
|
||||
- **Todo**: The concrete execution list with actionable items. Todos define the "how" - specific tasks to accomplish the plan. **Todos are dynamic** - they can be added, updated, completed, and removed as work progresses.
|
||||
|
||||
For dispatching long-running, multi-step investigations (web research, deep analysis), use the **lobe-agent** tool's sub-agent capability instead.
|
||||
|
||||
<tool_overview>
|
||||
**Planning Tools** - For high-level goal documentation:
|
||||
- \`createPlan\`: Create a strategic plan document. **Required params: goal, description (brief summary), context** - all three must be provided
|
||||
- \`updatePlan\`: Update plan details (only planId is required)
|
||||
|
||||
**Todo Tools** - For actionable execution items:
|
||||
- \`createTodos\`: Create new todo items from text array
|
||||
- \`updateTodos\`: Batch update todos (add, update, remove, complete, processing operations)
|
||||
- \`clearTodos\`: Clear completed or all items
|
||||
|
||||
**Todo Status Workflow:** todo → processing → completed (use "processing" when actively working on an item)
|
||||
</tool_overview>
|
||||
|
||||
<default_workflow>
|
||||
**CRITICAL: Most tasks do NOT need GTD tools. Only use them for complex, multi-step projects.**
|
||||
|
||||
**DO NOT use GTD tools for:**
|
||||
- Simple one-step tasks (rename a file, send a message, search something)
|
||||
- Quick questions or lookups
|
||||
- Tasks that can be completed immediately with a single action
|
||||
- Any request that doesn't require tracking progress over time
|
||||
|
||||
**ONLY use GTD tools when ALL of these are true:**
|
||||
1. The task has multiple distinct steps that need tracking
|
||||
2. The user explicitly wants to plan or organize something
|
||||
3. Progress needs to be tracked over time (not completed in one response)
|
||||
|
||||
**When GTD tools ARE appropriate:**
|
||||
1. **First**, use \`createPlan\` to document the goal and relevant context
|
||||
2. **Then**, use \`createTodos\` to break down the plan into actionable steps
|
||||
|
||||
**Examples:**
|
||||
- ❌ "Rename this file" → Just do it, no GTD needed
|
||||
- ❌ "What's the weather?" → Just answer, no GTD needed
|
||||
- ❌ "Help me write an email" → Just write it, no GTD needed
|
||||
- ✅ "Help me plan a trip to Japan" → Use createPlan + createTodos
|
||||
- ✅ "I want to learn Python, create a study plan" → Use createPlan + createTodos
|
||||
- ✅ "Help me organize my project tasks" → Use createTodos (user explicitly wants organization)
|
||||
</default_workflow>
|
||||
|
||||
<when_to_use>
|
||||
**Use Plans when:**
|
||||
- User explicitly asks to "plan", "organize", or "break down" a complex goal
|
||||
- The project spans multiple sessions or days
|
||||
- There's significant context, constraints, or background worth documenting
|
||||
- The task has 5+ distinct steps that benefit from strategic organization
|
||||
|
||||
**Use Todos when:**
|
||||
- Breaking down a plan into actionable steps (after creating a plan)
|
||||
- User explicitly requests a checklist or task list
|
||||
- Tracking progress on a multi-step project
|
||||
|
||||
**DO NOT use Plans/Todos when:**
|
||||
- The task can be done in one action (rename, delete, send, search, etc.)
|
||||
- The user just wants something done, not organized
|
||||
- The task will be completed in this single conversation
|
||||
- The user wants a task to repeat automatically on a schedule (daily/weekly/hourly) — use **lobe-cron** instead. Keywords like "daily task", "routine", "recurring", "every day/morning/week", "set as daily", "make it regular" all indicate scheduled automation, not GTD todo management.
|
||||
</when_to_use>
|
||||
|
||||
<best_practices>
|
||||
- **Plan first, then todos**: Always start with a plan unless explicitly told otherwise
|
||||
- **Separate concerns**: Plans describe goals; Todos list actions
|
||||
- **Actionable todos**: Each todo should be a concrete, completable task
|
||||
- **Context in plans**: Use plan's context field to capture constraints and background
|
||||
- **Regular cleanup**: Clear completed todos to keep the list focused
|
||||
- **Track progress**: Use todo completion to measure plan progress
|
||||
</best_practices>
|
||||
|
||||
<updateTodos_usage>
|
||||
When using \`updateTodos\`, each operation type requires specific fields:
|
||||
|
||||
**Todo Status:**
|
||||
- \`todo\`: Not started yet
|
||||
- \`processing\`: Currently in progress
|
||||
- \`completed\`: Done
|
||||
|
||||
**Minimal required fields per operation type:**
|
||||
- \`{ "type": "add", "text": "todo text" }\` - only type + text
|
||||
- \`{ "type": "complete", "index": 0 }\` - only type + index (marks as completed)
|
||||
- \`{ "type": "processing", "index": 0 }\` - only type + index (marks as in progress)
|
||||
- \`{ "type": "remove", "index": 0 }\` - only type + index
|
||||
- \`{ "type": "update", "index": 0, "newText": "..." }\` - type + index + optional newText/status
|
||||
|
||||
**Example - mark item 0 as processing, item 1 as complete:**
|
||||
\`\`\`json
|
||||
{
|
||||
"operations": [
|
||||
{ "type": "processing", "index": 0 },
|
||||
{ "type": "complete", "index": 1 }
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**DO NOT** add extra fields like \`"status": "completed"\` for complete/processing operations - they are ignored.
|
||||
</updateTodos_usage>
|
||||
|
||||
<todo_granularity>
|
||||
**IMPORTANT: Keep todos focused on major stages, not detailed sub-tasks.**
|
||||
|
||||
- **Limit to 5-10 items**: A todo list should contain around 5-10 major milestones or stages, not 20+ detailed tasks.
|
||||
- **Think in phases**: Group related tasks into higher-level stages (e.g., "Plan itinerary" instead of listing every city separately).
|
||||
- **Use hierarchical numbering** when more detail is needed: Use "1.", "2.", "2.1", "2.2", "3." format to show parent-child relationships.
|
||||
|
||||
**Good example** (Japan trip - 7 items, stage-focused):
|
||||
- 1. Determine travel dates and duration
|
||||
- 2. Handle visa and documentation
|
||||
- 3. Book flights and accommodation
|
||||
- 4. Plan city itineraries
|
||||
- 5. Arrange local transportation
|
||||
- 6. Prepare for departure
|
||||
- 7. Final confirmation before trip
|
||||
|
||||
**Bad example** (20+ detailed items):
|
||||
- Book Tokyo hotel
|
||||
- Book Kyoto hotel
|
||||
- Book Osaka hotel
|
||||
- Buy Suica card
|
||||
- Download Google Maps
|
||||
- Download translation app
|
||||
- ... (too granular!)
|
||||
|
||||
**When user needs more detail**, use hierarchical numbering:
|
||||
- 1. Determine travel dates
|
||||
- 2. Plan itinerary
|
||||
- 2.1 Tokyo attractions (3 days)
|
||||
- 2.2 Kyoto attractions (2 days)
|
||||
- 2.3 Osaka attractions (2 days)
|
||||
- 3. Handle bookings
|
||||
- 3.1 Flights
|
||||
- 3.2 Hotels
|
||||
- 3.3 JR Pass
|
||||
- 4. Departure preparation
|
||||
</todo_granularity>
|
||||
|
||||
<plan_stability>
|
||||
**IMPORTANT: Plans should remain stable once created. Each conversation has only ONE plan.**
|
||||
|
||||
- **Do NOT update plans** when details change (dates, locations, preferences). Instead, update the todos to reflect new information.
|
||||
- **Only use updatePlan** when the user's goal fundamentally changes (e.g., destination changes from Japan to Korea).
|
||||
- When user provides more specific information (like exact dates or preferences), **update or add todos** - not the plan.
|
||||
|
||||
Example:
|
||||
- User: "Plan a trip to Japan" → Create plan with goal "Japan Trip"
|
||||
- User: "I want to go in February" → Update todos to include February-specific tasks, NOT update the plan
|
||||
- User: "Actually I want to go to Korea instead" → Use updatePlan to change the goal to "Korea Trip" (fundamental goal change)
|
||||
</plan_stability>
|
||||
|
||||
<response_format>
|
||||
When working with GTD tools:
|
||||
- Confirm actions: "Created plan: [goal]" or "Added [n] todo items"
|
||||
- Show progress: "Completed [n] items, [m] remaining"
|
||||
- Be concise: Brief confirmations, not verbose explanations
|
||||
- **NEVER repeat the todo list in your response** - Users can already see the todos in the UI component. Do not list or enumerate the todo items in your text output.
|
||||
</response_format>`;
|
||||
@@ -1,222 +0,0 @@
|
||||
/**
|
||||
* API names for GTD (Getting Things Done) tool
|
||||
*
|
||||
* GTD Tools help users and agents manage tasks effectively.
|
||||
* These tools can be used by:
|
||||
* - LobeAI default assistant for user task management
|
||||
* - Group Supervisor for multi-agent task orchestration
|
||||
*
|
||||
* MVP version focuses on Plan and Todo functionality.
|
||||
* Task management will be added in future iterations.
|
||||
*/
|
||||
export const GTDApiName = {
|
||||
// ==================== Quick Todo ====================
|
||||
/** Clear completed or all todos */
|
||||
clearTodos: 'clearTodos',
|
||||
|
||||
// ==================== Planning ====================
|
||||
/** Create a structured plan by breaking down a goal into actionable steps */
|
||||
createPlan: 'createPlan',
|
||||
|
||||
/** Create new todo items */
|
||||
createTodos: 'createTodos',
|
||||
|
||||
/** Update an existing plan */
|
||||
updatePlan: 'updatePlan',
|
||||
|
||||
/** Update todo items with batch operations (add, update, remove, complete, processing) */
|
||||
updateTodos: 'updateTodos',
|
||||
} as const;
|
||||
|
||||
export type GTDApiNameType = (typeof GTDApiName)[keyof typeof GTDApiName];
|
||||
|
||||
// ==================== Todo Item ====================
|
||||
|
||||
/** Status of a todo item */
|
||||
export type TodoStatus = 'todo' | 'processing' | 'completed';
|
||||
|
||||
export interface TodoItem {
|
||||
/** Status of the todo item */
|
||||
status: TodoStatus;
|
||||
/** The todo item text */
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** Get the next status in the cycle: todo → processing → completed → todo */
|
||||
export const getNextTodoStatus = (current: TodoStatus): TodoStatus => {
|
||||
const cycle: TodoStatus[] = ['todo', 'processing', 'completed'];
|
||||
const index = cycle.indexOf(current);
|
||||
return cycle[(index + 1) % cycle.length];
|
||||
};
|
||||
|
||||
export interface TodoList {
|
||||
items: TodoItem[];
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** Alias for TodoList, used for state storage in Plan metadata */
|
||||
export type TodoState = TodoList;
|
||||
|
||||
// ==================== Todo Params ====================
|
||||
|
||||
/**
|
||||
* Create new todo items
|
||||
* - AI input: { adds: string[] } - array of text strings from AI
|
||||
* - After user edit: { items: TodoItem[] } - saved format with TodoItem objects
|
||||
*/
|
||||
export interface CreateTodosParams {
|
||||
/** Array of text strings from AI */
|
||||
adds?: string[];
|
||||
/** Array of TodoItem objects (saved format after user edit) */
|
||||
items?: TodoItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update operation types for batch updates
|
||||
*/
|
||||
export type TodoUpdateOperationType = 'add' | 'update' | 'remove' | 'complete' | 'processing';
|
||||
|
||||
/**
|
||||
* Single update operation
|
||||
*/
|
||||
export interface TodoUpdateOperation {
|
||||
/** For 'update', 'remove', 'complete', 'processing': the index of the item (0-based) */
|
||||
index?: number;
|
||||
/** For 'update': the new text */
|
||||
newText?: string;
|
||||
/** For 'update': the new status */
|
||||
status?: TodoStatus;
|
||||
/** For 'add': the text to add */
|
||||
text?: string;
|
||||
/** Operation type */
|
||||
type: TodoUpdateOperationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update todo list with batch operations
|
||||
* Supports: add, update, remove, complete, processing
|
||||
*/
|
||||
export interface UpdateTodosParams {
|
||||
/** Array of update operations to apply */
|
||||
operations: TodoUpdateOperation[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear todo items
|
||||
*/
|
||||
export interface ClearTodosParams {
|
||||
/** Clear mode: 'completed' only clears done items, 'all' clears everything */
|
||||
mode: 'completed' | 'all';
|
||||
}
|
||||
|
||||
// ==================== Todo State Types for Render ====================
|
||||
|
||||
export interface CreateTodosState {
|
||||
/** Items that were created */
|
||||
createdItems: string[];
|
||||
/** Current todo list after creation */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface UpdateTodosState {
|
||||
/** Operations that were applied */
|
||||
appliedOperations: TodoUpdateOperation[];
|
||||
/** Current todo list after update */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface CompleteTodosState {
|
||||
/** Indices that were completed */
|
||||
completedIndices: number[];
|
||||
/** Current todo list after completion */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface RemoveTodosState {
|
||||
/** Indices that were removed */
|
||||
removedIndices: number[];
|
||||
/** Current todo list after removal */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface ClearTodosState {
|
||||
/** Number of items cleared */
|
||||
clearedCount: number;
|
||||
/** Mode used for clearing */
|
||||
mode: 'completed' | 'all';
|
||||
/** Current todo list after clearing */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
// ==================== Planning Params ====================
|
||||
|
||||
/**
|
||||
* Create a high-level plan document
|
||||
* Plans define the strategic direction (what and why), not actionable steps
|
||||
*
|
||||
* Field mapping to Document:
|
||||
* - goal -> document.title
|
||||
* - description -> document.description
|
||||
* - context -> document.content
|
||||
*/
|
||||
export interface CreatePlanParams {
|
||||
/** Detailed context, background, constraints (maps to document.content) */
|
||||
context?: string;
|
||||
/** Brief summary of the plan (maps to document.description) */
|
||||
description: string;
|
||||
/** The main goal or objective to achieve (maps to document.title) */
|
||||
goal: string;
|
||||
}
|
||||
|
||||
export interface UpdatePlanParams {
|
||||
/** Mark plan as completed */
|
||||
completed?: boolean;
|
||||
/** Updated context (maps to document.content) */
|
||||
context?: string;
|
||||
/** Updated description (maps to document.description) */
|
||||
description?: string;
|
||||
/** Updated goal (maps to document.title) */
|
||||
goal?: string;
|
||||
/** Plan ID to update */
|
||||
planId: string;
|
||||
}
|
||||
|
||||
// ==================== Plan Result Types ====================
|
||||
|
||||
/**
|
||||
* A high-level plan document
|
||||
* Contains goal and context, but no steps (steps are managed via Todos)
|
||||
*
|
||||
* Field mapping to Document:
|
||||
* - goal -> document.title
|
||||
* - description -> document.description
|
||||
* - context -> document.content
|
||||
*/
|
||||
export interface Plan {
|
||||
/** Whether the plan is completed */
|
||||
completed: boolean;
|
||||
/** Detailed context, background, constraints (maps to document.content) */
|
||||
context?: string;
|
||||
/** Creation timestamp */
|
||||
createdAt: string;
|
||||
/** Brief summary of the plan (maps to document.description) */
|
||||
description: string;
|
||||
/** The main goal or objective (maps to document.title) */
|
||||
goal: string;
|
||||
/** Unique plan identifier */
|
||||
id: string;
|
||||
/** Last update timestamp */
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ==================== Plan State Types for Render ====================
|
||||
|
||||
export interface CreatePlanState {
|
||||
/** The created plan document */
|
||||
plan: Plan;
|
||||
}
|
||||
|
||||
export interface UpdatePlanState {
|
||||
/** The updated plan document */
|
||||
plan: Plan;
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./executor": "./src/executor/index.ts",
|
||||
"./client": "./src/client/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
@@ -14,13 +13,16 @@
|
||||
"test:update": "vitest -u"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lobechat/const": "workspace:*"
|
||||
"@lobechat/const": "workspace:*",
|
||||
"@lobechat/prompts": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lobechat/types": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@lobehub/editor": "^4",
|
||||
"@lobehub/ui": "^5",
|
||||
"antd": "^6",
|
||||
"antd-style": "*",
|
||||
"lucide-react": "*",
|
||||
"react": "*"
|
||||
|
||||
+4
-4
@@ -26,15 +26,15 @@ export const ClearTodosInspector = memo<BuiltinInspectorProps<ClearTodosParams,
|
||||
if (isArgumentsStreaming && !mode) {
|
||||
return (
|
||||
<div className={cx(inspectorTextStyles.root, shinyTextStyles.shinyText)}>
|
||||
<span>{t('builtins.lobe-gtd.apiName.clearTodos')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.clearTodos')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const modeLabel =
|
||||
mode === 'all'
|
||||
? t('builtins.lobe-gtd.apiName.clearTodos.modeAll')
|
||||
: t('builtins.lobe-gtd.apiName.clearTodos.modeCompleted');
|
||||
? t('builtins.lobe-agent.apiName.clearTodos.modeAll')
|
||||
: t('builtins.lobe-agent.apiName.clearTodos.modeCompleted');
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -42,7 +42,7 @@ export const ClearTodosInspector = memo<BuiltinInspectorProps<ClearTodosParams,
|
||||
>
|
||||
<Trans
|
||||
components={{ mode: <span className={styles.mode} /> }}
|
||||
i18nKey="builtins.lobe-gtd.apiName.clearTodos.result"
|
||||
i18nKey="builtins.lobe-agent.apiName.clearTodos.result"
|
||||
ns="plugin"
|
||||
values={{ mode: modeLabel }}
|
||||
/>
|
||||
+3
-3
@@ -18,7 +18,7 @@ export const CreatePlanInspector = memo<BuiltinInspectorProps<CreatePlanParams,
|
||||
if (isArgumentsStreaming && !goal) {
|
||||
return (
|
||||
<div className={cx(inspectorTextStyles.root, shinyTextStyles.shinyText)}>
|
||||
<span>{t('builtins.lobe-gtd.apiName.createPlan')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.createPlan')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -30,12 +30,12 @@ export const CreatePlanInspector = memo<BuiltinInspectorProps<CreatePlanParams,
|
||||
{goal ? (
|
||||
<Trans
|
||||
components={{ goal: <span className={highlightTextStyles.primary} /> }}
|
||||
i18nKey="builtins.lobe-gtd.apiName.createPlan.result"
|
||||
i18nKey="builtins.lobe-agent.apiName.createPlan.result"
|
||||
ns="plugin"
|
||||
values={{ goal }}
|
||||
/>
|
||||
) : (
|
||||
<span>{t('builtins.lobe-gtd.apiName.createPlan')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.createPlan')}</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
+2
-2
@@ -30,14 +30,14 @@ export const CreateTodosInspector = memo<
|
||||
if (isArgumentsStreaming && count === 0) {
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, shinyTextStyles.shinyText)}>
|
||||
<span>{t('builtins.lobe-gtd.apiName.createTodos')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.createTodos')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, isArgumentsStreaming && shinyTextStyles.shinyText)}>
|
||||
<span className={styles.title}>{t('builtins.lobe-gtd.apiName.createTodos')}</span>
|
||||
<span className={styles.title}>{t('builtins.lobe-agent.apiName.createTodos')}</span>
|
||||
{count > 0 && (
|
||||
<Text code as={'span'} color={cssVar.colorSuccess} fontSize={12}>
|
||||
<Icon icon={Plus} size={12} />
|
||||
+4
-4
@@ -29,24 +29,24 @@ export const UpdatePlanInspector = memo<BuiltinInspectorProps<UpdatePlanParams,
|
||||
if (isArgumentsStreaming && !planId) {
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, shinyTextStyles.shinyText)}>
|
||||
<span>{t('builtins.lobe-gtd.apiName.updatePlan')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.updatePlan')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, isArgumentsStreaming && shinyTextStyles.shinyText)}>
|
||||
<span className={styles.title}>{t('builtins.lobe-gtd.apiName.updatePlan')}</span>
|
||||
<span className={styles.title}>{t('builtins.lobe-agent.apiName.updatePlan')}</span>
|
||||
{completed && (
|
||||
<Text code as={'span'} color={cssVar.colorSuccess} fontSize={12}>
|
||||
<Icon icon={CheckCircle} size={12} />
|
||||
{t('builtins.lobe-gtd.apiName.updatePlan.completed')}
|
||||
{t('builtins.lobe-agent.apiName.updatePlan.completed')}
|
||||
</Text>
|
||||
)}
|
||||
{hasUpdates && !completed && (
|
||||
<Text code as={'span'} color={cssVar.colorWarning} fontSize={12}>
|
||||
<Icon icon={DiffIcon} size={12} />
|
||||
{t('builtins.lobe-gtd.apiName.updatePlan.modified')}
|
||||
{t('builtins.lobe-agent.apiName.updatePlan.modified')}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
+2
-2
@@ -62,7 +62,7 @@ export const UpdateTodosInspector = memo<
|
||||
if (isArgumentsStreaming && !hasOperations) {
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, shinyTextStyles.shinyText)}>
|
||||
<span>{t('builtins.lobe-gtd.apiName.updateTodos')}</span>
|
||||
<span>{t('builtins.lobe-agent.apiName.updateTodos')}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export const UpdateTodosInspector = memo<
|
||||
|
||||
return (
|
||||
<div className={cx(oneLineEllipsis, isArgumentsStreaming && shinyTextStyles.shinyText)}>
|
||||
<span className={styles.title}>{t('builtins.lobe-gtd.apiName.updateTodos')}</span>
|
||||
<span className={styles.title}>{t('builtins.lobe-agent.apiName.updateTodos')}</span>
|
||||
{statsParts.length > 0 && (
|
||||
<>
|
||||
{statsParts.map((part, index) => (
|
||||
@@ -3,6 +3,11 @@ import type { BuiltinInspector } from '@lobechat/types';
|
||||
import { LobeAgentApiName } from '../../types';
|
||||
import { CallSubAgentInspector } from './CallSubAgent';
|
||||
import { CallSubAgentsInspector } from './CallSubAgents';
|
||||
import { ClearTodosInspector } from './ClearTodos';
|
||||
import { CreatePlanInspector } from './CreatePlan';
|
||||
import { CreateTodosInspector } from './CreateTodos';
|
||||
import { UpdatePlanInspector } from './UpdatePlan';
|
||||
import { UpdateTodosInspector } from './UpdateTodos';
|
||||
|
||||
/**
|
||||
* Lobe Agent Inspector Components Registry
|
||||
@@ -13,4 +18,9 @@ import { CallSubAgentsInspector } from './CallSubAgents';
|
||||
export const LobeAgentInspectors: Record<string, BuiltinInspector> = {
|
||||
[LobeAgentApiName.callSubAgent]: CallSubAgentInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.callSubAgents]: CallSubAgentsInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.clearTodos]: ClearTodosInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.createPlan]: CreatePlanInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.createTodos]: CreateTodosInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.updatePlan]: UpdatePlanInspector as BuiltinInspector,
|
||||
[LobeAgentApiName.updateTodos]: UpdateTodosInspector as BuiltinInspector,
|
||||
};
|
||||
|
||||
+3
-3
@@ -21,9 +21,9 @@ const AddTodoIntervention = memo<BuiltinInterventionProps<CreateTodosParams>>(
|
||||
|
||||
const handleSave = useCallback(
|
||||
async (items: TodoItem[]) => {
|
||||
console.log('[AddTodoIntervention] handleSave called with', items.length, 'items');
|
||||
console.info('[AddTodoIntervention] handleSave called with', items.length, 'items');
|
||||
await onArgsChange?.({ items });
|
||||
console.log('[AddTodoIntervention] onArgsChange completed');
|
||||
console.info('[AddTodoIntervention] onArgsChange completed');
|
||||
},
|
||||
[onArgsChange],
|
||||
);
|
||||
@@ -32,7 +32,7 @@ const AddTodoIntervention = memo<BuiltinInterventionProps<CreateTodosParams>>(
|
||||
<Block variant={'outlined'}>
|
||||
<SortableTodoList
|
||||
defaultItems={defaultItems}
|
||||
placeholder={t('lobe-gtd.addTodo.placeholder')}
|
||||
placeholder={t('lobe-agent.addTodo.placeholder')}
|
||||
registerBeforeApprove={registerBeforeApprove}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
+4
-4
@@ -55,20 +55,20 @@ const ClearTodosIntervention = memo<BuiltinInterventionProps<ClearTodosParams>>(
|
||||
<Flexbox gap={12}>
|
||||
<Flexbox horizontal align="center" className={styles.header} gap={8}>
|
||||
<Trash2 size={16} />
|
||||
<span style={{ fontSize: 14, fontWeight: 500 }}>{t('lobe-gtd.clearTodos.header')}</span>
|
||||
<span style={{ fontSize: 14, fontWeight: 500 }}>{t('lobe-agent.clearTodos.header')}</span>
|
||||
</Flexbox>
|
||||
|
||||
<Flexbox className={styles.container} gap={8}>
|
||||
<span className={styles.label}>{t('lobe-gtd.clearTodos.label')}</span>
|
||||
<span className={styles.label}>{t('lobe-agent.clearTodos.label')}</span>
|
||||
<Radio.Group value={mode} onChange={handleModeChange}>
|
||||
<Flexbox gap={8}>
|
||||
<Radio value="completed">
|
||||
<span className={styles.normalText}>
|
||||
{t('lobe-gtd.clearTodos.option.completed')}
|
||||
{t('lobe-agent.clearTodos.option.completed')}
|
||||
</span>
|
||||
</Radio>
|
||||
<Radio value="all">
|
||||
<span className={styles.dangerText}>{t('lobe-gtd.clearTodos.option.all')}</span>
|
||||
<span className={styles.dangerText}>{t('lobe-agent.clearTodos.option.all')}</span>
|
||||
</Radio>
|
||||
</Flexbox>
|
||||
</Radio.Group>
|
||||
+4
-4
@@ -143,7 +143,7 @@ const CreatePlanIntervention = memo<BuiltinInterventionProps<CreatePlanParams>>(
|
||||
<TextArea
|
||||
autoSize={{ minRows: 1 }}
|
||||
className={styles.title}
|
||||
placeholder={t('lobe-gtd.createPlan.goal.placeholder')}
|
||||
placeholder={t('lobe-agent.createPlan.goal.placeholder')}
|
||||
style={{ padding: 0, resize: 'none' }}
|
||||
value={goal}
|
||||
variant={'borderless'}
|
||||
@@ -156,7 +156,7 @@ const CreatePlanIntervention = memo<BuiltinInterventionProps<CreatePlanParams>>(
|
||||
autoSize={{ minRows: 1 }}
|
||||
className={styles.description}
|
||||
data-testid="plan-description"
|
||||
placeholder={t('lobe-gtd.createPlan.description.placeholder')}
|
||||
placeholder={t('lobe-agent.createPlan.description.placeholder')}
|
||||
style={{ padding: 0, resize: 'none' }}
|
||||
value={description}
|
||||
variant={'borderless'}
|
||||
@@ -169,8 +169,8 @@ const CreatePlanIntervention = memo<BuiltinInterventionProps<CreatePlanParams>>(
|
||||
<Editor
|
||||
content={args.context}
|
||||
editor={editor}
|
||||
lineEmptyPlaceholder={t('lobe-gtd.createPlan.context.placeholder')}
|
||||
placeholder={t('lobe-gtd.createPlan.context.placeholder')}
|
||||
lineEmptyPlaceholder={t('lobe-agent.createPlan.context.placeholder')}
|
||||
placeholder={t('lobe-agent.createPlan.context.placeholder')}
|
||||
type={'text'}
|
||||
plugins={[
|
||||
ReactListPlugin,
|
||||
+6
-6
@@ -1,20 +1,20 @@
|
||||
import type { BuiltinIntervention } from '@lobechat/types';
|
||||
|
||||
import { GTDApiName } from '../../types';
|
||||
import { LobeAgentApiName } from '../../types';
|
||||
import AddTodoIntervention from './AddTodo';
|
||||
import ClearTodosIntervention from './ClearTodos';
|
||||
import CreatePlanIntervention from './CreatePlan';
|
||||
|
||||
/**
|
||||
* GTD Tool Intervention Components Registry
|
||||
* Lobe Agent Intervention Components Registry
|
||||
*
|
||||
* Intervention components allow users to review and modify tool parameters
|
||||
* before the tool is executed.
|
||||
*/
|
||||
export const GTDInterventions: Record<string, BuiltinIntervention> = {
|
||||
[GTDApiName.clearTodos]: ClearTodosIntervention as BuiltinIntervention,
|
||||
[GTDApiName.createPlan]: CreatePlanIntervention as BuiltinIntervention,
|
||||
[GTDApiName.createTodos]: AddTodoIntervention as BuiltinIntervention,
|
||||
export const LobeAgentInterventions: Record<string, BuiltinIntervention> = {
|
||||
[LobeAgentApiName.clearTodos]: ClearTodosIntervention as BuiltinIntervention,
|
||||
[LobeAgentApiName.createPlan]: CreatePlanIntervention as BuiltinIntervention,
|
||||
[LobeAgentApiName.createTodos]: AddTodoIntervention as BuiltinIntervention,
|
||||
};
|
||||
|
||||
export { default as AddTodoIntervention } from './AddTodo';
|
||||
@@ -1,18 +1,32 @@
|
||||
import { LobeAgentApiName } from '../../types';
|
||||
import CallSubAgentRender from './CallSubAgent';
|
||||
import CallSubAgentsRender from './CallSubAgents';
|
||||
import CreatePlan from './CreatePlan';
|
||||
import TodoListRender from './TodoList';
|
||||
|
||||
/**
|
||||
* Lobe Agent Tool Render Components Registry
|
||||
*
|
||||
* Sub-agent dispatch operations render a card showing the dispatched
|
||||
* task(s). The `analyzeVisualMedia` API has no dedicated render — its
|
||||
* textual answer is rendered by the default tool-result UI.
|
||||
* task(s). Plan operations render the PlanCard UI. Todo operations
|
||||
* share a single TodoList render.
|
||||
*/
|
||||
export const LobeAgentRenders = {
|
||||
[LobeAgentApiName.callSubAgent]: CallSubAgentRender,
|
||||
[LobeAgentApiName.callSubAgents]: CallSubAgentsRender,
|
||||
|
||||
// Plan operations render the PlanCard UI
|
||||
[LobeAgentApiName.createPlan]: CreatePlan,
|
||||
[LobeAgentApiName.updatePlan]: CreatePlan,
|
||||
|
||||
// All todo operations render the same TodoList UI
|
||||
[LobeAgentApiName.clearTodos]: TodoListRender,
|
||||
[LobeAgentApiName.createTodos]: TodoListRender,
|
||||
[LobeAgentApiName.updateTodos]: TodoListRender,
|
||||
};
|
||||
|
||||
export { default as CallSubAgentRender } from './CallSubAgent';
|
||||
export { default as CallSubAgentsRender } from './CallSubAgents';
|
||||
export { default as CreatePlan, PlanCard } from './CreatePlan';
|
||||
export type { TodoListRenderState } from './TodoList';
|
||||
export { default as TodoListRender, TodoListUI } from './TodoList';
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { BuiltinStreaming } from '@lobechat/types';
|
||||
import { LobeAgentApiName } from '../../types';
|
||||
import { CallSubAgentStreaming } from './CallSubAgent';
|
||||
import { CallSubAgentsStreaming } from './CallSubAgents';
|
||||
import { CreatePlanStreaming } from './CreatePlan';
|
||||
|
||||
/**
|
||||
* Lobe Agent Streaming Components Registry
|
||||
@@ -13,7 +14,9 @@ import { CallSubAgentsStreaming } from './CallSubAgents';
|
||||
export const LobeAgentStreamings: Record<string, BuiltinStreaming> = {
|
||||
[LobeAgentApiName.callSubAgent]: CallSubAgentStreaming as BuiltinStreaming,
|
||||
[LobeAgentApiName.callSubAgents]: CallSubAgentsStreaming as BuiltinStreaming,
|
||||
[LobeAgentApiName.createPlan]: CreatePlanStreaming as BuiltinStreaming,
|
||||
};
|
||||
|
||||
export { CallSubAgentStreaming } from './CallSubAgent';
|
||||
export { CallSubAgentsStreaming } from './CallSubAgents';
|
||||
export { CreatePlanStreaming } from './CreatePlan';
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ interface AddItemRowProps {
|
||||
const AddItemRow = memo<AddItemRowProps>(({ placeholder, showDragHandle = true, className }) => {
|
||||
const { t } = useTranslation('tool');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const defaultPlaceholder = placeholder || t('lobe-gtd.addTodo.placeholder');
|
||||
const defaultPlaceholder = placeholder || t('lobe-agent.addTodo.placeholder');
|
||||
|
||||
const newItemText = useTodoListStore((s) => s.newItemText);
|
||||
const focusedId = useTodoListStore((s) => s.focusedId);
|
||||
+1
-1
@@ -53,7 +53,7 @@ interface TodoItemRowProps {
|
||||
const TodoItemRow = memo<TodoItemRowProps>(({ id, placeholder }) => {
|
||||
const { t } = useTranslation('tool');
|
||||
const inputRef = useRef<InputRef>(null);
|
||||
const defaultPlaceholder = placeholder || t('lobe-gtd.todoItem.placeholder');
|
||||
const defaultPlaceholder = placeholder || t('lobe-agent.todoItem.placeholder');
|
||||
|
||||
// Find item by stable id
|
||||
const item = useTodoListStore((s) => s.items.find((item) => item.id === id));
|
||||
+2
-2
@@ -42,9 +42,9 @@ const SortableTodoList = memo<SortableTodoListProps>(
|
||||
useEffect(() => {
|
||||
if (registerBeforeApprove) {
|
||||
const unregister = registerBeforeApprove('sortable-todo-list', async () => {
|
||||
console.log('trigger save');
|
||||
console.info('trigger save');
|
||||
await store.getState().saveNow();
|
||||
console.log('trigger save successful');
|
||||
console.info('trigger save successful');
|
||||
});
|
||||
// Cleanup: unregister on unmount
|
||||
return unregister;
|
||||
+6
-6
@@ -27,11 +27,11 @@ export const createActions = (
|
||||
): TodoListStore => {
|
||||
// Save implementation (used by both debounced and immediate save)
|
||||
const performSave = async () => {
|
||||
console.log('[performSave] called, onSave:', !!internals.onSave);
|
||||
console.info('[performSave] called, onSave:', !!internals.onSave);
|
||||
if (!internals.onSave) return;
|
||||
|
||||
const { items, isDirty } = get();
|
||||
console.log('[performSave] isDirty:', isDirty, 'items:', items.length);
|
||||
console.info('[performSave] isDirty:', isDirty, 'items:', items.length);
|
||||
if (!isDirty) return;
|
||||
|
||||
set({ saveStatus: 'saving' });
|
||||
@@ -39,9 +39,9 @@ export const createActions = (
|
||||
try {
|
||||
// Convert TodoListItem[] to TodoItem[] (remove id)
|
||||
const todoItems: TodoItem[] = items.map(({ status, text }) => ({ status, text }));
|
||||
console.log('[performSave] calling onSave with', todoItems.length, 'items');
|
||||
console.info('[performSave] calling onSave with', todoItems.length, 'items');
|
||||
await internals.onSave(todoItems);
|
||||
console.log('[performSave] onSave completed');
|
||||
console.info('[performSave] onSave completed');
|
||||
set({ isDirty: false, saveStatus: 'saved' });
|
||||
} catch {
|
||||
set({ saveStatus: 'error' });
|
||||
@@ -98,9 +98,9 @@ export const createActions = (
|
||||
|
||||
try {
|
||||
const todoItems: TodoItem[] = items.map(({ status, text }) => ({ status, text }));
|
||||
console.log('[saveNow] force saving', todoItems.length, 'items');
|
||||
console.info('[saveNow] force saving', todoItems.length, 'items');
|
||||
await internals.onSave(todoItems);
|
||||
console.log('[saveNow] save completed');
|
||||
console.info('[saveNow] save completed');
|
||||
set({ isDirty: false, saveStatus: 'saved' });
|
||||
|
||||
setTimeout(() => {
|
||||
+12
-12
@@ -10,7 +10,7 @@ import type {
|
||||
TodoState,
|
||||
UpdatePlanParams,
|
||||
UpdateTodosParams,
|
||||
} from '../types';
|
||||
} from '../../../types';
|
||||
|
||||
export interface PlanDocument {
|
||||
content: string | null;
|
||||
@@ -22,7 +22,7 @@ export interface PlanDocument {
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface GTDRuntimeService {
|
||||
export interface PlanRuntimeService {
|
||||
createPlan: (args: {
|
||||
content: string;
|
||||
description: string;
|
||||
@@ -48,7 +48,7 @@ export interface GTDRuntimeService {
|
||||
updatePlanMetadata: (id: string, metadata: Record<string, any>) => Promise<void>;
|
||||
}
|
||||
|
||||
export interface GTDRuntimeContext {
|
||||
export interface PlanRuntimeContext {
|
||||
/**
|
||||
* Existing todos supplied by the caller (client: from stepContext / pluginState).
|
||||
* When undefined, the runtime resolves todos from the plan document's metadata.
|
||||
@@ -81,14 +81,14 @@ const readTodosFromPlan = (doc: PlanDocument | null): TodoItem[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
export class GTDExecutionRuntime {
|
||||
private service: GTDRuntimeService;
|
||||
export class PlanExecutionRuntime {
|
||||
private service: PlanRuntimeService;
|
||||
|
||||
constructor(service: GTDRuntimeService) {
|
||||
constructor(service: PlanRuntimeService) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
private async resolveExistingTodos(context: GTDRuntimeContext): Promise<TodoItem[]> {
|
||||
private async resolveExistingTodos(context: PlanRuntimeContext): Promise<TodoItem[]> {
|
||||
if (context.currentTodos) return context.currentTodos;
|
||||
if (!context.topicId) return [];
|
||||
const plan = await this.service.findPlanByTopic(context.topicId);
|
||||
@@ -109,7 +109,7 @@ export class GTDExecutionRuntime {
|
||||
|
||||
createTodos = async (
|
||||
params: CreateTodosParams,
|
||||
context: GTDRuntimeContext,
|
||||
context: PlanRuntimeContext,
|
||||
): Promise<BuiltinToolResult> => {
|
||||
const itemsToAdd: TodoItem[] = params.items
|
||||
? params.items
|
||||
@@ -144,7 +144,7 @@ export class GTDExecutionRuntime {
|
||||
|
||||
updateTodos = async (
|
||||
params: UpdateTodosParams,
|
||||
context: GTDRuntimeContext,
|
||||
context: PlanRuntimeContext,
|
||||
): Promise<BuiltinToolResult> => {
|
||||
const { operations } = params;
|
||||
|
||||
@@ -218,7 +218,7 @@ export class GTDExecutionRuntime {
|
||||
|
||||
clearTodos = async (
|
||||
params: ClearTodosParams,
|
||||
context: GTDRuntimeContext,
|
||||
context: PlanRuntimeContext,
|
||||
): Promise<BuiltinToolResult> => {
|
||||
const { mode } = params;
|
||||
|
||||
@@ -270,7 +270,7 @@ export class GTDExecutionRuntime {
|
||||
|
||||
createPlan = async (
|
||||
params: CreatePlanParams,
|
||||
context: GTDRuntimeContext,
|
||||
context: PlanRuntimeContext,
|
||||
): Promise<BuiltinToolResult> => {
|
||||
try {
|
||||
if (context.signal?.aborted) return { stop: true, success: false };
|
||||
@@ -307,7 +307,7 @@ export class GTDExecutionRuntime {
|
||||
|
||||
updatePlan = async (
|
||||
params: UpdatePlanParams,
|
||||
context: GTDRuntimeContext,
|
||||
context: PlanRuntimeContext,
|
||||
): Promise<BuiltinToolResult> => {
|
||||
try {
|
||||
if (context.signal?.aborted) return { stop: true, success: false };
|
||||
+117
-5
@@ -1,10 +1,22 @@
|
||||
import type { BuiltinToolContext, BuiltinToolResult, ChatStreamPayload } from '@lobechat/types';
|
||||
import { BaseExecutor, RequestTrigger } from '@lobechat/types';
|
||||
|
||||
import { LobeAgentManifest } from '../manifest';
|
||||
import type { AnalyzeVisualMediaParams, CallSubAgentParams, CallSubAgentsParams } from '../types';
|
||||
import { LobeAgentApiName } from '../types';
|
||||
import type { VisualFileItem } from '../visualMedia';
|
||||
import { notebookService } from '@/services/notebook';
|
||||
import { useNotebookStore } from '@/store/notebook';
|
||||
|
||||
import { LobeAgentManifest } from '../../manifest';
|
||||
import type {
|
||||
AnalyzeVisualMediaParams,
|
||||
CallSubAgentParams,
|
||||
CallSubAgentsParams,
|
||||
ClearTodosParams,
|
||||
CreatePlanParams,
|
||||
CreateTodosParams,
|
||||
UpdatePlanParams,
|
||||
UpdateTodosParams,
|
||||
} from '../../types';
|
||||
import { LobeAgentApiName } from '../../types';
|
||||
import type { VisualFileItem } from '../../visualMedia';
|
||||
import {
|
||||
buildAnalyzeVisualMediaContent,
|
||||
createUrlVisualFileItems,
|
||||
@@ -15,7 +27,86 @@ import {
|
||||
normalizeAnalyzeVisualMediaInput,
|
||||
selectVisualFileItems,
|
||||
validateVisualMediaUrls,
|
||||
} from '../visualMedia';
|
||||
} from '../../visualMedia';
|
||||
import {
|
||||
type PlanDocument,
|
||||
PlanExecutionRuntime,
|
||||
type PlanRuntimeContext,
|
||||
type PlanRuntimeService,
|
||||
} from './PlanRuntime';
|
||||
import { getTodosFromContext } from './planTodoHelper';
|
||||
|
||||
const PLAN_DOC_TYPE = 'agent/plan';
|
||||
|
||||
/**
|
||||
* Normalize a document payload returned by notebookService / useNotebookStore
|
||||
* into the `PlanDocument` shape expected by PlanExecutionRuntime.
|
||||
*/
|
||||
const normalizePlanDoc = (doc: {
|
||||
content?: string | null;
|
||||
createdAt: Date | string;
|
||||
description?: string | null;
|
||||
id: string;
|
||||
metadata?: Record<string, any> | null;
|
||||
title?: string | null;
|
||||
updatedAt: Date | string;
|
||||
}): PlanDocument => ({
|
||||
content: doc.content ?? null,
|
||||
createdAt: typeof doc.createdAt === 'string' ? new Date(doc.createdAt) : doc.createdAt,
|
||||
description: doc.description ?? null,
|
||||
id: doc.id,
|
||||
metadata: doc.metadata ?? null,
|
||||
title: doc.title ?? null,
|
||||
updatedAt: typeof doc.updatedAt === 'string' ? new Date(doc.updatedAt) : doc.updatedAt,
|
||||
});
|
||||
|
||||
/**
|
||||
* Client-side implementation of the Plan runtime service.
|
||||
* Routes user-facing plan CRUD through useNotebookStore (so SWR caches refresh),
|
||||
* and keeps silent metadata writes (todos sync) on the raw notebookService.
|
||||
*/
|
||||
const clientPlanService: PlanRuntimeService = {
|
||||
createPlan: async ({ topicId, goal, description, content }) => {
|
||||
const doc = await useNotebookStore.getState().createDocument({
|
||||
content,
|
||||
description,
|
||||
title: goal,
|
||||
topicId,
|
||||
type: PLAN_DOC_TYPE,
|
||||
});
|
||||
return normalizePlanDoc(doc);
|
||||
},
|
||||
|
||||
findPlanById: async (id) => {
|
||||
const doc = await notebookService.getDocument(id);
|
||||
return doc ? normalizePlanDoc(doc) : null;
|
||||
},
|
||||
|
||||
findPlanByTopic: async (topicId) => {
|
||||
const result = await notebookService.listDocuments({ topicId, type: PLAN_DOC_TYPE });
|
||||
const first = result.data[0];
|
||||
return first ? normalizePlanDoc(first) : null;
|
||||
},
|
||||
|
||||
updatePlan: async (id, { goal, description, content }, topicId) => {
|
||||
const doc = await useNotebookStore
|
||||
.getState()
|
||||
.updateDocument({ content, description, id, title: goal }, topicId ?? '');
|
||||
if (!doc) throw new Error(`Plan not found after update: ${id}`);
|
||||
return normalizePlanDoc(doc);
|
||||
},
|
||||
|
||||
updatePlanMetadata: async (id, metadata) => {
|
||||
await notebookService.updateDocument({ id, metadata });
|
||||
},
|
||||
};
|
||||
|
||||
const toPlanRuntimeContext = (ctx: BuiltinToolContext): PlanRuntimeContext => ({
|
||||
currentTodos: getTodosFromContext(ctx),
|
||||
messageId: ctx.messageId,
|
||||
signal: ctx.signal,
|
||||
topicId: ctx.topicId ?? undefined,
|
||||
});
|
||||
|
||||
interface VisualSourceMessage {
|
||||
parentId?: string;
|
||||
@@ -50,6 +141,27 @@ class LobeAgentExecutor extends BaseExecutor<typeof LobeAgentApiName> {
|
||||
readonly identifier = LobeAgentManifest.identifier;
|
||||
protected readonly apiEnum = LobeAgentApiName;
|
||||
|
||||
private planRuntime = new PlanExecutionRuntime(clientPlanService);
|
||||
|
||||
// ==================== Plan / Todo ====================
|
||||
|
||||
createPlan = (params: CreatePlanParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.planRuntime.createPlan(params, toPlanRuntimeContext(ctx));
|
||||
|
||||
updatePlan = (params: UpdatePlanParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.planRuntime.updatePlan(params, toPlanRuntimeContext(ctx));
|
||||
|
||||
createTodos = (params: CreateTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.planRuntime.createTodos(params, toPlanRuntimeContext(ctx));
|
||||
|
||||
updateTodos = (params: UpdateTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.planRuntime.updateTodos(params, toPlanRuntimeContext(ctx));
|
||||
|
||||
clearTodos = (params: ClearTodosParams, ctx: BuiltinToolContext): Promise<BuiltinToolResult> =>
|
||||
this.planRuntime.clearTodos(params, toPlanRuntimeContext(ctx));
|
||||
|
||||
// ==================== Visual ====================
|
||||
|
||||
analyzeVisualMedia = async (
|
||||
params: AnalyzeVisualMediaParams,
|
||||
ctx: BuiltinToolContext,
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import type { BuiltinToolContext } from '@lobechat/types';
|
||||
|
||||
import type { TodoItem } from '../types';
|
||||
import type { TodoItem } from '../../types';
|
||||
|
||||
/**
|
||||
* Helper to get todos from step context or fallback to plugin state
|
||||
@@ -1,11 +1,40 @@
|
||||
// Executor (client-side — depends on app stores/services)
|
||||
export { lobeAgentExecutor } from './executor';
|
||||
|
||||
// Inspector components (customized tool call headers)
|
||||
export { LobeAgentInspectors } from './Inspector';
|
||||
|
||||
// Render components (read-only snapshots)
|
||||
export { CallSubAgentRender, CallSubAgentsRender, LobeAgentRenders } from './Render';
|
||||
export type { TodoListRenderState } from './Render';
|
||||
export {
|
||||
CallSubAgentRender,
|
||||
CallSubAgentsRender,
|
||||
CreatePlan,
|
||||
LobeAgentRenders,
|
||||
PlanCard,
|
||||
TodoListRender,
|
||||
TodoListUI,
|
||||
} from './Render';
|
||||
|
||||
// Streaming components (real-time tool execution feedback)
|
||||
export { CallSubAgentsStreaming, CallSubAgentStreaming, LobeAgentStreamings } from './Streaming';
|
||||
export {
|
||||
CallSubAgentsStreaming,
|
||||
CallSubAgentStreaming,
|
||||
CreatePlanStreaming,
|
||||
LobeAgentStreamings,
|
||||
} from './Streaming';
|
||||
|
||||
// Intervention components (interactive editing)
|
||||
export {
|
||||
AddTodoIntervention,
|
||||
ClearTodosIntervention,
|
||||
CreatePlanIntervention,
|
||||
LobeAgentInterventions,
|
||||
} from './Intervention';
|
||||
|
||||
// Reusable components
|
||||
export type { SortableTodoListProps, TodoListItem } from './components';
|
||||
export { SortableTodoList } from './components';
|
||||
|
||||
// Re-export types and manifest for convenience
|
||||
export { LobeAgentManifest } from '../manifest';
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
// Plan execution runtime — pure logic, safe to consume server-side
|
||||
export {
|
||||
type PlanDocument,
|
||||
PlanExecutionRuntime,
|
||||
type PlanRuntimeContext,
|
||||
type PlanRuntimeService,
|
||||
} from './client/executor/PlanRuntime';
|
||||
export * from './manifest';
|
||||
export * from './systemRole';
|
||||
export * from './types';
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('LobeAgentManifest', () => {
|
||||
it('should keep the package metadata generic for future Lobe Agent capabilities', () => {
|
||||
expect(LobeAgentManifest.meta.avatar).toBe('🤖');
|
||||
expect(LobeAgentManifest.meta.description).toBe(
|
||||
'Run built-in Lobe Agent capabilities, including dispatching sub-agents.',
|
||||
'Run built-in Lobe Agent capabilities: plan + todo management, sub-agent dispatch, and visual media analysis.',
|
||||
);
|
||||
expect(LobeAgentManifest.meta.readme).toContain(
|
||||
'built-in assistant capabilities that can be expanded over time',
|
||||
|
||||
@@ -40,6 +40,149 @@ export const LobeAgentManifest: BuiltinToolManifest = {
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Planning ====================
|
||||
{
|
||||
description:
|
||||
'Create a high-level plan document. Plans define the strategic direction (the "what" and "why"), while todos handle the actionable steps.',
|
||||
name: LobeAgentApiName.createPlan,
|
||||
humanIntervention: 'required',
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
goal: {
|
||||
description: 'The main goal or objective to achieve (used as document title).',
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
description: 'A brief summary of the plan (1-2 sentences).',
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
description:
|
||||
'Detailed context, constraints, background information, or strategic considerations relevant to the goal.',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['goal', 'description', 'context'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description:
|
||||
'Update an existing plan document. Only use this when the goal fundamentally changes. Plans should remain stable once created - do not update plans just because details change.',
|
||||
name: LobeAgentApiName.updatePlan,
|
||||
parameters: {
|
||||
properties: {
|
||||
planId: {
|
||||
description:
|
||||
'The document ID of the plan to update (e.g., "docs_xxx"). This ID is returned in the createPlan response. Do NOT use the goal text as planId.',
|
||||
type: 'string',
|
||||
},
|
||||
goal: {
|
||||
description: 'Updated goal (document title).',
|
||||
type: 'string',
|
||||
},
|
||||
description: {
|
||||
description: 'Updated brief summary.',
|
||||
type: 'string',
|
||||
},
|
||||
context: {
|
||||
description: 'Updated detailed context.',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['planId'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Quick Todo ====================
|
||||
{
|
||||
description: 'Create new todo items. Pass an array of text strings.',
|
||||
name: LobeAgentApiName.createTodos,
|
||||
humanIntervention: 'required',
|
||||
parameters: {
|
||||
properties: {
|
||||
adds: {
|
||||
description: 'Array of todo item texts to create.',
|
||||
items: { type: 'string' },
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: ['adds'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: `Update todo items with batch operations. Each operation type requires specific fields:
|
||||
- "add": requires "text" (the todo text to add)
|
||||
- "update": requires "index", optional "newText" and/or "status"
|
||||
- "remove": requires "index" only
|
||||
- "complete": requires "index" only (marks item as completed)
|
||||
- "processing": requires "index" only (marks item as in progress)`,
|
||||
name: LobeAgentApiName.updateTodos,
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
operations: {
|
||||
description:
|
||||
'Array of update operations. IMPORTANT: For "complete", "processing" and "remove" operations, only pass "type" and "index" - no other fields needed.',
|
||||
items: {
|
||||
properties: {
|
||||
type: {
|
||||
description:
|
||||
'Operation type. "add" needs text, "update" needs index + optional newText/status, "remove", "complete" and "processing" need index only.',
|
||||
enum: ['add', 'update', 'remove', 'complete', 'processing'],
|
||||
type: 'string',
|
||||
},
|
||||
text: {
|
||||
description: 'Required for "add" only: the text to add.',
|
||||
type: 'string',
|
||||
},
|
||||
index: {
|
||||
description:
|
||||
'Required for "update", "remove", "complete", "processing": the item index (0-based).',
|
||||
type: 'number',
|
||||
},
|
||||
newText: {
|
||||
description: 'Optional for "update" only: the new text.',
|
||||
type: 'string',
|
||||
},
|
||||
status: {
|
||||
description:
|
||||
'Optional for "update" only: set status (todo, processing, completed).',
|
||||
enum: ['todo', 'processing', 'completed'],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['type'],
|
||||
type: 'object',
|
||||
},
|
||||
type: 'array',
|
||||
},
|
||||
},
|
||||
required: ['operations'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
{
|
||||
description: 'Clear todo items. Can clear only completed items or all items.',
|
||||
name: LobeAgentApiName.clearTodos,
|
||||
humanIntervention: 'always',
|
||||
renderDisplayControl: 'expand',
|
||||
parameters: {
|
||||
properties: {
|
||||
mode: {
|
||||
description: '"completed" clears only done items, "all" clears the entire list.',
|
||||
enum: ['completed', 'all'],
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['mode'],
|
||||
type: 'object',
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Sub-Agent ====================
|
||||
{
|
||||
description:
|
||||
@@ -125,7 +268,8 @@ export const LobeAgentManifest: BuiltinToolManifest = {
|
||||
identifier: LobeAgentIdentifier,
|
||||
meta: {
|
||||
avatar: '🤖',
|
||||
description: 'Run built-in Lobe Agent capabilities, including dispatching sub-agents.',
|
||||
description:
|
||||
'Run built-in Lobe Agent capabilities: plan + todo management, sub-agent dispatch, and visual media analysis.',
|
||||
readme: 'Lobe Agent provides built-in assistant capabilities that can be expanded over time.',
|
||||
title: 'Lobe Agent',
|
||||
},
|
||||
|
||||
@@ -60,5 +60,151 @@ Use \`callSubAgent\` for a single sub-agent, \`callSubAgents\` for multiple para
|
||||
</sub_agents>
|
||||
${isDesktop ? runInClientSection : ''}`;
|
||||
|
||||
const planTodoSection = `
|
||||
<plan_and_todos>
|
||||
You have **plan and todo management** tools to organize multi-step work over time.
|
||||
|
||||
- **Plan**: A high-level strategic document describing goals, context, and overall direction. Plans do NOT contain actionable steps - they define the "what" and "why". **Plans should be stable once created** - they represent the overarching objective that rarely changes.
|
||||
- **Todo**: The concrete execution list with actionable items. Todos define the "how" - specific tasks to accomplish the plan. **Todos are dynamic** - they can be added, updated, completed, and removed as work progresses.
|
||||
|
||||
**Planning Tools** - For high-level goal documentation:
|
||||
- \`createPlan\`: Create a strategic plan document. **Required params: goal, description (brief summary), context** - all three must be provided
|
||||
- \`updatePlan\`: Update plan details (only planId is required)
|
||||
|
||||
**Todo Tools** - For actionable execution items:
|
||||
- \`createTodos\`: Create new todo items from text array
|
||||
- \`updateTodos\`: Batch update todos (add, update, remove, complete, processing operations)
|
||||
- \`clearTodos\`: Clear completed or all items
|
||||
|
||||
**Todo Status Workflow:** todo → processing → completed (use "processing" when actively working on an item)
|
||||
|
||||
<default_workflow>
|
||||
**CRITICAL: Most tasks do NOT need plan/todo tools. Only use them for complex, multi-step projects.**
|
||||
|
||||
**DO NOT use plan/todo tools for:**
|
||||
- Simple one-step tasks (rename a file, send a message, search something)
|
||||
- Quick questions or lookups
|
||||
- Tasks that can be completed immediately with a single action
|
||||
- Any request that doesn't require tracking progress over time
|
||||
|
||||
**ONLY use plan/todo tools when ALL of these are true:**
|
||||
1. The task has multiple distinct steps that need tracking
|
||||
2. The user explicitly wants to plan or organize something
|
||||
3. Progress needs to be tracked over time (not completed in one response)
|
||||
|
||||
**When plan/todo tools ARE appropriate:**
|
||||
1. **First**, use \`createPlan\` to document the goal and relevant context
|
||||
2. **Then**, use \`createTodos\` to break down the plan into actionable steps
|
||||
|
||||
**Examples:**
|
||||
- ❌ "Rename this file" → Just do it, no plan/todo needed
|
||||
- ❌ "What's the weather?" → Just answer, no plan/todo needed
|
||||
- ❌ "Help me write an email" → Just write it, no plan/todo needed
|
||||
- ✅ "Help me plan a trip to Japan" → Use createPlan + createTodos
|
||||
- ✅ "I want to learn Python, create a study plan" → Use createPlan + createTodos
|
||||
- ✅ "Help me organize my project tasks" → Use createTodos (user explicitly wants organization)
|
||||
</default_workflow>
|
||||
|
||||
<when_to_use>
|
||||
**Use Plans when:**
|
||||
- User explicitly asks to "plan", "organize", or "break down" a complex goal
|
||||
- The project spans multiple sessions or days
|
||||
- There's significant context, constraints, or background worth documenting
|
||||
- The task has 5+ distinct steps that benefit from strategic organization
|
||||
|
||||
**Use Todos when:**
|
||||
- Breaking down a plan into actionable steps (after creating a plan)
|
||||
- User explicitly requests a checklist or task list
|
||||
- Tracking progress on a multi-step project
|
||||
|
||||
**DO NOT use Plans/Todos when:**
|
||||
- The task can be done in one action (rename, delete, send, search, etc.)
|
||||
- The user just wants something done, not organized
|
||||
- The task will be completed in this single conversation
|
||||
- The user wants a task to repeat automatically on a schedule (daily/weekly/hourly) — use **lobe-cron** instead. Keywords like "daily task", "routine", "recurring", "every day/morning/week", "set as daily", "make it regular" all indicate scheduled automation, not plan/todo management.
|
||||
</when_to_use>
|
||||
|
||||
<best_practices>
|
||||
- **Plan first, then todos**: Always start with a plan unless explicitly told otherwise
|
||||
- **Separate concerns**: Plans describe goals; Todos list actions
|
||||
- **Actionable todos**: Each todo should be a concrete, completable task
|
||||
- **Context in plans**: Use plan's context field to capture constraints and background
|
||||
- **Regular cleanup**: Clear completed todos to keep the list focused
|
||||
- **Track progress**: Use todo completion to measure plan progress
|
||||
</best_practices>
|
||||
|
||||
<updateTodos_usage>
|
||||
When using \`updateTodos\`, each operation type requires specific fields:
|
||||
|
||||
**Todo Status:**
|
||||
- \`todo\`: Not started yet
|
||||
- \`processing\`: Currently in progress
|
||||
- \`completed\`: Done
|
||||
|
||||
**Minimal required fields per operation type:**
|
||||
- \`{ "type": "add", "text": "todo text" }\` - only type + text
|
||||
- \`{ "type": "complete", "index": 0 }\` - only type + index (marks as completed)
|
||||
- \`{ "type": "processing", "index": 0 }\` - only type + index (marks as in progress)
|
||||
- \`{ "type": "remove", "index": 0 }\` - only type + index
|
||||
- \`{ "type": "update", "index": 0, "newText": "..." }\` - type + index + optional newText/status
|
||||
|
||||
**Example - mark item 0 as processing, item 1 as complete:**
|
||||
\`\`\`json
|
||||
{
|
||||
"operations": [
|
||||
{ "type": "processing", "index": 0 },
|
||||
{ "type": "complete", "index": 1 }
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
**DO NOT** add extra fields like \`"status": "completed"\` for complete/processing operations - they are ignored.
|
||||
</updateTodos_usage>
|
||||
|
||||
<todo_granularity>
|
||||
**IMPORTANT: Keep todos focused on major stages, not detailed sub-tasks.**
|
||||
|
||||
- **Limit to 5-10 items**: A todo list should contain around 5-10 major milestones or stages, not 20+ detailed tasks.
|
||||
- **Think in phases**: Group related tasks into higher-level stages (e.g., "Plan itinerary" instead of listing every city separately).
|
||||
- **Use hierarchical numbering** when more detail is needed: Use "1.", "2.", "2.1", "2.2", "3." format to show parent-child relationships.
|
||||
|
||||
**Good example** (Japan trip - 7 items, stage-focused):
|
||||
- 1. Determine travel dates and duration
|
||||
- 2. Handle visa and documentation
|
||||
- 3. Book flights and accommodation
|
||||
- 4. Plan city itineraries
|
||||
- 5. Arrange local transportation
|
||||
- 6. Prepare for departure
|
||||
- 7. Final confirmation before trip
|
||||
|
||||
**Bad example** (20+ detailed items):
|
||||
- Book Tokyo hotel
|
||||
- Book Kyoto hotel
|
||||
- Book Osaka hotel
|
||||
- Buy Suica card
|
||||
- Download Google Maps
|
||||
- Download translation app
|
||||
- ... (too granular!)
|
||||
</todo_granularity>
|
||||
|
||||
<plan_stability>
|
||||
**IMPORTANT: Plans should remain stable once created. Each conversation has only ONE plan.**
|
||||
|
||||
- **Do NOT update plans** when details change (dates, locations, preferences). Instead, update the todos to reflect new information.
|
||||
- **Only use updatePlan** when the user's goal fundamentally changes (e.g., destination changes from Japan to Korea).
|
||||
- When user provides more specific information (like exact dates or preferences), **update or add todos** - not the plan.
|
||||
</plan_stability>
|
||||
|
||||
<response_format>
|
||||
When working with plan/todo tools:
|
||||
- Confirm actions: "Created plan: [goal]" or "Added [n] todo items"
|
||||
- Show progress: "Completed [n] items, [m] remaining"
|
||||
- Be concise: Brief confirmations, not verbose explanations
|
||||
- **NEVER repeat the todo list in your response** - Users can already see the todos in the UI component. Do not list or enumerate the todo items in your text output.
|
||||
</response_format>
|
||||
</plan_and_todos>
|
||||
`;
|
||||
|
||||
export const systemPrompt = `Use Lobe Agent capabilities only when the active model needs built-in assistance. Prefer the active model's native capabilities whenever they are sufficient. Follow each tool's description and schema, and use tool results to answer the user directly.
|
||||
${planTodoSection}
|
||||
${subAgentSection}`;
|
||||
|
||||
@@ -4,6 +4,11 @@ export const LobeAgentApiName = {
|
||||
analyzeVisualMedia: 'analyzeVisualMedia',
|
||||
callSubAgent: 'callSubAgent',
|
||||
callSubAgents: 'callSubAgents',
|
||||
clearTodos: 'clearTodos',
|
||||
createPlan: 'createPlan',
|
||||
createTodos: 'createTodos',
|
||||
updatePlan: 'updatePlan',
|
||||
updateTodos: 'updateTodos',
|
||||
} as const;
|
||||
|
||||
export type LobeAgentApiNameType = (typeof LobeAgentApiName)[keyof typeof LobeAgentApiName];
|
||||
@@ -95,3 +100,194 @@ export interface CallClientSubAgentsState {
|
||||
tasks: SubAgentTask[];
|
||||
type: 'execClientSubAgents';
|
||||
}
|
||||
|
||||
// ==================== Todo Item ====================
|
||||
|
||||
/** Status of a todo item */
|
||||
export type TodoStatus = 'todo' | 'processing' | 'completed';
|
||||
|
||||
export interface TodoItem {
|
||||
/** Status of the todo item */
|
||||
status: TodoStatus;
|
||||
/** The todo item text */
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** Get the next status in the cycle: todo → processing → completed → todo */
|
||||
export const getNextTodoStatus = (current: TodoStatus): TodoStatus => {
|
||||
const cycle: TodoStatus[] = ['todo', 'processing', 'completed'];
|
||||
const index = cycle.indexOf(current);
|
||||
return cycle[(index + 1) % cycle.length];
|
||||
};
|
||||
|
||||
export interface TodoList {
|
||||
items: TodoItem[];
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
/** Alias for TodoList, used for state storage in Plan metadata */
|
||||
export type TodoState = TodoList;
|
||||
|
||||
// ==================== Todo Params ====================
|
||||
|
||||
/**
|
||||
* Create new todo items
|
||||
* - AI input: { adds: string[] } - array of text strings from AI
|
||||
* - After user edit: { items: TodoItem[] } - saved format with TodoItem objects
|
||||
*/
|
||||
export interface CreateTodosParams {
|
||||
/** Array of text strings from AI */
|
||||
adds?: string[];
|
||||
/** Array of TodoItem objects (saved format after user edit) */
|
||||
items?: TodoItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update operation types for batch updates
|
||||
*/
|
||||
export type TodoUpdateOperationType = 'add' | 'update' | 'remove' | 'complete' | 'processing';
|
||||
|
||||
/**
|
||||
* Single update operation
|
||||
*/
|
||||
export interface TodoUpdateOperation {
|
||||
/** For 'update', 'remove', 'complete', 'processing': the index of the item (0-based) */
|
||||
index?: number;
|
||||
/** For 'update': the new text */
|
||||
newText?: string;
|
||||
/** For 'update': the new status */
|
||||
status?: TodoStatus;
|
||||
/** For 'add': the text to add */
|
||||
text?: string;
|
||||
/** Operation type */
|
||||
type: TodoUpdateOperationType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update todo list with batch operations
|
||||
* Supports: add, update, remove, complete, processing
|
||||
*/
|
||||
export interface UpdateTodosParams {
|
||||
/** Array of update operations to apply */
|
||||
operations: TodoUpdateOperation[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear todo items
|
||||
*/
|
||||
export interface ClearTodosParams {
|
||||
/** Clear mode: 'completed' only clears done items, 'all' clears everything */
|
||||
mode: 'completed' | 'all';
|
||||
}
|
||||
|
||||
// ==================== Todo State Types for Render ====================
|
||||
|
||||
export interface CreateTodosState {
|
||||
/** Items that were created */
|
||||
createdItems: string[];
|
||||
/** Current todo list after creation */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface UpdateTodosState {
|
||||
/** Operations that were applied */
|
||||
appliedOperations: TodoUpdateOperation[];
|
||||
/** Current todo list after update */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface CompleteTodosState {
|
||||
/** Indices that were completed */
|
||||
completedIndices: number[];
|
||||
/** Current todo list after completion */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface RemoveTodosState {
|
||||
/** Indices that were removed */
|
||||
removedIndices: number[];
|
||||
/** Current todo list after removal */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
export interface ClearTodosState {
|
||||
/** Number of items cleared */
|
||||
clearedCount: number;
|
||||
/** Mode used for clearing */
|
||||
mode: 'completed' | 'all';
|
||||
/** Current todo list after clearing */
|
||||
todos: TodoList;
|
||||
}
|
||||
|
||||
// ==================== Planning Params ====================
|
||||
|
||||
/**
|
||||
* Create a high-level plan document
|
||||
* Plans define the strategic direction (what and why), not actionable steps
|
||||
*
|
||||
* Field mapping to Document:
|
||||
* - goal -> document.title
|
||||
* - description -> document.description
|
||||
* - context -> document.content
|
||||
*/
|
||||
export interface CreatePlanParams {
|
||||
/** Detailed context, background, constraints (maps to document.content) */
|
||||
context?: string;
|
||||
/** Brief summary of the plan (maps to document.description) */
|
||||
description: string;
|
||||
/** The main goal or objective to achieve (maps to document.title) */
|
||||
goal: string;
|
||||
}
|
||||
|
||||
export interface UpdatePlanParams {
|
||||
/** Mark plan as completed */
|
||||
completed?: boolean;
|
||||
/** Updated context (maps to document.content) */
|
||||
context?: string;
|
||||
/** Updated description (maps to document.description) */
|
||||
description?: string;
|
||||
/** Updated goal (maps to document.title) */
|
||||
goal?: string;
|
||||
/** Plan ID to update */
|
||||
planId: string;
|
||||
}
|
||||
|
||||
// ==================== Plan Result Types ====================
|
||||
|
||||
/**
|
||||
* A high-level plan document
|
||||
* Contains goal and context, but no steps (steps are managed via Todos)
|
||||
*
|
||||
* Field mapping to Document:
|
||||
* - goal -> document.title
|
||||
* - description -> document.description
|
||||
* - context -> document.content
|
||||
*/
|
||||
export interface Plan {
|
||||
/** Whether the plan is completed */
|
||||
completed: boolean;
|
||||
/** Detailed context, background, constraints (maps to document.content) */
|
||||
context?: string;
|
||||
/** Creation timestamp */
|
||||
createdAt: string;
|
||||
/** Brief summary of the plan (maps to document.description) */
|
||||
description: string;
|
||||
/** The main goal or objective (maps to document.title) */
|
||||
goal: string;
|
||||
/** Unique plan identifier */
|
||||
id: string;
|
||||
/** Last update timestamp */
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
// ==================== Plan State Types for Render ====================
|
||||
|
||||
export interface CreatePlanState {
|
||||
/** The created plan document */
|
||||
plan: Plan;
|
||||
}
|
||||
|
||||
export interface UpdatePlanState {
|
||||
/** The updated plan document */
|
||||
plan: Plan;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"@lobechat/builtin-tool-creds": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-agent-builder": "workspace:*",
|
||||
"@lobechat/builtin-tool-group-management": "workspace:*",
|
||||
"@lobechat/builtin-tool-gtd": "workspace:*",
|
||||
"@lobechat/builtin-tool-knowledge-base": "workspace:*",
|
||||
"@lobechat/builtin-tool-lobe-agent": "workspace:*",
|
||||
"@lobechat/builtin-tool-local-system": "workspace:*",
|
||||
|
||||
@@ -7,7 +7,6 @@ import { CloudSandboxManifest } from '@lobechat/builtin-tool-cloud-sandbox';
|
||||
import { CredsManifest } from '@lobechat/builtin-tool-creds';
|
||||
import { GroupAgentBuilderManifest } from '@lobechat/builtin-tool-group-agent-builder';
|
||||
import { GroupManagementManifest } from '@lobechat/builtin-tool-group-management';
|
||||
import { GTDManifest } from '@lobechat/builtin-tool-gtd';
|
||||
import { KnowledgeBaseManifest } from '@lobechat/builtin-tool-knowledge-base';
|
||||
import { LobeAgentManifest } from '@lobechat/builtin-tool-lobe-agent';
|
||||
import { LocalSystemManifest } from '@lobechat/builtin-tool-local-system';
|
||||
@@ -31,7 +30,6 @@ export const builtinToolIdentifiers: string[] = [
|
||||
CredsManifest.identifier,
|
||||
GroupAgentBuilderManifest.identifier,
|
||||
GroupManagementManifest.identifier,
|
||||
GTDManifest.identifier,
|
||||
KnowledgeBaseManifest.identifier,
|
||||
LocalSystemManifest.identifier,
|
||||
MemoryManifest.identifier,
|
||||
|
||||
@@ -8,7 +8,6 @@ import { CloudSandboxManifest } from '@lobechat/builtin-tool-cloud-sandbox';
|
||||
import { CredsManifest } from '@lobechat/builtin-tool-creds';
|
||||
import { GroupAgentBuilderManifest } from '@lobechat/builtin-tool-group-agent-builder';
|
||||
import { GroupManagementManifest } from '@lobechat/builtin-tool-group-management';
|
||||
import { GTDManifest } from '@lobechat/builtin-tool-gtd';
|
||||
import { KnowledgeBaseManifest } from '@lobechat/builtin-tool-knowledge-base';
|
||||
import { LobeAgentManifest } from '@lobechat/builtin-tool-lobe-agent';
|
||||
import { LocalSystemManifest } from '@lobechat/builtin-tool-local-system';
|
||||
@@ -43,7 +42,6 @@ export const defaultToolIds = [
|
||||
CloudSandboxManifest.identifier,
|
||||
TopicReferenceManifest.identifier,
|
||||
AgentDocumentsManifest.identifier,
|
||||
GTDManifest.identifier,
|
||||
TaskManifest.identifier,
|
||||
LobeAgentManifest.identifier,
|
||||
];
|
||||
@@ -202,11 +200,6 @@ export const builtinTools: LobeBuiltinTool[] = [
|
||||
manifest: AgentManagementManifest,
|
||||
type: 'builtin',
|
||||
},
|
||||
{
|
||||
identifier: GTDManifest.identifier,
|
||||
manifest: GTDManifest,
|
||||
type: 'builtin',
|
||||
},
|
||||
{
|
||||
identifier: CalculatorManifest.identifier,
|
||||
manifest: CalculatorManifest,
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
GroupManagementInspectors,
|
||||
GroupManagementManifest,
|
||||
} from '@lobechat/builtin-tool-group-management/client';
|
||||
import { GTDInspectors, GTDManifest } from '@lobechat/builtin-tool-gtd/client';
|
||||
import {
|
||||
KnowledgeBaseInspectors,
|
||||
KnowledgeBaseManifest,
|
||||
@@ -87,7 +86,6 @@ const BuiltinToolInspectors: Record<string, Record<string, BuiltinInspector>> =
|
||||
string,
|
||||
BuiltinInspector
|
||||
>,
|
||||
[GTDManifest.identifier]: GTDInspectors as Record<string, BuiltinInspector>,
|
||||
[KnowledgeBaseManifest.identifier]: KnowledgeBaseInspectors as Record<string, BuiltinInspector>,
|
||||
[LobeAgentManifest.identifier]: LobeAgentInspectors as Record<string, BuiltinInspector>,
|
||||
[LocalSystemManifest.identifier]: LocalSystemInspectors as Record<string, BuiltinInspector>,
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
GroupManagementInterventions,
|
||||
GroupManagementManifest,
|
||||
} from '@lobechat/builtin-tool-group-management/client';
|
||||
import { GTDInterventions, GTDManifest } from '@lobechat/builtin-tool-gtd/client';
|
||||
import {
|
||||
LobeAgentInterventions,
|
||||
LobeAgentManifest,
|
||||
} from '@lobechat/builtin-tool-lobe-agent/client';
|
||||
import {
|
||||
LocalSystemIdentifier,
|
||||
LocalSystemInterventions,
|
||||
@@ -39,7 +42,7 @@ export const BuiltinToolInterventions: Record<string, Record<string, any>> = {
|
||||
[ClaudeCodeIdentifier]: ClaudeCodeInterventions,
|
||||
[CloudSandboxManifest.identifier]: CloudSandboxInterventions,
|
||||
[GroupManagementManifest.identifier]: GroupManagementInterventions,
|
||||
[GTDManifest.identifier]: GTDInterventions,
|
||||
[LobeAgentManifest.identifier]: LobeAgentInterventions,
|
||||
[LocalSystemIdentifier]: LocalSystemInterventions,
|
||||
[MemoryManifest.identifier]: MemoryInterventions,
|
||||
[MessageManifest.identifier]: MessageInterventions,
|
||||
|
||||
@@ -15,7 +15,6 @@ import { GroupAgentBuilderManifest } from '@lobechat/builtin-tool-group-agent-bu
|
||||
import { GroupAgentBuilderRenders } from '@lobechat/builtin-tool-group-agent-builder/client';
|
||||
import { GroupManagementManifest } from '@lobechat/builtin-tool-group-management';
|
||||
import { GroupManagementRenders } from '@lobechat/builtin-tool-group-management/client';
|
||||
import { GTDManifest, GTDRenders } from '@lobechat/builtin-tool-gtd/client';
|
||||
import {
|
||||
KnowledgeBaseManifest,
|
||||
KnowledgeBaseRenders,
|
||||
@@ -64,7 +63,6 @@ const BuiltinToolsRenders: Record<string, Record<string, BuiltinRender>> = {
|
||||
[CloudSandboxManifest.identifier]: CloudSandboxRenders as Record<string, BuiltinRender>,
|
||||
[GroupAgentBuilderManifest.identifier]: GroupAgentBuilderRenders as Record<string, BuiltinRender>,
|
||||
[GroupManagementManifest.identifier]: GroupManagementRenders as Record<string, BuiltinRender>,
|
||||
[GTDManifest.identifier]: GTDRenders as Record<string, BuiltinRender>,
|
||||
[KnowledgeBaseManifest.identifier]: KnowledgeBaseRenders as Record<string, BuiltinRender>,
|
||||
[LobeAgentManifest.identifier]: LobeAgentRenders as Record<string, BuiltinRender>,
|
||||
[LocalSystemManifest.identifier]: LocalSystemRenders as Record<string, BuiltinRender>,
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
GroupManagementManifest,
|
||||
GroupManagementStreamings,
|
||||
} from '@lobechat/builtin-tool-group-management/client';
|
||||
import { GTDManifest, GTDStreamings } from '@lobechat/builtin-tool-gtd/client';
|
||||
import { LobeAgentManifest, LobeAgentStreamings } from '@lobechat/builtin-tool-lobe-agent/client';
|
||||
import {
|
||||
LocalSystemManifest,
|
||||
@@ -61,7 +60,6 @@ const BuiltinToolStreamings: Record<string, Record<string, BuiltinStreaming>> =
|
||||
string,
|
||||
BuiltinStreaming
|
||||
>,
|
||||
[GTDManifest.identifier]: GTDStreamings as Record<string, BuiltinStreaming>,
|
||||
[LobeAgentManifest.identifier]: LobeAgentStreamings as Record<string, BuiltinStreaming>,
|
||||
[LocalSystemManifest.identifier]: LocalSystemStreamings as Record<string, BuiltinStreaming>,
|
||||
[MemoryManifest.identifier]: MemoryStreamings as Record<string, BuiltinStreaming>,
|
||||
|
||||
@@ -14,7 +14,6 @@ export const RECOMMENDED_SKILLS: RecommendedSkillItem[] = [
|
||||
{ id: 'lobe-artifacts', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-user-memory', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-cloud-sandbox', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-gtd', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-task', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-agent-documents', type: RecommendedSkillType.Builtin },
|
||||
{ id: 'lobe-message', type: RecommendedSkillType.Builtin },
|
||||
|
||||
@@ -1391,7 +1391,7 @@ describe('GoogleGenerativeAIStream', () => {
|
||||
parts: [
|
||||
{
|
||||
functionCall: {
|
||||
name: 'lobe-gtd____createPlan',
|
||||
name: 'lobe-agent____createPlan',
|
||||
args: {
|
||||
goal: 'Fix Linear API Argument Validation Error',
|
||||
description: 'Investigate the Linear API error.',
|
||||
@@ -1424,7 +1424,7 @@ describe('GoogleGenerativeAIStream', () => {
|
||||
parts: [
|
||||
{
|
||||
functionCall: {
|
||||
name: 'lobe-gtd____createTodos',
|
||||
name: 'lobe-agent____createTodos',
|
||||
args: {
|
||||
adds: [
|
||||
'Verify Linear GraphQL API requirements',
|
||||
@@ -1498,12 +1498,12 @@ describe('GoogleGenerativeAIStream', () => {
|
||||
// First tool call (createPlan)
|
||||
'id: chat_test',
|
||||
'event: tool_calls',
|
||||
'data: [{"function":{"arguments":"{\\"goal\\":\\"Fix Linear API Argument Validation Error\\",\\"description\\":\\"Investigate the Linear API error.\\",\\"context\\":\\"The user is encountering a validation error.\\"}","name":"lobe-gtd____createPlan"},"id":"lobe-gtd____createPlan_0_tool_id_1","index":0,"thoughtSignature":"EoIYCv8XAXLI2nx+C18votz5l0A...","type":"function"}]\n',
|
||||
'data: [{"function":{"arguments":"{\\"goal\\":\\"Fix Linear API Argument Validation Error\\",\\"description\\":\\"Investigate the Linear API error.\\",\\"context\\":\\"The user is encountering a validation error.\\"}","name":"lobe-agent____createPlan"},"id":"lobe-agent____createPlan_0_tool_id_1","index":0,"thoughtSignature":"EoIYCv8XAXLI2nx+C18votz5l0A...","type":"function"}]\n',
|
||||
|
||||
// Second tool call (createTodos) - should be a SEPARATE event with index:0
|
||||
'id: chat_test',
|
||||
'event: tool_calls',
|
||||
'data: [{"function":{"arguments":"{\\"adds\\":[\\"Verify Linear GraphQL API requirements\\",\\"Determine if code needs to look up Team UUID\\",\\"Provide corrected code\\"]}","name":"lobe-gtd____createTodos"},"id":"lobe-gtd____createTodos_0_tool_id_2","index":0,"type":"function"}]\n',
|
||||
'data: [{"function":{"arguments":"{\\"adds\\":[\\"Verify Linear GraphQL API requirements\\",\\"Determine if code needs to look up Team UUID\\",\\"Provide corrected code\\"]}","name":"lobe-agent____createTodos"},"id":"lobe-agent____createTodos_0_tool_id_2","index":0,"type":"function"}]\n',
|
||||
|
||||
// Stop and usage
|
||||
'id: chat_test',
|
||||
|
||||
@@ -15,7 +15,7 @@ export type StepContextTodoStatus = 'todo' | 'processing' | 'completed';
|
||||
|
||||
/**
|
||||
* Todo item structure
|
||||
* Duplicated here to avoid circular dependency with builtin-tool-gtd
|
||||
* Duplicated here to avoid circular dependency with builtin-tool-lobe-agent
|
||||
*/
|
||||
export interface StepContextTodoItem {
|
||||
status: StepContextTodoStatus;
|
||||
|
||||
@@ -16,7 +16,6 @@ import lobeAgentManagement from './lobe-agent-management';
|
||||
import lobeCloudSandbox from './lobe-cloud-sandbox';
|
||||
import lobeGroupAgentBuilder from './lobe-group-agent-builder';
|
||||
import lobeGroupManagement from './lobe-group-management';
|
||||
import lobeGtd from './lobe-gtd';
|
||||
import lobeKnowledgeBase from './lobe-knowledge-base';
|
||||
import lobeLocalSystem from './lobe-local-system';
|
||||
import lobeNotebook from './lobe-notebook';
|
||||
@@ -75,7 +74,6 @@ const toolsetModules: ToolsetFixtureModule[] = [
|
||||
lobeCloudSandbox,
|
||||
lobeGroupAgentBuilder,
|
||||
lobeGroupManagement,
|
||||
lobeGtd,
|
||||
lobeKnowledgeBase,
|
||||
lobeLocalSystem,
|
||||
lobeNotebook,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { defineFixtures, single } from './_helpers';
|
||||
import { defineFixtures, single, variants } from './_helpers';
|
||||
|
||||
export default defineFixtures({
|
||||
identifier: 'lobe-agent',
|
||||
@@ -28,5 +28,85 @@ export default defineFixtures({
|
||||
],
|
||||
},
|
||||
}),
|
||||
clearTodos: single({
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Capture real stream data' },
|
||||
{ status: 'processing', text: 'Build /devtools route' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
createPlan: single({
|
||||
pluginState: {
|
||||
plan: {
|
||||
context:
|
||||
'We want a reusable development-only page that renders every registered builtin tool card with a stable sample fixture.',
|
||||
description: 'Create a maintainable preview harness for builtin tool renders.',
|
||||
goal: 'Build /devtools render preview',
|
||||
id: 'plan_devtools_preview',
|
||||
},
|
||||
},
|
||||
}),
|
||||
createTodos: variants([
|
||||
{
|
||||
label: 'Mixed',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Enumerate all render entries' },
|
||||
{ status: 'processing', text: 'Create preview fixtures' },
|
||||
{ status: 'todo', text: 'Smoke test the route locally' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Many todos',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: Array.from({ length: 10 }, (_, i) => ({
|
||||
status: i < 3 ? 'completed' : i < 5 ? 'processing' : 'todo',
|
||||
text: `Subtask ${i + 1}: prepare fixtures for batch ${i + 1}`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'All done',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Enumerate render entries' },
|
||||
{ status: 'completed', text: 'Author preview fixtures' },
|
||||
{ status: 'completed', text: 'Smoke-test the gallery' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
updatePlan: single({
|
||||
pluginState: {
|
||||
plan: {
|
||||
context:
|
||||
'The route is now in place; expand the preview harness by keeping fixture data next to the page.',
|
||||
description: 'Track the follow-up work for richer render fixtures.',
|
||||
goal: 'Expand /devtools coverage',
|
||||
id: 'plan_devtools_preview',
|
||||
},
|
||||
},
|
||||
}),
|
||||
updateTodos: single({
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Export render registry entries' },
|
||||
{ status: 'processing', text: 'Hydrate grouped task fixtures' },
|
||||
{ status: 'todo', text: 'Add richer missing-state cases' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { defineFixtures, single, variants } from './_helpers';
|
||||
|
||||
export default defineFixtures({
|
||||
identifier: 'lobe-gtd',
|
||||
fixtures: {
|
||||
clearTodos: single({
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Capture real stream data' },
|
||||
{ status: 'processing', text: 'Build /devtools route' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
createPlan: single({
|
||||
pluginState: {
|
||||
plan: {
|
||||
context:
|
||||
'We want a reusable development-only page that renders every registered builtin tool card with a stable sample fixture.',
|
||||
description: 'Create a maintainable preview harness for builtin tool renders.',
|
||||
goal: 'Build /devtools render preview',
|
||||
id: 'plan_devtools_preview',
|
||||
},
|
||||
},
|
||||
}),
|
||||
createTodos: variants([
|
||||
{
|
||||
label: 'Mixed',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Enumerate all render entries' },
|
||||
{ status: 'processing', text: 'Create preview fixtures' },
|
||||
{ status: 'todo', text: 'Smoke test the route locally' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Many todos',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: Array.from({ length: 10 }, (_, i) => ({
|
||||
status: i < 3 ? 'completed' : i < 5 ? 'processing' : 'todo',
|
||||
text: `Subtask ${i + 1}: prepare fixtures for batch ${i + 1}`,
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'All done',
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Enumerate render entries' },
|
||||
{ status: 'completed', text: 'Author preview fixtures' },
|
||||
{ status: 'completed', text: 'Smoke-test the gallery' },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]),
|
||||
updatePlan: single({
|
||||
pluginState: {
|
||||
plan: {
|
||||
context:
|
||||
'The route is now in place; expand the preview harness by keeping fixture data next to the page.',
|
||||
description: 'Track the follow-up work for richer render fixtures.',
|
||||
goal: 'Expand /devtools coverage',
|
||||
id: 'plan_devtools_preview',
|
||||
},
|
||||
},
|
||||
}),
|
||||
updateTodos: single({
|
||||
pluginState: {
|
||||
todos: {
|
||||
items: [
|
||||
{ status: 'completed', text: 'Export render registry entries' },
|
||||
{ status: 'processing', text: 'Hydrate grouped task fixtures' },
|
||||
{ status: 'todo', text: 'Add richer missing-state cases' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -124,20 +124,17 @@ export default {
|
||||
'builtins.lobe-group-management.inspector.executeAgentTasks.title': 'Assigning tasks to:',
|
||||
'builtins.lobe-group-management.inspector.speak.title': 'Designated Agent speaks:',
|
||||
'builtins.lobe-group-management.title': 'Group Coordinator',
|
||||
'builtins.lobe-gtd.apiName.clearTodos': 'Clear todos',
|
||||
'builtins.lobe-gtd.apiName.clearTodos.modeAll': 'all',
|
||||
'builtins.lobe-gtd.apiName.clearTodos.modeCompleted': 'completed',
|
||||
'builtins.lobe-gtd.apiName.clearTodos.result': 'Clear <mode>{{mode}}</mode> todos',
|
||||
'builtins.lobe-gtd.apiName.completeTodos': 'Complete todos',
|
||||
'builtins.lobe-gtd.apiName.createPlan': 'Create plan',
|
||||
'builtins.lobe-gtd.apiName.createPlan.result': 'Create plan: <goal>{{goal}}</goal>',
|
||||
'builtins.lobe-gtd.apiName.createTodos': 'Create todos',
|
||||
'builtins.lobe-gtd.apiName.removeTodos': 'Delete todos',
|
||||
'builtins.lobe-gtd.apiName.updatePlan': 'Update plan',
|
||||
'builtins.lobe-gtd.apiName.updatePlan.completed': 'Completed',
|
||||
'builtins.lobe-gtd.apiName.updatePlan.modified': 'Modified',
|
||||
'builtins.lobe-gtd.apiName.updateTodos': 'Update todos',
|
||||
'builtins.lobe-gtd.title': 'Task Tools',
|
||||
'builtins.lobe-agent.apiName.clearTodos': 'Clear todos',
|
||||
'builtins.lobe-agent.apiName.clearTodos.modeAll': 'all',
|
||||
'builtins.lobe-agent.apiName.clearTodos.modeCompleted': 'completed',
|
||||
'builtins.lobe-agent.apiName.clearTodos.result': 'Clear <mode>{{mode}}</mode> todos',
|
||||
'builtins.lobe-agent.apiName.createPlan': 'Create plan',
|
||||
'builtins.lobe-agent.apiName.createPlan.result': 'Create plan: <goal>{{goal}}</goal>',
|
||||
'builtins.lobe-agent.apiName.createTodos': 'Create todos',
|
||||
'builtins.lobe-agent.apiName.updatePlan': 'Update plan',
|
||||
'builtins.lobe-agent.apiName.updatePlan.completed': 'Completed',
|
||||
'builtins.lobe-agent.apiName.updatePlan.modified': 'Modified',
|
||||
'builtins.lobe-agent.apiName.updateTodos': 'Update todos',
|
||||
'builtins.lobe-knowledge-base.apiName.readKnowledge': 'Read Library content',
|
||||
'builtins.lobe-knowledge-base.apiName.searchKnowledgeBase': 'Search Library',
|
||||
'builtins.lobe-knowledge-base.inspector.andMoreFiles': 'and {{count}} more',
|
||||
|
||||
@@ -1049,10 +1049,6 @@ When I am ___, I need ___
|
||||
'tools.builtins.lobe-cloud-sandbox.readme':
|
||||
'Execute Python, JavaScript, and TypeScript code in an isolated cloud environment. Run shell commands, manage files, search content with regex, and export results securely.',
|
||||
'tools.builtins.lobe-cloud-sandbox.title': 'Cloud Sandbox',
|
||||
'tools.builtins.lobe-gtd.description': 'Plan goals and track progress with GTD methodology',
|
||||
'tools.builtins.lobe-gtd.readme':
|
||||
'Plan goals and track progress using GTD methodology. Create strategic plans, manage todo lists with status tracking, and execute long-running async tasks.',
|
||||
'tools.builtins.lobe-gtd.title': 'GTD Tools',
|
||||
'tools.builtins.lobe-local-system.description':
|
||||
'Access and manage local files, run shell commands on your desktop',
|
||||
'tools.builtins.lobe-local-system.readme':
|
||||
|
||||
+45
-45
@@ -59,51 +59,51 @@ export default {
|
||||
'dalle.generating': 'Generating...',
|
||||
'dalle.images': 'Images:',
|
||||
'dalle.prompt': 'Prompt',
|
||||
'lobe-gtd.actions.add': 'Add',
|
||||
'lobe-gtd.actions.clearCompleted': 'Clear Completed',
|
||||
'lobe-gtd.actions.placeholder': 'Enter a to-do item...',
|
||||
'lobe-gtd.addTodo.placeholder': 'Add a todo item...',
|
||||
'lobe-gtd.clearTodos.cleared': '{{count}} item(s) cleared',
|
||||
'lobe-gtd.clearTodos.clearedCompleted': '{{count}} completed item(s) cleared',
|
||||
'lobe-gtd.clearTodos.clearedCompleted_one': '{{count}} completed item cleared',
|
||||
'lobe-gtd.clearTodos.clearedCompleted_other': '{{count}} completed items cleared',
|
||||
'lobe-gtd.clearTodos.cleared_one': '{{count}} item cleared',
|
||||
'lobe-gtd.clearTodos.cleared_other': '{{count}} items cleared',
|
||||
'lobe-gtd.clearTodos.header': 'Clear Todo Items',
|
||||
'lobe-gtd.clearTodos.label': 'Choose what to clear:',
|
||||
'lobe-gtd.clearTodos.noItems': 'No items to clear',
|
||||
'lobe-gtd.clearTodos.option.all': 'Clear all items (including pending)',
|
||||
'lobe-gtd.clearTodos.option.completed': 'Clear completed items only',
|
||||
'lobe-gtd.clearTodos.remaining': '{{count}} item(s) remaining',
|
||||
'lobe-gtd.clearTodos.remaining_one': '{{count}} item remaining',
|
||||
'lobe-gtd.clearTodos.remaining_other': '{{count}} items remaining',
|
||||
'lobe-gtd.completeTodos.completed': '{{count}} item(s) completed',
|
||||
'lobe-gtd.completeTodos.completed_one': '{{count}} item completed',
|
||||
'lobe-gtd.completeTodos.completed_other': '{{count}} items completed',
|
||||
'lobe-gtd.createPlan.context.label': 'Context (optional)',
|
||||
'lobe-gtd.createPlan.context.placeholder': 'Background, constraints, considerations...',
|
||||
'lobe-gtd.createPlan.description.label': 'Description',
|
||||
'lobe-gtd.createPlan.description.placeholder': 'Brief summary of the plan',
|
||||
'lobe-gtd.createPlan.goal.label': 'Goal',
|
||||
'lobe-gtd.createPlan.goal.placeholder': 'What do you want to achieve?',
|
||||
'lobe-gtd.createTodos.created': '{{count}} to-do item(s) created',
|
||||
'lobe-gtd.createTodos.created_one': '{{count}} to-do item created',
|
||||
'lobe-gtd.createTodos.created_other': '{{count}} to-do items created',
|
||||
'lobe-gtd.createTodos.total': 'Total: {{count}} item(s)',
|
||||
'lobe-gtd.createTodos.total_one': 'Total: {{count}} item',
|
||||
'lobe-gtd.createTodos.total_other': 'Total: {{count}} items',
|
||||
'lobe-gtd.removeTodos.removed': '{{count}} item(s) removed',
|
||||
'lobe-gtd.removeTodos.removed_one': '{{count}} item removed',
|
||||
'lobe-gtd.removeTodos.removed_other': '{{count}} items removed',
|
||||
'lobe-gtd.status.done': '{{count}} completed',
|
||||
'lobe-gtd.status.pending': '{{count}} pending',
|
||||
'lobe-gtd.todoItem.placeholder': 'Enter todo item...',
|
||||
'lobe-gtd.todoList.empty': 'To-do list is empty',
|
||||
'lobe-gtd.todoList.items': '{{count}} item(s)',
|
||||
'lobe-gtd.todoList.items_one': '{{count}} item',
|
||||
'lobe-gtd.todoList.items_other': '{{count}} items',
|
||||
'lobe-gtd.todoList.title': 'To-Do List',
|
||||
'lobe-gtd.updateTodos.updated': 'To-do list updated',
|
||||
'lobe-agent.actions.add': 'Add',
|
||||
'lobe-agent.actions.clearCompleted': 'Clear Completed',
|
||||
'lobe-agent.actions.placeholder': 'Enter a to-do item...',
|
||||
'lobe-agent.addTodo.placeholder': 'Add a todo item...',
|
||||
'lobe-agent.clearTodos.cleared': '{{count}} item(s) cleared',
|
||||
'lobe-agent.clearTodos.clearedCompleted': '{{count}} completed item(s) cleared',
|
||||
'lobe-agent.clearTodos.clearedCompleted_one': '{{count}} completed item cleared',
|
||||
'lobe-agent.clearTodos.clearedCompleted_other': '{{count}} completed items cleared',
|
||||
'lobe-agent.clearTodos.cleared_one': '{{count}} item cleared',
|
||||
'lobe-agent.clearTodos.cleared_other': '{{count}} items cleared',
|
||||
'lobe-agent.clearTodos.header': 'Clear Todo Items',
|
||||
'lobe-agent.clearTodos.label': 'Choose what to clear:',
|
||||
'lobe-agent.clearTodos.noItems': 'No items to clear',
|
||||
'lobe-agent.clearTodos.option.all': 'Clear all items (including pending)',
|
||||
'lobe-agent.clearTodos.option.completed': 'Clear completed items only',
|
||||
'lobe-agent.clearTodos.remaining': '{{count}} item(s) remaining',
|
||||
'lobe-agent.clearTodos.remaining_one': '{{count}} item remaining',
|
||||
'lobe-agent.clearTodos.remaining_other': '{{count}} items remaining',
|
||||
'lobe-agent.completeTodos.completed': '{{count}} item(s) completed',
|
||||
'lobe-agent.completeTodos.completed_one': '{{count}} item completed',
|
||||
'lobe-agent.completeTodos.completed_other': '{{count}} items completed',
|
||||
'lobe-agent.createPlan.context.label': 'Context (optional)',
|
||||
'lobe-agent.createPlan.context.placeholder': 'Background, constraints, considerations...',
|
||||
'lobe-agent.createPlan.description.label': 'Description',
|
||||
'lobe-agent.createPlan.description.placeholder': 'Brief summary of the plan',
|
||||
'lobe-agent.createPlan.goal.label': 'Goal',
|
||||
'lobe-agent.createPlan.goal.placeholder': 'What do you want to achieve?',
|
||||
'lobe-agent.createTodos.created': '{{count}} to-do item(s) created',
|
||||
'lobe-agent.createTodos.created_one': '{{count}} to-do item created',
|
||||
'lobe-agent.createTodos.created_other': '{{count}} to-do items created',
|
||||
'lobe-agent.createTodos.total': 'Total: {{count}} item(s)',
|
||||
'lobe-agent.createTodos.total_one': 'Total: {{count}} item',
|
||||
'lobe-agent.createTodos.total_other': 'Total: {{count}} items',
|
||||
'lobe-agent.removeTodos.removed': '{{count}} item(s) removed',
|
||||
'lobe-agent.removeTodos.removed_one': '{{count}} item removed',
|
||||
'lobe-agent.removeTodos.removed_other': '{{count}} items removed',
|
||||
'lobe-agent.status.done': '{{count}} completed',
|
||||
'lobe-agent.status.pending': '{{count}} pending',
|
||||
'lobe-agent.todoItem.placeholder': 'Enter todo item...',
|
||||
'lobe-agent.todoList.empty': 'To-do list is empty',
|
||||
'lobe-agent.todoList.items': '{{count}} item(s)',
|
||||
'lobe-agent.todoList.items_one': '{{count}} item',
|
||||
'lobe-agent.todoList.items_other': '{{count}} items',
|
||||
'lobe-agent.todoList.title': 'To-Do List',
|
||||
'lobe-agent.updateTodos.updated': 'To-do list updated',
|
||||
'lobe-knowledge-base.readKnowledge.meta.chars': 'Character Count',
|
||||
'lobe-knowledge-base.readKnowledge.meta.lines': 'Line Count',
|
||||
'localFiles.editFile.newString': 'Replace with',
|
||||
|
||||
@@ -14,7 +14,6 @@ import { briefRuntime } from './brief';
|
||||
import { calculatorRuntime } from './calculator';
|
||||
import { cloudSandboxRuntime } from './cloudSandbox';
|
||||
import { credsRuntime } from './creds';
|
||||
import { gtdRuntime } from './gtd';
|
||||
import { knowledgeBaseRuntime } from './knowledgeBase';
|
||||
import { lobeAgentRuntime } from './lobeAgent';
|
||||
import { localSystemRuntime } from './localSystem';
|
||||
@@ -68,7 +67,6 @@ registerRuntimes([
|
||||
topicReferenceRuntime,
|
||||
userInteractionRuntime,
|
||||
credsRuntime,
|
||||
gtdRuntime,
|
||||
knowledgeBaseRuntime,
|
||||
webOnboardingRuntime,
|
||||
lobeAgentRuntime,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
hasUserVisualFiles,
|
||||
LobeAgentIdentifier,
|
||||
normalizeAnalyzeVisualMediaInput,
|
||||
PlanExecutionRuntime,
|
||||
selectVisualFileItems,
|
||||
validateVisualMediaUrls,
|
||||
} from '@lobechat/builtin-tool-lobe-agent';
|
||||
@@ -22,6 +23,7 @@ import { toolsEnv } from '@/envs/tools';
|
||||
import { initModelRuntimeFromDB } from '@/server/modules/ModelRuntime';
|
||||
import { FileService } from '@/server/services/file';
|
||||
|
||||
import { createServerPlanRuntimeService } from './lobeAgentPlan';
|
||||
import type { ServerRuntimeRegistration } from './types';
|
||||
|
||||
interface AnalyzeVisualMediaParams {
|
||||
@@ -69,6 +71,7 @@ class LobeAgentExecutionRuntime {
|
||||
private messageId: string;
|
||||
private threadId?: string | null;
|
||||
private topicId?: string;
|
||||
private planRuntime: PlanExecutionRuntime;
|
||||
|
||||
constructor(context: LobeAgentRuntimeContext) {
|
||||
this.agentId = context.agentId;
|
||||
@@ -78,8 +81,28 @@ class LobeAgentExecutionRuntime {
|
||||
this.threadId = context.threadId;
|
||||
this.topicId = context.topicId;
|
||||
this.userId = context.userId;
|
||||
this.planRuntime = new PlanExecutionRuntime(
|
||||
createServerPlanRuntimeService(context.serverDB, context.userId),
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== Plan / Todo (delegated to PlanExecutionRuntime) ====================
|
||||
|
||||
createPlan = (params: any) =>
|
||||
this.planRuntime.createPlan(params, { messageId: this.messageId, topicId: this.topicId });
|
||||
|
||||
updatePlan = (params: any) =>
|
||||
this.planRuntime.updatePlan(params, { messageId: this.messageId, topicId: this.topicId });
|
||||
|
||||
createTodos = (params: any) =>
|
||||
this.planRuntime.createTodos(params, { messageId: this.messageId, topicId: this.topicId });
|
||||
|
||||
updateTodos = (params: any) =>
|
||||
this.planRuntime.updateTodos(params, { messageId: this.messageId, topicId: this.topicId });
|
||||
|
||||
clearTodos = (params: any) =>
|
||||
this.planRuntime.clearTodos(params, { messageId: this.messageId, topicId: this.topicId });
|
||||
|
||||
private queryScopeMessages = (
|
||||
messageModel: MessageModel,
|
||||
sourceMessage: ServerVisualSourceMessage,
|
||||
|
||||
+13
-22
@@ -1,19 +1,22 @@
|
||||
import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
|
||||
import {
|
||||
GTDExecutionRuntime,
|
||||
type GTDRuntimeService,
|
||||
type PlanDocument,
|
||||
} from '@lobechat/builtin-tool-gtd/executionRuntime';
|
||||
import { type PlanDocument, type PlanRuntimeService } from '@lobechat/builtin-tool-lobe-agent';
|
||||
import { type LobeChatDatabase } from '@lobechat/database';
|
||||
|
||||
import { DocumentModel } from '@/database/models/document';
|
||||
import { TopicDocumentModel } from '@/database/models/topicDocument';
|
||||
|
||||
import { type ServerRuntimeRegistration } from './types';
|
||||
|
||||
const PLAN_FILE_TYPE = 'agent/plan';
|
||||
|
||||
const createGTDRuntimeService = (serverDB: LobeChatDatabase, userId: string): GTDRuntimeService => {
|
||||
/**
|
||||
* Build a server-side `PlanRuntimeService` backed by the application database.
|
||||
*
|
||||
* The factory is consumed by `lobeAgent.ts`'s `LobeAgentExecutionRuntime`,
|
||||
* which folds plan/todo execution into the lobe-agent server runtime so the
|
||||
* registry has a single runtime per identifier.
|
||||
*/
|
||||
export const createServerPlanRuntimeService = (
|
||||
serverDB: LobeChatDatabase,
|
||||
userId: string,
|
||||
): PlanRuntimeService => {
|
||||
const documentModel = new DocumentModel(serverDB, userId);
|
||||
const topicDocumentModel = new TopicDocumentModel(serverDB, userId);
|
||||
|
||||
@@ -47,7 +50,7 @@ const createGTDRuntimeService = (serverDB: LobeChatDatabase, userId: string): GT
|
||||
content,
|
||||
description,
|
||||
fileType: PLAN_FILE_TYPE,
|
||||
source: `gtd:${topicId}`,
|
||||
source: `lobe-agent:${topicId}`,
|
||||
sourceType: 'api',
|
||||
title: goal,
|
||||
totalCharCount: content.length,
|
||||
@@ -93,15 +96,3 @@ const createGTDRuntimeService = (serverDB: LobeChatDatabase, userId: string): GT
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const gtdRuntime: ServerRuntimeRegistration = {
|
||||
factory: (context) => {
|
||||
if (!context.userId || !context.serverDB) {
|
||||
throw new Error('userId and serverDB are required for GTD tool execution');
|
||||
}
|
||||
|
||||
const service = createGTDRuntimeService(context.serverDB, context.userId);
|
||||
return new GTDExecutionRuntime(service);
|
||||
},
|
||||
identifier: GTDIdentifier,
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as builtinAgents from '@lobechat/builtin-agents';
|
||||
import { GroupManagementIdentifier } from '@lobechat/builtin-tool-group-management';
|
||||
import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
|
||||
import { LobeAgentIdentifier } from '@lobechat/builtin-tool-lobe-agent';
|
||||
import { NotebookIdentifier } from '@lobechat/builtin-tool-notebook';
|
||||
import { PageAgentIdentifier } from '@lobechat/builtin-tool-page-agent';
|
||||
import { TaskIdentifier } from '@lobechat/builtin-tool-task';
|
||||
@@ -464,13 +464,13 @@ describe('resolveAgentConfig', () => {
|
||||
|
||||
it('should include GTD and Notebook tools in plugins', () => {
|
||||
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
||||
plugins: [GTDIdentifier, NotebookIdentifier],
|
||||
plugins: [LobeAgentIdentifier, NotebookIdentifier],
|
||||
systemRole: 'Inbox system role',
|
||||
});
|
||||
|
||||
const result = resolveAgentConfig({ agentId: 'inbox-agent' });
|
||||
|
||||
expect(result.plugins).toContain(GTDIdentifier);
|
||||
expect(result.plugins).toContain(LobeAgentIdentifier);
|
||||
expect(result.plugins).toContain(NotebookIdentifier);
|
||||
expect(result.isBuiltinAgent).toBe(true);
|
||||
expect(result.slug).toBe('inbox');
|
||||
@@ -478,7 +478,7 @@ describe('resolveAgentConfig', () => {
|
||||
|
||||
it('should preserve user plugins while including GTD and Notebook', () => {
|
||||
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
||||
plugins: [GTDIdentifier, NotebookIdentifier, 'user-plugin'],
|
||||
plugins: [LobeAgentIdentifier, NotebookIdentifier, 'user-plugin'],
|
||||
systemRole: 'Inbox system role',
|
||||
});
|
||||
|
||||
@@ -487,7 +487,7 @@ describe('resolveAgentConfig', () => {
|
||||
plugins: ['user-plugin'],
|
||||
});
|
||||
|
||||
expect(result.plugins).toContain(GTDIdentifier);
|
||||
expect(result.plugins).toContain(LobeAgentIdentifier);
|
||||
expect(result.plugins).toContain(NotebookIdentifier);
|
||||
expect(result.plugins).toContain('user-plugin');
|
||||
});
|
||||
@@ -510,8 +510,8 @@ describe('resolveAgentConfig', () => {
|
||||
const getAgentRuntimeConfigSpy = vi
|
||||
.spyOn(builtinAgents, 'getAgentRuntimeConfig')
|
||||
.mockImplementation((slug, ctx) => ({
|
||||
// This simulates the actual INBOX runtime: [GTDIdentifier, NotebookIdentifier, ...(ctx.plugins || [])]
|
||||
plugins: [GTDIdentifier, NotebookIdentifier, ...(ctx.plugins || [])],
|
||||
// This simulates the actual INBOX runtime: [LobeAgentIdentifier, NotebookIdentifier, ...(ctx.plugins || [])]
|
||||
plugins: [LobeAgentIdentifier, NotebookIdentifier, ...(ctx.plugins || [])],
|
||||
systemRole: 'Inbox system role',
|
||||
}));
|
||||
|
||||
@@ -527,7 +527,7 @@ describe('resolveAgentConfig', () => {
|
||||
);
|
||||
|
||||
// Verify final plugins include both builtin tools AND user-configured plugins
|
||||
expect(result.plugins).toContain(GTDIdentifier);
|
||||
expect(result.plugins).toContain(LobeAgentIdentifier);
|
||||
expect(result.plugins).toContain(NotebookIdentifier);
|
||||
expect(result.plugins).toContain('web-search');
|
||||
expect(result.plugins).toContain('memory');
|
||||
@@ -853,7 +853,7 @@ describe('resolveAgentConfig', () => {
|
||||
|
||||
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
||||
chatConfig: { enableHistoryCount: false },
|
||||
plugins: [GroupManagementIdentifier, GTDIdentifier],
|
||||
plugins: [GroupManagementIdentifier, LobeAgentIdentifier],
|
||||
systemRole: 'You are a group supervisor...',
|
||||
});
|
||||
|
||||
@@ -888,7 +888,7 @@ describe('resolveAgentConfig', () => {
|
||||
// Mock: getAgentRuntimeConfig for supervisor agent
|
||||
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
||||
chatConfig: { enableHistoryCount: false },
|
||||
plugins: [GroupManagementIdentifier, GTDIdentifier],
|
||||
plugins: [GroupManagementIdentifier, LobeAgentIdentifier],
|
||||
systemRole: 'You are a group supervisor...',
|
||||
});
|
||||
|
||||
@@ -901,7 +901,7 @@ describe('resolveAgentConfig', () => {
|
||||
expect(result.isBuiltinAgent).toBe(true);
|
||||
expect(result.slug).toBe('group-supervisor');
|
||||
expect(result.plugins).toContain(GroupManagementIdentifier);
|
||||
expect(result.plugins).toContain(GTDIdentifier);
|
||||
expect(result.plugins).toContain(LobeAgentIdentifier);
|
||||
});
|
||||
|
||||
it('should pass groupSupervisorContext to getAgentRuntimeConfig', () => {
|
||||
@@ -1022,7 +1022,7 @@ describe('resolveAgentConfig', () => {
|
||||
|
||||
vi.spyOn(builtinAgents, 'getAgentRuntimeConfig').mockReturnValue({
|
||||
chatConfig: { enableHistoryCount: false },
|
||||
plugins: [GroupManagementIdentifier, GTDIdentifier],
|
||||
plugins: [GroupManagementIdentifier, LobeAgentIdentifier],
|
||||
systemRole: 'Supervisor system role',
|
||||
});
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
type KlavisServiceSummary,
|
||||
} from '@lobechat/builtin-tool-creds';
|
||||
import { GroupAgentBuilderIdentifier } from '@lobechat/builtin-tool-group-agent-builder';
|
||||
import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
|
||||
import { LobeAgentIdentifier } from '@lobechat/builtin-tool-lobe-agent';
|
||||
import { PageAgentIdentifier } from '@lobechat/builtin-tool-page-agent';
|
||||
import { WebOnboardingIdentifier } from '@lobechat/builtin-tool-web-onboarding';
|
||||
import { KLAVIS_SERVER_TYPES, LOBEHUB_SKILL_PROVIDERS } from '@lobechat/const';
|
||||
@@ -317,9 +317,9 @@ export const contextEngineering = async ({
|
||||
userMemoryData = combineUserMemoryData(topicMemories, persona);
|
||||
}
|
||||
|
||||
// Resolve GTD context: plan and todos
|
||||
// GTD tool must be enabled and topicId must be provided
|
||||
const isGTDEnabled = tools?.includes(GTDIdentifier) ?? false;
|
||||
// Resolve plan + todos context (now part of the lobe-agent tool).
|
||||
// Lobe-agent must be enabled and topicId must be provided.
|
||||
const isGTDEnabled = tools?.includes(LobeAgentIdentifier) ?? false;
|
||||
let gtdConfig: GTDConfig | undefined;
|
||||
|
||||
if (isGTDEnabled && topicId) {
|
||||
|
||||
@@ -13,7 +13,7 @@ describe('selectTodosFromMessages', () => {
|
||||
role: 'tool',
|
||||
content: 'Todos updated',
|
||||
plugin: {
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'createTodos',
|
||||
arguments: '{}',
|
||||
},
|
||||
@@ -127,7 +127,7 @@ describe('selectTodosFromMessages', () => {
|
||||
role: 'tool',
|
||||
content: 'Something',
|
||||
plugin: {
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'someOtherApi',
|
||||
arguments: '{}',
|
||||
},
|
||||
@@ -149,7 +149,7 @@ describe('selectTodosFromMessages', () => {
|
||||
role: 'tool',
|
||||
content: 'Todos',
|
||||
plugin: {
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'createTodos',
|
||||
arguments: '{}',
|
||||
},
|
||||
@@ -242,7 +242,7 @@ describe('selectTodosFromMessages', () => {
|
||||
role: 'tool',
|
||||
content: 'Todos',
|
||||
plugin: {
|
||||
identifier: 'lobe-gtd',
|
||||
identifier: 'lobe-agent',
|
||||
apiName: 'createTodos',
|
||||
arguments: '{}',
|
||||
},
|
||||
@@ -271,7 +271,7 @@ describe('selectCurrentTurnTodosFromMessages', () => {
|
||||
id: `tool-${text}`,
|
||||
role: 'tool',
|
||||
content: 'Todos updated',
|
||||
plugin: { identifier: 'lobe-gtd', apiName: 'createTodos', arguments: '{}' },
|
||||
plugin: { identifier: 'lobe-agent', apiName: 'createTodos', arguments: '{}' },
|
||||
pluginState: {
|
||||
todos: { items: [{ text, status }], updatedAt: '2026-04-20T00:00:00.000Z' },
|
||||
},
|
||||
|
||||
@@ -11,9 +11,8 @@ import { cloudSandboxExecutor } from '@lobechat/builtin-tool-cloud-sandbox/execu
|
||||
import { credsExecutor } from '@lobechat/builtin-tool-creds/executor';
|
||||
import { groupAgentBuilderExecutor } from '@lobechat/builtin-tool-group-agent-builder/executor';
|
||||
import { groupManagementExecutor } from '@lobechat/builtin-tool-group-management/executor';
|
||||
import { gtdExecutor } from '@lobechat/builtin-tool-gtd/executor';
|
||||
import { knowledgeBaseExecutor } from '@lobechat/builtin-tool-knowledge-base/client';
|
||||
import { lobeAgentExecutor } from '@lobechat/builtin-tool-lobe-agent/executor';
|
||||
import { lobeAgentExecutor } from '@lobechat/builtin-tool-lobe-agent/client';
|
||||
import { localSystemExecutor } from '@lobechat/builtin-tool-local-system/executor';
|
||||
import { memoryExecutor } from '@lobechat/builtin-tool-memory/executor';
|
||||
import { taskExecutor } from '@lobechat/builtin-tool-task/executor';
|
||||
@@ -141,7 +140,6 @@ registerExecutors([
|
||||
credsExecutor,
|
||||
groupAgentBuilderExecutor,
|
||||
groupManagementExecutor,
|
||||
gtdExecutor,
|
||||
knowledgeBaseExecutor,
|
||||
localSystemExecutor,
|
||||
memoryExecutor,
|
||||
|
||||
Reference in New Issue
Block a user