@tt-a1i/openpi 0.6.1 → 0.7.0
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/THIRD_PARTY_NOTICES.md +242 -0
- package/extensions/ai-providers/cursor/connect-frame-reader.ts +76 -0
- package/extensions/ai-providers/cursor/provider.ts +6 -19
- package/extensions/file-mutation-display/index.ts +13 -9
- package/extensions/shared/agent-transcript.ts +3 -2
- package/extensions/web/index.ts +69 -5
- package/extensions/workflows/artifacts.ts +362 -23
- package/extensions/workflows/dashboard.ts +2 -0
- package/package.json +28 -4
- package/web/dist/app.js +87 -0
- package/web/dist/favicon.svg +9 -0
- package/web/dist/index.html +15 -0
- package/web/dist/styles.css +3 -0
- package/web/host/web-host.ts +6 -9
- package/web/ui/index.html +3 -131
- package/web/ui/public/favicon.svg +9 -0
- package/web/ui/src/app/App.tsx +134 -0
- package/web/ui/src/app/providers.tsx +38 -0
- package/web/ui/src/components/Markdown.tsx +58 -0
- package/web/ui/src/components/OpenPiLogo.tsx +41 -0
- package/web/ui/src/features/activity/ActivityBar.tsx +120 -0
- package/web/ui/src/features/composer/Composer.tsx +237 -0
- package/web/ui/src/features/sessions/SessionSidebar.tsx +418 -0
- package/web/ui/src/features/transcript/Transcript.tsx +860 -0
- package/web/ui/src/i18n.ts +159 -0
- package/web/ui/src/lib/format.ts +57 -0
- package/web/ui/src/main.tsx +16 -0
- package/web/ui/src/protocol/client.ts +199 -0
- package/web/ui/src/protocol/event-stream.ts +88 -0
- package/web/ui/src/store/web-store.ts +926 -0
- package/web/ui/src/styles.css +420 -0
- package/web/ui/tsconfig.json +12 -0
- package/web/ui/vite-env.d.ts +1 -0
- package/web/vite.config.mjs +21 -1
- package/web/host/static-assets.ts +0 -4
- package/web/ui/app.js +0 -1700
- package/web/ui/styles.css +0 -680
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -29,3 +29,245 @@ The upstream project is distributed under the MIT License. Its notice states
|
|
|
29
29
|
OpenPI is distributed under the MIT License; see [`LICENSE`](LICENSE). Portions
|
|
30
30
|
identified in this notice retain their original copyright notices and license
|
|
31
31
|
terms. The project-wide MIT license does not replace or remove those notices.
|
|
32
|
+
|
|
33
|
+
## Web production bundle
|
|
34
|
+
|
|
35
|
+
`web/dist/app.js` and `web/dist/styles.css` contain code from the packages
|
|
36
|
+
listed below. This inventory was taken from the Vite production module graph;
|
|
37
|
+
build-only packages are not included.
|
|
38
|
+
|
|
39
|
+
### MIT-licensed packages
|
|
40
|
+
|
|
41
|
+
Copyright (c) 2026 Meta Platforms, Inc.:
|
|
42
|
+
|
|
43
|
+
- `@astryxdesign/core@0.5.2`
|
|
44
|
+
- `@astryxdesign/theme-neutral@0.5.2`
|
|
45
|
+
|
|
46
|
+
Copyright (c) Meta Platforms, Inc. and affiliates:
|
|
47
|
+
|
|
48
|
+
- `@stylexjs/stylex@0.19.0`
|
|
49
|
+
- `react@19.2.8`
|
|
50
|
+
- `react-dom@19.2.8`
|
|
51
|
+
- `scheduler@0.27.0`
|
|
52
|
+
- `use-sync-external-store@1.6.0`
|
|
53
|
+
|
|
54
|
+
Copyright (c) 2023 FormatJS:
|
|
55
|
+
|
|
56
|
+
- `@formatjs/fast-memoize@3.1.7`
|
|
57
|
+
- `@formatjs/icu-messageformat-parser@3.5.17`
|
|
58
|
+
- `@formatjs/icu-skeleton-parser@2.1.11`
|
|
59
|
+
|
|
60
|
+
Copyright (c) 2011-present i18next:
|
|
61
|
+
|
|
62
|
+
- `i18next@26.4.1`
|
|
63
|
+
|
|
64
|
+
Copyright (c) 2015-present i18next:
|
|
65
|
+
|
|
66
|
+
- `react-i18next@17.0.13`
|
|
67
|
+
|
|
68
|
+
Copyright (c) 2026 Espen Hovlandsdal <espen@hovlandsdal.com>:
|
|
69
|
+
|
|
70
|
+
- `eventsource-parser@4.1.0`
|
|
71
|
+
|
|
72
|
+
Copyright (c) Espen Hovlandsdal:
|
|
73
|
+
|
|
74
|
+
- `react-markdown@10.1.0`
|
|
75
|
+
|
|
76
|
+
Copyright (c) 2019 Paul Henschel:
|
|
77
|
+
|
|
78
|
+
- `zustand@5.0.15`
|
|
79
|
+
|
|
80
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com>
|
|
81
|
+
(<https://sindresorhus.com>):
|
|
82
|
+
|
|
83
|
+
- `escape-string-regexp@5.0.0`
|
|
84
|
+
- `is-plain-obj@4.1.0`
|
|
85
|
+
|
|
86
|
+
Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>:
|
|
87
|
+
|
|
88
|
+
- `inline-style-parser@0.2.7`
|
|
89
|
+
|
|
90
|
+
Copyright (c) 2014 Stefan Thomas:
|
|
91
|
+
|
|
92
|
+
- `extend@3.0.2`
|
|
93
|
+
|
|
94
|
+
Copyright (c) 2017 Menglin "Mark" Xu <mark@remarkablemark.org>:
|
|
95
|
+
|
|
96
|
+
- `style-to-object@1.0.14`
|
|
97
|
+
|
|
98
|
+
Copyright (c) 2020 Menglin "Mark" Xu <mark@remarkablemark.org>:
|
|
99
|
+
|
|
100
|
+
- `style-to-js@1.1.21`
|
|
101
|
+
|
|
102
|
+
Copyright (c) 2014 Titus Wormer <tituswormer@gmail.com>:
|
|
103
|
+
|
|
104
|
+
- `remark-parse@11.0.0`
|
|
105
|
+
|
|
106
|
+
Copyright (c) 2015 Titus Wormer <tituswormer@gmail.com>:
|
|
107
|
+
|
|
108
|
+
- `bail@2.0.2`, `ccount@2.0.1`, `mdast-util-to-string@4.0.0`
|
|
109
|
+
- `unified@11.0.5`, `unist-util-is@6.0.1`, `unist-util-position@5.0.0`
|
|
110
|
+
- `unist-util-visit@5.1.0`, `vfile@6.0.3`
|
|
111
|
+
|
|
112
|
+
Copyright (c) 2015 Titus Wormer <mailto:tituswormer@gmail.com>:
|
|
113
|
+
|
|
114
|
+
- `longest-streak@3.1.0`, `trim-lines@3.0.1`
|
|
115
|
+
|
|
116
|
+
Copyright (c) 2016 Titus Wormer <tituswormer@gmail.com>:
|
|
117
|
+
|
|
118
|
+
- `comma-separated-tokens@2.0.3`, `hast-util-whitespace@3.0.0`
|
|
119
|
+
- `mdast-util-to-hast@13.2.1`, `rehype-sanitize@6.0.0`
|
|
120
|
+
- `space-separated-tokens@2.0.2`, `trough@2.2.0`
|
|
121
|
+
- `unist-util-stringify-position@4.0.0`, `unist-util-visit-parents@6.0.2`
|
|
122
|
+
|
|
123
|
+
Copyright (c) 2017 Titus Wormer <tituswormer@gmail.com>:
|
|
124
|
+
|
|
125
|
+
- `mdast-util-newline-to-break@2.0.0`, `remark-breaks@4.0.0`
|
|
126
|
+
|
|
127
|
+
Copyright (c) 2020 Titus Wormer <tituswormer@gmail.com>:
|
|
128
|
+
|
|
129
|
+
- `estree-util-is-identifier-name@3.0.0`
|
|
130
|
+
- `mdast-util-gfm-autolink-literal@2.0.1`
|
|
131
|
+
- `mdast-util-gfm-strikethrough@2.0.0`, `mdast-util-gfm-table@2.0.0`
|
|
132
|
+
- `mdast-util-gfm-task-list-item@2.0.0`, `micromark-extension-gfm@3.0.0`
|
|
133
|
+
- `micromark-extension-gfm-autolink-literal@2.1.0`
|
|
134
|
+
- `micromark-extension-gfm-strikethrough@2.1.0`
|
|
135
|
+
- `micromark-extension-gfm-task-list-item@2.1.0`
|
|
136
|
+
|
|
137
|
+
Copyright (c) 2021 Titus Wormer <tituswormer@gmail.com>:
|
|
138
|
+
|
|
139
|
+
- `micromark-extension-gfm-footnote@2.1.0`
|
|
140
|
+
|
|
141
|
+
Copyright (c) 2023 Titus Wormer <tituswormer@gmail.com>:
|
|
142
|
+
|
|
143
|
+
- `devlop@1.1.0`
|
|
144
|
+
|
|
145
|
+
Copyright (c) Titus Wormer <tituswormer@gmail.com>:
|
|
146
|
+
|
|
147
|
+
- `decode-named-character-reference@1.3.0`, `hast-util-sanitize@5.0.2`
|
|
148
|
+
- `hast-util-to-jsx-runtime@2.3.6`, `markdown-table@3.0.4`
|
|
149
|
+
- `mdast-util-find-and-replace@3.0.2`, `mdast-util-from-markdown@2.0.3`
|
|
150
|
+
- `mdast-util-gfm@3.1.0`, `mdast-util-gfm-footnote@2.1.0`
|
|
151
|
+
- `mdast-util-to-markdown@2.1.2`, `micromark@4.0.2`
|
|
152
|
+
- `micromark-core-commonmark@2.0.3`, `micromark-extension-gfm-table@2.1.1`
|
|
153
|
+
- `micromark-factory-destination@2.0.1`, `micromark-factory-label@2.0.1`
|
|
154
|
+
- `micromark-factory-space@2.0.1`, `micromark-factory-title@2.0.1`
|
|
155
|
+
- `micromark-factory-whitespace@2.0.1`, `micromark-util-character@2.1.1`
|
|
156
|
+
- `micromark-util-chunked@2.0.1`, `micromark-util-classify-character@2.0.1`
|
|
157
|
+
- `micromark-util-combine-extensions@2.0.1`
|
|
158
|
+
- `micromark-util-decode-numeric-character-reference@2.0.2`
|
|
159
|
+
- `micromark-util-decode-string@2.0.1`, `micromark-util-html-tag-name@2.0.1`
|
|
160
|
+
- `micromark-util-normalize-identifier@2.0.1`
|
|
161
|
+
- `micromark-util-resolve-all@2.0.1`, `micromark-util-sanitize-uri@2.0.1`
|
|
162
|
+
- `micromark-util-subtokenize@2.1.0`, `remark-gfm@4.0.1`
|
|
163
|
+
- `remark-rehype@11.1.2`, `vfile-message@4.0.3`
|
|
164
|
+
|
|
165
|
+
Copyright (c) Titus Wormer:
|
|
166
|
+
|
|
167
|
+
- `html-url-attributes@3.0.1`
|
|
168
|
+
|
|
169
|
+
Copyright (c) Titus Wormer <mailto:tituswormer@gmail.com>:
|
|
170
|
+
|
|
171
|
+
- `property-information@7.2.0`
|
|
172
|
+
|
|
173
|
+
`mdast-util-phrasing@4.1.0` retains both of its notices:
|
|
174
|
+
|
|
175
|
+
- Copyright (c) 2017 Titus Wormer <tituswormer@gmail.com>
|
|
176
|
+
- Copyright (c) 2017 Victor Felder <victor@draft.li>
|
|
177
|
+
|
|
178
|
+
The MIT terms for the packages above are:
|
|
179
|
+
|
|
180
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
181
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
182
|
+
> in the Software without restriction, including without limitation the rights
|
|
183
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
184
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
185
|
+
> furnished to do so, subject to the following conditions:
|
|
186
|
+
>
|
|
187
|
+
> The above copyright notice and this permission notice shall be included in
|
|
188
|
+
> all copies or substantial portions of the Software.
|
|
189
|
+
>
|
|
190
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
191
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
192
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
193
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
194
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
195
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
196
|
+
> SOFTWARE.
|
|
197
|
+
|
|
198
|
+
### ISC-licensed packages
|
|
199
|
+
|
|
200
|
+
Copyright (c) 2021, Andrea Giammarchi, @WebReflection:
|
|
201
|
+
|
|
202
|
+
- `@ungap/structured-clone@1.4.0`
|
|
203
|
+
|
|
204
|
+
Copyright (c) 2026 Lucide Icons and Contributors:
|
|
205
|
+
|
|
206
|
+
- `lucide-react@1.39.0`
|
|
207
|
+
|
|
208
|
+
The ISC terms for these packages are:
|
|
209
|
+
|
|
210
|
+
> Permission to use, copy, modify, and/or distribute this software for any
|
|
211
|
+
> purpose with or without fee is hereby granted, provided that the above
|
|
212
|
+
> copyright notice and this permission notice appear in all copies.
|
|
213
|
+
>
|
|
214
|
+
> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
215
|
+
> REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
216
|
+
> AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
217
|
+
> INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
218
|
+
> LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
219
|
+
> OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
220
|
+
> PERFORMANCE OF THIS SOFTWARE.
|
|
221
|
+
|
|
222
|
+
Some Lucide icons are derived from Feather and retain this additional notice:
|
|
223
|
+
|
|
224
|
+
> Copyright (c) 2013-present Cole Bemis
|
|
225
|
+
>
|
|
226
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
227
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
228
|
+
> in the Software without restriction, including without limitation the rights
|
|
229
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
230
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
231
|
+
> furnished to do so, subject to the following conditions:
|
|
232
|
+
>
|
|
233
|
+
> The above copyright notice and this permission notice shall be included in
|
|
234
|
+
> all copies or substantial portions of the Software.
|
|
235
|
+
>
|
|
236
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
237
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
238
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
239
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
240
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
241
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
242
|
+
> SOFTWARE.
|
|
243
|
+
|
|
244
|
+
### BSD-3-Clause package
|
|
245
|
+
|
|
246
|
+
`intl-messageformat@11.2.14` retains this notice:
|
|
247
|
+
|
|
248
|
+
> Copyright (c) 2023, Oath Inc.
|
|
249
|
+
>
|
|
250
|
+
> Redistribution and use of this software in source and binary forms, with or
|
|
251
|
+
> without modification, are permitted provided that the following conditions
|
|
252
|
+
> are met:
|
|
253
|
+
>
|
|
254
|
+
> - Redistributions of source code must retain the above copyright notice,
|
|
255
|
+
> this list of conditions and the following disclaimer.
|
|
256
|
+
> - Redistributions in binary form must reproduce the above copyright notice,
|
|
257
|
+
> this list of conditions and the following disclaimer in the documentation
|
|
258
|
+
> and/or other materials provided with the distribution.
|
|
259
|
+
> - Neither the name of Oath Inc. nor the names of its contributors may be used
|
|
260
|
+
> to endorse or promote products derived from this software without specific
|
|
261
|
+
> prior written permission.
|
|
262
|
+
>
|
|
263
|
+
> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
264
|
+
> AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
265
|
+
> IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
266
|
+
> ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
267
|
+
> LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
268
|
+
> CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
269
|
+
> SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
270
|
+
> INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
271
|
+
> CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
272
|
+
> ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
273
|
+
> POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Incremental Cursor Connect framing; callbacks consume complete frames synchronously. */
|
|
2
|
+
export class ConnectFrameReader {
|
|
3
|
+
private readonly maxBytes: number;
|
|
4
|
+
private readonly header = Buffer.alloc(5);
|
|
5
|
+
private headerBytes = 0;
|
|
6
|
+
private payload: Buffer | undefined;
|
|
7
|
+
private payloadBytes = 0;
|
|
8
|
+
|
|
9
|
+
constructor(maxBytes: number) {
|
|
10
|
+
this.maxBytes = maxBytes;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
get incomplete() {
|
|
14
|
+
return this.headerBytes !== 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get awaitingPayload() {
|
|
18
|
+
return this.payload !== undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
push(chunk: Buffer, onFrame: (flags: number, data: Buffer) => void) {
|
|
22
|
+
let offset = 0;
|
|
23
|
+
while (offset < chunk.length) {
|
|
24
|
+
if (this.payload) {
|
|
25
|
+
const copied = chunk.copy(
|
|
26
|
+
this.payload,
|
|
27
|
+
this.payloadBytes,
|
|
28
|
+
offset,
|
|
29
|
+
offset +
|
|
30
|
+
Math.min(
|
|
31
|
+
chunk.length - offset,
|
|
32
|
+
this.payload.length - this.payloadBytes,
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
offset += copied;
|
|
36
|
+
this.payloadBytes += copied;
|
|
37
|
+
} else {
|
|
38
|
+
if (this.headerBytes < 5) {
|
|
39
|
+
const copied = chunk.copy(
|
|
40
|
+
this.header,
|
|
41
|
+
this.headerBytes,
|
|
42
|
+
offset,
|
|
43
|
+
offset + Math.min(5 - this.headerBytes, chunk.length - offset),
|
|
44
|
+
);
|
|
45
|
+
offset += copied;
|
|
46
|
+
this.headerBytes += copied;
|
|
47
|
+
if (this.headerBytes < 5) return;
|
|
48
|
+
}
|
|
49
|
+
const size = this.header.readUInt32BE(1);
|
|
50
|
+
if (size > this.maxBytes)
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Cursor Connect frame exceeds ${this.maxBytes} bytes`,
|
|
53
|
+
);
|
|
54
|
+
if (chunk.length - offset >= size) {
|
|
55
|
+
const data = chunk.subarray(offset, offset + size);
|
|
56
|
+
const flags = this.header[0]!;
|
|
57
|
+
offset += size;
|
|
58
|
+
this.headerBytes = 0;
|
|
59
|
+
onFrame(flags, data);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
this.payload = Buffer.allocUnsafe(size);
|
|
63
|
+
this.payloadBytes = 0;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (this.payloadBytes === this.payload.length) {
|
|
67
|
+
const data = this.payload;
|
|
68
|
+
const flags = this.header[0]!;
|
|
69
|
+
this.payload = undefined;
|
|
70
|
+
this.payloadBytes = 0;
|
|
71
|
+
this.headerBytes = 0;
|
|
72
|
+
onFrame(flags, data);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
} from "@earendil-works/pi-ai/compat";
|
|
15
15
|
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai/compat";
|
|
16
16
|
import { emptyUsage } from "../usage.ts";
|
|
17
|
+
import { ConnectFrameReader } from "./connect-frame-reader.ts";
|
|
17
18
|
import {
|
|
18
19
|
CURSOR_API_URL,
|
|
19
20
|
CURSOR_CLIENT_VERSION,
|
|
@@ -833,7 +834,7 @@ export function streamCursor(
|
|
|
833
834
|
h2Request?.close(http2.constants.NGHTTP2_CANCEL);
|
|
834
835
|
}, requestTimeoutMs);
|
|
835
836
|
};
|
|
836
|
-
|
|
837
|
+
const frames = new ConnectFrameReader(MAX_CONNECT_FRAME_BYTES);
|
|
837
838
|
const processFrame = (flags: number, bytes: Uint8Array) => {
|
|
838
839
|
if (handingOffTools) return;
|
|
839
840
|
if ((flags & CONNECT_COMPRESSED_FLAG) !== 0) {
|
|
@@ -1033,23 +1034,9 @@ export function streamCursor(
|
|
|
1033
1034
|
);
|
|
1034
1035
|
};
|
|
1035
1036
|
const processData = (chunk: Buffer) => {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
: Buffer.concat([frameBuffer, chunk]);
|
|
1040
|
-
while (frameBuffer.length >= 5) {
|
|
1041
|
-
const size = frameBuffer.readUInt32BE(1);
|
|
1042
|
-
if (size > MAX_CONNECT_FRAME_BYTES) {
|
|
1043
|
-
throw new Error(
|
|
1044
|
-
`Cursor Connect frame exceeds ${MAX_CONNECT_FRAME_BYTES} bytes`,
|
|
1045
|
-
);
|
|
1046
|
-
}
|
|
1047
|
-
if (frameBuffer.length < size + 5) return;
|
|
1048
|
-
const flags = frameBuffer[0]!;
|
|
1049
|
-
const data = frameBuffer.subarray(5, size + 5);
|
|
1050
|
-
frameBuffer = frameBuffer.subarray(size + 5);
|
|
1051
|
-
processFrame(flags, data);
|
|
1052
|
-
}
|
|
1037
|
+
frames.push(chunk, processFrame);
|
|
1038
|
+
// Preserve tool batching while a complete header awaits its body.
|
|
1039
|
+
if (frames.awaitingPayload) return;
|
|
1053
1040
|
if (pendingCalls.size > 0 && !handingOffTools) {
|
|
1054
1041
|
if (terminalError) throw terminalError;
|
|
1055
1042
|
if (options?.signal?.aborted)
|
|
@@ -1158,7 +1145,7 @@ export function streamCursor(
|
|
|
1158
1145
|
.then(() => {
|
|
1159
1146
|
if (!responseSeen)
|
|
1160
1147
|
throw new Error("Cursor response headers were not received");
|
|
1161
|
-
if (
|
|
1148
|
+
if (frames.incomplete)
|
|
1162
1149
|
throw new Error("Incomplete Cursor Connect frame");
|
|
1163
1150
|
settle();
|
|
1164
1151
|
})
|
|
@@ -27,15 +27,19 @@ function compact<TParams extends TSchema, TDetails, TState>(
|
|
|
27
27
|
export default function fileMutationDisplay(pi: ExtensionAPI) {
|
|
28
28
|
pi.on("session_start", (_event, ctx) => {
|
|
29
29
|
const display = loadSetupConfig().ui;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
// This extension changes only the interactive TUI projection. Headless
|
|
31
|
+
// sessions must keep Pi's native definitions, especially bash: replacing
|
|
32
|
+
// it here would drop the SettingsManager-provided shellPath and can make
|
|
33
|
+
// Windows resolve the WSL System32 stub instead of the configured shell.
|
|
34
|
+
if (ctx.mode !== "tui") return;
|
|
35
|
+
|
|
36
|
+
// Ctrl+O remains a temporary override. A new/reloaded session starts from
|
|
37
|
+
// the persisted defaults instead of inheriting an old expanded toggle.
|
|
38
|
+
ctx.ui.setToolsExpanded(
|
|
39
|
+
display.subagentResultDisplay === "full" &&
|
|
40
|
+
display.bashToolDisplay === "full" &&
|
|
41
|
+
display.fileMutationDisplay === "full",
|
|
42
|
+
);
|
|
39
43
|
|
|
40
44
|
pi.registerTool(
|
|
41
45
|
compact(
|
|
@@ -409,7 +409,7 @@ function findResult(
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
/**
|
|
412
|
-
* Caches finalized transcript items by identity and
|
|
412
|
+
* Caches finalized transcript items by identity, width, and cwd. Live state remains
|
|
413
413
|
* uncached because it changes on every stream tick; callers clear this cache
|
|
414
414
|
* from their component's invalidate() when Pi changes theme.
|
|
415
415
|
*/
|
|
@@ -434,7 +434,8 @@ export class AgentTranscriptRenderer {
|
|
|
434
434
|
for (let index = 0; index < document.items.length; index++) {
|
|
435
435
|
const item = document.items[index];
|
|
436
436
|
const context = itemContext(document.items, index, liveIds, pairing);
|
|
437
|
-
|
|
437
|
+
// Fallback tool rows relativize paths against the document's cwd.
|
|
438
|
+
const key = JSON.stringify([width, context.token, document.cwd]);
|
|
438
439
|
const cacheable = !document.toolRenderer || !itemHasTool(item);
|
|
439
440
|
const cached = cacheable ? this.itemCache.get(item)?.get(key) : undefined;
|
|
440
441
|
const lines =
|
package/extensions/web/index.ts
CHANGED
|
@@ -10,12 +10,23 @@ import {
|
|
|
10
10
|
PI_CODING_AGENT_ENTRY_ENV,
|
|
11
11
|
resolvePiCodingAgentEntry,
|
|
12
12
|
} from "../../web/host/pi-coding-agent-entry.ts";
|
|
13
|
+
import { TerminalTextSanitizer } from "../shared/terminal-text.ts";
|
|
13
14
|
|
|
14
15
|
const DEFAULT_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
16
|
+
const WEB_ERROR_TAIL_MAX_BYTES = 8 * 1024;
|
|
17
|
+
|
|
18
|
+
interface WebProcessErrorStream {
|
|
19
|
+
on(event: "data", listener: (chunk: Buffer | string) => void): this;
|
|
20
|
+
removeListener(
|
|
21
|
+
event: "data",
|
|
22
|
+
listener: (chunk: Buffer | string) => void,
|
|
23
|
+
): this;
|
|
24
|
+
}
|
|
15
25
|
|
|
16
26
|
export interface WebProcess {
|
|
17
27
|
readonly exitCode: number | null;
|
|
18
28
|
readonly signalCode: NodeJS.Signals | null;
|
|
29
|
+
readonly stderr: WebProcessErrorStream | null;
|
|
19
30
|
once(event: "error", listener: (error: Error) => void): this;
|
|
20
31
|
once(
|
|
21
32
|
event: "close",
|
|
@@ -28,7 +39,7 @@ interface SpawnWebOptions {
|
|
|
28
39
|
cwd: string;
|
|
29
40
|
env: NodeJS.ProcessEnv;
|
|
30
41
|
shell: false;
|
|
31
|
-
stdio: "inherit";
|
|
42
|
+
stdio: ["inherit", "inherit", "pipe"];
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
function webProcessEnvironment(
|
|
@@ -51,6 +62,7 @@ function webProcessEnvironment(
|
|
|
51
62
|
export interface WebCommandDependencies {
|
|
52
63
|
entrypoint: string;
|
|
53
64
|
spawn(command: string, args: string[], options: SpawnWebOptions): WebProcess;
|
|
65
|
+
writeStderr(chunk: Buffer | string): void;
|
|
54
66
|
clearTerminal(): void;
|
|
55
67
|
holdParentSigint(): () => void;
|
|
56
68
|
resolvePiCodingAgentEntry(): string | undefined;
|
|
@@ -58,7 +70,12 @@ export interface WebCommandDependencies {
|
|
|
58
70
|
}
|
|
59
71
|
|
|
60
72
|
type WebExit =
|
|
61
|
-
| {
|
|
73
|
+
| {
|
|
74
|
+
kind: "close";
|
|
75
|
+
code: number | null;
|
|
76
|
+
signal: NodeJS.Signals | null;
|
|
77
|
+
errorDetail?: string;
|
|
78
|
+
}
|
|
62
79
|
| { kind: "error"; error: Error };
|
|
63
80
|
|
|
64
81
|
interface ActiveWebProcess {
|
|
@@ -71,6 +88,9 @@ const defaultDependencies: WebCommandDependencies = {
|
|
|
71
88
|
spawn(command, args, options) {
|
|
72
89
|
return nodeSpawn(command, args, options);
|
|
73
90
|
},
|
|
91
|
+
writeStderr(chunk) {
|
|
92
|
+
process.stderr.write(chunk);
|
|
93
|
+
},
|
|
74
94
|
clearTerminal() {
|
|
75
95
|
process.stdout.write("\u001b[2J\u001b[H");
|
|
76
96
|
},
|
|
@@ -91,6 +111,31 @@ function delay(milliseconds: number) {
|
|
|
91
111
|
});
|
|
92
112
|
}
|
|
93
113
|
|
|
114
|
+
function boundedUtf8Tail(text: string, maxBytes: number) {
|
|
115
|
+
let bytes = 0;
|
|
116
|
+
let start = text.length;
|
|
117
|
+
for (const character of Array.from(text).reverse()) {
|
|
118
|
+
const characterBytes = Buffer.byteLength(character, "utf8");
|
|
119
|
+
if (bytes + characterBytes > maxBytes) break;
|
|
120
|
+
bytes += characterBytes;
|
|
121
|
+
start -= character.length;
|
|
122
|
+
}
|
|
123
|
+
return text.slice(start);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function actionableWebError(stderr: string) {
|
|
127
|
+
const lines = stderr
|
|
128
|
+
.split(/\r?\n/u)
|
|
129
|
+
.map((line) => line.trim())
|
|
130
|
+
.filter(Boolean);
|
|
131
|
+
const newestFirst = [...lines].reverse();
|
|
132
|
+
return (
|
|
133
|
+
newestFirst.find((line) =>
|
|
134
|
+
line.includes("Failed to start OpenPI Web Workbench:"),
|
|
135
|
+
) ?? newestFirst.find((line) => /^(?:Error|Failed):/u.test(line))
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
94
139
|
async function stopWebProcess(active: ActiveWebProcess, timeoutMs: number) {
|
|
95
140
|
if (active.child.exitCode !== null || active.child.signalCode !== null)
|
|
96
141
|
return;
|
|
@@ -116,15 +161,26 @@ function runWebInForeground(
|
|
|
116
161
|
return ctx.ui.custom<WebExit>((tui, _theme, _keybindings, done) => {
|
|
117
162
|
let finished = false;
|
|
118
163
|
let tuiStopped = false;
|
|
164
|
+
let stderrTail = "";
|
|
165
|
+
let childStderr: WebProcessErrorStream | null | undefined;
|
|
119
166
|
let resolveClosed = () => {};
|
|
120
167
|
const closed = new Promise<void>((resolve) => {
|
|
121
168
|
resolveClosed = resolve;
|
|
122
169
|
});
|
|
123
170
|
const releaseParentSigint = dependencies.holdParentSigint();
|
|
171
|
+
const sanitizer = new TerminalTextSanitizer();
|
|
172
|
+
const captureStderr = (chunk: Buffer | string) => {
|
|
173
|
+
dependencies.writeStderr(chunk);
|
|
174
|
+
stderrTail = boundedUtf8Tail(
|
|
175
|
+
`${stderrTail}${sanitizer.push(String(chunk))}`,
|
|
176
|
+
WEB_ERROR_TAIL_MAX_BYTES,
|
|
177
|
+
);
|
|
178
|
+
};
|
|
124
179
|
|
|
125
180
|
const finish = (result: WebExit) => {
|
|
126
181
|
if (finished) return;
|
|
127
182
|
finished = true;
|
|
183
|
+
childStderr?.removeListener("data", captureStderr);
|
|
128
184
|
releaseParentSigint();
|
|
129
185
|
setActive(undefined);
|
|
130
186
|
resolveClosed();
|
|
@@ -147,13 +203,20 @@ function runWebInForeground(
|
|
|
147
203
|
cwd: childCwd,
|
|
148
204
|
env: webProcessEnvironment(childCwd, piCodingAgentEntry),
|
|
149
205
|
shell: false,
|
|
150
|
-
stdio: "inherit",
|
|
206
|
+
stdio: ["inherit", "inherit", "pipe"],
|
|
151
207
|
},
|
|
152
208
|
);
|
|
209
|
+
childStderr = child.stderr;
|
|
210
|
+
childStderr?.on("data", captureStderr);
|
|
153
211
|
setActive({ child, closed });
|
|
154
212
|
child.once("error", (error) => finish({ kind: "error", error }));
|
|
155
213
|
child.once("close", (code, signal) =>
|
|
156
|
-
finish({
|
|
214
|
+
finish({
|
|
215
|
+
kind: "close",
|
|
216
|
+
code,
|
|
217
|
+
signal,
|
|
218
|
+
errorDetail: actionableWebError(stderrTail),
|
|
219
|
+
}),
|
|
157
220
|
);
|
|
158
221
|
} catch (error) {
|
|
159
222
|
finish({
|
|
@@ -242,7 +305,8 @@ export default function web(
|
|
|
242
305
|
}
|
|
243
306
|
if (result.code !== 0) {
|
|
244
307
|
ctx.ui.notify(
|
|
245
|
-
|
|
308
|
+
result.errorDetail ??
|
|
309
|
+
`OpenPI Web Workbench exited with code ${result.code ?? "unknown"}.`,
|
|
246
310
|
"error",
|
|
247
311
|
);
|
|
248
312
|
return;
|