@kaoruisaac/pedelec 0.1.4 → 0.1.6
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 +89 -501
- package/dist/index.d.ts +26 -0
- package/dist/index.js +125 -1
- package/dist/index.type-test.js +17 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,50 +2,83 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@kaoruisaac/pedelec)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Documentation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- [Pedelec documentation](https://kaoruisaac.github.io/pedelec/)
|
|
8
|
+
- [GitHub repository](https://github.com/kaoruisaac/pedelec)
|
|
8
9
|
|
|
9
|
-
## Requirements
|
|
10
10
|
|
|
11
|
-
Pedelec SDK
|
|
11
|
+
Pedelec is a browser SDK and local bridge for applications that want to work with AI coding agents such as Codex, Gemini, OpenCode, Cursor, Claude Code, or an Ollama-backed agent.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
A web application can use Pedelec to:
|
|
14
14
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
15
|
+
- create an agent session on the user's machine;
|
|
16
|
+
- send user instructions and receive streamed assistant text;
|
|
17
|
+
- expose narrowly scoped browser-side tools to the agent;
|
|
18
|
+
- resume or end sessions; and
|
|
19
|
+
- upload and list completed sandbox assets; and
|
|
20
|
+
- show connection, approval, provider, and lifecycle state in the UI.
|
|
19
21
|
|
|
20
|
-
##
|
|
22
|
+
## Why Pedelec exists
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
A normal chat API accepts text and returns text. An agent integration often needs more:
|
|
25
|
+
|
|
26
|
+
- The agent may need to inspect the page, editor, canvas, selection, or application state.
|
|
27
|
+
- The application may need to ask the user for confirmation while an agent turn is paused.
|
|
28
|
+
- The user may want to use a provider CLI that is already installed and authenticated locally.
|
|
29
|
+
- The browser should not receive permission to launch arbitrary local processes directly.
|
|
30
|
+
|
|
31
|
+
Pedelec separates those responsibilities. Your web application owns the UI and the tool handlers. The Pedelec extension and desktop runtime own the local transport, session lifecycle, and provider process.
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
Web application
|
|
35
|
+
↓ @kaoruisaac/pedelec
|
|
36
|
+
Pedelec Chrome Extension
|
|
37
|
+
↓ Chrome Native Messaging
|
|
38
|
+
Pedelec native host
|
|
39
|
+
↓ local Core IPC
|
|
40
|
+
Pedelec Desktop Runtime
|
|
41
|
+
↓ provider process
|
|
42
|
+
Codex / Gemini / OpenCode / Cursor / Claude Code / Ollama
|
|
43
|
+
```
|
|
44
|
+
## SDK Prerequisites
|
|
45
|
+
|
|
46
|
+
The Pedelec SDK must run in a browser page environment and requires:
|
|
47
|
+
|
|
48
|
+
1. The user has installed the Pedelec Chrome Extension.
|
|
49
|
+
2. The user has started the Pedelec Desktop App.
|
|
50
|
+
3. The Desktop App has registered the Chrome Native Messaging host.
|
|
51
|
+
4. The target provider is available on the user's machine. CLI-backed providers use commands such as `codex`, `gemini`, `opencode`, `cursor`, or `claude`; the Ollama provider uses Pedelec's bundled `pedelec-agent`.
|
|
52
|
+
|
|
53
|
+
The SDK is not suitable for direct use in Node.js, an SSR server, or a background worker; it needs extension runtime messaging from a Chrome page environment.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installing and Importing the SDK
|
|
58
|
+
|
|
59
|
+
The SDK package is located in `sdk/`:
|
|
23
60
|
|
|
24
61
|
```bash
|
|
25
|
-
|
|
62
|
+
cd sdk
|
|
63
|
+
npm install
|
|
64
|
+
npm run build
|
|
26
65
|
```
|
|
27
66
|
|
|
28
|
-
Import
|
|
67
|
+
Import it from the Web App:
|
|
29
68
|
|
|
30
69
|
```ts
|
|
31
70
|
import { Pedelec, defineTool } from "@kaoruisaac/pedelec";
|
|
32
71
|
```
|
|
33
72
|
|
|
34
|
-
|
|
73
|
+
If it has not been published to npm yet, install it from a local path first:
|
|
35
74
|
|
|
36
75
|
```bash
|
|
37
76
|
npm install ../path/to/pedelec/sdk
|
|
38
77
|
```
|
|
39
78
|
|
|
40
|
-
|
|
79
|
+
---
|
|
41
80
|
|
|
42
|
-
|
|
43
|
-
cd sdk
|
|
44
|
-
npm install
|
|
45
|
-
npm run build
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Quick Start
|
|
81
|
+
## Minimal Example
|
|
49
82
|
|
|
50
83
|
```ts
|
|
51
84
|
import { Pedelec, defineTool } from "@kaoruisaac/pedelec";
|
|
@@ -66,175 +99,87 @@ const session = await pedelec.createSession({
|
|
|
66
99
|
properties: {},
|
|
67
100
|
required: [],
|
|
68
101
|
},
|
|
69
|
-
handler: (
|
|
102
|
+
handler: () => ({
|
|
70
103
|
url: location.href,
|
|
71
104
|
title: document.title,
|
|
72
|
-
turnId: ctx.turnId,
|
|
73
105
|
}),
|
|
74
106
|
}),
|
|
75
107
|
],
|
|
76
108
|
},
|
|
77
109
|
});
|
|
78
110
|
|
|
79
|
-
session.onChat((text
|
|
80
|
-
|
|
111
|
+
session.onChat((text) => {
|
|
112
|
+
// Incremental text stream from the agent.
|
|
113
|
+
console.log(text);
|
|
81
114
|
});
|
|
82
115
|
|
|
83
|
-
session.onStatus((status
|
|
84
|
-
|
|
116
|
+
session.onStatus((status) => {
|
|
117
|
+
// idle | running | waiting_tool_result | ended | error
|
|
118
|
+
console.log("status", status);
|
|
85
119
|
});
|
|
86
120
|
|
|
87
|
-
session.onError((error
|
|
88
|
-
console.error(
|
|
121
|
+
session.onError((error) => {
|
|
122
|
+
console.error(error.code, error.message, error.details);
|
|
89
123
|
});
|
|
90
124
|
|
|
91
|
-
await session.sendText("
|
|
125
|
+
await session.sendText("Please help me analyze the current page state");
|
|
92
126
|
```
|
|
93
127
|
|
|
94
|
-
`sendText()` resolves after the current agent
|
|
128
|
+
`sendText()` resolves after the current agent response completes. If the session is already handling a previous prompt, the new `sendText()` call is rejected to prevent multiple concurrent requests from running in the same session.
|
|
95
129
|
|
|
96
|
-
##
|
|
130
|
+
## Listing uploaded assets
|
|
97
131
|
|
|
98
132
|
```ts
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
});
|
|
133
|
+
const path = await session.uploadAsset(file);
|
|
134
|
+
const assets = await session.listAssets();
|
|
102
135
|
```
|
|
103
136
|
|
|
104
|
-
`
|
|
137
|
+
`listAssets()` lists only the first level of `input/`, ordered by filesystem modification time (newest first). Each item's `name` is the actual sandbox filename and may differ from the original `File.name`; `modifiedAt` is a filesystem modification timestamp, not an exact upload time. `path` is an agent-sandbox-relative `input/...` path and never an absolute local path. It may be called while the agent is running. Ended sessions reject with `SESSION_ENDED`. Recursive listing, pagination, download, deletion, rename, and move are not supported.
|
|
138
|
+
|
|
139
|
+
---
|
|
105
140
|
|
|
106
|
-
##
|
|
141
|
+
## Creating a Session
|
|
107
142
|
|
|
108
|
-
The
|
|
143
|
+
The first time an origin calls `createSession()` or `resumeSession()`, the extension asks the user to approve that origin in the popup. After approval, the same origin can create sessions directly.
|
|
144
|
+
|
|
145
|
+
You can query the current origin's approval status first to decide whether to show UI such as "Connect Pedelec":
|
|
109
146
|
|
|
110
147
|
```ts
|
|
111
148
|
const status = await pedelec.getApprovalStatus();
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
console.log("Pedelec extension is not available.");
|
|
115
|
-
} else if (!status.approved) {
|
|
116
|
-
console.log(`${status.origin} still needs approval.`);
|
|
117
|
-
}
|
|
150
|
+
console.log(status.installed, status.approved, status.origin);
|
|
118
151
|
```
|
|
119
152
|
|
|
120
|
-
|
|
153
|
+
For a complete Extension, approval, and Desktop readiness probe, use `checkAvailability()`. It never creates/resumes a session or opens approval. When approved it validates Desktop by calling `getSettings()`, which may invoke the existing Native Host auto-launch fallback.
|
|
121
154
|
|
|
122
155
|
```ts
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
approved: boolean;
|
|
126
|
-
origin: string | null;
|
|
127
|
-
};
|
|
156
|
+
const availability = await pedelec.checkAvailability();
|
|
157
|
+
if (availability.available) startUi();
|
|
128
158
|
```
|
|
129
159
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
## Providers
|
|
133
|
-
|
|
134
|
-
List available providers on the user's machine:
|
|
160
|
+
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.
|
|
135
161
|
|
|
136
|
-
|
|
137
|
-
const providers = await pedelec.listProviders();
|
|
138
|
-
|
|
139
|
-
for (const provider of providers) {
|
|
140
|
-
console.log(provider.code, provider.available, provider.path, provider.error);
|
|
141
|
-
}
|
|
142
|
-
```
|
|
162
|
+
### Specifying Provider and Model
|
|
143
163
|
|
|
144
164
|
```ts
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
code: ProviderCode;
|
|
150
|
-
path: string | null;
|
|
151
|
-
available: boolean;
|
|
152
|
-
error: string | null;
|
|
153
|
-
};
|
|
165
|
+
const session = await pedelec.createSession({
|
|
166
|
+
provider: "opencode",
|
|
167
|
+
model: "ollama/qwen2.5-coder:14b",
|
|
168
|
+
});
|
|
154
169
|
```
|
|
155
170
|
|
|
156
|
-
|
|
171
|
+
Currently supported provider codes in the SDK:
|
|
157
172
|
|
|
158
173
|
| Provider | Code | Example model |
|
|
159
174
|
| --- | --- | --- |
|
|
160
175
|
| Codex | `codex` | `gpt-5` |
|
|
161
|
-
| Gemini | `gemini` |
|
|
176
|
+
| Gemini | `gemini` | Any model ID supported by the provider |
|
|
162
177
|
| OpenCode | `opencode` | `ollama/qwen2.5-coder:14b` |
|
|
163
178
|
| Cursor | `cursor` | `gpt-5` |
|
|
164
179
|
| Claude Code | `claude` | `sonnet` |
|
|
165
180
|
| Ollama | `ollama` | `qwen3-14b-32k:latest` |
|
|
166
181
|
|
|
167
|
-
|
|
168
|
-
For Ollama, `available: true` only means Pedelec can find `pedelec-agent`; it does not mean the Ollama server is running or the selected model is installed.
|
|
169
|
-
|
|
170
|
-
## Settings
|
|
171
|
-
|
|
172
|
-
Read the Desktop App default provider and per-provider models:
|
|
173
|
-
|
|
174
|
-
```ts
|
|
175
|
-
const settings = await pedelec.getSettings();
|
|
176
|
-
|
|
177
|
-
console.log(settings.defaultProvider);
|
|
178
|
-
console.log(settings.defaultModels.codex);
|
|
179
|
-
console.log(settings.defaultModels.gemini);
|
|
180
|
-
console.log(settings.defaultModels.ollama);
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
type PedelecSettings = {
|
|
185
|
-
defaultProvider: ProviderCode | null;
|
|
186
|
-
defaultModels: Partial<Record<ProviderCode, string>>;
|
|
187
|
-
};
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
## Create Sessions
|
|
191
|
-
|
|
192
|
-
Use the Desktop App default provider:
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
const session = await pedelec.createSession();
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
Use default provider with skills:
|
|
199
|
-
|
|
200
|
-
```ts
|
|
201
|
-
const session = await pedelec.createSession({
|
|
202
|
-
skills: {
|
|
203
|
-
guidance: "Use update_counter when the user asks to change the counter.",
|
|
204
|
-
tools: [
|
|
205
|
-
defineTool({
|
|
206
|
-
name: "update_counter",
|
|
207
|
-
description: "Update the visible counter by delta.",
|
|
208
|
-
argsSchema: {
|
|
209
|
-
type: "object",
|
|
210
|
-
required: ["delta"],
|
|
211
|
-
properties: {
|
|
212
|
-
delta: {
|
|
213
|
-
type: "number",
|
|
214
|
-
description: "Counter delta.",
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
}),
|
|
219
|
-
],
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
If no default provider is configured, this rejects with `DEFAULT_PROVIDER_NOT_SET`. If the default provider is configured but unavailable, it rejects with `DEFAULT_PROVIDER_UNAVAILABLE`. If the default provider has its own default model, the SDK sends that model with the session request.
|
|
225
|
-
|
|
226
|
-
Use an explicit provider:
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
const session = await pedelec.createSession({
|
|
230
|
-
provider: "codex",
|
|
231
|
-
});
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
When only `provider` is passed, the SDK uses that provider's own Desktop App default model if one is configured. Otherwise, it sends the provider without a model and lets the provider CLI use its own default behavior.
|
|
235
|
-
Ollama is the exception: it requires a model, so provider-only Ollama sessions need `defaultModels.ollama` or they fail with `MODEL_REQUIRED`.
|
|
236
|
-
|
|
237
|
-
Use an explicit provider and model:
|
|
182
|
+
Ollama sessions are executed by the `pedelec-agent` binary bundled with the Desktop App, not by the `ollama` CLI. You still need to start the local Ollama server yourself and specify a model explicitly or configure `defaultModels.ollama` in Settings:
|
|
238
183
|
|
|
239
184
|
```ts
|
|
240
185
|
const session = await pedelec.createSession({
|
|
@@ -242,360 +187,3 @@ const session = await pedelec.createSession({
|
|
|
242
187
|
model: "qwen3-14b-32k:latest",
|
|
243
188
|
});
|
|
244
189
|
```
|
|
245
|
-
|
|
246
|
-
Ollama sessions are executed by the `pedelec-agent` binary bundled with the Desktop App, not by the `ollama` CLI. You still need to start the local Ollama server yourself. Ollama requires a model from the session input or `defaultModels.ollama`; otherwise the CoreRuntime returns `MODEL_REQUIRED`.
|
|
247
|
-
|
|
248
|
-
By default, SDK-created sessions are page-scoped. `autoEndOnDisconnect` defaults to `true`, so the Desktop thread is ended when the last SDK connection for that session disconnects, such as on page refresh or tab close. Keep the default for demo and page-scoped apps. Set `autoEndOnDisconnect: false` when you need to resume the same session after navigation or share it across pages:
|
|
249
|
-
|
|
250
|
-
```ts
|
|
251
|
-
const session = await pedelec.createSession({
|
|
252
|
-
provider: "codex",
|
|
253
|
-
autoEndOnDisconnect: false,
|
|
254
|
-
});
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
`model` cannot be provided without `provider`.
|
|
258
|
-
|
|
259
|
-
```ts
|
|
260
|
-
type CreateSessionInput =
|
|
261
|
-
| {
|
|
262
|
-
provider: ProviderCode;
|
|
263
|
-
model?: string;
|
|
264
|
-
skills?: SkillsInput;
|
|
265
|
-
autoEndOnDisconnect?: boolean;
|
|
266
|
-
}
|
|
267
|
-
| {
|
|
268
|
-
provider?: undefined;
|
|
269
|
-
skills?: SkillsInput;
|
|
270
|
-
autoEndOnDisconnect?: boolean;
|
|
271
|
-
};
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
## Stream Responses
|
|
275
|
-
|
|
276
|
-
`onChat()` receives text deltas. A delta is not guaranteed to be a sentence or paragraph, so UI code should append chunks.
|
|
277
|
-
|
|
278
|
-
```ts
|
|
279
|
-
let assistantText = "";
|
|
280
|
-
|
|
281
|
-
session.onChat((text, ctx) => {
|
|
282
|
-
console.debug("chat delta for turn", ctx.turnId, ctx.eventReceivedAt);
|
|
283
|
-
assistantText += text;
|
|
284
|
-
renderAssistantMessage(assistantText);
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
The returned function unsubscribes the handler:
|
|
289
|
-
|
|
290
|
-
```ts
|
|
291
|
-
const unsubscribe = session.onChat((text, ctx) => {
|
|
292
|
-
console.log(ctx.turnId, text);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
unsubscribe();
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Send Text
|
|
299
|
-
|
|
300
|
-
```ts
|
|
301
|
-
try {
|
|
302
|
-
await session.sendText("Summarize this workspace.");
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.error("send failed", error);
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
`sendText(text)` sets the session status to `running`, sends the prompt to the runtime, and resolves after a `done` or idle status event. It rejects if the session ends, errors, disconnects, or is already busy.
|
|
309
|
-
|
|
310
|
-
## Session Status
|
|
311
|
-
|
|
312
|
-
```ts
|
|
313
|
-
session.onStatus((status, ctx) => {
|
|
314
|
-
console.debug("status changed from", ctx.previousStatus, "to", ctx.status);
|
|
315
|
-
switch (status) {
|
|
316
|
-
case "idle":
|
|
317
|
-
break;
|
|
318
|
-
case "running":
|
|
319
|
-
break;
|
|
320
|
-
case "waiting_tool_result":
|
|
321
|
-
break;
|
|
322
|
-
case "ended":
|
|
323
|
-
break;
|
|
324
|
-
case "error":
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
console.log(session.getStatus());
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
```ts
|
|
333
|
-
type PedelecSessionStatus =
|
|
334
|
-
| "idle"
|
|
335
|
-
| "running"
|
|
336
|
-
| "waiting_tool_result"
|
|
337
|
-
| "ended"
|
|
338
|
-
| "error";
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
| Status | Meaning |
|
|
342
|
-
| --- | --- |
|
|
343
|
-
| `idle` | The session can accept another prompt. |
|
|
344
|
-
| `running` | The agent is processing user text. |
|
|
345
|
-
| `waiting_tool_result` | The agent requested a tool result from the web app. |
|
|
346
|
-
| `ended` | The session has ended and cannot receive more text. |
|
|
347
|
-
| `error` | The session entered an error state. |
|
|
348
|
-
|
|
349
|
-
## Tool Calling
|
|
350
|
-
|
|
351
|
-
Use `skills` to tell the agent what tools your web app can handle. `guidance` is high-level instruction for the agent, and `tools` is the typed list of callable frontend capabilities. Core automatically generates `tools.md` and per-tool spec artifacts inside the sandbox; SDK users do not write or host those files.
|
|
352
|
-
|
|
353
|
-
Inline handlers are registered locally and are not sent to Core:
|
|
354
|
-
|
|
355
|
-
```ts
|
|
356
|
-
const session = await pedelec.createSession({
|
|
357
|
-
provider: "codex",
|
|
358
|
-
skills: {
|
|
359
|
-
guidance: "Use get_current_page for browser context.",
|
|
360
|
-
tools: [
|
|
361
|
-
defineTool({
|
|
362
|
-
name: "get_current_page",
|
|
363
|
-
description: "Read the current browser page title and URL.",
|
|
364
|
-
argsSchema: {
|
|
365
|
-
type: "object",
|
|
366
|
-
properties: {},
|
|
367
|
-
required: [],
|
|
368
|
-
},
|
|
369
|
-
handler: (_args, ctx) => ({
|
|
370
|
-
url: location.href,
|
|
371
|
-
title: document.title,
|
|
372
|
-
selectedText: window.getSelection()?.toString() ?? "",
|
|
373
|
-
turnId: ctx.turnId,
|
|
374
|
-
}),
|
|
375
|
-
}),
|
|
376
|
-
],
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
You can also register handlers after session creation. A named `onTool(name, handler)` handler overrides an inline handler for the same tool:
|
|
382
|
-
|
|
383
|
-
```ts
|
|
384
|
-
const session = await pedelec.createSession({
|
|
385
|
-
provider: "codex",
|
|
386
|
-
skills: {
|
|
387
|
-
guidance: "Use update_counter when the user asks to change the counter.",
|
|
388
|
-
tools: [
|
|
389
|
-
defineTool({
|
|
390
|
-
name: "update_counter",
|
|
391
|
-
description: "Update the visible counter by delta.",
|
|
392
|
-
argsSchema: {
|
|
393
|
-
type: "object",
|
|
394
|
-
required: ["delta"],
|
|
395
|
-
properties: {
|
|
396
|
-
delta: {
|
|
397
|
-
type: "number",
|
|
398
|
-
description: "Counter delta.",
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
}),
|
|
403
|
-
],
|
|
404
|
-
},
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
session.onTool("update_counter", async (args, ctx) => {
|
|
408
|
-
const { delta } = args as { delta: number };
|
|
409
|
-
console.debug("tool call", ctx.toolRequestId, ctx.turnId);
|
|
410
|
-
counter.value += delta;
|
|
411
|
-
return {
|
|
412
|
-
counter: counter.value,
|
|
413
|
-
delta,
|
|
414
|
-
};
|
|
415
|
-
});
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
Tool results must be JSON-serializable. If no handler is registered, the SDK returns a `TOOL_HANDLER_NOT_FOUND` error result. If the handler throws, the SDK returns a `TOOL_HANDLER_ERROR` error result.
|
|
419
|
-
|
|
420
|
-
The generic form `session.onTool((tool, args, ctx) => ...)` remains available as a fallback handler.
|
|
421
|
-
|
|
422
|
-
### Event Context and UI Lifecycles
|
|
423
|
-
|
|
424
|
-
All user-facing event callbacks receive context metadata. The SDK provides `ctx.sessionId`, `ctx.provider`, `ctx.model`, `ctx.turnId`, `ctx.turnStartedAt`, `ctx.eventReceivedAt`, `ctx.eventEmittedAt`, and `ctx.source` where they apply. `turnId` is SDK-local metadata for one accepted `sendText()` turn; it is not sent to Core and should not be parsed.
|
|
425
|
-
|
|
426
|
-
Apps should still use their own UI lifecycle state to decide whether a late callback is stale:
|
|
427
|
-
|
|
428
|
-
```ts
|
|
429
|
-
let lifecycleId = 0;
|
|
430
|
-
|
|
431
|
-
function createWorldSession(session: PedelecSession) {
|
|
432
|
-
const generation = ++lifecycleId;
|
|
433
|
-
|
|
434
|
-
session.onTool("spawn_basic_shapes", async (args, ctx) => {
|
|
435
|
-
if (generation !== lifecycleId) {
|
|
436
|
-
return {
|
|
437
|
-
error: {
|
|
438
|
-
code: "STALE_TOOL_CALL",
|
|
439
|
-
message: "This tool call belongs to an older UI lifecycle.",
|
|
440
|
-
},
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
console.debug("handling tool", ctx.toolRequestId, ctx.turnId, ctx.turnStartedAt);
|
|
445
|
-
return spawnBasicShapes(args);
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
The SDK context helps you inspect which session and turn produced an event. It does not automatically know whether your current canvas, page, render mode, or world instance is still valid.
|
|
451
|
-
|
|
452
|
-
### Tool Args Schema
|
|
453
|
-
|
|
454
|
-
`defineTool` uses `argsSchema` to describe the tool arguments sent to the provider and agent. The root schema must be an object:
|
|
455
|
-
|
|
456
|
-
```ts
|
|
457
|
-
defineTool({
|
|
458
|
-
name: "update_counter",
|
|
459
|
-
description: "Update the visible counter by delta.",
|
|
460
|
-
argsSchema: {
|
|
461
|
-
type: "object",
|
|
462
|
-
required: ["delta"],
|
|
463
|
-
properties: {
|
|
464
|
-
delta: {
|
|
465
|
-
type: "number",
|
|
466
|
-
description: "Counter delta.",
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
});
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
`argsSchema` is the Pedelec Tool Args Schema subset, not full JSON Schema. It supports common `string`, `number`, `integer`, `boolean`, `array`, `object`, and `oneOf` nodes with fields such as `description`, `default`, `examples`, `enum`, `minimum`, `maximum`, `minItems`, `maxItems`, and `required`.
|
|
474
|
-
|
|
475
|
-
`default` is guidance for the agent; the SDK does not automatically fill missing argument values. Shorthand schemas such as `input: { delta: "number" }` are no longer supported.
|
|
476
|
-
|
|
477
|
-
The first version does not support `$defs`, `$ref`, `additionalProperties`, `exclusiveMinimum`, `exclusiveMaximum`, `multipleOf`, or `format`. Reuse schema fragments with TypeScript constants instead of JSON Schema references.
|
|
478
|
-
|
|
479
|
-
## Resume Sessions
|
|
480
|
-
|
|
481
|
-
If you have saved a `sessionId`, resume it later:
|
|
482
|
-
|
|
483
|
-
```ts
|
|
484
|
-
const session = await pedelec.resumeSession("thread_abc123");
|
|
485
|
-
|
|
486
|
-
session.onChat((text, ctx) => {
|
|
487
|
-
console.log(ctx.turnId, text);
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
await session.sendText("Continue the previous task.");
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
`resumeSession(sessionId)` rejects with `INVALID_INPUT` when `sessionId` is empty.
|
|
494
|
-
|
|
495
|
-
## End Sessions
|
|
496
|
-
|
|
497
|
-
```ts
|
|
498
|
-
await session.end();
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
After a session ends, future `sendText()` calls reject with `SESSION_ENDED`.
|
|
502
|
-
|
|
503
|
-
Listen for runtime-ended sessions:
|
|
504
|
-
|
|
505
|
-
```ts
|
|
506
|
-
session.onEnded((ctx) => {
|
|
507
|
-
console.log("session ended", ctx.source);
|
|
508
|
-
});
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
## Low-Level Requests
|
|
512
|
-
|
|
513
|
-
Most apps should use the typed methods above. `request<T>(type, payload)` is exposed for advanced integrations that need to call bridge operations directly.
|
|
514
|
-
|
|
515
|
-
```ts
|
|
516
|
-
const result = await pedelec.request<{ sessionId: string }>("create_session", {
|
|
517
|
-
input: {
|
|
518
|
-
provider: "codex",
|
|
519
|
-
skills: undefined,
|
|
520
|
-
autoEndOnDisconnect: true,
|
|
521
|
-
},
|
|
522
|
-
});
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
Low-level requests still use the SDK bridge timeout and reject with `PedelecError` objects.
|
|
526
|
-
|
|
527
|
-
## Error Handling
|
|
528
|
-
|
|
529
|
-
Register `onError()` for session-level errors and use `try/catch` around async SDK calls.
|
|
530
|
-
|
|
531
|
-
```ts
|
|
532
|
-
session.onError((error, ctx) => {
|
|
533
|
-
console.error("session error", ctx.type, error.code, error.message, error.details);
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
try {
|
|
537
|
-
await session.sendText("Update the selected content.");
|
|
538
|
-
} catch (error) {
|
|
539
|
-
console.error("request failed", error);
|
|
540
|
-
}
|
|
541
|
-
```
|
|
542
|
-
|
|
543
|
-
```ts
|
|
544
|
-
type PedelecError = {
|
|
545
|
-
code: string;
|
|
546
|
-
message: string;
|
|
547
|
-
details?: unknown;
|
|
548
|
-
};
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
Common error codes:
|
|
552
|
-
|
|
553
|
-
| Code | Meaning |
|
|
554
|
-
| --- | --- |
|
|
555
|
-
| `EXTENSION_UNAVAILABLE` | The SDK is not running in a supported browser page, or the extension cannot be reached. |
|
|
556
|
-
| `EXTENSION_DISCONNECTED` | The extension connection was interrupted. |
|
|
557
|
-
| `SDK_BRIDGE_TIMEOUT` | The extension did not respond before `bridgeTimeoutMs`. |
|
|
558
|
-
| `SDK_PROTOCOL_ERROR` | The extension response did not match the SDK protocol. |
|
|
559
|
-
| `SDK_TRANSPORT_ERROR` | A bridge request failed at the transport layer. |
|
|
560
|
-
| `APPROVAL_REJECTED` | The user rejected approval for the current origin. |
|
|
561
|
-
| `APPROVAL_TIMEOUT` | The user did not complete origin approval in time. |
|
|
562
|
-
| `OPEN_POPUP_FAILED` | The extension could not open the approval popup. |
|
|
563
|
-
| `NATIVE_HOST_UNAVAILABLE` | Chrome Native Messaging host cannot be reached. |
|
|
564
|
-
| `DEFAULT_PROVIDER_NOT_SET` | The Desktop App has no default provider configured. |
|
|
565
|
-
| `DEFAULT_PROVIDER_UNAVAILABLE` | The configured default provider is not currently available. |
|
|
566
|
-
| `INVALID_INPUT` | The request input is invalid, such as model without provider or empty session id. |
|
|
567
|
-
| `SESSION_BUSY` | The session already has a prompt in progress. |
|
|
568
|
-
| `SESSION_ENDED` | The session has ended. |
|
|
569
|
-
| `SESSION_ERROR` | The runtime reported a session error. |
|
|
570
|
-
| `TOOL_HANDLER_NOT_FOUND` | The agent called a tool but no web app handler is registered. |
|
|
571
|
-
| `TOOL_HANDLER_ERROR` | The web app tool handler threw an error. |
|
|
572
|
-
| `SUBMIT_TOOL_RESULT_FAILED` | The SDK could not submit a tool result back to the runtime. |
|
|
573
|
-
|
|
574
|
-
## Public API Summary
|
|
575
|
-
|
|
576
|
-
```ts
|
|
577
|
-
class Pedelec {
|
|
578
|
-
constructor(options?: { bridgeTimeoutMs?: number });
|
|
579
|
-
createSession(input?: CreateSessionInput): Promise<PedelecSession>;
|
|
580
|
-
listProviders(): Promise<ProviderInfo[]>;
|
|
581
|
-
getSettings(): Promise<PedelecSettings>;
|
|
582
|
-
getApprovalStatus(): Promise<ApprovalStatus>;
|
|
583
|
-
resumeSession(sessionId: string): Promise<PedelecSession>;
|
|
584
|
-
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
class PedelecSession {
|
|
588
|
-
readonly sessionId: string;
|
|
589
|
-
readonly provider: string;
|
|
590
|
-
readonly model?: string;
|
|
591
|
-
|
|
592
|
-
sendText(text: string): Promise<void>;
|
|
593
|
-
onChat(handler: (text: string, ctx: ChatEventContext) => void): () => void;
|
|
594
|
-
onTool(handler: (tool: string, args: unknown, ctx: ToolCallContext) => unknown | Promise<unknown>): () => void;
|
|
595
|
-
onError(handler: (error: PedelecError, ctx: ErrorEventContext) => void): () => void;
|
|
596
|
-
onStatus(handler: (status: PedelecSessionStatus, ctx: StatusEventContext) => void): () => void;
|
|
597
|
-
onEnded(handler: (ctx: EndedEventContext) => void): () => void;
|
|
598
|
-
getStatus(): PedelecSessionStatus;
|
|
599
|
-
end(): Promise<void>;
|
|
600
|
-
}
|
|
601
|
-
```
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export type JsonPrimitive = string | number | boolean | null;
|
|
|
6
6
|
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
7
7
|
[key: string]: JsonValue;
|
|
8
8
|
};
|
|
9
|
+
export type SandboxAssetPath = `input/${string}`;
|
|
10
|
+
export type SandboxAsset = {
|
|
11
|
+
name: string;
|
|
12
|
+
path: SandboxAssetPath;
|
|
13
|
+
sizeBytes: number;
|
|
14
|
+
modifiedAt: number;
|
|
15
|
+
};
|
|
9
16
|
export type ToolArgsSchemaMeta<TDefault extends JsonValue = JsonValue> = {
|
|
10
17
|
description?: string;
|
|
11
18
|
default?: TDefault;
|
|
@@ -103,6 +110,21 @@ export type ApprovalStatus = {
|
|
|
103
110
|
approved: boolean;
|
|
104
111
|
origin: string | null;
|
|
105
112
|
};
|
|
113
|
+
export type PedelecAvailability = {
|
|
114
|
+
available: boolean;
|
|
115
|
+
extension: {
|
|
116
|
+
available: boolean;
|
|
117
|
+
};
|
|
118
|
+
approval: {
|
|
119
|
+
approved: boolean;
|
|
120
|
+
origin: string | null;
|
|
121
|
+
};
|
|
122
|
+
desktop: {
|
|
123
|
+
available: boolean;
|
|
124
|
+
launchAttempted: boolean;
|
|
125
|
+
};
|
|
126
|
+
error?: PedelecError;
|
|
127
|
+
};
|
|
106
128
|
export type PedelecError = {
|
|
107
129
|
code: string;
|
|
108
130
|
message: string;
|
|
@@ -213,6 +235,7 @@ export declare class Pedelec {
|
|
|
213
235
|
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
214
236
|
unregisterSession(sessionId: string): void;
|
|
215
237
|
private resolveCreateSessionInput;
|
|
238
|
+
checkAvailability(): Promise<PedelecAvailability>;
|
|
216
239
|
private resolveDefaultCreateSessionInput;
|
|
217
240
|
private resolveProviderOnlyCreateSessionInput;
|
|
218
241
|
private assertDefaultProviderAvailable;
|
|
@@ -234,6 +257,7 @@ export declare class PedelecSession<TToolName extends string = string> {
|
|
|
234
257
|
private pendingSend;
|
|
235
258
|
private pendingPrepare;
|
|
236
259
|
private preparePromise;
|
|
260
|
+
private uploadPromise;
|
|
237
261
|
private prepared;
|
|
238
262
|
private activeTurn;
|
|
239
263
|
private genericToolHandler;
|
|
@@ -249,6 +273,8 @@ export declare class PedelecSession<TToolName extends string = string> {
|
|
|
249
273
|
onChat(handler: ChatHandler): () => void;
|
|
250
274
|
onTool(handler: GenericToolHandler<TToolName>): () => void;
|
|
251
275
|
onTool<TArgs = unknown, TResult = unknown>(toolName: TToolName, handler: ToolSpecificHandler<TArgs, TResult>): () => void;
|
|
276
|
+
uploadAsset(file: File): Promise<SandboxAssetPath>;
|
|
277
|
+
listAssets(): Promise<SandboxAsset[]>;
|
|
252
278
|
onError(handler: ErrorHandler): () => void;
|
|
253
279
|
onStatus(handler: StatusHandler): () => void;
|
|
254
280
|
onEnded(handler: EndedHandler): () => void;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { PEDELEC_EXTENSION_ID } from "./extension-id.js";
|
|
2
2
|
const SDK_EXTERNAL_PORT_NAME = "pedelec-sdk-external";
|
|
3
3
|
const DEFAULT_BRIDGE_TIMEOUT_MS = 30_000;
|
|
4
|
+
function normalizeListAssetsResponse(response) {
|
|
5
|
+
if (!response || typeof response !== "object" || Array.isArray(response))
|
|
6
|
+
return invalidListAssetsResponse(response);
|
|
7
|
+
const assets = response.assets;
|
|
8
|
+
if (!Array.isArray(assets))
|
|
9
|
+
return invalidListAssetsResponse(response);
|
|
10
|
+
return assets.map((asset, index) => {
|
|
11
|
+
if (!asset || typeof asset !== "object" || Array.isArray(asset))
|
|
12
|
+
return invalidListAssetsResponse({ index, asset });
|
|
13
|
+
const { name, path, sizeBytes, modifiedAt } = asset;
|
|
14
|
+
const validName = typeof name === "string" && name.length > 0 && name !== "." && name !== ".." && !name.includes("/") && !name.includes("\\");
|
|
15
|
+
const validPath = typeof path === "string" && !path.includes("\\") && path === `input/${name}` && !path.split("/").includes("..");
|
|
16
|
+
const validNumber = (value) => typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0;
|
|
17
|
+
if (!validName || !validPath || !validNumber(sizeBytes) || !validNumber(modifiedAt)) {
|
|
18
|
+
return invalidListAssetsResponse({ index, asset });
|
|
19
|
+
}
|
|
20
|
+
return { name, path: path, sizeBytes: sizeBytes, modifiedAt: modifiedAt };
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function invalidListAssetsResponse(details) {
|
|
24
|
+
throw makeError("SDK_PROTOCOL_ERROR", "list_assets response had an invalid shape", { response: details });
|
|
25
|
+
}
|
|
26
|
+
const MAX_ASSET_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
4
27
|
const TOOL_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_.-]*$/;
|
|
5
28
|
export function defineTool(tool) {
|
|
6
29
|
return tool;
|
|
@@ -241,6 +264,58 @@ export class Pedelec {
|
|
|
241
264
|
autoEndOnDisconnect,
|
|
242
265
|
};
|
|
243
266
|
}
|
|
267
|
+
async checkAvailability() {
|
|
268
|
+
const origin = getCurrentOrigin(this.pageWindow);
|
|
269
|
+
let approval;
|
|
270
|
+
try {
|
|
271
|
+
approval = await this.getApprovalStatus();
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
const error = normalizeError(err, "SDK_TRANSPORT_ERROR", "Could not read Pedelec approval status.");
|
|
275
|
+
return {
|
|
276
|
+
available: false,
|
|
277
|
+
extension: { available: !isExtensionUnavailableError(error) },
|
|
278
|
+
approval: { approved: false, origin },
|
|
279
|
+
desktop: { available: false, launchAttempted: false },
|
|
280
|
+
error,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
if (!approval.installed) {
|
|
284
|
+
return {
|
|
285
|
+
available: false,
|
|
286
|
+
extension: { available: false },
|
|
287
|
+
approval: { approved: false, origin: approval.origin ?? origin },
|
|
288
|
+
desktop: { available: false, launchAttempted: false },
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (!approval.approved) {
|
|
292
|
+
return {
|
|
293
|
+
available: false,
|
|
294
|
+
extension: { available: true },
|
|
295
|
+
approval: { approved: false, origin: approval.origin },
|
|
296
|
+
desktop: { available: false, launchAttempted: false },
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
try {
|
|
300
|
+
await this.getSettings();
|
|
301
|
+
return {
|
|
302
|
+
available: true,
|
|
303
|
+
extension: { available: true },
|
|
304
|
+
approval: { approved: true, origin: approval.origin },
|
|
305
|
+
desktop: { available: true, launchAttempted: true },
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
const error = normalizeError(err, "SDK_TRANSPORT_ERROR", "Could not reach Pedelec Desktop.");
|
|
310
|
+
return {
|
|
311
|
+
available: false,
|
|
312
|
+
extension: { available: true },
|
|
313
|
+
approval: { approved: true, origin: approval.origin },
|
|
314
|
+
desktop: { available: false, launchAttempted: true },
|
|
315
|
+
error,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
244
319
|
async resolveDefaultCreateSessionInput(normalizedSkills, autoEndOnDisconnect) {
|
|
245
320
|
const settings = await this.getSettings();
|
|
246
321
|
if (!settings.defaultProvider) {
|
|
@@ -372,6 +447,7 @@ export class PedelecSession {
|
|
|
372
447
|
pendingSend = null;
|
|
373
448
|
pendingPrepare = null;
|
|
374
449
|
preparePromise = null;
|
|
450
|
+
uploadPromise = null;
|
|
375
451
|
prepared = false;
|
|
376
452
|
activeTurn = null;
|
|
377
453
|
genericToolHandler = null;
|
|
@@ -392,7 +468,7 @@ export class PedelecSession {
|
|
|
392
468
|
if (this.status === "ended") {
|
|
393
469
|
return Promise.reject(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
394
470
|
}
|
|
395
|
-
if (this.pendingSend) {
|
|
471
|
+
if (this.pendingSend || this.uploadPromise) {
|
|
396
472
|
return Promise.reject(makeError("SESSION_BUSY", "session is already running", { sessionId: this.sessionId }));
|
|
397
473
|
}
|
|
398
474
|
if (this.prepared) {
|
|
@@ -428,6 +504,9 @@ export class PedelecSession {
|
|
|
428
504
|
return this.preparePromise;
|
|
429
505
|
}
|
|
430
506
|
async sendText(text) {
|
|
507
|
+
if (this.uploadPromise) {
|
|
508
|
+
return Promise.reject(makeError("SESSION_BUSY", "an asset upload is in progress", { sessionId: this.sessionId }));
|
|
509
|
+
}
|
|
431
510
|
if (this.preparePromise) {
|
|
432
511
|
try {
|
|
433
512
|
await this.preparePromise;
|
|
@@ -490,6 +569,48 @@ export class PedelecSession {
|
|
|
490
569
|
}
|
|
491
570
|
};
|
|
492
571
|
}
|
|
572
|
+
uploadAsset(file) {
|
|
573
|
+
if (this.status === "ended")
|
|
574
|
+
return Promise.reject(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
575
|
+
if (this.pendingSend || this.pendingPrepare || this.preparePromise || this.uploadPromise || this.status !== "idle") {
|
|
576
|
+
return Promise.reject(makeError("SESSION_BUSY", "session is busy", { sessionId: this.sessionId }));
|
|
577
|
+
}
|
|
578
|
+
if (typeof File === "undefined" || !(file instanceof File) || !file.name || file.size < 0) {
|
|
579
|
+
return Promise.reject(makeError("INVALID_INPUT", "uploadAsset requires a browser File with a filename"));
|
|
580
|
+
}
|
|
581
|
+
if (file.size > MAX_ASSET_UPLOAD_BYTES)
|
|
582
|
+
return Promise.reject(makeError("ASSET_TOO_LARGE", "asset exceeds the 100 MiB limit"));
|
|
583
|
+
this.uploadPromise = this.client.request("create_asset_upload", {
|
|
584
|
+
sessionId: this.sessionId, filename: file.name, sizeBytes: file.size, mimeType: file.type,
|
|
585
|
+
}).then(async (ticket) => {
|
|
586
|
+
if (!ticket?.uploadUrl || !ticket.token)
|
|
587
|
+
throw makeError("SDK_PROTOCOL_ERROR", "asset upload ticket was invalid");
|
|
588
|
+
let response;
|
|
589
|
+
try {
|
|
590
|
+
response = await fetch(ticket.uploadUrl, { method: "PUT", headers: { Authorization: `Bearer ${ticket.token}`, "Content-Type": file.type || "application/octet-stream" }, body: file, credentials: "omit" });
|
|
591
|
+
}
|
|
592
|
+
catch (error) {
|
|
593
|
+
throw normalizeError(error, "ASSET_UPLOAD_FAILED", "asset upload failed");
|
|
594
|
+
}
|
|
595
|
+
let payload = null;
|
|
596
|
+
try {
|
|
597
|
+
payload = await response.json();
|
|
598
|
+
}
|
|
599
|
+
catch { /* normalized below */ }
|
|
600
|
+
if (!response.ok)
|
|
601
|
+
throw normalizeError(payload?.error, "ASSET_UPLOAD_FAILED", "asset upload failed");
|
|
602
|
+
if (typeof payload?.path !== "string" || !payload.path.startsWith("input/") || payload.path.includes(".."))
|
|
603
|
+
throw makeError("SDK_PROTOCOL_ERROR", "asset upload response had an invalid path");
|
|
604
|
+
return payload.path;
|
|
605
|
+
}).finally(() => { this.uploadPromise = null; });
|
|
606
|
+
return this.uploadPromise;
|
|
607
|
+
}
|
|
608
|
+
listAssets() {
|
|
609
|
+
if (this.status === "ended") {
|
|
610
|
+
return Promise.reject(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
611
|
+
}
|
|
612
|
+
return this.client.request("list_assets", { sessionId: this.sessionId }).then(normalizeListAssetsResponse);
|
|
613
|
+
}
|
|
493
614
|
onError(handler) {
|
|
494
615
|
this.errorHandlers.add(handler);
|
|
495
616
|
return () => this.errorHandlers.delete(handler);
|
|
@@ -839,6 +960,9 @@ function getCurrentOrigin(pageWindow) {
|
|
|
839
960
|
const origin = pageWindow?.location?.origin;
|
|
840
961
|
return typeof origin === "string" && origin ? origin : null;
|
|
841
962
|
}
|
|
963
|
+
function isExtensionUnavailableError(error) {
|
|
964
|
+
return error.code === "EXTENSION_UNAVAILABLE" || error.code === "EXTENSION_DISCONNECTED";
|
|
965
|
+
}
|
|
842
966
|
function normalizeError(err, fallbackCode, fallbackMessage) {
|
|
843
967
|
if (!err)
|
|
844
968
|
return makeError(fallbackCode, fallbackMessage);
|
package/dist/index.type-test.js
CHANGED
|
@@ -88,9 +88,26 @@ async function noSkillsFallsBackToString() {
|
|
|
88
88
|
return { ok: true };
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
+
async function listAssetsHasPublicTypes() {
|
|
92
|
+
const pedelec = new Pedelec();
|
|
93
|
+
const session = await pedelec.resumeSession("thread_1");
|
|
94
|
+
const assets = await session.listAssets();
|
|
95
|
+
assets;
|
|
96
|
+
const path = assets[0].path;
|
|
97
|
+
path;
|
|
98
|
+
}
|
|
99
|
+
async function availabilityHasPublicType() {
|
|
100
|
+
const pedelec = new Pedelec();
|
|
101
|
+
const availability = await pedelec.checkAvailability();
|
|
102
|
+
const promise = pedelec.checkAvailability();
|
|
103
|
+
void availability;
|
|
104
|
+
void promise;
|
|
105
|
+
}
|
|
91
106
|
void typedOnToolNameFromCreateSession;
|
|
92
107
|
void resumedSessionFallsBackToString;
|
|
93
108
|
void noSkillsFallsBackToString;
|
|
109
|
+
void listAssetsHasPublicTypes;
|
|
110
|
+
void availabilityHasPublicType;
|
|
94
111
|
const baseContext = {
|
|
95
112
|
sessionId: "thread_1",
|
|
96
113
|
provider: "codex",
|
package/package.json
CHANGED