duclaw-cli 1.8.44 → 1.8.46

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.44" : "unknown"}`);
30245
+ console.log(`duclaw-cli v${true ? "1.8.46" : "unknown"}`);
30246
30246
  }
30247
30247
  function getDuclawTemplate() {
30248
30248
  return {
@@ -37985,8 +37985,8 @@ var getMessages = async (storage, userId, limit = 300, date, cronTitle) => {
37985
37985
  var isInjectedMemoryContextMessage = (msg) => {
37986
37986
  if (msg.role !== "user" || msg.content.length === 0) return false;
37987
37987
  if (!msg.content.every((block) => block.type === "text")) return false;
37988
- const textContent = msg.content.map((block) => block.type === "text" ? block.text : "").join("\n").trim();
37989
- return textContent.startsWith("<memory-context>") && textContent.endsWith("</memory-context>");
37988
+ const textContent2 = msg.content.map((block) => block.type === "text" ? block.text : "").join("\n").trim();
37989
+ return textContent2.startsWith("<memory-context>") && textContent2.endsWith("</memory-context>");
37990
37990
  };
37991
37991
  var hasProtocolBlock = (msg) => {
37992
37992
  return msg.content.some((block) => block.type === "tool_use" || block.type === "tool_result");
@@ -38226,12 +38226,12 @@ var searchChatHistory = async (messageStorage, topicStorage, userId, query, date
38226
38226
  for (let j = from; j <= to; j++) {
38227
38227
  const msg = messages[j];
38228
38228
  if (!msg) continue;
38229
- const textContent = msg.content.filter((b) => b.type === "text").map((b) => b.text).join(" ").slice(0, 200);
38230
- if (textContent) {
38229
+ const textContent2 = msg.content.filter((b) => b.type === "text").map((b) => b.text).join(" ").slice(0, 200);
38230
+ if (textContent2) {
38231
38231
  snippets.push({
38232
38232
  index: j,
38233
38233
  role: msg.role,
38234
- text: textContent,
38234
+ text: textContent2,
38235
38235
  isMatch: j === match2.i
38236
38236
  });
38237
38237
  }
@@ -45235,9 +45235,10 @@ var getCompactConfig = () => ({
45235
45235
  enabled: readBoolean("COMPACT_ENABLED", true),
45236
45236
  contextWindowTokens: readNumber("COMPACT_CONTEXT_WINDOW_TOKENS", 2e5),
45237
45237
  outputReserveTokens: readNumber("COMPACT_OUTPUT_RESERVE_TOKENS", 2e4),
45238
- bufferTokens: readNumber("COMPACT_BUFFER_TOKENS", 13e3),
45238
+ bufferTokens: readNumber("COMPACT_BUFFER_TOKENS", 0),
45239
45239
  warningBufferTokens: readNumber("COMPACT_WARNING_BUFFER_TOKENS", 2e4),
45240
45240
  hotTailMessages: readNumber("COMPACT_HOT_TAIL_MESSAGES", 24),
45241
+ compactUserMessageMaxTokens: readNumber("COMPACT_USER_MESSAGE_MAX_TOKENS", 2e4),
45241
45242
  maxFailures: readNumber("COMPACT_MAX_FAILURES", 3),
45242
45243
  microCompactKeepToolResults: readNumber("MICRO_COMPACT_KEEP_TOOL_RESULTS", 5),
45243
45244
  microCompactToolResultChars: readNumber("MICRO_COMPACT_TOOL_RESULT_CHARS", 1200)
@@ -45363,70 +45364,50 @@ var tokenCountWithEstimation = (messages, systemPrompt = "") => {
45363
45364
  };
45364
45365
 
45365
45366
  // src/compact/compactPrompt.ts
45366
- var stringify2 = (value) => {
45367
- try {
45368
- return JSON.stringify(value);
45369
- } catch {
45370
- return "[unserializable]";
45371
- }
45372
- };
45373
- var renderBlock2 = (block) => {
45374
- switch (block.type) {
45375
- case "text":
45376
- return block.text;
45377
- case "tool_use":
45378
- return `[tool_use id=${block.id} name=${block.name} input=${stringify2(block.input)}]`;
45379
- case "tool_result":
45380
- return `[tool_result tool_use_id=${block.tool_use_id} content=${block.content}]`;
45381
- case "image":
45382
- return `[image source=${block.source.type}]`;
45383
- case "thinking":
45384
- return `[thinking ${block.thinking}]`;
45385
- default:
45386
- return stringify2(block);
45387
- }
45388
- };
45389
- var renderMessagesForCompact = (messages) => messages.map((message, index) => {
45390
- const content = message.content.map(renderBlock2).join("\n");
45391
- return `<message index="${index}" role="${message.role}">
45392
- ${content}
45393
- </message>`;
45394
- }).join("\n\n");
45395
- var COMPACT_SYSTEM_PROMPT = `\u4F60\u662F Duclaw \u7684\u4F1A\u8BDD\u538B\u7F29\u5668\u3002\u4F60\u53EA\u8D1F\u8D23\u628A\u7814\u53D1\u667A\u80FD\u4F53\u7684\u5386\u53F2\u5BF9\u8BDD\u538B\u7F29\u6210\u53EF\u7EE7\u7EED\u5DE5\u4F5C\u7684\u7ED3\u6784\u5316\u6458\u8981\uFF0C\u4E0D\u8C03\u7528\u5DE5\u5177\uFF0C\u4E0D\u548C\u7528\u6237\u804A\u5929\u3002
45396
-
45397
- \u8981\u6C42\uFF1A
45398
- 1. \u4FDD\u7559\u5F53\u524D\u7528\u6237\u76EE\u6807\u3001\u6700\u65B0\u660E\u786E\u8981\u6C42\u3001\u7EA6\u675F\u548C\u504F\u597D\u3002
45399
- 2. \u4FDD\u7559\u5F53\u524D\u4EFB\u52A1\u72B6\u6001\u3001\u5DF2\u5B8C\u6210\u5DE5\u4F5C\u3001\u672A\u5B8C\u6210\u4E8B\u9879\u548C\u4E0B\u4E00\u6B65\u3002
45400
- 3. \u4FDD\u7559\u5173\u952E\u6587\u4EF6\u8DEF\u5F84\u3001\u547D\u4EE4\u3001\u9519\u8BEF\u539F\u6587\u7247\u6BB5\u3001\u5916\u90E8\u6587\u6863/\u63A5\u53E3\u7ED3\u8BBA\u3002
45401
- 4. \u4FDD\u7559\u91CD\u8981\u5DE5\u5177\u7ED3\u679C\uFF0C\u4F46\u4E0D\u8981\u7C98\u8D34\u957F\u65E5\u5FD7\uFF1B\u53EA\u5199\u53EF\u884C\u52A8\u7ED3\u8BBA\u3002
45402
- 5. \u6807\u51FA\u5DF2\u4E22\u5F03\u6216\u4E0D\u518D\u91CD\u8981\u7684\u4FE1\u606F\u7C7B\u578B\u3002
45403
- 6. \u5982\u679C\u5386\u53F2\u4E2D\u6709 Department Head\u3001Executor\u3001mailbox\u3001cron \u7EBF\u7D22\uFF0C\u8BB0\u5F55\u8D23\u4EFB\u4EBA\u548C\u7EBF\u7A0B\u72B6\u6001\u3002
45404
- 7. \u8F93\u51FA\u5FC5\u987B\u662F\u7EAF\u6587\u672C\uFF0C\u5E76\u4E25\u683C\u4F7F\u7528\u4E0B\u9762\u7ED3\u6784\uFF1A
45367
+ var COMPACT_SUMMARIZATION_PROMPT = `You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.
45368
+
45369
+ The summary must be concise, factual, and directly useful for continuing the work. Do not invent missing details. Clearly separate confirmed facts from assumptions or items that still need verification.
45370
+
45371
+ Use this exact schema:
45405
45372
 
45406
45373
  <compact-summary>
45407
- 1. \u7528\u6237\u76EE\u6807\uFF1A
45408
- 2. \u5F53\u524D\u4EFB\u52A1\u72B6\u6001\uFF1A
45409
- 3. \u5DF2\u5B8C\u6210\u5DE5\u4F5C\uFF1A
45410
- 4. \u5DF2\u67E5\u770B/\u4FEE\u6539\u7684\u6587\u4EF6\uFF1A
45411
- 5. \u5173\u952E\u5DE5\u5177\u7ED3\u679C\uFF1A
45412
- 6. \u9519\u8BEF\u4E0E\u4FEE\u590D\uFF1A
45413
- 7. \u7528\u6237\u660E\u786E\u7EA6\u675F\uFF1A
45414
- 8. \u672A\u5B8C\u6210\u4E8B\u9879\uFF1A
45415
- 9. \u4E0B\u4E00\u6B65\u5EFA\u8BAE\uFF1A
45416
- 10. \u5DF2\u4E22\u5F03\u4FE1\u606F\uFF1A
45417
- </compact-summary>`;
45418
- var buildCompactUserPrompt = (messages, reason) => `\u8BF7\u538B\u7F29\u4E0B\u9762\u7684\u4F1A\u8BDD\u5386\u53F2\uFF0C\u89E6\u53D1\u539F\u56E0\uFF1A${reason}\u3002
45419
-
45420
- <conversation>
45421
- ${renderMessagesForCompact(messages)}
45422
- </conversation>`;
45374
+ 1. \u7528\u6237\u6700\u65B0\u76EE\u6807:
45375
+ 2. \u7528\u6237\u786C\u6027\u7EA6\u675F:
45376
+ 3. \u5F53\u524D\u4E8B\u5B9E\u72B6\u6001:
45377
+ 4. \u5DF2\u5B8C\u6210\u52A8\u4F5C:
45378
+ 5. \u6587\u4EF6/\u547D\u4EE4/\u8BC1\u636E:
45379
+ 6. mailbox/department \u7EBF\u7A0B:
45380
+ 7. \u7528\u6237\u53EF\u89C1\u627F\u8BFA:
45381
+ 8. \u672A\u5B8C\u6210\u4E8B\u9879:
45382
+ 9. \u5F53\u524D\u963B\u585E\u4E0E\u7B49\u5F85\u5BF9\u8C61:
45383
+ 10. \u4E0B\u4E00\u6B65\u5FC5\u987B\u52A8\u4F5C:
45384
+ 11. \u4E0D\u786E\u5B9A/\u5F85\u786E\u8BA4:
45385
+ 12. \u5DF2\u4E22\u5F03\u4FE1\u606F:
45386
+ </compact-summary>
45387
+
45388
+ Field guidance:
45389
+ - \u7528\u6237\u6700\u65B0\u76EE\u6807: the latest user intent, not stale earlier goals.
45390
+ - \u7528\u6237\u786C\u6027\u7EA6\u675F: explicit user constraints, safety boundaries, deployment/data-persistence constraints, style preferences, or "do not" instructions.
45391
+ - \u5F53\u524D\u4E8B\u5B9E\u72B6\u6001: verified state only. Say "\u65E0" if there is no verified fact.
45392
+ - \u5DF2\u5B8C\u6210\u52A8\u4F5C: concrete actions already performed by the agent or team.
45393
+ - \u6587\u4EF6/\u547D\u4EE4/\u8BC1\u636E: important paths, commands, test results, errors, URLs, versions, sandbox IDs, mailbox IDs, and other evidence required to continue.
45394
+ - mailbox/department \u7EBF\u7A0B: responsible owner, mailbox thread/message IDs, CEO followups, department head/executor status, who owes a reply to whom, and whether the user-visible closure is complete.
45395
+ - \u7528\u6237\u53EF\u89C1\u627F\u8BFA: what the user has been told or promised, including pending follow-ups.
45396
+ - \u672A\u5B8C\u6210\u4E8B\u9879: work still not done.
45397
+ - \u5F53\u524D\u963B\u585E\u4E0E\u7B49\u5F85\u5BF9\u8C61: blockers, external waits, running commands, pending approvals, or a named agent/person/system being waited on.
45398
+ - \u4E0B\u4E00\u6B65\u5FC5\u987B\u52A8\u4F5C: the minimal next actions the next LLM should take, in order.
45399
+ - \u4E0D\u786E\u5B9A/\u5F85\u786E\u8BA4: assumptions, ambiguous facts, or things that must be re-checked.
45400
+ - \u5DF2\u4E22\u5F03\u4FE1\u606F: categories of old details intentionally omitted, such as long logs, obsolete failed attempts, or completed transient steps.
45401
+
45402
+ If a field does not apply, write "\u65E0".`;
45403
+ var COMPACT_SUMMARY_PREFIX = `Another language model started to solve this problem and produced a summary of its thinking process. You also have access to the state of the tools that were used by that language model. Use this to build on the work that has already been done and avoid duplicating work. Here is the summary produced by the other language model, use the information in this summary to assist with your own analysis:`;
45404
+ var COMPACT_SYSTEM_PROMPT = `You are Duclaw's context compaction worker. Produce only the requested handoff summary. Do not call tools and do not chat with the user.`;
45405
+ var buildCompactUserPrompt = (_messages, reason) => `${COMPACT_SUMMARIZATION_PROMPT}
45406
+
45407
+ Compaction trigger: ${reason}`;
45423
45408
 
45424
45409
  // src/compact/CompactEngine.ts
45425
45410
  var buildCompactSummaryKey = (userId, date, cronTitle) => `compact:summary:${userId}:${date}${cronTitle ? `:${cronTitle}` : ""}`;
45426
- var assistantAck = {
45427
- role: "assistant",
45428
- content: [text("\u6211\u5DF2\u8BFB\u53D6\u538B\u7F29\u6458\u8981\uFF0C\u5C06\u57FA\u4E8E\u8BE5\u72B6\u6001\u7EE7\u7EED\u3002")]
45429
- };
45430
45411
  var extractSummaryText = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
45431
45412
  var stripAssistantUsage = (message) => {
45432
45413
  if (message.role !== "assistant") return { ...message, content: [...message.content] };
@@ -45435,20 +45416,28 @@ var stripAssistantUsage = (message) => {
45435
45416
  content: [...message.content]
45436
45417
  };
45437
45418
  };
45438
- var selectProtocolSafeHotTail = (messages, hotTailMessages) => {
45439
- if (messages.length === 0 || hotTailMessages <= 0) return [];
45440
- let start = Math.max(0, messages.length - hotTailMessages);
45441
- while (start > 0) {
45442
- while (start > 0 && messages[start]?.role !== "user") {
45443
- start--;
45444
- }
45445
- const startsWithToolResult = messages[start]?.role === "user" && messages[start]?.content.some((block) => block.type === "tool_result");
45446
- if (!startsWithToolResult) break;
45447
- start--;
45448
- }
45449
- const tail = sanitizeMessages(messages.slice(start).map(stripAssistantUsage));
45450
- return tail.slice(-Math.max(hotTailMessages, tail.length));
45451
- };
45419
+ var textContent = (message) => message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").trim();
45420
+ var isSummaryUserMessage = (message) => message.role === "user" && textContent(message).startsWith(COMPACT_SUMMARY_PREFIX);
45421
+ var isRawTextUserMessage = (message) => message.role === "user" && message.content.length > 0 && message.content.every((block) => block.type === "text") && !isSummaryUserMessage(message);
45422
+ var collectRecentUserMessages = (messages, maxTokens) => {
45423
+ if (maxTokens <= 0) return [];
45424
+ const selected = [];
45425
+ let usedTokens = 0;
45426
+ for (let i = messages.length - 1; i >= 0; i--) {
45427
+ const message = messages[i];
45428
+ if (!message || !isRawTextUserMessage(message)) continue;
45429
+ const messageTokens = roughTokenEstimateText(textContent(message));
45430
+ if (selected.length > 0 && usedTokens + messageTokens > maxTokens) break;
45431
+ if (messageTokens > maxTokens && selected.length === 0) continue;
45432
+ selected.push({ role: "user", content: [...message.content] });
45433
+ usedTokens += messageTokens;
45434
+ }
45435
+ return selected.reverse();
45436
+ };
45437
+ var buildCompactionRequestMessages = (messages, reason) => sanitizeMessagesWithReport([
45438
+ ...messages.map(stripAssistantUsage),
45439
+ userMessage(text(buildCompactUserPrompt(messages, reason)))
45440
+ ], { mergeAdjacentNonProtocolMessages: false }).messages;
45452
45441
  var CompactEngine = class {
45453
45442
  storage;
45454
45443
  summaryStorage;
@@ -45469,20 +45458,21 @@ var CompactEngine = class {
45469
45458
  }
45470
45459
  const tokenCountBefore = tokenCountWithEstimation(sourceMessages, input.systemPrompt);
45471
45460
  const response = await this.llm.chat(
45472
- [userMessage(text(buildCompactUserPrompt(sourceMessages, input.reason)))],
45461
+ buildCompactionRequestMessages(sourceMessages, input.reason),
45473
45462
  COMPACT_SYSTEM_PROMPT,
45474
45463
  []
45475
45464
  );
45476
- const summary = extractSummaryText(response);
45477
- if (!summary) {
45465
+ const rawSummary = extractSummaryText(response);
45466
+ if (!rawSummary) {
45478
45467
  throw new Error("[compact] compact LLM \u6CA1\u6709\u8FD4\u56DE\u6458\u8981\u6587\u672C");
45479
45468
  }
45480
- const hotTail = selectProtocolSafeHotTail(sourceMessages, this.config.hotTailMessages);
45481
- const compactedMessages = sanitizeMessages([
45482
- userMessage(text(summary)),
45483
- assistantAck,
45484
- ...hotTail
45485
- ]);
45469
+ const summary = `${COMPACT_SUMMARY_PREFIX}
45470
+ ${rawSummary}`.trim();
45471
+ const recentUserMessages = collectRecentUserMessages(sourceMessages, this.config.compactUserMessageMaxTokens);
45472
+ const compactedMessages = sanitizeMessagesWithReport([
45473
+ ...recentUserMessages,
45474
+ userMessage(text(summary))
45475
+ ], { mergeAdjacentNonProtocolMessages: false }).messages;
45486
45476
  if (compactedMessages.length === 0) {
45487
45477
  throw new Error("[compact] compact \u540E\u6CA1\u6709\u53EF\u5199\u56DE\u7684\u5408\u6CD5\u6D88\u606F");
45488
45478
  }
@@ -45494,7 +45484,7 @@ var CompactEngine = class {
45494
45484
  ...input.threadId ? { threadId: input.threadId } : {},
45495
45485
  summary,
45496
45486
  sourceMessageCount: sourceMessages.length,
45497
- hotTailCount: hotTail.length,
45487
+ hotTailCount: recentUserMessages.length,
45498
45488
  contextTokensBefore: tokenCountBefore.tokens,
45499
45489
  contextTokensAfter: roughTokenEstimateMessages(compactedMessages),
45500
45490
  tokenCountSource: tokenCountBefore.source,
@@ -45534,7 +45524,7 @@ var CompactEngine = class {
45534
45524
 
45535
45525
  // src/compact/microCompact.ts
45536
45526
  var COMPACTED_MARKER = "[\u65E7\u5DE5\u5177\u7ED3\u679C\u5DF2\u538B\u7F29]";
45537
- var stringify3 = (value) => {
45527
+ var stringify2 = (value) => {
45538
45528
  try {
45539
45529
  return JSON.stringify(value);
45540
45530
  } catch {
@@ -45582,7 +45572,7 @@ var stripAssistantUsage2 = (message) => {
45582
45572
  };
45583
45573
  var buildCompactedToolResultContent = (block, toolUse, maxChars) => {
45584
45574
  const toolName = toolUse?.name ?? "unknown";
45585
- const toolInput = toolUse ? stringify3(toolUse.input) : "{}";
45575
+ const toolInput = toolUse ? stringify2(toolUse.input) : "{}";
45586
45576
  const previewChars = Math.max(200, Math.floor(maxChars * 0.6));
45587
45577
  return `${COMPACTED_MARKER}
45588
45578
  \u5DE5\u5177: ${toolName}
@@ -46338,9 +46328,9 @@ ${memoryInjection}` : "") + dreamInjection;
46338
46328
  const textBlocks = response.content.filter(
46339
46329
  (b) => b.type === "text"
46340
46330
  );
46341
- const textContent = textBlocks.map((b) => b.text).join(`
46331
+ const textContent2 = textBlocks.map((b) => b.text).join(`
46342
46332
  `).trim();
46343
- if (textContent) {
46333
+ if (textContent2) {
46344
46334
  await addMessage(storage, userId, assistantMessageFromResponse(response), beijingTime, job?.title);
46345
46335
  const lateInterrupts = drainInterrupts(userId);
46346
46336
  if (lateInterrupts.length > 0) {
@@ -46367,27 +46357,27 @@ ${msg}</user-interrupt>`
46367
46357
  }
46368
46358
  const visibleCeoFollowupIds = ceoFollowupIdsFromMetadata(request.metadata);
46369
46359
  if (!hasSentMessage && visibleCeoFollowupIds.length > 0 && config2.channelPlugin) {
46370
- if (claimOutboundSend(request.userId, textContent)) {
46360
+ if (claimOutboundSend(request.userId, textContent2)) {
46371
46361
  await config2.channelPlugin.outbound.sendText({
46372
46362
  cfg: {},
46373
46363
  to: request.userId,
46374
- text: textContent,
46364
+ text: textContent2,
46375
46365
  accountId: request.requestId
46376
46366
  });
46377
46367
  } else {
46378
46368
  console.log(`[outbound-dedup] \u8DF3\u8FC7\u5BF9\u7528\u6237 ${request.userId} \u7684\u8FD1\u91CD\u590D\u515C\u5E95\u53D1\u9001`);
46379
46369
  }
46380
46370
  hasSentMessage = true;
46381
- sentMessageContent = textContent;
46371
+ sentMessageContent = textContent2;
46382
46372
  completeUserVisibleCeoFollowups();
46383
46373
  }
46384
46374
  if (topicStorage) {
46385
- const topicSummary = hasSentMessage ? sentMessageContent : textContent;
46375
+ const topicSummary = hasSentMessage ? sentMessageContent : textContent2;
46386
46376
  await updateTopicSummary(topicStorage, userId, beijingTime, topicSummary, job?.title);
46387
46377
  }
46388
46378
  markAgentEventsHandled([...injectedEventIds]);
46389
46379
  return {
46390
- content: hasSentMessage ? sentMessageContent : textContent,
46380
+ content: hasSentMessage ? sentMessageContent : textContent2,
46391
46381
  alreadySent: hasSentMessage
46392
46382
  };
46393
46383
  }
@@ -52610,7 +52600,7 @@ var systemRoutes = new Hono2();
52610
52600
  var startTime = Date.now();
52611
52601
  systemRoutes.get("/system/info", (c) => {
52612
52602
  return c.json({
52613
- version: true ? "1.8.44" : "unknown",
52603
+ version: true ? "1.8.46" : "unknown",
52614
52604
  uptime: Math.floor((Date.now() - startTime) / 1e3),
52615
52605
  env: process.env.NODE_ENV || "development",
52616
52606
  nodeVersion: process.version