@cydm/happy-elves 0.1.0-beta.502 → 0.1.0-beta.503

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/README.md CHANGED
@@ -6,8 +6,8 @@ Public beta CLI for Happy Elves.
6
6
 
7
7
  Run once in the existing user account; it detects %USERPROFILE%\.happy-elves\hosted, keeps the current daemon and sessions running, installs the independent updater, and registers per-user startup.
8
8
 
9
- npm.cmd install --prefix $HOME\.happy-elves\management-entry --ignore-scripts --no-audit --no-fund --package-lock=false @cydm/happy-elves@0.1.0-beta.502
10
- npm.cmd exec --yes --package @cydm/happy-elves@0.1.0-beta.502 -- happy-elves-updater install --state-root $HOME\.happy-elves\hosted --autostart
9
+ npm.cmd install --prefix $HOME\.happy-elves\management-entry --ignore-scripts --no-audit --no-fund --package-lock=false @cydm/happy-elves@0.1.0-beta.503
10
+ npm.cmd exec --yes --package @cydm/happy-elves@0.1.0-beta.503 -- happy-elves-updater install --state-root $HOME\.happy-elves\hosted --autostart
11
11
 
12
12
  The same updater entrypoint works on macOS after selecting a permanent user prefix.
13
13
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-cli",
3
- "version": "0.1.0-beta.502",
3
+ "version": "0.1.0-beta.503",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves-daemon",
3
- "version": "0.1.0-beta.502",
3
+ "version": "0.1.0-beta.503",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@cydm/happy-elves",
3
- "version": "0.1.0-beta.502",
3
+ "version": "0.1.0-beta.503",
4
4
  "description": "Remote controller for local coding agents with hosted or self-hosted relay support.",
5
- "buildId": "9ee7dcc44e6e7465aaa011b2c13085c8",
5
+ "buildId": "0c1a102010e1f13050d123a2ff389e79",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "happy-elves": "./apps/cli/dist/index.js",
@@ -575,7 +575,7 @@ export class HeadlessRuntimeCore {
575
575
  .filter((item) => !mappedItemIds.has(item.id))
576
576
  .filter((item) => item.type === "agentMessage" || item.type === "userMessage")
577
577
  .filter((item) => mappedSourceItems.some((sourceItem) => (sourceItem.kind === "assistant" ? item.type === "agentMessage" : item.type === "userMessage")
578
- && item.text === sourceItem.text))
578
+ && (item.text === sourceItem.text || nativeUserInputTextEqual(sourceItem, item))))
579
579
  .map((item) => item.id);
580
580
  if (duplicateTextItemIds.length > 0) {
581
581
  removedItemIds.push(...duplicateTextItemIds);
@@ -3094,6 +3094,21 @@ function runtimeTurnIdForProviderTurn(thread, providerTurnId, providerCheckpoint
3094
3094
  function nativeItemBindingKey(providerTurnId, providerItemId) {
3095
3095
  return `${providerTurnId}\0${providerItemId}`;
3096
3096
  }
3097
+ /**
3098
+ * Native providers occasionally append a transport newline to a user input
3099
+ * when reopening a turn. Treat that as the same input only at this mapping
3100
+ * boundary: the comparison is turn-scoped, kind-scoped, and one-to-one. We
3101
+ * intentionally do not trim arbitrary whitespace or apply this to assistant
3102
+ * output, so legitimate repeated prompts remain distinct.
3103
+ */
3104
+ function nativeUserInputTextEqual(source, projected) {
3105
+ if (source.kind !== "user" || projected.type !== "userMessage" || !("text" in projected))
3106
+ return false;
3107
+ if (projected.text === source.text)
3108
+ return true;
3109
+ const withoutTerminalNewline = (value) => value.replace(/(?:\r\n|\n|\r)+$/g, "");
3110
+ return withoutTerminalNewline(projected.text) === withoutTerminalNewline(source.text);
3111
+ }
3097
3112
  /**
3098
3113
  * Map provider-native items onto an existing HE turn projection.
3099
3114
  *
@@ -3141,7 +3156,7 @@ function mapNativeItemsToProjection(sourceItems, existingItems, priorBindings, p
3141
3156
  continue;
3142
3157
  }
3143
3158
  const sourceItem = sourceItems[sourceIndex];
3144
- const exact = candidateAtOrAfterCursor((item) => item.type === sourceType(sourceItem) && "text" in item && item.text === sourceItem.text);
3159
+ const exact = candidateAtOrAfterCursor((item) => item.type === sourceType(sourceItem) && "text" in item && (item.text === sourceItem.text || nativeUserInputTextEqual(sourceItem, item)));
3145
3160
  if (exact) {
3146
3161
  itemIds[sourceIndex] = exact.item.id;
3147
3162
  usedExisting.add(exact.item.id);
@@ -3188,7 +3203,8 @@ function mapNativeItemsToProjection(sourceItems, existingItems, priorBindings, p
3188
3203
  continue;
3189
3204
  }
3190
3205
  const mappedText = indexes.map((index) => sourceItems[index].text).join("");
3191
- if (mappedText !== existing.text)
3206
+ const singleUserItem = indexes.length === 1 && sourceItems[indexes[0]]?.kind === "user";
3207
+ if (!(singleUserItem ? nativeUserInputTextEqual(sourceItems[indexes[0]], existing) : mappedText === existing.text))
3192
3208
  valid = false;
3193
3209
  }
3194
3210
  const complete = valid