@evandu/super-pi 0.1.84 → 0.1.85

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 (33) hide show
  1. package/agents/super-pi-knowledge/index.ts +40 -0
  2. package/assets/knowledge-model/README.md +37 -0
  3. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/config.json +31 -0
  4. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/onnx/model_quantized.onnx +0 -0
  5. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/special_tokens_map.json +7 -0
  6. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/tokenizer.json +21278 -0
  7. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/tokenizer_config.json +15 -0
  8. package/assets/knowledge-model/Xenova/bge-small-zh-v1.5/vocab.txt +21128 -0
  9. package/dist/.super-pi-check-inputs.json +4 -0
  10. package/dist/client/.super-pi-client-sources.json +8 -7
  11. package/dist/client/assets/index-B_jOwjyp.js +72 -0
  12. package/dist/client/assets/index-CNNGl4eF.css +1 -0
  13. package/dist/client/index.html +2 -2
  14. package/dist/server/application/run-service.d.ts +50 -11
  15. package/dist/server/application/run-service.js +206 -77
  16. package/dist/server/command-handler.js +16 -4
  17. package/dist/server/http-server.js +59 -1
  18. package/dist/server/index.js +7 -0
  19. package/dist/server/knowledge-vectors.d.ts +133 -0
  20. package/dist/server/knowledge-vectors.js +302 -0
  21. package/dist/server/knowledge.d.ts +263 -0
  22. package/dist/server/knowledge.js +743 -0
  23. package/dist/server/persistence/app-state-repository.d.ts +25 -1
  24. package/dist/server/persistence/sqlite-state-repository.js +47 -4
  25. package/dist/server/runtime.d.ts +48 -1
  26. package/dist/server/runtime.js +83 -7
  27. package/dist/server/server-context.d.ts +1 -0
  28. package/dist/server/store.d.ts +153 -68
  29. package/dist/server/store.js +405 -187
  30. package/dist/server/types.d.ts +97 -65
  31. package/package.json +163 -155
  32. package/dist/client/assets/index-8Pf3MG2d.js +0 -72
  33. package/dist/client/assets/index-Jnj15dhT.css +0 -1
@@ -1,7 +1,7 @@
1
1
  import type { AppStateRepository } from "./persistence/app-state-repository.js";
2
2
  import { defaultJsonStatePath } from "./persistence/json-state-repository.js";
3
3
  import type { WorkflowRun } from "./workflow.js";
4
- import type { AppState, Conversation, Message, MessageImage, ModelSelection, QueuedMessage, SubagentRun, SubagentRunUpdate, TodoItem, ToolCallQuestion, ToolCallArg, ToolCallBlock, ToolResultSummary, Workspace } from "./types.js";
4
+ import type { AppState, Conversation, Message, MessageImage, ModelSelection, SubagentRun, SubagentRunUpdate, TodoItem, ToolCallQuestion, ToolCallArg, ToolCallBlock, ToolResultSummary, Workspace } from "./types.js";
5
5
  /** Mints the id a forked conversation will have, BEFORE its Pi transcript exists.
6
6
  *
7
7
  * Exported because the ordering demands it and no other arrangement provides it. Pi's copied file's header
@@ -185,7 +185,40 @@ export declare class Store {
185
185
  * the Archived group renders the same name.
186
186
  */
187
187
  renameConversation(conversationId: string, name: string): Promise<Conversation>;
