@kaoruisaac/pedelec 0.3.5 → 0.4.1
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 +59 -16
- package/dist/index.d.ts +53 -15
- package/dist/index.js +260 -58
- package/dist/index.type-test.js +59 -17
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/dist/vite.js +12 -2
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ Pedelec is a browser SDK and local bridge for applications that want to work wit
|
|
|
24
24
|
A web application can use Pedelec to:
|
|
25
25
|
|
|
26
26
|
- create an agent session on the user's machine;
|
|
27
|
-
-
|
|
27
|
+
- open an application-managed workspace with the native directory picker or an explicit path;
|
|
28
28
|
- send user instructions and receive streamed assistant text;
|
|
29
29
|
- expose narrowly scoped browser-side tools to the agent;
|
|
30
30
|
- resume or end sessions; and
|
|
@@ -223,7 +223,7 @@ const path = await session.uploadAsset(file);
|
|
|
223
223
|
const assets = await session.listAssets();
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
The physical shared App/Agent directory is `.pedelec-runtime/assets/`. In the SDK contract, `assets/` is its implicit root: `listAssets()` recursively lists regular files at every level as a flat array, ordered by filesystem modification time (newest first) and then by name; nested paths such as `/results/report.json` are returned in full. Directory entries and symlinks are excluded, as are `.pedelec-*` entries at every level; other dotfiles are included. It may run while the agent runs. One session can only upload one file at a time, but uploads can run alongside prepare or agent execution.
|
|
226
|
+
Assets are stored in the Workspace shared by the Session. The physical shared App/Agent directory is `.pedelec-runtime/assets/`. In the SDK contract, `assets/` is its implicit root: `listAssets()` recursively lists regular files at every level as a flat array, ordered by filesystem modification time (newest first) and then by name; nested paths such as `/results/report.json` are returned in full. Directory entries and symlinks are excluded, as are `.pedelec-*` entries at every level; other dotfiles are included. It may run while the agent runs. One session can only upload one file at a time, but uploads can run alongside prepare or agent execution. When several sessions share a Workspace, they share this `.pedelec-runtime/assets/` area.
|
|
227
227
|
|
|
228
228
|
```ts
|
|
229
229
|
const text = await session.readAsset("/report.txt", "text");
|
|
@@ -235,37 +235,80 @@ Public asset paths use `/...` with `assets/` as their implicit root; nested path
|
|
|
235
235
|
|
|
236
236
|
## Workspace
|
|
237
237
|
|
|
238
|
-
Workspace is the filesystem root in which the Agent works. Pedelec stores its private runtime data under `<workspace>/.pedelec-runtime/`.
|
|
238
|
+
Workspace is the filesystem root in which the Agent works. Pedelec stores its private runtime data under `<workspace>/.pedelec-runtime/`. There are two supported flows:
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
### Explicit Workspace
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
const workspace = await pedelec.openWorkspace();
|
|
244
|
+
if (!workspace) return;
|
|
245
|
+
|
|
246
|
+
const session = await workspace.createSession({
|
|
247
|
+
model: "gpt-5.6-sol",
|
|
248
|
+
effort: "high",
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
`openWorkspace()` without a path opens the native folder picker. User cancellation resolves to `null`; no Workspace or Session is created. The returned `PedelecWorkspace` handle is the object to retain and reuse. An explicit path bypasses the picker:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
const workspace = await pedelec.openWorkspace("C:\\workspace\\project-a");
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Pedelec initializes `.pedelec-runtime` for Workspace-owned runtime data without clearing project files or existing private data. Assets, logs, and temporary data are Workspace-level, while generated Session skills and Deno Module state are isolated per Thread. Applications should not depend on the private `.pedelec-runtime` subdirectory layout. Explicit Workspaces are application-managed, are never deleted by Pedelec, and can own multiple Sessions. An explicit path must not overlap Pedelec's managed workspace root.
|
|
259
|
+
|
|
260
|
+
### Managed convenience
|
|
241
261
|
|
|
242
262
|
```ts
|
|
243
263
|
const session = await pedelec.createSession({
|
|
244
|
-
|
|
245
|
-
workspace: { path: "C:\\workspace\\project-a" },
|
|
264
|
+
effortLevel: "high",
|
|
246
265
|
});
|
|
266
|
+
console.log(session.workspace.path); // null for managed Workspace
|
|
247
267
|
```
|
|
248
268
|
|
|
249
|
-
Pedelec creates
|
|
269
|
+
`Pedelec.createSession()` creates a temporary Desktop-managed Workspace automatically. It no longer accepts Workspace configuration; use `openWorkspace()` followed by `workspace.createSession()` for an explicit Workspace. Every created or resumed Session exposes its Workspace as `session.workspace`.
|
|
270
|
+
|
|
271
|
+
### Workspace methods
|
|
250
272
|
|
|
251
|
-
|
|
273
|
+
`workspace.listFiles(path?)` and `workspace.listFolders(path?)` recursively return Workspace-relative paths. An omitted path lists from the Workspace root; `/` is the separator and results are lexicographically sorted. The `.pedelec-runtime` directory is included. Symlinks and junctions are neither followed nor returned. Results are not silently truncated: an oversized response fails with `WORKSPACE_LIST_TOO_LARGE`, so retry with a narrower path.
|
|
252
274
|
|
|
253
|
-
|
|
275
|
+
`workspace.run(script, options?)` executes application-supplied Deno code in the Workspace:
|
|
254
276
|
|
|
255
277
|
```ts
|
|
256
|
-
const
|
|
257
|
-
|
|
278
|
+
const result = await workspace.run(
|
|
279
|
+
"console.log(JSON.stringify({ ok: true }))",
|
|
280
|
+
{ timeoutMs: 60_000 },
|
|
281
|
+
);
|
|
258
282
|
|
|
259
|
-
if (
|
|
260
|
-
|
|
283
|
+
if (result.stdoutTruncated) {
|
|
284
|
+
throw new Error("Workspace script output was truncated");
|
|
261
285
|
}
|
|
286
|
+
const value = JSON.parse(result.stdout);
|
|
287
|
+
```
|
|
262
288
|
|
|
263
|
-
|
|
264
|
-
|
|
289
|
+
The default timeout is 60 seconds; `timeoutMs` must be a positive integer. The return value is the raw `DenoRunOutput` shape: `exitCode`, `stdout`, `stderr`, `stdoutTruncated`, and `stderrTruncated`. A non-zero `exitCode` still resolves normally. The application parses `stdout` when it wants a function-like return value. Check truncation flags before relying on output; an oversized browser response fails with `WORKSPACE_RUN_OUTPUT_TOO_LARGE`.
|
|
290
|
+
|
|
291
|
+
`workspace.run()` has read and write access to the Workspace, but no network, host environment, subprocess execution, FFI, or `sys` permission. The existing no-remote, cached-only, and no-npm runtime restrictions still apply. This is a direct deterministic capability for an approved Web application to read and modify the opened Workspace; it is not unrestricted host code execution and is not mediated by an Agent.
|
|
292
|
+
|
|
293
|
+
Several `workspace.run()` calls in one Workspace may execute concurrently. A Workspace run cannot start while any Session in that Workspace has an active Agent/provider operation. Conversely, a Session in that Workspace cannot begin `sendText()` or provider preparation while one or more Workspace runs are active. This rule is Workspace-wide, including operations started through another `PedelecSession` handle; conflicts reject with `WORKSPACE_BUSY`.
|
|
294
|
+
|
|
295
|
+
Session `skills.denoModules` belong to the Session/Thread that declares them and are available only to Agent-side `pedelec-deno` execution for that Thread. Workspace runs accept the same Vite-prepared declarations explicitly:
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
const sceneTools = defineDenoModule({
|
|
299
|
+
name: "scene-tools",
|
|
300
|
+
entry: "./deno/scene-tools.ts",
|
|
265
301
|
});
|
|
302
|
+
|
|
303
|
+
const result = await workspace.run(
|
|
304
|
+
`import { buildScene } from "scene-tools";\nawait buildScene();`,
|
|
305
|
+
{ denoModules: [sceneTools] },
|
|
306
|
+
);
|
|
266
307
|
```
|
|
267
308
|
|
|
268
|
-
`
|
|
309
|
+
`workspace.run()` does not inherit any Session Deno Modules, and Sessions do not inherit modules used by Workspace runs. The Vite-prepared package is cached privately per Workspace and authorized SDK origin; each run receives an import map containing exactly its requested names. A ready module name is immutable for the lifetime of the Core process, and Workspace-run modules never appear in Agent Host Context. The private Workspace-run cache is reset when Core restarts; project files, assets, and Thread module snapshots are preserved.
|
|
310
|
+
|
|
311
|
+
The Workspace handle outlives an individual Session conceptually. Ending a Session does not close or delete a custom Workspace, and Pedelec does not delete it during app cleanup. Managed Workspaces remain under Desktop cleanup ownership and are not promised to disappear immediately when a Thread ends. The browser capability is runtime-scoped; reopen the custom Workspace after a Desktop/Core restart when necessary. `pedelec.resumeSession(sessionId)` returns a Session that already includes `session.workspace`; the application does not need to call `openWorkspace()` separately. Same-handle `session.resume()` preserves the existing Workspace handle, subject to the existing Core/thread and transport caveats.
|
|
269
312
|
|
|
270
313
|
---
|
|
271
314
|
|
package/dist/index.d.ts
CHANGED
|
@@ -18,11 +18,15 @@ export type Asset = {
|
|
|
18
18
|
};
|
|
19
19
|
export type DenoModuleDefinition<TName extends string = string> = {
|
|
20
20
|
name: TName;
|
|
21
|
-
description
|
|
21
|
+
description?: string;
|
|
22
22
|
entry: string;
|
|
23
|
-
usage
|
|
23
|
+
usage?: string;
|
|
24
24
|
preferStdinExecution?: boolean;
|
|
25
25
|
};
|
|
26
|
+
type AgentDenoModuleDefinition<TName extends string = string> = DenoModuleDefinition<TName> & {
|
|
27
|
+
description: string;
|
|
28
|
+
usage: string;
|
|
29
|
+
};
|
|
26
30
|
export type ToolArgsSchemaMeta<TDefault extends JsonValue = JsonValue> = {
|
|
27
31
|
description?: string;
|
|
28
32
|
default?: TDefault;
|
|
@@ -79,7 +83,7 @@ export type ToolDefinition<TArgs = unknown, TResult = unknown, TName extends str
|
|
|
79
83
|
export type SkillsInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
80
84
|
guidance: string;
|
|
81
85
|
tools: TTools;
|
|
82
|
-
denoModules?: readonly
|
|
86
|
+
denoModules?: readonly AgentDenoModuleDefinition[];
|
|
83
87
|
};
|
|
84
88
|
export type ToolNameOf<TTools extends readonly ToolDefinition[]> = Extract<TTools[number]["name"], string>;
|
|
85
89
|
export type SerializableToolManifest = {
|
|
@@ -99,17 +103,19 @@ export type SerializableSkillsManifest = {
|
|
|
99
103
|
tools: SerializableToolManifest[];
|
|
100
104
|
denoModules?: SerializableDenoModuleManifest[];
|
|
101
105
|
};
|
|
102
|
-
export type
|
|
103
|
-
|
|
106
|
+
export type WorkspaceRunOptions = {
|
|
107
|
+
timeoutMs?: number;
|
|
108
|
+
denoModules?: readonly DenoModuleDefinition[];
|
|
109
|
+
};
|
|
110
|
+
export type WorkspaceRunResult = {
|
|
111
|
+
exitCode: number;
|
|
112
|
+
stdout: string;
|
|
113
|
+
stderr: string;
|
|
114
|
+
stdoutTruncated: boolean;
|
|
115
|
+
stderrTruncated: boolean;
|
|
104
116
|
};
|
|
105
|
-
export interface WorkspaceFolderPickerResult {
|
|
106
|
-
path: string;
|
|
107
|
-
isEmptyFolder: boolean;
|
|
108
|
-
hasWorkspaceConfig: boolean;
|
|
109
|
-
}
|
|
110
117
|
type CreateSessionSharedInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
111
118
|
skills?: SkillsInput<TTools>;
|
|
112
|
-
workspace?: CreateSessionWorkspaceInput;
|
|
113
119
|
autoEndOnDisconnect?: boolean;
|
|
114
120
|
};
|
|
115
121
|
type ProfileSessionConfiguration = {
|
|
@@ -308,7 +314,29 @@ type ErrorHandler = (error: PedelecError, ctx: ErrorEventContext) => void;
|
|
|
308
314
|
type StatusHandler = (status: PedelecSessionStatus, ctx: StatusEventContext) => void;
|
|
309
315
|
type EndedHandler = (ctx: EndedEventContext) => void;
|
|
310
316
|
export declare function defineTool<TArgs = unknown, TResult = unknown, const TName extends string = string>(tool: ToolDefinition<TArgs, TResult, TName>): ToolDefinition<TArgs, TResult, TName>;
|
|
311
|
-
export declare function defineDenoModule<const
|
|
317
|
+
export declare function defineDenoModule<const TModule extends DenoModuleDefinition>(module: TModule): TModule;
|
|
318
|
+
type PreparedWorkspaceDenoModule = {
|
|
319
|
+
name: string;
|
|
320
|
+
runtimeSource: string;
|
|
321
|
+
typesSource: string;
|
|
322
|
+
};
|
|
323
|
+
export declare class PedelecWorkspace {
|
|
324
|
+
private readonly client;
|
|
325
|
+
private workspaceId;
|
|
326
|
+
private currentPath;
|
|
327
|
+
constructor(client: Pedelec, workspaceId: string, path: string | null);
|
|
328
|
+
get path(): string | null;
|
|
329
|
+
createSession(): Promise<PedelecSession<string>>;
|
|
330
|
+
createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
331
|
+
listFiles(path?: string): Promise<string[]>;
|
|
332
|
+
listFolders(path?: string): Promise<string[]>;
|
|
333
|
+
run(script: string, options?: WorkspaceRunOptions): Promise<WorkspaceRunResult>;
|
|
334
|
+
/** @internal */
|
|
335
|
+
get workspaceIdInternal(): string;
|
|
336
|
+
/** @internal */
|
|
337
|
+
hydrate(workspaceId: string, path: string | null): void;
|
|
338
|
+
private list;
|
|
339
|
+
}
|
|
312
340
|
export declare class Pedelec {
|
|
313
341
|
private readonly pageWindow;
|
|
314
342
|
private readonly channelId;
|
|
@@ -323,16 +351,25 @@ export declare class Pedelec {
|
|
|
323
351
|
constructor(options?: PedelecOptions);
|
|
324
352
|
createSession(): Promise<PedelecSession<string>>;
|
|
325
353
|
createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
354
|
+
/** @internal */
|
|
355
|
+
createSessionInternal<const TTools extends readonly ToolDefinition[]>(input?: CreateSessionInput<TTools>, workspace?: PedelecWorkspace): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
326
356
|
private uploadDenoModuleArtifact;
|
|
357
|
+
/** @internal */
|
|
358
|
+
uploadWorkspaceDenoModuleArtifact(workspaceId: string, module: PreparedWorkspaceDenoModule): Promise<void>;
|
|
359
|
+
private uploadDenoModuleArtifactWithTicket;
|
|
327
360
|
private abortSessionSetup;
|
|
328
361
|
private completeSessionSetup;
|
|
329
|
-
|
|
362
|
+
openWorkspace(): Promise<PedelecWorkspace | null>;
|
|
363
|
+
openWorkspace(path: string): Promise<PedelecWorkspace>;
|
|
330
364
|
listProviders(): Promise<ProviderInfo[]>;
|
|
331
365
|
getSettings(): Promise<PedelecSettings>;
|
|
332
366
|
getApprovalStatus(): Promise<ApprovalStatus>;
|
|
333
367
|
resumeSession(sessionId: string): Promise<PedelecSession<string>>;
|
|
334
368
|
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
335
|
-
|
|
369
|
+
/** @internal */
|
|
370
|
+
requestWithOptions<T>(type: string, payload?: Record<string, unknown>, options?: {
|
|
371
|
+
timeoutMs?: number | null;
|
|
372
|
+
}): Promise<T>;
|
|
336
373
|
unregisterSession(sessionId: string): void;
|
|
337
374
|
/** @internal */
|
|
338
375
|
registerExistingSession(session: PedelecSession<any>): void;
|
|
@@ -363,6 +400,7 @@ export declare class PedelecSession<TToolName extends string = string> {
|
|
|
363
400
|
readonly sessionId: string;
|
|
364
401
|
readonly provider: string;
|
|
365
402
|
readonly effortLevel?: EffortLevel;
|
|
403
|
+
readonly workspace: PedelecWorkspace;
|
|
366
404
|
readonly sessionCreatedAt: number;
|
|
367
405
|
readonly usage: PedelecSessionUsage;
|
|
368
406
|
private status;
|
|
@@ -386,7 +424,7 @@ export declare class PedelecSession<TToolName extends string = string> {
|
|
|
386
424
|
private readonly recoveredToolCalls;
|
|
387
425
|
private readonly handledToolRequestIds;
|
|
388
426
|
private authoritativePendingToolRequest;
|
|
389
|
-
constructor(client: Pedelec, sessionId: string, provider: string, effortLevel?: EffortLevel, inlineToolHandlers?: Map<string, ToolSpecificHandler>, autoEndOnDisconnect?: boolean);
|
|
427
|
+
constructor(client: Pedelec, sessionId: string, provider: string, workspace: PedelecWorkspace, effortLevel?: EffortLevel, inlineToolHandlers?: Map<string, ToolSpecificHandler>, autoEndOnDisconnect?: boolean);
|
|
390
428
|
prepare(): Promise<void>;
|
|
391
429
|
sendText(text: string): Promise<void>;
|
|
392
430
|
onChat(handler: ChatHandler): () => void;
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,46 @@ export function defineTool(tool) {
|
|
|
39
39
|
export function defineDenoModule(module) {
|
|
40
40
|
return module;
|
|
41
41
|
}
|
|
42
|
+
function normalizeWorkspaceDenoModules(value) {
|
|
43
|
+
if (value === undefined)
|
|
44
|
+
return [];
|
|
45
|
+
if (!Array.isArray(value)) {
|
|
46
|
+
throw makeError("INVALID_INPUT", "options.denoModules must be an array");
|
|
47
|
+
}
|
|
48
|
+
const seen = new Set();
|
|
49
|
+
return value.map((module, index) => {
|
|
50
|
+
if (!module || typeof module !== "object" || Array.isArray(module)) {
|
|
51
|
+
throw makeError("INVALID_INPUT", "options.denoModules entries must be objects", { index });
|
|
52
|
+
}
|
|
53
|
+
const rawModule = module;
|
|
54
|
+
if (!isValidDenoModuleName(rawModule.name)) {
|
|
55
|
+
throw makeError("INVALID_INPUT", "Deno Module name is invalid", {
|
|
56
|
+
index,
|
|
57
|
+
moduleName: rawModule.name,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
if (seen.has(rawModule.name)) {
|
|
61
|
+
throw makeError("INVALID_INPUT", "duplicate Deno Module name", {
|
|
62
|
+
moduleName: rawModule.name,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
seen.add(rawModule.name);
|
|
66
|
+
if (rawModule.preferStdinExecution !== undefined && typeof rawModule.preferStdinExecution !== "boolean") {
|
|
67
|
+
throw makeError("INVALID_INPUT", "Deno Module preferStdinExecution must be a boolean", {
|
|
68
|
+
moduleName: rawModule.name,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const artifact = getPreparedDenoModuleArtifact(module);
|
|
72
|
+
if (!artifact) {
|
|
73
|
+
throw makeError("INVALID_INPUT", `Deno module "${rawModule.name}" was not prepared. Configure pedelecVitePlugin() in the Vite project.`, { moduleName: rawModule.name });
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
name: rawModule.name,
|
|
77
|
+
runtimeSource: artifact.runtimeSource,
|
|
78
|
+
typesSource: artifact.typesSource,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
42
82
|
function normalizeSkillsInput(value) {
|
|
43
83
|
const handlers = new Map();
|
|
44
84
|
if (value === undefined)
|
|
@@ -190,6 +230,92 @@ function normalizeToolArgsSchema(argsSchema, toolName) {
|
|
|
190
230
|
});
|
|
191
231
|
}
|
|
192
232
|
}
|
|
233
|
+
export class PedelecWorkspace {
|
|
234
|
+
client;
|
|
235
|
+
workspaceId;
|
|
236
|
+
currentPath;
|
|
237
|
+
constructor(client, workspaceId, path) {
|
|
238
|
+
this.client = client;
|
|
239
|
+
this.workspaceId = workspaceId;
|
|
240
|
+
this.currentPath = path;
|
|
241
|
+
}
|
|
242
|
+
get path() {
|
|
243
|
+
return this.currentPath;
|
|
244
|
+
}
|
|
245
|
+
async createSession(input = {}) {
|
|
246
|
+
return this.client.createSessionInternal(input, this);
|
|
247
|
+
}
|
|
248
|
+
listFiles(path) {
|
|
249
|
+
return this.list("workspace_list_files", path);
|
|
250
|
+
}
|
|
251
|
+
listFolders(path) {
|
|
252
|
+
return this.list("workspace_list_folders", path);
|
|
253
|
+
}
|
|
254
|
+
run(script, options = {}) {
|
|
255
|
+
if (typeof script !== "string") {
|
|
256
|
+
return Promise.reject(makeError("INVALID_INPUT", "script must be a string"));
|
|
257
|
+
}
|
|
258
|
+
if (!isPlainObject(options)) {
|
|
259
|
+
return Promise.reject(makeError("INVALID_INPUT", "options must be an object"));
|
|
260
|
+
}
|
|
261
|
+
const timeoutMs = options.timeoutMs;
|
|
262
|
+
if (timeoutMs !== undefined && (!Number.isInteger(timeoutMs) || timeoutMs <= 0)) {
|
|
263
|
+
return Promise.reject(makeError("INVALID_INPUT", "timeoutMs must be a positive integer"));
|
|
264
|
+
}
|
|
265
|
+
let modules;
|
|
266
|
+
try {
|
|
267
|
+
modules = normalizeWorkspaceDenoModules(options.denoModules);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
return Promise.reject(error);
|
|
271
|
+
}
|
|
272
|
+
const run = async () => {
|
|
273
|
+
if (modules.length > 0) {
|
|
274
|
+
const prepared = await this.client.request("prepare_workspace_deno_modules", {
|
|
275
|
+
workspaceId: this.workspaceId,
|
|
276
|
+
moduleNames: modules.map(({ name }) => name),
|
|
277
|
+
});
|
|
278
|
+
const missingModuleNames = normalizeWorkspaceDenoModulePrepareResponse(prepared, modules.map(({ name }) => name));
|
|
279
|
+
const modulesByName = new Map(modules.map((module) => [module.name, module]));
|
|
280
|
+
for (const name of missingModuleNames) {
|
|
281
|
+
const module = modulesByName.get(name);
|
|
282
|
+
if (!module) {
|
|
283
|
+
throw makeError("SDK_PROTOCOL_ERROR", "prepare_workspace_deno_modules response was inconsistent");
|
|
284
|
+
}
|
|
285
|
+
await this.client.uploadWorkspaceDenoModuleArtifact(this.workspaceId, module);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return this.client.requestWithOptions("workspace_run", {
|
|
289
|
+
workspaceId: this.workspaceId,
|
|
290
|
+
script,
|
|
291
|
+
...(timeoutMs === undefined ? {} : { timeoutMs }),
|
|
292
|
+
...(modules.length === 0 ? {} : { denoModules: modules.map(({ name }) => name) }),
|
|
293
|
+
}, { timeoutMs: null }).then(normalizeWorkspaceRunResponse);
|
|
294
|
+
};
|
|
295
|
+
return run();
|
|
296
|
+
}
|
|
297
|
+
/** @internal */
|
|
298
|
+
get workspaceIdInternal() {
|
|
299
|
+
return this.workspaceId;
|
|
300
|
+
}
|
|
301
|
+
/** @internal */
|
|
302
|
+
hydrate(workspaceId, path) {
|
|
303
|
+
this.workspaceId = workspaceId;
|
|
304
|
+
this.currentPath = path;
|
|
305
|
+
}
|
|
306
|
+
list(type, path) {
|
|
307
|
+
if (path !== undefined && typeof path !== "string") {
|
|
308
|
+
return Promise.reject(makeError("INVALID_INPUT", "path must be a string when provided"));
|
|
309
|
+
}
|
|
310
|
+
if (path !== undefined && path.trim().length === 0) {
|
|
311
|
+
return Promise.reject(makeError("INVALID_INPUT", "path must not be empty when provided"));
|
|
312
|
+
}
|
|
313
|
+
return this.client.request(type, {
|
|
314
|
+
workspaceId: this.workspaceId,
|
|
315
|
+
...(path === undefined ? {} : { path }),
|
|
316
|
+
}).then(normalizeWorkspaceListResponse);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
193
319
|
export class Pedelec {
|
|
194
320
|
pageWindow;
|
|
195
321
|
channelId;
|
|
@@ -212,6 +338,10 @@ export class Pedelec {
|
|
|
212
338
|
this.connectExtension();
|
|
213
339
|
}
|
|
214
340
|
async createSession(input = {}) {
|
|
341
|
+
return this.createSessionInternal(input);
|
|
342
|
+
}
|
|
343
|
+
/** @internal */
|
|
344
|
+
async createSessionInternal(input = {}, workspace) {
|
|
215
345
|
const resolvedOrPromise = this.resolveCreateSessionInput(input);
|
|
216
346
|
const resolvedInput = resolvedOrPromise instanceof Promise ? await resolvedOrPromise : resolvedOrPromise;
|
|
217
347
|
const result = await this.request("create_session", {
|
|
@@ -221,36 +351,48 @@ export class Pedelec {
|
|
|
221
351
|
effort: resolvedInput.effort,
|
|
222
352
|
effortLevel: resolvedInput.effortLevel,
|
|
223
353
|
skills: resolvedInput.skills,
|
|
224
|
-
workspace:
|
|
354
|
+
...(workspace ? { workspaceId: workspace.workspaceIdInternal } : {}),
|
|
225
355
|
autoEndOnDisconnect: resolvedInput.autoEndOnDisconnect,
|
|
226
356
|
},
|
|
227
357
|
});
|
|
228
|
-
|
|
229
|
-
|
|
358
|
+
const normalizedResult = normalizeCreateSessionResponse(result);
|
|
359
|
+
if (workspace && normalizedResult.workspace.workspaceId !== workspace.workspaceIdInternal) {
|
|
360
|
+
throw makeError("SDK_PROTOCOL_ERROR", "create_session response returned a different Workspace", {
|
|
361
|
+
expectedWorkspaceId: workspace.workspaceIdInternal,
|
|
362
|
+
actualWorkspaceId: normalizedResult.workspace.workspaceId,
|
|
363
|
+
});
|
|
230
364
|
}
|
|
231
|
-
if (resolvedInput.model !== undefined &&
|
|
365
|
+
if (resolvedInput.model !== undefined && normalizedResult.explicitModelConfigApplied !== true) {
|
|
232
366
|
const error = makeError("SDK_PROTOCOL_ERROR", "The connected Pedelec Extension/Desktop does not support explicit model configuration. Update Pedelec components and try again.", { feature: "explicitModelConfig" });
|
|
233
|
-
await this.abortSessionSetup(
|
|
367
|
+
await this.abortSessionSetup(normalizedResult.sessionId);
|
|
234
368
|
throw error;
|
|
235
369
|
}
|
|
236
370
|
try {
|
|
237
371
|
for (const module of resolvedInput.denoModules) {
|
|
238
|
-
await this.uploadDenoModuleArtifact(
|
|
372
|
+
await this.uploadDenoModuleArtifact(normalizedResult.sessionId, module);
|
|
239
373
|
}
|
|
240
374
|
if (resolvedInput.denoModules.length > 0) {
|
|
241
|
-
await this.completeSessionSetup(
|
|
375
|
+
await this.completeSessionSetup(normalizedResult.sessionId);
|
|
242
376
|
}
|
|
243
377
|
}
|
|
244
378
|
catch (error) {
|
|
245
379
|
// The thread exists at this point but is not exposed through the SDK
|
|
246
380
|
// session registry yet. Keep the setup failure as the caller-visible
|
|
247
381
|
// error even if the best-effort cleanup transport also fails.
|
|
248
|
-
await this.abortSessionSetup(
|
|
382
|
+
await this.abortSessionSetup(normalizedResult.sessionId);
|
|
249
383
|
throw normalizeError(error, "DENO_MODULE_UPLOAD_FAILED", "Deno Module setup failed");
|
|
250
384
|
}
|
|
251
|
-
|
|
385
|
+
const sessionWorkspace = workspace ?? new PedelecWorkspace(this, normalizedResult.workspace.workspaceId, normalizedResult.workspace.path);
|
|
386
|
+
return this.registerSession(normalizedResult.sessionId, resolvedInput.provider, resolvedInput.effortLevel, resolvedInput.inlineToolHandlers, resolvedInput.autoEndOnDisconnect, sessionWorkspace);
|
|
252
387
|
}
|
|
253
388
|
async uploadDenoModuleArtifact(sessionId, module) {
|
|
389
|
+
await this.uploadDenoModuleArtifactWithTicket(module, "create_deno_module_upload", { sessionId, moduleName: module.name });
|
|
390
|
+
}
|
|
391
|
+
/** @internal */
|
|
392
|
+
async uploadWorkspaceDenoModuleArtifact(workspaceId, module) {
|
|
393
|
+
await this.uploadDenoModuleArtifactWithTicket(module, "create_workspace_deno_module_upload", { workspaceId, moduleName: module.name });
|
|
394
|
+
}
|
|
395
|
+
async uploadDenoModuleArtifactWithTicket(module, ticketType, ticketPayload) {
|
|
254
396
|
const envelope = JSON.stringify({
|
|
255
397
|
version: 1,
|
|
256
398
|
format: "esm",
|
|
@@ -262,9 +404,8 @@ export class Pedelec {
|
|
|
262
404
|
if (expectedSizeBytes <= 0 || expectedSizeBytes > MAX_ASSET_UPLOAD_BYTES) {
|
|
263
405
|
throw makeError("DENO_MODULE_ARTIFACT_TOO_LARGE", `Deno Module "${module.name}" artifact exceeds the 100 MiB limit`, { moduleName: module.name, expectedSizeBytes, maxSizeBytes: MAX_ASSET_UPLOAD_BYTES });
|
|
264
406
|
}
|
|
265
|
-
const ticket = await this.request(
|
|
266
|
-
|
|
267
|
-
moduleName: module.name,
|
|
407
|
+
const ticket = await this.request(ticketType, {
|
|
408
|
+
...ticketPayload,
|
|
268
409
|
expectedSizeBytes,
|
|
269
410
|
});
|
|
270
411
|
if (!isPlainObject(ticket) || typeof ticket.uploadUrl !== "string" || ticket.uploadUrl.length === 0 ||
|
|
@@ -316,9 +457,13 @@ export class Pedelec {
|
|
|
316
457
|
async completeSessionSetup(sessionId) {
|
|
317
458
|
await this.request("complete_session_setup", { sessionId });
|
|
318
459
|
}
|
|
319
|
-
async
|
|
320
|
-
|
|
321
|
-
|
|
460
|
+
async openWorkspace(path) {
|
|
461
|
+
if (path !== undefined && (typeof path !== "string" || path.trim().length === 0)) {
|
|
462
|
+
throw makeError("INVALID_INPUT", "path must be a non-empty string when provided");
|
|
463
|
+
}
|
|
464
|
+
const result = await this.requestWithOptions("open_workspace", path === undefined ? {} : { path }, path === undefined ? { timeoutMs: null } : {});
|
|
465
|
+
const workspace = normalizeOpenWorkspaceResponse(result);
|
|
466
|
+
return workspace === null ? null : new PedelecWorkspace(this, workspace.workspaceId, workspace.path);
|
|
322
467
|
}
|
|
323
468
|
async listProviders() {
|
|
324
469
|
const providers = normalizeProviderInfoList(await this.request("list_providers"));
|
|
@@ -358,16 +503,23 @@ export class Pedelec {
|
|
|
358
503
|
const existing = this.sessions.get(sessionId);
|
|
359
504
|
const session = existing && !existing.isTransportDetached()
|
|
360
505
|
? existing
|
|
361
|
-
: this.registerSession(sessionId, "", undefined, new Map(), false);
|
|
506
|
+
: this.registerSession(sessionId, "", undefined, new Map(), false, new PedelecWorkspace(this, "", null));
|
|
362
507
|
try {
|
|
363
508
|
const result = await this.request("resume_session", {
|
|
364
509
|
sessionId,
|
|
365
510
|
});
|
|
366
|
-
|
|
367
|
-
|
|
511
|
+
const normalizedResult = normalizeResumeSessionResponse(result);
|
|
512
|
+
if (normalizedResult.sessionId !== sessionId) {
|
|
513
|
+
throw makeError("SDK_PROTOCOL_ERROR", "resume_session response did not match sessionId");
|
|
514
|
+
}
|
|
515
|
+
if (existing && existing.workspace.workspaceIdInternal !== normalizedResult.workspace.workspaceId) {
|
|
516
|
+
throw makeError("SDK_PROTOCOL_ERROR", "resume_session response returned a different Workspace");
|
|
517
|
+
}
|
|
518
|
+
if (session !== existing) {
|
|
519
|
+
session.workspace.hydrate(normalizedResult.workspace.workspaceId, normalizedResult.workspace.path);
|
|
368
520
|
}
|
|
369
|
-
if (typeof
|
|
370
|
-
session.setAutoEndOnDisconnect(
|
|
521
|
+
if (typeof normalizedResult.autoEndOnDisconnect === "boolean") {
|
|
522
|
+
session.setAutoEndOnDisconnect(normalizedResult.autoEndOnDisconnect);
|
|
371
523
|
}
|
|
372
524
|
return session;
|
|
373
525
|
}
|
|
@@ -380,6 +532,7 @@ export class Pedelec {
|
|
|
380
532
|
request(type, payload = {}) {
|
|
381
533
|
return this.requestWithOptions(type, payload);
|
|
382
534
|
}
|
|
535
|
+
/** @internal */
|
|
383
536
|
requestWithOptions(type, payload = {}, options = {}) {
|
|
384
537
|
let port;
|
|
385
538
|
try {
|
|
@@ -394,7 +547,9 @@ export class Pedelec {
|
|
|
394
547
|
type,
|
|
395
548
|
requestId,
|
|
396
549
|
...payload,
|
|
397
|
-
...(type === "create_session"
|
|
550
|
+
...(type === "create_session" || type === "open_workspace"
|
|
551
|
+
? { callerSdkVersion: SDK_VERSION }
|
|
552
|
+
: {}),
|
|
398
553
|
};
|
|
399
554
|
return new Promise((resolve, reject) => {
|
|
400
555
|
const timeoutMs = options.timeoutMs === undefined ? this.bridgeTimeoutMs : options.timeoutMs;
|
|
@@ -514,16 +669,14 @@ export class Pedelec {
|
|
|
514
669
|
const effort = normalizeCreateSessionEffortInput(raw.effort);
|
|
515
670
|
const autoEndOnDisconnect = raw.autoEndOnDisconnect !== false;
|
|
516
671
|
const normalizedSkills = normalizeSkillsInput(raw.skills);
|
|
517
|
-
const workspace = normalizeCreateSessionWorkspaceInput(raw.workspace);
|
|
518
672
|
if (!hasProvider) {
|
|
519
|
-
return this.resolveDefaultCreateSessionInput(model, effort, undefined, normalizedSkills,
|
|
673
|
+
return this.resolveDefaultCreateSessionInput(model, effort, undefined, normalizedSkills, autoEndOnDisconnect);
|
|
520
674
|
}
|
|
521
675
|
return {
|
|
522
676
|
provider: provider,
|
|
523
677
|
model,
|
|
524
678
|
effort,
|
|
525
679
|
skills: normalizedSkills.manifest,
|
|
526
|
-
workspace,
|
|
527
680
|
inlineToolHandlers: normalizedSkills.handlers,
|
|
528
681
|
denoModules: normalizedSkills.denoModules,
|
|
529
682
|
autoEndOnDisconnect,
|
|
@@ -538,15 +691,13 @@ export class Pedelec {
|
|
|
538
691
|
}
|
|
539
692
|
const autoEndOnDisconnect = raw.autoEndOnDisconnect !== false;
|
|
540
693
|
const normalizedSkills = normalizeSkillsInput(raw.skills);
|
|
541
|
-
const workspace = normalizeCreateSessionWorkspaceInput(raw.workspace);
|
|
542
694
|
if (!hasProvider) {
|
|
543
|
-
return this.resolveDefaultCreateSessionInput(undefined, undefined, effortLevel, normalizedSkills,
|
|
695
|
+
return this.resolveDefaultCreateSessionInput(undefined, undefined, effortLevel, normalizedSkills, autoEndOnDisconnect);
|
|
544
696
|
}
|
|
545
697
|
return {
|
|
546
698
|
provider: provider,
|
|
547
699
|
effortLevel,
|
|
548
700
|
skills: normalizedSkills.manifest,
|
|
549
|
-
workspace,
|
|
550
701
|
inlineToolHandlers: normalizedSkills.handlers,
|
|
551
702
|
denoModules: normalizedSkills.denoModules,
|
|
552
703
|
autoEndOnDisconnect,
|
|
@@ -604,7 +755,7 @@ export class Pedelec {
|
|
|
604
755
|
};
|
|
605
756
|
}
|
|
606
757
|
}
|
|
607
|
-
async resolveDefaultCreateSessionInput(model, effort, effortLevel, normalizedSkills,
|
|
758
|
+
async resolveDefaultCreateSessionInput(model, effort, effortLevel, normalizedSkills, autoEndOnDisconnect) {
|
|
608
759
|
const settings = await this.getSettings();
|
|
609
760
|
if (!settings.defaultProvider) {
|
|
610
761
|
throw makeError("DEFAULT_PROVIDER_NOT_SET", "Default provider is not set. Open the Pedelec desktop app Settings page and choose a default provider.");
|
|
@@ -616,7 +767,6 @@ export class Pedelec {
|
|
|
616
767
|
effort,
|
|
617
768
|
effortLevel,
|
|
618
769
|
skills: normalizedSkills.manifest,
|
|
619
|
-
workspace,
|
|
620
770
|
inlineToolHandlers: normalizedSkills.handlers,
|
|
621
771
|
denoModules: normalizedSkills.denoModules,
|
|
622
772
|
autoEndOnDisconnect,
|
|
@@ -629,13 +779,13 @@ export class Pedelec {
|
|
|
629
779
|
throw makeError("DEFAULT_PROVIDER_UNAVAILABLE", "Default provider is not currently available. Open the Pedelec desktop app Settings page and choose an available provider.", { provider });
|
|
630
780
|
}
|
|
631
781
|
}
|
|
632
|
-
registerSession(sessionId, provider, effortLevel, inlineToolHandlers = new Map(), autoEndOnDisconnect = false) {
|
|
782
|
+
registerSession(sessionId, provider, effortLevel, inlineToolHandlers = new Map(), autoEndOnDisconnect = false, workspace) {
|
|
633
783
|
const existing = this.sessions.get(sessionId);
|
|
634
784
|
if (existing && !existing.isTransportDetached()) {
|
|
635
785
|
existing.replaceInlineToolHandlers(inlineToolHandlers);
|
|
636
786
|
return existing;
|
|
637
787
|
}
|
|
638
|
-
const session = new PedelecSession(this, sessionId, provider, effortLevel, inlineToolHandlers, autoEndOnDisconnect);
|
|
788
|
+
const session = new PedelecSession(this, sessionId, provider, workspace, effortLevel, inlineToolHandlers, autoEndOnDisconnect);
|
|
639
789
|
this.sessions.set(sessionId, session);
|
|
640
790
|
return session;
|
|
641
791
|
}
|
|
@@ -766,21 +916,6 @@ export class Pedelec {
|
|
|
766
916
|
}
|
|
767
917
|
}
|
|
768
918
|
}
|
|
769
|
-
function normalizeCreateSessionWorkspaceInput(value) {
|
|
770
|
-
if (value === undefined)
|
|
771
|
-
return undefined;
|
|
772
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
773
|
-
throw makeError("INVALID_INPUT", "workspace must be an object");
|
|
774
|
-
}
|
|
775
|
-
const path = value.path;
|
|
776
|
-
if (typeof path !== "string") {
|
|
777
|
-
throw makeError("INVALID_INPUT", "workspace.path must be a string");
|
|
778
|
-
}
|
|
779
|
-
if (path.trim().length === 0) {
|
|
780
|
-
throw makeError("INVALID_INPUT", "workspace.path must not be empty");
|
|
781
|
-
}
|
|
782
|
-
return { path };
|
|
783
|
-
}
|
|
784
919
|
function normalizeCreateSessionModelInput(value) {
|
|
785
920
|
if (value === undefined)
|
|
786
921
|
return undefined;
|
|
@@ -802,6 +937,7 @@ export class PedelecSession {
|
|
|
802
937
|
sessionId;
|
|
803
938
|
provider;
|
|
804
939
|
effortLevel;
|
|
940
|
+
workspace;
|
|
805
941
|
sessionCreatedAt = Date.now();
|
|
806
942
|
usage = { totalTokens: undefined };
|
|
807
943
|
status = "idle";
|
|
@@ -825,11 +961,12 @@ export class PedelecSession {
|
|
|
825
961
|
recoveredToolCalls = new Map();
|
|
826
962
|
handledToolRequestIds = new Set();
|
|
827
963
|
authoritativePendingToolRequest = undefined;
|
|
828
|
-
constructor(client, sessionId, provider, effortLevel, inlineToolHandlers = new Map(), autoEndOnDisconnect = false) {
|
|
964
|
+
constructor(client, sessionId, provider, workspace, effortLevel, inlineToolHandlers = new Map(), autoEndOnDisconnect = false) {
|
|
829
965
|
this.client = client;
|
|
830
966
|
this.sessionId = sessionId;
|
|
831
967
|
this.provider = provider;
|
|
832
968
|
this.effortLevel = effortLevel;
|
|
969
|
+
this.workspace = workspace;
|
|
833
970
|
this.inlineToolHandlers = new Map(inlineToolHandlers);
|
|
834
971
|
this.autoEndOnDisconnect = autoEndOnDisconnect;
|
|
835
972
|
}
|
|
@@ -1615,21 +1752,86 @@ function normalizePedelecSettings(value) {
|
|
|
1615
1752
|
}
|
|
1616
1753
|
return { defaultProvider: value.defaultProvider };
|
|
1617
1754
|
}
|
|
1618
|
-
function
|
|
1619
|
-
if (!isPlainObject(value) ||
|
|
1620
|
-
|
|
1755
|
+
function normalizeWorkspaceCapability(value, context) {
|
|
1756
|
+
if (!isPlainObject(value) || typeof value.workspaceId !== "string" || value.workspaceId.trim().length === 0 ||
|
|
1757
|
+
(value.path !== null && (typeof value.path !== "string" || value.path.length === 0))) {
|
|
1758
|
+
throw makeError("SDK_PROTOCOL_ERROR", `${context} response had an invalid Workspace shape`);
|
|
1621
1759
|
}
|
|
1622
|
-
|
|
1623
|
-
|
|
1760
|
+
return {
|
|
1761
|
+
workspaceId: value.workspaceId,
|
|
1762
|
+
path: value.path,
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function normalizeOpenWorkspaceResponse(value) {
|
|
1766
|
+
if (value === null)
|
|
1624
1767
|
return null;
|
|
1625
|
-
if (
|
|
1626
|
-
|
|
1627
|
-
|
|
1768
|
+
if (!isPlainObject(value) || !("workspace" in value)) {
|
|
1769
|
+
throw makeError("SDK_PROTOCOL_ERROR", "open_workspace response had an invalid shape");
|
|
1770
|
+
}
|
|
1771
|
+
if (value.workspace === null)
|
|
1772
|
+
return null;
|
|
1773
|
+
return normalizeWorkspaceCapability(value.workspace, "open_workspace");
|
|
1774
|
+
}
|
|
1775
|
+
function normalizeCreateSessionResponse(value) {
|
|
1776
|
+
if (!isPlainObject(value) || typeof value.sessionId !== "string" || value.sessionId.trim().length === 0 ||
|
|
1777
|
+
!("workspace" in value)) {
|
|
1778
|
+
throw makeError("SDK_PROTOCOL_ERROR", "create_session response had an invalid shape");
|
|
1779
|
+
}
|
|
1780
|
+
if ("explicitModelConfigApplied" in value && typeof value.explicitModelConfigApplied !== "boolean") {
|
|
1781
|
+
throw makeError("SDK_PROTOCOL_ERROR", "create_session response had an invalid model configuration flag");
|
|
1782
|
+
}
|
|
1783
|
+
return {
|
|
1784
|
+
sessionId: value.sessionId,
|
|
1785
|
+
workspace: normalizeWorkspaceCapability(value.workspace, "create_session"),
|
|
1786
|
+
...(typeof value.explicitModelConfigApplied === "boolean"
|
|
1787
|
+
? { explicitModelConfigApplied: value.explicitModelConfigApplied }
|
|
1788
|
+
: {}),
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function normalizeResumeSessionResponse(value) {
|
|
1792
|
+
if (!isPlainObject(value) || typeof value.sessionId !== "string" || value.sessionId.trim().length === 0 ||
|
|
1793
|
+
typeof value.autoEndOnDisconnect !== "boolean" || !("workspace" in value)) {
|
|
1794
|
+
throw makeError("SDK_PROTOCOL_ERROR", "resume_session response had an invalid shape");
|
|
1795
|
+
}
|
|
1796
|
+
return {
|
|
1797
|
+
sessionId: value.sessionId,
|
|
1798
|
+
autoEndOnDisconnect: value.autoEndOnDisconnect,
|
|
1799
|
+
workspace: normalizeWorkspaceCapability(value.workspace, "resume_session"),
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function normalizeWorkspaceListResponse(value) {
|
|
1803
|
+
const paths = Array.isArray(value) ? value : isPlainObject(value) ? value.paths : undefined;
|
|
1804
|
+
if (!Array.isArray(paths) || paths.some((path) => typeof path !== "string")) {
|
|
1805
|
+
throw makeError("SDK_PROTOCOL_ERROR", "workspace list response had an invalid shape");
|
|
1806
|
+
}
|
|
1807
|
+
return paths;
|
|
1808
|
+
}
|
|
1809
|
+
function normalizeWorkspaceDenoModulePrepareResponse(value, requestedNames) {
|
|
1810
|
+
if (!isPlainObject(value) || !Array.isArray(value.missingModuleNames)) {
|
|
1811
|
+
throw makeError("SDK_PROTOCOL_ERROR", "prepare_workspace_deno_modules response had an invalid shape");
|
|
1812
|
+
}
|
|
1813
|
+
const requested = new Set(requestedNames);
|
|
1814
|
+
const seen = new Set();
|
|
1815
|
+
for (const name of value.missingModuleNames) {
|
|
1816
|
+
if (typeof name !== "string" || !requested.has(name) || seen.has(name)) {
|
|
1817
|
+
throw makeError("SDK_PROTOCOL_ERROR", "prepare_workspace_deno_modules response had unknown or duplicate modules");
|
|
1818
|
+
}
|
|
1819
|
+
seen.add(name);
|
|
1820
|
+
}
|
|
1821
|
+
return value.missingModuleNames;
|
|
1822
|
+
}
|
|
1823
|
+
function normalizeWorkspaceRunResponse(value) {
|
|
1824
|
+
if (!isPlainObject(value) || !Number.isInteger(value.exitCode) || typeof value.stdout !== "string" ||
|
|
1825
|
+
typeof value.stderr !== "string" || typeof value.stdoutTruncated !== "boolean" ||
|
|
1826
|
+
typeof value.stderrTruncated !== "boolean") {
|
|
1827
|
+
throw makeError("SDK_PROTOCOL_ERROR", "workspace_run response had an invalid shape");
|
|
1628
1828
|
}
|
|
1629
1829
|
return {
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1830
|
+
exitCode: value.exitCode,
|
|
1831
|
+
stdout: value.stdout,
|
|
1832
|
+
stderr: value.stderr,
|
|
1833
|
+
stdoutTruncated: value.stdoutTruncated,
|
|
1834
|
+
stderrTruncated: value.stderrTruncated,
|
|
1633
1835
|
};
|
|
1634
1836
|
}
|
|
1635
1837
|
function normalizeProviderInfoList(value) {
|
package/dist/index.type-test.js
CHANGED
|
@@ -6,16 +6,16 @@ const spriteTools = defineDenoModule({
|
|
|
6
6
|
usage: `import { previewActorSource } from "sprite-tools";`,
|
|
7
7
|
preferStdinExecution: true,
|
|
8
8
|
});
|
|
9
|
+
const workspaceTools = defineDenoModule({
|
|
10
|
+
name: "workspace-tools",
|
|
11
|
+
entry: "./workspace/workspace-tools.ts",
|
|
12
|
+
});
|
|
9
13
|
const spriteName = spriteTools.name;
|
|
10
14
|
const spriteDefinition = spriteTools;
|
|
11
15
|
void spriteName;
|
|
12
16
|
void spriteDefinition;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
name: "missing-usage",
|
|
16
|
-
description: "Missing usage.",
|
|
17
|
-
entry: "./agent/missing-usage.ts",
|
|
18
|
-
});
|
|
17
|
+
const workspaceName = workspaceTools.name;
|
|
18
|
+
void workspaceName;
|
|
19
19
|
// @ts-expect-error Deno Module entry remains required at authoring time
|
|
20
20
|
defineDenoModule({
|
|
21
21
|
name: "missing-entry",
|
|
@@ -29,9 +29,6 @@ defineDenoModule({
|
|
|
29
29
|
entry: 42,
|
|
30
30
|
usage: 'import "non-string-entry";',
|
|
31
31
|
});
|
|
32
|
-
function workspaceInputHasPublicType() {
|
|
33
|
-
return { path: "C:\\workspace\\project" };
|
|
34
|
-
}
|
|
35
32
|
async function typedOnToolNameFromCreateSession() {
|
|
36
33
|
const pedelec = new Pedelec();
|
|
37
34
|
const session = await pedelec.createSession({
|
|
@@ -126,10 +123,37 @@ async function noSkillsFallsBackToString() {
|
|
|
126
123
|
const pedelec = new Pedelec();
|
|
127
124
|
const session = await pedelec.createSession({
|
|
128
125
|
provider: "codex",
|
|
129
|
-
workspace: { path: "C:\\workspace\\project" },
|
|
130
126
|
});
|
|
131
|
-
|
|
132
|
-
|
|
127
|
+
session.workspace.run("console.log('workspace')", { denoModules: [workspaceTools] });
|
|
128
|
+
session.workspace.run("console.log('workspace')", { denoModules: [spriteTools] });
|
|
129
|
+
await pedelec.createSession({
|
|
130
|
+
provider: "codex",
|
|
131
|
+
skills: { guidance: "Use modules.", tools: [],
|
|
132
|
+
// @ts-expect-error Workspace-only declarations do not satisfy Session metadata requirements
|
|
133
|
+
denoModules: [workspaceTools] },
|
|
134
|
+
});
|
|
135
|
+
const descriptionOnly = defineDenoModule({
|
|
136
|
+
name: "description-only",
|
|
137
|
+
description: "Missing usage.",
|
|
138
|
+
entry: "./agent/description-only.ts",
|
|
139
|
+
});
|
|
140
|
+
await pedelec.createSession({
|
|
141
|
+
provider: "codex",
|
|
142
|
+
skills: { guidance: "Use modules.", tools: [],
|
|
143
|
+
// @ts-expect-error Session metadata requires usage as well as description
|
|
144
|
+
denoModules: [descriptionOnly] },
|
|
145
|
+
});
|
|
146
|
+
const usageOnly = defineDenoModule({
|
|
147
|
+
name: "usage-only",
|
|
148
|
+
entry: "./agent/usage-only.ts",
|
|
149
|
+
usage: 'import "usage-only";',
|
|
150
|
+
});
|
|
151
|
+
await pedelec.createSession({
|
|
152
|
+
provider: "codex",
|
|
153
|
+
skills: { guidance: "Use modules.", tools: [],
|
|
154
|
+
// @ts-expect-error Session metadata requires description as well as usage
|
|
155
|
+
denoModules: [usageOnly] },
|
|
156
|
+
});
|
|
133
157
|
session.onTool((name) => {
|
|
134
158
|
const anyString = name;
|
|
135
159
|
return anyString;
|
|
@@ -192,14 +216,32 @@ async function availabilityHasPublicType() {
|
|
|
192
216
|
void availability;
|
|
193
217
|
void promise;
|
|
194
218
|
}
|
|
195
|
-
async function
|
|
219
|
+
async function workspacePublicTypeContract() {
|
|
196
220
|
const pedelec = new Pedelec();
|
|
197
|
-
const
|
|
198
|
-
|
|
221
|
+
const maybeWorkspace = await pedelec.openWorkspace();
|
|
222
|
+
const workspace = await pedelec.openWorkspace("C:\\workspace\\project");
|
|
223
|
+
const files = workspace.listFiles();
|
|
224
|
+
const folders = workspace.listFolders("src");
|
|
225
|
+
const run = workspace.run("console.log('hello')", { timeoutMs: 1000 });
|
|
226
|
+
const session = await workspace.createSession({
|
|
227
|
+
skills: {
|
|
228
|
+
guidance: "Use tools.",
|
|
229
|
+
tools: [defineTool({ name: "workspace_tool", description: "Tool", argsSchema: { type: "object" } })],
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
session.workspace;
|
|
233
|
+
maybeWorkspace?.listFiles();
|
|
234
|
+
void files;
|
|
235
|
+
void folders;
|
|
236
|
+
void run;
|
|
237
|
+
// @ts-expect-error workspace is now selected through openWorkspace
|
|
238
|
+
pedelec.createSession({ workspace: { path: "C:\\workspace\\project" } });
|
|
239
|
+
// @ts-expect-error the removed picker API must not be public
|
|
240
|
+
pedelec.workspaceFolderPicker();
|
|
199
241
|
}
|
|
200
242
|
function directoryPickerIsRemoved() {
|
|
201
243
|
const pedelec = new Pedelec();
|
|
202
|
-
// @ts-expect-error directoryPicker was removed in favor of
|
|
244
|
+
// @ts-expect-error directoryPicker was removed in favor of openWorkspace
|
|
203
245
|
pedelec.directoryPicker();
|
|
204
246
|
}
|
|
205
247
|
function publicSecurityTypesAreRestricted() {
|
|
@@ -222,7 +264,7 @@ void noSkillsFallsBackToString;
|
|
|
222
264
|
void effortLevelPublicTypeContract;
|
|
223
265
|
void listAssetsHasPublicTypes;
|
|
224
266
|
void availabilityHasPublicType;
|
|
225
|
-
void
|
|
267
|
+
void workspacePublicTypeContract;
|
|
226
268
|
void directoryPickerIsRemoved;
|
|
227
269
|
void publicSecurityTypesAreRestricted;
|
|
228
270
|
const baseContext = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "0.
|
|
1
|
+
export declare const SDK_VERSION = "0.4.1";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated from sdk/package.json. Do not edit manually.
|
|
2
|
-
export const SDK_VERSION = "0.
|
|
2
|
+
export const SDK_VERSION = "0.4.1";
|
package/dist/vite.js
CHANGED
|
@@ -980,7 +980,7 @@ function readDeclaration(node, sourceFile) {
|
|
|
980
980
|
values.set(name, property.initializer);
|
|
981
981
|
}
|
|
982
982
|
const name = readStringField(values, "name", sourceFile, node);
|
|
983
|
-
const description =
|
|
983
|
+
const description = readOptionalStringField(values, "description", sourceFile, node);
|
|
984
984
|
if (values.has("code")) {
|
|
985
985
|
throw declarationError(sourceFile, values.get("code"), "defineDenoModule() does not support inline code; use a static entry");
|
|
986
986
|
}
|
|
@@ -993,7 +993,7 @@ function readDeclaration(node, sourceFile) {
|
|
|
993
993
|
const entry = readStaticString(entryExpression);
|
|
994
994
|
if (entry === null)
|
|
995
995
|
throw declarationError(sourceFile, entryExpression, "defineDenoModule() entry must be a string literal");
|
|
996
|
-
const usage =
|
|
996
|
+
const usage = readOptionalStringField(values, "usage", sourceFile, node);
|
|
997
997
|
return {
|
|
998
998
|
name,
|
|
999
999
|
description,
|
|
@@ -1007,6 +1007,16 @@ function readDeclaration(node, sourceFile) {
|
|
|
1007
1007
|
entryEnd: entryExpression.end,
|
|
1008
1008
|
};
|
|
1009
1009
|
}
|
|
1010
|
+
function readOptionalStringField(values, field, sourceFile, node) {
|
|
1011
|
+
if (!values.has(field))
|
|
1012
|
+
return undefined;
|
|
1013
|
+
const expression = values.get(field);
|
|
1014
|
+
const value = readStaticString(expression);
|
|
1015
|
+
if (value === null) {
|
|
1016
|
+
throw declarationError(sourceFile, expression ?? node, `defineDenoModule() field must be a static string when provided: ${field}`);
|
|
1017
|
+
}
|
|
1018
|
+
return value;
|
|
1019
|
+
}
|
|
1010
1020
|
function replaceSourceRange(source, start, end, replacement) {
|
|
1011
1021
|
return source.slice(0, start) + replacement + source.slice(end);
|
|
1012
1022
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaoruisaac/pedelec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prepublishOnly": "npm run test && npm run type-test && npm run build"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"typescript": "
|
|
40
|
+
"typescript": "5.9.3"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"vite": "^8.0.0"
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/node": "
|
|
52
|
-
"vite": "
|
|
53
|
-
"vitest": "
|
|
51
|
+
"@types/node": "24.13.6",
|
|
52
|
+
"vite": "8.3.0",
|
|
53
|
+
"vitest": "4.1.11"
|
|
54
54
|
}
|
|
55
55
|
}
|