adhdev 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.js CHANGED
@@ -11420,27 +11420,86 @@ function sliceFromOffset(text, start) {
11420
11420
  function hydrateCliParsedMessages(parsedMessages, options) {
11421
11421
  const { committedMessages, scope, lastOutputAt } = options;
11422
11422
  const referenceMessages = [...committedMessages];
11423
- const referenceComparables = referenceMessages.map((message) => normalizeComparableMessageContent(message?.content || ""));
11423
+ const referenceComparables = new Array(referenceMessages.length);
11424
11424
  const usedReferenceIndexes = /* @__PURE__ */ new Set();
11425
11425
  const now = options.now ?? Date.now();
11426
- const findReferenceTimestamp = (role, content, parsedIndex) => {
11426
+ let exactReferenceIndexesByKey = null;
11427
+ const exactReferenceCursorByKey = /* @__PURE__ */ new Map();
11428
+ const hasFiniteTimestamp = (message) => typeof message?.timestamp === "number" && Number.isFinite(message.timestamp);
11429
+ const getReferenceComparable = (index) => {
11430
+ if (typeof referenceComparables[index] === "string") return referenceComparables[index] || "";
11431
+ const comparable = normalizeComparableMessageContent(referenceMessages[index]?.content || "");
11432
+ referenceComparables[index] = comparable;
11433
+ return comparable;
11434
+ };
11435
+ const messagesShareStableIdentity = (parsed, reference) => {
11436
+ if (!parsed || !reference) return false;
11437
+ const parsedId = typeof parsed.id === "string" ? parsed.id.trim() : "";
11438
+ const referenceId = typeof reference.id === "string" ? reference.id.trim() : "";
11439
+ if (parsedId && referenceId && parsedId === referenceId) return true;
11440
+ return typeof parsed.index === "number" && Number.isFinite(parsed.index) && typeof reference.index === "number" && Number.isFinite(reference.index) && parsed.index === reference.index;
11441
+ };
11442
+ const exactReferenceKey = (role, comparable) => `${role}\0${comparable}`;
11443
+ const ensureExactReferenceIndex = () => {
11444
+ if (exactReferenceIndexesByKey) return exactReferenceIndexesByKey;
11445
+ const byKey = /* @__PURE__ */ new Map();
11446
+ for (let i = 0; i < referenceMessages.length; i++) {
11447
+ const candidate = referenceMessages[i];
11448
+ if (!candidate || candidate.role !== "user" && candidate.role !== "assistant" || !hasFiniteTimestamp(candidate)) continue;
11449
+ const comparable = getReferenceComparable(i);
11450
+ if (!comparable) continue;
11451
+ const key = exactReferenceKey(candidate.role, comparable);
11452
+ const indexes = byKey.get(key);
11453
+ if (indexes) {
11454
+ indexes.push(i);
11455
+ } else {
11456
+ byKey.set(key, [i]);
11457
+ }
11458
+ }
11459
+ exactReferenceIndexesByKey = byKey;
11460
+ return byKey;
11461
+ };
11462
+ const takeExactReferenceTimestamp = (role, normalizedContent) => {
11463
+ const key = exactReferenceKey(role, normalizedContent);
11464
+ const indexes = ensureExactReferenceIndex().get(key);
11465
+ if (!indexes) return void 0;
11466
+ let cursor = exactReferenceCursorByKey.get(key) || 0;
11467
+ while (cursor < indexes.length) {
11468
+ const candidateIndex = indexes[cursor];
11469
+ cursor += 1;
11470
+ if (usedReferenceIndexes.has(candidateIndex)) continue;
11471
+ const candidate = referenceMessages[candidateIndex];
11472
+ if (!candidate || candidate.role !== role || !hasFiniteTimestamp(candidate)) continue;
11473
+ usedReferenceIndexes.add(candidateIndex);
11474
+ exactReferenceCursorByKey.set(key, cursor);
11475
+ return candidate.timestamp;
11476
+ }
11477
+ exactReferenceCursorByKey.set(key, cursor);
11478
+ return void 0;
11479
+ };
11480
+ const findReferenceTimestamp = (message, role, content, parsedIndex) => {
11481
+ const sameIndex = referenceMessages[parsedIndex];
11482
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && hasFiniteTimestamp(sameIndex) && messagesShareStableIdentity(message, sameIndex)) {
11483
+ usedReferenceIndexes.add(parsedIndex);
11484
+ return sameIndex.timestamp;
11485
+ }
11427
11486
  const normalizedContent = normalizeComparableMessageContent(content);
11428
11487
  if (!normalizedContent) return void 0;
11429
- const sameIndex = referenceMessages[parsedIndex];
11430
- if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && referenceComparables[parsedIndex] === normalizedContent && typeof sameIndex.timestamp === "number" && Number.isFinite(sameIndex.timestamp)) {
11488
+ if (sameIndex && !usedReferenceIndexes.has(parsedIndex) && sameIndex.role === role && getReferenceComparable(parsedIndex) === normalizedContent && hasFiniteTimestamp(sameIndex)) {
11431
11489
  usedReferenceIndexes.add(parsedIndex);
11432
11490
  return sameIndex.timestamp;
11433
11491
  }
11492
+ const exactTimestamp = takeExactReferenceTimestamp(role, normalizedContent);
11493
+ if (typeof exactTimestamp === "number") return exactTimestamp;
11434
11494
  for (let i = 0; i < referenceMessages.length; i++) {
11435
11495
  if (usedReferenceIndexes.has(i)) continue;
11436
11496
  const candidate = referenceMessages[i];
11437
11497
  if (!candidate || candidate.role !== role) continue;
11438
- const candidateContent = referenceComparables[i];
11498
+ const candidateContent = getReferenceComparable(i);
11439
11499
  if (!candidateContent) continue;
11440
- const exactMatch = candidateContent === normalizedContent;
11441
11500
  const fuzzyMatch = candidateContent.includes(normalizedContent) || normalizedContent.includes(candidateContent);
11442
- if (!exactMatch && !fuzzyMatch) continue;
11443
- if (typeof candidate.timestamp === "number" && Number.isFinite(candidate.timestamp)) {
11501
+ if (!fuzzyMatch) continue;
11502
+ if (hasFiniteTimestamp(candidate)) {
11444
11503
  usedReferenceIndexes.add(i);
11445
11504
  return candidate.timestamp;
11446
11505
  }
@@ -11451,7 +11510,7 @@ function hydrateCliParsedMessages(parsedMessages, options) {
11451
11510
  const role = message.role;
11452
11511
  const content = typeof message.content === "string" ? message.content : String(message.content || "");
11453
11512
  const parsedTimestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : void 0;
11454
- const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(role, content, index);
11513
+ const referenceTimestamp = parsedTimestamp ?? findReferenceTimestamp(message, role, content, index);
11455
11514
  const fallbackTimestamp = role === "user" ? scope?.startedAt || now : lastOutputAt || scope?.startedAt || now;
11456
11515
  const timestamp = referenceTimestamp ?? fallbackTimestamp;
11457
11516
  return {
@@ -11957,7 +12016,16 @@ var init_provider_cli_adapter = __esm({
11957
12016
  if (baseMessages.length <= _ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT) return baseMessages;
11958
12017
  return baseMessages.slice(-_ProviderCliAdapter.PARSE_MESSAGE_TAIL_LIMIT);
11959
12018
  }
12019
+ messagesShareStableIdentity(left2, right2) {
12020
+ if (left2 === right2) return true;
12021
+ if (!left2 || !right2) return false;
12022
+ if ((left2.role || "") !== (right2.role || "")) return false;
12023
+ if (left2.id && right2.id && String(left2.id) === String(right2.id)) return true;
12024
+ if (typeof left2.index === "number" && typeof right2.index === "number" && left2.index === right2.index) return true;
12025
+ return false;
12026
+ }
11960
12027
  messagesComparable(left2, right2) {
12028
+ if (this.messagesShareStableIdentity(left2, right2)) return true;
11961
12029
  if (!left2 || !right2) return false;
11962
12030
  if ((left2.role || "") !== (right2.role || "")) return false;
11963
12031
  const leftText = normalizeComparableTranscriptText(left2.content);
@@ -13062,11 +13130,12 @@ var init_provider_cli_adapter = __esm({
13062
13130
  };
13063
13131
  }
13064
13132
  // ─── Public API (CliAdapter) ───────────────────
13065
- getStatus() {
13066
- const startupModal = this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
13133
+ getStatus(options = {}) {
13134
+ const allowParse = options.allowParse !== false;
13135
+ const startupModal = allowParse && this.startupParseGate ? this.runParseApproval(this.recentOutputBuffer) : null;
13067
13136
  let effectiveStatus = this.projectEffectiveStatus(startupModal);
13068
13137
  let effectiveModal = startupModal || this.activeModal;
13069
- if (!startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
13138
+ if (allowParse && !startupModal && !effectiveModal && typeof this.cliScripts?.parseOutput === "function") {
13070
13139
  let parsed = this.getFreshParsedStatusCache();
13071
13140
  if (!parsed && effectiveStatus !== "idle") {
13072
13141
  const now = Date.now();
@@ -13108,6 +13177,69 @@ var init_provider_cli_adapter = __esm({
13108
13177
  this.committedMessages = normalized;
13109
13178
  this.syncMessageViews();
13110
13179
  }
13180
+ getSharedCommittedPrefixLength(parsedMessages) {
13181
+ const committedMessages = this.committedMessages;
13182
+ const max = Math.min(parsedMessages.length, committedMessages.length);
13183
+ let index = 0;
13184
+ while (index < max && this.messagesShareStableIdentity(parsedMessages[index], committedMessages[index])) {
13185
+ index += 1;
13186
+ }
13187
+ return index;
13188
+ }
13189
+ hydrateCommittedPrefixForParsedStatus(parsedMessages) {
13190
+ const sharedPrefixLength = this.getSharedCommittedPrefixLength(parsedMessages);
13191
+ if (sharedPrefixLength !== this.committedMessages.length) return null;
13192
+ const committedHydratedMessages = this.committedMessages.map((message, index) => {
13193
+ const timestamp = typeof message.timestamp === "number" && Number.isFinite(message.timestamp) ? message.timestamp : this.lastOutputAt || this.currentTurnScope?.startedAt || Date.now();
13194
+ const contentValue = message.content;
13195
+ return {
13196
+ role: message.role,
13197
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
13198
+ timestamp,
13199
+ receivedAt: typeof message.receivedAt === "number" && Number.isFinite(message.receivedAt) ? message.receivedAt : timestamp,
13200
+ kind: message.kind,
13201
+ id: message.id || `msg_${index}`,
13202
+ index: typeof message.index === "number" ? message.index : index,
13203
+ meta: message.meta,
13204
+ senderName: message.senderName
13205
+ };
13206
+ });
13207
+ const extraMessages = parsedMessages.slice(sharedPrefixLength);
13208
+ if (extraMessages.length === 0) return committedHydratedMessages;
13209
+ const extraHydratedMessages = hydrateCliParsedMessages(extraMessages, {
13210
+ committedMessages: [],
13211
+ scope: this.currentTurnScope,
13212
+ lastOutputAt: this.lastOutputAt
13213
+ }).map((message, offset) => ({
13214
+ ...message,
13215
+ id: message.id || `msg_${sharedPrefixLength + offset}`,
13216
+ index: typeof message.index === "number" ? message.index : sharedPrefixLength + offset
13217
+ }));
13218
+ return [...committedHydratedMessages, ...extraHydratedMessages];
13219
+ }
13220
+ hydrateParsedMessagesForStatus(parsedMessages) {
13221
+ return this.hydrateCommittedPrefixForParsedStatus(parsedMessages) || hydrateCliParsedMessages(parsedMessages, {
13222
+ committedMessages: this.committedMessages,
13223
+ scope: this.currentTurnScope,
13224
+ lastOutputAt: this.lastOutputAt
13225
+ });
13226
+ }
13227
+ buildCommittedChatMessages() {
13228
+ return this.committedMessages.map((message, index) => {
13229
+ const contentValue = message.content;
13230
+ return buildChatMessage({
13231
+ role: message.role,
13232
+ content: typeof contentValue === "string" ? contentValue : String(contentValue || ""),
13233
+ timestamp: message.timestamp,
13234
+ kind: message.kind,
13235
+ meta: message.meta,
13236
+ senderName: message.senderName,
13237
+ id: message.id || `msg_${index}`,
13238
+ index: typeof message.index === "number" ? message.index : index,
13239
+ receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
13240
+ });
13241
+ });
13242
+ }
13111
13243
  /**
13112
13244
  * Script-based full parse — returns ReadChatResult.
13113
13245
  * Called by command handler / dashboard for rich content rendering.
@@ -13133,7 +13265,7 @@ var init_provider_cli_adapter = __esm({
13133
13265
  this.onStatusChange?.();
13134
13266
  }
13135
13267
  }
13136
- if (parsed && Array.isArray(parsed.messages)) {
13268
+ if (parsed && Array.isArray(parsed.messages) && this.provider.allowInputDuringGeneration === true) {
13137
13269
  const hydratedForCommit = normalizeCliParsedMessages(parsed.messages, {
13138
13270
  committedMessages: this.committedMessages,
13139
13271
  scope: this.currentTurnScope,
@@ -13152,21 +13284,21 @@ var init_provider_cli_adapter = __esm({
13152
13284
  const shouldPreferCommittedMessages = !this.currentTurnScope && !this.activeModal && this.currentStatus === "idle";
13153
13285
  let result;
13154
13286
  if (parsed && Array.isArray(parsed.messages)) {
13155
- const parsedHydratedMessages = hydrateCliParsedMessages(parsed.messages, {
13156
- committedMessages: this.committedMessages,
13157
- scope: this.currentTurnScope,
13158
- lastOutputAt: this.lastOutputAt
13159
- });
13160
- const committedHydratedMessages = this.committedMessages.map((message, index) => buildChatMessage({
13161
- ...message,
13162
- id: message.id || `msg_${index}`,
13163
- index: typeof message.index === "number" ? message.index : index,
13164
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
13165
- }));
13287
+ const parsedHydratedMessages = this.hydrateParsedMessagesForStatus(parsed.messages);
13166
13288
  const parsedLastAssistant = [...parsedHydratedMessages].reverse().find((message) => message.role === "assistant" && typeof message.content === "string" && message.content.trim());
13167
- 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");
13289
+ 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");
13168
13290
  if (shouldAdoptParsedIdleReplay) {
13169
- this.committedMessages = normalizeCliParsedMessages(parsed.messages, {
13291
+ this.committedMessages = this.getSharedCommittedPrefixLength(parsed.messages) === this.committedMessages.length ? parsedHydratedMessages.map((message) => ({
13292
+ role: message.role,
13293
+ content: typeof message.content === "string" ? message.content : String(message.content || ""),
13294
+ timestamp: message.timestamp,
13295
+ receivedAt: message.receivedAt,
13296
+ kind: message.kind,
13297
+ id: message.id,
13298
+ index: message.index,
13299
+ meta: message.meta,
13300
+ senderName: message.senderName
13301
+ })) : normalizeCliParsedMessages(parsed.messages, {
13170
13302
  committedMessages: this.committedMessages,
13171
13303
  scope: this.currentTurnScope,
13172
13304
  lastOutputAt: this.lastOutputAt
@@ -13185,15 +13317,9 @@ var init_provider_cli_adapter = __esm({
13185
13317
  this.onStatusChange?.();
13186
13318
  }
13187
13319
  }
13188
- const effectiveCommittedHydratedMessages = shouldAdoptParsedIdleReplay ? this.committedMessages.map((message, index) => buildChatMessage({
13189
- ...message,
13190
- id: message.id || `msg_${index}`,
13191
- index: typeof message.index === "number" ? message.index : index,
13192
- receivedAt: typeof message.receivedAt === "number" ? message.receivedAt : message.timestamp
13193
- })) : committedHydratedMessages;
13194
- const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && effectiveCommittedHydratedMessages.length > parsedHydratedMessages.length;
13320
+ const shouldPreferCommittedHistoryReplay = !this.currentTurnScope && !this.activeModal && this.committedMessages.length > parsedHydratedMessages.length;
13195
13321
  const shouldPreferCommittedIdleReplay = shouldPreferCommittedMessages && !shouldAdoptParsedIdleReplay;
13196
- const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? effectiveCommittedHydratedMessages : parsedHydratedMessages;
13322
+ const hydratedMessages = shouldPreferCommittedIdleReplay || shouldPreferCommittedHistoryReplay ? this.buildCommittedChatMessages() : parsedHydratedMessages;
13197
13323
  result = {
13198
13324
  id: parsed.id || "cli_session",
13199
13325
  status: parsed.status || this.currentStatus,
@@ -14310,7 +14436,7 @@ var init_cli_provider_instance = __esm({
14310
14436
  return this.presentationMode;
14311
14437
  }
14312
14438
  getHotChatSessionState() {
14313
- const adapterStatus = this.adapter.getStatus();
14439
+ const adapterStatus = this.adapter.getStatus({ allowParse: false });
14314
14440
  const autoApproveActive = adapterStatus.status === "waiting_approval" && this.shouldAutoApprove();
14315
14441
  const visibleStatus = autoApproveActive ? "generating" : adapterStatus.status;
14316
14442
  const runtime = this.adapter.getRuntimeMetadata();
@@ -56449,7 +56575,7 @@ var init_adhdev_daemon = __esm({
56449
56575
  init_version();
56450
56576
  init_src();
56451
56577
  init_runtime_defaults();
56452
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.35" });
56578
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.37" });
56453
56579
  AdhdevDaemon = class _AdhdevDaemon {
56454
56580
  localHttpServer = null;
56455
56581
  localWss = null;
@@ -56466,7 +56592,7 @@ var init_adhdev_daemon = __esm({
56466
56592
  p2pChatOutputActiveAt = /* @__PURE__ */ new Map();
56467
56593
  p2pChatOutputFlushTimer = null;
56468
56594
  hotChatSnapshotCache = null;
56469
- static HOT_CHAT_SNAPSHOT_CACHE_TTL_MS = 1500;
56595
+ static HOT_CHAT_SNAPSHOT_CACHE_TTL_MS = 2400;
56470
56596
  static CHAT_OUTPUT_ACTIVITY_HOT_MS = DEFAULT_CHAT_TAIL_RECENT_MESSAGE_GRACE_MS;
56471
56597
  static CHAT_OUTPUT_FLUSH_DEBOUNCE_MS = 700;
56472
56598
  components = null;