188
- addUserMessage(conversationId: string, content: string, images?: MessageImage[], attachments?: import("./types.js").MessageAttachments): Promise<Message>;
188
+ /**
189
+ * Writes ONE user message into the transcript - the single place a user utterance comes into being.
190
+ *
191
+ * Two callers exist and they differ only in `delivery`, because whether a send starts a turn or waits
192
+ * for one is decided by the RUN GUARD in `run-service`, not here. This method records the words and,
193
+ * if they are waiting, records that they are waiting. Deciding anything about turns would put a second,
194
+ * contradictory answer next to the guard's.
195
+ *
196
+ * `clientId` is the browser's own id for the message it has already drawn. When it is well formed and
197
+ * not already taken, the message IS that id - which is what lets the optimistic bubble and the stored
198
+ * message be the same object rather than two that have to be matched up by content afterwards. A
199
+ * collision falls back to a minted id rather than throwing: the browser is asking for its words to be
200
+ * kept, and refusing the whole send because an id was already in use would lose them.
201
+ *
202
+ * `delivery: "waiting"` ALSO SKIPS EVERY SIDE EFFECT BELOW - no `running`, no title, no `updatedAt`,
203
+ * no clearing of a failure reason. Those all describe a turn, and a message waiting for one has not
204
+ * started anything. It is why a conversation holding only waiting messages still reads as idle in the
205
+ * sidebar, and why the title comes from the first message that was actually asked rather than from
206
+ * whatever was typed into a box that was not listening yet. `openTurnOnMessage` applies them at the
207
+ * moment a turn genuinely begins.
208
+ */
209
+ addUserMessage(conversationId: string, content: string, images?: MessageImage[], attachments?: import("./types.js").MessageAttachments, options?: {
210
+ clientId?: string;
211
+ delivery?: Message["delivery"];
212
+ }): Promise<Message>;
213
+ /**
214
+ * Takes the browser's id for its own optimistic message, when it is safe to.
215
+ *
216
+ * A client that names an id already in this conversation gets a minted one instead: two messages sharing
217
+ * an id would break `updateToolCall`, `revertMessage` and every per-message write that follows, so
218
+ * uniqueness within the conversation is worth more than the client getting its own id back in that case.
219
+ * The `message_` prefix is required so a folded legacy id (which is `q_`-prefixed) can never be adopted.
220
+ */
221
+ private claimClientMessageId;
189
222
  /**
190
223
  * Appends one background-result message to a conversation, without starting a turn.
191
224
  *
@@ -366,100 +399,152 @@ export declare class Store {
366
399
  revertMessages(conversationId: string, messageId: string, withdraw?: boolean): Promise<RevertPlan>;
367
400
  private requireConversation;
368
401
  /**
369
- * This conversation's waiting list, oldest first.
402
+ * This conversation's waiting messages, in the order they will be started.
370
403
  *
371
404
  * Cloned like every other value this store hands out, because the caller broadcasts it straight to
372
- * browsers and the array is mutated in place by the very next enqueue.
405
+ * browsers and the array is mutated in place by the very next send.
406
+ */
407
+ listWaitingMessages(conversationId: string): Message[];
408
+ /** The same list by reference, for callers that only read a field and must not pay for a clone. */
409
+ private waitingMessages;
410
+ private requireWaitingMessage;
411
+ private persistMessages;
412
+ /**
413
+ * Claims one waiting message for delivery to the live Pi process: `waiting` becomes `sending`, and the
414
+ * message STAYS WHERE IT IS - still on screen, still in the transcript, still at the same index.
415
+ *
416
+ * The read and the write are adjacent statements with no `await` between them, which is what makes the
417
+ * claim atomic here. This store has no mutex and does not need one: JavaScript runs this synchronous
418
+ * run-to-completion, so a second caller arriving in the same tick observes the write above and cannot
419
+ * claim the same message.
420
+ */
421
+ claimWaitingMessage(conversationId: string, messageId: string): Promise<Message>;
422
+ /**
423
+ * Puts ONE `sending` message back to `waiting`, for the one case that proves Pi never took it.
424
+ *
425
+ * The one legal use, and it is narrow on purpose: this is called ONLY when we can prove the message
426
+ * never reached a model - the turn ended while Pi still held it, or the write to Pi's stdin failed
427
+ * before it left. Pi drains steering before its loop can end and announces the delivery before the turn
428
+ * settles, so a message still `sending` when the child is gone provably was not delivered, and
429
+ * returning it is a fact rather than a guess. It is deliberately NOT used for "Pi has not answered yet",
430
+ * where the outcome is genuinely unknown and the honest state is still `sending`.
373
431
  */
