@evandu/super-pi 0.1.30 → 0.1.32

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.
@@ -164,8 +164,7 @@ const MAX_CHUNK_BYTES = 64 * 1024;
164
164
  /** Hard per-session backlog. Beyond this the session is considered runaway and is killed. */
165
165
  const MAX_BACKLOG_BYTES = 4 * 1024 * 1024;
166
166
  /**
167
- * Reads the opt-in flag. Anything other than an explicit truthy string leaves the terminal OFF, so a
168
- * typo in a deployment config fails closed rather than silently exposing a shell.
167
+ * Reads the opt-in flag. Anything other than an explicit truthy string leaves the terminal OFF.
169
168
  */
170
169
  export function isTerminalEnabled(env = process.env) {
171
170
  const raw = env.SUPER_PI_TERMINAL?.trim().toLowerCase();
@@ -174,7 +173,7 @@ export function isTerminalEnabled(env = process.env) {
174
173
  /** The sentence a browser shows when the terminal is missing. Says WHICH of the three gates closed. */
175
174
  export function terminalDisabledReason(env = process.env) {
176
175
  if (!isTerminalEnabled(env))
177
- return "终端未启用。设置 SUPER_PI_TERMINAL=1 后重启 Super Pi 即可使用。";
176
+ return "终端未启用。设置 SUPER_PI_TERMINAL=1 后重启即可使用。";
178
177
  if (ptyLoadFailure)
179
178
  return `node-pty 未能加载(${ptyLoadFailure})。请重新安装依赖,或确认 node-pty 版本不低于 ${MINIMUM_PTY_VERSION}。`;
180
179
  return "";
@@ -62,6 +62,27 @@ export declare function extractToolResultText(result: unknown): ToolResultSummar
62
62
  * shape can be regression-tested at all.
63
63
  */
64
64
  export declare function describeToolCall(name: string, args: unknown): string | undefined;
65
+ /**
66
+ * THE SAME LINE, READ OUT OF A CALL THAT IS STILL BEING WRITTEN.
67
+ *
68
+ * The block for a call appears the instant the model starts writing it, and for an `edit` that is several
69
+ * seconds before the arguments are complete. Showing 「工具 · 正在生成参数…」 through all of it says the
70
+ * reader nothing they can act on - not WHICH tool, and not what it is aimed at, which are the two things
71
+ * the finished block is going to say. So the same `describeToolCall` mapping runs against the fragments
72
+ * that have arrived so far.
73
+ *
74
+ * TWO STEPS, because a partial JSON document almost never parses. The fragments are tried whole first -
75
+ * that is the case where the last one closed the object. Failing that, the FIRST `"key": "value"` pair in
76
+ * the text is read out on its own, and its value does not have to be CLOSED - a fragment usually stops
77
+ * mid-string, and a path that has arrived half-written still says more than a placeholder does. A fragment
78
+ * that has produced neither yet yields `undefined`, and the caller decides what an unknown call says
79
+ * rather than this module inventing a word for it.
80
+ *
81
+ * The pair is read with a pattern rather than by parsing a repaired document: repairing JSON is a parser's
82
+ * job, and a half-repaired document here would risk naming the wrong file - the one thing this line must
83
+ * never do.
84
+ */
85
+ export declare function describeToolCallInProgress(name: string, argumentsSoFar: string): string | undefined;
65
86
  /**
66
87
  * Every parameter of one call, as a bounded list of `key`/`value` pairs a block can show when expanded.
67
88
  *
@@ -152,6 +152,46 @@ export function describeToolCall(name, args) {
152
152
  }
153
153
  return undefined;
154
154
  }
155
+ /**
156
+ * THE SAME LINE, READ OUT OF A CALL THAT IS STILL BEING WRITTEN.
157
+ *
158
+ * The block for a call appears the instant the model starts writing it, and for an `edit` that is several
159
+ * seconds before the arguments are complete. Showing 「工具 · 正在生成参数…」 through all of it says the
160
+ * reader nothing they can act on - not WHICH tool, and not what it is aimed at, which are the two things
161
+ * the finished block is going to say. So the same `describeToolCall` mapping runs against the fragments
162
+ * that have arrived so far.
163
+ *
164
+ * TWO STEPS, because a partial JSON document almost never parses. The fragments are tried whole first -
165
+ * that is the case where the last one closed the object. Failing that, the FIRST `"key": "value"` pair in
166
+ * the text is read out on its own, and its value does not have to be CLOSED - a fragment usually stops
167
+ * mid-string, and a path that has arrived half-written still says more than a placeholder does. A fragment
168
+ * that has produced neither yet yields `undefined`, and the caller decides what an unknown call says
169
+ * rather than this module inventing a word for it.
170
+ *
171
+ * The pair is read with a pattern rather than by parsing a repaired document: repairing JSON is a parser's
172
+ * job, and a half-repaired document here would risk naming the wrong file - the one thing this line must
173
+ * never do.
174
+ */
175
+ export function describeToolCallInProgress(name, argumentsSoFar) {
176
+ const text = argumentsSoFar.trim();
177
+ if (!text)
178
+ return undefined;
179
+ try {
180
+ const parsed = JSON.parse(text);
181
+ const described = describeToolCall(name, parsed);
182
+ if (described)
183
+ return described;
184
+ }
185
+ catch { /* the document is still being written, which is the normal case here */ }
186
+ const pair = /"([A-Za-z_][A-Za-z0-9_-]*)"\s*:\s*"((?:[^"\\]|\\.)*)/.exec(text);
187
+ if (!pair)
188
+ return undefined;
189
+ // The closing quote is OPTIONAL in the pattern because a fragment almost always stops mid-string, and a
190
+ // path that has arrived half-written is still worth more than the placeholder word. What is captured is
191
+ // unescaped because an escaped quote is part of the VALUE, not the end of it - without this, a file
192
+ // named with a quote in it would be displayed with a backslash the reader never typed.
193
+ return describeToolCall(name, { [pair[1]]: pair[2].replace(/\\(["\\])/g, "$1") });
194
+ }
155
195
  /**
156
196
  * How one non-string argument value is NAMED without being read.
157
197
  *
@@ -131,6 +131,10 @@ export type ToolCallBlock = {
131
131
  * distinction that matters when a reader copies it.
132
132
  */
133
133
  live?: string;
134
+ /** The model is still GENERATING this call's arguments. Nothing has reached the host yet, so this is
135
+ * the only thing on screen during a long parameter stream - an 'edit' block carries a whole file's
136
+ * replacement text, and without this the screen is blank until the last token arrives. */
137
+ preparing?: boolean;
134
138
  at?: number;
135
139
  };
136
140
  /**
@@ -164,7 +168,21 @@ export type ToolCallNotice = {
164
168
  durationMs?: number;
165
169
  result?: ToolResultSummary;
166
170
  live?: string;
171
+ preparing?: boolean;
167
172
  };
173
+ /** WHAT THE RUNTIME HANDS A CALLER WHEN THE MODEL STARTS, OR CONTINUES, BUILDING A TOOL CALL. *
174
+ *
175
+ * `detail` is the line naming what the call is aimed at - the file for a `read`, the command for a `bash`
176
+ * - read out of the arguments as far as they have arrived. It is `undefined` when nothing identifying has
177
+ * been written yet, which is a different fact from an empty one: the caller has a placeholder word for the
178
+ * second case and must not invent one for the first.
179
+ */
180
+ export interface ToolCallPreparing {
181
+ contentIndex: number;
182
+ name: string;
183
+ delta?: string;
184
+ detail?: string;
185
+ }
168
186
  /**
169
187
  * `archivedAt` is the whole archive mechanism: a state MARKER, not a move. The conversation object never
170
188
  * changes workspace or position, so archiving and restoring are a stamp and a clear of one optional field.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evandu/super-pi",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Web control plane and companion for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -96,7 +96,8 @@
96
96
  "scripts/queue-dock.test.mjs",
97
97
  "scripts/md-render-browser.test.mjs",
98
98
  "scripts/right-sidebar-browser.test.mjs",
99
- "scripts/sidebar-tools-browser.test.mjs", "scripts/session-context-menu-browser.test.mjs",
99
+ "scripts/sidebar-tools-browser.test.mjs",
100
+ "scripts/session-context-menu-browser.test.mjs",
100
101
  "scripts/tool-call-blocks-browser.test.mjs",
101
102
  "scripts/files-tab-browser.test.mjs",
102
103
  "scripts/msg-tabs-browser.test.mjs",