duclaw-cli 1.9.8 → 1.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bundle.js CHANGED
@@ -30242,7 +30242,7 @@ function printHelp() {
30242
30242
  `);
30243
30243
  }
30244
30244
  function printVersion() {
30245
- console.log(`duclaw-cli v${true ? "1.9.8" : "unknown"}`);
30245
+ console.log(`duclaw-cli v${true ? "1.9.9" : "unknown"}`);
30246
30246
  }
30247
30247
  function getDuclawTemplate() {
30248
30248
  return {
@@ -41782,9 +41782,9 @@ var imageUnderstand = {
41782
41782
  };
41783
41783
 
41784
41784
  // src/skill/SkillRegistry.ts
41785
- var import_fs11 = require("fs");
41785
+ var import_fs13 = require("fs");
41786
41786
  var import_os3 = require("os");
41787
- var import_path16 = __toESM(require("path"));
41787
+ var import_path18 = __toESM(require("path"));
41788
41788
 
41789
41789
  // src/runtime/paths.ts
41790
41790
  var import_fs10 = require("fs");
@@ -41829,2322 +41829,2346 @@ var resolveWebDistRoot = () => {
41829
41829
  return candidates[0];
41830
41830
  };
41831
41831
 
41832
- // src/skill/SkillRegistry.ts
41833
- var getProjectSkillsPath = () => {
41834
- const projectRoot = findProjectRoot();
41835
- return projectRoot ? (0, import_path16.join)(projectRoot, "skills") : null;
41832
+ // src/department/Department.ts
41833
+ var import_path16 = __toESM(require("path"));
41834
+ var import_fs11 = require("fs");
41835
+ var legacyMigrationChecked = false;
41836
+ var getDepartmentBaseDir = () => {
41837
+ return import_path16.default.join(getDuclawHomeDir(), "department");
41836
41838
  };
41837
- var getSkillPaths = () => {
41838
- const paths = [];
41839
- const seenPaths = /* @__PURE__ */ new Set();
41840
- const pushPath = (candidate) => {
41841
- if (!(0, import_fs11.existsSync)(candidate) || !(0, import_fs11.statSync)(candidate).isDirectory()) return;
41842
- const normalized = import_path16.default.resolve(candidate);
41843
- if (seenPaths.has(normalized)) return;
41844
- seenPaths.add(normalized);
41845
- paths.push(normalized);
41846
- };
41847
- const projectSkillsPath = getProjectSkillsPath();
41848
- if (projectSkillsPath) {
41849
- pushPath(projectSkillsPath);
41850
- }
41851
- pushPath((0, import_path16.join)((0, import_os3.homedir)(), ".duclaw", "skills"));
41852
- pushPath((0, import_path16.join)((0, import_os3.homedir)(), ".agents", "skills"));
41853
- return paths;
41839
+ var getLegacyTeamBaseDir = () => {
41840
+ return import_path16.default.join(getDuclawHomeDir(), "team");
41854
41841
  };
41855
- var getDirectories = (dirPath) => {
41856
- return (0, import_fs11.readdirSync)(dirPath).filter((name) => (0, import_fs11.statSync)((0, import_path16.join)(dirPath, name)).isDirectory());
41842
+ var getDepartmentWorkSpaceDir = (departmentName) => {
41843
+ return import_path16.default.join(getDepartmentBaseDir(), "workspace", departmentName);
41857
41844
  };
41858
- var parseSkill = (mdPath, skillDir) => {
41859
- if (!(0, import_fs11.existsSync)(mdPath)) return null;
41860
- const raw2 = (0, import_fs11.readFileSync)(mdPath, "utf-8");
41861
- let name = "";
41862
- let description = "";
41863
- let body = raw2;
41864
- const fmMatch = raw2.match(/^---\r?\n([\s\S]*?)\r?\n---/);
41865
- if (fmMatch) {
41866
- const frontmatter = fmMatch[1];
41867
- body = raw2.slice(fmMatch[0].length).trim();
41868
- for (const line of frontmatter.split("\n")) {
41869
- const [key, ...rest] = line.split(":");
41870
- const value = rest.join(":").trim();
41871
- if (key.trim() === "name") name = value;
41872
- if (key.trim() === "description") description = value;
41873
- }
41874
- }
41875
- if (!name) {
41876
- name = import_path16.default.basename(import_path16.default.dirname(mdPath));
41877
- }
41878
- if (!description && body) {
41879
- description = body.split("\n")[0].replace(/^#+\s*/, "").trim();
41880
- }
41881
- const projectSkillsPath = getProjectSkillsPath();
41882
- const normalizedSkillDir = import_path16.default.resolve(skillDir);
41883
- const deletable = projectSkillsPath ? normalizedSkillDir === import_path16.default.resolve(projectSkillsPath, import_path16.default.basename(normalizedSkillDir)) && normalizedSkillDir.startsWith(import_path16.default.resolve(projectSkillsPath) + import_path16.default.sep) : false;
41845
+ var getLegacyTeamWorkSpaceDir = (teamName) => {
41846
+ return import_path16.default.join(getLegacyTeamBaseDir(), "workspace", teamName);
41847
+ };
41848
+ var getDepartmentJsonPath = (departmentName) => {
41849
+ return import_path16.default.join(getDepartmentWorkSpaceDir(departmentName), "department.json");
41850
+ };
41851
+ var getLegacyTeamJsonPath = (teamName) => {
41852
+ return import_path16.default.join(getLegacyTeamWorkSpaceDir(teamName), "team.json");
41853
+ };
41854
+ var mapLegacyRole = (role) => {
41855
+ return role === "team_manager" ? "department_head" : "executor";
41856
+ };
41857
+ var mapLegacyDepartment = (legacy) => {
41858
+ const departmentMembers = (legacy.teamMembers ?? []).map((member) => ({
41859
+ id: member.id,
41860
+ name: member.name,
41861
+ departmentId: legacy.id,
41862
+ mailBoxId: member.mailBoxId,
41863
+ workspaceId: member.workspaceId,
41864
+ role: mapLegacyRole(member.role),
41865
+ focusOn: member.focusOn
41866
+ }));
41867
+ const headMemberId = legacy.managerMemberId ?? departmentMembers.find((member) => member.role === "department_head")?.id;
41884
41868
  return {
41885
- name,
41886
- description,
41887
- baseDir: skillDir,
41888
- deletable,
41889
- getDetail: () => {
41890
- let content = body.replace(/<Skill目录>/g, skillDir).replace(/\$\{CLAUDE_SKILL_DIR\}/g, skillDir);
41891
- return `Base directory for this skill: ${skillDir}
41892
-
41893
- ${content}`;
41894
- }
41869
+ id: legacy.id,
41870
+ name: legacy.name,
41871
+ sourceGoalId: legacy.goalId,
41872
+ charter: legacy.goalId ? `Legacy department migrated from team goal ${legacy.goalId}.` : "Legacy department migrated from team data.",
41873
+ workpath: legacy.workpath,
41874
+ headMemberId,
41875
+ departmentMembers
41895
41876
  };
41896
41877
  };
41897
- var loadSkill = () => {
41898
- const skillPaths = getSkillPaths();
41899
- if (skillPaths.length === 0) return [];
41900
- const seen = /* @__PURE__ */ new Set();
41901
- const skills = [];
41902
- for (const skillPath of skillPaths) {
41903
- const dirs = getDirectories(skillPath);
41904
- for (const skillName of dirs) {
41905
- const eachSkillPath = (0, import_path16.join)(skillPath, skillName);
41906
- const eachSkillMdPath = (0, import_path16.join)(eachSkillPath, "SKILL.md");
41907
- const skill = parseSkill(eachSkillMdPath, eachSkillPath);
41908
- if (skill && !seen.has(skill.name)) {
41909
- seen.add(skill.name);
41910
- skills.push(skill);
41911
- }
41878
+ var migrateLegacyTeamsToDepartments = () => {
41879
+ if (legacyMigrationChecked) return;
41880
+ legacyMigrationChecked = true;
41881
+ const legacyWorkspaceDir = import_path16.default.join(getLegacyTeamBaseDir(), "workspace");
41882
+ if (!(0, import_fs11.existsSync)(legacyWorkspaceDir)) return;
41883
+ for (const legacyName of (0, import_fs11.readdirSync)(legacyWorkspaceDir)) {
41884
+ const legacyJsonPath = getLegacyTeamJsonPath(legacyName);
41885
+ if (!(0, import_fs11.existsSync)(legacyJsonPath)) continue;
41886
+ const departmentJsonPath = getDepartmentJsonPath(legacyName);
41887
+ if ((0, import_fs11.existsSync)(departmentJsonPath)) continue;
41888
+ try {
41889
+ const legacy = JSON.parse((0, import_fs11.readFileSync)(legacyJsonPath, "utf-8"));
41890
+ const department = mapLegacyDepartment(legacy);
41891
+ (0, import_fs11.mkdirSync)(getDepartmentWorkSpaceDir(department.name), { recursive: true });
41892
+ (0, import_fs11.writeFileSync)(departmentJsonPath, JSON.stringify(department, null, " "), "utf-8");
41893
+ } catch (err) {
41894
+ console.warn(`[department] Failed to migrate legacy team ${legacyName}: ${err.message}`);
41912
41895
  }
41913
41896
  }
41914
- return skills;
41915
41897
  };
41916
- var deleteSkill = (name) => {
41917
- const skills = loadSkill();
41918
- const skill = skills.find((s) => s.name === name);
41919
- if (!skill || !skill.deletable) return false;
41920
- (0, import_fs11.rmSync)(skill.baseDir, { recursive: true, force: false });
41921
- return true;
41898
+ var createDepartment = (departmentDefinition) => {
41899
+ if (!departmentDefinition) throw new Error(`[createDepartment] departmentDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
41900
+ const departmentPath = getDepartmentWorkSpaceDir(departmentDefinition.name);
41901
+ (0, import_fs11.mkdirSync)(departmentPath, { recursive: true });
41902
+ (0, import_fs11.writeFileSync)(getDepartmentJsonPath(departmentDefinition.name), JSON.stringify(departmentDefinition, null, " "), "utf-8");
41903
+ return departmentDefinition;
41922
41904
  };