374
- listQueuedMessages(conversationId: string): QueuedMessage[];
432
+ releaseWaitingMessage(conversationId: string, messageId: string): Promise<void>;
375
433
  /**
376
- * Adds one message to the waiting list and returns it.
434
+ * Returns every `sending` message of this conversation to `waiting`, for the end of a turn.
377
435
  *
378
- * The id is minted here and is the entry's identity for its whole life. Pi matches its own queue by
379
- * message TEXT (`agent-session.ts:1123`), so two identical messages are one entry to Pi and two here;
380
- * `cancel` and `steer` name the id, which is the only reason "cancel the second of two identical
381
- * messages" is expressible at all. `crypto.randomUUID`, like every other id in this store.
436
+ * See `releaseWaitingMessage` for why this is a fact and not a guess. Called both when a turn ends and
437
+ * when the user stops it: in the stop case the child is killed mid-turn and a steer it was still holding
438
+ * was never delivered either, so putting it back is what lets the user decide what to do with it instead
439
+ * of silently dropping a message they typed - a message they can still see, because it never left the
440
+ * transcript in the first place.
382
441
  */
383
- enqueueMessage(conversationId: string, content: string, images?: MessageImage[], attachments?: import("./types.js").MessageAttachments): Promise<QueuedMessage>;
442
+ releaseSendingMessages(conversationId: string): Promise<void>;
384
443
  /**
385
- * Removes one waiting message. Returns it, or `undefined` when it is already gone.
444
+ * Withdraws one waiting message, by id. Returns it, or `undefined` when it is already gone.
386
445
  *
387
- * "Already gone" is a no-op rather than an error, and the reason is the same as `SubagentRun.stop`:
388
- * the click that sent this was almost always triggered by a dock row the browser has not been told
389
- * about yet, so an id that has already been delivered or cancelled is the ordinary case and must not
390
- * surface as a failure.
446
+ * "Already gone" is a no-op rather than an error, and the reason is the same as `SubagentRun.stop`: the
447
+ * click that sent this was almost always triggered by a bubble the browser has not been told about
448
+ * yet, so an id that has already been answered or cancelled is the ordinary case and must not surface
449
+ * as a failure.
391
450
  *
392
- * A `sending` entry REFUSES, because the text is already in a live Pi child's stdin and Pi has no way
451
+ * A `sending` message REFUSES, because the text is already in a live Pi child's stdin and Pi has no way
393
452
  * to forget one item (its only queue command is `clear_queue`, which clears every one of them,
394
- * including follow-ups that have nothing to do with this click). Removing it from this list would not
395
- * stop Pi from delivering it - it would make the model act on a message the user believed they had
396
- * withdrawn.
453
+ * including follow-ups that have nothing to do with this click). Removing it here would not stop Pi from
454
+ * delivering it - it would make the model act on a message the user believed they had withdrawn, while
455
+ * their own transcript quietly lost the words.
397
456
  */
398
- cancelQueuedMessage(conversationId: string, queueId: string): Promise<QueuedMessage | undefined>;
457
+ cancelWaitingMessage(conversationId: string, messageId: string): Promise<Message | undefined>;
399
458
  /**
400
- * Claims one waiting message for delivery to the live Pi process: `queued` becomes `sending`, and the
401
- * entry STAYS in the list until Pi reports it delivered.
459
+ * Rewrites the words of one waiting message.
402
460
  *
403
- * The read and the write are adjacent statements with no `await` between them, which is what makes
404
- * the claim atomic here. This store has no mutex, and it does not need one: JavaScript runs this
405
- * synchronous run-to-completion, so a second caller arriving in the same tick observes the write above
406
- * and cannot claim the same entry. Anything that needed to await between reading and writing would
407
- * need real serialization instead.
461
+ * Only legal while nothing has read it. `waiting` means the text is still only in this transcript, so
462
+ * changing it is changing the whole truth of the message - there is no second copy anywhere that could
463
+ * fall out of step. `sending` means Pi already holds it, and a message the model is about to answer is
464
+ * not one the user gets to rewrite after the fact.
465
+ */
466
+ editWaitingMessage(conversationId: string, messageId: string, content: string): Promise<Message>;
467
+ /**
468
+ * Moves one waiting message to a new position among the waiting messages.
469
+ *
470
+ * `toIndex` is counted over the WAITING messages only, so `0` is the next one the agent will start and
471
+ * the caller never has to know how many answered messages sit above it. Moving to the front and moving
472
+ * back are the same operation, which is the point: the old arrangement had a `priority` boolean for the
473
+ * first and no way at all to express the second, so undoing a priority meant cancelling the message and
474
+ * retyping it.
408
475
  *
409
- * Throws for an unknown id and for one that is already `sending`, because both mean the caller is
410
- * acting on a state the store has already moved past - and the second one means a second steer of the
411
- * same message would put the text into Pi twice.
476
+ * NOTHING CAN GO IN FRONT OF A `sending` MESSAGE. It is already in Pi's stdin and Pi's steering queue
477
+ * is FIFO, so it will be delivered before anything this server could do about the order. Accepting the
478
+ * move and then delivering the other one first would be the UI promising an outcome the transport has
479
+ * already decided, so it is refused instead.
412
480
  */
