@adhdev/daemon-core 0.9.35 → 0.9.37

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/index.mjs CHANGED
@@ -1688,27 +1688,86 @@ function sliceFromOffset(text, start) {
1688
1688
  function hydrateCliParsedMessages(parsedMessages, options) {
1689
1689
  const { committedMessages, scope, lastOutputAt } = options;
1690
1690
  const referenceMessages = [...committedMessages];
1691
- const referenceComparables = referenceMessages.map((message) => normalizeComparableMessageContent(message?.content || ""));
1691
+ const referenceComparables = new Array(referenceMessages.length);
1692
1692
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
1693
1693
  const now = options.now ?? Date.now();
1694
- const findReferenceTimestamp = (role, content, parsedIndex) => {
1694
+ let exactReferenceIndexesByKey = null;
1695
+ const exactReferenceCursorByKey = /* @__PURE__ */ new Map();
1696
+ const hasFiniteTimestamp = (message) => typeof message?.timestamp === "number" && Number.isFinite(message.timestamp);
1697
+ const getReferenceComparable = (index) => {
1698
+ if (typeof referenceComparables[index] === "string") return referenceComparables[index] || "";
1699
+ const comparable = normalizeComparableMessageContent(referenceMessages[index]?.content || "");
1700
+ referenceComparables[index] = comparable;
1701
+ return comparable;
1702
+ };
1703
+ const messagesShareStableIdentity = (parsed, reference) => {
1704
+ if (!parsed || !reference) return false;
1705
+ const parsedId = typeof parsed.id === "string" ? parsed.id.trim() : "";
1706
+ const referenceId = typeof reference.id === "string" ? reference.id.trim() : "";
1707
+ if (parsedId && referenceId && parsedId === referenceId) return true;
1708
+ return typeof parsed.index === "number" && Number.isFinite(parsed.index) && typeof reference.index === "number" && Number.isFinite(reference.index) && parsed.index === reference.index;
1709
+ };
1710
+ const exactReferenceKey = (role, comparable) => `${role}\0${comparable}`;
1711
+ const ensureExactReferenceIndex = () => {
1712
+ if (exactReferenceIndexesByKey) return exactReferenceIndexesByKey;
1713
+ const byKey = /* @__PURE__ */ new Map();
1714
+ for (let i = 0; i < referenceMessages.length; i++) {
1715
+ const candidate = referenceMessages[i];
1716
+ if (!candidate || candidate.role !== "user" && candidate.role !== "assistant" || !hasFiniteTimestamp(candidate)) continue;
1717
+ const comparable = getReferenceComparable(i);
1718
+ if (!comparable) continue;
1719
+ const key = exactReferenceKey(candidate.role, comparable);
1720
+ const indexes = byKey.get(key);
1721
+ if (indexes) {
1722
+ indexes.push(i);
1723
+ } else {
1724
+ byKey.set(key, [i]);
1725
+ }
1726
+ }
1727
+ exactReferenceIndexesByKey = byKey;
1728
+ return byKey;
1729
+ };
1730
+ const takeExactReferenceTimestamp = (role, normalizedContent) => {
1731
+ const key = exactReferenceKey(role, normalizedContent);
1732
+ const indexes = ensureExactReferenceIndex().get(key);
1733
+ if (!indexes) return void 0;
1734
+ let cursor = exactReferenceCursorByKey.get(key) || 0;
1735
+ while (cursor < indexes.length) {
1736
+ const candidateIndex = indexes[cursor];
1737
+ cursor += 1;
1738
+ if (usedReferenceIndexes.has(candidateIndex)) continue;
1739
+ const candidate = referenceMessages[candidateIndex];
1740
+ if (!candidate || candidate.role !== role || !hasFiniteTimestamp(candidate)) continue;
1741
+ usedReferenceIndexes.add(candidateIndex);
1742
+ exactReferenceCursorByKey.set(key, cursor);
1743
+ return candidate.timestamp;
1744
+ }
1745
+ exactReferenceCursorByKey.set(key, cursor);
1746
+ return void 0;
1747
+ };
1748
+ const findReferenceTimestamp = (message, role, content, parsedIndex) => {
1749
+ const sameIndex = referenceMessages[parsedIndex];
1750
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && hasFiniteTimestamp(sameIndex) && messagesShareStableIdentity(message, sameIndex)) {
1751
+ usedReferenceIndexes.add(parsedIndex);
1752
+ return sameIndex.timestamp;
1753
+ }
1695
1754
  const normalizedContent = normalizeComparableMessageContent(content);
1696
1755
  if (!normalizedContent) return void 0;
1697
- const sameIndex = referenceMessages[parsedIndex];
1698
- if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && referenceComparables[parsedIndex] === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1756
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && getReferenceComparable(parsedIndex) === normalizedContent && hasFiniteTimestamp(sameIndex)) {
1699
1757
  usedReferenceIndexes.add(parsedIndex);
1700
1758
  return sameIndex.timestamp;
1701
1759
  }
1760
+ const exactTimestamp = takeExactReferenceTimestamp(role, normalizedContent);
1761
+ if (typeof exactTimestamp === "number") return exactTimestamp;
1702
1762
  for (let i = 0; i < referenceMessages.length; i++) {
1703
1763
  if (usedReferenceIndexes.has(i)) continue;
1704
1764
  const candidate = referenceMessages[i];
1705
1765
  if (!candidate || candidate.role !== role) continue;
1706
- const candidateContent = referenceComparables[i];
1766
+ const candidateContent = getReferenceComparable(i);
1707
1767
  if (!candidateContent) continue;
1708
- const exactMatch = candidateContent === normalizedContent;
1709
1768
  const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
1710
- if (!exactMatch && !fuzzyMatch) continue;
1711
- if (typeof candidate.timestamp === "number" && Number.isFinite(candidate.timestamp)) {
1769
+ if (!fuzzyMatch) continue;
1770
+ if (hasFiniteTimestamp(candidate)) {
1712
1771
  usedReferenceIndexes.add(i);
1713
1772
  return candidate.timestamp;
1714
1773
  }
@@ -1719,7 +1778,7 @@ function hydrateCliParsedMessages(parsedMessages, options) {
1719
1778
  const role = message.role;
1720
1779
  const content = typeof message.content === "string" ? message.content : String(message.content || "");
1721
1780
  const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
1722
- const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
1781
+ const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(message, role, content, index);
1723
1782
  const fallbackTimestamp = role === "user" ? scope?.startedAt || now : lastOutputAt || scope?.startedAt || now;
1724
1783
  const timestamp = referenceTimestamp ?? fallbackTimestamp;
1725
1784
  return {
@@ -2224,7 +2283,16 @@ var init_provider_cli_adapter = __esm({
2224
2283
  if (baseMessages.length <= _ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
2225
2284
  return baseMessages.slice(-_ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
2226
2285
  }
2286
+ messagesShareStableIdentity(left, right) {
2287
+ if (left === right) return true;
2288
+ if (!left || !right) return false;
2289
+ if ((left.role || "") !== (right.role || "")) return false;
2290
+ if (left.id && right.id && String(left.id) === String(right.id)) return true;
2291
+ if (typeof left.index === "number" && typeof right.index === "number" && left.index === right.index) return true;
2292
+ return false;
2293
+ }
2227
2294
  messagesComparable(left, right) {
2295
+ if (this.messagesShareStableIdentity(left, right)) return true;
2228
2296
  if (!left || !right) return false;
2229
2297
  if ((left.role || "") !== (right.role || "")) return false;
2230
2298
  const leftText = normalizeComparableTranscriptText(left.content);
@@ -3329,11 +3397,12 @@ var init_provider_cli_adapter = __esm({
3329
3397
  };
3330
3398
  }
3331
3399
  // ─── Public API (CliAdapter) ───────────────────
3332
- getStatus() {
3333
- const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
3400
+ getStatus(options = {}) {
3401
+ const allowParse = options.allowParse !== false;
3402
+ const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
3334
3403
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
3335
3404
  let effectiveModal = startupModal || this.activeModal;
3336
- if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3405
+ if (allowParse && !startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3337
3406
  let parsed = this.getFreshParsedStatusCache();
3338
3407
  if (!parsed && effectiveStatus !== "idle") {
3339
3408
  const now = Date.now();
@@ -3375,6 +3444,69 @@ var init_provider_cli_adapter = __esm({
3375
3444
  this.committedMessages = normalized;
3376
3445
  this.syncMessageViews();
3377
3446
  }
3447
+ getSharedCommittedPrefixLength(parsedMessages) {
3448
+ const committedMessages = this.committedMessages;
3449
+ const max = Math.min(parsedMessages.length, committedMessages.length);
3450
+ let index = 0;
3451
+ while (index < max && this.messagesShareStableIdentity(parsedMessages[index], committedMessages[index])) {
3452
+ index += 1;
3453
+ }
3454
+ return index;
3455
+ }
3456
+ hydrateCommittedPrefixForParsedStatus(parsedMessages) {
3457
+ const sharedPrefixLength = this.getSharedCommittedPrefixLength(parsedMessages);
3458
+ if (sharedPrefixLength !== this.committedMessages.length) return null;
3459
+ const committedHydratedMessages = this.committedMessages.map((message, index) => {
3460
+ const timestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : this.lastOutputAt || this.currentTurnScope?.startedAt || Date.now();
3461
+ const contentValue = message.content;
3462
+ return {
3463
+ role: message.role,
3464
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
3465
+ timestamp,
3466
+ receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp,
3467
+ kind: message.kind,
3468
+ id: message.id || `msg_${index}`,
3469
+ index: typeof message.index === "number" ? message.index : index,
3470
+ meta: message.meta,
3471
+ senderName: message.senderName
3472
+ };
3473
+ });
3474
+ const extraMessages = parsedMessages.slice(sharedPrefixLength);
3475
+ if (extraMessages.length === 0) return committedHydratedMessages;
3476
+ const extraHydratedMessages = hydrateCliParsedMessages(extraMessages, {
3477
+ committedMessages: [],
3478
+ scope: this.currentTurnScope,
3479
+ lastOutputAt: this.lastOutputAt
3480
+ }).map((message, offset) => ({
3481
+ ...message,
3482
+ id: message.id || `msg_${sharedPrefixLength + offset}`,
3483
+ index: typeof message.index === "number" ? message.index : sharedPrefixLength + offset
3484
+ }));
3485
+ return [...committedHydratedMessages, ...extraHydratedMessages];
3486
+ }
3487
+ hydrateParsedMessagesForStatus(parsedMessages) {
3488
+ return this.hydrateCommittedPrefixForParsedStatus(parsedMessages) || hydrateCliParsedMessages(parsedMessages, {
3489
+ committedMessages: this.committedMessages,
3490
+ scope: this.currentTurnScope,
3491
+ lastOutputAt: this.lastOutputAt
3492
+ });
3493
+ }
3494
+ buildCommittedChatMessages() {
3495
+ return this.committedMessages.map((message, index) => {
3496
+ const contentValue = message.content;
3497
+ return buildChatMessage({
3498
+ role: message.role,
3499
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
3500
+ timestamp: message.timestamp,
3501
+ kind: message.kind,
3502
+ meta: message.meta,
3503
+ senderName: message.senderName,
3504
+ id: message.id || `msg_${index}`,
3505
+ index: typeof message.index === "number" ? message.index : index,
3506
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3507
+ });
3508
+ });
3509
+ }
3378
3510
  /**
3379
3511
  * Script-based full parse — returns ReadChatResult.
3380
3512
  * Called by command handler / dashboard for rich content rendering.
@@ -3400,7 +3532,7 @@ var init_provider_cli_adapter = __esm({
3400
3532
  this.onStatusChange?.();
3401
3533
  }
3402
3534
  }
3403
- if (parsed && Array.isArray(parsed.messages)) {
3535
+ if (parsed && Array.isArray(parsed.messages) && this.provider.allowInputDuringGeneration === true) {
3404
3536
  const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
3405
3537
  committedMessages: this.committedMessages,
3406
3538
  scope: this.currentTurnScope,
@@ -3419,21 +3551,21 @@ var init_provider_cli_adapter = __esm({
3419
3551
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
3420
3552
  let result;
3421
3553
  if (parsed && Array.isArray(parsed.messages)) {
3422
- const parsedHydratedMessages = hydrateCliParsedMessages(parsed.messages, {
3423
- committedMessages: this.committedMessages,
3424
- scope: this.currentTurnScope,
3425
- lastOutputAt: this.lastOutputAt
3426
- });
3427
- const committedHydratedMessages = this.committedMessages.map((message, index) => buildChatMessage({
3428
- ...message,
3429
- id: message.id || `msg_${index}`,
3430
- index: typeof message.index === "number" ? message.index : index,
3431
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3432
- }));
3554
+ const parsedHydratedMessages = this.hydrateParsedMessagesForStatus(parsed.messages);
3433
3555
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
3434
- const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, committedHydratedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle");
3556
+ const shouldAdoptParsedIdleReplay = !this.currentTurnScope && !this.activeModal && !!parsedLastAssistant && parsedTranscriptIsRicherThanCommitted(parsedHydratedMessages, this.committedMessages) && (this.currentStatus === "idle" || this.currentStatus === "generating" && this.isWaitingForResponse && parsed.status === "idle" && this.runDetectStatus(this.recentOutputBuffer) === "idle");
3435
3557
  if (shouldAdoptParsedIdleReplay) {
3436
- this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
3558
+ this.committedMessages = this.getSharedCommittedPrefixLength(parsed.messages) === this.committedMessages.length ? parsedHydratedMessages.map((message) => ({
3559
+ role: message.role,
3560
+ content: typeof message.content === "string" ? message.content : String(message.content || ""),
3561
+ timestamp: message.timestamp,
3562
+ receivedAt: message.receivedAt,
3563
+ kind: message.kind,
3564
+ id: message.id,
3565
+ index: message.index,
3566
+ meta: message.meta,
3567
+ senderName: message.senderName
3568
+ })) : normalizeCliParsedMessages(parsed.messages, {
3437
3569
  committedMessages: this.committedMessages,
3438
3570
  scope: this.currentTurnScope,
3439
3571
  lastOutputAt: this.lastOutputAt
@@ -3452,15 +3584,9 @@ var init_provider_cli_adapter = __esm({
3452
3584
  this.onStatusChange?.();
3453
3585
  }
3454
3586
  }
3455
- const effectiveCommittedHydratedMessages = shouldAdoptParsedIdleReplay ? this.committedMessages.map((message, index) => buildChatMessage({
3456
- ...message,
3457
- id: message.id || `msg_${index}`,
3458
- index: typeof message.index === "number" ? message.index : index,
3459
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3460
- })) : committedHydratedMessages;
3461
- const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && effectiveCommittedHydratedMessages.length > parsedHydratedMessages.length;
3587
+ const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && this.committedMessages.length > parsedHydratedMessages.length;
3462
3588
  const shouldPreferCommittedIdleReplay = shouldPreferCommittedMessages && !shouldAdoptParsedIdleReplay;
3463
- const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? effectiveCommittedHydratedMessages : parsedHydratedMessages;
3589
+ const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? this.buildCommittedChatMessages() : parsedHydratedMessages;
3464
3590
  result = {
3465
3591
  id: parsed.id || "cli_session",
3466
3592
  status: parsed.status || this.currentStatus,
@@ -13332,7 +13458,7 @@ var CliProviderInstance = class {
13332
13458
  return this.presentationMode;
13333
13459
  }
13334
13460
  getHotChatSessionState() {
13335
- const adapterStatus = this.adapter.getStatus();
13461
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
13336
13462
  const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
13337
13463
  const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
13338
13464
  const runtime = this.adapter.getRuntimeMetadata();