41923
- var SkillRegistry_default = loadSkill;
41924
-
41925
- // src/skill/index.ts
41926
- var getSkillMeta = () => {
41927
- const skills = SkillRegistry_default();
41928
- if (skills.length === 0) return "";
41929
- const skillXmls = skills.map((skill) => {
41930
- return [
41931
- " <skill>",
41932
- ` <name>${skill.name}</name>`,
41933
- ` <description>`,
41934
- ` ${skill.description}`,
41935
- ` </description>`,
41936
- ` <location>user</location>`,
41937
- " </skill>"
41938
- ].join("\n");
41939
- });
41940
- return [
41941
- "<available_skills>",
41942
- ...skillXmls,
41943
- "</available_skills>"
41944
- ].join("\n");
41905
+ var getDepartment = (name) => {
41906
+ migrateLegacyTeamsToDepartments();
41907
+ const departmentJsonPath = getDepartmentJsonPath(name);
41908
+ if (!(0, import_fs11.existsSync)(departmentJsonPath)) return null;
41909
+ const text2 = (0, import_fs11.readFileSync)(departmentJsonPath, "utf-8");
41910
+ return JSON.parse(text2);
41945
41911
  };
41946
- var getSkillDetail = (skillName) => {
41947
- const skills = SkillRegistry_default();
41948
- for (let skill of skills) {
41949
- if (skill.name === skillName) {
41950
- return skill.getDetail();
41951
- }
41912
+ var listDepartments = () => {
41913
+ migrateLegacyTeamsToDepartments();
41914
+ const workspaceDir = import_path16.default.join(getDepartmentBaseDir(), "workspace");
41915
+ if (!(0, import_fs11.existsSync)(workspaceDir)) return [];
41916
+ const departments = [];
41917
+ for (const departmentName of (0, import_fs11.readdirSync)(workspaceDir)) {
41918
+ const department = getDepartment(departmentName);
41919
+ if (department) departments.push(department);
41952
41920
  }
41953
- return void 0;
41921
+ return departments;
41922
+ };
41923
+ var getDepartmentById = (id) => {
41924
+ const department = listDepartments().find((item) => item.id === id);
41925
+ return department ?? null;
41926
+ };
41927
+ var deleteDepartment = (name) => {
41928
+ if (!(0, import_fs11.existsSync)(getDepartmentJsonPath(name))) {
41929
+ throw new Error(`[deleteDepartment] \u4E0D\u5B58\u5728\u5BF9\u5E94\u7684\u90E8\u95E8 ${name} \u7684 department.json \u6587\u4EF6`);
41930
+ }
41931
+ (0, import_fs11.rmSync)(getDepartmentJsonPath(name));
41954
41932
  };
41955
41933
 
41956
- // src/tools/tools/Skill.ts
41957
- var DESCRIPTION15 = `
41958
- Execute a skill within the main conversation
41959
-
41960
- <skills_instructions>
41961
- When users ask you to perform tasks, check if any of the available skills
41962
- below can help complete the task more effectively. Skills provide specialized
41963
- capabilities and domain knowledge.
41964
-
41965
- How to use skills:
41966
- - Invoke skills using this tool with the skill name only (no arguments)
41967
- - When you invoke a skill, you will see <command-message>The "{name}" skill is loading</command-message>
41968
- - The skill's prompt will expand and provide detailed instructions on how to complete the task
41969
- - Examples:
41970
- - \`command: "pdf"\` - invoke the pdf skill
41971
- - \`command: "xlsx"\` - invoke the xlsx skill
41972
- - \`command: "ms-office-suite:pdf"\` - invoke using fully qualified name
41973
-
41974
- Important:
41975
- - Only use skills listed in <available_skills> below
41976
- - Do not invoke a skill that is already running
41977
- - Do not use this tool for built-in CLI commands (like /help, /clear, etc.)
41978
- </skills_instructions>
41979
-
41980
- ${getSkillMeta()}
41981
- `;
41982
- var skillTool = {
41983
- name: `skill`,
41984
- description: DESCRIPTION15,
41985
- input_schema: {
41986
- type: `object`,
41987
- properties: {
41988
- command: {
41989
- type: `string`,
41990
- description: `The skill name (no arguments). E.g., "pdf" or "xlsx"`
41991
- }
41992
- },
41993
- required: [`command`]
41994
- },
41995
- async execute(input, userRequest) {
41996
- console.log(`[\u6267\u884C\u5DE5\u5177]${input.command} skill`);
41997
- const command = input.command;
41998
- const detail = getSkillDetail(command);
41999
- if (!detail) return `<command-message> skill name: \u201C${command}\u201D \u4E0D\u5B58\u5728</command-message>
42000
- <command-name>${command}</command-name>`;
42001
- const skillXml = `
42002
- <command-message>The "${command}" skill is running</command-message>
41934
+ // src/department/learning.ts
41935
+ var import_node_fs4 = require("node:fs");
41936
+ var import_node_path13 = __toESM(require("node:path"));
41937
+ var import_node_crypto4 = require("node:crypto");
42003
41938
 
42004
- <command-name>${command}</command-name>
41939
+ // src/department/DepartmentMember.ts
41940
+ var import_fs12 = require("fs");
41941
+ var import_path17 = __toESM(require("path"));
42005
41942
 
42006
- <command-detail>${detail}</command-detail>
42007
- `;
42008
- return skillXml;
42009
- }
41943
+ // src/department/workspace/workspace.ts
41944
+ var db = createSqliteDB();
41945
+ var getWorkspaceId = (departmentName, memberName) => {
41946
+ return `${departmentName}::${memberName}`;
41947
+ };
41948
+ var createWorkspace = (workspace) => {
41949
+ const stmt = db.prepare(`INSERT INTO workspace (id, team_name, teammate_name, team_workpath) VALUES (?, ?, ?, ?) `);
41950
+ stmt.run(
41951
+ workspace.id,
41952
+ workspace.departmentName,
41953
+ workspace.memberName,
41954
+ workspace.departmentWorkPath
41955
+ );
41956
+ };
41957
+ var deleteWorkspace = (workspaceId) => {
41958
+ const stmt = db.prepare(`delete from workspace where id = ?`);
41959
+ stmt.run(workspaceId);
42010
41960
  };
42011
41961
 
42012
- // src/tools/tools/tasks/GoalCreate.ts
42013
- var goalCreate = {
42014
- name: `goal_create`,
42015
- description: `\u5728\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u521B\u5EFA\u4E00\u4E2A\u4EFB\u52A1\u76EE\u6807\uFF0C\u5F53\u8981\u8C03\u7528 task_ \u7CFB\u5217\u5DE5\u5177\u65F6\uFF0C\u5148\u8C03\u7528\u6B64\u5DE5\u5177\u521B\u5EFA\u4EFB\u52A1\u76EE\u6807`,
42016
- input_schema: {
42017
- type: `object`,
42018
- properties: {
42019
- subject: {
42020
- type: `string`,
42021
- description: `\u4EFB\u52A1\u76EE\u6807\u7684\u6838\u5FC3\u6807\u9898\uFF0C\u7B80\u6D01\u660E\u4E86`
42022
- },
42023
- description: {
42024
- type: `string`,
42025
- description: `\u4EFB\u52A1\u76EE\u6807\u7684\u8BE6\u7EC6\u8BF4\u660E`
42026
- }
42027
- },
42028
- required: [`subject`]
42029
- },
42030
- async execute(input, userRequest) {
42031
- const subject = input.subject;
42032
- const description = input.description;
42033
- const taskGoal = await createGoal(subject, description);
42034
- if (userRequest) {
42035
- ensureGoalConversationContext(taskGoal.id, {
42036
- threadId: userRequest.userId,
42037
- originPlatform: userRequest.platform,
42038
- originUserId: userRequest.userId,
42039
- syncReplyToOrigin: userRequest.platform === "feishu"
42040
- });
42041
- }
42042
- if (taskGoal) {
42043
- return `[GoalCreate] \u521B\u5EFA\u4EFB\u52A1\u76EE\u6807\u6210\u529F: ${JSON.stringify(taskGoal)}`;
42044
- }
42045
- return `[GoalCreate] \u521B\u5EFA\u4EFB\u52A1\u76EE\u6807\u5931\u8D25: ${taskGoal}`;
41962
+ // src/department/DepartmentMember.ts
41963
+ var createDepartmentMember = (departmentMemberDefinition) => {
41964
+ if (!departmentMemberDefinition) throw new Error(`[createDepartmentMember] departmentMemberDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
41965
+ const { name, departmentId, workspaceId } = departmentMemberDefinition;
41966
+ const department = getDepartmentById(departmentId);
41967
+ if (!department) throw new Error(`[createDepartmentMember] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentId}`);
41968
+ const memberPath = import_path17.default.join(getDepartmentWorkSpaceDir(department.name), name);
41969
+ (0, import_fs12.mkdirSync)(memberPath, { recursive: true });
41970
+ const workspace = {
41971
+ id: getWorkspaceId(department.name, departmentMemberDefinition.name),
41972
+ departmentName: department.name,
41973
+ memberName: name,
41974
+ departmentWorkPath: memberPath
41975
+ };
41976
+ createWorkspace(workspace);
41977
+ if (!department.departmentMembers) {
41978
+ department.departmentMembers = [];
42046
41979
  }
42047
- };
42048
-
42049
- // src/tools/tools/tasks/GoalGet.ts
42050
- var DESCRIPTION16 = `
42051
- \u83B7\u53D6\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal
42052
- `;
42053
- var goalGet = {
42054
- name: `goal_get`,
42055
- description: DESCRIPTION16,
42056
- input_schema: {
42057
- type: `object`,
42058
- properties: {
42059
- id: {
42060
- type: `string`,
42061
- description: `\u6839\u636Eid\u83B7\u53D6\u6307\u5B9A\u76EE\u6807,\u53EF\u9009`
42062
- },
42063
- subject: {
42064
- type: `string`,
42065
- description: `\u6839\u636Esubject\u83B7\u53D6\u7279\u5B9A\u76EE\u6807,\u53EF\u9009`
42066
- }
42067
- }
42068
- },
42069
- async execute(input, userRequest) {
42070
- const id = input.id;
42071
- const subject = input.subject;
42072
- let goal = void 0;
42073
- if (id) {
42074
- goal = getGoalById(id);
42075
- } else if (subject) {
42076
- goal = getGoalBySubject(subject);
42077
- }
42078
- if (!goal) {
42079
- return `[GetGoal] \u627E\u4E0D\u5230\u6307\u5B9A\u7684\u76EE\u6807`;
42080
- }
42081
- return `[GetGoal] \u627E\u5230\u76EE\u6807: ${JSON.stringify(goal)}`;
41980
+ if (department.departmentMembers.some((member) => member.id === departmentMemberDefinition.id || member.name === departmentMemberDefinition.name)) {
41981
+ throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728\u540C\u540D\u6216\u540C id \u6210\u5458: ${departmentMemberDefinition.name}/${departmentMemberDefinition.id}`);
42082
41982
  }
42083
- };
42084
-
42085
- // src/tools/tools/tasks/GoalList.ts
42086
- var DESCRIPTION17 = `
42087
- \u5728\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u5217\u51FA\u6240\u6709\u76EE\u6807
42088
- `;
42089
- var goalList = {
42090
- name: `goal_list`,
42091
- description: DESCRIPTION17,
42092
- input_schema: {
42093
- type: `object`,
42094
- properties: {}
42095
- },
42096
- async execute(input, userRequest) {
42097
- const goals = listGoal();
42098
- if (goals.length > 0) {
42099
- return `[GoalList]goals\u5217\u8868: ${JSON.stringify(goals)}`;
41983
+ if (departmentMemberDefinition.role === "department_head") {
41984
+ const existingHead = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
41985
+ if (existingHead) {
41986
+ throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728 Department Head: ${existingHead.name}`);
42100
41987
  }
42101
- return `[GoalList]\u6CA1\u6709goals\u6570\u636E`;
41988
+ department.headMemberId = departmentMemberDefinition.id;
42102
41989
  }
41990
+ department.departmentMembers.push(departmentMemberDefinition);
41991
+ (0, import_fs12.writeFileSync)(getDepartmentJsonPath(department.name), JSON.stringify(department, null, " "), "utf-8");
41992
+ return departmentMemberDefinition;
42103
41993
  };
42104
-
42105
- // src/tools/tools/tasks/GoalUpdate.ts
42106
- var DESCRIPTION18 = `
42107
- \u66F4\u65B0\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal
42108
-
42109
- \u5DE5\u5177\u4F7F\u7528\u89C4\u5219:
42110
- - \u5F53\u76EE\u6807\u7684\u72B6\u6001status\u9700\u8981\u66F4\u65B0\u65F6;
42111
- - \u5F53\u76EE\u6807\u7684subject\u6216description\u9700\u8981\u4FEE\u6539\u65F6;
42112
- `;
42113
- var goalUpdate = {
42114
- name: `goal_update`,
42115
- description: DESCRIPTION18,
42116
- input_schema: {
42117
- type: `object`,
42118
- properties: {
42119
- id: {
42120
- type: `string`,
42121
- description: `\u6839\u636Eid\u627E\u5230\u5BF9\u5E94goal\u8FDB\u884C\u66F4\u65B0`
42122
- },
42123
- subject: {
42124
- type: `string`,
42125
- description: `\u66F4\u65B0\u540Egoal\u7684\u6838\u5FC3\u6807\u9898\uFF0C\u7B80\u6D01\u660E\u4E86`
42126
- },
42127
- description: {
42128
- type: `string`,
42129
- description: `\u66F4\u65B0\u540E\u7684goal\u7684\u8BE6\u7EC6\u8BF4\u660E`
42130
- },
42131
- status: {
42132
- type: `string`,
42133
- description: `pending | in_progress | completed | failed`,
42134
- enum: [`pending`, `in_progress`, `completed`, `failed`]
42135
- }
42136
- },
42137
- required: [`id`]
42138
- },
42139
- async execute(input, userRequest) {
42140
- const id = input.id;
42141
- const subject = input.subject;
42142
- const description = input.description;
42143
- const status = input.status;
42144
- const validStatuses = ["pending", "in_progress", "completed", "failed"];
42145
- if (status && !validStatuses.includes(status)) {
42146
- return `[UpdateGoal] status\u53C2\u6570\u9519\u8BEF,status\u5FC5\u987B\u8981\u662F pending | in_progress | completed | failed \u4E2D\u7684\u4EFB\u610F\u4E00\u4E2A`;
42147
- }
42148
- const goal = getGoalById(id);
42149
- if (!goal) {
42150
- return `[UpdateGoal] \u627E\u4E0D\u5230\u6307\u5B9Aid\u7684\u76EE\u6807`;
42151
- }
42152
- const updatedGoal = {
42153
- ...goal,
42154
- subject: subject || goal.subject,
42155
- description: description !== void 0 ? description : goal.description,
42156
- status: status || goal.status,
42157
- updateAt: getDate()
42158
- };
42159
- try {
42160
- updateGoal(id, updatedGoal);
42161
- } catch (err) {
42162
- if (err instanceof GoalCompletionBlockedError) {
42163
- return err.message;
42164
- }
42165
- throw err;
42166
- }
42167
- return `[UpdateGoal] \u66F4\u65B0\u76EE\u6807\u6210\u529F: ${JSON.stringify(updatedGoal)}`;
41994
+ var listDepartmentMembers = (departmentName) => {
41995
+ const departmentJsonPath = getDepartmentJsonPath(departmentName);
41996
+ if (!(0, import_fs12.existsSync)(departmentJsonPath)) return [];
41997
+ const text2 = (0, import_fs12.readFileSync)(departmentJsonPath, "utf-8");
41998
+ const department = JSON.parse(text2);
41999
+ if (!department) throw new Error(`[listDepartmentMembers] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
42000
+ return department.departmentMembers ?? [];
42001
+ };
42002
+ var getDepartmentMember = (departmentName, departmentMemberId) => {
42003
+ const members = listDepartmentMembers(departmentName);
42004
+ return members.find((member) => member.id === departmentMemberId) || null;
42005
+ };
42006
+ var getDepartmentMemberByName = (departmentName, departmentMemberName) => {
42007
+ const members = listDepartmentMembers(departmentName);
42008
+ return members.find((member) => member.name === departmentMemberName) || null;
42009
+ };
42010
+ var getDepartmentMemberByMailboxId = (mailboxId) => {
42011
+ const [departmentName, memberName] = mailboxId.split("::");
42012
+ if (!departmentName || !memberName) return null;
42013
+ return getDepartmentMemberByName(departmentName, memberName);
42014
+ };
42015
+ var getDepartmentMemberById = (memberId) => {
42016
+ for (const department of listDepartments()) {
42017
+ const targetMember = department.departmentMembers.find((member) => member.id === memberId);
42018
+ if (targetMember) return targetMember;
42168
42019
  }
42020
+ return null;
42169
42021
  };
42170
-
42171
- // src/tools/tools/tasks/GoalDelete.ts
42172
- var DESCRIPTION19 = `
42173
- \u5220\u9664\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal\u53CA\u5176\u5BF9\u5E94\u7684JSON\u6587\u4EF6
42174
-
42175
- \u5DE5\u5177\u4F7F\u7528\u89C4\u5219:
42176
- - \u5F53\u76EE\u6807\u5DF2\u7ECF\u5B8C\u6210(completed)\u6216\u5931\u8D25(failed)\u540E,\u9700\u8981\u6E05\u7406\u65F6\u8C03\u7528;
42177
- - \u5220\u9664\u540E\u4E0D\u53EF\u6062\u590D,\u8BF7\u8C28\u614E\u4F7F\u7528;
42178
- `;
42179
- var goalDelete = {
42180
- name: `goal_delete`,
42181
- description: DESCRIPTION19,
42182
- input_schema: {
42183
- type: `object`,
42184
- properties: {
42185
- id: {
42186
- type: `string`,
42187
- description: `\u8981\u5220\u9664\u7684\u76EE\u6807ID`
42188
- }
42189
- },
42190
- required: [`id`]
42191
- },
42192
- async execute(input, userRequest) {
42193
- const id = input.id;
42194
- const goal = getGoalById(id);
42195
- if (!goal) {
42196
- return `[GoalDelete] \u627E\u4E0D\u5230\u6307\u5B9Aid\u7684\u76EE\u6807`;
42197
- }
42198
- const deleted = deleteGoal(id);
42199
- if (deleted) {
42200
- return `[GoalDelete] \u76EE\u6807\u5DF2\u5220\u9664: id=${id}, subject=${goal.subject}`;
42201
- }
42202
- return `[GoalDelete] \u5220\u9664\u76EE\u6807\u5931\u8D25: id=${id}`;
42022
+ var deleteDepartmentMemberById = (departmentName, memberId) => {
42023
+ const department = getDepartment(departmentName);
42024
+ if (!department) throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
42025
+ const memberIdx = department.departmentMembers.findIndex((member) => member.id === memberId);
42026
+ if (memberIdx === -1) {
42027
+ throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94 id \u7684 department member: ${memberId}`);
42028
+ }
42029
+ const workspaceId = department.departmentMembers[memberIdx].workspaceId;
42030
+ if (department.headMemberId === memberId) {
42031
+ delete department.headMemberId;
42203
42032
  }
42033
+ department.departmentMembers = department.departmentMembers.filter((_, index) => index !== memberIdx);
42034
+ (0, import_fs12.writeFileSync)(getDepartmentJsonPath(departmentName), JSON.stringify(department, null, " "), "utf-8");
42035
+ deleteWorkspace(workspaceId);
42204
42036
  };
42205
42037
 
42206
- // src/tools/tools/department/DepartmentCreate.ts
42207
- var import_node_crypto8 = require("node:crypto");
42208
-
42209
- // src/department/mailbox/mailbox.ts
42210
- var import_node_crypto7 = require("node:crypto");
42211
-
42212
- // src/agent/interruptRegistry.ts
42213
- var registry = /* @__PURE__ */ new Map();
42214
- var markRunning = (userId) => {
42215
- const current = registry.get(userId);
42216
- if (current) {
42217
- current.startedAt = Date.now();
42218
- return;
42038
+ // src/skill/SkillValidator.ts
42039
+ var import_node_fs3 = require("node:fs");
42040
+ var import_node_path12 = __toESM(require("node:path"));
42041
+ var SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
42042
+ var REQUIRED_SECTIONS = [
42043
+ /(^|\n)##\s+(when to use|何时使用|trigger|triggers)\b/i,
42044
+ /(^|\n)##\s+(steps|workflow|procedure|执行步骤|工作流)\b/i
42045
+ ];
42046
+ var stripYamlQuotes = (value) => {
42047
+ const trimmed = value.trim();
42048
+ if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) {
42049
+ return trimmed.slice(1, -1).trim();
42219
42050
  }
42220
- registry.set(userId, { messages: [], startedAt: Date.now(), listeners: /* @__PURE__ */ new Set() });
42051
+ return trimmed;
42221
42052
  };
42222
- var markDone = (userId) => {
42223
- registry.delete(userId);
42053
+ var addError = (errors, code, message) => {
42054
+ errors.push({ code, message });
42224
42055
  };
42225
- var hasRunningAgent = (userId) => {
42226
- return registry.has(userId);
42056
+ var addWarning = (warnings, code, message) => {
42057
+ warnings.push({ code, message });
42227
42058
  };
42228
- var queueInterrupt = (userId, message) => {
42229
- const entry = registry.get(userId);
42230
- if (!entry) return false;
42231
- const interruptMessage = typeof message === "string" ? { content: message } : message;
42232
- entry.messages.push(interruptMessage);
42233
- console.log(`[interrupt] \u7528\u6237 ${userId} \u65B0\u6D88\u606F\u5DF2\u5165\u961F\uFF0C\u5F53\u524D\u961F\u5217\u957F\u5EA6: ${entry.messages.length}`);
42234
- for (const listener of entry.listeners) {
42235
- try {
42236
- listener();
42237
- } catch (err) {
42238
- console.warn(`[interrupt] \u7528\u6237 ${userId} \u4E2D\u65AD\u76D1\u542C\u5668\u6267\u884C\u5931\u8D25: ${err.message}`);
42239
- }
42059
+ var isValidSkillName = (name) => {
42060
+ return SKILL_NAME_PATTERN.test(name) && !name.includes(`--`);
42061
+ };
42062
+ var assertSafeSkillTarget = (rootDir, skillName) => {
42063
+ if (!isValidSkillName(skillName)) {
42064
+ throw new Error(`Invalid skill name "${skillName}". Use lowercase letters, digits, and single hyphens only.`);
42240
42065
  }
42241
- return true;
42066
+ const root = import_node_path12.default.resolve(rootDir);
42067
+ const target = import_node_path12.default.resolve(root, skillName);
42068
+ if (target !== import_node_path12.default.join(root, skillName) || !target.startsWith(root + import_node_path12.default.sep)) {
42069
+ throw new Error(`Invalid skill install target for "${skillName}".`);
42070
+ }
42071
+ return target;
42242
42072
  };
42243
- var registerInterruptListener = (userId, listener) => {
42244
- const entry = registry.get(userId);
42245
- if (!entry) return () => void 0;
42246
- entry.listeners.add(listener);
42247
- return () => {
42248
- entry.listeners.delete(listener);
42073
+ var parseSkillDocument = (skillMd) => {
42074
+ const fmMatch = skillMd.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
42075
+ if (!fmMatch) return null;
42076
+ const frontmatter = {};
42077
+ for (const rawLine of fmMatch[1].split(/\r?\n/)) {
42078
+ const line = rawLine.trim();
42079
+ if (!line || line.startsWith(`#`)) continue;
42080
+ const match2 = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
42081
+ if (!match2) {
42082
+ frontmatter[`__invalid__${Object.keys(frontmatter).length}`] = line;
42083
+ continue;
42084
+ }
42085
+ frontmatter[match2[1]] = stripYamlQuotes(match2[2]);
42086
+ }
42087
+ return {
42088
+ name: frontmatter.name ?? ``,
42089
+ description: frontmatter.description ?? ``,
42090
+ body: skillMd.slice(fmMatch[0].length).trim(),
42091
+ frontmatter
42249
42092
  };
42250
42093
  };
42251
- var drainInterrupts = (userId) => {
42252
- const entry = registry.get(userId);
42253
- if (!entry || entry.messages.length === 0) return [];
42254
- const messages = [...entry.messages];
42255
- entry.messages.length = 0;
42256
- console.log(`[interrupt] \u7528\u6237 ${userId} drain ${messages.length} \u6761\u4E2D\u65AD\u6D88\u606F`);
42257
- return messages;
42094
+ var validateSkillDocument = (input) => {
42095
+ const errors = [];
42096
+ const warnings = [];
42097
+ const skillMd = input.skillMd;
42098
+ if (!skillMd || !skillMd.trim()) {
42099
+ addError(errors, `empty_skill_md`, `SKILL.md content is required.`);
42100
+ return { ok: false, errors, warnings };
42101
+ }
42102
+ const parsed = parseSkillDocument(skillMd);
42103
+ if (!parsed) {
42104
+ addError(errors, `missing_frontmatter`, `SKILL.md must start with YAML frontmatter delimited by "---".`);
42105
+ return { ok: false, errors, warnings };
42106
+ }
42107
+ const invalidFrontmatterLines = Object.keys(parsed.frontmatter).filter((key) => key.startsWith(`__invalid__`));
42108
+ if (invalidFrontmatterLines.length > 0) {
42109
+ addError(errors, `invalid_frontmatter`, `Frontmatter must contain simple "key: value" lines.`);
42110
+ }
42111
+ if (!parsed.name) {
42112
+ addError(errors, `missing_name`, `Frontmatter field "name" is required.`);
42113
+ } else if (!isValidSkillName(parsed.name)) {
42114
+ addError(errors, `invalid_name`, `Skill name "${parsed.name}" must be kebab-case, under 64 characters, and path-safe.`);
42115
+ }
42116
+ if (!parsed.description) {
42117
+ addError(errors, `missing_description`, `Frontmatter field "description" is required.`);
42118
+ } else if (parsed.description.length > 500) {
42119
+ addWarning(warnings, `long_description`, `Skill description is long; keep trigger metadata concise.`);
42120
+ }
42121
+ if (input.skillName && parsed.name && input.skillName !== parsed.name) {
42122
+ addError(errors, `name_mismatch`, `Input skillName "${input.skillName}" must match frontmatter name "${parsed.name}".`);
42123
+ }
42124
+ if (input.description && parsed.description && input.description !== parsed.description) {
42125
+ addError(errors, `description_mismatch`, `Input description must match frontmatter description.`);
42126
+ }
42127
+ if (!parsed.body) {
42128
+ addError(errors, `empty_body`, `SKILL.md body is required.`);
42129
+ } else {
42130
+ if (!/(^|\n)#\s+\S/.test(parsed.body)) {
42131
+ addWarning(warnings, `missing_title`, `Skill body should start with a Markdown H1 title.`);
42132
+ }
42133
+ for (const sectionPattern of REQUIRED_SECTIONS) {
42134
+ if (!sectionPattern.test(parsed.body)) {
42135
+ addWarning(warnings, `missing_recommended_section`, `Skill body should include when-to-use and execution workflow sections.`);
42136
+ break;
42137
+ }
42138
+ }
42139
+ }
42140
+ if (input.baseDir) {
42141
+ const normalizedBase = import_node_path12.default.resolve(input.baseDir);
42142
+ for (const match2 of parsed.body.matchAll(/\]\(([^)]+)\)/g)) {
42143
+ const href = match2[1].trim();
42144
+ if (!href || /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith(`#`)) continue;
42145
+ const target = import_node_path12.default.resolve(normalizedBase, href.split(`#`)[0]);
42146
+ if (!target.startsWith(normalizedBase + import_node_path12.default.sep) && target !== normalizedBase) {
42147
+ addError(errors, `unsafe_reference`, `Relative link "${href}" escapes the skill directory.`);
42148
+ } else if (!(0, import_node_fs3.existsSync)(target)) {
42149
+ addError(errors, `missing_reference`, `Relative link "${href}" does not exist in the skill directory.`);
42150
+ }
42151
+ }
42152
+ }
42153
+ return {
42154
+ ok: errors.length === 0,
42155
+ skill: parsed,
42156
+ errors,
42157
+ warnings
42158
+ };
42258
42159
  };
42259
-
42260
- // src/agent/events.ts
42261
- var import_node_crypto4 = require("node:crypto");
42262
- var rowToEvent = (row) => ({
42263
- id: row.id,
42264
- userId: row.userId,
42265
- type: row.type,
42266
- source: row.source,
42267
- sourceId: row.sourceId,
42268
- status: row.status,
42269
- payload: JSON.parse(row.payloadJson || "{}"),
42270
- createdAt: row.createdAt,
42271
- injectedAt: row.injectedAt ?? void 0,
42272
- handledAt: row.handledAt ?? void 0,
42273
- updatedAt: row.updatedAt
42274
- });
42275
- var recordAgentEvent = (input) => {
42276
- const db3 = createSqliteDB();
42277
- const now = Date.now();
42278
- const id = `evt_${(0, import_node_crypto4.randomUUID)().slice(0, 12)}`;
42279
- const payloadJson = JSON.stringify(input.payload);
42280
- db3.prepare(`
42281
- INSERT INTO agent_events (
42282
- id, user_id, type, source, source_id, status, payload_json, created_at, updated_at
42283
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
42284
- ON CONFLICT(type, source, source_id) DO UPDATE SET
42285
- user_id = excluded.user_id,
42286
- status = CASE
42287
- WHEN agent_events.status IN ('handled', 'ignored') THEN agent_events.status
42288
- ELSE excluded.status
42289
- END,
42290
- payload_json = excluded.payload_json,
42291
- updated_at = excluded.updated_at
42292
- `).run(
42293
- id,
42294
- input.userId,
42295
- input.type,
42296
- input.source,
42297
- input.sourceId,
42298
- input.status ?? "pending",
42299
- payloadJson,
42300
- now,
42301
- now
42302
- );
42303
- const row = db3.prepare(`
42304
- SELECT
42305
- id,
42306
- user_id as userId,
42307
- type,
42308
- source,
42309
- source_id as sourceId,
42310
- status,
42311
- payload_json as payloadJson,
42312
- created_at as createdAt,
42313
- injected_at as injectedAt,
42314
- handled_at as handledAt,
42315
- updated_at as updatedAt
42316
- FROM agent_events
42317
- WHERE type = ? AND source = ? AND source_id = ?
42318
- `).get(input.type, input.source, input.sourceId);
42319
- return rowToEvent(row);
42160
+ var mergeIssues = (target, source) => {
42161
+ target.errors.push(...source.errors);
42162
+ target.warnings.push(...source.warnings);
42320
42163
  };
42321
- var listPendingAgentEvents = (userId, limit = 10) => {
42322
- const db3 = createSqliteDB();
42323
- const rows = db3.prepare(`
42324
- SELECT
42325
- id,
42326
- user_id as userId,
42327
- type,
42328
- source,
42329
- source_id as sourceId,
42330
- status,
42331
- payload_json as payloadJson,
42332
- created_at as createdAt,
42333
- injected_at as injectedAt,
42334
- handled_at as handledAt,
42335
- updated_at as updatedAt
42336
- FROM agent_events
42337
- WHERE user_id = ?
42338
- AND status IN ('pending', 'processing')
42339
- ORDER BY created_at ASC
42340
- LIMIT ?
42341
- `).all(userId, limit);
42342
- return rows.map(rowToEvent);
42164
+ var validateScriptsDirectory = (skillDir, warnings) => {
42165
+ const scriptsDir = import_node_path12.default.join(skillDir, `scripts`);
42166
+ if (!(0, import_node_fs3.existsSync)(scriptsDir)) return;
42167
+ const entries = (0, import_node_fs3.readdirSync)(scriptsDir, { withFileTypes: true }).filter((entry) => entry.isFile());
42168
+ if (entries.length === 0) {
42169
+ addWarning(warnings, `empty_scripts_dir`, `scripts/ exists but contains no files.`);
42170
+ }
42343
42171
  };
42344
- var markAgentEventsInjected = (eventIds) => {
42345
- if (eventIds.length === 0) return;
42346
- const db3 = createSqliteDB();
42347
- const now = Date.now();
42348
- const stmt = db3.prepare(`
42349
- UPDATE agent_events
42350
- SET status = CASE WHEN status = 'pending' THEN 'processing' ELSE status END,
42351
- injected_at = COALESCE(injected_at, ?),
42352
- updated_at = ?
42353
- WHERE id = ?
42354
- AND status IN ('pending', 'processing')
42355
- `);
42356
- const tx = db3.transaction((ids) => {
42357
- for (const id of ids) stmt.run(now, now, id);
42172
+ var validateSkillDirectory = (skillDir, options = {}) => {
42173
+ const errors = [];
42174
+ const warnings = [];
42175
+ const normalizedSkillDir = import_node_path12.default.resolve(skillDir);
42176
+ const skillMdPath = import_node_path12.default.join(normalizedSkillDir, `SKILL.md`);
42177
+ if (!(0, import_node_fs3.existsSync)(normalizedSkillDir) || !(0, import_node_fs3.statSync)(normalizedSkillDir).isDirectory()) {
42178
+ addError(errors, `missing_skill_dir`, `Skill directory does not exist: ${normalizedSkillDir}`);
42179
+ return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
42180
+ }
42181
+ if (!(0, import_node_fs3.existsSync)(skillMdPath) || !(0, import_node_fs3.statSync)(skillMdPath).isFile()) {
42182
+ addError(errors, `missing_skill_md`, `Skill directory must contain SKILL.md.`);
42183
+ return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
42184
+ }
42185
+ const raw2 = (0, import_node_fs3.readFileSync)(skillMdPath, `utf-8`);
42186
+ const documentResult = validateSkillDocument({
42187
+ skillName: options.expectedName ?? import_node_path12.default.basename(normalizedSkillDir),
42188
+ skillMd: raw2,
42189
+ baseDir: normalizedSkillDir
42358
42190
  });
42359
- tx(eventIds);
42360
- };
42361
- var markAgentEventsHandled = (eventIds, status = "handled") => {
42362
- if (eventIds.length === 0) return;
42363
- const db3 = createSqliteDB();
42364
- const now = Date.now();
42365
- const stmt = db3.prepare(`
42366
- UPDATE agent_events
42367
- SET status = ?,
42368
- handled_at = COALESCE(handled_at, ?),
42369
- updated_at = ?
42370
- WHERE id = ?
42371
- AND status IN ('pending', 'processing')
42372
- `);
42373
- const tx = db3.transaction((ids) => {
42374
- for (const id of ids) stmt.run(status, now, now, id);
42375
- });
42376
- tx(eventIds);
42377
- };
42378
- var renderAgentEventReminder = (events) => {
42379
- if (events.length === 0) return "";
42380
- const lines = events.map((event) => {
42381
- const owner = typeof event.payload.ownerMailboxId === "string" ? event.payload.ownerMailboxId : void 0;
42382
- const mailboxMessageId = typeof event.payload.mailboxMessageId === "string" ? event.payload.mailboxMessageId : event.sourceId;
42383
- const summary = typeof event.payload.summary === "string" ? event.payload.summary : typeof event.payload.contentPreview === "string" ? event.payload.contentPreview : "";
42384
- return [
42385
- `- eventId=${event.id}`,
42386
- `type=${event.type}`,
42387
- owner ? `owner=${owner}` : "",
42388
- mailboxMessageId ? `mailboxMessageId=${mailboxMessageId}` : "",
42389
- summary ? `summary=${summary}` : ""
42390
- ].filter(Boolean).join(" ");
42391
- }).join("\n");
42392
- const hasCeoFollowup = events.some((event) => event.type === "ceo.followup_required");
42393
- const ceoFollowupInstruction = hasCeoFollowup ? [
42394
- ``,
42395
- `\u5176\u4E2D type=ceo.followup_required \u7684\u4E8B\u4EF6\uFF0C\u662F\u56E2\u961F\u5DF2\u7ECF\u628A\u7ED3\u679C\u56DE\u5230\u4E86\u4F60\u8FD9\u91CC\u2014\u2014\u8001\u677F\u8FD8\u5728\u7B49\u4F60\u628A\u8FD9\u4E9B\u7ED3\u679C\u8F6C\u8FBE\u7ED9\u4ED6\u3002`,
42396
- `\u8BFB\u4E00\u904D\uFF0C\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u7ED9\u8001\u677F\u6C47\u62A5\u4E00\u6B21\uFF1A\u6709\u591A\u6761\u5C31\u5408\u5E76\u6210\u4E00\u6761\u8BF4\u6E05\u695A\uFF0C\u522B\u4E00\u6761\u4E8B\u4EF6\u56DE\u4E00\u53E5\u3001\u628A\u540C\u4E00\u4EF6\u4E8B\u91CD\u590D\u53D1\u7ED9\u8001\u677F\u3002`,
42397
- `\u5982\u679C\u7ED3\u679C\u8BF4\u660E\u4E8B\u60C5\u8FD8\u6CA1\u505A\u5B8C\u6216\u5361\u4F4F\u4E86\uFF0C\u5C31\u5982\u5B9E\u8FD9\u4E48\u8BB2\uFF0C\u800C\u4E0D\u662F\u542B\u7CCA\u5730\u201C\u7A0D\u540E\u540C\u6B65\u201D\u3002`
42398
- ].join("\n") : "";
42399
- return `<system-reminder>
42400
- \u672C\u8F6E\u6709 ${events.length} \u6761\u5185\u90E8\u4E8B\u4EF6\u53EF\u7528\uFF1A
42401
- ${lines}
42402
-
42403
- \u8FD9\u4E9B\u4E8B\u4EF6\u4E0D\u662F\u7528\u6237\u7684\u65B0\u8BF7\u6C42\uFF0C\u4E5F\u4E0D\u5E94\u8BE5\u4F5C\u4E3A\u7528\u6237\u786E\u8BA4\u3002\u82E5\u4E8B\u4EF6\u7C7B\u578B\u662F mailbox.message_received\uFF0C\u8868\u793A\u4F60\u7684 mailbox \u5728\u5F53\u524D\u5DE5\u4F5C\u671F\u95F4\u6536\u5230\u65B0\u5185\u90E8\u534F\u4F5C\u6D88\u606F\uFF1B\u8BF7\u5C3D\u5FEB\u8C03\u7528 list_mailbox \u67E5\u770B\u961F\u5217\uFF0C\u5E76\u5728\u9700\u8981\u65F6\u7528 get_mailbox(message_id) \u9886\u53D6\u5173\u8054\u6D88\u606F\u3002\u4E0D\u8981\u76F4\u63A5\u628A\u4E8B\u4EF6\u5F53\u4F5C\u5DF2\u9886\u53D6\u90AE\u4EF6\u3002\u8BF7\u53EA\u5728\u9700\u8981\u65F6\u81EA\u7136\u5730\u6C47\u603B\u7ED9\u7528\u6237\uFF1B\u5982\u9700\u7EE7\u7EED\u534F\u4F5C\uFF0C\u4F18\u5148\u4F7F\u7528\u4E8B\u4EF6\u4E2D\u5173\u8054\u7684 mailboxMessageId/thread \u7EE7\u7EED\u5904\u7406\u3002\u4E0D\u8981\u590D\u8FF0\u672C system-reminder\u3002
42404
- ${ceoFollowupInstruction}
42405
- </system-reminder>`;
42191
+ mergeIssues({ errors, warnings }, documentResult);
42192
+ validateScriptsDirectory(normalizedSkillDir, warnings);
42193
+ return {
42194
+ ok: errors.length === 0,
42195
+ skill: documentResult.skill,
42196
+ errors,
42197
+ warnings,
42198
+ skillDir: normalizedSkillDir,
42199
+ skillMdPath
42200
+ };
42406
42201
  };
42202
+ var smokeTestSkillDirectory = (skillDir, options = {}) => {
42203
+ const directoryResult = validateSkillDirectory(skillDir, options);
42204
+ const errors = [...directoryResult.errors];
42205
+ const warnings = [...directoryResult.warnings];
42206
+ let detail;
42207
+ if (directoryResult.ok && directoryResult.skill) {
42208
+ detail = `Base directory for this skill: ${directoryResult.skillDir}
42407
42209
 
42408
- // src/department/mailbox/events.ts
42409
- var import_node_crypto5 = require("node:crypto");
42410
- var parseDetail = (detailJson) => {
42411
- if (!detailJson) return void 0;
42412
- try {
42413
- const parsed = JSON.parse(detailJson);
42414
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
42415
- return parsed;
42210
+ ${directoryResult.skill.body}`;
42211
+ if (!detail.includes(directoryResult.skillDir)) {
42212
+ addError(errors, `smoke_missing_base_dir`, `Skill detail does not include its base directory.`);
42213
+ }
42214
+ if (!detail.includes(directoryResult.skill.body)) {
42215
+ addError(errors, `smoke_missing_body`, `Skill detail does not include the SKILL.md body.`);
42216
+ }
42217
+ if (detail.length < 80) {
42218
+ addWarning(warnings, `smoke_short_detail`, `Skill detail is unusually short.`);
42416
42219
  }
42417
- } catch {
42418
- return void 0;
42419
42220
  }
42420
- return void 0;
42421
- };
42422
- var mapMailboxEventRow = (row) => {
42423
- const hasMessage = typeof row.toMailboxId === "string" && typeof row.fromMailboxId === "string" && typeof row.content === "string" && typeof row.sendTime === "number" && typeof row.status === "string";
42424
42221
  return {
42425
- id: row.id,
42426
- messageId: row.messageId || void 0,
42427
- mailboxId: row.mailboxId,
42428
- actorMailboxId: row.actorMailboxId || void 0,
42429
- counterpartMailboxId: row.counterpartMailboxId || void 0,
42430
- eventType: row.eventType,
42431
- detail: parseDetail(row.detailJson),
42432
- createdAt: row.createdAt,
42433
- message: hasMessage ? {
42434
- id: row.messageId || "",
42435
- toMailboxId: row.toMailboxId,
42436
- fromMailboxId: row.fromMailboxId,
42437
- content: row.content,
42438
- sendTime: row.sendTime,
42439
- status: row.status,
42440
- originUserId: row.originUserId || void 0,
42441
- originPlatform: row.originPlatform || void 0,
42442
- threadId: row.threadId || void 0,
42443
- parentMessageId: row.parentMessageId || void 0
42444
- } : null
42222
+ ...directoryResult,
42223
+ ok: errors.length === 0,
42224
+ errors,
42225
+ warnings,
42226
+ detail
42445
42227
  };
42446
42228
  };
42447
- var recordMailboxEvent = (input) => {
42448
- const db3 = createSqliteDB();
42449
- const event = {
42450
- id: (0, import_node_crypto5.randomUUID)().slice(0, 12),
42451
- messageId: input.messageId,
42452
- mailboxId: input.mailboxId,
42453
- actorMailboxId: input.actorMailboxId,
42454
- counterpartMailboxId: input.counterpartMailboxId,
42455
- eventType: input.eventType,
42456
- detail: input.detail,
42457
- createdAt: input.createdAt ?? Date.now()
42458
- };
42459
- const stmt = db3.prepare(
42460
- `INSERT INTO mailbox_events (
42461
- id,
42462
- message_id,
42463
- mailbox_id,
42464
- actor_mailbox_id,
42465
- counterpart_mailbox_id,
42466
- event_type,
42467
- detail_json,
42468
- created_at
42469
- ) VALUES (?,?,?,?,?,?,?,?)`
42470
- );
42471
- stmt.run(
42472
- event.id,
42473
- event.messageId || null,
42474
- event.mailboxId,
42475
- event.actorMailboxId || null,
42476
- event.counterpartMailboxId || null,
42477
- event.eventType,
42478
- event.detail ? JSON.stringify(event.detail) : null,
42479
- event.createdAt
42480
- );
42481
- return event;
42229
+ var formatSkillValidationIssues = (result) => {
42230
+ const lines = [
42231
+ ...result.errors.map((issue) => `- [${issue.code}] ${issue.message}`),
42232
+ ...result.warnings.map((issue) => `- [warning:${issue.code}] ${issue.message}`)
42233
+ ];
42234
+ return lines.join(`
42235
+ `);
42482
42236
  };
42483
- var recordMailboxStatusChange = (params) => {
42484
- if (params.previousStatus === params.nextStatus) {
42485
- return null;
42486
- }
42487
- return recordMailboxEvent({
42488
- messageId: params.messageId,
42489
- mailboxId: params.mailboxId,
42490
- actorMailboxId: params.actorMailboxId,
42491
- counterpartMailboxId: params.counterpartMailboxId,
42492
- eventType: "message_status_changed",
42493
- detail: {
42494
- previousStatus: params.previousStatus,
42495
- nextStatus: params.nextStatus,
42496
- reason: params.reason
42497
- }
42498
- });
42237
+
42238
+ // src/department/learning.ts
42239
+ var ensureDir = (dir) => (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
42240
+ var readJsonArray = (filePath) => {
42241
+ if (!(0, import_node_fs4.existsSync)(filePath)) return [];
42242
+ return JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf-8"));
42499
42243
  };
42500
- var listMailboxEvents = (params) => {
42501
- const db3 = createSqliteDB();
42502
- const limit = Math.max(1, Math.min(params.limit ?? 100, 500));
42503
- const stmt = db3.prepare(
42504
- `SELECT
42505
- e.id as id,
42506
- e.message_id as messageId,
42507
- e.mailbox_id as mailboxId,
42508
- e.actor_mailbox_id as actorMailboxId,
42509
- e.counterpart_mailbox_id as counterpartMailboxId,
42510
- e.event_type as eventType,
42511
- e.detail_json as detailJson,
42512
- e.created_at as createdAt,
42513
- m.to_mailbox_id as toMailboxId,
42514
- m.from_mailbox_id as fromMailboxId,
42515
- m.content as content,
42516
- m.send_time as sendTime,
42517
- m.status as status,
42518
- m.origin_user_id as originUserId,
42519
- m.origin_platform as originPlatform,
42520
- m.thread_id as threadId,
42521
- m.parent_message_id as parentMessageId
42522
- FROM mailbox_events e
42523
- LEFT JOIN mailbox m ON m.id = e.message_id
42524
- WHERE e.mailbox_id = ? OR e.actor_mailbox_id = ? OR e.counterpart_mailbox_id = ?
42525
- ORDER BY e.created_at DESC
42526
- LIMIT ?`
42527
- );
42528
- const rows = stmt.all(params.mailboxId, params.mailboxId, params.mailboxId, limit);
42529
- return rows.map(mapMailboxEventRow);
42244
+ var writeJsonArray = (filePath, records) => {
42245
+ ensureDir(import_node_path13.default.dirname(filePath));
42246
+ (0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(records, null, " "), "utf-8");
42530
42247
  };
42531
-
42532
- // src/department/DepartmentMember.ts
42533
- var import_fs13 = require("fs");
42534
- var import_path18 = __toESM(require("path"));
42535
-
42536
- // src/department/Department.ts
42537
- var import_path17 = __toESM(require("path"));
42538
- var import_fs12 = require("fs");
42539
- var legacyMigrationChecked = false;
42540
- var getDepartmentBaseDir = () => {
42541
- return import_path17.default.join(getDuclawHomeDir(), "department");
42248
+ var departmentMemoryPath = (departmentName) => {
42249
+ return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-memory.json");
42542
42250
  };
42543
- var getLegacyTeamBaseDir = () => {
42544
- return import_path17.default.join(getDuclawHomeDir(), "team");
42251
+ var departmentSkillPath = (departmentName) => {
42252
+ return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-skills.json");
42545
42253
  };
42546
- var getDepartmentWorkSpaceDir = (departmentName) => {
42547
- return import_path17.default.join(getDepartmentBaseDir(), "workspace", departmentName);
42254
+ var departmentSkillDir = (departmentName, skillName) => {
42255
+ return assertSafeSkillTarget(import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "skills"), skillName);
42548
42256
  };
42549
- var getLegacyTeamWorkSpaceDir = (teamName) => {
42550
- return import_path17.default.join(getLegacyTeamBaseDir(), "workspace", teamName);
42257
+ var proposalsPath = () => {
42258
+ return import_node_path13.default.join(getDepartmentBaseDir(), "department-proposals.json");
42551
42259
  };
42552
- var getDepartmentJsonPath = (departmentName) => {
42553
- return import_path17.default.join(getDepartmentWorkSpaceDir(departmentName), "department.json");
42260
+ var getDepartmentNameForHead = (request) => {
42261
+ const mailboxId = request?.departmentAgentId;
42262
+ if (!mailboxId) return null;
42263
+ const member = getDepartmentMemberByMailboxId(mailboxId);
42264
+ if (!member || member.role !== "department_head") return null;
42265
+ const [departmentName] = mailboxId.split("::");
42266
+ return departmentName || null;
42554
42267
  };
42555
- var getLegacyTeamJsonPath = (teamName) => {
42556
- return import_path17.default.join(getLegacyTeamWorkSpaceDir(teamName), "team.json");
42268
+ var listDepartmentMemories = (departmentName) => {
42269
+ return readJsonArray(departmentMemoryPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
42557
42270
  };
42558
- var mapLegacyRole = (role) => {
42559
- return role === "team_manager" ? "department_head" : "executor";
42271
+ var createDepartmentMemory = (departmentName, input) => {
42272
+ const now = Date.now();
42273
+ const memory = {
42274
+ id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
42275
+ departmentName,
42276
+ title: input.title,
42277
+ content: input.content,
42278
+ sourceMailboxId: input.sourceMailboxId,
42279
+ createdAt: now,
42280
+ updatedAt: now
42281
+ };
42282
+ const records = listDepartmentMemories(departmentName);
42283
+ records.push(memory);
42284
+ writeJsonArray(departmentMemoryPath(departmentName), records);
42285
+ return memory;
42560
42286
  };
42561
- var mapLegacyDepartment = (legacy) => {
42562
- const departmentMembers = (legacy.teamMembers ?? []).map((member) => ({
42563
- id: member.id,
42564
- name: member.name,
42565
- departmentId: legacy.id,
42566
- mailBoxId: member.mailBoxId,
42567
- workspaceId: member.workspaceId,
42568
- role: mapLegacyRole(member.role),
42569
- focusOn: member.focusOn
42570
- }));
42571
- const headMemberId = legacy.managerMemberId ?? departmentMembers.find((member) => member.role === "department_head")?.id;
42572
- return {
42573
- id: legacy.id,
42574
- name: legacy.name,
42575
- sourceGoalId: legacy.goalId,
42576
- charter: legacy.goalId ? `Legacy department migrated from team goal ${legacy.goalId}.` : "Legacy department migrated from team data.",
42577
- workpath: legacy.workpath,
42578
- headMemberId,
42579
- departmentMembers
42287
+ var updateDepartmentMemory = (departmentName, id, patch) => {
42288
+ const records = listDepartmentMemories(departmentName);
42289
+ const idx = records.findIndex((record) => record.id === id);
42290
+ if (idx < 0) return null;
42291
+ records[idx] = {
42292
+ ...records[idx],
42293
+ ...patch.title !== void 0 ? { title: patch.title } : {},
42294
+ ...patch.content !== void 0 ? { content: patch.content } : {},
42295
+ updatedAt: Date.now()
42580
42296
  };
42297
+ writeJsonArray(departmentMemoryPath(departmentName), records);
42298
+ return records[idx];
42581
42299
  };
42582
- var migrateLegacyTeamsToDepartments = () => {
42583
- if (legacyMigrationChecked) return;
42584
- legacyMigrationChecked = true;
42585
- const legacyWorkspaceDir = import_path17.default.join(getLegacyTeamBaseDir(), "workspace");
42586
- if (!(0, import_fs12.existsSync)(legacyWorkspaceDir)) return;
42587
- for (const legacyName of (0, import_fs12.readdirSync)(legacyWorkspaceDir)) {
42588
- const legacyJsonPath = getLegacyTeamJsonPath(legacyName);
42589
- if (!(0, import_fs12.existsSync)(legacyJsonPath)) continue;
42590
- const departmentJsonPath = getDepartmentJsonPath(legacyName);
42591
- if ((0, import_fs12.existsSync)(departmentJsonPath)) continue;
42592
- try {
42593
- const legacy = JSON.parse((0, import_fs12.readFileSync)(legacyJsonPath, "utf-8"));
42594
- const department = mapLegacyDepartment(legacy);
42595
- (0, import_fs12.mkdirSync)(getDepartmentWorkSpaceDir(department.name), { recursive: true });
42596
- (0, import_fs12.writeFileSync)(departmentJsonPath, JSON.stringify(department, null, " "), "utf-8");
42597
- } catch (err) {
42598
- console.warn(`[department] Failed to migrate legacy team ${legacyName}: ${err.message}`);
42599
- }
42600
- }
42601
- };
42602
- var createDepartment = (departmentDefinition) => {
42603
- if (!departmentDefinition) throw new Error(`[createDepartment] departmentDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
42604
- const departmentPath = getDepartmentWorkSpaceDir(departmentDefinition.name);
42605
- (0, import_fs12.mkdirSync)(departmentPath, { recursive: true });
42606
- (0, import_fs12.writeFileSync)(getDepartmentJsonPath(departmentDefinition.name), JSON.stringify(departmentDefinition, null, " "), "utf-8");
42607
- return departmentDefinition;
42300
+ var deleteDepartmentMemory = (departmentName, id) => {
42301
+ const records = listDepartmentMemories(departmentName);
42302
+ const target = records.find((record) => record.id === id) ?? null;
42303
+ writeJsonArray(departmentMemoryPath(departmentName), records.filter((record) => record.id !== id));
42304
+ return target;
42608
42305
  };
42609
- var getDepartment = (name) => {
42610
- migrateLegacyTeamsToDepartments();
42611
- const departmentJsonPath = getDepartmentJsonPath(name);
42612
- if (!(0, import_fs12.existsSync)(departmentJsonPath)) return null;
42613
- const text2 = (0, import_fs12.readFileSync)(departmentJsonPath, "utf-8");
42614
- return JSON.parse(text2);
42306
+ var listDepartmentSkills = (departmentName) => {
42307
+ return readJsonArray(departmentSkillPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
42615
42308
  };
42616
- var listDepartments = () => {
42617
- migrateLegacyTeamsToDepartments();
42618
- const workspaceDir = import_path17.default.join(getDepartmentBaseDir(), "workspace");
42619
- if (!(0, import_fs12.existsSync)(workspaceDir)) return [];
42620
- const departments = [];
42621
- for (const departmentName of (0, import_fs12.readdirSync)(workspaceDir)) {
42622
- const department = getDepartment(departmentName);
42623
- if (department) departments.push(department);
42309
+ var proposeDepartmentSkill = (departmentName, input) => {
42310
+ const validation = validateSkillDocument({
42311
+ skillName: input.skillName,
42312
+ description: input.description,
42313
+ skillMd: input.skillMd
42314
+ });
42315
+ if (!validation.ok) {
42316
+ throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF1A
42317
+ ${formatSkillValidationIssues(validation)}`);
42624
42318
  }
42625
- return departments;
42626
- };
42627
- var getDepartmentById = (id) => {
42628
- const department = listDepartments().find((item) => item.id === id);
42629
- return department ?? null;
42319
+ const records = listDepartmentSkills(departmentName);
42320
+ if (records.some((record) => record.skillName === input.skillName && record.status !== "dropped")) {
42321
+ return null;
42322
+ }
42323
+ const now = Date.now();
42324
+ const skill = {
42325
+ id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
42326
+ departmentName,
42327
+ skillName: input.skillName,
42328
+ description: input.description,
42329
+ skillMd: input.skillMd,
42330
+ status: "pending",
42331
+ createdByMailboxId: input.createdByMailboxId,
42332
+ createdAt: now,
42333
+ updatedAt: now
42334
+ };
42335
+ records.push(skill);
42336
+ writeJsonArray(departmentSkillPath(departmentName), records);
42337
+ return skill;
42630
42338
  };
42631
- var deleteDepartment = (name) => {
42632
- if (!(0, import_fs12.existsSync)(getDepartmentJsonPath(name))) {
42633
- throw new Error(`[deleteDepartment] \u4E0D\u5B58\u5728\u5BF9\u5E94\u7684\u90E8\u95E8 ${name} \u7684 department.json \u6587\u4EF6`);
42339
+ var keepDepartmentSkill = (departmentName, id) => {
42340
+ const records = listDepartmentSkills(departmentName);
42341
+ const idx = records.findIndex((record) => record.id === id);
42342
+ if (idx < 0) return null;
42343
+ records[idx] = { ...records[idx], status: "active", updatedAt: Date.now() };
42344
+ const skillDir = departmentSkillDir(departmentName, records[idx].skillName);
42345
+ const validation = validateSkillDocument({
42346
+ skillName: records[idx].skillName,
42347
+ description: records[idx].description,
42348
+ skillMd: records[idx].skillMd,
42349
+ baseDir: skillDir
42350
+ });
42351
+ if (!validation.ok) {
42352
+ throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u4FDD\u7559\uFF1A
42353
+ ${formatSkillValidationIssues(validation)}`);
42354
+ }
42355
+ ensureDir(skillDir);
42356
+ (0, import_node_fs4.writeFileSync)(import_node_path13.default.join(skillDir, "SKILL.md"), records[idx].skillMd, "utf-8");
42357
+ const smokeTest = smokeTestSkillDirectory(skillDir, { expectedName: records[idx].skillName });
42358
+ if (!smokeTest.ok) {
42359
+ throw new Error(`[departmentSkill] Skill smoke test \u5931\u8D25\uFF1A
42360
+ ${formatSkillValidationIssues(smokeTest)}`);
42634
42361
  }
42635
- (0, import_fs12.rmSync)(getDepartmentJsonPath(name));
42362
+ writeJsonArray(departmentSkillPath(departmentName), records);
42363
+ return records[idx];
42636
42364
  };
42637
-
42638
- // src/department/workspace/workspace.ts
42639
- var db = createSqliteDB();
42640
- var getWorkspaceId = (departmentName, memberName) => {
42641
- return `${departmentName}::${memberName}`;
42365
+ var dropDepartmentSkill = (departmentName, id) => {
42366
+ const records = listDepartmentSkills(departmentName);
42367
+ const idx = records.findIndex((record) => record.id === id);
42368
+ if (idx < 0) return null;
42369
+ records[idx] = { ...records[idx], status: "dropped", updatedAt: Date.now() };
42370
+ writeJsonArray(departmentSkillPath(departmentName), records);
42371
+ return records[idx];
42642
42372
  };
42643
- var createWorkspace = (workspace) => {
42644
- const stmt = db.prepare(`INSERT INTO workspace (id, team_name, teammate_name, team_workpath) VALUES (?, ?, ?, ?) `);
42645
- stmt.run(
42646
- workspace.id,
42647
- workspace.departmentName,
42648
- workspace.memberName,
42649
- workspace.departmentWorkPath
42650
- );
42373
+ var createDepartmentProposal = (input) => {
42374
+ const records = readJsonArray(proposalsPath());
42375
+ const proposal = {
42376
+ ...input,
42377
+ id: (0, import_node_crypto4.randomUUID)().slice(0, 8),
42378
+ status: "pending",
42379
+ createdAt: Date.now()
42380
+ };
42381
+ records.push(proposal);
42382
+ writeJsonArray(proposalsPath(), records);
42383
+ return proposal;
42651
42384
  };
42652
- var deleteWorkspace = (workspaceId) => {
42653
- const stmt = db.prepare(`delete from workspace where id = ?`);
42654
- stmt.run(workspaceId);
42385
+ var buildDepartmentLearningContext = (departmentName) => {
42386
+ if (!departmentName) return "";
42387
+ const memories = listDepartmentMemories(departmentName);
42388
+ const activeSkills = listDepartmentSkills(departmentName).filter((skill) => skill.status === "active");
42389
+ if (memories.length === 0 && activeSkills.length === 0) return "";
42390
+ const memoryLines = memories.map(
42391
+ (memory) => ` - [id=${memory.id}] ${memory.title}
42392
+ ${memory.content.replace(/\n/g, "\n ")}`
42393
+ ).join("\n");
42394
+ const skillLines = activeSkills.map(
42395
+ (skill) => ` - ${skill.skillName}: ${skill.description}`
42396
+ ).join("\n");
42397
+ return [
42398
+ `<department-learning-context department="${departmentName}">`,
42399
+ memories.length > 0 ? `Department memories:
42400
+ ${memoryLines}` : "",
42401
+ activeSkills.length > 0 ? `Department skills:
42402
+ ${skillLines}` : "",
42403
+ `</department-learning-context>`
42404
+ ].filter(Boolean).join("\n");
42655
42405
  };
42656
42406
 
42657
- // src/department/DepartmentMember.ts
42658
- var createDepartmentMember = (departmentMemberDefinition) => {
42659
- if (!departmentMemberDefinition) throw new Error(`[createDepartmentMember] departmentMemberDefinition\u4E0D\u80FD\u4E3A\u7A7A`);
42660
- const { name, departmentId, workspaceId } = departmentMemberDefinition;
42661
- const department = getDepartmentById(departmentId);
42662
- if (!department) throw new Error(`[createDepartmentMember] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentId}`);
42663
- const memberPath = import_path18.default.join(getDepartmentWorkSpaceDir(department.name), name);
42664
- (0, import_fs13.mkdirSync)(memberPath, { recursive: true });
42665
- const workspace = {
42666
- id: getWorkspaceId(department.name, departmentMemberDefinition.name),
42667
- departmentName: department.name,
42668
- memberName: name,
42669
- departmentWorkPath: memberPath
42407
+ // src/skill/SkillRegistry.ts
42408
+ var getProjectSkillsPath = () => {
42409
+ const projectRoot = findProjectRoot();
42410
+ return projectRoot ? (0, import_path18.join)(projectRoot, "skills") : null;
42411
+ };
42412
+ var getSkillPaths = () => {
42413
+ const paths = [];
42414
+ const seenPaths = /* @__PURE__ */ new Set();
42415
+ const pushPath = (candidate) => {
42416
+ if (!(0, import_fs13.existsSync)(candidate) || !(0, import_fs13.statSync)(candidate).isDirectory()) return;
42417
+ const normalized = import_path18.default.resolve(candidate);
42418
+ if (seenPaths.has(normalized)) return;
42419
+ seenPaths.add(normalized);
42420
+ paths.push(normalized);
42670
42421
  };
42671
- createWorkspace(workspace);
42672
- if (!department.departmentMembers) {
42673
- department.departmentMembers = [];
42422
+ const projectSkillsPath = getProjectSkillsPath();
42423
+ if (projectSkillsPath) {
42424
+ pushPath(projectSkillsPath);
42674
42425
  }
42675
- if (department.departmentMembers.some((member) => member.id === departmentMemberDefinition.id || member.name === departmentMemberDefinition.name)) {
42676
- throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728\u540C\u540D\u6216\u540C id \u6210\u5458: ${departmentMemberDefinition.name}/${departmentMemberDefinition.id}`);
42426
+ pushPath((0, import_path18.join)((0, import_os3.homedir)(), ".duclaw", "skills"));
42427
+ pushPath((0, import_path18.join)((0, import_os3.homedir)(), ".agents", "skills"));
42428
+ return paths;
42429
+ };
42430
+ var getDirectories = (dirPath) => {
42431
+ return (0, import_fs13.readdirSync)(dirPath).filter((name) => (0, import_fs13.statSync)((0, import_path18.join)(dirPath, name)).isDirectory());
42432
+ };
42433
+ var parseSkill = (mdPath, skillDir, options = {}) => {
42434
+ if (!(0, import_fs13.existsSync)(mdPath)) return null;
42435
+ const raw2 = (0, import_fs13.readFileSync)(mdPath, "utf-8");
42436
+ let name = "";
42437
+ let description = "";
42438
+ let body = raw2;
42439
+ const fmMatch = raw2.match(/^---\r?\n([\s\S]*?)\r?\n---/);
42440
+ if (fmMatch) {
42441
+ const frontmatter = fmMatch[1];
42442
+ body = raw2.slice(fmMatch[0].length).trim();
42443
+ for (const line of frontmatter.split("\n")) {
42444
+ const [key, ...rest] = line.split(":");
42445
+ const value = rest.join(":").trim();
42446
+ if (key.trim() === "name") name = value;
42447
+ if (key.trim() === "description") description = value;
42448
+ }
42677
42449
  }
42678
- if (departmentMemberDefinition.role === "department_head") {
42679
- const existingHead = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
42680
- if (existingHead) {
42681
- throw new Error(`[createDepartmentMember] \u90E8\u95E8 ${department.name} \u5DF2\u5B58\u5728 Department Head: ${existingHead.name}`);
42450
+ if (!name) {
42451
+ name = import_path18.default.basename(import_path18.default.dirname(mdPath));
42452
+ }
42453
+ if (!description && body) {
42454
+ description = body.split("\n")[0].replace(/^#+\s*/, "").trim();
42455
+ }
42456
+ const normalizedSkillDir = import_path18.default.resolve(skillDir);
42457
+ const projectSkillsPath = getProjectSkillsPath();
42458
+ const isProjectSkill = projectSkillsPath ? normalizedSkillDir === import_path18.default.resolve(projectSkillsPath, import_path18.default.basename(normalizedSkillDir)) && normalizedSkillDir.startsWith(import_path18.default.resolve(projectSkillsPath) + import_path18.default.sep) : false;
42459
+ const scope = options.scope ?? (isProjectSkill ? "project" : "global");
42460
+ const deletable = options.deletable ?? (scope === "project" && isProjectSkill);
42461
+ return {
42462
+ name,
42463
+ description,
42464
+ baseDir: skillDir,
42465
+ scope,
42466
+ departmentName: options.departmentName,
42467
+ deletable,
42468
+ getDetail: () => {
42469
+ let content = body.replace(/<Skill目录>/g, skillDir).replace(/\$\{CLAUDE_SKILL_DIR\}/g, skillDir);
42470
+ return `Base directory for this skill: ${skillDir}
42471
+
42472
+ ${content}`;
42473
+ }
42474
+ };
42475
+ };
42476
+ var loadDepartmentSkills = (seen) => {
42477
+ const skills = [];
42478
+ for (const department of listDepartments()) {
42479
+ const activeSkills = listDepartmentSkills(department.name).filter((skill) => skill.status === "active");
42480
+ for (const departmentSkill of activeSkills) {
42481
+ if (seen.has(departmentSkill.skillName)) continue;
42482
+ const skillDir = (0, import_path18.join)(getDepartmentWorkSpaceDir(department.name), "skills", departmentSkill.skillName);
42483
+ const skill = parseSkill((0, import_path18.join)(skillDir, "SKILL.md"), skillDir, {
42484
+ scope: "department",
42485
+ departmentName: department.name,
42486
+ deletable: false
42487
+ });
42488
+ if (skill) {
42489
+ seen.add(skill.name);
42490
+ skills.push(skill);
42491
+ }
42682
42492
  }
42683
- department.headMemberId = departmentMemberDefinition.id;
42684
42493
  }
42685
- department.departmentMembers.push(departmentMemberDefinition);
42686
- (0, import_fs13.writeFileSync)(getDepartmentJsonPath(department.name), JSON.stringify(department, null, " "), "utf-8");
42687
- return departmentMemberDefinition;
42494
+ return skills;
42688
42495
  };
42689
- var listDepartmentMembers = (departmentName) => {
42690
- const departmentJsonPath = getDepartmentJsonPath(departmentName);
42691
- if (!(0, import_fs13.existsSync)(departmentJsonPath)) return [];
42692
- const text2 = (0, import_fs13.readFileSync)(departmentJsonPath, "utf-8");
42693
- const department = JSON.parse(text2);
42694
- if (!department) throw new Error(`[listDepartmentMembers] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
42695
- return department.departmentMembers ?? [];
42696
- };
42697
- var getDepartmentMember = (departmentName, departmentMemberId) => {
42698
- const members = listDepartmentMembers(departmentName);
42699
- return members.find((member) => member.id === departmentMemberId) || null;
42496
+ var loadSkill = () => {
42497
+ const skillPaths = getSkillPaths();
42498
+ const seen = /* @__PURE__ */ new Set();
42499
+ const skills = [];
42500
+ for (const skillPath of skillPaths) {
42501
+ const dirs = getDirectories(skillPath);
42502
+ for (const skillName of dirs) {
42503
+ const eachSkillPath = (0, import_path18.join)(skillPath, skillName);
42504
+ const eachSkillMdPath = (0, import_path18.join)(eachSkillPath, "SKILL.md");
42505
+ const skill = parseSkill(eachSkillMdPath, eachSkillPath);
42506
+ if (skill && !seen.has(skill.name)) {
42507
+ seen.add(skill.name);
42508
+ skills.push(skill);
42509
+ }
42510
+ }
42511
+ }
42512
+ skills.push(...loadDepartmentSkills(seen));
42513
+ return skills;
42700
42514
  };
42701
- var getDepartmentMemberByName = (departmentName, departmentMemberName) => {
42702
- const members = listDepartmentMembers(departmentName);
42703
- return members.find((member) => member.name === departmentMemberName) || null;
42515
+ var deleteSkill = (name) => {
42516
+ const skills = loadSkill();
42517
+ const skill = skills.find((s) => s.name === name);
42518
+ if (!skill || !skill.deletable) return false;
42519
+ (0, import_fs13.rmSync)(skill.baseDir, { recursive: true, force: false });
42520
+ return true;
42704
42521
  };
42705
- var getDepartmentMemberByMailboxId = (mailboxId) => {
42706
- const [departmentName, memberName] = mailboxId.split("::");
42707
- if (!departmentName || !memberName) return null;
42708
- return getDepartmentMemberByName(departmentName, memberName);
42522
+ var SkillRegistry_default = loadSkill;
42523
+
42524
+ // src/skill/index.ts
42525
+ var getSkillMeta = () => {
42526
+ const skills = SkillRegistry_default();
42527
+ if (skills.length === 0) return "";
42528
+ const skillXmls = skills.map((skill) => {
42529
+ return [
42530
+ " <skill>",
42531
+ ` <name>${skill.name}</name>`,
42532
+ ` <description>`,
42533
+ ` ${skill.description}`,
42534
+ ` </description>`,
42535
+ ` <location>user</location>`,
42536
+ " </skill>"
42537
+ ].join("\n");
42538
+ });
42539
+ return [
42540
+ "<available_skills>",
42541
+ ...skillXmls,
42542
+ "</available_skills>"
42543
+ ].join("\n");
42709
42544
  };
42710
- var getDepartmentMemberById = (memberId) => {
42711
- for (const department of listDepartments()) {
42712
- const targetMember = department.departmentMembers.find((member) => member.id === memberId);
42713
- if (targetMember) return targetMember;
42545
+ var getSkillDetail = (skillName) => {
42546
+ const skills = SkillRegistry_default();
42547
+ for (let skill of skills) {
42548
+ if (skill.name === skillName) {
42549
+ return skill.getDetail();
42550
+ }
42714
42551
  }
42715
- return null;
42552
+ return void 0;
42716
42553
  };
42717
- var deleteDepartmentMemberById = (departmentName, memberId) => {
42718
- const department = getDepartment(departmentName);
42719
- if (!department) throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94\u7684 department: ${departmentName}`);
42720
- const memberIdx = department.departmentMembers.findIndex((member) => member.id === memberId);
42721
- if (memberIdx === -1) {
42722
- throw new Error(`[deleteDepartmentMemberById] \u627E\u4E0D\u5230\u5BF9\u5E94 id \u7684 department member: ${memberId}`);
42554
+
42555
+ // src/tools/tools/Skill.ts
42556
+ var DESCRIPTION15 = `
42557
+ Execute a skill within the main conversation
42558
+
42559
+ <skills_instructions>
42560
+ When users ask you to perform tasks, check if any of the available skills
42561
+ below can help complete the task more effectively. Skills provide specialized
42562
+ capabilities and domain knowledge.
42563
+
42564
+ How to use skills:
42565
+ - Invoke skills using this tool with the skill name only (no arguments)
42566
+ - When you invoke a skill, you will see <command-message>The "{name}" skill is loading</command-message>
42567
+ - The skill's prompt will expand and provide detailed instructions on how to complete the task
42568
+ - Examples:
42569
+ - \`command: "pdf"\` - invoke the pdf skill
42570
+ - \`command: "xlsx"\` - invoke the xlsx skill
42571
+ - \`command: "ms-office-suite:pdf"\` - invoke using fully qualified name
42572
+
42573
+ Important:
42574
+ - Only use skills listed in <available_skills> below
42575
+ - Do not invoke a skill that is already running
42576
+ - Do not use this tool for built-in CLI commands (like /help, /clear, etc.)
42577
+ </skills_instructions>
42578
+
42579
+ ${getSkillMeta()}
42580
+ `;
42581
+ var skillTool = {
42582
+ name: `skill`,
42583
+ description: DESCRIPTION15,
42584
+ input_schema: {
42585
+ type: `object`,
42586
+ properties: {
42587
+ command: {
42588
+ type: `string`,
42589
+ description: `The skill name (no arguments). E.g., "pdf" or "xlsx"`
42590
+ }
42591
+ },
42592
+ required: [`command`]
42593
+ },
42594
+ async execute(input, userRequest) {
42595
+ console.log(`[\u6267\u884C\u5DE5\u5177]${input.command} skill`);
42596
+ const command = input.command;
42597
+ const detail = getSkillDetail(command);
42598
+ if (!detail) return `<command-message> skill name: \u201C${command}\u201D \u4E0D\u5B58\u5728</command-message>
42599
+ <command-name>${command}</command-name>`;
42600
+ const skillXml = `
42601
+ <command-message>The "${command}" skill is running</command-message>
42602
+
42603
+ <command-name>${command}</command-name>
42604
+
42605
+ <command-detail>${detail}</command-detail>
42606
+ `;
42607
+ return skillXml;
42723
42608
  }
42724
- const workspaceId = department.departmentMembers[memberIdx].workspaceId;
42725
- if (department.headMemberId === memberId) {
42726
- delete department.headMemberId;
42609
+ };
42610
+
42611
+ // src/tools/tools/tasks/GoalCreate.ts
42612
+ var goalCreate = {
42613
+ name: `goal_create`,
42614
+ description: `\u5728\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u521B\u5EFA\u4E00\u4E2A\u4EFB\u52A1\u76EE\u6807\uFF0C\u5F53\u8981\u8C03\u7528 task_ \u7CFB\u5217\u5DE5\u5177\u65F6\uFF0C\u5148\u8C03\u7528\u6B64\u5DE5\u5177\u521B\u5EFA\u4EFB\u52A1\u76EE\u6807`,
42615
+ input_schema: {
42616
+ type: `object`,
42617
+ properties: {
42618
+ subject: {
42619
+ type: `string`,
42620
+ description: `\u4EFB\u52A1\u76EE\u6807\u7684\u6838\u5FC3\u6807\u9898\uFF0C\u7B80\u6D01\u660E\u4E86`
42621
+ },
42622
+ description: {
42623
+ type: `string`,
42624
+ description: `\u4EFB\u52A1\u76EE\u6807\u7684\u8BE6\u7EC6\u8BF4\u660E`
42625
+ }
42626
+ },
42627
+ required: [`subject`]
42628
+ },
42629
+ async execute(input, userRequest) {
42630
+ const subject = input.subject;
42631
+ const description = input.description;
42632
+ const taskGoal = await createGoal(subject, description);
42633
+ if (userRequest) {
42634
+ ensureGoalConversationContext(taskGoal.id, {
42635
+ threadId: userRequest.userId,
42636
+ originPlatform: userRequest.platform,
42637
+ originUserId: userRequest.userId,
42638
+ syncReplyToOrigin: userRequest.platform === "feishu"
42639
+ });
42640
+ }
42641
+ if (taskGoal) {
42642
+ return `[GoalCreate] \u521B\u5EFA\u4EFB\u52A1\u76EE\u6807\u6210\u529F: ${JSON.stringify(taskGoal)}`;
42643
+ }
42644
+ return `[GoalCreate] \u521B\u5EFA\u4EFB\u52A1\u76EE\u6807\u5931\u8D25: ${taskGoal}`;
42727
42645
  }
42728
- department.departmentMembers = department.departmentMembers.filter((_, index) => index !== memberIdx);
42729
- (0, import_fs13.writeFileSync)(getDepartmentJsonPath(departmentName), JSON.stringify(department, null, " "), "utf-8");
42730
- deleteWorkspace(workspaceId);
42731
42646
  };
42732
42647
 
42733
- // src/department/mailbox/ceoFollowup.ts
42734
- var import_node_crypto6 = require("node:crypto");
42735
- var rowToFollowup = (row) => ({
42736
- id: row.id,
42737
- sourceMessageId: row.sourceMessageId,
42738
- status: row.status,
42739
- originUserId: row.originUserId,
42740
- originPlatform: row.originPlatform,
42741
- fromMailboxId: row.fromMailboxId,
42742
- threadId: row.threadId ?? void 0,
42743
- parentMessageId: row.parentMessageId ?? void 0,
42744
- workItemId: row.workItemId ?? void 0,
42745
- content: row.content,
42746
- attempts: row.attempts,
42747
- lastError: row.lastError ?? void 0,
42748
- createdAt: row.createdAt,
42749
- updatedAt: row.updatedAt,
42750
- completedAt: row.completedAt ?? void 0
42751
- });
42752
- var selectById = (id) => {
42753
- const db3 = createSqliteDB();
42754
- const row = db3.prepare(`
42755
- SELECT
42756
- id,
42757
- source_message_id as sourceMessageId,
42758
- status,
42759
- origin_user_id as originUserId,
42760
- origin_platform as originPlatform,
42761
- from_mailbox_id as fromMailboxId,
42762
- thread_id as threadId,
42763
- parent_message_id as parentMessageId,
42764
- work_item_id as workItemId,
42765
- content,
42766
- attempts,
42767
- last_error as lastError,
42768
- created_at as createdAt,
42769
- updated_at as updatedAt,
42770
- completed_at as completedAt
42771
- FROM ceo_followups
42772
- WHERE id = ?
42773
- `).get(id);
42774
- return row ? rowToFollowup(row) : null;
42648
+ // src/tools/tools/tasks/GoalGet.ts
42649
+ var DESCRIPTION16 = `
42650
+ \u83B7\u53D6\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal
42651
+ `;
42652
+ var goalGet = {
42653
+ name: `goal_get`,
42654
+ description: DESCRIPTION16,
42655
+ input_schema: {
42656
+ type: `object`,
42657
+ properties: {
42658
+ id: {
42659
+ type: `string`,
42660
+ description: `\u6839\u636Eid\u83B7\u53D6\u6307\u5B9A\u76EE\u6807,\u53EF\u9009`
42661
+ },
42662
+ subject: {
42663
+ type: `string`,
42664
+ description: `\u6839\u636Esubject\u83B7\u53D6\u7279\u5B9A\u76EE\u6807,\u53EF\u9009`
42665
+ }
42666
+ }
42667
+ },
42668
+ async execute(input, userRequest) {
42669
+ const id = input.id;
42670
+ const subject = input.subject;
42671
+ let goal = void 0;
42672
+ if (id) {
42673
+ goal = getGoalById(id);
42674
+ } else if (subject) {
42675
+ goal = getGoalBySubject(subject);
42676
+ }
42677
+ if (!goal) {
42678
+ return `[GetGoal] \u627E\u4E0D\u5230\u6307\u5B9A\u7684\u76EE\u6807`;
42679
+ }
42680
+ return `[GetGoal] \u627E\u5230\u76EE\u6807: ${JSON.stringify(goal)}`;
42681
+ }
42775
42682
  };
42776
- var enqueueCeoFollowupFromMailbox = (message) => {
42777
- if (message.toMailboxId !== "manager") return null;
42778
- if (!message.originUserId || !message.originPlatform) return null;
42779
- const db3 = createSqliteDB();
42780
- const now = Date.now();
42781
- const id = `cfu_${(0, import_node_crypto6.randomUUID)().slice(0, 12)}`;
42782
- db3.prepare(`
42783
- INSERT INTO ceo_followups (
42784
- id,
42785
- source_message_id,
42786
- status,
42787
- origin_user_id,
42788
- origin_platform,
42789
- from_mailbox_id,
42790
- thread_id,
42791
- parent_message_id,
42792
- work_item_id,
42793
- content,
42794
- created_at,
42795
- updated_at
42796
- ) VALUES (?, ?, 'pending', ?, ?, ?, ?, ?, ?, ?, ?, ?)
42797
- ON CONFLICT(source_message_id) DO UPDATE SET
42683
+
42684
+ // src/tools/tools/tasks/GoalList.ts
42685
+ var DESCRIPTION17 = `
42686
+ \u5728\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u5217\u51FA\u6240\u6709\u76EE\u6807
42687
+ `;
42688
+ var goalList = {
42689
+ name: `goal_list`,
42690
+ description: DESCRIPTION17,
42691
+ input_schema: {
42692
+ type: `object`,
42693
+ properties: {}
42694
+ },
42695
+ async execute(input, userRequest) {
42696
+ const goals = listGoal();
42697
+ if (goals.length > 0) {
42698
+ return `[GoalList]goals\u5217\u8868: ${JSON.stringify(goals)}`;
42699
+ }
42700
+ return `[GoalList]\u6CA1\u6709goals\u6570\u636E`;
42701
+ }
42702
+ };
42703
+
42704
+ // src/tools/tools/tasks/GoalUpdate.ts
42705
+ var DESCRIPTION18 = `
42706
+ \u66F4\u65B0\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal
42707
+
42708
+ \u5DE5\u5177\u4F7F\u7528\u89C4\u5219:
42709
+ - \u5F53\u76EE\u6807\u7684\u72B6\u6001status\u9700\u8981\u66F4\u65B0\u65F6;
42710
+ - \u5F53\u76EE\u6807\u7684subject\u6216description\u9700\u8981\u4FEE\u6539\u65F6;
42711
+ `;
42712
+ var goalUpdate = {
42713
+ name: `goal_update`,
42714
+ description: DESCRIPTION18,
42715
+ input_schema: {
42716
+ type: `object`,
42717
+ properties: {
42718
+ id: {
42719
+ type: `string`,
42720
+ description: `\u6839\u636Eid\u627E\u5230\u5BF9\u5E94goal\u8FDB\u884C\u66F4\u65B0`
42721
+ },
42722
+ subject: {
42723
+ type: `string`,
42724
+ description: `\u66F4\u65B0\u540Egoal\u7684\u6838\u5FC3\u6807\u9898\uFF0C\u7B80\u6D01\u660E\u4E86`
42725
+ },
42726
+ description: {
42727
+ type: `string`,
42728
+ description: `\u66F4\u65B0\u540E\u7684goal\u7684\u8BE6\u7EC6\u8BF4\u660E`
42729
+ },
42730
+ status: {
42731
+ type: `string`,
42732
+ description: `pending | in_progress | completed | failed`,
42733
+ enum: [`pending`, `in_progress`, `completed`, `failed`]
42734
+ }
42735
+ },
42736
+ required: [`id`]
42737
+ },
42738
+ async execute(input, userRequest) {
42739
+ const id = input.id;
42740
+ const subject = input.subject;
42741
+ const description = input.description;
42742
+ const status = input.status;
42743
+ const validStatuses = ["pending", "in_progress", "completed", "failed"];
42744
+ if (status && !validStatuses.includes(status)) {
42745
+ return `[UpdateGoal] status\u53C2\u6570\u9519\u8BEF,status\u5FC5\u987B\u8981\u662F pending | in_progress | completed | failed \u4E2D\u7684\u4EFB\u610F\u4E00\u4E2A`;
42746
+ }
42747
+ const goal = getGoalById(id);
42748
+ if (!goal) {
42749
+ return `[UpdateGoal] \u627E\u4E0D\u5230\u6307\u5B9Aid\u7684\u76EE\u6807`;
42750
+ }
42751
+ const updatedGoal = {
42752
+ ...goal,
42753
+ subject: subject || goal.subject,
42754
+ description: description !== void 0 ? description : goal.description,
42755
+ status: status || goal.status,
42756
+ updateAt: getDate()
42757
+ };
42758
+ try {
42759
+ updateGoal(id, updatedGoal);
42760
+ } catch (err) {
42761
+ if (err instanceof GoalCompletionBlockedError) {
42762
+ return err.message;
42763
+ }
42764
+ throw err;
42765
+ }
42766
+ return `[UpdateGoal] \u66F4\u65B0\u76EE\u6807\u6210\u529F: ${JSON.stringify(updatedGoal)}`;
42767
+ }
42768
+ };
42769
+
42770
+ // src/tools/tools/tasks/GoalDelete.ts
42771
+ var DESCRIPTION19 = `
42772
+ \u5220\u9664\u4EFB\u52A1\u7CFB\u7EDF\u4E2D\u6307\u5B9A\u7684Goal\u53CA\u5176\u5BF9\u5E94\u7684JSON\u6587\u4EF6
42773
+
42774
+ \u5DE5\u5177\u4F7F\u7528\u89C4\u5219:
42775
+ - \u5F53\u76EE\u6807\u5DF2\u7ECF\u5B8C\u6210(completed)\u6216\u5931\u8D25(failed)\u540E,\u9700\u8981\u6E05\u7406\u65F6\u8C03\u7528;
42776
+ - \u5220\u9664\u540E\u4E0D\u53EF\u6062\u590D,\u8BF7\u8C28\u614E\u4F7F\u7528;
42777
+ `;
42778
+ var goalDelete = {
42779
+ name: `goal_delete`,
42780
+ description: DESCRIPTION19,
42781
+ input_schema: {
42782
+ type: `object`,
42783
+ properties: {
42784
+ id: {
42785
+ type: `string`,
42786
+ description: `\u8981\u5220\u9664\u7684\u76EE\u6807ID`
42787
+ }
42788
+ },
42789
+ required: [`id`]
42790
+ },
42791
+ async execute(input, userRequest) {
42792
+ const id = input.id;
42793
+ const goal = getGoalById(id);
42794
+ if (!goal) {
42795
+ return `[GoalDelete] \u627E\u4E0D\u5230\u6307\u5B9Aid\u7684\u76EE\u6807`;
42796
+ }
42797
+ const deleted = deleteGoal(id);
42798
+ if (deleted) {
42799
+ return `[GoalDelete] \u76EE\u6807\u5DF2\u5220\u9664: id=${id}, subject=${goal.subject}`;
42800
+ }
42801
+ return `[GoalDelete] \u5220\u9664\u76EE\u6807\u5931\u8D25: id=${id}`;
42802
+ }
42803
+ };
42804
+
42805
+ // src/tools/tools/department/DepartmentCreate.ts
42806
+ var import_node_crypto9 = require("node:crypto");
42807
+
42808
+ // src/department/mailbox/mailbox.ts
42809
+ var import_node_crypto8 = require("node:crypto");
42810
+
42811
+ // src/agent/interruptRegistry.ts
42812
+ var registry = /* @__PURE__ */ new Map();
42813
+ var markRunning = (userId) => {
42814
+ const current = registry.get(userId);
42815
+ if (current) {
42816
+ current.startedAt = Date.now();
42817
+ return;
42818
+ }
42819
+ registry.set(userId, { messages: [], startedAt: Date.now(), listeners: /* @__PURE__ */ new Set() });
42820
+ };
42821
+ var markDone = (userId) => {
42822
+ registry.delete(userId);
42823
+ };
42824
+ var hasRunningAgent = (userId) => {
42825
+ return registry.has(userId);
42826
+ };
42827
+ var queueInterrupt = (userId, message) => {
42828
+ const entry = registry.get(userId);
42829
+ if (!entry) return false;
42830
+ const interruptMessage = typeof message === "string" ? { content: message } : message;
42831
+ entry.messages.push(interruptMessage);
42832
+ console.log(`[interrupt] \u7528\u6237 ${userId} \u65B0\u6D88\u606F\u5DF2\u5165\u961F\uFF0C\u5F53\u524D\u961F\u5217\u957F\u5EA6: ${entry.messages.length}`);
42833
+ for (const listener of entry.listeners) {
42834
+ try {
42835
+ listener();
42836
+ } catch (err) {
42837
+ console.warn(`[interrupt] \u7528\u6237 ${userId} \u4E2D\u65AD\u76D1\u542C\u5668\u6267\u884C\u5931\u8D25: ${err.message}`);
42838
+ }
42839
+ }
42840
+ return true;
42841
+ };
42842
+ var registerInterruptListener = (userId, listener) => {
42843
+ const entry = registry.get(userId);
42844
+ if (!entry) return () => void 0;
42845
+ entry.listeners.add(listener);
42846
+ return () => {
42847
+ entry.listeners.delete(listener);
42848
+ };
42849
+ };
42850
+ var drainInterrupts = (userId) => {
42851
+ const entry = registry.get(userId);
42852
+ if (!entry || entry.messages.length === 0) return [];
42853
+ const messages = [...entry.messages];
42854
+ entry.messages.length = 0;
42855
+ console.log(`[interrupt] \u7528\u6237 ${userId} drain ${messages.length} \u6761\u4E2D\u65AD\u6D88\u606F`);
42856
+ return messages;
42857
+ };
42858
+
42859
+ // src/agent/events.ts
42860
+ var import_node_crypto5 = require("node:crypto");
42861
+ var rowToEvent = (row) => ({
42862
+ id: row.id,
42863
+ userId: row.userId,
42864
+ type: row.type,
42865
+ source: row.source,
42866
+ sourceId: row.sourceId,
42867
+ status: row.status,
42868
+ payload: JSON.parse(row.payloadJson || "{}"),
42869
+ createdAt: row.createdAt,
42870
+ injectedAt: row.injectedAt ?? void 0,
42871
+ handledAt: row.handledAt ?? void 0,
42872
+ updatedAt: row.updatedAt
42873
+ });
42874
+ var recordAgentEvent = (input) => {
42875
+ const db3 = createSqliteDB();
42876
+ const now = Date.now();
42877
+ const id = `evt_${(0, import_node_crypto5.randomUUID)().slice(0, 12)}`;
42878
+ const payloadJson = JSON.stringify(input.payload);
42879
+ db3.prepare(`
42880
+ INSERT INTO agent_events (
42881
+ id, user_id, type, source, source_id, status, payload_json, created_at, updated_at
42882
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
42883
+ ON CONFLICT(type, source, source_id) DO UPDATE SET
42884
+ user_id = excluded.user_id,
42798
42885
  status = CASE
42799
- WHEN ceo_followups.status = 'completed' THEN ceo_followups.status
42800
- ELSE 'pending'
42886
+ WHEN agent_events.status IN ('handled', 'ignored') THEN agent_events.status
42887
+ ELSE excluded.status
42801
42888
  END,
42802
- origin_user_id = excluded.origin_user_id,
42803
- origin_platform = excluded.origin_platform,
42804
- from_mailbox_id = excluded.from_mailbox_id,
42805
- thread_id = excluded.thread_id,
42806
- parent_message_id = excluded.parent_message_id,
42807
- work_item_id = excluded.work_item_id,
42808
- content = excluded.content,
42889
+ payload_json = excluded.payload_json,
42809
42890
  updated_at = excluded.updated_at
42810
42891
  `).run(
42811
42892
  id,
42812
- message.id,
42813
- message.originUserId,
42814
- message.originPlatform,
42815
- message.fromMailboxId,
42816
- message.threadId ?? message.id,
42817
- message.parentMessageId ?? null,
42818
- message.workItemId ?? null,
42819
- message.content,
42893
+ input.userId,
42894
+ input.type,
42895
+ input.source,
42896
+ input.sourceId,
42897
+ input.status ?? "pending",
42898
+ payloadJson,
42820
42899
  now,
42821
42900
  now
42822
42901
  );
42823
42902
  const row = db3.prepare(`
42824
- SELECT id FROM ceo_followups WHERE source_message_id = ?
42825
- `).get(message.id);
42826
- return row ? selectById(row.id) : null;
42903
+ SELECT
42904
+ id,
42905
+ user_id as userId,
42906
+ type,
42907
+ source,
42908
+ source_id as sourceId,
42909
+ status,
42910
+ payload_json as payloadJson,
42911
+ created_at as createdAt,
42912
+ injected_at as injectedAt,
42913
+ handled_at as handledAt,
42914
+ updated_at as updatedAt
42915
+ FROM agent_events
42916
+ WHERE type = ? AND source = ? AND source_id = ?
42917
+ `).get(input.type, input.source, input.sourceId);
42918
+ return rowToEvent(row);
42827
42919
  };
42828
- var listPendingCeoFollowups = (limit = 10) => {
42920
+ var listPendingAgentEvents = (userId, limit = 10) => {
42829
42921
  const db3 = createSqliteDB();
42830
42922
  const rows = db3.prepare(`
42831
42923
  SELECT
42832
42924
  id,
42833
- source_message_id as sourceMessageId,
42925
+ user_id as userId,
42926
+ type,
42927
+ source,
42928
+ source_id as sourceId,
42834
42929
  status,
42835
- origin_user_id as originUserId,
42836
- origin_platform as originPlatform,
42837
- from_mailbox_id as fromMailboxId,
42838
- thread_id as threadId,
42839
- parent_message_id as parentMessageId,
42840
- work_item_id as workItemId,
42841
- content,
42842
- attempts,
42843
- last_error as lastError,
42930
+ payload_json as payloadJson,
42844
42931
  created_at as createdAt,
42845
- updated_at as updatedAt,
42846
- completed_at as completedAt
42847
- FROM ceo_followups
42848
- WHERE status IN ('pending', 'failed')
42932
+ injected_at as injectedAt,
42933
+ handled_at as handledAt,
42934
+ updated_at as updatedAt
42935
+ FROM agent_events
42936
+ WHERE user_id = ?
42937
+ AND status IN ('pending', 'processing')
42849
42938
  ORDER BY created_at ASC
42850
42939
  LIMIT ?
42851
- `).all(limit);
42852
- return rows.map(rowToFollowup);
42940
+ `).all(userId, limit);
42941
+ return rows.map(rowToEvent);
42853
42942
  };
42854
- var claimCeoFollowup = (id) => {
42943
+ var markAgentEventsInjected = (eventIds) => {
42944
+ if (eventIds.length === 0) return;
42855
42945
  const db3 = createSqliteDB();
42856
42946
  const now = Date.now();
42857
- const result = db3.prepare(`
42858
- UPDATE ceo_followups
42859
- SET status = 'processing',
42860
- attempts = attempts + 1,
42947
+ const stmt = db3.prepare(`
42948
+ UPDATE agent_events
42949
+ SET status = CASE WHEN status = 'pending' THEN 'processing' ELSE status END,
42950
+ injected_at = COALESCE(injected_at, ?),
42861
42951
  updated_at = ?
42862
42952
  WHERE id = ?
42863
- AND status IN ('pending', 'failed')
42864
- `).run(now, id);
42865
- if (result.changes === 0) return null;
42866
- return selectById(id);
42867
- };
42868
- var completeCeoFollowup = (id) => {
42869
- const db3 = createSqliteDB();
42870
- const now = Date.now();
42871
- db3.prepare(`
42872
- UPDATE ceo_followups
42873
- SET status = 'completed',
42874
- completed_at = COALESCE(completed_at, ?),
42875
- updated_at = ?,
42876
- last_error = NULL
42877
- WHERE id = ?
42878
- AND status IN ('pending', 'processing', 'failed')
42879
- `).run(now, now, id);
42880
- db3.prepare(`
42881
- UPDATE mailbox
42882
- SET status = 'done',
42883
- updated_at = ?
42884
- WHERE id = (SELECT source_message_id FROM ceo_followups WHERE id = ?)
42885
- AND status IN ('pending', 'processing', 'read')
42886
- `).run(now, id);
42953
+ AND status IN ('pending', 'processing')
42954
+ `);
42955
+ const tx = db3.transaction((ids) => {
42956
+ for (const id of ids) stmt.run(now, now, id);
42957
+ });
42958
+ tx(eventIds);
42887
42959
  };
42888
- var failCeoFollowup = (id, error) => {
42960
+ var markAgentEventsHandled = (eventIds, status = "handled") => {
42961
+ if (eventIds.length === 0) return;
42889
42962
  const db3 = createSqliteDB();
42890
42963
  const now = Date.now();
42891
- db3.prepare(`
42892
- UPDATE ceo_followups
42893
- SET status = 'failed',
42894
- last_error = ?,
42964
+ const stmt = db3.prepare(`
42965
+ UPDATE agent_events
42966
+ SET status = ?,
42967
+ handled_at = COALESCE(handled_at, ?),
42895
42968
  updated_at = ?
42896
42969
  WHERE id = ?
42897
- AND status IN ('pending', 'processing', 'failed')
42898
- `).run(error.slice(0, 1e3), now, id);
42899
- };
42900
- var recoverStaleProcessingCeoFollowups = (staleBefore) => {
42901
- const db3 = createSqliteDB();
42902
- const now = Date.now();
42903
- const result = db3.prepare(`
42904
- UPDATE ceo_followups
42905
- SET status = 'failed',
42906
- last_error = 'stale_processing_recovered',
42907
- updated_at = ?
42908
- WHERE status = 'processing'
42909
- AND updated_at < ?
42910
- `).run(now, staleBefore);
42911
- return result.changes;
42912
- };
42913
- var completePendingCeoFollowupsForUser = (originUserId) => {
42914
- const db3 = createSqliteDB();
42915
- const now = Date.now();
42916
- const rows = db3.prepare(`
42917
- SELECT id, source_message_id as sourceMessageId
42918
- FROM ceo_followups
42919
- WHERE origin_user_id = ?
42920
- AND status IN ('pending', 'processing', 'failed')
42921
- `).all(originUserId);
42922
- const tx = db3.transaction((items) => {
42923
- const completeStmt = db3.prepare(`
42924
- UPDATE ceo_followups
42925
- SET status = 'completed',
42926
- completed_at = COALESCE(completed_at, ?),
42927
- updated_at = ?,
42928
- last_error = NULL
42929
- WHERE id = ?
42930
- AND status IN ('pending', 'processing', 'failed')
42931
- `);
42932
- const doneStmt = db3.prepare(`
42933
- UPDATE mailbox
42934
- SET status = 'done',
42935
- updated_at = ?
42936
- WHERE id = ?
42937
- AND status IN ('pending', 'processing', 'read')
42938
- `);
42939
- for (const item of items) {
42940
- completeStmt.run(now, now, item.id);
42941
- doneStmt.run(now, item.sourceMessageId);
42942
- }
42970
+ AND status IN ('pending', 'processing')
42971
+ `);
42972
+ const tx = db3.transaction((ids) => {
42973
+ for (const id of ids) stmt.run(status, now, now, id);
42943
42974
  });
42944
- tx(rows);
42945
- return rows.length;
42975
+ tx(eventIds);
42946
42976
  };
42977
+ var renderAgentEventReminder = (events) => {
42978
+ if (events.length === 0) return "";
42979
+ const lines = events.map((event) => {
42980
+ const owner = typeof event.payload.ownerMailboxId === "string" ? event.payload.ownerMailboxId : void 0;
42981
+ const mailboxMessageId = typeof event.payload.mailboxMessageId === "string" ? event.payload.mailboxMessageId : event.sourceId;
42982
+ const summary = typeof event.payload.summary === "string" ? event.payload.summary : typeof event.payload.contentPreview === "string" ? event.payload.contentPreview : "";
42983
+ return [
42984
+ `- eventId=${event.id}`,
42985
+ `type=${event.type}`,
42986
+ owner ? `owner=${owner}` : "",
42987
+ mailboxMessageId ? `mailboxMessageId=${mailboxMessageId}` : "",
42988
+ summary ? `summary=${summary}` : ""
42989
+ ].filter(Boolean).join(" ");
42990
+ }).join("\n");
42991
+ const hasCeoFollowup = events.some((event) => event.type === "ceo.followup_required");
42992
+ const ceoFollowupInstruction = hasCeoFollowup ? [
42993
+ ``,
42994
+ `\u5176\u4E2D type=ceo.followup_required \u7684\u4E8B\u4EF6\uFF0C\u662F\u56E2\u961F\u5DF2\u7ECF\u628A\u7ED3\u679C\u56DE\u5230\u4E86\u4F60\u8FD9\u91CC\u2014\u2014\u8001\u677F\u8FD8\u5728\u7B49\u4F60\u628A\u8FD9\u4E9B\u7ED3\u679C\u8F6C\u8FBE\u7ED9\u4ED6\u3002`,
42995
+ `\u8BFB\u4E00\u904D\uFF0C\u7528\u4F60\u81EA\u5DF1\u7684\u8BDD\u7ED9\u8001\u677F\u6C47\u62A5\u4E00\u6B21\uFF1A\u6709\u591A\u6761\u5C31\u5408\u5E76\u6210\u4E00\u6761\u8BF4\u6E05\u695A\uFF0C\u522B\u4E00\u6761\u4E8B\u4EF6\u56DE\u4E00\u53E5\u3001\u628A\u540C\u4E00\u4EF6\u4E8B\u91CD\u590D\u53D1\u7ED9\u8001\u677F\u3002`,
42996
+ `\u5982\u679C\u7ED3\u679C\u8BF4\u660E\u4E8B\u60C5\u8FD8\u6CA1\u505A\u5B8C\u6216\u5361\u4F4F\u4E86\uFF0C\u5C31\u5982\u5B9E\u8FD9\u4E48\u8BB2\uFF0C\u800C\u4E0D\u662F\u542B\u7CCA\u5730\u201C\u7A0D\u540E\u540C\u6B65\u201D\u3002`
42997
+ ].join("\n") : "";
42998
+ return `<system-reminder>
42999
+ \u672C\u8F6E\u6709 ${events.length} \u6761\u5185\u90E8\u4E8B\u4EF6\u53EF\u7528\uFF1A
43000
+ ${lines}
42947
43001
 
42948
- // src/department/mailbox/mailbox.ts
42949
- var getMailBoxId = (departmentName, memberName) => {
42950
- return `${departmentName}::${memberName}`;
43002
+ \u8FD9\u4E9B\u4E8B\u4EF6\u4E0D\u662F\u7528\u6237\u7684\u65B0\u8BF7\u6C42\uFF0C\u4E5F\u4E0D\u5E94\u8BE5\u4F5C\u4E3A\u7528\u6237\u786E\u8BA4\u3002\u82E5\u4E8B\u4EF6\u7C7B\u578B\u662F mailbox.message_received\uFF0C\u8868\u793A\u4F60\u7684 mailbox \u5728\u5F53\u524D\u5DE5\u4F5C\u671F\u95F4\u6536\u5230\u65B0\u5185\u90E8\u534F\u4F5C\u6D88\u606F\uFF1B\u8BF7\u5C3D\u5FEB\u8C03\u7528 list_mailbox \u67E5\u770B\u961F\u5217\uFF0C\u5E76\u5728\u9700\u8981\u65F6\u7528 get_mailbox(message_id) \u9886\u53D6\u5173\u8054\u6D88\u606F\u3002\u4E0D\u8981\u76F4\u63A5\u628A\u4E8B\u4EF6\u5F53\u4F5C\u5DF2\u9886\u53D6\u90AE\u4EF6\u3002\u8BF7\u53EA\u5728\u9700\u8981\u65F6\u81EA\u7136\u5730\u6C47\u603B\u7ED9\u7528\u6237\uFF1B\u5982\u9700\u7EE7\u7EED\u534F\u4F5C\uFF0C\u4F18\u5148\u4F7F\u7528\u4E8B\u4EF6\u4E2D\u5173\u8054\u7684 mailboxMessageId/thread \u7EE7\u7EED\u5904\u7406\u3002\u4E0D\u8981\u590D\u8FF0\u672C system-reminder\u3002
43003
+ ${ceoFollowupInstruction}
43004
+ </system-reminder>`;
42951
43005
  };
42952
- var getMailBoxIdFromDepartmentMemberId = (memberId) => {
42953
- const member = getDepartmentMemberById(memberId);
42954
- if (!member) return null;
42955
- const department = getDepartmentById(member.departmentId);
42956
- if (!department) throw new Error(`[getMailBoxIdFromDepartmentMemberId] \u6B64\u6210\u5458\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u90E8\u95E8: ${member.departmentId}`);
42957
- return getMailBoxId(department.name, member.name);
43006
+
43007
+ // src/department/mailbox/events.ts
43008
+ var import_node_crypto6 = require("node:crypto");
43009
+ var parseDetail = (detailJson) => {
43010
+ if (!detailJson) return void 0;
43011
+ try {
43012
+ const parsed = JSON.parse(detailJson);
43013
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
43014
+ return parsed;
43015
+ }
43016
+ } catch {
43017
+ return void 0;
43018
+ }
43019
+ return void 0;
42958
43020
  };
42959
- var cancelMailboxMessages = (mailboxId) => {
43021
+ var mapMailboxEventRow = (row) => {
43022
+ const hasMessage = typeof row.toMailboxId === "string" && typeof row.fromMailboxId === "string" && typeof row.content === "string" && typeof row.sendTime === "number" && typeof row.status === "string";
43023
+ return {
43024
+ id: row.id,
43025
+ messageId: row.messageId || void 0,
43026
+ mailboxId: row.mailboxId,
43027
+ actorMailboxId: row.actorMailboxId || void 0,
43028
+ counterpartMailboxId: row.counterpartMailboxId || void 0,
43029
+ eventType: row.eventType,
43030
+ detail: parseDetail(row.detailJson),
43031
+ createdAt: row.createdAt,
43032
+ message: hasMessage ? {
43033
+ id: row.messageId || "",
43034
+ toMailboxId: row.toMailboxId,
43035
+ fromMailboxId: row.fromMailboxId,
43036
+ content: row.content,
43037
+ sendTime: row.sendTime,
43038
+ status: row.status,
43039
+ originUserId: row.originUserId || void 0,
43040
+ originPlatform: row.originPlatform || void 0,
43041
+ threadId: row.threadId || void 0,
43042
+ parentMessageId: row.parentMessageId || void 0
43043
+ } : null
43044
+ };
43045
+ };
43046
+ var recordMailboxEvent = (input) => {
42960
43047
  const db3 = createSqliteDB();
43048
+ const event = {
43049
+ id: (0, import_node_crypto6.randomUUID)().slice(0, 12),
43050
+ messageId: input.messageId,
43051
+ mailboxId: input.mailboxId,
43052
+ actorMailboxId: input.actorMailboxId,
43053
+ counterpartMailboxId: input.counterpartMailboxId,
43054
+ eventType: input.eventType,
43055
+ detail: input.detail,
43056
+ createdAt: input.createdAt ?? Date.now()
43057
+ };
42961
43058
  const stmt = db3.prepare(
42962
- `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, status
42963
- FROM mailbox
42964
- WHERE (to_mailbox_id = ? or from_mailbox_id = ?) AND status in ('pending', 'processing')`
43059
+ `INSERT INTO mailbox_events (
43060
+ id,
43061
+ message_id,
43062
+ mailbox_id,
43063
+ actor_mailbox_id,
43064
+ counterpart_mailbox_id,
43065
+ event_type,
43066
+ detail_json,
43067
+ created_at
43068
+ ) VALUES (?,?,?,?,?,?,?,?)`
42965
43069
  );
42966
- const messages = stmt.all(mailboxId, mailboxId);
42967
- for (const message of messages) {
42968
- updateMailboxMessageStatus(message.id, "cancelled", {
42969
- counterpartMailboxId: message.fromMailboxId,
42970
- reason: "mailbox_cancelled"
42971
- });
42972
- }
42973
- return messages.length;
43070
+ stmt.run(
43071
+ event.id,
43072
+ event.messageId || null,
43073
+ event.mailboxId,
43074
+ event.actorMailboxId || null,
43075
+ event.counterpartMailboxId || null,
43076
+ event.eventType,
43077
+ event.detail ? JSON.stringify(event.detail) : null,
43078
+ event.createdAt
43079
+ );
43080
+ return event;
42974
43081
  };
42975
- var getWorkItemContextFromMessage = (messageId) => {
42976
- if (!messageId) return null;
42977
- const db3 = createSqliteDB();
42978
- const row = db3.prepare(
42979
- `SELECT
42980
- work_item_id as workItemId,
42981
- work_item_role as workItemRole,
42982
- upstream_message_id as upstreamMessageId
42983
- FROM mailbox
42984
- WHERE id = ?`
42985
- ).get(messageId);
42986
- if (!row?.workItemId) return null;
42987
- return row;
42988
- };
42989
- var getWorkItemContextFromThread = (threadId) => {
42990
- if (!threadId) return null;
42991
- const db3 = createSqliteDB();
42992
- const row = db3.prepare(
42993
- `SELECT
42994
- work_item_id as workItemId,
42995
- work_item_role as workItemRole,
42996
- upstream_message_id as upstreamMessageId
42997
- FROM mailbox
42998
- WHERE thread_id = ?
42999
- AND work_item_id IS NOT NULL
43000
- ORDER BY send_time ASC
43001
- LIMIT 1`
43002
- ).get(threadId);
43003
- if (!row?.workItemId) return null;
43004
- return row;
43005
- };
43006
- var mailboxLooksLikeRole = (mailboxId, role) => {
43007
- const lower = mailboxId.toLowerCase();
43008
- if (role === "department_head") {
43009
- return lower.includes("department-head") || mailboxId.includes("\u8D1F\u8D23\u4EBA");
43010
- }
43011
- return lower.includes("executor") || mailboxId.includes("\u6267\u884C\u8005") || mailboxId.includes("\u4E13\u5458");
43012
- };
43013
- var inferWorkItemRole = (fromMailboxId, toMailboxId, inherited) => {
43014
- if (fromMailboxId === "manager") return "upstream_request";
43015
- if (toMailboxId === "manager") return "upstream_report";
43016
- const fromMember = getDepartmentMemberByMailboxId(fromMailboxId);
43017
- const toMember = getDepartmentMemberByMailboxId(toMailboxId);
43018
- const fromIsHead = fromMember?.role === "department_head" || mailboxLooksLikeRole(fromMailboxId, "department_head");
43019
- const toIsHead = toMember?.role === "department_head" || mailboxLooksLikeRole(toMailboxId, "department_head");
43020
- const fromIsExecutor = fromMember?.role === "executor" || mailboxLooksLikeRole(fromMailboxId, "executor");
43021
- const toIsExecutor = toMember?.role === "executor" || mailboxLooksLikeRole(toMailboxId, "executor");
43022
- if (fromIsHead && toIsExecutor) {
43023
- return inherited?.workItemRole === "executor_result" ? "downstream_reply" : "delegation";
43024
- }
43025
- if (fromIsExecutor && toIsHead) return "executor_result";
43026
- return inherited?.workItemRole ? "followup" : "message";
43027
- };
43028
- var resolveWorkItemContext = (fromMailboxId, toMailboxId, id, options) => {
43029
- if (options?.workItemId) {
43030
- return {
43031
- workItemId: options.workItemId,
43032
- workItemRole: options.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId),
43033
- upstreamMessageId: options.upstreamMessageId ?? options.workItemId
43034
- };
43035
- }
43036
- const inherited = getWorkItemContextFromMessage(options?.parentMessageId) ?? getWorkItemContextFromThread(options?.threadId);
43037
- if (inherited?.workItemId) {
43038
- return {
43039
- workItemId: inherited.workItemId,
43040
- workItemRole: options?.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId, inherited),
43041
- upstreamMessageId: inherited.upstreamMessageId ?? inherited.workItemId
43042
- };
43043
- }
43044
- const targetMember = getDepartmentMemberByMailboxId(toMailboxId);
43045
- if (fromMailboxId === "manager" && targetMember?.role === "department_head") {
43046
- return {
43047
- workItemId: id,
43048
- workItemRole: "upstream_request",
43049
- upstreamMessageId: id
43050
- };
43051
- }
43052
- return {
43053
- workItemRole: options?.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId)
43054
- };
43055
- };
43056
- var buildMailboxInterruptContent = (msg) => {
43057
- const from = msg.fromMailboxId;
43058
- return [
43059
- `<system_reminder>`,
43060
- `\u4F60\u7684 mailbox \u6536\u5230\u4E00\u5C01\u65B0\u7684\u5185\u90E8\u534F\u4F5C\u6D88\u606F\u3002`,
43061
- ``,
43062
- `message_id: ${msg.id}`,
43063
- `from: ${from}`,
43064
- `thread: ${msg.threadId || msg.id}`,
43065
- ``,
43066
- `\u8FD9\u662F\u4E00\u6761\u4E2D\u65AD\u63D0\u9192\uFF0C\u4E0D\u4EE3\u8868\u90AE\u4EF6\u5DF2\u88AB\u9886\u53D6\u3002\u8BF7\u5C3D\u5FEB\u91CD\u65B0\u8BC4\u4F30\u5F53\u524D\u5DE5\u4F5C\u4F18\u5148\u7EA7\uFF1A`,
43067
- `1. \u8C03\u7528 list_mailbox \u67E5\u770B\u5F85\u5904\u7406\u961F\u5217\u3002`,
43068
- `2. \u5982\u8BE5\u6D88\u606F\u66F4\u7D27\u6025\uFF0C\u8C03\u7528 get_mailbox(message_id="${msg.id}") \u9886\u53D6\u5E76\u8BFB\u53D6\u3002`,
43069
- `3. \u5982\u679C\u4F60\u6B63\u5728\u8FD0\u884C\u957F\u4EFB\u52A1\uFF0C\u5E94\u5148\u8BB0\u5F55\u5F53\u524D\u8FDB\u5C55\uFF0C\u5FC5\u8981\u65F6\u7528 mailbox_followup \u540C\u6B65\u72B6\u6001\uFF0C\u518D\u5904\u7406\u8FD9\u5C01\u65B0\u6D88\u606F\u3002`,
43070
- `4. \u5F62\u6210\u6B63\u5F0F\u7B54\u590D\u540E\u5FC5\u987B\u7528 reply_mailbox \u56DE\u590D\u5BF9\u5E94 message_id\u3002`,
43071
- `</system_reminder>`
43072
- ].join("\n");
43073
- };
43074
- var queueMailboxInterruptIfRunning = (msg) => {
43075
- if (msg.toMailboxId === "manager") return;
43076
- if (!hasRunningAgent(msg.toMailboxId)) return;
43077
- const queued = queueInterrupt(msg.toMailboxId, {
43078
- content: buildMailboxInterruptContent(msg),
43079
- metadata: {
43080
- trigger: "mailbox.message_received",
43081
- mailboxMessageId: msg.id,
43082
- fromMailboxId: msg.fromMailboxId,
43083
- toMailboxId: msg.toMailboxId,
43084
- threadId: msg.threadId || msg.id
43085
- }
43086
- });
43087
- if (queued) {
43088
- console.log(`[mailbox] \u76EE\u6807 agent ${msg.toMailboxId} \u6B63\u5728\u8FD0\u884C\uFF0C\u5DF2\u5C06\u65B0\u90AE\u4EF6 ${msg.id} \u4F5C\u4E3A\u4E2D\u65AD\u63D0\u9192\u5165\u961F`);
43082
+ var recordMailboxStatusChange = (params) => {
43083
+ if (params.previousStatus === params.nextStatus) {
43084
+ return null;
43089
43085
  }
43090
- };
43091
- var recordMailboxReceivedAgentEvent = (msg) => {
43092
- if (msg.toMailboxId === "manager") return;
43093
- recordAgentEvent({
43094
- userId: msg.toMailboxId,
43095
- type: "mailbox.message_received",
43096
- source: "mailbox",
43097
- sourceId: msg.id,
43098
- payload: {
43099
- ownerMailboxId: msg.toMailboxId,
43100
- mailboxMessageId: msg.id,
43101
- fromMailboxId: msg.fromMailboxId,
43102
- toMailboxId: msg.toMailboxId,
43103
- threadId: msg.threadId || msg.id,
43104
- contentPreview: msg.content.slice(0, 160),
43105
- summary: `\u6536\u5230\u6765\u81EA ${msg.fromMailboxId} \u7684\u65B0\u5185\u90E8\u534F\u4F5C\u6D88\u606F\uFF0C\u8BF7\u7528 list_mailbox/get_mailbox \u8BC4\u4F30\u5E76\u9886\u53D6\u3002`
43086
+ return recordMailboxEvent({
43087
+ messageId: params.messageId,
43088
+ mailboxId: params.mailboxId,
43089
+ actorMailboxId: params.actorMailboxId,
43090
+ counterpartMailboxId: params.counterpartMailboxId,
43091
+ eventType: "message_status_changed",
43092
+ detail: {
43093
+ previousStatus: params.previousStatus,
43094
+ nextStatus: params.nextStatus,
43095
+ reason: params.reason
43106
43096
  }
43107
43097
  });
43108
43098
  };
43109
- var sendMessage2 = (fromMailboxId, toMailboxId, content, options) => {
43099
+ var listMailboxEvents = (params) => {
43110
43100
  const db3 = createSqliteDB();
43111
- const id = (0, import_node_crypto7.randomUUID)().slice(0, 8);
43112
- const threadId = options?.threadId || id;
43113
- const workItemContext = resolveWorkItemContext(fromMailboxId, toMailboxId, id, options);
43114
- const stmt = db3.prepare(`insert into mailbox (
43115
- id,
43116
- to_mailbox_id,
43117
- from_mailbox_id,
43118
- content,
43119
- send_time,
43120
- status,
43121
- origin_user_id,
43122
- origin_platform,
43123
- thread_id,
43124
- parent_message_id,
43125
- work_item_id,
43126
- work_item_role,
43127
- upstream_message_id
43128
- ) values (?,?,?,?,?,?,?,?,?,?,?,?,?) `);
43129
- let mailboxMsg = {
43130
- id,
43131
- toMailboxId,
43132
- fromMailboxId,
43133
- content,
43134
- sendTime: (/* @__PURE__ */ new Date()).getTime(),
43135
- status: "pending",
43136
- originUserId: options?.originUserId,
43137
- originPlatform: options?.originPlatform,
43138
- threadId,
43139
- parentMessageId: options?.parentMessageId,
43140
- workItemId: workItemContext.workItemId,
43141
- workItemRole: workItemContext.workItemRole,
43142
- upstreamMessageId: workItemContext.upstreamMessageId
43143
- };
43144
- const result = stmt.run(
43145
- mailboxMsg.id,
43146
- mailboxMsg.toMailboxId,
43147
- mailboxMsg.fromMailboxId,
43148
- mailboxMsg.content,
43149
- mailboxMsg.sendTime,
43150
- mailboxMsg.status,
43151
- mailboxMsg.originUserId || null,
43152
- mailboxMsg.originPlatform || null,
43153
- mailboxMsg.threadId || mailboxMsg.id,
43154
- mailboxMsg.parentMessageId || null,
43155
- mailboxMsg.workItemId || null,
43156
- mailboxMsg.workItemRole || null,
43157
- mailboxMsg.upstreamMessageId || null
43101
+ const limit = Math.max(1, Math.min(params.limit ?? 100, 500));
43102
+ const stmt = db3.prepare(
43103
+ `SELECT
43104
+ e.id as id,
43105
+ e.message_id as messageId,
43106
+ e.mailbox_id as mailboxId,
43107
+ e.actor_mailbox_id as actorMailboxId,
43108
+ e.counterpart_mailbox_id as counterpartMailboxId,
43109
+ e.event_type as eventType,
43110
+ e.detail_json as detailJson,
43111
+ e.created_at as createdAt,
43112
+ m.to_mailbox_id as toMailboxId,
43113
+ m.from_mailbox_id as fromMailboxId,
43114
+ m.content as content,
43115
+ m.send_time as sendTime,
43116
+ m.status as status,
43117
+ m.origin_user_id as originUserId,
43118
+ m.origin_platform as originPlatform,
43119
+ m.thread_id as threadId,
43120
+ m.parent_message_id as parentMessageId
43121
+ FROM mailbox_events e
43122
+ LEFT JOIN mailbox m ON m.id = e.message_id
43123
+ WHERE e.mailbox_id = ? OR e.actor_mailbox_id = ? OR e.counterpart_mailbox_id = ?
43124
+ ORDER BY e.created_at DESC
43125
+ LIMIT ?`
43158
43126
  );
43159
- recordMailboxEvent({
43160
- messageId: mailboxMsg.id,
43161
- mailboxId: toMailboxId,
43162
- actorMailboxId: fromMailboxId,
43163
- counterpartMailboxId: fromMailboxId,
43164
- eventType: "message_sent",
43165
- detail: {
43166
- initialStatus: mailboxMsg.status,
43167
- workItemId: mailboxMsg.workItemId ?? null,
43168
- workItemRole: mailboxMsg.workItemRole ?? null,
43169
- upstreamMessageId: mailboxMsg.upstreamMessageId ?? null,
43170
- ...options?.auditDetail ?? {}
43171
- },
43172
- createdAt: mailboxMsg.sendTime
43173
- });
43174
- recordMailboxReceivedAgentEvent(mailboxMsg);
43175
- queueMailboxInterruptIfRunning(mailboxMsg);
43176
- enqueueCeoFollowupFromMailbox(mailboxMsg);
43177
- return mailboxMsg;
43127
+ const rows = stmt.all(params.mailboxId, params.mailboxId, params.mailboxId, limit);
43128
+ return rows.map(mapMailboxEventRow);
43178
43129
  };
43179
- var updateMailboxMessageStatus = (messageId, nextStatus, options) => {
43130
+
43131
+ // src/department/mailbox/ceoFollowup.ts
43132
+ var import_node_crypto7 = require("node:crypto");
43133
+ var rowToFollowup = (row) => ({
43134
+ id: row.id,
43135
+ sourceMessageId: row.sourceMessageId,
43136
+ status: row.status,
43137
+ originUserId: row.originUserId,
43138
+ originPlatform: row.originPlatform,
43139
+ fromMailboxId: row.fromMailboxId,
43140
+ threadId: row.threadId ?? void 0,
43141
+ parentMessageId: row.parentMessageId ?? void 0,
43142
+ workItemId: row.workItemId ?? void 0,
43143
+ content: row.content,
43144
+ attempts: row.attempts,
43145
+ lastError: row.lastError ?? void 0,
43146
+ createdAt: row.createdAt,
43147
+ updatedAt: row.updatedAt,
43148
+ completedAt: row.completedAt ?? void 0
43149
+ });
43150
+ var selectById = (id) => {
43180
43151
  const db3 = createSqliteDB();
43181
- const selectStmt2 = db3.prepare(
43182
- `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, status
43183
- FROM mailbox WHERE id = ?`
43184
- );
43185
- const existing = selectStmt2.get(messageId);
43186
- if (!existing) return false;
43187
- if (existing.status === nextStatus) return false;
43188
- const allowedFrom = options?.fromStatus ? Array.isArray(options.fromStatus) ? options.fromStatus : [options.fromStatus] : null;
43189
- if (allowedFrom && !allowedFrom.includes(existing.status)) {
43190
- return false;
43191
- }
43192
- const updateStmt = db3.prepare(
43193
- `UPDATE mailbox
43194
- SET status = ?, updated_at = ?
43195
- WHERE id = ? AND status = ?`
43196
- );
43197
- const result = updateStmt.run(nextStatus, Date.now(), messageId, existing.status);
43198
- if (result.changes === 0) return false;
43199
- recordMailboxStatusChange({
43200
- messageId,
43201
- mailboxId: existing.toMailboxId,
43202
- previousStatus: existing.status,
43203
- nextStatus,
43204
- actorMailboxId: options?.actorMailboxId,
43205
- counterpartMailboxId: options?.counterpartMailboxId || existing.fromMailboxId,
43206
- reason: options?.reason
43207
- });
43208
- return true;
43209
- };
43210
-
43211
- // src/tools/tools/department/DepartmentCreate.ts
43212
- var DESCRIPTION20 = `
43213
- \u521B\u5EFA\u90E8\u95E8\u3002Department \u662F\u516C\u53F8\u7EC4\u7EC7\u4E2D\u7684\u957F\u671F\u804C\u8D23\u5355\u5143\uFF0C\u4E0D\u662F\u4E00\u6B21\u6027\u9879\u76EE\u5C0F\u961F\u3002
43214
-
43215
- \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43216
- - \u5F53\u4E00\u4E2A\u590D\u6742\u3001\u957F\u671F\u3001\u4E13\u4E1A\u804C\u8D23\u9700\u8981\u7A33\u5B9A\u7EC4\u7EC7\u627F\u63A5\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43217
- - sourceGoalId \u662F\u53EF\u9009\u6765\u6E90\u76EE\u6807\uFF1B\u90E8\u95E8\u804C\u8D23\u5E94\u5199\u5165 charter\uFF0C\u4E0D\u8981\u628A\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282\u5199\u8FDB charter\u3002
43218
- - \u521B\u5EFA\u90E8\u95E8\u540E\uFF0CCEO \u5E94\u4F7F\u7528 department_member_create \u4EFB\u547D Department Head\uFF0C\u518D\u7528 department_communicate \u53D1\u9001\u672C\u6B21\u5177\u4F53\u4EFB\u52A1\u3002
43219
- `;
43220
- var departmentCreate = {
43221
- name: `department_create`,
43222
- description: DESCRIPTION20,
43223
- input_schema: {
43224
- type: `object`,
43225
- properties: {
43226
- name: {
43227
- type: `string`,
43228
- description: `\u90E8\u95E8\u540D\u79F0`
43229
- },
43230
- charter: {
43231
- type: `string`,
43232
- description: `\u90E8\u95E8\u957F\u671F\u804C\u8D23\u8FB9\u754C\uFF0C\u4E0D\u80FD\u5199\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282`
43233
- },
43234
- sourceGoalId: {
43235
- type: `string`,
43236
- description: `\u53EF\u9009\uFF1A\u521B\u5EFA\u8BE5\u90E8\u95E8\u65F6\u5173\u8054\u7684\u76EE\u6807 id`
43237
- },
43238
- workpath: {
43239
- type: `string`,
43240
- description: `\u90E8\u95E8\u9879\u76EE\u5DE5\u4F5C\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u90E8\u95E8\u6210\u5458\u7684\u6587\u4EF6\u4FEE\u6539\u5E94\u9650\u5236\u5728\u6B64\u76EE\u5F55\u8303\u56F4\u5185`
43241
- }
43242
- },
43243
- required: [`name`, `charter`, `workpath`]
43244
- },
43245
- async execute(input) {
43246
- const name = input.name;
43247
- const charter = input.charter;
43248
- const sourceGoalId = input.sourceGoalId;
43249
- const workpath = input.workpath;
43250
- if (sourceGoalId && !getGoalById(sourceGoalId)) {
43251
- return `[departmentCreate] \u4E0D\u5B58\u5728 id=${sourceGoalId} \u7684\u76EE\u6807`;
43252
- }
43253
- let departmentDefinition = {
43254
- id: (0, import_node_crypto8.randomUUID)().slice(0, 8),
43255
- name,
43256
- charter,
43257
- sourceGoalId,
43258
- workpath,
43259
- departmentMembers: []
43260
- };
43261
- try {
43262
- departmentDefinition = createDepartment(departmentDefinition);
43263
- } catch (err) {
43264
- return `[departmentCreate] \u521B\u5EFA\u90E8\u95E8\u5931\u8D25: ${err.message}`;
43265
- }
43266
- return `[departmentCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u529F: ${JSON.stringify(departmentDefinition)}`;
43267
- }
43152
+ const row = db3.prepare(`
43153
+ SELECT
43154
+ id,
43155
+ source_message_id as sourceMessageId,
43156
+ status,
43157
+ origin_user_id as originUserId,
43158
+ origin_platform as originPlatform,
43159
+ from_mailbox_id as fromMailboxId,
43160
+ thread_id as threadId,
43161
+ parent_message_id as parentMessageId,
43162
+ work_item_id as workItemId,
43163
+ content,
43164
+ attempts,
43165
+ last_error as lastError,
43166
+ created_at as createdAt,
43167
+ updated_at as updatedAt,
43168
+ completed_at as completedAt
43169
+ FROM ceo_followups
43170
+ WHERE id = ?
43171
+ `).get(id);
43172
+ return row ? rowToFollowup(row) : null;
43268
43173
  };
43269
-
43270
- // src/tools/tools/department/DepartmentCommunicate.ts
43271
- var CEO_MAILBOX_ID = `manager`;
43272
- var MANAGER_ALIAS_PATTERN = /(^|::)(manager|main[-_\s]?manager|ceo)$/i;
43273
- var looksLikePseudoManagerMailbox = (mailboxId) => {
43274
- if (mailboxId === CEO_MAILBOX_ID) return true;
43275
- if (!mailboxId.includes(`::`)) return false;
43276
- return MANAGER_ALIAS_PATTERN.test(mailboxId.trim());
43174
+ var enqueueCeoFollowupFromMailbox = (message) => {
43175
+ if (message.toMailboxId !== "manager") return null;
43176
+ if (!message.originUserId || !message.originPlatform) return null;
43177
+ const db3 = createSqliteDB();
43178
+ const now = Date.now();
43179
+ const id = `cfu_${(0, import_node_crypto7.randomUUID)().slice(0, 12)}`;
43180
+ db3.prepare(`
43181
+ INSERT INTO ceo_followups (
43182
+ id,
43183
+ source_message_id,
43184
+ status,
43185
+ origin_user_id,
43186
+ origin_platform,
43187
+ from_mailbox_id,
43188
+ thread_id,
43189
+ parent_message_id,
43190
+ work_item_id,
43191
+ content,
43192
+ created_at,
43193
+ updated_at
43194
+ ) VALUES (?, ?, 'pending', ?, ?, ?, ?, ?, ?, ?, ?, ?)
43195
+ ON CONFLICT(source_message_id) DO UPDATE SET
43196
+ status = CASE
43197
+ WHEN ceo_followups.status = 'completed' THEN ceo_followups.status
43198
+ ELSE 'pending'
43199
+ END,
43200
+ origin_user_id = excluded.origin_user_id,
43201
+ origin_platform = excluded.origin_platform,
43202
+ from_mailbox_id = excluded.from_mailbox_id,
43203
+ thread_id = excluded.thread_id,
43204
+ parent_message_id = excluded.parent_message_id,
43205
+ work_item_id = excluded.work_item_id,
43206
+ content = excluded.content,
43207
+ updated_at = excluded.updated_at
43208
+ `).run(
43209
+ id,
43210
+ message.id,
43211
+ message.originUserId,
43212
+ message.originPlatform,
43213
+ message.fromMailboxId,
43214
+ message.threadId ?? message.id,
43215
+ message.parentMessageId ?? null,
43216
+ message.workItemId ?? null,
43217
+ message.content,
43218
+ now,
43219
+ now
43220
+ );
43221
+ const row = db3.prepare(`
43222
+ SELECT id FROM ceo_followups WHERE source_message_id = ?
43223
+ `).get(message.id);
43224
+ return row ? selectById(row.id) : null;
43277
43225
  };
43278
- var buildManagerRoutingRejection = (activeContext) => [
43279
- `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4F2A manager mailbox\u3002`,
43280
- `Main Manager/CEO \u4E0D\u662F\u90E8\u95E8\u6210\u5458\uFF0C\u4E0D\u80FD\u7528 department_communicate \u53D1\u9001\u5230 manager\u3001Department::Manager\u3001CEO::Manager \u7B49\u5730\u5740\u3002`,
43281
- activeContext ? `\u5982\u679C\u8981\u5411\u4E0A\u6E38 CEO \u6C47\u62A5\uFF0C\u8BF7\u8C03\u7528 reply_mailbox(message_id="${activeContext.id}", content="...") \u6B63\u5F0F\u56DE\u590D\u539F\u90AE\u4EF6\uFF1B\u82E5\u53EA\u662F\u540C\u6B65\u9636\u6BB5\u8FDB\u5C55\uFF0C\u8BF7\u8C03\u7528 mailbox_followup(message_id="${activeContext.id}", content="...", kind="progress")\u3002` : `\u5982\u679C\u8981\u5411\u4E0A\u6E38 CEO \u6C47\u62A5\uFF0C\u8BF7\u56DE\u5230\u539F manager \u90AE\u4EF6\uFF0C\u4F7F\u7528 reply_mailbox(message_id="\u539F\u90AE\u4EF6ID", content="...") \u6216 mailbox_followup(message_id="\u539F\u90AE\u4EF6ID", content="...", kind="progress")\u3002`
43282
- ].join(`
43283
- `);
43284
- var getActiveMailboxContext = (actorMailboxId, messageId) => {
43285
- if (!messageId) return null;
43226
+ var listPendingCeoFollowups = (limit = 10) => {
43286
43227
  const db3 = createSqliteDB();
43287
- const row = db3.prepare(
43288
- `SELECT
43228
+ const rows = db3.prepare(`
43229
+ SELECT
43289
43230
  id,
43290
- from_mailbox_id as fromMailboxId,
43291
- to_mailbox_id as toMailboxId,
43231
+ source_message_id as sourceMessageId,
43232
+ status,
43292
43233
  origin_user_id as originUserId,
43293
43234
  origin_platform as originPlatform,
43235
+ from_mailbox_id as fromMailboxId,
43294
43236
  thread_id as threadId,
43237
+ parent_message_id as parentMessageId,
43295
43238
  work_item_id as workItemId,
43296
- upstream_message_id as upstreamMessageId
43297
- FROM mailbox
43298
- WHERE id = ?
43299
- AND (from_mailbox_id = ? OR to_mailbox_id = ?)`
43300
- ).get(messageId, actorMailboxId, actorMailboxId);
43301
- return row ?? null;
43239
+ content,
43240
+ attempts,
43241
+ last_error as lastError,
43242
+ created_at as createdAt,
43243
+ updated_at as updatedAt,
43244
+ completed_at as completedAt
43245
+ FROM ceo_followups
43246
+ WHERE status IN ('pending', 'failed')
43247
+ ORDER BY created_at ASC
43248
+ LIMIT ?
43249
+ `).all(limit);
43250
+ return rows.map(rowToFollowup);
43302
43251
  };
43303
- var DESCRIPTION21 = `
43304
- \u5411\u90E8\u95E8\u6210\u5458\u53D1\u9001\u6D88\u606F\u6216\u6307\u4EE4\uFF0C\u662F\u516C\u53F8\u5185\u90E8\u5F02\u6B65\u534F\u4F5C\u901A\u4FE1\u6E20\u9053\u3002
43305
-
43306
- \u9ED8\u8BA4\u7EC4\u7EC7\u6CBB\u7406\uFF1A
43307
- - CEO \u9ED8\u8BA4\u901A\u8FC7 department_list \u53EA\u770B\u5230\u90E8\u95E8\u548C Department Head\uFF0C\u4E0D\u4E3B\u52A8\u66B4\u9732 Executor\u3002
43308
- - Department Head \u9ED8\u8BA4\u7BA1\u7406\u672C\u90E8\u95E8\u6210\u5458\u3002
43309
- - \u5982\u679C\u667A\u80FD\u4F53\u5DF2\u7ECF\u901A\u8FC7\u6B63\u5E38\u6C9F\u901A\u77E5\u9053\u4E86\u5176\u4ED6\u6210\u5458\u7684 mailbox \u5730\u5740\uFF0C\u5DE5\u5177\u4E0D\u4F1A\u786C\u6027\u963B\u6B62\u901A\u4FE1\uFF1B\u8FD9\u7C7B\u975E\u9ED8\u8BA4\u8DEF\u5F84\u4F1A\u5199\u5165\u5BA1\u8BA1\u4FE1\u606F\u3002
43310
- - Main Manager/CEO \u7684 mailbox id \u56FA\u5B9A\u662F manager\u3002Department agent \u4E0D\u5E94\u4F7F\u7528 department_communicate \u7ED9 manager \u6216 Department::Manager \u53D1\u4FE1\uFF1B\u8981\u56DE\u590D\u4E0A\u6E38\u5FC5\u987B\u4F7F\u7528\u539F\u90AE\u4EF6\u7684 reply_mailbox \u6216 mailbox_followup\u3002
43311
-
43312
- \u53C2\u6570\u8BF4\u660E\uFF1A
43313
- - department_name + member_name: \u9ED8\u8BA4\u5BFB\u5740\u65B9\u5F0F\u3002
43314
- - target_mailbox_id: \u53EF\u9009\uFF0C\u5DF2\u77E5\u5BF9\u65B9 mailbox \u5730\u5740\u65F6\u4F7F\u7528\uFF1B\u4F7F\u7528\u65F6\u5EFA\u8BAE\u586B\u5199 reason\u3002
43315
- - content: \u8981\u53D1\u9001\u7684\u6D88\u606F\u5185\u5BB9\u3002
43316
- `;
43317
- var departmentCommunicate = {
43318
- name: `department_communicate`,
43319
- description: DESCRIPTION21,
43320
- input_schema: {
43321
- type: `object`,
43322
- properties: {
43323
- department_name: {
43324
- type: `string`,
43325
- description: `\u76EE\u6807\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0\uFF1B\u4F7F\u7528 target_mailbox_id \u65F6\u53EF\u7701\u7565`
43326
- },
43327
- member_name: {
43328
- type: `string`,
43329
- description: `\u76EE\u6807\u6210\u5458\u540D\u79F0\uFF1B\u4F7F\u7528 target_mailbox_id \u65F6\u53EF\u7701\u7565`
43330
- },
43331
- target_mailbox_id: {
43332
- type: `string`,
43333
- description: `\u53EF\u9009\uFF1A\u5DF2\u77E5\u5BF9\u65B9 mailbox \u5730\u5740\u65F6\u76F4\u63A5\u586B\u5199\uFF0C\u4F8B\u5982 Department::Member`
43334
- },
43335
- content: {
43336
- type: `string`,
43337
- description: `\u8981\u6295\u9001\u7ED9\u5BF9\u65B9\u7684\u4FE1\u606F\u5185\u5BB9`
43338
- },
43339
- reason: {
43340
- type: `string`,
43341
- description: `\u53EF\u9009\uFF1A\u8DE8\u9ED8\u8BA4\u7EC4\u7EC7\u94FE\u8DEF\u6C9F\u901A\u7684\u539F\u56E0\uFF0C\u4FBF\u4E8E\u5BA1\u8BA1`
43342
- }
43343
- },
43344
- required: [`content`]
43345
- },
43346
- async execute(input, userRequest) {
43347
- if (!userRequest) {
43348
- throw new Error(`[departmentCommunicate] userRequest\u4E0D\u80FD\u4E3A\u7A7A`);
43349
- }
43350
- const departmentName = input.department_name;
43351
- const memberName = input.member_name;
43352
- const targetMailboxId = input.target_mailbox_id;
43353
- const content = input.content;
43354
- const reason = input.reason;
43355
- let fromMailboxId;
43356
- const departmentAgentId = userRequest.departmentAgentId;
43357
- const actorMember = departmentAgentId ? getDepartmentMemberByMailboxId(
43358
- departmentAgentId.includes(`::`) ? departmentAgentId : getMailBoxIdFromDepartmentMemberId(departmentAgentId) ?? departmentAgentId
43359
- ) : null;
43360
- if (departmentAgentId) {
43361
- const resolved = departmentAgentId.includes(`::`) ? departmentAgentId : getMailBoxIdFromDepartmentMemberId(departmentAgentId);
43362
- if (!resolved) {
43363
- return `[departmentCommunicate] \u65E0\u6CD5\u83B7\u53D6\u5F53\u524D agent \u7684 mailboxId\uFF0CdepartmentAgentId: ${departmentAgentId}`;
43364
- }
43365
- fromMailboxId = resolved;
43366
- } else {
43367
- fromMailboxId = CEO_MAILBOX_ID;
43368
- }
43369
- const activeContext = departmentAgentId ? getActiveMailboxContext(fromMailboxId, userRequest.requestId) : null;
43370
- let toMailboxId = targetMailboxId;
43371
- let targetMember = targetMailboxId ? getDepartmentMemberByMailboxId(targetMailboxId) : null;
43372
- if (departmentAgentId && targetMailboxId && looksLikePseudoManagerMailbox(targetMailboxId)) {
43373
- return buildManagerRoutingRejection(activeContext);
43374
- }
43375
- if (departmentAgentId && targetMailboxId && !targetMember) {
43376
- return [
43377
- `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4E0D\u5B58\u5728\u7684 mailbox: ${targetMailboxId}`,
43378
- `Department agent \u4F7F\u7528 target_mailbox_id \u65F6\uFF0C\u76EE\u6807\u5FC5\u987B\u662F\u5DF2\u5B58\u5728\u7684\u90E8\u95E8\u6210\u5458 mailbox\u3002`,
43379
- `\u5982\u679C\u4F60\u60F3\u56DE\u590D\u4E0A\u6E38 CEO/Main Manager\uFF0C\u8BF7\u4E0D\u8981\u4F2A\u9020 manager \u5730\u5740\uFF1B\u8BF7\u4F7F\u7528\u539F\u90AE\u4EF6\u7684 reply_mailbox \u6216 mailbox_followup\u3002`
43380
- ].join(`
43381
- `);
43382
- }
43383
- if (!toMailboxId) {
43384
- if (!departmentName || !memberName) {
43385
- return `[departmentCommunicate] department_name/member_name \u6216 target_mailbox_id \u81F3\u5C11\u9700\u8981\u63D0\u4F9B\u4E00\u79CD`;
43386
- }
43387
- targetMember = getDepartmentMemberByName(departmentName, memberName);
43388
- if (!targetMember) {
43389
- return `[departmentCommunicate] \u4E0D\u5B58\u5728 ${departmentName} \u90E8\u95E8\u7684\u6210\u5458: ${memberName}`;
43390
- }
43391
- toMailboxId = getMailBoxId(departmentName, memberName);
43252
+ var claimCeoFollowup = (id) => {
43253
+ const db3 = createSqliteDB();
43254
+ const now = Date.now();
43255
+ const result = db3.prepare(`
43256
+ UPDATE ceo_followups
43257
+ SET status = 'processing',
43258
+ attempts = attempts + 1,
43259
+ updated_at = ?
43260
+ WHERE id = ?
43261
+ AND status IN ('pending', 'failed')
43262
+ `).run(now, id);
43263
+ if (result.changes === 0) return null;
43264
+ return selectById(id);
43265
+ };
43266
+ var completeCeoFollowup = (id) => {
43267
+ const db3 = createSqliteDB();
43268
+ const now = Date.now();
43269
+ db3.prepare(`
43270
+ UPDATE ceo_followups
43271
+ SET status = 'completed',
43272
+ completed_at = COALESCE(completed_at, ?),
43273
+ updated_at = ?,
43274
+ last_error = NULL
43275
+ WHERE id = ?
43276
+ AND status IN ('pending', 'processing', 'failed')
43277
+ `).run(now, now, id);
43278
+ db3.prepare(`
43279
+ UPDATE mailbox
43280
+ SET status = 'done',
43281
+ updated_at = ?
43282
+ WHERE id = (SELECT source_message_id FROM ceo_followups WHERE id = ?)
43283
+ AND status IN ('pending', 'processing', 'read')
43284
+ `).run(now, id);
43285
+ };
43286
+ var failCeoFollowup = (id, error) => {
43287
+ const db3 = createSqliteDB();
43288
+ const now = Date.now();
43289
+ db3.prepare(`
43290
+ UPDATE ceo_followups
43291
+ SET status = 'failed',
43292
+ last_error = ?,
43293
+ updated_at = ?
43294
+ WHERE id = ?
43295
+ AND status IN ('pending', 'processing', 'failed')
43296
+ `).run(error.slice(0, 1e3), now, id);
43297
+ };
43298
+ var recoverStaleProcessingCeoFollowups = (staleBefore) => {
43299
+ const db3 = createSqliteDB();
43300
+ const now = Date.now();
43301
+ const result = db3.prepare(`
43302
+ UPDATE ceo_followups
43303
+ SET status = 'failed',
43304
+ last_error = 'stale_processing_recovered',
43305
+ updated_at = ?
43306
+ WHERE status = 'processing'
43307
+ AND updated_at < ?
43308
+ `).run(now, staleBefore);
43309
+ return result.changes;
43310
+ };
43311
+ var completePendingCeoFollowupsForUser = (originUserId) => {
43312
+ const db3 = createSqliteDB();
43313
+ const now = Date.now();
43314
+ const rows = db3.prepare(`
43315
+ SELECT id, source_message_id as sourceMessageId
43316
+ FROM ceo_followups
43317
+ WHERE origin_user_id = ?
43318
+ AND status IN ('pending', 'processing', 'failed')
43319
+ `).all(originUserId);
43320
+ const tx = db3.transaction((items) => {
43321
+ const completeStmt = db3.prepare(`
43322
+ UPDATE ceo_followups
43323
+ SET status = 'completed',
43324
+ completed_at = COALESCE(completed_at, ?),
43325
+ updated_at = ?,
43326
+ last_error = NULL
43327
+ WHERE id = ?
43328
+ AND status IN ('pending', 'processing', 'failed')
43329
+ `);
43330
+ const doneStmt = db3.prepare(`
43331
+ UPDATE mailbox
43332
+ SET status = 'done',
43333
+ updated_at = ?
43334
+ WHERE id = ?
43335
+ AND status IN ('pending', 'processing', 'read')
43336
+ `);
43337
+ for (const item of items) {
43338
+ completeStmt.run(now, now, item.id);
43339
+ doneStmt.run(now, item.sourceMessageId);
43392
43340
  }
43393
- const isCrossDepartment = Boolean(actorMember && targetMember && actorMember.departmentId !== targetMember.departmentId);
43394
- const isKnownAddressPath = Boolean(targetMailboxId);
43395
- const options = {
43396
- auditDetail: {
43397
- communicationPolicy: "allowed_known_address",
43398
- isKnownAddressPath,
43399
- isCrossDepartment,
43400
- actorRole: actorMember?.role ?? "ceo",
43401
- targetRole: targetMember?.role ?? "unknown",
43402
- reason: reason ?? null
43403
- }
43341
+ });
43342
+ tx(rows);
43343
+ return rows.length;
43344
+ };
43345
+
43346
+ // src/department/mailbox/mailbox.ts
43347
+ var getMailBoxId = (departmentName, memberName) => {
43348
+ return `${departmentName}::${memberName}`;
43349
+ };
43350
+ var getMailBoxIdFromDepartmentMemberId = (memberId) => {
43351
+ const member = getDepartmentMemberById(memberId);
43352
+ if (!member) return null;
43353
+ const department = getDepartmentById(member.departmentId);
43354
+ if (!department) throw new Error(`[getMailBoxIdFromDepartmentMemberId] \u6B64\u6210\u5458\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u90E8\u95E8: ${member.departmentId}`);
43355
+ return getMailBoxId(department.name, member.name);
43356
+ };
43357
+ var cancelMailboxMessages = (mailboxId) => {
43358
+ const db3 = createSqliteDB();
43359
+ const stmt = db3.prepare(
43360
+ `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, status
43361
+ FROM mailbox
43362
+ WHERE (to_mailbox_id = ? or from_mailbox_id = ?) AND status in ('pending', 'processing')`
43363
+ );
43364
+ const messages = stmt.all(mailboxId, mailboxId);
43365
+ for (const message of messages) {
43366
+ updateMailboxMessageStatus(message.id, "cancelled", {
43367
+ counterpartMailboxId: message.fromMailboxId,
43368
+ reason: "mailbox_cancelled"
43369
+ });
43370
+ }
43371
+ return messages.length;
43372
+ };
43373
+ var getWorkItemContextFromMessage = (messageId) => {
43374
+ if (!messageId) return null;
43375
+ const db3 = createSqliteDB();
43376
+ const row = db3.prepare(
43377
+ `SELECT
43378
+ work_item_id as workItemId,
43379
+ work_item_role as workItemRole,
43380
+ upstream_message_id as upstreamMessageId
43381
+ FROM mailbox
43382
+ WHERE id = ?`
43383
+ ).get(messageId);
43384
+ if (!row?.workItemId) return null;
43385
+ return row;
43386
+ };
43387
+ var getWorkItemContextFromThread = (threadId) => {
43388
+ if (!threadId) return null;
43389
+ const db3 = createSqliteDB();
43390
+ const row = db3.prepare(
43391
+ `SELECT
43392
+ work_item_id as workItemId,
43393
+ work_item_role as workItemRole,
43394
+ upstream_message_id as upstreamMessageId
43395
+ FROM mailbox
43396
+ WHERE thread_id = ?
43397
+ AND work_item_id IS NOT NULL
43398
+ ORDER BY send_time ASC
43399
+ LIMIT 1`
43400
+ ).get(threadId);
43401
+ if (!row?.workItemId) return null;
43402
+ return row;
43403
+ };
43404
+ var mailboxLooksLikeRole = (mailboxId, role) => {
43405
+ const lower = mailboxId.toLowerCase();
43406
+ if (role === "department_head") {
43407
+ return lower.includes("department-head") || mailboxId.includes("\u8D1F\u8D23\u4EBA");
43408
+ }
43409
+ return lower.includes("executor") || mailboxId.includes("\u6267\u884C\u8005") || mailboxId.includes("\u4E13\u5458");
43410
+ };
43411
+ var inferWorkItemRole = (fromMailboxId, toMailboxId, inherited) => {
43412
+ if (fromMailboxId === "manager") return "upstream_request";
43413
+ if (toMailboxId === "manager") return "upstream_report";
43414
+ const fromMember = getDepartmentMemberByMailboxId(fromMailboxId);
43415
+ const toMember = getDepartmentMemberByMailboxId(toMailboxId);
43416
+ const fromIsHead = fromMember?.role === "department_head" || mailboxLooksLikeRole(fromMailboxId, "department_head");
43417
+ const toIsHead = toMember?.role === "department_head" || mailboxLooksLikeRole(toMailboxId, "department_head");
43418
+ const fromIsExecutor = fromMember?.role === "executor" || mailboxLooksLikeRole(fromMailboxId, "executor");
43419
+ const toIsExecutor = toMember?.role === "executor" || mailboxLooksLikeRole(toMailboxId, "executor");
43420
+ if (fromIsHead && toIsExecutor) {
43421
+ return inherited?.workItemRole === "executor_result" ? "downstream_reply" : "delegation";
43422
+ }
43423
+ if (fromIsExecutor && toIsHead) return "executor_result";
43424
+ return inherited?.workItemRole ? "followup" : "message";
43425
+ };
43426
+ var resolveWorkItemContext = (fromMailboxId, toMailboxId, id, options) => {
43427
+ if (options?.workItemId) {
43428
+ return {
43429
+ workItemId: options.workItemId,
43430
+ workItemRole: options.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId),
43431
+ upstreamMessageId: options.upstreamMessageId ?? options.workItemId
43432
+ };
43433
+ }
43434
+ const inherited = getWorkItemContextFromMessage(options?.parentMessageId) ?? getWorkItemContextFromThread(options?.threadId);
43435
+ if (inherited?.workItemId) {
43436
+ return {
43437
+ workItemId: inherited.workItemId,
43438
+ workItemRole: options?.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId, inherited),
43439
+ upstreamMessageId: inherited.upstreamMessageId ?? inherited.workItemId
43404
43440
  };
43405
- if (!departmentAgentId) {
43406
- options.originUserId = userRequest.userId;
43407
- options.originPlatform = userRequest.platform;
43408
- } else {
43409
- if (activeContext) {
43410
- options.originUserId = activeContext.originUserId;
43411
- options.originPlatform = activeContext.originPlatform;
43412
- options.threadId = activeContext.threadId || activeContext.id;
43413
- options.parentMessageId = activeContext.id;
43414
- options.workItemId = activeContext.workItemId;
43415
- options.upstreamMessageId = activeContext.upstreamMessageId;
43416
- }
43417
- }
43418
- try {
43419
- sendMessage2(fromMailboxId, toMailboxId, content, options);
43420
- } catch (err) {
43421
- return `[departmentCommunicate] \u53D1\u9001\u6D88\u606F\u5931\u8D25: ${err.message}`;
43422
- }
43423
- return `[departmentCommunicate] \u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${toMailboxId}`;
43424
43441
  }
43442
+ const targetMember = getDepartmentMemberByMailboxId(toMailboxId);
43443
+ if (fromMailboxId === "manager" && targetMember?.role === "department_head") {
43444
+ return {
43445
+ workItemId: id,
43446
+ workItemRole: "upstream_request",
43447
+ upstreamMessageId: id
43448
+ };
43449
+ }
43450
+ return {
43451
+ workItemRole: options?.workItemRole ?? inferWorkItemRole(fromMailboxId, toMailboxId)
43452
+ };
43453
+ };
43454
+ var buildMailboxInterruptContent = (msg) => {
43455
+ const from = msg.fromMailboxId;
43456
+ return [
43457
+ `<system_reminder>`,
43458
+ `\u4F60\u7684 mailbox \u6536\u5230\u4E00\u5C01\u65B0\u7684\u5185\u90E8\u534F\u4F5C\u6D88\u606F\u3002`,
43459
+ ``,
43460
+ `message_id: ${msg.id}`,
43461
+ `from: ${from}`,
43462
+ `thread: ${msg.threadId || msg.id}`,
43463
+ ``,
43464
+ `\u8FD9\u662F\u4E00\u6761\u4E2D\u65AD\u63D0\u9192\uFF0C\u4E0D\u4EE3\u8868\u90AE\u4EF6\u5DF2\u88AB\u9886\u53D6\u3002\u8BF7\u5C3D\u5FEB\u91CD\u65B0\u8BC4\u4F30\u5F53\u524D\u5DE5\u4F5C\u4F18\u5148\u7EA7\uFF1A`,
43465
+ `1. \u8C03\u7528 list_mailbox \u67E5\u770B\u5F85\u5904\u7406\u961F\u5217\u3002`,
43466
+ `2. \u5982\u8BE5\u6D88\u606F\u66F4\u7D27\u6025\uFF0C\u8C03\u7528 get_mailbox(message_id="${msg.id}") \u9886\u53D6\u5E76\u8BFB\u53D6\u3002`,
43467
+ `3. \u5982\u679C\u4F60\u6B63\u5728\u8FD0\u884C\u957F\u4EFB\u52A1\uFF0C\u5E94\u5148\u8BB0\u5F55\u5F53\u524D\u8FDB\u5C55\uFF0C\u5FC5\u8981\u65F6\u7528 mailbox_followup \u540C\u6B65\u72B6\u6001\uFF0C\u518D\u5904\u7406\u8FD9\u5C01\u65B0\u6D88\u606F\u3002`,
43468
+ `4. \u5F62\u6210\u6B63\u5F0F\u7B54\u590D\u540E\u5FC5\u987B\u7528 reply_mailbox \u56DE\u590D\u5BF9\u5E94 message_id\u3002`,
43469
+ `</system_reminder>`
43470
+ ].join("\n");
43425
43471
  };
43426
-
43427
- // src/tools/tools/department/DepartmentDelete.ts
43428
- var DESCRIPTION22 = `
43429
- \u5220\u9664\u90E8\u95E8\u3002
43430
-
43431
- \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43432
- - \u89E3\u6563\u90E8\u95E8\u662F\u4E0D\u53EF\u9006\u64CD\u4F5C\uFF0C\u5FC5\u987B\u5148\u5F81\u5F97\u7528\u6237\u660E\u786E\u786E\u8BA4\u3002
43433
- - \u9996\u6B21\u8C03\u7528 confirmed \u5FC5\u987B\u4E3A false \u6216\u4E0D\u4F20\uFF1B\u5F97\u5230\u7528\u6237\u660E\u786E\u540C\u610F\u540E\uFF0C\u518D\u4EE5 confirmed=true \u8C03\u7528\u3002
43434
- - \u4E25\u7981\u5728\u672A\u5F97\u5230\u7528\u6237\u660E\u786E\u786E\u8BA4\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5\u4F20 confirmed=true\u3002
43435
- `;
43436
- var departmentDelete = {
43437
- name: `department_delete`,
43438
- description: DESCRIPTION22,
43439
- input_schema: {
43440
- type: `object`,
43441
- properties: {
43442
- name: {
43443
- type: `string`,
43444
- description: `\u8981\u5220\u9664\u7684\u90E8\u95E8\u540D\u79F0`
43445
- },
43446
- confirmed: {
43447
- type: `boolean`,
43448
- description: `\u7528\u6237\u662F\u5426\u5DF2\u660E\u786E\u786E\u8BA4\u89E3\u6563\u8BE5\u90E8\u95E8`
43449
- }
43450
- },
43451
- required: [`name`]
43452
- },
43453
- async execute(input) {
43454
- const name = input.name;
43455
- const confirmed = input.confirmed ?? false;
43456
- if (!confirmed) {
43457
- return `[departmentDelete] \u89E3\u6563\u90E8\u95E8\u662F\u4E0D\u53EF\u9006\u64CD\u4F5C\uFF0C\u8BF7\u5148\u7528 send_message \u5411\u7528\u6237\u786E\u8BA4"\u662F\u5426\u89E3\u6563\u90E8\u95E8 ${name}"\uFF0C\u5F97\u5230\u7528\u6237\u660E\u786E\u540C\u610F\u540E\uFF0C\u518D\u4EE5 confirmed=true \u91CD\u65B0\u8C03\u7528\u672C\u5DE5\u5177\u3002`;
43458
- }
43459
- try {
43460
- const members = listDepartmentMembers(name);
43461
- let totalCancelled = 0;
43462
- for (const member of members) {
43463
- const mailboxId = getMailBoxId(name, member.name);
43464
- totalCancelled += cancelMailboxMessages(mailboxId);
43465
- }
43466
- if (totalCancelled > 0) {
43467
- console.log(`[departmentDelete] \u5DF2\u53D6\u6D88\u90E8\u95E8 ${name} \u7684 ${totalCancelled} \u6761\u6B8B\u7559 mailbox \u6D88\u606F`);
43468
- }
43469
- deleteDepartment(name);
43470
- } catch (err) {
43471
- return `[departmentDelete] \u5220\u9664\u90E8\u95E8\u5931\u8D25: ${err.message}`;
43472
+ var queueMailboxInterruptIfRunning = (msg) => {
43473
+ if (msg.toMailboxId === "manager") return;
43474
+ if (!hasRunningAgent(msg.toMailboxId)) return;
43475
+ const queued = queueInterrupt(msg.toMailboxId, {
43476
+ content: buildMailboxInterruptContent(msg),
43477
+ metadata: {
43478
+ trigger: "mailbox.message_received",
43479
+ mailboxMessageId: msg.id,
43480
+ fromMailboxId: msg.fromMailboxId,
43481
+ toMailboxId: msg.toMailboxId,
43482
+ threadId: msg.threadId || msg.id
43472
43483
  }
43473
- return `[departmentDelete] \u5220\u9664\u90E8\u95E8\u6210\u529F: ${name}`;
43484
+ });
43485
+ if (queued) {
43486
+ console.log(`[mailbox] \u76EE\u6807 agent ${msg.toMailboxId} \u6B63\u5728\u8FD0\u884C\uFF0C\u5DF2\u5C06\u65B0\u90AE\u4EF6 ${msg.id} \u4F5C\u4E3A\u4E2D\u65AD\u63D0\u9192\u5165\u961F`);
43474
43487
  }
43475
43488
  };
43476
-
43477
- // src/tools/tools/department/DepartmentList.ts
43478
- var DESCRIPTION23 = `
43479
- \u5217\u51FA\u6240\u6709\u90E8\u95E8\u3002\u8BE5\u5DE5\u5177\u9762\u5411 CEO\uFF0C\u53EA\u8FD4\u56DE\u90E8\u95E8\u4E0E Department Head \u4FE1\u606F\uFF0C\u4E0D\u66B4\u9732 Executor \u660E\u7EC6\u3002
43480
-
43481
- \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43482
- - \u5F53\u7528\u6237\u8981\u6C42\u5217\u51FA\u6240\u6709\u90E8\u95E8\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43483
- - \u5F53\u590D\u6742\u4EFB\u52A1\u9700\u8981\u5224\u65AD\u73B0\u6709\u90E8\u95E8\u804C\u8D23\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43484
- `;
43485
- var departmentList = {
43486
- name: `department_list`,
43487
- description: DESCRIPTION23,
43488
- input_schema: {
43489
- type: `object`,
43490
- properties: {},
43491
- required: []
43492
- },
43493
- async execute() {
43494
- const departments = listDepartments();
43495
- if (departments.length === 0) {
43496
- return `[departmentList] \u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u90E8\u95E8\u3002`;
43489
+ var recordMailboxReceivedAgentEvent = (msg) => {
43490
+ if (msg.toMailboxId === "manager") return;
43491
+ recordAgentEvent({
43492
+ userId: msg.toMailboxId,
43493
+ type: "mailbox.message_received",
43494
+ source: "mailbox",
43495
+ sourceId: msg.id,
43496
+ payload: {
43497
+ ownerMailboxId: msg.toMailboxId,
43498
+ mailboxMessageId: msg.id,
43499
+ fromMailboxId: msg.fromMailboxId,
43500
+ toMailboxId: msg.toMailboxId,
43501
+ threadId: msg.threadId || msg.id,
43502
+ contentPreview: msg.content.slice(0, 160),
43503
+ summary: `\u6536\u5230\u6765\u81EA ${msg.fromMailboxId} \u7684\u65B0\u5185\u90E8\u534F\u4F5C\u6D88\u606F\uFF0C\u8BF7\u7528 list_mailbox/get_mailbox \u8BC4\u4F30\u5E76\u9886\u53D6\u3002`
43497
43504
  }
43498
- const visibleDepartments = departments.map((department) => {
43499
- const head = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
43500
- const executors = department.departmentMembers.filter((member) => member.id !== head?.id);
43501
- return {
43502
- id: department.id,
43503
- name: department.name,
43504
- sourceGoalId: department.sourceGoalId,
43505
- charter: department.charter,
43506
- workpath: department.workpath,
43507
- head: head ? {
43508
- id: head.id,
43509
- name: head.name,
43510
- mailBoxId: head.mailBoxId,
43511
- focusOn: head.focusOn
43512
- } : null,
43513
- executorCount: executors.length
43514
- };
43515
- });
43516
- return `[departmentList] \u627E\u5230\u90E8\u95E8: ${JSON.stringify(visibleDepartments)}`;
43505
+ });
43506
+ };
43507
+ var sendMessage2 = (fromMailboxId, toMailboxId, content, options) => {
43508
+ const db3 = createSqliteDB();
43509
+ const id = (0, import_node_crypto8.randomUUID)().slice(0, 8);
43510
+ const threadId = options?.threadId || id;
43511
+ const workItemContext = resolveWorkItemContext(fromMailboxId, toMailboxId, id, options);
43512
+ const stmt = db3.prepare(`insert into mailbox (
43513
+ id,
43514
+ to_mailbox_id,
43515
+ from_mailbox_id,
43516
+ content,
43517
+ send_time,
43518
+ status,
43519
+ origin_user_id,
43520
+ origin_platform,
43521
+ thread_id,
43522
+ parent_message_id,
43523
+ work_item_id,
43524
+ work_item_role,
43525
+ upstream_message_id
43526
+ ) values (?,?,?,?,?,?,?,?,?,?,?,?,?) `);
43527
+ let mailboxMsg = {
43528
+ id,
43529
+ toMailboxId,
43530
+ fromMailboxId,
43531
+ content,
43532
+ sendTime: (/* @__PURE__ */ new Date()).getTime(),
43533
+ status: "pending",
43534
+ originUserId: options?.originUserId,
43535
+ originPlatform: options?.originPlatform,
43536
+ threadId,
43537
+ parentMessageId: options?.parentMessageId,
43538
+ workItemId: workItemContext.workItemId,
43539
+ workItemRole: workItemContext.workItemRole,
43540
+ upstreamMessageId: workItemContext.upstreamMessageId
43541
+ };
43542
+ const result = stmt.run(
43543
+ mailboxMsg.id,
43544
+ mailboxMsg.toMailboxId,
43545
+ mailboxMsg.fromMailboxId,
43546
+ mailboxMsg.content,
43547
+ mailboxMsg.sendTime,
43548
+ mailboxMsg.status,
43549
+ mailboxMsg.originUserId || null,
43550
+ mailboxMsg.originPlatform || null,
43551
+ mailboxMsg.threadId || mailboxMsg.id,
43552
+ mailboxMsg.parentMessageId || null,
43553
+ mailboxMsg.workItemId || null,
43554
+ mailboxMsg.workItemRole || null,
43555
+ mailboxMsg.upstreamMessageId || null
43556
+ );
43557
+ recordMailboxEvent({
43558
+ messageId: mailboxMsg.id,
43559
+ mailboxId: toMailboxId,
43560
+ actorMailboxId: fromMailboxId,
43561
+ counterpartMailboxId: fromMailboxId,
43562
+ eventType: "message_sent",
43563
+ detail: {
43564
+ initialStatus: mailboxMsg.status,
43565
+ workItemId: mailboxMsg.workItemId ?? null,
43566
+ workItemRole: mailboxMsg.workItemRole ?? null,
43567
+ upstreamMessageId: mailboxMsg.upstreamMessageId ?? null,
43568
+ ...options?.auditDetail ?? {}
43569
+ },
43570
+ createdAt: mailboxMsg.sendTime
43571
+ });
43572
+ recordMailboxReceivedAgentEvent(mailboxMsg);
43573
+ queueMailboxInterruptIfRunning(mailboxMsg);
43574
+ enqueueCeoFollowupFromMailbox(mailboxMsg);
43575
+ return mailboxMsg;
43576
+ };
43577
+ var updateMailboxMessageStatus = (messageId, nextStatus, options) => {
43578
+ const db3 = createSqliteDB();
43579
+ const selectStmt2 = db3.prepare(
43580
+ `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, status
43581
+ FROM mailbox WHERE id = ?`
43582
+ );
43583
+ const existing = selectStmt2.get(messageId);
43584
+ if (!existing) return false;
43585
+ if (existing.status === nextStatus) return false;
43586
+ const allowedFrom = options?.fromStatus ? Array.isArray(options.fromStatus) ? options.fromStatus : [options.fromStatus] : null;
43587
+ if (allowedFrom && !allowedFrom.includes(existing.status)) {
43588
+ return false;
43517
43589
  }
43590
+ const updateStmt = db3.prepare(
43591
+ `UPDATE mailbox
43592
+ SET status = ?, updated_at = ?
43593
+ WHERE id = ? AND status = ?`
43594
+ );
43595
+ const result = updateStmt.run(nextStatus, Date.now(), messageId, existing.status);
43596
+ if (result.changes === 0) return false;
43597
+ recordMailboxStatusChange({
43598
+ messageId,
43599
+ mailboxId: existing.toMailboxId,
43600
+ previousStatus: existing.status,
43601
+ nextStatus,
43602
+ actorMailboxId: options?.actorMailboxId,
43603
+ counterpartMailboxId: options?.counterpartMailboxId || existing.fromMailboxId,
43604
+ reason: options?.reason
43605
+ });
43606
+ return true;
43518
43607
  };
43519
43608
 
43520
- // src/tools/tools/department/DepartmentMemberCreate.ts
43521
- var import_node_crypto9 = require("node:crypto");
43522
- var DESCRIPTION24 = `
43523
- \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u3002
43609
+ // src/tools/tools/department/DepartmentCreate.ts
43610
+ var DESCRIPTION20 = `
43611
+ \u521B\u5EFA\u90E8\u95E8\u3002Department \u662F\u516C\u53F8\u7EC4\u7EC7\u4E2D\u7684\u957F\u671F\u804C\u8D23\u5355\u5143\uFF0C\u4E0D\u662F\u4E00\u6B21\u6027\u9879\u76EE\u5C0F\u961F\u3002
43524
43612
 
43525
- \u7BA1\u7406\u6743\u9650\uFF1A
43526
- - CEO \u53EA\u80FD\u521B\u5EFA Department Head\u3002
43527
- - Department Head \u53EF\u4EE5\u5728\u81EA\u5DF1\u90E8\u95E8\u5185\u521B\u5EFA Executor\u3002
43528
- - Executor \u4E0D\u80FD\u521B\u5EFA\u6210\u5458\u3002
43613
+ \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43614
+ - \u5F53\u4E00\u4E2A\u590D\u6742\u3001\u957F\u671F\u3001\u4E13\u4E1A\u804C\u8D23\u9700\u8981\u7A33\u5B9A\u7EC4\u7EC7\u627F\u63A5\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43615
+ - sourceGoalId \u662F\u53EF\u9009\u6765\u6E90\u76EE\u6807\uFF1B\u90E8\u95E8\u804C\u8D23\u5E94\u5199\u5165 charter\uFF0C\u4E0D\u8981\u628A\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282\u5199\u8FDB charter\u3002
43616
+ - \u521B\u5EFA\u90E8\u95E8\u540E\uFF0CCEO \u5E94\u4F7F\u7528 department_member_create \u4EFB\u547D Department Head\uFF0C\u518D\u7528 department_communicate \u53D1\u9001\u672C\u6B21\u5177\u4F53\u4EFB\u52A1\u3002
43529
43617
  `;
43530
- var departmentMemberCreate = {
43531
- name: `department_member_create`,
43532
- description: DESCRIPTION24,
43618
+ var departmentCreate = {
43619
+ name: `department_create`,
43620
+ description: DESCRIPTION20,
43533
43621
  input_schema: {
43534
43622
  type: `object`,
43535
43623
  properties: {
43536
43624
  name: {
43537
43625
  type: `string`,
43538
- description: `\u90E8\u95E8\u6210\u5458\u540D\u79F0`
43626
+ description: `\u90E8\u95E8\u540D\u79F0`
43539
43627
  },
43540
- departmentName: {
43628
+ charter: {
43541
43629
  type: `string`,
43542
- description: `\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0\uFF0C\u4E0D\u8981\u81C6\u9020`
43630
+ description: `\u90E8\u95E8\u957F\u671F\u804C\u8D23\u8FB9\u754C\uFF0C\u4E0D\u80FD\u5199\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282`
43543
43631
  },
43544
- focusOn: {
43632
+ sourceGoalId: {
43545
43633
  type: `string`,
43546
- description: `\u8BE5\u6210\u5458\u7684\u957F\u671F\u5C97\u4F4D\u804C\u8D23\u4E0E\u884C\u4E3A\u8FB9\u754C\uFF0C\u4E0D\u8981\u5199\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282`
43634
+ description: `\u53EF\u9009\uFF1A\u521B\u5EFA\u8BE5\u90E8\u95E8\u65F6\u5173\u8054\u7684\u76EE\u6807 id`
43547
43635
  },
43548
- role: {
43636
+ workpath: {
43549
43637
  type: `string`,
43550
- enum: [`department_head`, `executor`],
43551
- description: `\u6210\u5458\u89D2\u8272\u3002CEO \u521B\u5EFA\u90E8\u95E8\u8D1F\u8D23\u4EBA\u65F6\u4F20 department_head\uFF1BDepartment Head \u521B\u5EFA\u6267\u884C\u8005\u65F6\u4F20 executor`
43638
+ description: `\u90E8\u95E8\u9879\u76EE\u5DE5\u4F5C\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u90E8\u95E8\u6210\u5458\u7684\u6587\u4EF6\u4FEE\u6539\u5E94\u9650\u5236\u5728\u6B64\u76EE\u5F55\u8303\u56F4\u5185`
43552
43639
  }
43553
43640
  },
43554
- required: [`name`, `departmentName`, `focusOn`]
43641
+ required: [`name`, `charter`, `workpath`]
43555
43642
  },
43556
- async execute(input, userRequest) {
43643
+ async execute(input) {
43557
43644
  const name = input.name;
43558
- const departmentName = input.departmentName;
43559
- const requestedRole = input.role;
43560
- const department = getDepartment(departmentName);
43561
- if (!department) return `[departmentMemberCreate] \u4E0D\u5B58\u5728\u540D\u79F0\u4E3A ${departmentName} \u7684\u90E8\u95E8`;
43562
- const focusOn = input.focusOn;
43563
- const currentMailboxId = userRequest?.departmentAgentId;
43564
- let role;
43565
- if (currentMailboxId) {
43566
- const currentMember = getDepartmentMemberByMailboxId(currentMailboxId);
43567
- if (!currentMember) {
43568
- return `[departmentMemberCreate] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${currentMailboxId}`;
43569
- }
43570
- if (currentMember.role !== "department_head") {
43571
- return `[departmentMemberCreate] \u53EA\u6709 Department Head \u53EF\u4EE5\u521B\u5EFA Executor`;
43572
- }
43573
- if (currentMember.departmentId !== department.id) {
43574
- return `[departmentMemberCreate] Department Head \u53EA\u80FD\u5728\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8\u5185\u521B\u5EFA\u6210\u5458`;
43575
- }
43576
- role = requestedRole ?? "executor";
43577
- if (role === "department_head") {
43578
- return `[departmentMemberCreate] \u90E8\u95E8\u5DF2\u7531\u5F53\u524D Department Head \u7BA1\u7406\uFF0C\u4E0D\u80FD\u7531 Department Head \u518D\u521B\u5EFA Department Head`;
43579
- }
43580
- } else {
43581
- role = requestedRole ?? "department_head";
43582
- if (role !== "department_head") {
43583
- return `[departmentMemberCreate] CEO \u53EA\u80FD\u521B\u5EFA Department Head\uFF1BExecutor \u5E94\u7531 Department Head \u521B\u5EFA`;
43584
- }
43645
+ const charter = input.charter;
43646
+ const sourceGoalId = input.sourceGoalId;
43647
+ const workpath = input.workpath;
43648
+ if (sourceGoalId && !getGoalById(sourceGoalId)) {
43649
+ return `[departmentCreate] \u4E0D\u5B58\u5728 id=${sourceGoalId} \u7684\u76EE\u6807`;
43585
43650
  }
43586
- let departmentMember = {
43651
+ let departmentDefinition = {
43587
43652
  id: (0, import_node_crypto9.randomUUID)().slice(0, 8),
43588
43653
  name,
43589
- departmentId: department.id,
43590
- mailBoxId: getMailBoxId(department.name, name),
43591
- workspaceId: getWorkspaceId(department.name, name),
43592
- role,
43593
- focusOn
43654
+ charter,
43655
+ sourceGoalId,
43656
+ workpath,
43657
+ departmentMembers: []
43594
43658
  };
43595
43659
  try {
43596
- departmentMember = createDepartmentMember(departmentMember);
43660
+ departmentDefinition = createDepartment(departmentDefinition);
43597
43661
  } catch (err) {
43598
- return `[departmentMemberCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u5931\u8D25: ${err.message}`;
43662
+ return `[departmentCreate] \u521B\u5EFA\u90E8\u95E8\u5931\u8D25: ${err.message}`;
43599
43663
  }
43600
- return `[departmentMemberCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u6210\u529F: ${JSON.stringify(departmentMember)}`;
43664
+ return `[departmentCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u529F: ${JSON.stringify(departmentDefinition)}`;
43601
43665
  }
43602
43666
  };
43603
43667
 
43604
- // src/tools/tools/department/DepartmentMemberDelete.ts
43605
- var DESCRIPTION25 = `
43606
- \u5220\u9664\u90E8\u95E8\u6210\u5458\u3002
43668
+ // src/tools/tools/department/DepartmentCommunicate.ts
43669
+ var CEO_MAILBOX_ID = `manager`;
43670
+ var MANAGER_ALIAS_PATTERN = /(^|::)(manager|main[-_\s]?manager|ceo)$/i;
43671
+ var looksLikePseudoManagerMailbox = (mailboxId) => {
43672
+ if (mailboxId === CEO_MAILBOX_ID) return true;
43673
+ if (!mailboxId.includes(`::`)) return false;
43674
+ return MANAGER_ALIAS_PATTERN.test(mailboxId.trim());
43675
+ };
43676
+ var buildManagerRoutingRejection = (activeContext) => [
43677
+ `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4F2A manager mailbox\u3002`,
43678
+ `Main Manager/CEO \u4E0D\u662F\u90E8\u95E8\u6210\u5458\uFF0C\u4E0D\u80FD\u7528 department_communicate \u53D1\u9001\u5230 manager\u3001Department::Manager\u3001CEO::Manager \u7B49\u5730\u5740\u3002`,
43679
+ activeContext ? `\u5982\u679C\u8981\u5411\u4E0A\u6E38 CEO \u6C47\u62A5\uFF0C\u8BF7\u8C03\u7528 reply_mailbox(message_id="${activeContext.id}", content="...") \u6B63\u5F0F\u56DE\u590D\u539F\u90AE\u4EF6\uFF1B\u82E5\u53EA\u662F\u540C\u6B65\u9636\u6BB5\u8FDB\u5C55\uFF0C\u8BF7\u8C03\u7528 mailbox_followup(message_id="${activeContext.id}", content="...", kind="progress")\u3002` : `\u5982\u679C\u8981\u5411\u4E0A\u6E38 CEO \u6C47\u62A5\uFF0C\u8BF7\u56DE\u5230\u539F manager \u90AE\u4EF6\uFF0C\u4F7F\u7528 reply_mailbox(message_id="\u539F\u90AE\u4EF6ID", content="...") \u6216 mailbox_followup(message_id="\u539F\u90AE\u4EF6ID", content="...", kind="progress")\u3002`
43680
+ ].join(`
43681
+ `);
43682
+ var getActiveMailboxContext = (actorMailboxId, messageId) => {
43683
+ if (!messageId) return null;
43684
+ const db3 = createSqliteDB();
43685
+ const row = db3.prepare(
43686
+ `SELECT
43687
+ id,
43688
+ from_mailbox_id as fromMailboxId,
43689
+ to_mailbox_id as toMailboxId,
43690
+ origin_user_id as originUserId,
43691
+ origin_platform as originPlatform,
43692
+ thread_id as threadId,
43693
+ work_item_id as workItemId,
43694
+ upstream_message_id as upstreamMessageId
43695
+ FROM mailbox
43696
+ WHERE id = ?
43697
+ AND (from_mailbox_id = ? OR to_mailbox_id = ?)`
43698
+ ).get(messageId, actorMailboxId, actorMailboxId);
43699
+ return row ?? null;
43700
+ };
43701
+ var DESCRIPTION21 = `
43702
+ \u5411\u90E8\u95E8\u6210\u5458\u53D1\u9001\u6D88\u606F\u6216\u6307\u4EE4\uFF0C\u662F\u516C\u53F8\u5185\u90E8\u5F02\u6B65\u534F\u4F5C\u901A\u4FE1\u6E20\u9053\u3002
43607
43703
 
43608
- \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43609
- - \u5FC5\u987B\u662F\u7528\u6237\u660E\u786E\u8981\u6C42\u5220\u9664\u6210\u5458\u65F6\u4F7F\u7528\u3002
43610
- - Department Head \u53EA\u80FD\u5220\u9664\u81EA\u5DF1\u90E8\u95E8\u5185\u7684 Executor\uFF0C\u4E0D\u80FD\u5220\u9664 Department Head\u3002
43611
- - CEO \u53EF\u4EE5\u5728\u7528\u6237\u660E\u786E\u8981\u6C42\u65F6\u5220\u9664\u90E8\u95E8\u6210\u5458\u3002
43704
+ \u9ED8\u8BA4\u7EC4\u7EC7\u6CBB\u7406\uFF1A
43705
+ - CEO \u9ED8\u8BA4\u901A\u8FC7 department_list \u53EA\u770B\u5230\u90E8\u95E8\u548C Department Head\uFF0C\u4E0D\u4E3B\u52A8\u66B4\u9732 Executor\u3002
43706
+ - Department Head \u9ED8\u8BA4\u7BA1\u7406\u672C\u90E8\u95E8\u6210\u5458\u3002
43707
+ - \u5982\u679C\u667A\u80FD\u4F53\u5DF2\u7ECF\u901A\u8FC7\u6B63\u5E38\u6C9F\u901A\u77E5\u9053\u4E86\u5176\u4ED6\u6210\u5458\u7684 mailbox \u5730\u5740\uFF0C\u5DE5\u5177\u4E0D\u4F1A\u786C\u6027\u963B\u6B62\u901A\u4FE1\uFF1B\u8FD9\u7C7B\u975E\u9ED8\u8BA4\u8DEF\u5F84\u4F1A\u5199\u5165\u5BA1\u8BA1\u4FE1\u606F\u3002
43708
+ - Main Manager/CEO \u7684 mailbox id \u56FA\u5B9A\u662F manager\u3002Department agent \u4E0D\u5E94\u4F7F\u7528 department_communicate \u7ED9 manager \u6216 Department::Manager \u53D1\u4FE1\uFF1B\u8981\u56DE\u590D\u4E0A\u6E38\u5FC5\u987B\u4F7F\u7528\u539F\u90AE\u4EF6\u7684 reply_mailbox \u6216 mailbox_followup\u3002
43709
+
43710
+ \u53C2\u6570\u8BF4\u660E\uFF1A
43711
+ - department_name + member_name: \u9ED8\u8BA4\u5BFB\u5740\u65B9\u5F0F\u3002
43712
+ - target_mailbox_id: \u53EF\u9009\uFF0C\u5DF2\u77E5\u5BF9\u65B9 mailbox \u5730\u5740\u65F6\u4F7F\u7528\uFF1B\u4F7F\u7528\u65F6\u5EFA\u8BAE\u586B\u5199 reason\u3002
43713
+ - content: \u8981\u53D1\u9001\u7684\u6D88\u606F\u5185\u5BB9\u3002
43612
43714
  `;
43613
- var departmentMemberDelete = {
43614
- name: `department_member_delete`,
43615
- description: DESCRIPTION25,
43715
+ var departmentCommunicate = {
43716
+ name: `department_communicate`,
43717
+ description: DESCRIPTION21,
43616
43718
  input_schema: {
43617
43719
  type: `object`,
43618
43720
  properties: {
43619
43721
  department_name: {
43620
43722
  type: `string`,
43621
- description: `\u5F85\u5220\u9664\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0`
43723
+ description: `\u76EE\u6807\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0\uFF1B\u4F7F\u7528 target_mailbox_id \u65F6\u53EF\u7701\u7565`
43622
43724
  },
43623
- member_id: {
43725
+ member_name: {
43624
43726
  type: `string`,
43625
- description: `\u5F85\u5220\u9664\u6210\u5458 id`
43727
+ description: `\u76EE\u6807\u6210\u5458\u540D\u79F0\uFF1B\u4F7F\u7528 target_mailbox_id \u65F6\u53EF\u7701\u7565`
43728
+ },
43729
+ target_mailbox_id: {
43730
+ type: `string`,
43731
+ description: `\u53EF\u9009\uFF1A\u5DF2\u77E5\u5BF9\u65B9 mailbox \u5730\u5740\u65F6\u76F4\u63A5\u586B\u5199\uFF0C\u4F8B\u5982 Department::Member`
43732
+ },
43733
+ content: {
43734
+ type: `string`,
43735
+ description: `\u8981\u6295\u9001\u7ED9\u5BF9\u65B9\u7684\u4FE1\u606F\u5185\u5BB9`
43736
+ },
43737
+ reason: {
43738
+ type: `string`,
43739
+ description: `\u53EF\u9009\uFF1A\u8DE8\u9ED8\u8BA4\u7EC4\u7EC7\u94FE\u8DEF\u6C9F\u901A\u7684\u539F\u56E0\uFF0C\u4FBF\u4E8E\u5BA1\u8BA1`
43626
43740
  }
43627
43741
  },
43628
- required: [`department_name`, `member_id`]
43742
+ required: [`content`]
43629
43743
  },
43630
43744
  async execute(input, userRequest) {
43631
- const departmentName = input.department_name;
43632
- const memberId = input.member_id;
43633
- const department = getDepartment(departmentName);
43634
- if (!department) return `[departmentMemberDelete] \u4E0D\u5B58\u5728\u540D\u79F0\u4E3A ${departmentName} \u7684\u90E8\u95E8`;
43635
- const member = getDepartmentMember(departmentName, memberId);
43636
- if (!member) return `[departmentMemberDelete] \u627E\u4E0D\u5230\u6210\u5458: ${memberId}`;
43637
- const currentMailboxId = userRequest?.departmentAgentId;
43638
- if (currentMailboxId) {
43639
- const currentMember = getDepartmentMemberByMailboxId(currentMailboxId);
43640
- if (!currentMember) return `[departmentMemberDelete] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${currentMailboxId}`;
43641
- if (currentMember.role !== "department_head") return `[departmentMemberDelete] \u53EA\u6709 Department Head \u53EF\u4EE5\u5220\u9664 Executor`;
43642
- if (currentMember.departmentId !== department.id) return `[departmentMemberDelete] Department Head \u53EA\u80FD\u7BA1\u7406\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8`;
43643
- if (member.role === "department_head") return `[departmentMemberDelete] Department Head \u4E0D\u80FD\u5220\u9664 Department Head`;
43745
+ if (!userRequest) {
43746
+ throw new Error(`[departmentCommunicate] userRequest\u4E0D\u80FD\u4E3A\u7A7A`);
43644
43747
  }
43645
- try {
43646
- deleteDepartmentMemberById(departmentName, memberId);
43647
- const mailboxId = getMailBoxId(departmentName, member.name);
43648
- const cancelled = cancelMailboxMessages(mailboxId);
43649
- if (cancelled > 0) {
43650
- console.log(`[departmentMemberDelete] \u5DF2\u53D6\u6D88 ${cancelled} \u6761\u6B8B\u7559 mailbox \u6D88\u606F: ${mailboxId}`);
43748
+ const departmentName = input.department_name;
43749
+ const memberName = input.member_name;
43750
+ const targetMailboxId = input.target_mailbox_id;
43751
+ const content = input.content;
43752
+ const reason = input.reason;
43753
+ let fromMailboxId;
43754
+ const departmentAgentId = userRequest.departmentAgentId;
43755
+ const actorMember = departmentAgentId ? getDepartmentMemberByMailboxId(
43756
+ departmentAgentId.includes(`::`) ? departmentAgentId : getMailBoxIdFromDepartmentMemberId(departmentAgentId) ?? departmentAgentId
43757
+ ) : null;
43758
+ if (departmentAgentId) {
43759
+ const resolved = departmentAgentId.includes(`::`) ? departmentAgentId : getMailBoxIdFromDepartmentMemberId(departmentAgentId);
43760
+ if (!resolved) {
43761
+ return `[departmentCommunicate] \u65E0\u6CD5\u83B7\u53D6\u5F53\u524D agent \u7684 mailboxId\uFF0CdepartmentAgentId: ${departmentAgentId}`;
43651
43762
  }
43652
- } catch (err) {
43653
- return `[departmentMemberDelete] \u5220\u9664\u90E8\u95E8\u6210\u5458\u5931\u8D25: ${err.message}`;
43763
+ fromMailboxId = resolved;
43764
+ } else {
43765
+ fromMailboxId = CEO_MAILBOX_ID;
43654
43766
  }
43655
- return `[departmentMemberDelete] \u5220\u9664\u90E8\u95E8\u6210\u5458\u6210\u529F: ${JSON.stringify(memberId)}`;
43656
- }
43657
- };
43658
-
43659
- // src/tools/tools/department/DepartmentMemberList.ts
43660
- var DESCRIPTION26 = `
43661
- \u67E5\u770B\u5F53\u524D Department Head \u6240\u5C5E\u90E8\u95E8\u7684\u6210\u5458\u76EE\u5F55\u3002
43662
-
43663
- \u4F7F\u7528\u8FB9\u754C\uFF1A
43664
- - \u4EC5 Department Head \u53EF\u4EE5\u4F7F\u7528\u3002
43665
- - CEO \u9ED8\u8BA4\u901A\u8FC7 department_list \u53EA\u770B\u5230\u90E8\u95E8\u4E0E\u8D1F\u8D23\u4EBA\u6458\u8981\u3002
43666
- - Department Head \u53EA\u80FD\u67E5\u770B\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8\u7684\u6210\u5458\uFF0C\u7528\u4E8E\u590D\u7528\u5DF2\u6709\u6210\u5458\u3001\u5224\u65AD\u662F\u5426\u9700\u8981\u521B\u5EFA\u65B0\u6210\u5458\u3001\u4E86\u89E3\u6210\u5458\u804C\u8D23\u3002
43667
- `;
43668
- var departmentMemberList = {
43669
- name: `department_member_list`,
43670
- description: DESCRIPTION26,
43671
- input_schema: {
43672
- type: `object`,
43673
- properties: {},
43674
- required: []
43675
- },
43676
- async execute(input, userRequest) {
43677
- const mailboxId = userRequest?.departmentAgentId;
43678
- if (!mailboxId) {
43679
- return `[departmentMemberList] \u53EA\u6709 Department Head \u53EF\u4EE5\u67E5\u770B\u90E8\u95E8\u6210\u5458\u76EE\u5F55`;
43767
+ const activeContext = departmentAgentId ? getActiveMailboxContext(fromMailboxId, userRequest.requestId) : null;
43768
+ let toMailboxId = targetMailboxId;
43769
+ let targetMember = targetMailboxId ? getDepartmentMemberByMailboxId(targetMailboxId) : null;
43770
+ if (departmentAgentId && targetMailboxId && looksLikePseudoManagerMailbox(targetMailboxId)) {
43771
+ return buildManagerRoutingRejection(activeContext);
43680
43772
  }
43681
- const currentMember = getDepartmentMemberByMailboxId(mailboxId);
43682
- if (!currentMember) {
43683
- return `[departmentMemberList] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${mailboxId}`;
43773
+ if (departmentAgentId && targetMailboxId && !targetMember) {
43774
+ return [
43775
+ `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4E0D\u5B58\u5728\u7684 mailbox: ${targetMailboxId}`,
43776
+ `Department agent \u4F7F\u7528 target_mailbox_id \u65F6\uFF0C\u76EE\u6807\u5FC5\u987B\u662F\u5DF2\u5B58\u5728\u7684\u90E8\u95E8\u6210\u5458 mailbox\u3002`,
43777
+ `\u5982\u679C\u4F60\u60F3\u56DE\u590D\u4E0A\u6E38 CEO/Main Manager\uFF0C\u8BF7\u4E0D\u8981\u4F2A\u9020 manager \u5730\u5740\uFF1B\u8BF7\u4F7F\u7528\u539F\u90AE\u4EF6\u7684 reply_mailbox \u6216 mailbox_followup\u3002`
43778
+ ].join(`
43779
+ `);
43684
43780
  }
43685
- if (currentMember.role !== "department_head") {
43686
- return `[departmentMemberList] \u5F53\u524D\u6210\u5458\u4E0D\u662F Department Head\uFF0C\u4E0D\u80FD\u67E5\u770B\u5B8C\u6574\u90E8\u95E8\u6210\u5458\u76EE\u5F55`;
43781
+ if (!toMailboxId) {
43782
+ if (!departmentName || !memberName) {
43783
+ return `[departmentCommunicate] department_name/member_name \u6216 target_mailbox_id \u81F3\u5C11\u9700\u8981\u63D0\u4F9B\u4E00\u79CD`;
43784
+ }
43785
+ targetMember = getDepartmentMemberByName(departmentName, memberName);
43786
+ if (!targetMember) {
43787
+ return `[departmentCommunicate] \u4E0D\u5B58\u5728 ${departmentName} \u90E8\u95E8\u7684\u6210\u5458: ${memberName}`;
43788
+ }
43789
+ toMailboxId = getMailBoxId(departmentName, memberName);
43790
+ }
43791
+ const isCrossDepartment = Boolean(actorMember && targetMember && actorMember.departmentId !== targetMember.departmentId);
43792
+ const isKnownAddressPath = Boolean(targetMailboxId);
43793
+ const options = {
43794
+ auditDetail: {
43795
+ communicationPolicy: "allowed_known_address",
43796
+ isKnownAddressPath,
43797
+ isCrossDepartment,
43798
+ actorRole: actorMember?.role ?? "ceo",
43799
+ targetRole: targetMember?.role ?? "unknown",
43800
+ reason: reason ?? null
43801
+ }
43802
+ };
43803
+ if (!departmentAgentId) {
43804
+ options.originUserId = userRequest.userId;
43805
+ options.originPlatform = userRequest.platform;
43806
+ } else {
43807
+ if (activeContext) {
43808
+ options.originUserId = activeContext.originUserId;
43809
+ options.originPlatform = activeContext.originPlatform;
43810
+ options.threadId = activeContext.threadId || activeContext.id;
43811
+ options.parentMessageId = activeContext.id;
43812
+ options.workItemId = activeContext.workItemId;
43813
+ options.upstreamMessageId = activeContext.upstreamMessageId;
43814
+ }
43687
43815
  }
43688
- const department = getDepartmentById(currentMember.departmentId);
43689
- if (!department) {
43690
- return `[departmentMemberList] \u627E\u4E0D\u5230\u5F53\u524D\u6210\u5458\u6240\u5C5E\u90E8\u95E8: ${currentMember.departmentId}`;
43816
+ try {
43817
+ sendMessage2(fromMailboxId, toMailboxId, content, options);
43818
+ } catch (err) {
43819
+ return `[departmentCommunicate] \u53D1\u9001\u6D88\u606F\u5931\u8D25: ${err.message}`;
43691
43820
  }
43692
- const members = department.departmentMembers.map((member) => ({
43693
- id: member.id,
43694
- name: member.name,
43695
- role: member.role ?? "executor",
43696
- mailBoxId: member.mailBoxId,
43697
- workspaceId: member.workspaceId,
43698
- focusOn: member.focusOn
43699
- }));
43700
- return `[departmentMemberList] ${department.name} \u90E8\u95E8\u6210\u5458\u76EE\u5F55: ${JSON.stringify(members)}`;
43821
+ return `[departmentCommunicate] \u6D88\u606F\u5DF2\u53D1\u9001\u5230 ${toMailboxId}`;
43701
43822
  }
43702
43823
  };
43703
43824
 
43704
- // src/tools/tools/department/CheckDepartmentReplies.ts
43705
- var DESCRIPTION27 = `
43706
- \u67E5\u770B Department Head \u6216\u90E8\u95E8\u6210\u5458\u53D1\u7ED9 CEO \u7684\u5DE5\u4F5C\u56DE\u590D\u3002
43825
+ // src/tools/tools/department/DepartmentDelete.ts
43826
+ var DESCRIPTION22 = `
43827
+ \u5220\u9664\u90E8\u95E8\u3002
43707
43828
 
43708
- \u5F53 CEO \u5411 Department Head \u6D3E\u53D1\u4EFB\u52A1\u540E\uFF0C\u5BF9\u65B9\u5B8C\u6210\u5DE5\u4F5C\u4F1A\u901A\u8FC7 mailbox \u56DE\u4FE1\u3002\u4F7F\u7528\u6B64\u5DE5\u5177\u53EF\u4EE5\u67E5\u770B\u672A\u8BFB\u7684\u90E8\u95E8\u56DE\u4FE1\u5185\u5BB9\uFF0C\u4E86\u89E3\u5DE5\u4F5C\u8FDB\u5C55\u548C\u6210\u679C\u3002
43829
+ \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43830
+ - \u89E3\u6563\u90E8\u95E8\u662F\u4E0D\u53EF\u9006\u64CD\u4F5C\uFF0C\u5FC5\u987B\u5148\u5F81\u5F97\u7528\u6237\u660E\u786E\u786E\u8BA4\u3002
43831
+ - \u9996\u6B21\u8C03\u7528 confirmed \u5FC5\u987B\u4E3A false \u6216\u4E0D\u4F20\uFF1B\u5F97\u5230\u7528\u6237\u660E\u786E\u540C\u610F\u540E\uFF0C\u518D\u4EE5 confirmed=true \u8C03\u7528\u3002
43832
+ - \u4E25\u7981\u5728\u672A\u5F97\u5230\u7528\u6237\u660E\u786E\u786E\u8BA4\u7684\u60C5\u51B5\u4E0B\u76F4\u63A5\u4F20 confirmed=true\u3002
43709
43833
  `;
43710
- var checkDepartmentReplies = {
43711
- name: `check_department_replies`,
43712
- description: DESCRIPTION27,
43834
+ var departmentDelete = {
43835
+ name: `department_delete`,
43836
+ description: DESCRIPTION22,
43713
43837
  input_schema: {
43714
43838
  type: `object`,
43715
43839
  properties: {
43716
- from_member: {
43840
+ name: {
43717
43841
  type: `string`,
43718
- description: `\u53EF\u9009\uFF1A\u53EA\u67E5\u770B\u6307\u5B9A mailboxId \u7684\u56DE\u4FE1\uFF0C\u4E0D\u586B\u5219\u67E5\u770B\u6240\u6709\u56DE\u4FE1`
43842
+ description: `\u8981\u5220\u9664\u7684\u90E8\u95E8\u540D\u79F0`
43843
+ },
43844
+ confirmed: {
43845
+ type: `boolean`,
43846
+ description: `\u7528\u6237\u662F\u5426\u5DF2\u660E\u786E\u786E\u8BA4\u89E3\u6563\u8BE5\u90E8\u95E8`
43719
43847
  }
43720
43848
  },
43721
- required: []
43849
+ required: [`name`]
43722
43850
  },
43723
43851
  async execute(input) {
43724
- const db3 = createSqliteDB();
43725
- const fromMember = input.from_member;
43726
- let msgs;
43727
- if (fromMember) {
43728
- const stmt = db3.prepare(
43729
- `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
43730
- FROM mailbox
43731
- WHERE to_mailbox_id = 'manager' AND from_mailbox_id = ? AND status in ('pending', 'processing')
43732
- ORDER BY send_time ASC`
43733
- );
43734
- msgs = stmt.all(fromMember);
43735
- } else {
43736
- const stmt = db3.prepare(
43737
- `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
43738
- FROM mailbox
43739
- WHERE to_mailbox_id = 'manager' AND status in ('pending', 'processing')
43740
- ORDER BY send_time ASC`
43741
- );
43742
- msgs = stmt.all();
43743
- }
43744
- if (msgs.length === 0) {
43745
- return [
43746
- `[checkDepartmentReplies] \u56E2\u961F\u8FD9\u4F1A\u513F\u8FD8\u6CA1\u56DE\u4FE1\uFF0C\u591A\u534A\u662F\u8D1F\u8D23\u4EBA\u4ECD\u5728\u5904\u7406\uFF0C\u8FC7\u4F1A\u513F\u518D\u6765\u770B\u3002`,
43747
- ``,
43748
- `\u51E0\u53E5\u63D0\u9192\uFF1A`,
43749
- `- \u8FD9\u4E0D\u4EE3\u8868\u8981\u4F60\u4EB2\u81EA\u4E0B\u573A read/bash/\u6D4B\u8BD5/\u6539\u4EE3\u7801\u2014\u2014\u56E2\u961F\u6CA1\u56DE\uFF0C\u4E0D\u662F\u4F60\u8BE5\u63A5\u624B\u7684\u4FE1\u53F7\u3002`,
43750
- `- \u8001\u677F\u8981\u662F\u6B63\u7B49\u7740\uFF0C\u5148\u7528 send_message \u5982\u5B9E\u8DDF\u4ED6\u8BF4\u4E00\u53E5\u201C\u8D1F\u8D23\u4EBA\u8FD8\u5728\u5904\u7406\uFF0C\u6211\u62FF\u5230\u7ED3\u679C\u5C31\u540C\u6B65\u201D\u3002`,
43751
- `- \u5982\u679C\u4EFB\u52A1\u521A\u6D3E\u51FA\u53BB\uFF0C\u4E0D\u8981\u5728\u540C\u4E00\u8F6E\u9A6C\u4E0A\u50AC\u529E\uFF1B\u7ED9\u8D1F\u8D23\u4EBA\u5408\u7406\u5904\u7406\u65F6\u95F4\u3002`,
43752
- `- \u5DF2\u7ECF\u7B49\u4E86\u4E00\u9635\u3001\u8001\u677F\u53C8\u8FFD\u95EE\uFF0C\u6216\u53D1\u73B0\u660E\u786E\u98CE\u9669\u65F6\uFF0C\u518D\u7528 mailbox_followup \u6216 department_communicate \u53BB\u95EE\u5BF9\u5E94 Department Head \u771F\u5B9E\u8FDB\u5C55\u3001\u5361\u5728\u54EA\u3001\u5927\u6982\u4EC0\u4E48\u65F6\u5019\u597D\u3002`,
43753
- `- \u522B\u5728\u8FD9\u4E00\u8F6E\u91CC\u53CD\u590D\u7A7A\u67E5 check_department_replies\uFF1B\u7B49\u56E2\u961F\u56DE\u4FE1\u3001\u8001\u677F\u8FFD\u95EE\u6216\u50AC\u529E\u6709\u4E86\u56DE\u97F3\uFF0C\u518D\u6765\u770B\u3002`
43754
- ].join("\n");
43755
- }
43756
- const updateStmt = db3.prepare(`UPDATE mailbox SET status = 'read' WHERE id = ?`);
43757
- for (const msg of msgs) {
43758
- updateStmt.run(msg.id);
43759
- }
43760
- const replies = msgs.map((msg, i) => {
43761
- const time = new Date(msg.sendTime).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
43762
- return `--- \u56DE\u4FE1 ${i + 1} ---
43763
- id: ${msg.id}
43764
- \u6765\u81EA: ${msg.fromMailboxId}
43765
- \u65F6\u95F4: ${time}
43766
- \u72B6\u6001: ${msg.status}
43767
- \u5185\u5BB9:
43768
- ${msg.content}`;
43769
- }).join("\n\n");
43770
- return `[checkDepartmentReplies] \u6536\u5230 ${msgs.length} \u6761\u90E8\u95E8\u56DE\u4FE1\uFF1A
43771
-
43772
- ${replies}`;
43773
- }
43774
- };
43775
-
43776
- // src/department/learning.ts
43777
- var import_node_fs4 = require("node:fs");
43778
- var import_node_path13 = __toESM(require("node:path"));
43779
- var import_node_crypto10 = require("node:crypto");
43780
-
43781
- // src/skill/SkillValidator.ts
43782
- var import_node_fs3 = require("node:fs");
43783
- var import_node_path12 = __toESM(require("node:path"));
43784
- var SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
43785
- var REQUIRED_SECTIONS = [
43786
- /(^|\n)##\s+(when to use|何时使用|trigger|triggers)\b/i,
43787
- /(^|\n)##\s+(steps|workflow|procedure|执行步骤|工作流)\b/i
43788
- ];
43789
- var stripYamlQuotes = (value) => {
43790
- const trimmed = value.trim();
43791
- if (trimmed.startsWith(`"`) && trimmed.endsWith(`"`) || trimmed.startsWith(`'`) && trimmed.endsWith(`'`)) {
43792
- return trimmed.slice(1, -1).trim();
43793
- }
43794
- return trimmed;
43795
- };
43796
- var addError = (errors, code, message) => {
43797
- errors.push({ code, message });
43798
- };
43799
- var addWarning = (warnings, code, message) => {
43800
- warnings.push({ code, message });
43801
- };
43802
- var isValidSkillName = (name) => {
43803
- return SKILL_NAME_PATTERN.test(name) && !name.includes(`--`);
43804
- };
43805
- var assertSafeSkillTarget = (rootDir, skillName) => {
43806
- if (!isValidSkillName(skillName)) {
43807
- throw new Error(`Invalid skill name "${skillName}". Use lowercase letters, digits, and single hyphens only.`);
43808
- }
43809
- const root = import_node_path12.default.resolve(rootDir);
43810
- const target = import_node_path12.default.resolve(root, skillName);
43811
- if (target !== import_node_path12.default.join(root, skillName) || !target.startsWith(root + import_node_path12.default.sep)) {
43812
- throw new Error(`Invalid skill install target for "${skillName}".`);
43813
- }
43814
- return target;
43815
- };
43816
- var parseSkillDocument = (skillMd) => {
43817
- const fmMatch = skillMd.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
43818
- if (!fmMatch) return null;
43819
- const frontmatter = {};
43820
- for (const rawLine of fmMatch[1].split(/\r?\n/)) {
43821
- const line = rawLine.trim();
43822
- if (!line || line.startsWith(`#`)) continue;
43823
- const match2 = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
43824
- if (!match2) {
43825
- frontmatter[`__invalid__${Object.keys(frontmatter).length}`] = line;
43826
- continue;
43827
- }
43828
- frontmatter[match2[1]] = stripYamlQuotes(match2[2]);
43829
- }
43830
- return {
43831
- name: frontmatter.name ?? ``,
43832
- description: frontmatter.description ?? ``,
43833
- body: skillMd.slice(fmMatch[0].length).trim(),
43834
- frontmatter
43835
- };
43836
- };
43837
- var validateSkillDocument = (input) => {
43838
- const errors = [];
43839
- const warnings = [];
43840
- const skillMd = input.skillMd;
43841
- if (!skillMd || !skillMd.trim()) {
43842
- addError(errors, `empty_skill_md`, `SKILL.md content is required.`);
43843
- return { ok: false, errors, warnings };
43844
- }
43845
- const parsed = parseSkillDocument(skillMd);
43846
- if (!parsed) {
43847
- addError(errors, `missing_frontmatter`, `SKILL.md must start with YAML frontmatter delimited by "---".`);
43848
- return { ok: false, errors, warnings };
43849
- }
43850
- const invalidFrontmatterLines = Object.keys(parsed.frontmatter).filter((key) => key.startsWith(`__invalid__`));
43851
- if (invalidFrontmatterLines.length > 0) {
43852
- addError(errors, `invalid_frontmatter`, `Frontmatter must contain simple "key: value" lines.`);
43853
- }
43854
- if (!parsed.name) {
43855
- addError(errors, `missing_name`, `Frontmatter field "name" is required.`);
43856
- } else if (!isValidSkillName(parsed.name)) {
43857
- addError(errors, `invalid_name`, `Skill name "${parsed.name}" must be kebab-case, under 64 characters, and path-safe.`);
43858
- }
43859
- if (!parsed.description) {
43860
- addError(errors, `missing_description`, `Frontmatter field "description" is required.`);
43861
- } else if (parsed.description.length > 500) {
43862
- addWarning(warnings, `long_description`, `Skill description is long; keep trigger metadata concise.`);
43863
- }
43864
- if (input.skillName && parsed.name && input.skillName !== parsed.name) {
43865
- addError(errors, `name_mismatch`, `Input skillName "${input.skillName}" must match frontmatter name "${parsed.name}".`);
43866
- }
43867
- if (input.description && parsed.description && input.description !== parsed.description) {
43868
- addError(errors, `description_mismatch`, `Input description must match frontmatter description.`);
43869
- }
43870
- if (!parsed.body) {
43871
- addError(errors, `empty_body`, `SKILL.md body is required.`);
43872
- } else {
43873
- if (!/(^|\n)#\s+\S/.test(parsed.body)) {
43874
- addWarning(warnings, `missing_title`, `Skill body should start with a Markdown H1 title.`);
43875
- }
43876
- for (const sectionPattern of REQUIRED_SECTIONS) {
43877
- if (!sectionPattern.test(parsed.body)) {
43878
- addWarning(warnings, `missing_recommended_section`, `Skill body should include when-to-use and execution workflow sections.`);
43879
- break;
43880
- }
43881
- }
43882
- }
43883
- if (input.baseDir) {
43884
- const normalizedBase = import_node_path12.default.resolve(input.baseDir);
43885
- for (const match2 of parsed.body.matchAll(/\]\(([^)]+)\)/g)) {
43886
- const href = match2[1].trim();
43887
- if (!href || /^[a-z][a-z0-9+.-]*:/i.test(href) || href.startsWith(`#`)) continue;
43888
- const target = import_node_path12.default.resolve(normalizedBase, href.split(`#`)[0]);
43889
- if (!target.startsWith(normalizedBase + import_node_path12.default.sep) && target !== normalizedBase) {
43890
- addError(errors, `unsafe_reference`, `Relative link "${href}" escapes the skill directory.`);
43891
- } else if (!(0, import_node_fs3.existsSync)(target)) {
43892
- addError(errors, `missing_reference`, `Relative link "${href}" does not exist in the skill directory.`);
43893
- }
43852
+ const name = input.name;
43853
+ const confirmed = input.confirmed ?? false;
43854
+ if (!confirmed) {
43855
+ return `[departmentDelete] \u89E3\u6563\u90E8\u95E8\u662F\u4E0D\u53EF\u9006\u64CD\u4F5C\uFF0C\u8BF7\u5148\u7528 send_message \u5411\u7528\u6237\u786E\u8BA4"\u662F\u5426\u89E3\u6563\u90E8\u95E8 ${name}"\uFF0C\u5F97\u5230\u7528\u6237\u660E\u786E\u540C\u610F\u540E\uFF0C\u518D\u4EE5 confirmed=true \u91CD\u65B0\u8C03\u7528\u672C\u5DE5\u5177\u3002`;
43894
43856
  }
43857
+ try {
43858
+ const members = listDepartmentMembers(name);
43859
+ let totalCancelled = 0;
43860
+ for (const member of members) {
43861
+ const mailboxId = getMailBoxId(name, member.name);
43862
+ totalCancelled += cancelMailboxMessages(mailboxId);
43863
+ }
43864
+ if (totalCancelled > 0) {
43865
+ console.log(`[departmentDelete] \u5DF2\u53D6\u6D88\u90E8\u95E8 ${name} \u7684 ${totalCancelled} \u6761\u6B8B\u7559 mailbox \u6D88\u606F`);
43866
+ }
43867
+ deleteDepartment(name);
43868
+ } catch (err) {
43869
+ return `[departmentDelete] \u5220\u9664\u90E8\u95E8\u5931\u8D25: ${err.message}`;
43870
+ }
43871
+ return `[departmentDelete] \u5220\u9664\u90E8\u95E8\u6210\u529F: ${name}`;
43895
43872
  }
43896
- return {
43897
- ok: errors.length === 0,
43898
- skill: parsed,
43899
- errors,
43900
- warnings
43901
- };
43902
- };
43903
- var mergeIssues = (target, source) => {
43904
- target.errors.push(...source.errors);
43905
- target.warnings.push(...source.warnings);
43906
- };
43907
- var validateScriptsDirectory = (skillDir, warnings) => {
43908
- const scriptsDir = import_node_path12.default.join(skillDir, `scripts`);
43909
- if (!(0, import_node_fs3.existsSync)(scriptsDir)) return;
43910
- const entries = (0, import_node_fs3.readdirSync)(scriptsDir, { withFileTypes: true }).filter((entry) => entry.isFile());
43911
- if (entries.length === 0) {
43912
- addWarning(warnings, `empty_scripts_dir`, `scripts/ exists but contains no files.`);
43913
- }
43914
- };
43915
- var validateSkillDirectory = (skillDir, options = {}) => {
43916
- const errors = [];
43917
- const warnings = [];
43918
- const normalizedSkillDir = import_node_path12.default.resolve(skillDir);
43919
- const skillMdPath = import_node_path12.default.join(normalizedSkillDir, `SKILL.md`);
43920
- if (!(0, import_node_fs3.existsSync)(normalizedSkillDir) || !(0, import_node_fs3.statSync)(normalizedSkillDir).isDirectory()) {
43921
- addError(errors, `missing_skill_dir`, `Skill directory does not exist: ${normalizedSkillDir}`);
43922
- return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
43923
- }
43924
- if (!(0, import_node_fs3.existsSync)(skillMdPath) || !(0, import_node_fs3.statSync)(skillMdPath).isFile()) {
43925
- addError(errors, `missing_skill_md`, `Skill directory must contain SKILL.md.`);
43926
- return { ok: false, errors, warnings, skillDir: normalizedSkillDir, skillMdPath };
43927
- }
43928
- const raw2 = (0, import_node_fs3.readFileSync)(skillMdPath, `utf-8`);
43929
- const documentResult = validateSkillDocument({
43930
- skillName: options.expectedName ?? import_node_path12.default.basename(normalizedSkillDir),
43931
- skillMd: raw2,
43932
- baseDir: normalizedSkillDir
43933
- });
43934
- mergeIssues({ errors, warnings }, documentResult);
43935
- validateScriptsDirectory(normalizedSkillDir, warnings);
43936
- return {
43937
- ok: errors.length === 0,
43938
- skill: documentResult.skill,
43939
- errors,
43940
- warnings,
43941
- skillDir: normalizedSkillDir,
43942
- skillMdPath
43943
- };
43944
43873
  };
43945
- var smokeTestSkillDirectory = (skillDir, options = {}) => {
43946
- const directoryResult = validateSkillDirectory(skillDir, options);
43947
- const errors = [...directoryResult.errors];
43948
- const warnings = [...directoryResult.warnings];
43949
- let detail;
43950
- if (directoryResult.ok && directoryResult.skill) {
43951
- detail = `Base directory for this skill: ${directoryResult.skillDir}
43952
43874
 
43953
- ${directoryResult.skill.body}`;
43954
- if (!detail.includes(directoryResult.skillDir)) {
43955
- addError(errors, `smoke_missing_base_dir`, `Skill detail does not include its base directory.`);
43956
- }
43957
- if (!detail.includes(directoryResult.skill.body)) {
43958
- addError(errors, `smoke_missing_body`, `Skill detail does not include the SKILL.md body.`);
43959
- }
43960
- if (detail.length < 80) {
43961
- addWarning(warnings, `smoke_short_detail`, `Skill detail is unusually short.`);
43875
+ // src/tools/tools/department/DepartmentList.ts
43876
+ var DESCRIPTION23 = `
43877
+ \u5217\u51FA\u6240\u6709\u90E8\u95E8\u3002\u8BE5\u5DE5\u5177\u9762\u5411 CEO\uFF0C\u53EA\u8FD4\u56DE\u90E8\u95E8\u4E0E Department Head \u4FE1\u606F\uFF0C\u4E0D\u66B4\u9732 Executor \u660E\u7EC6\u3002
43878
+
43879
+ \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
43880
+ - \u5F53\u7528\u6237\u8981\u6C42\u5217\u51FA\u6240\u6709\u90E8\u95E8\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43881
+ - \u5F53\u590D\u6742\u4EFB\u52A1\u9700\u8981\u5224\u65AD\u73B0\u6709\u90E8\u95E8\u804C\u8D23\u65F6\u8C03\u7528\u6B64\u5DE5\u5177\u3002
43882
+ `;
43883
+ var departmentList = {
43884
+ name: `department_list`,
43885
+ description: DESCRIPTION23,
43886
+ input_schema: {
43887
+ type: `object`,
43888
+ properties: {},
43889
+ required: []
43890
+ },
43891
+ async execute() {
43892
+ const departments = listDepartments();
43893
+ if (departments.length === 0) {
43894
+ return `[departmentList] \u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u90E8\u95E8\u3002`;
43962
43895
  }
43896
+ const visibleDepartments = departments.map((department) => {
43897
+ const head = department.headMemberId ? department.departmentMembers.find((member) => member.id === department.headMemberId) : department.departmentMembers.find((member) => member.role === "department_head");
43898
+ const executors = department.departmentMembers.filter((member) => member.id !== head?.id);
43899
+ return {
43900
+ id: department.id,
43901
+ name: department.name,
43902
+ sourceGoalId: department.sourceGoalId,
43903
+ charter: department.charter,
43904
+ workpath: department.workpath,
43905
+ head: head ? {
43906
+ id: head.id,
43907
+ name: head.name,
43908
+ mailBoxId: head.mailBoxId,
43909
+ focusOn: head.focusOn
43910
+ } : null,
43911
+ executorCount: executors.length
43912
+ };
43913
+ });
43914
+ return `[departmentList] \u627E\u5230\u90E8\u95E8: ${JSON.stringify(visibleDepartments)}`;
43963
43915
  }
43964
- return {
43965
- ...directoryResult,
43966
- ok: errors.length === 0,
43967
- errors,
43968
- warnings,
43969
- detail
43970
- };
43971
- };
43972
- var formatSkillValidationIssues = (result) => {
43973
- const lines = [
43974
- ...result.errors.map((issue) => `- [${issue.code}] ${issue.message}`),
43975
- ...result.warnings.map((issue) => `- [warning:${issue.code}] ${issue.message}`)
43976
- ];
43977
- return lines.join(`
43978
- `);
43979
43916
  };
43980
43917
 
43981
- // src/department/learning.ts
43982
- var ensureDir = (dir) => (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
43983
- var readJsonArray = (filePath) => {
43984
- if (!(0, import_node_fs4.existsSync)(filePath)) return [];
43985
- return JSON.parse((0, import_node_fs4.readFileSync)(filePath, "utf-8"));
43986
- };
43987
- var writeJsonArray = (filePath, records) => {
43988
- ensureDir(import_node_path13.default.dirname(filePath));
43989
- (0, import_node_fs4.writeFileSync)(filePath, JSON.stringify(records, null, " "), "utf-8");
43990
- };
43991
- var departmentMemoryPath = (departmentName) => {
43992
- return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-memory.json");
43993
- };
43994
- var departmentSkillPath = (departmentName) => {
43995
- return import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "department-skills.json");
43996
- };
43997
- var departmentSkillDir = (departmentName, skillName) => {
43998
- return assertSafeSkillTarget(import_node_path13.default.join(getDepartmentWorkSpaceDir(departmentName), "skills"), skillName);
43999
- };
44000
- var proposalsPath = () => {
44001
- return import_node_path13.default.join(getDepartmentBaseDir(), "department-proposals.json");
44002
- };
44003
- var getDepartmentNameForHead = (request) => {
44004
- const mailboxId = request?.departmentAgentId;
44005
- if (!mailboxId) return null;
44006
- const member = getDepartmentMemberByMailboxId(mailboxId);
44007
- if (!member || member.role !== "department_head") return null;
44008
- const [departmentName] = mailboxId.split("::");
44009
- return departmentName || null;
44010
- };
44011
- var listDepartmentMemories = (departmentName) => {
44012
- return readJsonArray(departmentMemoryPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
44013
- };
44014
- var createDepartmentMemory = (departmentName, input) => {
44015
- const now = Date.now();
44016
- const memory = {
44017
- id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
44018
- departmentName,
44019
- title: input.title,
44020
- content: input.content,
44021
- sourceMailboxId: input.sourceMailboxId,
44022
- createdAt: now,
44023
- updatedAt: now
44024
- };
44025
- const records = listDepartmentMemories(departmentName);
44026
- records.push(memory);
44027
- writeJsonArray(departmentMemoryPath(departmentName), records);
44028
- return memory;
44029
- };
44030
- var updateDepartmentMemory = (departmentName, id, patch) => {
44031
- const records = listDepartmentMemories(departmentName);
44032
- const idx = records.findIndex((record) => record.id === id);
44033
- if (idx < 0) return null;
44034
- records[idx] = {
44035
- ...records[idx],
44036
- ...patch.title !== void 0 ? { title: patch.title } : {},
44037
- ...patch.content !== void 0 ? { content: patch.content } : {},
44038
- updatedAt: Date.now()
44039
- };
44040
- writeJsonArray(departmentMemoryPath(departmentName), records);
44041
- return records[idx];
44042
- };
44043
- var deleteDepartmentMemory = (departmentName, id) => {
44044
- const records = listDepartmentMemories(departmentName);
44045
- const target = records.find((record) => record.id === id) ?? null;
44046
- writeJsonArray(departmentMemoryPath(departmentName), records.filter((record) => record.id !== id));
44047
- return target;
44048
- };
44049
- var listDepartmentSkills = (departmentName) => {
44050
- return readJsonArray(departmentSkillPath(departmentName)).sort((a, b) => b.updatedAt - a.updatedAt);
44051
- };
44052
- var proposeDepartmentSkill = (departmentName, input) => {
44053
- const validation = validateSkillDocument({
44054
- skillName: input.skillName,
44055
- description: input.description,
44056
- skillMd: input.skillMd
44057
- });
44058
- if (!validation.ok) {
44059
- throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF1A
44060
- ${formatSkillValidationIssues(validation)}`);
44061
- }
44062
- const records = listDepartmentSkills(departmentName);
44063
- if (records.some((record) => record.skillName === input.skillName && record.status !== "dropped")) {
44064
- return null;
43918
+ // src/tools/tools/department/DepartmentMemberCreate.ts
43919
+ var import_node_crypto10 = require("node:crypto");
43920
+ var DESCRIPTION24 = `
43921
+ \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u3002
43922
+
43923
+ \u7BA1\u7406\u6743\u9650\uFF1A
43924
+ - CEO \u53EA\u80FD\u521B\u5EFA Department Head\u3002
43925
+ - Department Head \u53EF\u4EE5\u5728\u81EA\u5DF1\u90E8\u95E8\u5185\u521B\u5EFA Executor\u3002
43926
+ - Executor \u4E0D\u80FD\u521B\u5EFA\u6210\u5458\u3002
43927
+ `;
43928
+ var departmentMemberCreate = {
43929
+ name: `department_member_create`,
43930
+ description: DESCRIPTION24,
43931
+ input_schema: {
43932
+ type: `object`,
43933
+ properties: {
43934
+ name: {
43935
+ type: `string`,
43936
+ description: `\u90E8\u95E8\u6210\u5458\u540D\u79F0`
43937
+ },
43938
+ departmentName: {
43939
+ type: `string`,
43940
+ description: `\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0\uFF0C\u4E0D\u8981\u81C6\u9020`
43941
+ },
43942
+ focusOn: {
43943
+ type: `string`,
43944
+ description: `\u8BE5\u6210\u5458\u7684\u957F\u671F\u5C97\u4F4D\u804C\u8D23\u4E0E\u884C\u4E3A\u8FB9\u754C\uFF0C\u4E0D\u8981\u5199\u4E00\u6B21\u6027\u4EFB\u52A1\u7EC6\u8282`
43945
+ },
43946
+ role: {
43947
+ type: `string`,
43948
+ enum: [`department_head`, `executor`],
43949
+ description: `\u6210\u5458\u89D2\u8272\u3002CEO \u521B\u5EFA\u90E8\u95E8\u8D1F\u8D23\u4EBA\u65F6\u4F20 department_head\uFF1BDepartment Head \u521B\u5EFA\u6267\u884C\u8005\u65F6\u4F20 executor`
43950
+ }
43951
+ },
43952
+ required: [`name`, `departmentName`, `focusOn`]
43953
+ },
43954
+ async execute(input, userRequest) {
43955
+ const name = input.name;
43956
+ const departmentName = input.departmentName;
43957
+ const requestedRole = input.role;
43958
+ const department = getDepartment(departmentName);
43959
+ if (!department) return `[departmentMemberCreate] \u4E0D\u5B58\u5728\u540D\u79F0\u4E3A ${departmentName} \u7684\u90E8\u95E8`;
43960
+ const focusOn = input.focusOn;
43961
+ const currentMailboxId = userRequest?.departmentAgentId;
43962
+ let role;
43963
+ if (currentMailboxId) {
43964
+ const currentMember = getDepartmentMemberByMailboxId(currentMailboxId);
43965
+ if (!currentMember) {
43966
+ return `[departmentMemberCreate] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${currentMailboxId}`;
43967
+ }
43968
+ if (currentMember.role !== "department_head") {
43969
+ return `[departmentMemberCreate] \u53EA\u6709 Department Head \u53EF\u4EE5\u521B\u5EFA Executor`;
43970
+ }
43971
+ if (currentMember.departmentId !== department.id) {
43972
+ return `[departmentMemberCreate] Department Head \u53EA\u80FD\u5728\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8\u5185\u521B\u5EFA\u6210\u5458`;
43973
+ }
43974
+ role = requestedRole ?? "executor";
43975
+ if (role === "department_head") {
43976
+ return `[departmentMemberCreate] \u90E8\u95E8\u5DF2\u7531\u5F53\u524D Department Head \u7BA1\u7406\uFF0C\u4E0D\u80FD\u7531 Department Head \u518D\u521B\u5EFA Department Head`;
43977
+ }
43978
+ } else {
43979
+ role = requestedRole ?? "department_head";
43980
+ if (role !== "department_head") {
43981
+ return `[departmentMemberCreate] CEO \u53EA\u80FD\u521B\u5EFA Department Head\uFF1BExecutor \u5E94\u7531 Department Head \u521B\u5EFA`;
43982
+ }
43983
+ }
43984
+ let departmentMember = {
43985
+ id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
43986
+ name,
43987
+ departmentId: department.id,
43988
+ mailBoxId: getMailBoxId(department.name, name),
43989
+ workspaceId: getWorkspaceId(department.name, name),
43990
+ role,
43991
+ focusOn
43992
+ };
43993
+ try {
43994
+ departmentMember = createDepartmentMember(departmentMember);
43995
+ } catch (err) {
43996
+ return `[departmentMemberCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u5931\u8D25: ${err.message}`;
43997
+ }
43998
+ return `[departmentMemberCreate] \u521B\u5EFA\u90E8\u95E8\u6210\u5458\u6210\u529F: ${JSON.stringify(departmentMember)}`;
44065
43999
  }
44066
- const now = Date.now();
44067
- const skill = {
44068
- id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
44069
- departmentName,
44070
- skillName: input.skillName,
44071
- description: input.description,
44072
- skillMd: input.skillMd,
44073
- status: "pending",
44074
- createdByMailboxId: input.createdByMailboxId,
44075
- createdAt: now,
44076
- updatedAt: now
44077
- };
44078
- records.push(skill);
44079
- writeJsonArray(departmentSkillPath(departmentName), records);
44080
- return skill;
44081
44000
  };
44082
- var keepDepartmentSkill = (departmentName, id) => {
44083
- const records = listDepartmentSkills(departmentName);
44084
- const idx = records.findIndex((record) => record.id === id);
44085
- if (idx < 0) return null;
44086
- records[idx] = { ...records[idx], status: "active", updatedAt: Date.now() };
44087
- const skillDir = departmentSkillDir(departmentName, records[idx].skillName);
44088
- const validation = validateSkillDocument({
44089
- skillName: records[idx].skillName,
44090
- description: records[idx].description,
44091
- skillMd: records[idx].skillMd,
44092
- baseDir: skillDir
44093
- });
44094
- if (!validation.ok) {
44095
- throw new Error(`[departmentSkill] Skill \u8349\u7A3F\u6821\u9A8C\u5931\u8D25\uFF0C\u62D2\u7EDD\u4FDD\u7559\uFF1A
44096
- ${formatSkillValidationIssues(validation)}`);
44097
- }
44098
- ensureDir(skillDir);
44099
- (0, import_node_fs4.writeFileSync)(import_node_path13.default.join(skillDir, "SKILL.md"), records[idx].skillMd, "utf-8");
44100
- const smokeTest = smokeTestSkillDirectory(skillDir, { expectedName: records[idx].skillName });
44101
- if (!smokeTest.ok) {
44102
- throw new Error(`[departmentSkill] Skill smoke test \u5931\u8D25\uFF1A
44103
- ${formatSkillValidationIssues(smokeTest)}`);
44001
+
44002
+ // src/tools/tools/department/DepartmentMemberDelete.ts
44003
+ var DESCRIPTION25 = `
44004
+ \u5220\u9664\u90E8\u95E8\u6210\u5458\u3002
44005
+
44006
+ \u5DE5\u5177\u8C03\u7528\u89C4\u5219\uFF1A
44007
+ - \u5FC5\u987B\u662F\u7528\u6237\u660E\u786E\u8981\u6C42\u5220\u9664\u6210\u5458\u65F6\u4F7F\u7528\u3002
44008
+ - Department Head \u53EA\u80FD\u5220\u9664\u81EA\u5DF1\u90E8\u95E8\u5185\u7684 Executor\uFF0C\u4E0D\u80FD\u5220\u9664 Department Head\u3002
44009
+ - CEO \u53EF\u4EE5\u5728\u7528\u6237\u660E\u786E\u8981\u6C42\u65F6\u5220\u9664\u90E8\u95E8\u6210\u5458\u3002
44010
+ `;
44011
+ var departmentMemberDelete = {
44012
+ name: `department_member_delete`,
44013
+ description: DESCRIPTION25,
44014
+ input_schema: {
44015
+ type: `object`,
44016
+ properties: {
44017
+ department_name: {
44018
+ type: `string`,
44019
+ description: `\u5F85\u5220\u9664\u6210\u5458\u6240\u5C5E\u90E8\u95E8\u540D\u79F0`
44020
+ },
44021
+ member_id: {
44022
+ type: `string`,
44023
+ description: `\u5F85\u5220\u9664\u6210\u5458 id`
44024
+ }
44025
+ },
44026
+ required: [`department_name`, `member_id`]
44027
+ },
44028
+ async execute(input, userRequest) {
44029
+ const departmentName = input.department_name;
44030
+ const memberId = input.member_id;
44031
+ const department = getDepartment(departmentName);
44032
+ if (!department) return `[departmentMemberDelete] \u4E0D\u5B58\u5728\u540D\u79F0\u4E3A ${departmentName} \u7684\u90E8\u95E8`;
44033
+ const member = getDepartmentMember(departmentName, memberId);
44034
+ if (!member) return `[departmentMemberDelete] \u627E\u4E0D\u5230\u6210\u5458: ${memberId}`;
44035
+ const currentMailboxId = userRequest?.departmentAgentId;
44036
+ if (currentMailboxId) {
44037
+ const currentMember = getDepartmentMemberByMailboxId(currentMailboxId);
44038
+ if (!currentMember) return `[departmentMemberDelete] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${currentMailboxId}`;
44039
+ if (currentMember.role !== "department_head") return `[departmentMemberDelete] \u53EA\u6709 Department Head \u53EF\u4EE5\u5220\u9664 Executor`;
44040
+ if (currentMember.departmentId !== department.id) return `[departmentMemberDelete] Department Head \u53EA\u80FD\u7BA1\u7406\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8`;
44041
+ if (member.role === "department_head") return `[departmentMemberDelete] Department Head \u4E0D\u80FD\u5220\u9664 Department Head`;
44042
+ }
44043
+ try {
44044
+ deleteDepartmentMemberById(departmentName, memberId);
44045
+ const mailboxId = getMailBoxId(departmentName, member.name);
44046
+ const cancelled = cancelMailboxMessages(mailboxId);
44047
+ if (cancelled > 0) {
44048
+ console.log(`[departmentMemberDelete] \u5DF2\u53D6\u6D88 ${cancelled} \u6761\u6B8B\u7559 mailbox \u6D88\u606F: ${mailboxId}`);
44049
+ }
44050
+ } catch (err) {
44051
+ return `[departmentMemberDelete] \u5220\u9664\u90E8\u95E8\u6210\u5458\u5931\u8D25: ${err.message}`;
44052
+ }
44053
+ return `[departmentMemberDelete] \u5220\u9664\u90E8\u95E8\u6210\u5458\u6210\u529F: ${JSON.stringify(memberId)}`;
44104
44054
  }
44105
- writeJsonArray(departmentSkillPath(departmentName), records);
44106
- return records[idx];
44107
- };
44108
- var dropDepartmentSkill = (departmentName, id) => {
44109
- const records = listDepartmentSkills(departmentName);
44110
- const idx = records.findIndex((record) => record.id === id);
44111
- if (idx < 0) return null;
44112
- records[idx] = { ...records[idx], status: "dropped", updatedAt: Date.now() };
44113
- writeJsonArray(departmentSkillPath(departmentName), records);
44114
- return records[idx];
44115
44055
  };
44116
- var createDepartmentProposal = (input) => {
44117
- const records = readJsonArray(proposalsPath());
44118
- const proposal = {
44119
- ...input,
44120
- id: (0, import_node_crypto10.randomUUID)().slice(0, 8),
44121
- status: "pending",
44122
- createdAt: Date.now()
44123
- };
44124
- records.push(proposal);
44125
- writeJsonArray(proposalsPath(), records);
44126
- return proposal;
44056
+
44057
+ // src/tools/tools/department/DepartmentMemberList.ts
44058
+ var DESCRIPTION26 = `
44059
+ \u67E5\u770B\u5F53\u524D Department Head \u6240\u5C5E\u90E8\u95E8\u7684\u6210\u5458\u76EE\u5F55\u3002
44060
+
44061
+ \u4F7F\u7528\u8FB9\u754C\uFF1A
44062
+ - \u4EC5 Department Head \u53EF\u4EE5\u4F7F\u7528\u3002
44063
+ - CEO \u9ED8\u8BA4\u901A\u8FC7 department_list \u53EA\u770B\u5230\u90E8\u95E8\u4E0E\u8D1F\u8D23\u4EBA\u6458\u8981\u3002
44064
+ - Department Head \u53EA\u80FD\u67E5\u770B\u81EA\u5DF1\u6240\u5C5E\u90E8\u95E8\u7684\u6210\u5458\uFF0C\u7528\u4E8E\u590D\u7528\u5DF2\u6709\u6210\u5458\u3001\u5224\u65AD\u662F\u5426\u9700\u8981\u521B\u5EFA\u65B0\u6210\u5458\u3001\u4E86\u89E3\u6210\u5458\u804C\u8D23\u3002
44065
+ `;
44066
+ var departmentMemberList = {
44067
+ name: `department_member_list`,
44068
+ description: DESCRIPTION26,
44069
+ input_schema: {
44070
+ type: `object`,
44071
+ properties: {},
44072
+ required: []
44073
+ },
44074
+ async execute(input, userRequest) {
44075
+ const mailboxId = userRequest?.departmentAgentId;
44076
+ if (!mailboxId) {
44077
+ return `[departmentMemberList] \u53EA\u6709 Department Head \u53EF\u4EE5\u67E5\u770B\u90E8\u95E8\u6210\u5458\u76EE\u5F55`;
44078
+ }
44079
+ const currentMember = getDepartmentMemberByMailboxId(mailboxId);
44080
+ if (!currentMember) {
44081
+ return `[departmentMemberList] \u65E0\u6CD5\u8BC6\u522B\u5F53\u524D\u90E8\u95E8\u6210\u5458: ${mailboxId}`;
44082
+ }
44083
+ if (currentMember.role !== "department_head") {
44084
+ return `[departmentMemberList] \u5F53\u524D\u6210\u5458\u4E0D\u662F Department Head\uFF0C\u4E0D\u80FD\u67E5\u770B\u5B8C\u6574\u90E8\u95E8\u6210\u5458\u76EE\u5F55`;
44085
+ }
44086
+ const department = getDepartmentById(currentMember.departmentId);
44087
+ if (!department) {
44088
+ return `[departmentMemberList] \u627E\u4E0D\u5230\u5F53\u524D\u6210\u5458\u6240\u5C5E\u90E8\u95E8: ${currentMember.departmentId}`;
44089
+ }
44090
+ const members = department.departmentMembers.map((member) => ({
44091
+ id: member.id,
44092
+ name: member.name,
44093
+ role: member.role ?? "executor",
44094
+ mailBoxId: member.mailBoxId,
44095
+ workspaceId: member.workspaceId,
44096
+ focusOn: member.focusOn
44097
+ }));
44098
+ return `[departmentMemberList] ${department.name} \u90E8\u95E8\u6210\u5458\u76EE\u5F55: ${JSON.stringify(members)}`;
44099
+ }
44127
44100
  };
44128
- var buildDepartmentLearningContext = (departmentName) => {
44129
- if (!departmentName) return "";
44130
- const memories = listDepartmentMemories(departmentName);
44131
- const activeSkills = listDepartmentSkills(departmentName).filter((skill) => skill.status === "active");
44132
- if (memories.length === 0 && activeSkills.length === 0) return "";
44133
- const memoryLines = memories.map(
44134
- (memory) => ` - [id=${memory.id}] ${memory.title}
44135
- ${memory.content.replace(/\n/g, "\n ")}`
44136
- ).join("\n");
44137
- const skillLines = activeSkills.map(
44138
- (skill) => ` - ${skill.skillName}: ${skill.description}`
44139
- ).join("\n");
44140
- return [
44141
- `<department-learning-context department="${departmentName}">`,
44142
- memories.length > 0 ? `Department memories:
44143
- ${memoryLines}` : "",
44144
- activeSkills.length > 0 ? `Department skills:
44145
- ${skillLines}` : "",
44146
- `</department-learning-context>`
44147
- ].filter(Boolean).join("\n");
44101
+
44102
+ // src/tools/tools/department/CheckDepartmentReplies.ts
44103
+ var DESCRIPTION27 = `
44104
+ \u67E5\u770B Department Head \u6216\u90E8\u95E8\u6210\u5458\u53D1\u7ED9 CEO \u7684\u5DE5\u4F5C\u56DE\u590D\u3002
44105
+
44106
+ \u5F53 CEO \u5411 Department Head \u6D3E\u53D1\u4EFB\u52A1\u540E\uFF0C\u5BF9\u65B9\u5B8C\u6210\u5DE5\u4F5C\u4F1A\u901A\u8FC7 mailbox \u56DE\u4FE1\u3002\u4F7F\u7528\u6B64\u5DE5\u5177\u53EF\u4EE5\u67E5\u770B\u672A\u8BFB\u7684\u90E8\u95E8\u56DE\u4FE1\u5185\u5BB9\uFF0C\u4E86\u89E3\u5DE5\u4F5C\u8FDB\u5C55\u548C\u6210\u679C\u3002
44107
+ `;
44108
+ var checkDepartmentReplies = {
44109
+ name: `check_department_replies`,
44110
+ description: DESCRIPTION27,
44111
+ input_schema: {
44112
+ type: `object`,
44113
+ properties: {
44114
+ from_member: {
44115
+ type: `string`,
44116
+ description: `\u53EF\u9009\uFF1A\u53EA\u67E5\u770B\u6307\u5B9A mailboxId \u7684\u56DE\u4FE1\uFF0C\u4E0D\u586B\u5219\u67E5\u770B\u6240\u6709\u56DE\u4FE1`
44117
+ }
44118
+ },
44119
+ required: []
44120
+ },
44121
+ async execute(input) {
44122
+ const db3 = createSqliteDB();
44123
+ const fromMember = input.from_member;
44124
+ let msgs;
44125
+ if (fromMember) {
44126
+ const stmt = db3.prepare(
44127
+ `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
44128
+ FROM mailbox
44129
+ WHERE to_mailbox_id = 'manager' AND from_mailbox_id = ? AND status in ('pending', 'processing')
44130
+ ORDER BY send_time ASC`
44131
+ );
44132
+ msgs = stmt.all(fromMember);
44133
+ } else {
44134
+ const stmt = db3.prepare(
44135
+ `SELECT id, to_mailbox_id as toMailboxId, from_mailbox_id as fromMailboxId, content, send_time as sendTime, status
44136
+ FROM mailbox
44137
+ WHERE to_mailbox_id = 'manager' AND status in ('pending', 'processing')
44138
+ ORDER BY send_time ASC`
44139
+ );
44140
+ msgs = stmt.all();
44141
+ }
44142
+ if (msgs.length === 0) {
44143
+ return [
44144
+ `[checkDepartmentReplies] \u56E2\u961F\u8FD9\u4F1A\u513F\u8FD8\u6CA1\u56DE\u4FE1\uFF0C\u591A\u534A\u662F\u8D1F\u8D23\u4EBA\u4ECD\u5728\u5904\u7406\uFF0C\u8FC7\u4F1A\u513F\u518D\u6765\u770B\u3002`,
44145
+ ``,
44146
+ `\u51E0\u53E5\u63D0\u9192\uFF1A`,
44147
+ `- \u8FD9\u4E0D\u4EE3\u8868\u8981\u4F60\u4EB2\u81EA\u4E0B\u573A read/bash/\u6D4B\u8BD5/\u6539\u4EE3\u7801\u2014\u2014\u56E2\u961F\u6CA1\u56DE\uFF0C\u4E0D\u662F\u4F60\u8BE5\u63A5\u624B\u7684\u4FE1\u53F7\u3002`,
44148
+ `- \u8001\u677F\u8981\u662F\u6B63\u7B49\u7740\uFF0C\u5148\u7528 send_message \u5982\u5B9E\u8DDF\u4ED6\u8BF4\u4E00\u53E5\u201C\u8D1F\u8D23\u4EBA\u8FD8\u5728\u5904\u7406\uFF0C\u6211\u62FF\u5230\u7ED3\u679C\u5C31\u540C\u6B65\u201D\u3002`,
44149
+ `- \u5982\u679C\u4EFB\u52A1\u521A\u6D3E\u51FA\u53BB\uFF0C\u4E0D\u8981\u5728\u540C\u4E00\u8F6E\u9A6C\u4E0A\u50AC\u529E\uFF1B\u7ED9\u8D1F\u8D23\u4EBA\u5408\u7406\u5904\u7406\u65F6\u95F4\u3002`,
44150
+ `- \u5DF2\u7ECF\u7B49\u4E86\u4E00\u9635\u3001\u8001\u677F\u53C8\u8FFD\u95EE\uFF0C\u6216\u53D1\u73B0\u660E\u786E\u98CE\u9669\u65F6\uFF0C\u518D\u7528 mailbox_followup \u6216 department_communicate \u53BB\u95EE\u5BF9\u5E94 Department Head \u771F\u5B9E\u8FDB\u5C55\u3001\u5361\u5728\u54EA\u3001\u5927\u6982\u4EC0\u4E48\u65F6\u5019\u597D\u3002`,
44151
+ `- \u522B\u5728\u8FD9\u4E00\u8F6E\u91CC\u53CD\u590D\u7A7A\u67E5 check_department_replies\uFF1B\u7B49\u56E2\u961F\u56DE\u4FE1\u3001\u8001\u677F\u8FFD\u95EE\u6216\u50AC\u529E\u6709\u4E86\u56DE\u97F3\uFF0C\u518D\u6765\u770B\u3002`
44152
+ ].join("\n");
44153
+ }
44154
+ const updateStmt = db3.prepare(`UPDATE mailbox SET status = 'read' WHERE id = ?`);
44155
+ for (const msg of msgs) {
44156
+ updateStmt.run(msg.id);
44157
+ }
44158
+ const replies = msgs.map((msg, i) => {
44159
+ const time = new Date(msg.sendTime).toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
44160
+ return `--- \u56DE\u4FE1 ${i + 1} ---
44161
+ id: ${msg.id}
44162
+ \u6765\u81EA: ${msg.fromMailboxId}
44163
+ \u65F6\u95F4: ${time}
44164
+ \u72B6\u6001: ${msg.status}
44165
+ \u5185\u5BB9:
44166
+ ${msg.content}`;
44167
+ }).join("\n\n");
44168
+ return `[checkDepartmentReplies] \u6536\u5230 ${msgs.length} \u6761\u90E8\u95E8\u56DE\u4FE1\uFF1A
44169
+
44170
+ ${replies}`;
44171
+ }
44148
44172
  };
44149
44173
 
44150
44174
  // src/tools/tools/department/DepartmentLearning.ts
@@ -44569,6 +44593,34 @@ function validateProtectedRuntimeCommand(command) {
44569
44593
  }
44570
44594
  return null;
44571
44595
  }
44596
+ function listProtectedRuntimeProcesses() {
44597
+ try {
44598
+ const output = (0, import_node_child_process.execFileSync)("ps", ["-eo", "pid=,ppid=,args="], { encoding: "utf8" });
44599
+ const currentPid = process.pid;
44600
+ return output.split("\n").map((line) => line.trim().match(/^(\d+)\s+(\d+)\s+(.*)$/)).filter((match2) => Boolean(match2)).map((match2) => ({
44601
+ pid: Number(match2[1]),
44602
+ command: match2[3] ?? ""
44603
+ })).filter(
44604
+ (processInfo) => Number.isFinite(processInfo.pid) && processInfo.pid !== currentPid && (processInfo.command.includes("duclaw-cli start") || processInfo.command.includes("duclaw-worker"))
44605
+ ).map(({ pid, command }) => ({ pid, command }));
44606
+ } catch {
44607
+ return [];
44608
+ }
44609
+ }
44610
+ function validateProtectedRuntimePidCommand(command) {
44611
+ if (!/\bkill\b/.test(command)) return null;
44612
+ const protectedProcesses = listProtectedRuntimeProcesses();
44613
+ for (const protectedProcess of protectedProcesses) {
44614
+ const pidRef = new RegExp(`(^|[^\\d])${protectedProcess.pid}([^\\d]|$)`);
44615
+ if (!pidRef.test(command)) continue;
44616
+ return [
44617
+ `[bash] \u62D2\u7EDD\u6267\u884C: \u547D\u4EE4\u8BD5\u56FE\u7EC8\u6B62 Duclaw runtime \u8FDB\u7A0B PID ${protectedProcess.pid}\u3002`,
44618
+ `\u5339\u914D\u8FDB\u7A0B: ${protectedProcess.command}`,
44619
+ `\u8BF7\u53EA\u7EC8\u6B62\u660E\u786E\u5C5E\u4E8E\u5F53\u524D\u9879\u76EE\u7684\u5F00\u53D1\u670D\u52A1 PID\uFF0C\u4E0D\u8981\u6740 duclaw-cli start \u6216 duclaw-worker\u3002`
44620
+ ].join("\n");
44621
+ }
44622
+ return null;
44623
+ }
44572
44624
  function truncateOutput(output, limit = MAX_OUTPUT_LENGTH) {
44573
44625
  if (output.length <= limit) return output;
44574
44626
  return output.slice(0, limit) + `
@@ -44692,6 +44744,8 @@ var bashTool = {
44692
44744
  }
44693
44745
  const protectedCommandError = validateProtectedRuntimeCommand(command);
44694
44746
  if (protectedCommandError) return protectedCommandError;
44747
+ const protectedRuntimePidError = validateProtectedRuntimePidCommand(command);
44748
+ if (protectedRuntimePidError) return protectedRuntimePidError;
44695
44749
  if (userRequest?.workspacePath && explicitCwd) {
44696
44750
  const rejection = validateWorkspacePath(explicitCwd, userRequest.workspacePath);
44697
44751
  if (rejection) return `[bash] ${rejection}`;
@@ -53500,7 +53554,13 @@ toolRoutes.get("/tools", (c) => {
53500
53554
  toolRoutes.get("/skills", (c) => {
53501
53555
  try {
53502
53556
  const skills = SkillRegistry_default();
53503
- return c.json(skills.map((s) => ({ name: s.name, description: s.description, deletable: !!s.deletable })));
53557
+ return c.json(skills.map((s) => ({
53558
+ name: s.name,
53559
+ description: s.description,
53560
+ deletable: !!s.deletable,
53561
+ scope: s.scope ?? (s.deletable ? "project" : "global"),
53562
+ departmentName: s.departmentName
53563
+ })));
53504
53564
  } catch (err) {
53505
53565
  return c.json({ error: err.message || "Failed to list skills" }, 500);
53506
53566
  }
@@ -53515,6 +53575,8 @@ toolRoutes.get("/skills/:name", (c) => {
53515
53575
  name: skill.name,
53516
53576
  description: skill.description,
53517
53577
  deletable: !!skill.deletable,
53578
+ scope: skill.scope ?? (skill.deletable ? "project" : "global"),
53579
+ departmentName: skill.departmentName,
53518
53580
  detail: skill.getDetail()
53519
53581
  });
53520
53582
  } catch (err) {
@@ -53537,7 +53599,7 @@ var systemRoutes = new Hono2();
53537
53599
  var startTime = Date.now();
53538
53600
  systemRoutes.get("/system/info", (c) => {
53539
53601
  return c.json({
53540
- version: true ? "1.9.8" : "unknown",
53602
+ version: true ? "1.9.9" : "unknown",
53541
53603
  uptime: Math.floor((Date.now() - startTime) / 1e3),
53542
53604
  env: process.env.NODE_ENV || "development",
53543
53605
  nodeVersion: process.version