@evandu/super-pi 0.1.110 → 0.1.111

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.
Files changed (26) hide show
  1. package/dist/.super-pi-check-inputs.json +2 -2
  2. package/dist/client/.super-pi-client-sources.json +5 -4
  3. package/dist/client/assets/{CodeMirrorEditor-DdNOmxYW.js → CodeMirrorEditor-CaF1aTpR.js} +2 -2
  4. package/dist/client/assets/{dist-CyuGJahk.js → dist-BG1mkRxC.js} +1 -1
  5. package/dist/client/assets/{dist-CmNELnsL.js → dist-BjrDHYro.js} +1 -1
  6. package/dist/client/assets/{dist-D_JtGl0X.js → dist-Bl_iPEld.js} +1 -1
  7. package/dist/client/assets/{dist-CB4FCjuA.js → dist-Bv-QAejS.js} +1 -1
  8. package/dist/client/assets/{dist-D6LObgqS.js → dist-BydrWuS9.js} +1 -1
  9. package/dist/client/assets/{dist-DyYBaVQ5.js → dist-C2JgfsQ0.js} +1 -1
  10. package/dist/client/assets/{dist-BMx01Qs-.js → dist-C9gmrpfb.js} +1 -1
  11. package/dist/client/assets/{dist-r4nQMwvT.js → dist-CJHSsi_2.js} +1 -1
  12. package/dist/client/assets/{dist-B0owH_FO.js → dist-CKziNS6e.js} +1 -1
  13. package/dist/client/assets/{dist-BaXshVGE.js → dist-CPo4IuhO.js} +1 -1
  14. package/dist/client/assets/{dist-DfUFnKum.js → dist-CbkXScEJ.js} +1 -1
  15. package/dist/client/assets/{dist-CiFn8_Gb.js → dist-DnwnynHW.js} +1 -1
  16. package/dist/client/assets/{dist-BcnfVRW2.js → dist-aC6cFQVF.js} +1 -1
  17. package/dist/client/assets/{dist-VljXHWNt.js → dist-f78PQc3f.js} +1 -1
  18. package/dist/client/assets/{dist-DFYECMpZ.js → dist-jDX3NY00.js} +1 -1
  19. package/dist/client/assets/{dist-CUsER3MR.js → dist-o-3gMx5l.js} +1 -1
  20. package/dist/client/assets/{index-DmN1fZRn.js → index-CME3BwBz.js} +8 -8
  21. package/dist/client/assets/{index-CGlVRKg9.css → index-CauO59H-.css} +1 -1
  22. package/dist/client/index.html +2 -2
  23. package/dist/server/application/run-service.js +14 -0
  24. package/dist/server/store.d.ts +9 -0
  25. package/dist/server/store.js +11 -0
  26. package/package.json +2 -1
@@ -8,8 +8,8 @@
8
8
  <link rel="alternate icon" href="/favicon.ico" />
9
9
  <link rel="apple-touch-icon" href="/favicon.svg" />
10
10
  <title>Super Pi</title>
11
- <script type="module" crossorigin src="/assets/index-DmN1fZRn.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-CGlVRKg9.css">
11
+ <script type="module" crossorigin src="/assets/index-CME3BwBz.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-CauO59H-.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -1253,6 +1253,20 @@ export class RunService {
1253
1253
  * Enqueues or starts execution for a user message.
1254
1254
  */
1255
1255
  async enqueueAndRun(conversationId, content, images, attachments, clientId) {
1256
+ /**
1257
+ * A CLIENT ID THIS CONVERSATION ALREADY HAS IS A RESEND, NOT A SECOND MESSAGE.
1258
+ *
1259
+ * The browser retries a send whose socket dropped before the answer arrived (see
1260
+ * `src/client/pending-operations.ts`), and it cannot know whether the first attempt reached this
1261
+ * server. It usually did - the drop is a restart or a sleeping laptop, not a refusal - so without
1262
+ * this guard the retry would be a second user message, answered twice, for words the person said
1263
+ * once. The id is minted per compose by the browser, so a match cannot be a coincidence.
1264
+ *
1265
+ * Returning silently is the whole answer: the original message is already in the transcript, and
1266
+ * the browser learns it from the state snapshot that arrives on connect.
1267
+ */
1268
+ if (clientId && this.store.hasMessage(conversationId, clientId))
1269
+ return;
1256
1270
  this.memory?.cancel(conversationId);
1257
1271
  const trimmed = content.trim();
1258
1272
  const validImages = Array.isArray(images) && images.length > 0 ? images : undefined;
@@ -91,6 +91,15 @@ export declare class Store {
91
91
  */
92
92
  setDefaultModel(model?: ModelSelection): Promise<void>;
93
93
  snapshot(): AppState;
94
+ /**
95
+ * Whether a conversation already holds a message with this id.
96
+ *
97
+ * A LIVE read with no clone, because its one caller is the send path's duplicate check: a resend
98
+ * that arrives within milliseconds of the original must be answered without paying for a full
99
+ * copy of every conversation in the app. The answer is a boolean and the message is not handed
100
+ * out, so nothing can mutate the store through it.
101
+ */
102
+ hasMessage(conversationId: string, messageId: string): boolean;
94
103
  /**
95
104
  * One conversation and its workspace, as DETACHED copies, without cloning the whole state.
96
105
  *
@@ -684,6 +684,17 @@ export class Store {
684
684
  }
685
685
  }
686
686
  snapshot() { return structuredClone(this.state); }
687
+ /**
688
+ * Whether a conversation already holds a message with this id.
689
+ *
690
+ * A LIVE read with no clone, because its one caller is the send path's duplicate check: a resend
691
+ * that arrives within milliseconds of the original must be answered without paying for a full
692
+ * copy of every conversation in the app. The answer is a boolean and the message is not handed
693
+ * out, so nothing can mutate the store through it.
694
+ */
695
+ hasMessage(conversationId, messageId) {
696
+ return Boolean(this.state.conversations.find((item) => item.id === conversationId)?.messages.some((message) => message.id === messageId));
697
+ }
687
698
  /**
688
699
  * One conversation and its workspace, as DETACHED copies, without cloning the whole state.
689
700
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.110",
3
+ "version": "0.1.111",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -95,6 +95,7 @@
95
95
  "scripts/tool-register-env.test.mjs",
96
96
  "scripts/bg-task-zombie.test.mjs",
97
97
  "scripts/scoped-sidebar-tabs.test.mjs",
98
+ "scripts/send-retry.test.mjs",
98
99
  "scripts/git-diff-view.test.mjs",
99
100
  "scripts/fork-server.test.mjs",
100
101
  "scripts/styles-no-dangling.test.mjs",