413
- claimQueuedMessage(conversationId: string, queueId: string): Promise<QueuedMessage>;
414
- /** Keep a priority request at the front even if the current Pi process has already exited. */
415
- prioritizeQueuedMessage(conversationId: string, queueId: string): Promise<QueuedMessage>;
481
+ reorderWaitingMessage(conversationId: string, messageId: string, toIndex: number): Promise<Message>;
416
482
  /**
417
- * Puts a `sending` entry back into the queue.
483
+ * Pi has just delivered one steered message: unmark it and open the answer to it, in ONE write.
418
484
  *
419
- * The one legal use, and it is narrow on purpose: this is called ONLY when we can prove the message
420
- * never reached a model - the turn ended while Pi still held it, or the write to Pi's stdin failed
421
- * before it left. Pi drains steering before its loop can end and reports the removal before the
422
- * delivered message, so a message still `sending` when the child is gone provably was not delivered,
423
- * and returning it is a fact rather than a guess. It is deliberately NOT used for "Pi has not answered
424
- * yet" (see `steer` in `index.ts`), where the outcome is genuinely unknown and the honest state is
425
- * still `sending`.
485
+ * Oldest rather than matched on text, and that is forced on us by Pi rather than chosen: Pi identifies
486
+ * its own queued entry by `indexOf` on the message text (`agent-session.ts:1123`), so two identical
487
+ * steered messages are ONE entry to it and no host-side matching could do better. Position is sufficient
488
+ * anyway because Pi's steering queue is FIFO and Super Pi only ever steers from the front of the waiting
489
+ * group.
490
+ *
491
+ * ONE WRITE, because the reader must never see the intermediate: a message that has stopped waiting but
492
+ * has no answer under it yet is the exact broken state this refactor exists to remove, and a browser
493
+ * that broadcast between the two halves would put it right back on screen.
494
+ *
495
+ * WHAT IS DIFFERENT FROM BEFORE, and is the whole point: the message used to be DELETED here. It is now
496
+ * merely unmarked, with an assistant message spliced in after it. It was on screen the whole time - it
497
+ * was written into the transcript when the user pressed send, long before Pi ever saw it - and it stays
498
+ * exactly where it was, now with the agent answering it. Deleting it is what made a steered reply appear
499
+ * with no question above it.
426
500
  */
427
- releaseQueuedMessage(queueId: string): Promise<void>;
501
+ deliverWaitingMessage(conversationId: string, agentIdentity?: Message["agentIdentity"]): Promise<{
502
+ user: Message;
503
+ assistant: Message;
504
+ } | undefined>;
428
505
  /**
429
- * Removes the OLDEST `count` messages this conversation has in `sending`, and returns them.
506
+ * Opens a turn on a message that is ALREADY IN THE TRANSCRIPT: clears its delivery state, applies every
507
+ * side effect that means "a turn started here", and splices the assistant placeholder in DIRECTLY AFTER
508
+ * IT.
509
+ *
510
+ * THE INSERT POSITION IS THE WHOLE DESIGN. `push` was the old behaviour and it is only correct when the
511
+ * message being answered is the last thing in the conversation. With two messages waiting, answering
512
+ * the first by appending puts its reply BELOW the second one, so the reader sees the answer to a
513
+ * question that is still waiting - a transcript lying about its own order. Splicing at
514
+ * `indexOf(user) + 1` keeps every waiting message below the reply that belongs to the one above it.
430
515
  *
431
- * Driven by a COUNT rather than by matching text, and that is the whole point. Pi's `queue_update`
432
- * reports only the strings it still holds, and Pi itself identifies which of its entries a delivered
433
- * message came from with `indexOf` on that text - so with two identical steered messages, "which one
434
- * was delivered" is exactly the question Pi cannot answer. It does not need to be answered: Pi's
435
- * steering queue is FIFO and Super Pi only ever steers from the front of this list, so a drop in
436
- * `steering.length` from N to N-k means Super Pi's oldest k `sending` entries were delivered, in
437
- * order, whatever their text was.
516
+ * The side effects - title, `updatedAt`, `status: "running"`, clearing a stale failure - all describe a
517
+ * TURN rather than a message, which is why `addUserMessage` skips them for a message that is only
518
+ * waiting. They belong here, at the moment the turn genuinely begins.
438
519
  */
