duclaw-cli 1.8.38 → 1.8.40

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.8.38" : "unknown"}`);
30245
+ console.log(`duclaw-cli v${true ? "1.8.40" : "unknown"}`);
30246
30246
  }
30247
30247
  function getDuclawTemplate() {
30248
30248
  return {
@@ -42310,6 +42310,19 @@ var failCeoFollowup = (id, error) => {
42310
42310
  AND status IN ('pending', 'processing', 'failed')
42311
42311
  `).run(error.slice(0, 1e3), now, id);
42312
42312
  };
42313
+ var recoverStaleProcessingCeoFollowups = (staleBefore) => {
42314
+ const db3 = createSqliteDB();
42315
+ const now = Date.now();
42316
+ const result = db3.prepare(`
42317
+ UPDATE ceo_followups
42318
+ SET status = 'failed',
42319
+ last_error = 'stale_processing_recovered',
42320
+ updated_at = ?
42321
+ WHERE status = 'processing'
42322
+ AND updated_at < ?
42323
+ `).run(now, staleBefore);
42324
+ return result.changes;
42325
+ };
42313
42326
  var completePendingCeoFollowupsForUser = (originUserId) => {
42314
42327
  const db3 = createSqliteDB();
42315
42328
  const now = Date.now();
@@ -42669,6 +42682,18 @@ var departmentCreate = {
42669
42682
 
42670
42683
  // src/tools/tools/department/DepartmentCommunicate.ts
42671
42684
  var CEO_MAILBOX_ID = `manager`;
42685
+ var MANAGER_ALIAS_PATTERN = /(^|::)(manager|main[-_\s]?manager|ceo)$/i;
42686
+ var looksLikePseudoManagerMailbox = (mailboxId) => {
42687
+ if (mailboxId === CEO_MAILBOX_ID) return true;
42688
+ if (!mailboxId.includes(`::`)) return false;
42689
+ return MANAGER_ALIAS_PATTERN.test(mailboxId.trim());
42690
+ };
42691
+ var buildManagerRoutingRejection = (activeContext) => [
42692
+ `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4F2A manager mailbox\u3002`,
42693
+ `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`,
42694
+ 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`
42695
+ ].join(`
42696
+ `);
42672
42697
  var getActiveMailboxContext = (actorMailboxId, messageId) => {
42673
42698
  if (!messageId) return null;
42674
42699
  const db3 = createSqliteDB();
@@ -42695,6 +42720,7 @@ var DESCRIPTION21 = `
42695
42720
  - CEO \u9ED8\u8BA4\u901A\u8FC7 department_list \u53EA\u770B\u5230\u90E8\u95E8\u548C Department Head\uFF0C\u4E0D\u4E3B\u52A8\u66B4\u9732 Executor\u3002
42696
42721
  - Department Head \u9ED8\u8BA4\u7BA1\u7406\u672C\u90E8\u95E8\u6210\u5458\u3002
42697
42722
  - \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
42723
+ - 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
42698
42724
 
42699
42725
  \u53C2\u6570\u8BF4\u660E\uFF1A
42700
42726
  - department_name + member_name: \u9ED8\u8BA4\u5BFB\u5740\u65B9\u5F0F\u3002
@@ -42739,18 +42765,6 @@ var departmentCommunicate = {
42739
42765
  const targetMailboxId = input.target_mailbox_id;
42740
42766
  const content = input.content;
42741
42767
  const reason = input.reason;
42742
- let toMailboxId = targetMailboxId;
42743
- let targetMember = targetMailboxId ? getDepartmentMemberByMailboxId(targetMailboxId) : null;
42744
- if (!toMailboxId) {
42745
- if (!departmentName || !memberName) {
42746
- return `[departmentCommunicate] department_name/member_name \u6216 target_mailbox_id \u81F3\u5C11\u9700\u8981\u63D0\u4F9B\u4E00\u79CD`;
42747
- }
42748
- targetMember = getDepartmentMemberByName(departmentName, memberName);
42749
- if (!targetMember) {
42750
- return `[departmentCommunicate] \u4E0D\u5B58\u5728 ${departmentName} \u90E8\u95E8\u7684\u6210\u5458: ${memberName}`;
42751
- }
42752
- toMailboxId = getMailBoxId(departmentName, memberName);
42753
- }
42754
42768
  let fromMailboxId;
42755
42769
  const departmentAgentId = userRequest.departmentAgentId;
42756
42770
  const actorMember = departmentAgentId ? getDepartmentMemberByMailboxId(
@@ -42765,6 +42779,30 @@ var departmentCommunicate = {
42765
42779
  } else {
42766
42780
  fromMailboxId = CEO_MAILBOX_ID;
42767
42781
  }
42782
+ const activeContext = departmentAgentId ? getActiveMailboxContext(fromMailboxId, userRequest.requestId) : null;
42783
+ let toMailboxId = targetMailboxId;
42784
+ let targetMember = targetMailboxId ? getDepartmentMemberByMailboxId(targetMailboxId) : null;
42785
+ if (departmentAgentId && targetMailboxId && looksLikePseudoManagerMailbox(targetMailboxId)) {
42786
+ return buildManagerRoutingRejection(activeContext);
42787
+ }
42788
+ if (departmentAgentId && targetMailboxId && !targetMember) {
42789
+ return [
42790
+ `[departmentCommunicate] \u62D2\u7EDD\u53D1\u9001\u5230\u4E0D\u5B58\u5728\u7684 mailbox: ${targetMailboxId}`,
42791
+ `Department agent \u4F7F\u7528 target_mailbox_id \u65F6\uFF0C\u76EE\u6807\u5FC5\u987B\u662F\u5DF2\u5B58\u5728\u7684\u90E8\u95E8\u6210\u5458 mailbox\u3002`,
42792
+ `\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`
42793
+ ].join(`
42794
+ `);
42795
+ }
42796
+ if (!toMailboxId) {
42797
+ if (!departmentName || !memberName) {
42798
+ return `[departmentCommunicate] department_name/member_name \u6216 target_mailbox_id \u81F3\u5C11\u9700\u8981\u63D0\u4F9B\u4E00\u79CD`;
42799
+ }
42800
+ targetMember = getDepartmentMemberByName(departmentName, memberName);
42801
+ if (!targetMember) {
42802
+ return `[departmentCommunicate] \u4E0D\u5B58\u5728 ${departmentName} \u90E8\u95E8\u7684\u6210\u5458: ${memberName}`;
42803
+ }
42804
+ toMailboxId = getMailBoxId(departmentName, memberName);
42805
+ }
42768
42806
  const isCrossDepartment = Boolean(actorMember && targetMember && actorMember.departmentId !== targetMember.departmentId);
42769
42807
  const isKnownAddressPath = Boolean(targetMailboxId);
42770
42808
  const options = {
@@ -42781,7 +42819,6 @@ var departmentCommunicate = {
42781
42819
  options.originUserId = userRequest.userId;
42782
42820
  options.originPlatform = userRequest.platform;
42783
42821
  } else {
42784
- const activeContext = getActiveMailboxContext(fromMailboxId, userRequest.requestId);
42785
42822
  if (activeContext) {
42786
42823
  options.originUserId = activeContext.originUserId;
42787
42824
  options.originPlatform = activeContext.originPlatform;
@@ -47042,6 +47079,8 @@ var replyMailbox = {
47042
47079
 
47043
47080
  // src/cron/mailbox.ts
47044
47081
  var db2 = createSqliteDB();
47082
+ var DEFAULT_MAILBOX_PROCESSING_STALE_MS = 15 * 6e4;
47083
+ var PSEUDO_MANAGER_MAILBOX_PATTERN = /(^|::)(manager|main[-_\s]?manager|ceo)$/i;
47045
47084
  var selectStmt = db2.prepare(`select
47046
47085
  id,
47047
47086
  to_mailbox_id as toMailboxId,
@@ -47057,6 +47096,79 @@ var selectStmt = db2.prepare(`select
47057
47096
  work_item_role as workItemRole,
47058
47097
  upstream_message_id as upstreamMessageId
47059
47098
  from mailbox where status = ?`);
47099
+ var getMailboxLivenessSnapshot = () => {
47100
+ const mailboxRows = db2.prepare(`
47101
+ SELECT status, COUNT(*) as count
47102
+ FROM mailbox
47103
+ WHERE status IN ('pending', 'processing')
47104
+ GROUP BY status
47105
+ `).all();
47106
+ const followupRows = db2.prepare(`
47107
+ SELECT status, COUNT(*) as count
47108
+ FROM ceo_followups
47109
+ WHERE status IN ('pending', 'processing', 'failed')
47110
+ GROUP BY status
47111
+ `).all();
47112
+ const mailboxCount = (status) => mailboxRows.find((row) => row.status === status)?.count ?? 0;
47113
+ const followupCount = (status) => followupRows.find((row) => row.status === status)?.count ?? 0;
47114
+ return {
47115
+ pendingMailbox: mailboxCount("pending"),
47116
+ processingMailbox: mailboxCount("processing"),
47117
+ pendingCeoFollowups: followupCount("pending"),
47118
+ processingCeoFollowups: followupCount("processing"),
47119
+ failedCeoFollowups: followupCount("failed")
47120
+ };
47121
+ };
47122
+ var hasUnresolvedInternalWork = (snapshot) => snapshot.pendingMailbox > 0 || snapshot.processingMailbox > 0 || snapshot.pendingCeoFollowups > 0 || snapshot.processingCeoFollowups > 0 || snapshot.failedCeoFollowups > 0;
47123
+ var looksLikePseudoManagerMailbox2 = (mailboxId) => mailboxId !== "manager" && mailboxId.includes("::") && PSEUDO_MANAGER_MAILBOX_PATTERN.test(mailboxId.trim());
47124
+ var reportMailboxLiveness = async (snapshot) => {
47125
+ if (!hasUnresolvedInternalWork(snapshot)) return;
47126
+ await reportRuntimeActivity({
47127
+ kind: "mailbox",
47128
+ toolName: "mailbox_poller",
47129
+ status: "active",
47130
+ metadata: {
47131
+ ...snapshot,
47132
+ inFlightDepartmentMailboxes: Array.from(inFlightDepartmentMailboxes),
47133
+ inFlightCeoFollowups: Array.from(inFlightCeoReplyMessages)
47134
+ }
47135
+ });
47136
+ };
47137
+ var recoverStaleProcessingMailboxMessages = () => {
47138
+ const staleMs = Number(process.env.MAILBOX_PROCESSING_STALE_MS ?? DEFAULT_MAILBOX_PROCESSING_STALE_MS);
47139
+ if (staleMs <= 0) return 0;
47140
+ const staleBefore = Date.now() - staleMs;
47141
+ const rows = db2.prepare(`
47142
+ SELECT
47143
+ id,
47144
+ to_mailbox_id as toMailboxId
47145
+ FROM mailbox
47146
+ WHERE status = 'processing'
47147
+ AND updated_at < ?
47148
+ `).all(staleBefore);
47149
+ let recovered = 0;
47150
+ for (const row of rows) {
47151
+ if (row.toMailboxId === "manager") continue;
47152
+ if (hasRunningAgent(row.toMailboxId) || inFlightDepartmentMailboxes.has(row.toMailboxId)) continue;
47153
+ if (updateMailboxMessageStatus(row.id, "pending", {
47154
+ fromStatus: "processing",
47155
+ actorMailboxId: row.toMailboxId,
47156
+ reason: "stale_processing_recovered"
47157
+ })) {
47158
+ recovered += 1;
47159
+ }
47160
+ }
47161
+ return recovered;
47162
+ };
47163
+ var recoverStaleProcessingWork = () => {
47164
+ const staleMs = Number(process.env.MAILBOX_PROCESSING_STALE_MS ?? DEFAULT_MAILBOX_PROCESSING_STALE_MS);
47165
+ if (staleMs <= 0) return;
47166
+ const mailboxRecovered = recoverStaleProcessingMailboxMessages();
47167
+ const followupsRecovered = recoverStaleProcessingCeoFollowups(Date.now() - staleMs);
47168
+ if (mailboxRecovered > 0 || followupsRecovered > 0) {
47169
+ console.warn(`[mailbox] \u5DF2\u6062\u590D stale processing: mailbox=${mailboxRecovered}, ceo_followups=${followupsRecovered}`);
47170
+ }
47171
+ };
47060
47172
  var markMailboxStatus = (msgId, status) => {
47061
47173
  const fromStatusMap = {
47062
47174
  processing: ["pending"],
@@ -47163,6 +47275,11 @@ var wakeDepartmentAgent = async (mailboxId, msgIds) => {
47163
47275
  const member = getDepartmentMemberByName(departmentName, memberName);
47164
47276
  if (!member) {
47165
47277
  console.warn(`[mailbox] \u76EE\u6807\u6210\u5458\u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u5DF2\u88AB\u5220\u9664\uFF09: ${mailboxId}`);
47278
+ if (looksLikePseudoManagerMailbox2(mailboxId)) {
47279
+ for (const id of msgIds) markMailboxStatus(id, "cancelled");
47280
+ console.warn(`[mailbox] \u5DF2\u53D6\u6D88\u4F2A manager mailbox \u6295\u9012 ${mailboxId}\uFF0C\u4E0D\u518D\u751F\u6210 CEO \u7CFB\u7EDF\u566A\u58F0`);
47281
+ return;
47282
+ }
47166
47283
  const notifyContent = `[\u7CFB\u7EDF\u901A\u77E5] \u90E8\u95E8\u6210\u5458 ${mailboxId} \u4E0D\u5B58\u5728\uFF08\u53EF\u80FD\u6240\u5C5E\u90E8\u95E8\u5DF2\u89E3\u6563\u6216\u6210\u5458\u5DF2\u88AB\u5220\u9664\uFF09\uFF0C\u4EE5\u4E0B\u6D88\u606F\u65E0\u6CD5\u6295\u9012\u5DF2\u88AB\u53D6\u6D88\u3002\u8BF7\u6839\u636E\u60C5\u51B5\u51B3\u5B9A\u662F\u5426\u9700\u8981\u91CD\u65B0\u7EC4\u5EFA\u90E8\u95E8\u6216\u8C03\u6574\u8BA1\u5212\u3002`;
47167
47284
  const originStmt = db2.prepare(
47168
47285
  `SELECT origin_user_id as originUserId, origin_platform as originPlatform
@@ -47303,6 +47420,8 @@ var pollMailbox = async () => {
47303
47420
  if (polling) return;
47304
47421
  polling = true;
47305
47422
  try {
47423
+ recoverStaleProcessingWork();
47424
+ await reportMailboxLiveness(getMailboxLivenessSnapshot());
47306
47425
  const mailBoxMsgs = selectStmt.all("pending");
47307
47426
  const pendingFollowups = listPendingCeoFollowups();
47308
47427
  if (mailBoxMsgs.length === 0 && pendingFollowups.length === 0) return;
@@ -52432,7 +52551,7 @@ var systemRoutes = new Hono2();
52432
52551
  var startTime = Date.now();
52433
52552
  systemRoutes.get("/system/info", (c) => {
52434
52553
  return c.json({
52435
- version: true ? "1.8.38" : "unknown",
52554
+ version: true ? "1.8.40" : "unknown",
52436
52555
  uptime: Math.floor((Date.now() - startTime) / 1e3),
52437
52556
  env: process.env.NODE_ENV || "development",
52438
52557
  nodeVersion: process.version