From 29542b6fd5c028dcd6437e313e5be02399e7438d Mon Sep 17 00:00:00 2001 From: lishuceo Date: Thu, 12 Mar 2026 13:38:18 +0800 Subject: [PATCH 1/2] feat: hardcode tasklist GUIDs in task tool description Bot creates tasks that must belong to one of 4 predefined tasklists (Bot/MCPTools/UrhoX/Maker). Since list_tasklists API returns empty for bot tokens, embed the GUIDs directly in the tool description so Claude always assigns tasks to the correct list. Co-Authored-By: Claude Opus 4.6 --- src/feishu/tools/task.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/feishu/tools/task.ts b/src/feishu/tools/task.ts index 4b191039..d6d77cff 100644 --- a/src/feishu/tools/task.ts +++ b/src/feishu/tools/task.ts @@ -157,7 +157,7 @@ export function feishuTaskTool(getUserToken?: () => Promise, '创建和管理飞书任务 (Task v2)。', '', 'Actions:', - '- create: 创建任务 (需要 summary, 可选 tasklists 指定归属清单)', + '- create: 创建任务 (需要 summary, 必须通过 tasklists 指定归属清单)', '- get: 获取任务详情 (需要 task_guid)', '- list: 查询任务列表 (支持 completed/page_size 过滤)', '- list_tasklists: 列出可用的任务清单 (返回 guid + name)', @@ -166,9 +166,16 @@ export function feishuTaskTool(getUserToken?: () => Promise, '- add_members: 添加任务成员 (需要 task_guid + members)', '- remove_members: 移除任务成员 (需要 task_guid + members)', '', + '⚠️ 创建任务时必须归入以下 4 个清单之一,根据任务内容选择最合适的清单,不确定时默认用 Bot:', + ' - Bot (默认): c7f1907f-4d80-430f-837d-94ce300d2efc', + ' - MCPTools: 57614ca5-c704-436a-820e-7b5b361d43de', + ' - UrhoX: 1e358420-cba4-4054-9b83-d3ac24bd84bd', + ' - Maker: f3d4c103-b2d7-4154-9f25-9a72e12a4bf3', + ' 示例: tasklists=\'[{"tasklist_guid": "c7f1907f-4d80-430f-837d-94ce300d2efc"}]\'', + '', '时间格式 (due/start): Unix 秒级时间戳 或 ISO 日期 (如 "2026-03-15" 或 "2026-03-15T10:00:00")', 'members 格式: JSON 数组 \'[{"id": "ou_xxx", "role": "assignee"}]\'', - 'tasklists 格式: JSON 数组 \'[{"tasklist_guid": "xxx"}]\' (通过 list_tasklists 获取可用清单)', + 'tasklists 格式: JSON 数组 \'[{"tasklist_guid": "xxx"}]\'', 'update_fields: 逗号分隔的字段名 (如 "summary,due,description,completed_at")', ' - completed_at: 设为当前时间表示完成任务,设为空字符串表示取消完成', ].join('\n'), From 0247775fc83972230404053a0dc44f39c113930a Mon Sep 17 00:00:00 2001 From: lishuceo Date: Fri, 13 Mar 2026 22:24:13 +0800 Subject: [PATCH 2/2] refactor: remove system prompt hash session invalidation Agent SDK re-sends the new systemPrompt on resume, so old sessions naturally get updated tool descriptions without needing a fresh start. - Keep hash computation for diagnostic logging - Remove resumeSkipped field from ClaudeResult - Remove _historyDedup clearing on hash mismatch Co-Authored-By: Claude Opus 4.6 --- src/claude/executor.ts | 20 ++++++-------------- src/claude/types.ts | 4 +--- src/feishu/event-handler.ts | 8 -------- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/claude/executor.ts b/src/claude/executor.ts index f1c91efe..8c9cb903 100644 --- a/src/claude/executor.ts +++ b/src/claude/executor.ts @@ -438,24 +438,20 @@ export class ClaudeExecutor { ? withKnowledge + input.memoryContext : withKnowledge; // System prompt 结构性哈希:仅包含 knowledge + base prompt(不含记忆和 historySummaries) - // 代码部署后 prompt 变化时自动使旧 session 失效,避免 resume 旧 session 丢失新工具描述 - // 排除运行时动态值(process.pid 等)——仅反映代码/配置变更 + // 用于诊断日志,追踪 prompt 变化。不再用于自动失效 session — + // Agent SDK resume 时会使用新传入的 systemPrompt,旧 session 自然获得最新工具描述。 const hashInput = withKnowledge.replace(/\*\*当前 PID\*\*: `\d+`/, '**当前 PID**: `0`'); const systemPromptHash = createHash('sha256').update(hashInput).digest('hex').slice(0, 16); - // 自动失效:system prompt 变化后跳过 resume,起新 session(工具描述、persona 等会刷新) - // 注意:storedHash 为空(旧 session 迁移前创建)也视为不匹配,强制起新 session - let effectiveResumeId = resumeSessionId; - let resumeSkipped = false; - if (resumeSessionId && input.storedSystemPromptHash !== systemPromptHash) { + if (resumeSessionId && input.storedSystemPromptHash && input.storedSystemPromptHash !== systemPromptHash) { logger.info( { sessionKey, storedHash: input.storedSystemPromptHash, currentHash: systemPromptHash }, - 'System prompt changed since last session — skipping resume, starting fresh', + 'System prompt changed since last session — continuing with updated prompt (no longer invalidates session)', ); - effectiveResumeId = undefined; - resumeSkipped = true; } + const effectiveResumeId = resumeSessionId; + const promptAppend = historySummaries ? withMemory + `\n\n## 历史会话摘要\n以下是该用户之前的会话记录,帮助你了解项目上下文:\n${historySummaries}` : withMemory; @@ -752,7 +748,6 @@ export class ClaudeExecutor { durationMs, needsRestart: workspaceChanged, newWorkingDir, - resumeSkipped, }; } @@ -806,7 +801,6 @@ export class ClaudeExecutor { numTurns: resultMessage.num_turns, needsRestart: workspaceChanged, newWorkingDir, - resumeSkipped, }; } else { // 错误结果 @@ -823,7 +817,6 @@ export class ClaudeExecutor { numTurns: resultMessage.num_turns, needsRestart: workspaceChanged, newWorkingDir, - resumeSkipped, }; } } @@ -837,7 +830,6 @@ export class ClaudeExecutor { durationMs, needsRestart: workspaceChanged, newWorkingDir, - resumeSkipped, }; } diff --git a/src/claude/types.ts b/src/claude/types.ts index 0e4e7add..cc5f3bc5 100644 --- a/src/claude/types.ts +++ b/src/claude/types.ts @@ -34,14 +34,12 @@ export interface ClaudeResult { costUsd?: number; /** 总轮数 */ numTurns?: number; - /** System prompt 结构性哈希(不含 historySummaries),用于自动失效旧 session */ + /** System prompt 结构性哈希(不含 historySummaries),用于诊断日志追踪 prompt 变化 */ systemPromptHash?: string; /** 是否需要重启 (workspace 变更后) */ needsRestart?: boolean; /** 重启目标工作目录 */ newWorkingDir?: string; - /** resume 因 system prompt hash 不匹配被跳过 */ - resumeSkipped?: boolean; } /** executor.execute() 的可选参数 */ diff --git a/src/feishu/event-handler.ts b/src/feishu/event-handler.ts index 845aa9d0..779bf51a 100644 --- a/src/feishu/event-handler.ts +++ b/src/feishu/event-handler.ts @@ -1311,7 +1311,6 @@ async function executeClaudeTask( try { // Resume 策略:activeConversationId/activeConversationCwd 已在上方提前计算 - // 额外检查 systemPromptHash:代码部署后 prompt 变化时自动使旧 session 失效 const activePromptHash = threadId ? threadSession?.systemPromptHash : session.systemPromptHash; const canResume = activeConversationId && (!activeConversationCwd || activeConversationCwd === workingDir); @@ -1795,13 +1794,6 @@ async function executeDirectTask( ...(discussionMcp ? { additionalMcpServers: { 'discussion-tools': discussionMcp } } : {}), }); - // resume 被跳过时清除 _historyDedup,让下次 query 重新注入完整历史 - // 否则新 session 只能看到 afterMsgId 之后的消息,丢失之前的上下文 - if (result.resumeSkipped) { - _historyDedup.delete(sessionKey); - logger.info({ sessionKey }, 'Cleared _historyDedup due to resume skip (system prompt hash mismatch)'); - } - // 保存 conversationId(下次消息可 resume) if (result.sessionId) { if (threadId) {