Files
Arcrun/mcp/src/tools/arcrun_report_feedback.ts
T
uncle6me-web b1e302b3b5 fix(kbdb): cypher proxy 補 /kbdb/entries CRUD + report_feedback 改打 /entries
kbdb-base Phase 9.6/9.7(HANDOFF §2 缺口① + §3b 連帶):

- 9.6 cypher kbdb-proxy 補 /kbdb/entries CRUD(POST/GET list/GET :id/PATCH :id)
  純轉發到 KBDB 基本盤 /entries,解鎖 mira _kbdb_client.py 主線遷移。
  租戶隔離同 9.5:寫入注入 owner_id、list 強制本租戶過濾、PATCH 剝 owner_id。
  刻意不開 DELETE(基本盤 delete 無 owner 檢查 → 跨租戶刪除風險)。
- 9.7 arcrun_report_feedback 從死 route /blocks 改打基本盤 /entries
  (entry_type=agent-feedback)。9.4 漏網的同類修;基本盤無 /blocks → 原本 404 假紅。

順帶(HANDOFF §6 harness 表達優化):
- 重寫 cli/harness/CLAUDE.block.md 補三盲點(recipe 是公共投稿 / 缺能力補 API 不拼裝 /
  自製零件退場路徑),目標 Haiku 級 CC 讀懂。
- README 零件 vs recipe 段對齊同三點。

cypher + mcp tsc exit 0。端到端 smoke test 隨後。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:06:58 +08:00

152 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* arcrun_report_feedback — explicit feedback tool for AI agents
*
* 對應 SDD .agents/specs/llm-interface/ M1.3
*
* AI agent 每次完成 workflow / 卡住 / 解掉問題後 **MUST** call 此 tool。
* 結構化 issue_type enum 防自由文字難聚合。寫入 KBDB type=agent-feedback block。
*
* 後續 M4 weekly_review workflow 聚合這些 block 產出 arcrun-roadmap。
*
* 命名注意:M5 全面 rename u6u → arcrun 前,本 tool 直接用新名 arcrun_ prefix
* 立下範例。其他 u6u_* tool 等 M5 一次切。
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
const ISSUE_TYPES = [
"success_story", // 順利完成,值得記錄這個 pattern
"doc_unclear", // AGENTS.md / skill / contract 講不清楚
"tool_missing", // 該有的 MCP tool 沒有
"error_unhelpful", // 錯誤訊息看不懂下一步
"unexpected_behavior", // 跟我預期的不一樣
"feature_request", // 我想要 X 功能
] as const;
export function registerReportFeedback(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"arcrun_report_feedback",
"AI agent 完成 workflow 任務 / 卡住 / 解掉問題後 **必須** call 此 tool 回報。即使順利也要 call (issue_type=success_story),那是告訴平台「這 pattern 已 work,可推廣」。回饋會寫進 KBDB type=agent-feedback,週報自動聚合產出平台改善 roadmap。",
{
issue_type: z.enum(ISSUE_TYPES).describe(
"回報類型。success_story=順利做完 / doc_unclear=文件不清楚 / tool_missing=該有的 MCP tool 缺 / error_unhelpful=錯誤訊息看不懂下一步 / unexpected_behavior=與預期不符 / feature_request=想要新功能"
),
description: z.string().min(10).describe(
"詳述:你做了什麼、發生什麼、為什麼這算 issue / story。至少 10 字。若是 success_story,描述 pattern 與適用情境"
),
workflow_name: z.string().optional().describe("相關 workflow 名稱(若有)"),
retry_count: z.number().int().min(0).optional().describe("為了搞定,你重試了幾次(含修 YAML / 改參數)"),
blocked: z.boolean().optional().describe("是否完全擋住(true = 無法繼續),預設 false"),
suggested_fix: z.string().optional().describe("你建議的修補方向(optional,但很有價值)"),
agent_user_agent: z.string().optional().describe(
"你(AI agent)的 client 識別字串。e.g. 'claude-code/1.x'、'cursor-mcp/0.4'、'mira-bot'。讓平台知道哪個 AI 客戶端踩到問題"
),
},
async ({ issue_type, description, workflow_name, retry_count, blocked, suggested_fix, agent_user_agent }) => {
try {
if (!env.KBDB) {
return { content: [{ type: "text", text: "Error: KBDB service binding unavailable" }], isError: true };
}
// kbdb-base 9.7:寫進基本盤 entriesentry_type=agent-feedback)。
// 舊版打 v3 死 route /blocks(基本盤只 mount entries/templates/records)→ 404 假紅,已改。
// owner_id = 用戶 namespaceself-hosted 單租戶聚集)。基本盤無 source/api_key 欄 → 併入 metadata。
const entryBody = {
entry_type: "agent-feedback",
owner_id: orgNamespace,
content: description,
metadata_json: JSON.stringify({
issue_type,
workflow_name,
retry_count,
blocked: blocked ?? false,
suggested_fix,
agent_user_agent,
source: "mcp-tool-call",
reported_at: new Date().toISOString(),
}),
tags_json: JSON.stringify([
"agent-feedback",
`issue:${issue_type}`,
...(blocked ? ["blocked"] : []),
...(workflow_name ? [`wf:${workflow_name}`] : []),
]),
};
// 走 KBDB service binding 打基本盤 /entries(薄殼模式不變)
const createResp = await kbdbFetch(env, `/entries`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(entryBody),
});
if (!createResp.ok) {
const errBody = await createResp.text();
return {
content: [
{
type: "text",
text: JSON.stringify({
ok: false,
error_code: "kbdb_write_failed",
human_message: `回饋寫入 KBDB 失敗:HTTP ${createResp.status}`,
next_actions: [
"確認 KBDB 服務在線(KBDB worker /health",
"若持續失敗,可暫先在本地記下回饋,稍後重試",
],
detail: errBody.slice(0, 200),
}, null, 2),
},
],
isError: true,
};
}
const data = await createResp.json().catch(() => null);
return {
content: [
{
type: "text",
text: JSON.stringify({
ok: true,
data: {
reported: true,
issue_type,
// 基本盤 /entries 回 { success, entry };舊 /blocks 回 { id } → 兩種都容忍
entry_id: (data as { entry?: { id?: string }; id?: string } | null)?.entry?.id
?? (data as { id?: string } | null)?.id,
},
hints: [
issue_type === "success_story"
? "感謝記錄成功 pattern!這會被納入週報自動推廣。"
: "感謝回報!平台週報會聚合這類問題(M4 完成後可看 arcrun-roadmap block",
"若還有相關問題(例如同 workflow 不同 issue),可繼續 call",
],
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
ok: false,
error_code: "internal_error",
human_message: `report_feedback 內部錯誤:${error instanceof Error ? error.message : String(error)}`,
next_actions: ["重試一次", "若持續失敗,請告訴用戶這個 issue 並貼錯誤訊息給 leo"],
}, null, 2),
},
],
isError: true,
};
}
}
);
}