439
- dropSendingMessages(conversationId: string, count: number): Promise<QueuedMessage[]>;
520
+ openTurnOnMessage(conversationId: string, messageId: string, agentIdentity?: Message["agentIdentity"]): Promise<{
521
+ user: Message;
522
+ assistant: Message;
523
+ }>;
440
524
  /**
441
- * Takes the oldest still-`queued` message of this conversation OUT of the list and returns it, for the
525
+ * Takes the OLDEST still-`waiting` message of this conversation, clearing its delivery state, for the
442
526
  * next turn to send as its prompt.
443
527
  *
444
- * Removing it here, rather than marking it and removing it after the turn, is what bounds the loop:
445
- * every pass through the follow-up loop strictly shrinks the list, so the loop cannot run forever
446
- * even if a turn succeeds without consuming anything. And removing it BEFORE the spawn is what makes a
447
- * crash honest - a server that dies mid-turn has already taken the message out of the file, and
448
- * `open` does not put it back, because it may well have been delivered.
528
+ * Clearing the state and taking the message are the same write on purpose: it bounds the follow-up loop
529
+ * (every pass strictly shrinks the set of waiting messages, so the loop cannot run forever even if a turn
530
+ * succeeds without consuming anything) and it is what makes the crash honest - a server that dies
531
+ * mid-turn has already taken the message out of the waiting set, so startup does not put it back, because
532
+ * it may well have been delivered.
449
533
  */
450
- takeNextQueuedMessage(conversationId: string): Promise<QueuedMessage | undefined>;
451
- /** Removes one still-cancellable row by id so an explicit user action can start it as a new turn. */
452
- takeQueuedMessage(conversationId: string, queueId: string): Promise<QueuedMessage>;
534
+ takeNextWaitingMessage(conversationId: string): Promise<Message | undefined>;
535
+ /** Takes one specific waiting message by id, so an explicit user action can start it as a new turn. */
536
+ takeWaitingMessage(conversationId: string, messageId: string): Promise<Message>;
453
537
  /**
454
- * Returns every `sending` message of this conversation to the queue, for the end of a turn.
538
+ * Opens a turn for a message the user chose to be answered now, when the conversation is idle.
455
539
  *
456
- * See `releaseQueuedMessage` for why this is a fact and not a guess, and `reconcileQueuedMessages` for
457
- * why the same treatment is right after a crash. Called both when a turn ends and when the user stops
458
- * it: in the stop case the child is killed mid-turn and a steer it was still holding was never
459
- * delivered either, so putting it back is what lets the user decide what to do with it instead of
460
- * silently dropping a message they typed.
540
+ * The one place that both claims a specific message and opens its turn, so there is no window between
541
+ * "the message stopped waiting" and "the turn began" in which a crash would leave a message that is
542
+ * neither waiting nor answered.
461
543
  */
462
- releaseSendingMessages(conversationId: string): Promise<void>;
544
+ openTurnOnSpecificMessage(conversationId: string, messageId: string, agentIdentity?: Message["agentIdentity"]): Promise<{
545
+ user: Message;
546
+ assistant: Message;
547
+ }>;
463
548
  /**
464
549
  * Records a sub-agent run as `running`, BEFORE the child is spawned.
465
550
  *