@evandu/super-pi 0.1.64 → 0.1.66

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.
@@ -185,7 +185,20 @@ 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[]): Promise<Message>;
188
+ addUserMessage(conversationId: string, content: string, images?: MessageImage[], attachments?: import("./types.js").MessageAttachments): Promise<Message>;
189
+ /**
190
+ * Appends one background-result message to a conversation, without starting a turn.
191
+ *
192
+ * Used by sub-agent dispatch completions and the exec service when they finish out of band - the
193
+ * result is the prompt for a follow-up turn in the old design, which made a "后台回传" render under
194
+ * the "你" bubble and asked the model to consume a tool envelope as user prose. This appends a
195
+ * `role: "tool"` message that the client renders with its own column and its own block UI, so the
196
+ * transcript shows the result AND nothing pretends the user said it.
197
+ *
198
+ * NO `turnContext` claim, NO `status: "running"`. The message sits in the transcript and a subsequent
199
+ * caller can start a turn normally if it wants the model to react to the result.
200
+ */
201
+ addToolMessage(conversationId: string, content: string): Promise<Message>;
189
202
  /**
190
203
  * Records the model a conversation runs on. `updatedAt` is deliberately NOT touched, for the same
191
204
  * reason rename does not touch it: it is the sidebar's recency key, and switching model is not
@@ -329,7 +342,7 @@ export declare class Store {
329
342
  * `cancel` and `steer` name the id, which is the only reason "cancel the second of two identical
330
343
  * messages" is expressible at all. `crypto.randomUUID`, like every other id in this store.
331
344
  */
332
- enqueueMessage(conversationId: string, content: string, images?: MessageImage[]): Promise<QueuedMessage>;
345
+ enqueueMessage(conversationId: string, content: string, images?: MessageImage[], attachments?: import("./types.js").MessageAttachments): Promise<QueuedMessage>;
333
346
  /**
334
347
  * Removes one waiting message. Returns it, or `undefined` when it is already gone.
335
348
  *
@@ -743,7 +743,7 @@ export class Store {
743
743
  await this.persistMetadata(conversationId);
744
744
  return structuredClone(conversation);
745
745
  }
746
- async addUserMessage(conversationId, content, images) {
746
+ async addUserMessage(conversationId, content, images, attachments) {
747
747
  const conversation = this.requireConversation(conversationId);
748
748
  // The browser cannot reach an archived conversation, so this guard exists for any other caller -
749
749
  // a direct WebSocket client, or a future code path. Archiving is the promise that a conversation is
@@ -753,6 +753,8 @@ export class Store {
753
753
  const message = { id: id("message"), role: "user", content, createdAt: now() };
754
754
  if (images && images.length > 0)
755
755
  message.images = structuredClone(images);
756
+ if (attachments && attachments.length > 0)
757
+ message.attachments = structuredClone(attachments);
756
758
  conversation.messages.push(message);
757
759
  const firstUser = conversation.messages.find((item) => item.role === "user");
758
760
  const autoTitle = firstUser?.content.trim()
@@ -772,6 +774,28 @@ export class Store {
772
774
  await this.persist();
773
775
  return structuredClone(message);
774
776
  }
777
+ /**
778
+ * Appends one background-result message to a conversation, without starting a turn.
779
+ *
780
+ * Used by sub-agent dispatch completions and the exec service when they finish out of band - the
781
+ * result is the prompt for a follow-up turn in the old design, which made a "后台回传" render under
782
+ * the "你" bubble and asked the model to consume a tool envelope as user prose. This appends a
783
+ * `role: "tool"` message that the client renders with its own column and its own block UI, so the
784
+ * transcript shows the result AND nothing pretends the user said it.
785
+ *
786
+ * NO `turnContext` claim, NO `status: "running"`. The message sits in the transcript and a subsequent
787
+ * caller can start a turn normally if it wants the model to react to the result.
788
+ */
789
+ async addToolMessage(conversationId, content) {
790
+ const conversation = this.requireConversation(conversationId);
791
+ if (conversation.archivedAt)
792
+ throw new Error("无法向已归档的会话追加后台回传,请先恢复该会话");
793
+ const message = { id: id("message"), role: "tool", content, createdAt: now() };
794
+ conversation.messages.push(message);
795
+ conversation.updatedAt = message.createdAt;
796
+ await this.persist();
797
+ return structuredClone(message);
798
+ }
775
799
  /**
776
800
  * Records the model a conversation runs on. `updatedAt` is deliberately NOT touched, for the same
777
801
  * reason rename does not touch it: it is the sidebar's recency key, and switching model is not
@@ -1096,13 +1120,15 @@ export class Store {
1096
1120
  * `cancel` and `steer` name the id, which is the only reason "cancel the second of two identical
1097
1121
  * messages" is expressible at all. `crypto.randomUUID`, like every other id in this store.
1098
1122
  */
1099
- async enqueueMessage(conversationId, content, images) {
1123
+ async enqueueMessage(conversationId, content, images, attachments) {
1100
1124
  this.requireConversation(conversationId);
1101
1125
  if (this.state.conversations.find((item) => item.id === conversationId)?.archivedAt)
1102
1126
  throw new Error("无法向已归档的会话排队消息,请先恢复该会话");
1103
1127
  const message = { id: id("q"), conversationId, content, state: "queued", createdAt: now() };
1104
1128
  if (images && images.length > 0)
1105
1129
  message.images = structuredClone(images);
1130
+ if (attachments && attachments.length > 0)
1131
+ message.attachments = structuredClone(attachments);
1106
1132
  this.state.queuedMessages.push(message);
1107
1133
  await this.persistCollections("queuedMessages");
1108
1134
  return structuredClone(message);
@@ -95,11 +95,26 @@ export type Message = {
95
95
  toolCalls?: ToolCallBlock[];
96
96
  thinking?: string;
97
97
  images?: MessageImage[];
98
+ attachments?: MessageAttachments;
98
99
  agentIdentity?: {
99
100
  id?: string;
100
101
  name: string;
101
102
  };
102
103
  };
104
+ /**
105
+ * A piece of context attached to a user message that is shown as its OWN block under the bubble, rather
106
+ * than flattened into the prompt. Today: a skill the person picked from the slash menu - the model gets
107
+ * the skill text inlined into the prompt for execution, and the transcript shows the user that the
108
+ * skill they asked for is the one that was loaded. Collapsed by default because skill bodies can be
109
+ * hundreds of lines and the user already chose the skill by name.
110
+ */
111
+ export type MessageAttachment = {
112
+ kind: "skill";
113
+ id: string;
114
+ name: string;
115
+ content: string;
116
+ };
117
+ export type MessageAttachments = MessageAttachment[];
103
118
  /**
104
119
  * ONE PARAMETER OF A TOOL CALL, as a call block lists it, and the boundary that keeps Pi's arguments out
105
120
  * of `state.json`.
@@ -674,6 +689,7 @@ export type QueuedMessage = {
674
689
  priority?: boolean;
675
690
  createdAt: string;
676
691
  images?: MessageImage[];
692
+ attachments?: MessageAttachments;
677
693
  };
678
694
  /**
679
695
  * What Pi reports about ITS OWN queue, forwarded to the browser as it arrives.
@@ -914,6 +930,7 @@ export type ClientCommandBody =
914
930
  conversationId: string;
915
931
  content: string;
916
932
  images?: MessageImage[];
933
+ attachments?: MessageAttachments;
917
934
  }
918
935
  /**
919
936
  * Revert the conversation to the state right AFTER one message: every message after it is removed from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.64",
3
+ "version": "0.1.66",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",