@mrclrchtr/supi-lsp 11.2.0 → 11.4.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/README.md +40 -3
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +2 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +3 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/declaration-level.ts +34 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/query-result.ts +9 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -5
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/child-web-tools.ts +110 -0
- package/node_modules/@mrclrchtr/supi-core/src/session.ts +11 -0
- package/node_modules/semver/classes/range.js +2 -2
- package/node_modules/semver/internal/constants.js +30 -0
- package/node_modules/semver/internal/trim-comparator-whitespace.js +438 -0
- package/node_modules/semver/package.json +1 -1
- package/package.json +3 -3
- package/src/client/client-diagnostic-collection.ts +11 -4
- package/src/client/client-diagnostics.ts +77 -6
- package/src/client/client-input-admission.ts +149 -0
- package/src/client/client-typescript-project.ts +40 -0
- package/src/client/client.ts +151 -44
- package/src/config/tsconfig-scope.ts +10 -0
- package/src/diagnostics/evidence.ts +2 -2
- package/src/manager/manager-typescript-coverage.ts +118 -0
- package/src/manager/manager-workspace-symbol.ts +157 -23
- package/src/manager/manager.ts +318 -58
- package/src/manager/typescript-file-local.ts +87 -0
- package/src/provider/semantic-symbol-mapper.ts +22 -17
- package/src/session/runtime-registry.ts +68 -6
- package/src/session/runtime-rename.ts +1 -0
- package/src/session/workspace-lsp-runtime.ts +5 -1
package/README.md
CHANGED
|
@@ -64,6 +64,8 @@ A configured route is not a ready server. Features depend on the active server,
|
|
|
64
64
|
|
|
65
65
|
The [operation-support model](src/config/operation-support.ts) separates server advertisement from SuPi planning limits. A server can advertise an action that SuPi cannot safely turn into a plan.
|
|
66
66
|
|
|
67
|
+
Workspace-symbol scopes select contributing routes and bound query completeness; returned data can still contain out-of-scope rows, which consumers must filter. An automatic-policy exclusion does not make a scoped query partial when both its observed path and physical identity are proven outside every requested scope. In-scope exclusions, aliases that may overlap, unresolved paths, and contributing route failures remain partial. Unscoped queries retain all exclusion evidence. Partial metadata sets `exclusionOnly` when policy exclusions are the only gap, with no route failure or inferred-project coverage gap. Explicitly selected excluded files and directories remain inspectable; when a scoped query returns no row inside any scope, warm-up may open files inside those selected scopes so a server that only indexes opened documents can report them. Automatic path rules and target-uniqueness checks are unchanged.
|
|
68
|
+
|
|
67
69
|
### Reference-server acceptance
|
|
68
70
|
|
|
69
71
|
The [Code Intelligence acceptance suite](../supi-code-intelligence/__tests__/acceptance/language-baseline-harness.ts) exercises public tools against real servers for TypeScript, JavaScript, Python, Java, Go, Rust, C, and C++. Its [pinned Linux/macOS configuration](../../scripts/language-baseline/pins.json) is separate from the built-in route matrix. Qualification remains pending until both pinned CI jobs pass. Missing test prerequisites are failures, not skipped passes.
|
|
@@ -98,7 +100,7 @@ For example, select a compiler installed as `my-ts6` without changing the TLS co
|
|
|
98
100
|
- `code_health` and `/supi-ci-status` show the `native` or `tsserver` backend and resolved compiler version when known, separately from wrapper version. Manual/legacy-fallback launches have no inferred compiler identity. Missing versions remain unknown. Selection failures identify the package/root and a corrective action.
|
|
99
101
|
- Topmost-root collapsing remains unchanged. Mixed nested backends, Windows live qualification, dependency watchers, performance benchmarking, and full TLS/native feature parity are outside this change. Existing legacy TLS support is not deliberately removed.
|
|
100
102
|
|
|
101
|
-
Focused qualification
|
|
103
|
+
Focused qualification uses the native TypeScript 7 alias (`@typescript/native`), TLS, ordinary TypeScript 6, and `@typescript/typescript6` backed by `@typescript/old` → TypeScript 6. Exact versions come from the root `package.json` and lockfile; the wrapper's dependency range does not fix its compiler version, so the lockfile does. SuPi retains TS6 for its compiler-API dependency and legacy-server tests. Workspace typechecking and declaration builds use the native TS7 launcher directly to avoid ambiguity between the installed `tsc` binaries.
|
|
102
104
|
|
|
103
105
|
The required [Linux/macOS CI matrix](../../.github/workflows/ci.yml) keeps [selection/layout tests](__tests__/unit/typescript-launch.test.ts), [real native lifecycle tests](__tests__/integration/workspace-runtime.integration.typescript-native.test.ts), and [wrapper lifecycle tests](__tests__/integration/workspace-runtime.integration.typescript-launch.test.ts) separate from the [public TS/JS semantic matrix](../supi-code-intelligence/__tests__/acceptance/language-baseline-harness.ts). Required missing/unavailable behavior fails rather than skips. The matrix covers hover, definitions, references, meaningful implementations, document/workspace symbols, confirmed error → clean diagnostics, refresh, cross-file invalidation, Unicode-safe rename plan/apply and stale-plan rejection, and precise organize-imports. The TypeScript public matrix also checks unused-import removal, unchanged source during planning, stale-plan rejection, and preservation of unrelated text; native support is imports-only. Native TSX/JSX and module suffixes receive routing/synchronization checks rather than duplicate matrices. Existing stale-result and edit-safety suites remain in place.
|
|
104
106
|
|
|
@@ -161,6 +163,22 @@ Protocol support is separate from the configured mode. A server may support pull
|
|
|
161
163
|
|
|
162
164
|
The LSP 3.18 specification adds `Diagnostic.message` markup content, guarded by the client capability `textDocument.diagnostic.markupMessageSupport`; SuPi's validator already accepts plaintext and markdown messages but does not advertise the capability. Other 3.18 features (snippet text edits, inline completion, folding-range refresh, multi-range formatting) are outside the diagnostic surface and are not implemented.
|
|
163
165
|
|
|
166
|
+
### Texlab diagnostic verification
|
|
167
|
+
|
|
168
|
+
Verified binary: **Texlab 5.26.0**. This separate verification does not revise the historical audit above or qualify other Texlab versions. The probe advertised pull support, opened a document with a syntax error, and then corrected and saved it.
|
|
169
|
+
|
|
170
|
+
| Protocol check | Observed result |
|
|
171
|
+
|---|---|
|
|
172
|
+
| Static `diagnosticProvider` | Not advertised |
|
|
173
|
+
| Dynamic `textDocument/diagnostic` registration | None observed |
|
|
174
|
+
| Explicit `textDocument/diagnostic` request | JSON-RPC `-32601` (`method not found`) |
|
|
175
|
+
| `publishDiagnostics` | Publications without a document version |
|
|
176
|
+
| Syntax error → corrected document | Error publication followed by empty publications |
|
|
177
|
+
|
|
178
|
+
Texlab supplies useful diagnostics through ambient push. SuPi shows observed errors as partial evidence and removes them after a correcting publication, but an empty publication does not confirm a completed clean check. `code_health` therefore keeps the file unconfirmed. A `push-settle` timeout ends the bounded observation window; it is not a failed diagnostic request or proof that Texlab is unresponsive. Longer waits, repeated publications, and restarts do not establish completion.
|
|
179
|
+
|
|
180
|
+
The [installed Texlab integration test](__tests__/integration/client.integration.texlab.test.ts) covers routing, the absence of a diagnostic provider/request adapter, and unconfirmed coverage. It runs when `texlab` is installed. See [ADR 0022](../../docs/adr/0022-request-confirmed-lsp-agnostic-diagnostics.md) for the unchanged evidence policy.
|
|
181
|
+
|
|
164
182
|
### Diagnostic evidence policy
|
|
165
183
|
|
|
166
184
|
See [`ADR 0022`](../../docs/adr/0022-request-confirmed-lsp-agnostic-diagnostics.md) for the decision record. SuPi uses one shared diagnostic engine with optional internal request adapters. It selects one source for each file:
|
|
@@ -185,11 +203,13 @@ Refresh and file collection share synchronization, evidence-revision, deadline,
|
|
|
185
203
|
|
|
186
204
|
Caller cancellation or deadline stops only that caller's wait. Explicit diagnostic requests use an owned transport lifetime: supersession drops queued jobs and stops future adapter phases but does not cancel an active protocol request. The route stays occupied until actual settlement or connection disposal. The owner timeout attempts protocol cancellation but does not prove that the backend stopped. The owner bound is at least 30 seconds (or a larger collection budget). A result from an obsolete document generation is discarded. Ordinary semantic requests still pass caller cancellation to the protocol transport.
|
|
187
205
|
|
|
206
|
+
First document enrollment is admitted between semantic query and refactor-planning attempts on the same current client. Each attempt covers input synchronization, its RPCs, and final acceptance; reads can still run concurrently. A refactor attempt retains admission through `prepareRename`/`rename` or `codeAction`/`resolve` and verified snapshot capture, but releases it before retries and the rename workflow's own cross-file enrollment. FIFO admission prevents later reads from overtaking a waiting first open. At most 32 operations may wait; excess demand fails rather than growing an unbounded queue. Waiting preserves the caller's original signal and absolute deadline, and interrupted operations are removed before they can open a document. Disk content is read after admission. First opens for semantic or refactor demand also discard the caller's original parameters after a known input change; ordinary tracking can still adopt fresh content. Tracked-document updates, closes, workspace changes, and lifecycle invalidation do not wait: they still invalidate active evidence and discard queued request parameters. Closing a file cancels its queued opens, and stopping a client discards its queued work. Admission does not span retries, project warm-up, or whole tool/refactor workflows. Diagnostic transport ownership and confirmation remain separate and unchanged.
|
|
207
|
+
|
|
188
208
|
If another request first opens a document during an active semantic or diagnostic request, the client can synchronize and repeat the request once. The retry retains the caller's original signal and deadline. Content, workspace, close, and lifecycle changes still reject stale results. A diagnostic retry collects new request evidence; it does not promote an old cache. This retry is not route recovery. A second enrollment during the retry still rejects the result. Semantic failure reasons state whether the retry failed or was exhausted. The bounded `request.retry` debug event records the retry reason and outcome with the public call's opaque operation ID, when supplied. A completed RPC alone does not establish accepted semantic evidence.
|
|
189
209
|
|
|
190
210
|
A read-only semantic request can also retry once after an LSP `ContentModified` error (`-32801`). This retry shares the same one-retry budget as enrollment; it does not add another try. Before this protocol retry, the client waits 100 ms so an immediate retry does not race the same server analysis. The wait is cancellable, respects the original absolute deadline, and does not delay enrollment-only retries. The client keeps the original request parameters and checks changes from the first failed input revision. It runs the full input sync before retry and checks current inputs before it accepts a result. An enrollment is still the only input change that can permit a retry. A content, workspace, close, lifecycle, or retirement change blocks it. Client replacement, caller cancellation, and a deadline also block retry. The post-request check rejects a result if inputs change while that attempt runs. This error-code retry applies only to read-only semantic requests. Diagnostics, rename, code actions, and refactors keep the default policy and do not retry `ContentModified`. See the [LSP 3.17 message-ordering rule](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#messageOrdering), the [retry guard](src/client/client-request-enrollment.ts), and the [client query path](src/client/client.ts).
|
|
191
211
|
|
|
192
|
-
Diagnostic collection does not close and reopen a document to obtain confirmation. It does not send a no-op `didChange` for that purpose. Refresh retains unchanged documents and their current server state, and resynchronizes changed or invalidated documents. A client restart may reopen tracked documents to restore client state; that is recovery, not diagnostic confirmation.
|
|
212
|
+
Diagnostic collection does not close and reopen a document to obtain confirmation. It does not send a no-op `didChange` for that purpose. Refresh retains unchanged documents and their current server state, and resynchronizes changed or invalidated documents. A client restart may reopen tracked documents to restore client state; that is recovery, not diagnostic confirmation. Recovery skips documents closed or already reopened on the replacement and checks client ownership before reading or restoring failed state.
|
|
193
213
|
|
|
194
214
|
Server readiness follows LSP work-done progress: a created progress token is pending and never blocks readiness; an observed `begin` marks active work and makes the client not ready until its `end` or the bounded per-token timeout.
|
|
195
215
|
|
|
@@ -233,7 +253,7 @@ Set `code-intelligence.exclude` in project or global SuPi configuration. A proje
|
|
|
233
253
|
|
|
234
254
|
An old `lsp.exclude` value is a migration error. Move it to `code-intelligence.exclude`; SuPi does not edit personal configuration for you.
|
|
235
255
|
|
|
236
|
-
An exact semantic request can still route an excluded file when a compatible server is available. This does not add the file to automatic work. Configured diagnostic suppression still applies to diagnostic output.
|
|
256
|
+
An exact semantic request or exact-file health refresh can still route an excluded or external file when a compatible server is available. Explicit refresh can retry that file's failed route without applying automatic-discovery exclusions. Original route and document identities are preserved; display-path canonicalization does not merge alias routes. This does not add the file to automatic work. Configured diagnostic suppression still applies to diagnostic output.
|
|
237
257
|
|
|
238
258
|
Tsconfig and jsconfig filtering applies only to TypeScript and JavaScript-family files (`.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, and `.cjs`). Other-language diagnostics are not filtered by a nearby TypeScript or JavaScript project config. Automatic path exclusions and configured diagnostic suppression still apply. A single-file health result omits the `Tsconfig` coverage line for other languages.
|
|
239
259
|
|
|
@@ -272,6 +292,23 @@ Server settings use one snapshot for the active workspace runtime:
|
|
|
272
292
|
- SuPi validates the JSON object shape, not server-specific setting names or values.
|
|
273
293
|
- Saved changes apply after reload or restart. A saved value does not prove that the server accepted it.
|
|
274
294
|
|
|
295
|
+
TypeScript and JavaScript servers format code-action edits, such as organize imports, with their own defaults (4-space indentation) unless settings say otherwise. SuPi does not infer formatting from Biome, Prettier, or EditorConfig. Both the `typescript-language-server` and native TS 7 backends read `typescript.format` / `javascript.format`:
|
|
296
|
+
|
|
297
|
+
```json
|
|
298
|
+
{
|
|
299
|
+
"lsp": {
|
|
300
|
+
"servers": {
|
|
301
|
+
"typescript": {
|
|
302
|
+
"settings": {
|
|
303
|
+
"typescript": { "format": { "indentSize": 2, "tabSize": 2, "convertTabsToSpaces": true } },
|
|
304
|
+
"javascript": { "format": { "indentSize": 2, "tabSize": 2, "convertTabsToSpaces": true } }
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
275
312
|
File types do not include a leading dot. If `rootMarkers` is omitted, the server uses the session root.
|
|
276
313
|
|
|
277
314
|
Disable one language with `lsp.servers.<language>.enabled: false`. Use `/supi-ci-status` from `@mrclrchtr/supi-code-intelligence` to see detected, running, and missing servers.
|
|
@@ -14,6 +14,7 @@ export type {
|
|
|
14
14
|
StructuralProvider,
|
|
15
15
|
StructuralResult,
|
|
16
16
|
} from "./capability/types.ts";
|
|
17
|
+
export { childDeclarationLevel } from "./declaration-level.ts";
|
|
17
18
|
export type {
|
|
18
19
|
CFamilyExtension,
|
|
19
20
|
CFamilyFileAssociationResolver,
|
|
@@ -50,7 +51,7 @@ export type {
|
|
|
50
51
|
CodeResult,
|
|
51
52
|
CodeSymbol,
|
|
52
53
|
ConfidenceMode,
|
|
53
|
-
|
|
54
|
+
DeclarationLevel,
|
|
54
55
|
DisambiguationCandidate,
|
|
55
56
|
DocumentCodeSymbol,
|
|
56
57
|
DocumentEditPrecondition,
|
|
@@ -77,7 +77,9 @@ export interface SemanticProvider {
|
|
|
77
77
|
): Promise<CodeQueryResult<DocumentCodeSymbol[]>>;
|
|
78
78
|
/**
|
|
79
79
|
* Collect workspace symbols from routes that can contribute within the
|
|
80
|
-
* optional
|
|
80
|
+
* optional path scopes. Completeness is relative to those scopes, but data
|
|
81
|
+
* may include out-of-scope rows; consumers must filter rows themselves.
|
|
82
|
+
* Policy exclusions proven outside every scope do not make the query partial.
|
|
81
83
|
*/
|
|
82
84
|
workspaceSymbols(
|
|
83
85
|
query: string,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DeclarationLevel } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
/** Declarations whose body holds statements, so nested declarations are local to it. */
|
|
4
|
+
const FUNCTION_LIKE_KINDS = new Set(["function", "method", "constructor"]);
|
|
5
|
+
/** Bindings whose value may be a function (locals) or an object literal (members). */
|
|
6
|
+
const VALUE_BINDING_KINDS = new Set(["variable", "constant", "property", "field"]);
|
|
7
|
+
/** Kinds that name an entry of an object, type, or enum rather than a statement-level binding. */
|
|
8
|
+
const MEMBER_ENTRY_KINDS = new Set(["property", "field", "method", "enummember", "key", "event"]);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Level of a declaration reported directly inside `parent`.
|
|
12
|
+
*
|
|
13
|
+
* Inside a local declaration everything stays local. A function, method, or
|
|
14
|
+
* constructor makes its nested declarations local. A variable, constant,
|
|
15
|
+
* property, or field holds either a function body (nested bindings are local)
|
|
16
|
+
* or an object literal (nested properties and methods are members). Every
|
|
17
|
+
* other container — class, interface, enum, struct, namespace, module, or a
|
|
18
|
+
* document section — makes its nested declarations members.
|
|
19
|
+
*
|
|
20
|
+
* Kinds compare case-insensitively, so LSP SymbolKind names and Tree-sitter
|
|
21
|
+
* outline kinds share one rule.
|
|
22
|
+
*/
|
|
23
|
+
export function childDeclarationLevel(
|
|
24
|
+
parent: { readonly kind: string | null; readonly level: DeclarationLevel },
|
|
25
|
+
childKind: string | null,
|
|
26
|
+
): Exclude<DeclarationLevel, "top-level" | "unknown"> {
|
|
27
|
+
if (parent.level === "local") return "local";
|
|
28
|
+
const parentKind = parent.kind?.toLowerCase() ?? "";
|
|
29
|
+
if (FUNCTION_LIKE_KINDS.has(parentKind)) return "local";
|
|
30
|
+
if (VALUE_BINDING_KINDS.has(parentKind)) {
|
|
31
|
+
return MEMBER_ENTRY_KINDS.has(childKind?.toLowerCase() ?? "") ? "member" : "local";
|
|
32
|
+
}
|
|
33
|
+
return "member";
|
|
34
|
+
}
|
|
@@ -3,8 +3,17 @@ export interface CodeQueryPartialMetadata {
|
|
|
3
3
|
/** Automatic path-policy evidence removed from a collected result. */
|
|
4
4
|
readonly automaticPathExclusion?: {
|
|
5
5
|
readonly excludedCount: number;
|
|
6
|
+
/** Observed policy paths, not canonical navigation paths; retain aliases when presenting exclusions. */
|
|
6
7
|
readonly examples: readonly string[];
|
|
7
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* True when automatic path-policy exclusions are the only reason the result
|
|
11
|
+
* is partial: every contributing provider branch completed and no coverage
|
|
12
|
+
* gap was observed. Excluded rows matched paths the workspace policy treats
|
|
13
|
+
* as out of automatic scope, so the retained rows are the complete in-policy
|
|
14
|
+
* answer.
|
|
15
|
+
*/
|
|
16
|
+
readonly exclusionOnly?: boolean;
|
|
8
17
|
}
|
|
9
18
|
|
|
10
19
|
/**
|
|
@@ -34,8 +34,16 @@ export interface SymbolAnchor {
|
|
|
34
34
|
character: number;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/**
|
|
38
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Where a declaration sits within its file, as reported by a provider.
|
|
39
|
+
*
|
|
40
|
+
* - `top-level`: directly in the file, not inside another declaration.
|
|
41
|
+
* - `member`: directly inside a class, interface, enum, struct, namespace,
|
|
42
|
+
* module, or object literal (methods, fields, properties, enum members).
|
|
43
|
+
* - `local`: inside the body of a function, method, or constructor.
|
|
44
|
+
* - `unknown`: the provider reported a flat symbol list without hierarchy.
|
|
45
|
+
*/
|
|
46
|
+
export type DeclarationLevel = "top-level" | "member" | "local" | "unknown";
|
|
39
47
|
|
|
40
48
|
/**
|
|
41
49
|
* A discovered symbol / declaration.
|
|
@@ -54,7 +62,7 @@ export interface CodeSymbol {
|
|
|
54
62
|
file: string;
|
|
55
63
|
declarationAnchor: SymbolAnchor;
|
|
56
64
|
nameAnchor?: SymbolAnchor;
|
|
57
|
-
/** Named
|
|
65
|
+
/** Named enclosing declaration when reported; absence does not prove a top-level declaration. */
|
|
58
66
|
container?: string | null;
|
|
59
67
|
}
|
|
60
68
|
|
|
@@ -63,10 +71,10 @@ export interface CodeSymbol {
|
|
|
63
71
|
*
|
|
64
72
|
* `unknown` means the provider returned a flat observation without an actual
|
|
65
73
|
* hierarchy. A reported container name remains metadata and does not promote
|
|
66
|
-
* a flat observation to known
|
|
74
|
+
* a flat observation to a known level.
|
|
67
75
|
*/
|
|
68
76
|
export interface DocumentCodeSymbol extends CodeSymbol {
|
|
69
|
-
|
|
77
|
+
level: DeclarationLevel;
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
// ── Result types ───────────────────────────────────────────────────────
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Session-provided Web tools for SuPi child sessions (Agent Runs and Reviewer Sessions).
|
|
2
|
+
//
|
|
3
|
+
// The loaded supi-web extension answers a synchronous request on the parent event bus.
|
|
4
|
+
// Consumers never depend on supi-web; when it is absent or provides no tools, the
|
|
5
|
+
// request returns undefined and the child simply lacks Web tools.
|
|
6
|
+
|
|
7
|
+
import type { ExtensionAPI, ExtensionFactory } from "@earendil-works/pi-coding-agent";
|
|
8
|
+
|
|
9
|
+
/** Web tools that child sessions may receive when the containing session provides them. */
|
|
10
|
+
export const CHILD_WEB_TOOL_NAMES = [
|
|
11
|
+
"web_fetch_md",
|
|
12
|
+
"web_search",
|
|
13
|
+
"web_docs_search",
|
|
14
|
+
"web_docs_fetch",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
/** One session-provided Web tool name. */
|
|
18
|
+
export type ChildWebToolName = (typeof CHILD_WEB_TOOL_NAMES)[number];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Web tools currently callable in the containing session plus a child-only extension
|
|
22
|
+
* factory that registers them. The factory must not capture the parent session; it may
|
|
23
|
+
* capture plain data such as the containing session's configuration directory.
|
|
24
|
+
*/
|
|
25
|
+
export interface ChildWebToolProvision {
|
|
26
|
+
readonly toolNames: readonly ChildWebToolName[];
|
|
27
|
+
/** Register the requested subset of `toolNames` in a child session. */
|
|
28
|
+
readonly createFactory: (toolNames: readonly ChildWebToolName[]) => ExtensionFactory;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Plain containing-session data supplied with a child Web tool request. */
|
|
32
|
+
export interface ChildWebToolsRequestContext {
|
|
33
|
+
/**
|
|
34
|
+
* Working directory whose SuPi configuration governs Web settings such as Firecrawl.
|
|
35
|
+
* Children with a different cwd (for example a frozen Review Workspace) keep this policy.
|
|
36
|
+
*/
|
|
37
|
+
readonly configCwd: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type EventBusLike = Pick<ExtensionAPI["events"], "on" | "emit">;
|
|
41
|
+
|
|
42
|
+
const REQUEST_CHANNEL = "supi:child-web-tools:request";
|
|
43
|
+
const RESPONSE_CHANNEL = "supi:child-web-tools:response";
|
|
44
|
+
|
|
45
|
+
interface ChildWebToolsRequest extends ChildWebToolsRequestContext {
|
|
46
|
+
readonly requestId: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface ChildWebToolsResponse {
|
|
50
|
+
readonly requestId: string;
|
|
51
|
+
readonly provision: ChildWebToolProvision;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let nextRequestId = 0;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Answer child Web tool requests on the parent event bus. The callback must be synchronous
|
|
58
|
+
* because Pi's event bus does not await listeners. Returns an unsubscribe function.
|
|
59
|
+
*/
|
|
60
|
+
export function provideChildWebTools(
|
|
61
|
+
events: EventBusLike,
|
|
62
|
+
resolve: (context: ChildWebToolsRequestContext) => ChildWebToolProvision | undefined,
|
|
63
|
+
): () => void {
|
|
64
|
+
return events.on(REQUEST_CHANNEL, (data) => {
|
|
65
|
+
const request = data as Partial<ChildWebToolsRequest> | undefined;
|
|
66
|
+
if (typeof request?.requestId !== "string" || typeof request.configCwd !== "string") return;
|
|
67
|
+
const { requestId, configCwd } = request;
|
|
68
|
+
const provision = resolve({ configCwd });
|
|
69
|
+
if (!provision || provision.toolNames.length === 0) return;
|
|
70
|
+
events.emit(RESPONSE_CHANNEL, { requestId, provision } satisfies ChildWebToolsResponse);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Capture the Web tools the containing session currently provides, if any. */
|
|
75
|
+
export function requestChildWebTools(
|
|
76
|
+
events: EventBusLike,
|
|
77
|
+
context: ChildWebToolsRequestContext,
|
|
78
|
+
): ChildWebToolProvision | undefined {
|
|
79
|
+
const requestId = `child-web-${process.pid}-${++nextRequestId}`;
|
|
80
|
+
let provision: ChildWebToolProvision | undefined;
|
|
81
|
+
const unsubscribe = events.on(RESPONSE_CHANNEL, (data) => {
|
|
82
|
+
const response = data as Partial<ChildWebToolsResponse> | undefined;
|
|
83
|
+
if (response?.requestId === requestId && response.provision) provision = response.provision;
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
events.emit(REQUEST_CHANNEL, { requestId, ...context } satisfies ChildWebToolsRequest);
|
|
87
|
+
} finally {
|
|
88
|
+
unsubscribe();
|
|
89
|
+
}
|
|
90
|
+
return provision;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Return the Web tools callable in a session under Pi's exposure rules: active `direct`
|
|
95
|
+
* tools, plus registered `codemode` and `deferred` tools. `model-only` and `hidden`
|
|
96
|
+
* tools are not callable.
|
|
97
|
+
*/
|
|
98
|
+
export function callableChildWebToolNames(
|
|
99
|
+
pi: Pick<ExtensionAPI, "getAllTools" | "getActiveTools">,
|
|
100
|
+
): ChildWebToolName[] {
|
|
101
|
+
const active = new Set(pi.getActiveTools());
|
|
102
|
+
const exposures = new Map(
|
|
103
|
+
pi.getAllTools().map((tool) => [tool.name, tool.exposure ?? "direct"] as const),
|
|
104
|
+
);
|
|
105
|
+
return CHILD_WEB_TOOL_NAMES.filter((name) => {
|
|
106
|
+
const exposure = exposures.get(name);
|
|
107
|
+
if (exposure === "direct") return active.has(name);
|
|
108
|
+
return exposure === "codemode" || exposure === "deferred";
|
|
109
|
+
});
|
|
110
|
+
}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
// supi-core session domain — session utilities and registries.
|
|
2
2
|
|
|
3
|
+
export type {
|
|
4
|
+
ChildWebToolName,
|
|
5
|
+
ChildWebToolProvision,
|
|
6
|
+
ChildWebToolsRequestContext,
|
|
7
|
+
} from "./child-web-tools.ts";
|
|
8
|
+
export {
|
|
9
|
+
CHILD_WEB_TOOL_NAMES,
|
|
10
|
+
callableChildWebToolNames,
|
|
11
|
+
provideChildWebTools,
|
|
12
|
+
requestChildWebTools,
|
|
13
|
+
} from "./child-web-tools.ts";
|
|
3
14
|
export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
|
|
4
15
|
export type {
|
|
5
16
|
SessionCapabilitySkill,
|
|
@@ -119,7 +119,7 @@ class Range {
|
|
|
119
119
|
debug('hyphen replace', range)
|
|
120
120
|
|
|
121
121
|
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
|
|
122
|
-
range = range
|
|
122
|
+
range = trimComparatorWhitespace(range)
|
|
123
123
|
debug('comparator trim', range)
|
|
124
124
|
|
|
125
125
|
// `~ 1.2.3` => `~1.2.3`
|
|
@@ -224,11 +224,11 @@ const parseOptions = require('../internal/parse-options')
|
|
|
224
224
|
const Comparator = require('./comparator')
|
|
225
225
|
const debug = require('../internal/debug')
|
|
226
226
|
const SemVer = require('./semver')
|
|
227
|
+
const trimComparatorWhitespace = require('../internal/trim-comparator-whitespace')
|
|
227
228
|
const {
|
|
228
229
|
safeRe: re,
|
|
229
230
|
src,
|
|
230
231
|
t,
|
|
231
|
-
comparatorTrimReplace,
|
|
232
232
|
tildeTrimReplace,
|
|
233
233
|
caretTrimReplace,
|
|
234
234
|
} = require('../internal/re')
|
|
@@ -15,6 +15,35 @@ const MAX_SAFE_COMPONENT_LENGTH = 16
|
|
|
15
15
|
// the shortest version with a build 0.0.0+BUILD.
|
|
16
16
|
const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
|
|
17
17
|
|
|
18
|
+
// ECMAScript whitespace and line terminators matched by \s.
|
|
19
|
+
const WHITESPACE_CHARACTERS = [
|
|
20
|
+
'\t',
|
|
21
|
+
'\n',
|
|
22
|
+
'\v',
|
|
23
|
+
'\f',
|
|
24
|
+
'\r',
|
|
25
|
+
' ',
|
|
26
|
+
'\u00a0',
|
|
27
|
+
'\u1680',
|
|
28
|
+
'\u2000',
|
|
29
|
+
'\u2001',
|
|
30
|
+
'\u2002',
|
|
31
|
+
'\u2003',
|
|
32
|
+
'\u2004',
|
|
33
|
+
'\u2005',
|
|
34
|
+
'\u2006',
|
|
35
|
+
'\u2007',
|
|
36
|
+
'\u2008',
|
|
37
|
+
'\u2009',
|
|
38
|
+
'\u200a',
|
|
39
|
+
'\u2028',
|
|
40
|
+
'\u2029',
|
|
41
|
+
'\u202f',
|
|
42
|
+
'\u205f',
|
|
43
|
+
'\u3000',
|
|
44
|
+
'\ufeff',
|
|
45
|
+
]
|
|
46
|
+
|
|
18
47
|
const RELEASE_TYPES = [
|
|
19
48
|
'major',
|
|
20
49
|
'premajor',
|
|
@@ -30,6 +59,7 @@ module.exports = {
|
|
|
30
59
|
MAX_SAFE_COMPONENT_LENGTH,
|
|
31
60
|
MAX_SAFE_BUILD_LENGTH,
|
|
32
61
|
MAX_SAFE_INTEGER,
|
|
62
|
+
WHITESPACE_CHARACTERS,
|
|
33
63
|
RELEASE_TYPES,
|
|
34
64
|
SEMVER_SPEC_VERSION,
|
|
35
65
|
FLAG_INCLUDE_PRERELEASE: 0b001,
|