@kaoruisaac/pedelec 0.3.3 → 0.3.4

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 CHANGED
@@ -90,6 +90,66 @@ npm install ../path/to/pedelec/sdk
90
90
 
91
91
  ---
92
92
 
93
+ ## Deno Modules
94
+
95
+ Vite applications can package local or installed JavaScript/TypeScript libraries for Agent-authored `pedelec-deno` scripts.
96
+
97
+ ### Setup
98
+
99
+ ```ts
100
+ // vite.config.ts
101
+ import { defineConfig } from "vite";
102
+ import { pedelecVitePlugin } from "@kaoruisaac/pedelec/vite";
103
+
104
+ export default defineConfig({
105
+ plugins: [pedelecVitePlugin()],
106
+ });
107
+ ```
108
+
109
+ ### Authoring
110
+
111
+ ```ts
112
+ import { Pedelec, defineDenoModule } from "@kaoruisaac/pedelec";
113
+
114
+ const pedelec = new Pedelec();
115
+ const session = await pedelec.createSession({
116
+ skills: {
117
+ guidance: "Use sprite-tools for sprite authoring tasks.",
118
+ tools: [],
119
+ denoModules: [
120
+ defineDenoModule({
121
+ name: "sprite-tools",
122
+ description: "Sprite authoring and preview utilities.",
123
+ entry: "./agent/sprite-tools.ts",
124
+ usage: `import { previewActorSource } from "sprite-tools";`,
125
+ preferStdinExecution: true,
126
+ }),
127
+ ],
128
+ },
129
+ });
130
+ ```
131
+
132
+ For an installed package, use its package specifier as the build-time entry:
133
+
134
+ ```ts
135
+ defineDenoModule({
136
+ name: "gsap",
137
+ description: "Animation utilities.",
138
+ entry: "gsap",
139
+ usage: `import { gsap } from "gsap";`,
140
+ });
141
+ ```
142
+
143
+ `entry` is resolved by Vite during build/dev and is never a Desktop filesystem instruction. The plugin bundles normal third-party runtime dependencies locally and generates the public `index.d.ts` declarations automatically. Node built-ins may be imported with either canonical `node:` specifiers or legacy bare specifiers; the prepared artifact preserves them as canonical `node:` imports for Deno's Node compatibility layer. This does not change the sandbox: runtime npm, JSR, network fetching, and filesystem access outside the permitted workspace remain unavailable.
144
+
145
+ The generated declaration describes the module's Agent-facing API, so implementation-only dependency types are omitted. Module-owned types are supported; packages that expose dependency-owned or Node-specific external types directly in their public API are not guaranteed to produce a standalone declaration in V1. `name` is the Agent import specifier, and `usage` is required common-case guidance. `createSession()` transfers the prepared artifact automatically; a session keeps an immutable module snapshot, and `session.end()`/`session.resume()` reuse it while Core still knows the thread.
146
+
147
+ Deno Modules are imported inside JavaScript/TypeScript run with `pedelec-deno`. They are different from `skills.tools`, whose browser/App RPC capabilities use `pedelec-cli` tool-spec and tool-call commands.
148
+
149
+ Set the optional `preferStdinExecution: true` when short scripts primarily use a module and should be steered toward stdin execution. Host Context then adds that module's concrete `runCommandTemplate` (`@'\n<typescript-source>\n'@ | pedelec-deno --thread-id <thread-id> run -`), so the Agent only needs to replace the TypeScript source placeholder instead of deciding how to serialize stdin. This is a preference, not a restriction: file-backed execution remains valid, and omitting the field or setting it to `false` preserves the default behavior. It does not change bundling, artifact contents, or runtime permissions.
150
+
151
+ ---
152
+
93
153
  ## Minimal Example
94
154
 
95
155
  ```ts
@@ -246,7 +306,7 @@ if (availability.available) startUi();
246
306
 
247
307
  An unavailable extension may be disconnected rather than absent. `desktop.launchAttempted` means the settings probe was sent, not that Desktop was confirmed to have launched. Invalid settings responses also count as Desktop unavailable in this probe.
248
308
 
249
- ### Selecting Provider and Effort
309
+ ### Selecting Provider, Model, and Effort
250
310
 
251
311
  ```ts
