You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 633d0a8
Browse filesBrowse the repository at this point in the historyBrowse files
fix(web): #105 review — a sign-in link nobody asked for signs nobody in; search, diagram links, completions and storage work in a tab
Sixteen findings: four from the code-quality check, twelve from Copilot. All held up against the code; each
substantive one is fixed with a test that fails on the code as reviewed.
Signing in
- callback.js forwarded every parameter of the link — `token=` and `refresh=` included — to the extension,
whose old token-in-the-link branch stored them, and the page's start-up delivers what callback.html left even
when no sign-in was pending. A crafted link to callback.html, followed by opening the editor, signed the user
in to the link-maker's account. callback.html now passes on a sign-in to levelcode.levelcode-ai/auth/callback
and only its one-time code; and in a browser (new capability `host.caps.unaskedSignIn`, off there) the
extension takes a code only while a sign-in of its own is pending — never a token in a link. The desktop is
unchanged.
- A sign-in started from a folder or workspace opened from the computer came back to `/`, the scratch
workspace. The editor notes the address it leaves (minus `signin=1`); callback.html returns there after
checking it is the editor page of this site.
- With no persistent secret store (no IndexedDB or WebCrypto, or a refused write), the PKCE verifier lived in
memory and was gone when the tab came back from the sign-in. The tab's secrets now ride through that one
navigation in sessionStorage and are taken back, and removed, at the next start.
- Two tabs opened at once on a fresh profile could each create and store a different sealing key; each then
read the other's sealed session as garbage and cleared it. The key is stored in one readwrite transaction,
only if none is there, and every tab uses the key that is.
The agent in a tab
- The content scans that stand in for ripgrep (agent.js scanSearch, extension.js contentHits) capped the file
list across the whole workspace before filtering to the folder searched: in a two-folder workspace the other
folder could use up the cap. Both now search under the folder (RelativePattern) and cap that.
- Diagram links were checked with fs.statSync/realpathSync, which in the browser build reach a shim with no
workspace in it, so every link was dropped. Drawing now checks against the files the extension has indexed;
a click checks the file system itself (host.withStats, the async-loader pattern of host.withReads).
- Pending Keep/Undo reviews are read back asynchronously in a browser; a chat that asked for its cards first
was sent none and never got them. They are sent once they are read.
- Inline completion refused every document that is not `file` or `untitled` — all of the scratch workspace.
It now asks host.isUserDocument, which in a browser also accepts the workspace folders' own schemes.
The scratch workspace
- A rename or copy over an existing folder deleted only the folder's own entry: its files stayed under the new
name, visible to search. The whole destination subtree goes in the same transaction; a move over a folder
that contains the source is refused.
- With IndexedDB unavailable, activation returned before registering "Open a folder from your computer" — the
one way out the warning names. It is registered first.
Hosting
- The generated nginx block answered 404 at `/`: `try_files $uri` looks for a file, and an exact location keeps
the request from the fallback. The root rule names /index.html.
- The build id hashed a list of inputs and missed the rest — the declarative built-ins (themes, grammars), the
web client's CSS, workers and nls — so a changed file was served stale under the same immutable URL. The
build is now staged first and named after a hash of everything staged. Checked by building: two builds give
the same id; a change to the client's CSS alone gives a new one (the reviewed script gave the same id).
Code quality: an unused SYSTEM_TOKENS_EST (unused on develop too), an unused import and two unused helpers in
the browser tests.
Tests: new suites callback, signin, headers, search-scope and review-rehydrate; main, host and workspace extend
theirs (fake-idb.js is a small IndexedDB stand-in for them). The unit gate passes, 75 files, on Node 24 and
Node 18. In Chrome against a build with these changes: e2e 28/28, e2e-signin-tab 20/20 (its left-behind-code
case now expects the code to be delivered and NOT exchanged), e2e-byok 5/5. Not run: a real nginx (Docker was
unavailable), Safari, Firefox.
|`--webview-origin 'https://{{uuid}}.view.example.com'` (`LEVELCODE_WEB_WEBVIEW_ORIGIN`) | Self-host the webview origin. Without it the page uses Code-OSS's CDN at this build's own commit. Needs wildcard DNS and TLS — see below. |
145
156
|`--ext-host-origin 'https://{{uuid}}.ext.example.com'` (`LEVELCODE_WEB_EXTHOST_ORIGIN`) | Run the extension host on an origin of its own. Optional while no third-party code is loaded. |
146
157
|`--static <dir>`| Use a prebuilt client (skips the gulp build). |
147
-
|`--id <id>`| Override the build id (default: a fingerprint of the client, the page and the extension). |
158
+
|`--id <id>`| Override the build id (default: a hash of everything the build put under `/_/<id>/`, staged first and named after it). |
Copy file name to clipboardExpand all lines: extensions/levelcode-ai/agent.js
+2-5Lines changed: 2 additions & 5 deletions
Original file line number
Diff line number
Diff line change
@@ -78,10 +78,6 @@ const TOOLS = [
78
78
{name: 'use_skill',description: 'Load an expert playbook (SKILL.md) for a task type, chosen from the "Available skills" list in your system prompt. Returns the skill\'s step-by-step instructions as the tool result — then follow them. Read-only and instant (no approval). Call it once, early, when the goal matches a skill\'s description.',input_schema: {type: 'object',properties: {name: {type: 'string',description: 'the exact skill name from the Available skills list'}},required: ['name']}}
79
79
];
80
80
81
-
// Rough token estimates (chars/4) for the static prompt segments, so the context popover can break
82
-
// down "what's filling the window" — system + tools are sent on every request, the rest is messages.
if(!r.ok){vscode.window.showWarningMessage('LevelCode: that link cannot be opened — '+r.reason+'.');dbg('diagram.link.refused',{reason: r.reason});return;}
1122
1126
recordDiagramStat({type: 'link'});
1123
1127
try{
@@ -3567,10 +3571,16 @@ async function handleAuthCallback(uri) {
0 commit comments