@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.
@@ -117,6 +117,7 @@ export declare class ProviderCliAdapter implements CliAdapter {
117
117
  private resetTerminalScreen;
118
118
  private getFreshParsedStatusCache;
119
119
  private selectParseBaseMessages;
120
+ private messagesShareStableIdentity;
120
121
  private messagesComparable;
121
122
  private stitchParsedMessagesWithCommittedBase;
122
123
  private getIdleFinishConfirmMs;
@@ -171,8 +172,14 @@ export declare class ProviderCliAdapter implements CliAdapter {
171
172
  private hasActionableApproval;
172
173
  private projectEffectiveStatus;
173
174
  private suppressStaleParsedApproval;
174
- getStatus(): CliSessionStatus;
175
+ getStatus(options?: {
176
+ allowParse?: boolean;
177
+ }): CliSessionStatus;
175
178
  seedCommittedMessages(messages: SeedCliChatMessage[]): void;
179
+ private getSharedCommittedPrefixLength;
180
+ private hydrateCommittedPrefixForParsedStatus;
181
+ private hydrateParsedMessagesForStatus;
182
+ private buildCommittedChatMessages;
176
183
  /**
177
184
  * Script-based full parse — returns ReadChatResult.
178
185
  * Called by command handler / dashboard for rich content rendering.
package/dist/index.js CHANGED
@@ -1690,27 +1690,86 @@ function sliceFromOffset(text, start) {
1690
1690
  function hydrateCliParsedMessages(parsedMessages, options) {
1691
1691
  const { committedMessages, scope, lastOutputAt } = options;
1692
1692
  const referenceMessages = [...committedMessages];
1693
- const referenceComparables = referenceMessages.map((message) => normalizeComparableMessageContent(message?.content || ""));
1693
+ const referenceComparables = new Array(referenceMessages.length);
1694
1694
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
1695
1695
  const now = options.now ?? Date.now();
1696
- const findReferenceTimestamp = (role, content, parsedIndex) => {
1696
+ let exactReferenceIndexesByKey = null;
1697
+ const exactReferenceCursorByKey = /* @__PURE__ */ new Map();
1698
+ const hasFiniteTimestamp = (message) => typeof message?.timestamp === "number" && Number.isFinite(message.timestamp);
1699
+ const getReferenceComparable = (index) => {
1700
+ if (typeof referenceComparables[index] === "string") return referenceComparables[index] || "";
1701
+ const comparable = normalizeComparableMessageContent(referenceMessages[index]?.content || "");
1702
+ referenceComparables[index] = comparable;
1703
+ return comparable;
1704
+ };
1705
+ const messagesShareStableIdentity = (parsed, reference) => {
1706
+ if (!parsed || !reference) return false;
1707
+ const parsedId = typeof parsed.id === "string" ? parsed.id.trim() : "";
1708
+ const referenceId = typeof reference.id === "string" ? reference.id.trim() : "";
1709
+ if (parsedId && referenceId && parsedId === referenceId) return true;
1710
+ return typeof parsed.index === "number" && Number.isFinite(parsed.index) && typeof reference.index === "number" && Number.isFinite(reference.index) && parsed.index === reference.index;
1711
+ };
1712
+ const exactReferenceKey = (role, comparable) => `${role}\0${comparable}`;
1713
+ const ensureExactReferenceIndex = () => {
1714
+ if (exactReferenceIndexesByKey) return exactReferenceIndexesByKey;
1715
+ const byKey = /* @__PURE__ */ new Map();
1716
+ for (let i = 0; i < referenceMessages.length; i++) {
1717
+ const candidate = referenceMessages[i];
1718
+ if (!candidate || candidate.role !== "user" && candidate.role !== "assistant" || !hasFiniteTimestamp(candidate)) continue;
1719
+ const comparable = getReferenceComparable(i);
1720
+ if (!comparable) continue;
1721
+ const key = exactReferenceKey(candidate.role, comparable);
1722
+ const indexes = byKey.get(key);
1723
+ if (indexes) {
1724
+ indexes.push(i);
1725
+ } else {
1726
+ byKey.set(key, [i]);
1727
+ }
1728
+ }
1729
+ exactReferenceIndexesByKey = byKey;
1730
+ return byKey;
1731
+ };
1732
+ const takeExactReferenceTimestamp = (role, normalizedContent) => {
1733
+ const key = exactReferenceKey(role, normalizedContent);
1734
+ const indexes = ensureExactReferenceIndex().get(key);
1735
+ if (!indexes) return void 0;
1736
+ let cursor = exactReferenceCursorByKey.get(key) || 0;
1737
+ while (cursor < indexes.length) {
1738
+ const candidateIndex = indexes[cursor];
1739
+ cursor += 1;
1740
+ if (usedReferenceIndexes.has(candidateIndex)) continue;
1741
+ const candidate = referenceMessages[candidateIndex];
1742
+ if (!candidate || candidate.role !== role || !hasFiniteTimestamp(candidate)) continue;
1743
+ usedReferenceIndexes.add(candidateIndex);
1744
+ exactReferenceCursorByKey.set(key, cursor);
1745
+ return candidate.timestamp;
1746
+ }
1747
+ exactReferenceCursorByKey.set(key, cursor);
1748
+ return void 0;
1749
+ };
1750
+ const findReferenceTimestamp = (message, role, content, parsedIndex) => {
1751
+ const sameIndex = referenceMessages[parsedIndex];
1752
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && hasFiniteTimestamp(sameIndex) && messagesShareStableIdentity(message, sameIndex)) {
1753
+ usedReferenceIndexes.add(parsedIndex);
1754
+ return sameIndex.timestamp;
1755
+ }
1697
1756
  const normalizedContent = normalizeComparableMessageContent(content);
1698
1757
  if (!normalizedContent) return void 0;
1699
- const sameIndex = referenceMessages[parsedIndex];
1700
- if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && referenceComparables[parsedIndex] === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
1758
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && getReferenceComparable(parsedIndex) === normalizedContent && hasFiniteTimestamp(sameIndex)) {
1701
1759
  usedReferenceIndexes.add(parsedIndex);
1702
1760
  return sameIndex.timestamp;
1703
1761
  }
1762
+ const exactTimestamp = takeExactReferenceTimestamp(role, normalizedContent);
1763
+ if (typeof exactTimestamp === "number") return exactTimestamp;
1704
1764
  for (let i = 0; i < referenceMessages.length; i++) {
1705
1765
  if (usedReferenceIndexes.has(i)) continue;
1706
1766
  const candidate = referenceMessages[i];
1707
1767
  if (!candidate || candidate.role !== role) continue;
1708
- const candidateContent = referenceComparables[i];
1768
+ const candidateContent = getReferenceComparable(i);
1709
1769
  if (!candidateContent) continue;
1710
- const exactMatch = candidateContent === normalizedContent;
1711
1770
  const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
1712
- if (!exactMatch && !fuzzyMatch) continue;
1713
- if (typeof candidate.timestamp === "number" && Number.isFinite(candidate.timestamp)) {
1771
+ if (!fuzzyMatch) continue;
1772
+ if (hasFiniteTimestamp(candidate)) {
1714
1773
  usedReferenceIndexes.add(i);
1715
1774
  return candidate.timestamp;
1716
1775
  }
@@ -1721,7 +1780,7 @@ function hydrateCliParsedMessages(parsedMessages, options) {
1721
1780
  const role = message.role;
1722
1781
  const content = typeof message.content === "string" ? message.content : String(message.content || "");
1723
1782
  const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
1724
- const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
1783
+ const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(message, role, content, index);
1725
1784
  const fallbackTimestamp = role === "user" ? scope?.startedAt || now : lastOutputAt || scope?.startedAt || now;
1726
1785
  const timestamp = referenceTimestamp ?? fallbackTimestamp;
1727
1786
  return {
@@ -2227,7 +2286,16 @@ var init_provider_cli_adapter = __esm({
2227
2286
  if (baseMessages.length <= _ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
2228
2287
  return baseMessages.slice(-_ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
2229
2288
  }
2289
+ messagesShareStableIdentity(left, right) {
2290
+ if (left === right) return true;
2291
+ if (!left || !right) return false;
2292
+ if ((left.role || "") !== (right.role || "")) return false;
2293
+ if (left.id && right.id && String(left.id) === String(right.id)) return true;
2294
+ if (typeof left.index === "number" && typeof right.index === "number" && left.index === right.index) return true;
2295
+ return false;
2296
+ }
2230
2297
  messagesComparable(left, right) {
2298
+ if (this.messagesShareStableIdentity(left, right)) return true;
2231
2299
  if (!left || !right) return false;
2232
2300
  if ((left.role || "") !== (right.role || "")) return false;
2233
2301
  const leftText = normalizeComparableTranscriptText(left.content);
@@ -3332,11 +3400,12 @@ var init_provider_cli_adapter = __esm({
3332
3400
  };
3333
3401
  }
3334
3402
  // ─── Public API (CliAdapter) ───────────────────
3335
- getStatus() {
3336
- const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
3403
+ getStatus(options = {}) {
3404
+ const allowParse = options.allowParse !== false;
3405
+ const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
3337
3406
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
3338
3407
  let effectiveModal = startupModal || this.activeModal;
3339
- if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3408
+ if (allowParse && !startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
3340
3409
  let parsed = this.getFreshParsedStatusCache();
3341
3410
  if (!parsed && effectiveStatus !== "idle") {
3342
3411
  const now = Date.now();
@@ -3378,6 +3447,69 @@ var init_provider_cli_adapter = __esm({
3378
3447
  this.committedMessages = normalized;
3379
3448
  this.syncMessageViews();
3380
3449
  }
3450
+ getSharedCommittedPrefixLength(parsedMessages) {
3451
+ const committedMessages = this.committedMessages;
3452
+ const max = Math.min(parsedMessages.length, committedMessages.length);
3453
+ let index = 0;
3454
+ while (index < max && this.messagesShareStableIdentity(parsedMessages[index], committedMessages[index])) {
3455
+ index += 1;
3456
+ }
3457
+ return index;
3458
+ }
3459
+ hydrateCommittedPrefixForParsedStatus(parsedMessages) {
3460
+ const sharedPrefixLength = this.getSharedCommittedPrefixLength(parsedMessages);
3461
+ if (sharedPrefixLength !== this.committedMessages.length) return null;
3462
+ const committedHydratedMessages = this.committedMessages.map((message, index) => {
3463
+ const timestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : this.lastOutputAt || this.currentTurnScope?.startedAt || Date.now();
3464
+ const contentValue = message.content;
3465
+ return {
3466
+ role: message.role,
3467
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
3468
+ timestamp,
3469
+ receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp,
3470
+ kind: message.kind,
3471
+ id: message.id || `msg_${index}`,
3472
+ index: typeof message.index === "number" ? message.index : index,
3473
+ meta: message.meta,
3474
+ senderName: message.senderName
3475
+ };
3476
+ });
3477
+ const extraMessages = parsedMessages.slice(sharedPrefixLength);
3478
+ if (extraMessages.length === 0) return committedHydratedMessages;
3479
+ const extraHydratedMessages = hydrateCliParsedMessages(extraMessages, {
3480
+ committedMessages: [],
3481
+ scope: this.currentTurnScope,
3482
+ lastOutputAt: this.lastOutputAt
3483
+ }).map((message, offset) => ({
3484
+ ...message,
3485
+ id: message.id || `msg_${sharedPrefixLength + offset}`,
3486
+ index: typeof message.index === "number" ? message.index : sharedPrefixLength + offset
3487
+ }));
3488
+ return [...committedHydratedMessages, ...extraHydratedMessages];
3489
+ }
3490
+ hydrateParsedMessagesForStatus(parsedMessages) {
3491
+ return this.hydrateCommittedPrefixForParsedStatus(parsedMessages) || hydrateCliParsedMessages(parsedMessages, {
3492
+ committedMessages: this.committedMessages,
3493
+ scope: this.currentTurnScope,
3494
+ lastOutputAt: this.lastOutputAt
3495
+ });
3496
+ }
3497
+ buildCommittedChatMessages() {
3498
+ return this.committedMessages.map((message, index) => {
3499
+ const contentValue = message.content;
3500
+ return buildChatMessage({
3501
+ role: message.role,
3502
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
3503
+ timestamp: message.timestamp,
3504
+ kind: message.kind,
3505
+ meta: message.meta,
3506
+ senderName: message.senderName,
3507
+ id: message.id || `msg_${index}`,
3508
+ index: typeof message.index === "number" ? message.index : index,
3509
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3510
+ });
3511
+ });
3512
+ }
3381
3513
  /**
3382
3514
  * Script-based full parse — returns ReadChatResult.
3383
3515
  * Called by command handler / dashboard for rich content rendering.
@@ -3403,7 +3535,7 @@ var init_provider_cli_adapter = __esm({
3403
3535
  this.onStatusChange?.();
3404
3536
  }
3405
3537
  }
3406
- if (parsed && Array.isArray(parsed.messages)) {
3538
+ if (parsed && Array.isArray(parsed.messages) && this.provider.allowInputDuringGeneration === true) {
3407
3539
  const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
3408
3540
  committedMessages: this.committedMessages,
3409
3541
  scope: this.currentTurnScope,
@@ -3422,21 +3554,21 @@ var init_provider_cli_adapter = __esm({
3422
3554
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
3423
3555
  let result;
3424
3556
  if (parsed && Array.isArray(parsed.messages)) {
3425
- const parsedHydratedMessages = hydrateCliParsedMessages(parsed.messages, {
3426
- committedMessages: this.committedMessages,
3427
- scope: this.currentTurnScope,
3428
- lastOutputAt: this.lastOutputAt
3429
- });
3430
- const committedHydratedMessages = this.committedMessages.map((message, index) => buildChatMessage({
3431
- ...message,
3432
- id: message.id || `msg_${index}`,
3433
- index: typeof message.index === "number" ? message.index : index,
3434
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3435
- }));
3557
+ const parsedHydratedMessages = this.hydrateParsedMessagesForStatus(parsed.messages);
3436
3558
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
3437
- 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");
3559
+ 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");
3438
3560
  if (shouldAdoptParsedIdleReplay) {
3439
- this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
3561
+ this.committedMessages = this.getSharedCommittedPrefixLength(parsed.messages) === this.committedMessages.length ? parsedHydratedMessages.map((message) => ({
3562
+ role: message.role,
3563
+ content: typeof message.content === "string" ? message.content : String(message.content || ""),
3564
+ timestamp: message.timestamp,
3565
+ receivedAt: message.receivedAt,
3566
+ kind: message.kind,
3567
+ id: message.id,
3568
+ index: message.index,
3569
+ meta: message.meta,
3570
+ senderName: message.senderName
3571
+ })) : normalizeCliParsedMessages(parsed.messages, {
3440
3572
  committedMessages: this.committedMessages,
3441
3573
  scope: this.currentTurnScope,
3442
3574
  lastOutputAt: this.lastOutputAt
@@ -3455,15 +3587,9 @@ var init_provider_cli_adapter = __esm({
3455
3587
  this.onStatusChange?.();
3456
3588
  }
3457
3589
  }
3458
- const effectiveCommittedHydratedMessages = shouldAdoptParsedIdleReplay ? this.committedMessages.map((message, index) => buildChatMessage({
3459
- ...message,
3460
- id: message.id || `msg_${index}`,
3461
- index: typeof message.index === "number" ? message.index : index,
3462
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
3463
- })) : committedHydratedMessages;
3464
- const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && effectiveCommittedHydratedMessages.length > parsedHydratedMessages.length;
3590
+ const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && this.committedMessages.length > parsedHydratedMessages.length;
3465
3591
  const shouldPreferCommittedIdleReplay = shouldPreferCommittedMessages && !shouldAdoptParsedIdleReplay;
3466
- const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? effectiveCommittedHydratedMessages : parsedHydratedMessages;
3592
+ const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? this.buildCommittedChatMessages() : parsedHydratedMessages;
3467
3593
  result = {
3468
3594
  id: parsed.id || "cli_session",
3469
3595
  status: parsed.status || this.currentStatus,
@@ -13485,7 +13611,7 @@ var CliProviderInstance = class {
13485
13611
  return this.presentationMode;
13486
13612
  }
13487
13613
  getHotChatSessionState() {
13488
- const adapterStatus = this.adapter.getStatus();
13614
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
13489
13615
  const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
13490
13616
  const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
13491
13617
  const runtime = this.adapter.getRuntimeMetadata();