252
312
  const session = await pedelec.createSession({
@@ -258,7 +318,19 @@ const session = await pedelec.createSession({
258
318
  const defaultSession = await pedelec.createSession({ effortLevel: "low" });
259
319
  ```
260
320
 
261
- The SDK uses the provider-independent effort levels `default`, `low`, and `high`. An omitted level means `default`. Desktop users map each provider's profiles to concrete model and native effort arguments; that mapping is not exposed through the SDK.
321
+ The SDK uses the provider-independent effort levels `default`, `low`, and `high`. An omitted level means `default`. Desktop users map each provider's profiles to concrete model and native effort arguments. That profile remains the default source of both values, but an application may optionally override only the model for one session:
322
+
323
+ ```ts
324
+ const specializedSession = await pedelec.createSession({
325
+ provider: "codex",
326
+ effortLevel: "high",
327
+ model: "gpt-x",
328
+ });
329
+ ```
330
+
331
+ `model` is an optional provider-native identifier. Omitting it preserves the existing Desktop-profile behavior; providing it does not disable `effortLevel`, replaces only the selected profile's model, and leaves its native effort arguments intact. The SDK does not expose or maintain a model catalog, and it does not translate the identifier into provider CLI flags. Model validity and account entitlement remain provider-dependent, so an invalid or unavailable model may fail when the provider starts.
332
+
333
+ For an explicit model, the SDK requires an acknowledgment from Core that the override was applied before it exposes the session or starts Deno Module setup. If the connected Extension/Desktop is too old to provide that acknowledgment, the SDK aborts the newly created setup and reports a compatibility error instead of silently ignoring the model.
262
334
 
263
335
  Currently supported provider codes in the SDK:
264
336
 
@@ -271,6 +343,6 @@ Currently supported provider codes in the SDK:
271
343
  | Claude Code | `claude` |
272
344
  | Ollama | `ollama` |
273
345
 
274
- Ollama sessions are executed by the bundled `pedelec-agent`. The selected Ollama effort profile must contain a model in Desktop Settings; otherwise Core returns `MODEL_REQUIRED`. Low and high profiles are optional, but there is no fallback between profiles.
346
+ Ollama sessions are executed by the bundled `pedelec-agent`. The selected Ollama effort profile must contain a model unless `createSession({ model })` supplies the explicit model override; otherwise Core returns `MODEL_REQUIRED`. Low and high profiles are optional, but there is no fallback between profiles.
275
347
 
276
348
  `getSettings()` returns only `{ defaultProvider }`. It never exposes provider settings, effort arguments, credentials, or model names.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Browser-safe private interop shared by the root SDK entry and the Vite
3
+ * build-time plugin. This file is intentionally not re-exported from the
4
+ * package root: application code authors the small DenoModuleDefinition only.
5
+ */
6
+ export declare const PREPARED_DENO_MODULE_ARTIFACT_PROPERTY = "__pedelecArtifact";
7
+ export type PreparedDenoModuleArtifact = {
8
+ readonly format: "esm";
9
+ readonly runtimeSource: string;
10
+ readonly typesSource: string;
11
+ readonly contentHash?: string;
12
+ };
13
+ export declare function getPreparedDenoModuleArtifact(value: unknown): PreparedDenoModuleArtifact | null;
14
+ /**
15
+ * Keep this deliberately narrower than arbitrary URL/path syntax. Core has
16
+ * the corresponding contract in Rust; both sides accept ordinary bare and
17
+ * scoped package-like specifiers only.
18
+ */
19
+ export declare function isValidDenoModuleName(value: unknown): value is string;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Browser-safe private interop shared by the root SDK entry and the Vite
3
+ * build-time plugin. This file is intentionally not re-exported from the
4
+ * package root: application code authors the small DenoModuleDefinition only.
5
+ */
6
+ export const PREPARED_DENO_MODULE_ARTIFACT_PROPERTY = "__pedelecArtifact";
7
+ export function getPreparedDenoModuleArtifact(value) {
8
+ if (!value || typeof value !== "object" || Array.isArray(value))
9
+ return null;
10
+ const artifact = value[PREPARED_DENO_MODULE_ARTIFACT_PROPERTY];
11
+ if (!artifact || typeof artifact !== "object" || Array.isArray(artifact))
12
+ return null;
13
+ const candidate = artifact;
14
+ if (candidate.format !== "esm" ||
15
+ typeof candidate.runtimeSource !== "string" ||
16
+ candidate.runtimeSource.trim().length === 0 ||
17
+ typeof candidate.typesSource !== "string" ||
18
+ candidate.typesSource.trim().length === 0) {
19
+ return null;
20
+ }
21
+ return {
22
+ format: "esm",
23
+ runtimeSource: candidate.runtimeSource,
24
+ typesSource: candidate.typesSource,
25
+ ...(typeof candidate.contentHash === "string" ? { contentHash: candidate.contentHash } : {}),
26
+ };
27
+ }
28
+ const DENO_MODULE_SEGMENT_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
29
+ /**
30
+ * Keep this deliberately narrower than arbitrary URL/path syntax. Core has
31
+ * the corresponding contract in Rust; both sides accept ordinary bare and
32
+ * scoped package-like specifiers only.
33
+ */
34
+ export function isValidDenoModuleName(value) {
35
+ if (typeof value !== "string" || value.length === 0 || value.trim() !== value)
36
+ return false;
37
+ if (value === "." || value === ".." || value.includes("\\") || value.includes("\0"))
38
+ return false;
39
+ if (value.includes(":") || value.startsWith("/") || value.endsWith("/"))
40
+ return false;
41
+ const parts = value.split("/");
42
+ if (parts.length === 1)
43
+ return DENO_MODULE_SEGMENT_PATTERN.test(parts[0]);
44
+ if (parts.length !== 2 || !parts[0].startsWith("@") || parts[0].length === 1)
45
+ return false;
46
+ const scope = parts[0].slice(1);
47
+ const packageName = parts[1];
48
+ return DENO_MODULE_SEGMENT_PATTERN.test(scope) && DENO_MODULE_SEGMENT_PATTERN.test(packageName);
49
+ }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,13 @@ export type Asset = {
15
15
  sizeBytes: number;
16
16
  modifiedAt: number;
17
17
  };
18
+ export type DenoModuleDefinition<TName extends string = string> = {
19
+ name: TName;
20
+ description: string;
21
+ entry: string;
22
+ usage: string;
23
+ preferStdinExecution?: boolean;
24
+ };
18
25
  export type ToolArgsSchemaMeta<TDefault extends JsonValue = JsonValue> = {
19
26
  description?: string;
20
27
  default?: TDefault;
@@ -71,6 +78,7 @@ export type ToolDefinition<TArgs = unknown, TResult = unknown, TName extends str
71
78
  export type SkillsInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
72
79
  guidance: string;
73
80
  tools: TTools;
81
+ denoModules?: readonly DenoModuleDefinition[];
74
82
  };
75
83
  export type ToolNameOf<TTools extends readonly ToolDefinition[]> = Extract<TTools[number]["name"], string>;
76
84
  export type SerializableToolManifest = {
@@ -79,9 +87,16 @@ export type SerializableToolManifest = {
79
87
  argsSchema: ToolArgsSchema;
80
88
  timeoutMs?: number;
81
89
  };
90
+ type SerializableDenoModuleManifest = {
91
+ name: string;
92
+ description: string;
93
+ usage: string;
94
+ preferStdinExecution?: boolean;
95
+ };
82
96
  export type SerializableSkillsManifest = {
83
97
  guidance: string;
84
98
  tools: SerializableToolManifest[];
99
+ denoModules?: SerializableDenoModuleManifest[];
85
100
  };
86
101
  export type CreateSessionWorkspaceInput = {
87
102
  path: string;
@@ -93,6 +108,7 @@ export interface WorkspaceFolderPickerResult {
93
108
  }
94
109
  type CreateSessionInputWithProvider<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
95
110
  provider: ProviderCode;
111
+ model?: string;
96
112
  effortLevel?: EffortLevel;
97
113
  skills?: SkillsInput<TTools>;
98
114
  workspace?: CreateSessionWorkspaceInput;
@@ -100,6 +116,7 @@ type CreateSessionInputWithProvider<TTools extends readonly ToolDefinition[] = r
100
116
  };
101
117
  type CreateSessionInputWithDefaults<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
102
118
  provider?: undefined;
119
+ model?: string;
103
120
  effortLevel?: EffortLevel;
104
121
  skills?: SkillsInput<TTools>;
105
122
  workspace?: CreateSessionWorkspaceInput;
@@ -287,6 +304,7 @@ type ErrorHandler = (error: PedelecError, ctx: ErrorEventContext) => void;
287
304
  type StatusHandler = (status: PedelecSessionStatus, ctx: StatusEventContext) => void;
288
305
  type EndedHandler = (ctx: EndedEventContext) => void;
289
306
  export declare function defineTool<TArgs = unknown, TResult = unknown, const TName extends string = string>(tool: ToolDefinition<TArgs, TResult, TName>): ToolDefinition<TArgs, TResult, TName>;
307
+ export declare function defineDenoModule<const TName extends string>(module: DenoModuleDefinition<TName>): DenoModuleDefinition<TName>;
290
308
  export declare class Pedelec {
291
309
  private readonly pageWindow;
292
310
  private readonly channelId;
@@ -301,6 +319,9 @@ export declare class Pedelec {
301
319
  constructor(options?: PedelecOptions);
302
320
  createSession(): Promise<PedelecSession<string>>;
303
321
  createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
322
+ private uploadDenoModuleArtifact;
323
+ private abortSessionSetup;
324
+ private completeSessionSetup;
304
325
  workspaceFolderPicker(): Promise<WorkspaceFolderPickerResult | null>;
305
326
  listProviders(): Promise<ProviderInfo[]>;
306
327
  getSettings(): Promise<PedelecSettings>;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { PEDELEC_EXTENSION_ID } from "./extension-id.js";
2
+ import { getPreparedDenoModuleArtifact, isValidDenoModuleName, } from "./deno-module-internal.js";
2
3
  import { SDK_VERSION } from "./version.generated.js";
3
4
  const SDK_EXTERNAL_PORT_NAME = "pedelec-sdk-external";
4
5
  const PAGE_ACTIVITY_MESSAGE_TYPE = "page_activity";
@@ -35,10 +36,13 @@ const TOOL_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_.-]*$/;
35
36
  export function defineTool(tool) {
36
37
  return tool;
37
38
  }
39
+ export function defineDenoModule(module) {
40
+ return module;
41
+ }
38
42
  function normalizeSkillsInput(value) {
39
43
  const handlers = new Map();
40
44
  if (value === undefined)
41
- return { handlers };
45
+ return { handlers, denoModules: [] };
42
46
  if (!value || typeof value !== "object" || Array.isArray(value)) {
43
47
  throw makeError("INVALID_INPUT", "skills must be an object");
44
48
  }
@@ -49,7 +53,11 @@ function normalizeSkillsInput(value) {
49
53
  if (!Array.isArray(skills.tools)) {
50
54
  throw makeError("INVALID_INPUT", "skills.tools must be an array");
51
55
  }
56
+ if (skills.denoModules !== undefined && !Array.isArray(skills.denoModules)) {
57
+ throw makeError("INVALID_INPUT", "skills.denoModules must be an array");
58
+ }
52
59
  const seen = new Set();
60
+ const seenDenoModuleNames = new Set();
53
61
  const tools = skills.tools.map((tool, index) => {
54
62
  if (!tool || typeof tool !== "object" || Array.isArray(tool)) {
55
63
  throw makeError("INVALID_INPUT", "skills.tools entries must be objects", { index });
@@ -91,12 +99,77 @@ function normalizeSkillsInput(value) {
91
99
  ...(rawTool.timeoutMs === undefined ? {} : { timeoutMs: rawTool.timeoutMs }),
92
100
  };
93
101
  });
102
+ const denoModules = (skills.denoModules ?? []).map((module, index) => {
103
+ if (!module || typeof module !== "object" || Array.isArray(module)) {
104
+ throw makeError("INVALID_INPUT", "skills.denoModules entries must be objects", { index });
105
+ }
106
+ const rawModule = module;
107
+ if (!isValidDenoModuleName(rawModule.name)) {
108
+ throw makeError("INVALID_INPUT", "Deno Module name is invalid", {
109
+ index,
110
+ moduleName: rawModule.name,
111
+ });
112
+ }
113
+ if (seenDenoModuleNames.has(rawModule.name)) {
114
+ throw makeError("INVALID_INPUT", "duplicate Deno Module name", {
115
+ moduleName: rawModule.name,
116
+ });
117
+ }
118
+ seenDenoModuleNames.add(rawModule.name);
119
+ if (typeof rawModule.description !== "string" || rawModule.description.trim().length === 0) {
120
+ throw makeError("INVALID_INPUT", "Deno Module description must be a non-empty string", {
121
+ moduleName: rawModule.name,
122
+ });
123
+ }
124
+ if (typeof rawModule.usage !== "string" || rawModule.usage.trim().length === 0) {
125
+ throw makeError("INVALID_INPUT", "Deno Module usage must be a non-empty string", {
126
+ moduleName: rawModule.name,
127
+ });
128
+ }
129
+ if (rawModule.entry !== undefined &&
130
+ (typeof rawModule.entry !== "string" || rawModule.entry.trim().length === 0)) {
131
+ throw makeError("INVALID_INPUT", "Deno Module entry must be a non-empty string", {
132
+ moduleName: rawModule.name,
133
+ });
134
+ }
135
+ if (rawModule.preferStdinExecution !== undefined && typeof rawModule.preferStdinExecution !== "boolean") {
136
+ throw makeError("INVALID_INPUT", "Deno Module preferStdinExecution must be a boolean", {
137
+ moduleName: rawModule.name,
138
+ });
139
+ }
140
+ const artifact = getPreparedDenoModuleArtifact(module);
141
+ if (!artifact) {
142
+ throw makeError("INVALID_INPUT", `Deno module "${rawModule.name}" was not prepared. Configure pedelecVitePlugin() in the Vite project.`, { moduleName: rawModule.name });
143
+ }
144
+ return {
145
+ name: rawModule.name,
146
+ description: rawModule.description,
147
+ usage: rawModule.usage,
148
+ ...(rawModule.preferStdinExecution === true ? { preferStdinExecution: true } : {}),
149
+ artifact,
150
+ };
151
+ });
94
152
  return {
95
153
  manifest: {
96
154
  guidance: skills.guidance,
97
155
  tools,
156
+ ...(denoModules.length > 0
157
+ ? {
158
+ denoModules: denoModules.map(({ name, description, usage, preferStdinExecution }) => ({
159
+ name,
160
+ description,
161
+ usage,
162
+ ...(preferStdinExecution === true ? { preferStdinExecution: true } : {}),
163
+ })),
164
+ }
165
+ : {}),
98
166
  },
99
167
  handlers,
168
+ denoModules: denoModules.map(({ name, artifact }) => ({
169
+ name,
170
+ runtimeSource: artifact.runtimeSource,
171
+ typesSource: artifact.typesSource,
172
+ })),
100
173
  };
101
174
  }
102
175
  function normalizeToolArgsSchema(argsSchema, toolName) {
@@ -144,6 +217,7 @@ export class Pedelec {
144
217
  const result = await this.request("create_session", {
145
218
  input: {
146
219
  provider: resolvedInput.provider,
220
+ model: resolvedInput.model,
147
221
  effortLevel: resolvedInput.effortLevel,
148
222
  skills: resolvedInput.skills,
149
223
  workspace: resolvedInput.workspace,
@@ -153,8 +227,94 @@ export class Pedelec {
153
227
  if (!result.sessionId) {
154
228
  throw makeError("SDK_PROTOCOL_ERROR", "create_session response did not include sessionId");
155
229
  }
230
+ if (resolvedInput.model !== undefined && result.modelOverrideApplied !== true) {
231
+ const error = makeError("SDK_PROTOCOL_ERROR", "The connected Pedelec Extension/Desktop does not support createSession model override. Update Pedelec components and try again.", { feature: "modelOverride" });
232
+ await this.abortSessionSetup(result.sessionId);
233
+ throw error;
234
+ }
235
+ try {
236
+ for (const module of resolvedInput.denoModules) {
237
+ await this.uploadDenoModuleArtifact(result.sessionId, module);
238
+ }
239
+ if (resolvedInput.denoModules.length > 0) {
240
+ await this.completeSessionSetup(result.sessionId);
241
+ }
242
+ }
243
+ catch (error) {
244
+ // The thread exists at this point but is not exposed through the SDK
245
+ // session registry yet. Keep the setup failure as the caller-visible
246
+ // error even if the best-effort cleanup transport also fails.
247
+ await this.abortSessionSetup(result.sessionId);
248
+ throw normalizeError(error, "DENO_MODULE_UPLOAD_FAILED", "Deno Module setup failed");
249
+ }
156
250
  return this.registerSession(result.sessionId, resolvedInput.provider, resolvedInput.effortLevel, resolvedInput.inlineToolHandlers, resolvedInput.autoEndOnDisconnect);
157
251
  }
252
+ async uploadDenoModuleArtifact(sessionId, module) {
253
+ const envelope = JSON.stringify({
254
+ version: 1,
255
+ format: "esm",
256
+ runtimeSource: module.runtimeSource,
257
+ typesSource: module.typesSource,
258
+ });
259
+ const body = new Blob([envelope], { type: "application/json" });
260
+ const expectedSizeBytes = body.size;
261
+ if (expectedSizeBytes <= 0 || expectedSizeBytes > MAX_ASSET_UPLOAD_BYTES) {
262
+ 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 });
263
+ }
264
+ const ticket = await this.request("create_deno_module_upload", {
265
+ sessionId,
266
+ moduleName: module.name,
267
+ expectedSizeBytes,
268
+ });
269
+ if (!isPlainObject(ticket) || typeof ticket.uploadUrl !== "string" || ticket.uploadUrl.length === 0 ||
270
+ typeof ticket.token !== "string" || ticket.token.length === 0) {
271
+ throw makeError("SDK_PROTOCOL_ERROR", "Deno Module upload ticket was invalid", {
272
+ moduleName: module.name,
273
+ });
274
+ }
275
+ let response;
276
+ try {
277
+ response = await fetch(ticket.uploadUrl, {
278
+ method: "PUT",
279
+ headers: {
280
+ Authorization: `Bearer ${ticket.token}`,
281
+ "Content-Type": "application/json",
282
+ },
283
+ body,
284
+ credentials: "omit",
285
+ });
286
+ }
287
+ catch (error) {
288
+ throw normalizeError(error, "DENO_MODULE_UPLOAD_FAILED", `Deno Module "${module.name}" upload failed`);
289
+ }
290
+ let payload = null;
291
+ try {
292
+ payload = await response.json();
293
+ }
294
+ catch {
295
+ // The status below remains authoritative for a successful response;
296
+ // failed responses without JSON receive the private fallback error.
297
+ }
298
+ if (!response.ok) {
299
+ const errorValue = isPlainObject(payload) ? payload.error : undefined;
300
+ throw normalizeError(errorValue, "DENO_MODULE_UPLOAD_FAILED", `Deno Module "${module.name}" upload failed`);
301
+ }
302
+ if (!isPlainObject(payload) || payload.ready !== true) {
303
+ throw makeError("DENO_MODULE_SETUP_FAILED", `Deno Module "${module.name}" was not materialized`, { moduleName: module.name });
304
+ }
305
+ }
306
+ async abortSessionSetup(sessionId) {
307
+ try {
308
+ await this.request("abort_session_setup", { sessionId });
309
+ }
310
+ catch {
311
+ // Rollback is deliberately best effort. The original setup/upload
312
+ // error is the only error exposed by createSession().
313
+ }
314
+ }
315
+ async completeSessionSetup(sessionId) {
316
+ await this.request("complete_session_setup", { sessionId });
317
+ }
158
318
  async workspaceFolderPicker() {
159
319
  const result = await this.requestWithOptions("pick_workspace_folder", {}, { timeoutMs: null });
160
320
  return normalizeWorkspaceFolderPickerResponse(result);
@@ -345,9 +505,7 @@ export class Pedelec {
345
505
  const raw = (input ?? {});
346
506
  const provider = typeof raw.provider === "string" ? raw.provider.trim() : "";
347
507
  const hasProvider = provider.length > 0;
348
- if (raw.model !== undefined) {
349
- throw makeError("INVALID_INPUT", "model is no longer supported; configure provider effort profiles in Desktop Settings");
350
- }
508
+ const model = normalizeCreateSessionModelInput(raw.model);
351
509
  const effortLevel = raw.effortLevel === undefined ? "default" : raw.effortLevel;
352
510
  if (!isEffortLevel(effortLevel)) {
353
511
  throw makeError("INVALID_INPUT", "effortLevel must be one of default, low, or high");
@@ -356,14 +514,16 @@ export class Pedelec {
356
514
  const normalizedSkills = normalizeSkillsInput(raw.skills);
357
515
  const workspace = normalizeCreateSessionWorkspaceInput(raw.workspace);
358
516
  if (!hasProvider) {
359
- return this.resolveDefaultCreateSessionInput(effortLevel, normalizedSkills, workspace, autoEndOnDisconnect);
517
+ return this.resolveDefaultCreateSessionInput(model, effortLevel, normalizedSkills, workspace, autoEndOnDisconnect);
360
518
  }
361
519
  return {
362
520
  provider: provider,
521
+ model,
363
522
  effortLevel,
364
523
  skills: normalizedSkills.manifest,
365
524
  workspace,
366
525
  inlineToolHandlers: normalizedSkills.handlers,
526
+ denoModules: normalizedSkills.denoModules,
367
527
  autoEndOnDisconnect,
368
528
  };
369
529
  }
@@ -419,7 +579,7 @@ export class Pedelec {
419
579
  };
420
580
  }
421
581
  }
422
- async resolveDefaultCreateSessionInput(effortLevel, normalizedSkills, workspace, autoEndOnDisconnect) {
582
+ async resolveDefaultCreateSessionInput(model, effortLevel, normalizedSkills, workspace, autoEndOnDisconnect) {
423
583
  const settings = await this.getSettings();
424
584
  if (!settings.defaultProvider) {
425
585
  throw makeError("DEFAULT_PROVIDER_NOT_SET", "Default provider is not set. Open the Pedelec desktop app Settings page and choose a default provider.");
@@ -427,10 +587,12 @@ export class Pedelec {
427
587
  await this.assertDefaultProviderAvailable(settings.defaultProvider);
428
588
  return {
429
589
  provider: settings.defaultProvider,
590
+ model,
430
591
  effortLevel,
431
592
  skills: normalizedSkills.manifest,
432
593
  workspace,
433
594
  inlineToolHandlers: normalizedSkills.handlers,
595
+ denoModules: normalizedSkills.denoModules,
434
596
  autoEndOnDisconnect,
435
597
  };
436
598
  }
@@ -593,6 +755,14 @@ function normalizeCreateSessionWorkspaceInput(value) {
593
755
  }
594
756
  return { path };
595
757
  }
758
+ function normalizeCreateSessionModelInput(value) {
759
+ if (value === undefined)
760
+ return undefined;
761
+ if (typeof value !== "string" || value.trim().length === 0) {
762
+ throw makeError("INVALID_INPUT", "model must be a non-empty string when provided");
763
+ }
764
+ return value.trim();
765
+ }
596
766
  export class PedelecSession {
597
767
  client;
598
768
  sessionId;
@@ -1504,6 +1674,10 @@ function isDeterministicResumeError(error) {
1504
1674
  error.code === "THREAD_NOT_FOUND" ||
1505
1675
  error.code === "WORKSPACE_OPEN_FAILED" ||
1506
1676
  error.code === "PROVIDER_COMMAND_FAILED" ||
1677
+ error.code === "DENO_MODULE_SETUP_INCOMPLETE" ||
1678
+ error.code === "DENO_MODULE_SETUP_FAILED" ||
1679
+ error.code === "DENO_MODULE_MATERIALIZATION_FAILED" ||
1680
+ error.code === "DENO_IMPORT_MAP_INVALID" ||
1507
1681
  error.code === "INVALID_INPUT";
1508
1682
  }
1509
1683
  function normalizeError(err, fallbackCode, fallbackMessage) {
@@ -1,4 +1,34 @@
1
- import { Pedelec, defineTool, } from "./index";
1
+ import { Pedelec, defineTool, defineDenoModule, } from "./index";
2
+ const spriteTools = defineDenoModule({
3
+ name: "sprite-tools",
4
+ description: "Sprite authoring utilities.",
5
+ entry: "./agent/sprite-tools.ts",
6
+ usage: `import { previewActorSource } from "sprite-tools";`,
7
+ preferStdinExecution: true,
8
+ });
9
+ const spriteName = spriteTools.name;
10
+ const spriteDefinition = spriteTools;
11
+ void spriteName;
12
+ void spriteDefinition;
13
+ // @ts-expect-error Deno Module usage is required by the public authoring API
14
+ defineDenoModule({
15
+ name: "missing-usage",
16
+ description: "Missing usage.",
17
+ entry: "./agent/missing-usage.ts",
18
+ });
19
+ // @ts-expect-error Deno Module entry remains required at authoring time
20
+ defineDenoModule({
21
+ name: "missing-entry",
22
+ description: "Missing entry.",
23
+ usage: 'import "missing-entry";',
24
+ });
25
+ defineDenoModule({
26
+ name: "non-string-entry",
27
+ description: "Invalid entry.",
28
+ // @ts-expect-error Deno Module entry is a build-time string authoring field
29
+ entry: 42,
30
+ usage: 'import "non-string-entry";',
31
+ });
2
32
  function workspaceInputHasPublicType() {
3
33
  return { path: "C:\\workspace\\project" };
4
34
  }
@@ -20,6 +50,7 @@ async function typedOnToolNameFromCreateSession() {
20
50
  argsSchema: { type: "object", properties: {}, required: [] },
21
51
  }),
22
52
  ],
53
+ denoModules: [spriteTools],
23
54
  },
24
55
  });
25
56
  session.onTool((name, args, ctx) => {
@@ -115,8 +146,8 @@ async function effortLevelPublicTypeContract() {
115
146
  pedelec.createSession({ provider: "codex", sandbox: { path: "C:\\workspace\\legacy" } });
116
147
  // @ts-expect-error the removed sandbox picker is not part of the public contract
117
148
  pedelec.sandboxFolderPicker();
118
- // @ts-expect-error model is no longer a createSession option
119
- pedelec.createSession({ provider: "codex", model: "gpt-5" });
149
+ await pedelec.createSession({ model: "gpt-5" });
150
+ await pedelec.createSession({ provider: "codex", model: "gpt-5", effortLevel: "high" });
120
151
  // @ts-expect-error session no longer exposes provider model
121
152
  session.model;
122
153
  // @ts-expect-error unsupported effort level
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.3.3";
1
+ export declare const SDK_VERSION = "0.3.4";
@@ -1,2 +1,2 @@
1
1
  // Generated from sdk/package.json. Do not edit manually.
2
- export const SDK_VERSION = "0.3.3";
2
+ export const SDK_VERSION = "0.3.4";
package/dist/vite.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import { type Plugin } from "vite";
2
+ /**
3
+ * Vite build-time support for the static Deno module authoring API.
4
+ *
5
+ * The plugin deliberately keeps the generated module inside the transformed
6
+ * browser module. The SDK can consume the non-enumerable private artifact in
7
+ * a later transport phase without giving the runtime the original entry path.
8
+ */
9
+ export declare function pedelecVitePlugin(): Plugin;