@kaoruisaac/pedelec 0.2.11 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -13
- package/dist/index.d.ts +118 -28
- package/dist/index.js +516 -114
- package/dist/index.type-test.js +23 -8
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ A web application can use Pedelec to:
|
|
|
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
|
|
31
|
-
- upload and list completed
|
|
31
|
+
- upload and list completed workspace assets; and
|
|
32
32
|
- show connection, approval, provider, and lifecycle state in the UI.
|
|
33
33
|
|
|
34
34
|
## Why Pedelec exists
|
|
@@ -121,10 +121,15 @@ const session = await pedelec.createSession({
|
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
session.onChat((text) => {
|
|
124
|
-
//
|
|
124
|
+
// One completed logical assistant message.
|
|
125
125
|
console.log(text);
|
|
126
126
|
});
|
|
127
127
|
|
|
128
|
+
session.onChatDelta((delta) => {
|
|
129
|
+
// Optional best-effort incremental text for live UI.
|
|
130
|
+
console.log(delta);
|
|
131
|
+
});
|
|
132
|
+
|
|
128
133
|
session.onStatus((status) => {
|
|
129
134
|
// idle | running | waiting_tool_result | ended | error
|
|
130
135
|
console.log("status", status);
|
|
@@ -137,7 +142,19 @@ session.onError((error) => {
|
|
|
137
142
|
await session.sendText("Please help me analyze the current page state");
|
|
138
143
|
```
|
|
139
144
|
|
|
140
|
-
`sendText()` resolves after the
|
|
145
|
+
`sendText()` resolves after Core reports the matching semantic operation as completed. An `idle` status or bridge request response alone does not complete it. 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.
|
|
146
|
+
|
|
147
|
+
## End and resume a session
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
await session.end();
|
|
151
|
+
await session.resume();
|
|
152
|
+
|
|
153
|
+
console.log(session.getStatus()); // "idle"
|
|
154
|
+
await session.sendText("Continue");
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`session.resume()` reactivates the same non-detached handle and Core thread when Core still knows the thread and its recorded workspace still exists. It preserves handlers and session state, does not contact the provider runtime, and leaves provider startup/resume lazy until the next operation. A missing workspace returns `WORKSPACE_OPEN_FAILED`; a thread lost after a Desktop/Core restart returns `THREAD_NOT_FOUND`. `pedelec.resumeSession(sessionId)` is a separate reattachment API: it returns a new handle and does not implicitly revive an ended thread. A transport-detached old handle must use that API.
|
|
141
158
|
|
|
142
159
|
## Session assets
|
|
143
160
|
|
|
@@ -146,7 +163,7 @@ const path = await session.uploadAsset(file);
|
|
|
146
163
|
const assets = await session.listAssets();
|
|
147
164
|
```
|
|
148
165
|
|
|
149
|
-
The physical shared App/Agent directory is `.pedelec-
|
|
166
|
+
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.
|
|
150
167
|
|
|
151
168
|
```ts
|
|
152
169
|
const text = await session.readAsset("/report.txt", "text");
|
|
@@ -156,37 +173,39 @@ const result = await session.readAsset("/model.glb", "file");
|
|
|
156
173
|
|
|
157
174
|
Public asset paths use `/...` with `assets/` as their implicit root; nested paths such as `/results/model.glb` are supported up to 100 MiB.
|
|
158
175
|
|
|
159
|
-
##
|
|
176
|
+
## Workspace
|
|
177
|
+
|
|
178
|
+
Workspace is the filesystem root in which the Agent works. Pedelec stores its private runtime data under `<workspace>/.pedelec-runtime/`.
|
|
160
179
|
|
|
161
|
-
By default, each session receives a temporary Desktop-managed
|
|
180
|
+
By default, each session receives a temporary Desktop-managed workspace. To use an application-owned workspace that can persist across sessions, pass an absolute path:
|
|
162
181
|
|
|
163
182
|
```ts
|
|
164
183
|
const session = await pedelec.createSession({
|
|
165
184
|
provider: "codex",
|
|
166
|
-
|
|
185
|
+
workspace: { path: "C:\\workspace\\project-a" },
|
|
167
186
|
});
|
|
168
187
|
```
|
|
169
188
|
|
|
170
|
-
Pedelec creates `.pedelec-
|
|
189
|
+
Pedelec creates `.pedelec-runtime/` with missing `assets/`, `logs/`, `skills/`, and `tmp/` subdirectories, preserving existing project files and existing private data. It never deletes an explicit workspace, and multiple active sessions may share it. Filesystem write conflicts are the application's responsibility. An explicit path must not overlap Pedelec's managed workspace root.
|
|
171
190
|
|
|
172
191
|
## Selecting an Application Workspace
|
|
173
192
|
|
|
174
|
-
Use `
|
|
193
|
+
Use `workspaceFolderPicker()` when the application wants the user to choose a local workspace before creating a session:
|
|
175
194
|
|
|
176
195
|
```ts
|
|
177
|
-
const folder = await pedelec.
|
|
196
|
+
const folder = await pedelec.workspaceFolderPicker();
|
|
178
197
|
if (!folder) return; // The user cancelled the native picker.
|
|
179
198
|
|
|
180
|
-
if (!folder.isEmptyFolder && !folder.
|
|
199
|
+
if (!folder.isEmptyFolder && !folder.hasWorkspaceConfig) {
|
|
181
200
|
// The application decides whether to warn the user.
|
|
182
201
|
}
|
|
183
202
|
|
|
184
203
|
const session = await pedelec.createSession({
|
|
185
|
-
|
|
204
|
+
workspace: { path: folder.path },
|
|
186
205
|
});
|
|
187
206
|
```
|
|
188
207
|
|
|
189
|
-
`
|
|
208
|
+
`workspaceFolderPicker()` returns `Promise<WorkspaceFolderPickerResult | null>`. It is a read-only snapshot: it does not validate or initialize the selected folder, create workspace directories, or create the workspace marker. `isEmptyFolder` only reports whether the selected folder root has any filesystem entry. `hasWorkspaceConfig` is true only when `.pedelec-workspace.json` exists as a regular file; its JSON is not parsed. The picker requires the same origin approval as other sensitive Desktop APIs, and cancellation returns `null`. `createSession()` still applies the full workspace rules and creates the marker after successful custom workspace initialization. The marker currently records the SDK version and normalized caller origin but does not restrict reuse by origin or SDK version.
|
|
190
209
|
|
|
191
210
|
---
|
|
192
211
|
|
package/dist/index.d.ts
CHANGED
|
@@ -7,11 +7,11 @@ export type JsonPrimitive = string | number | boolean | null;
|
|
|
7
7
|
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
8
8
|
[key: string]: JsonValue;
|
|
9
9
|
};
|
|
10
|
-
export type
|
|
10
|
+
export type AssetPath = `/${string}`;
|
|
11
11
|
export type ReadAssetType = "text" | "json" | "file";
|
|
12
|
-
export type
|
|
12
|
+
export type Asset = {
|
|
13
13
|
name: string;
|
|
14
|
-
path:
|
|
14
|
+
path: AssetPath;
|
|
15
15
|
sizeBytes: number;
|
|
16
16
|
modifiedAt: number;
|
|
17
17
|
};
|
|
@@ -83,26 +83,26 @@ export type SerializableSkillsManifest = {
|
|
|
83
83
|
guidance: string;
|
|
84
84
|
tools: SerializableToolManifest[];
|
|
85
85
|
};
|
|
86
|
-
export type
|
|
86
|
+
export type CreateSessionWorkspaceInput = {
|
|
87
87
|
path: string;
|
|
88
88
|
};
|
|
89
|
-
export interface
|
|
89
|
+
export interface WorkspaceFolderPickerResult {
|
|
90
90
|
path: string;
|
|
91
91
|
isEmptyFolder: boolean;
|
|
92
|
-
|
|
92
|
+
hasWorkspaceConfig: boolean;
|
|
93
93
|
}
|
|
94
94
|
type CreateSessionInputWithProvider<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
95
95
|
provider: ProviderCode;
|
|
96
96
|
effortLevel?: EffortLevel;
|
|
97
97
|
skills?: SkillsInput<TTools>;
|
|
98
|
-
|
|
98
|
+
workspace?: CreateSessionWorkspaceInput;
|
|
99
99
|
autoEndOnDisconnect?: boolean;
|
|
100
100
|
};
|
|
101
101
|
type CreateSessionInputWithDefaults<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
102
102
|
provider?: undefined;
|
|
103
103
|
effortLevel?: EffortLevel;
|
|
104
104
|
skills?: SkillsInput<TTools>;
|
|
105
|
-
|
|
105
|
+
workspace?: CreateSessionWorkspaceInput;
|
|
106
106
|
autoEndOnDisconnect?: boolean;
|
|
107
107
|
};
|
|
108
108
|
export type CreateSessionInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = CreateSessionInputWithProvider<TTools> | CreateSessionInputWithDefaults<TTools>;
|
|
@@ -142,6 +142,9 @@ export type PedelecError = {
|
|
|
142
142
|
details?: unknown;
|
|
143
143
|
};
|
|
144
144
|
export type PedelecSessionStatus = "idle" | "running" | "waiting_tool_result" | "ended" | "error";
|
|
145
|
+
export type PedelecSessionUsage = {
|
|
146
|
+
totalTokens?: number;
|
|
147
|
+
};
|
|
145
148
|
export type PedelecEventContext = {
|
|
146
149
|
sessionId: string;
|
|
147
150
|
provider: string;
|
|
@@ -155,6 +158,12 @@ export type PedelecEventContext = {
|
|
|
155
158
|
source: "core" | "sdk";
|
|
156
159
|
};
|
|
157
160
|
export type ChatEventContext = PedelecEventContext & {
|
|
161
|
+
type: "chat_message";
|
|
162
|
+
turnId: string;
|
|
163
|
+
turnStartedAt: number;
|
|
164
|
+
eventReceivedAt: number;
|
|
165
|
+
};
|
|
166
|
+
export type ChatDeltaEventContext = PedelecEventContext & {
|
|
158
167
|
type: "chat_delta";
|
|
159
168
|
turnId: string;
|
|
160
169
|
turnStartedAt: number;
|
|
@@ -184,31 +193,52 @@ type SessionEvent = {
|
|
|
184
193
|
channelId: string;
|
|
185
194
|
sessionId: string;
|
|
186
195
|
seq?: number;
|
|
196
|
+
operationId?: string;
|
|
197
|
+
text: string;
|
|
198
|
+
} | {
|
|
199
|
+
type: "chat_message";
|
|
200
|
+
channelId: string;
|
|
201
|
+
sessionId: string;
|
|
202
|
+
seq?: number;
|
|
203
|
+
operationId?: string;
|
|
187
204
|
text: string;
|
|
188
205
|
} | {
|
|
189
206
|
type: "status_changed";
|
|
190
207
|
channelId: string;
|
|
191
208
|
sessionId: string;
|
|
192
209
|
seq?: number;
|
|
210
|
+
operationId?: string;
|
|
193
211
|
status: PedelecSessionStatus;
|
|
194
212
|
} | {
|
|
195
213
|
type: "tool_call";
|
|
196
214
|
channelId: string;
|
|
197
215
|
sessionId: string;
|
|
198
216
|
seq?: number;
|
|
217
|
+
operationId: string;
|
|
199
218
|
toolRequestId: string;
|
|
200
219
|
tool: string;
|
|
201
220
|
args: unknown;
|
|
202
221
|
} | {
|
|
203
|
-
type: "
|
|
222
|
+
type: "operation_completed";
|
|
223
|
+
channelId: string;
|
|
224
|
+
sessionId: string;
|
|
225
|
+
seq?: number;
|
|
226
|
+
operationId: string;
|
|
227
|
+
operationKind: "user" | "prepare";
|
|
228
|
+
success: boolean;
|
|
229
|
+
error?: PedelecError;
|
|
230
|
+
} | {
|
|
231
|
+
type: "usage_updated";
|
|
204
232
|
channelId: string;
|
|
205
233
|
sessionId: string;
|
|
206
234
|
seq?: number;
|
|
235
|
+
totalTokens: number;
|
|
207
236
|
} | {
|
|
208
237
|
type: "error";
|
|
209
238
|
channelId?: string;
|
|
210
239
|
sessionId?: string;
|
|
211
240
|
seq?: number;
|
|
241
|
+
operationId?: string;
|
|
212
242
|
error?: PedelecError;
|
|
213
243
|
} | {
|
|
214
244
|
type: "ended";
|
|
@@ -216,11 +246,41 @@ type SessionEvent = {
|
|
|
216
246
|
sessionId: string;
|
|
217
247
|
seq?: number;
|
|
218
248
|
};
|
|
249
|
+
type SessionSnapshot = {
|
|
250
|
+
threadId: string;
|
|
251
|
+
status: string;
|
|
252
|
+
latestSeq: number;
|
|
253
|
+
usage?: {
|
|
254
|
+
totalTokens: number;
|
|
255
|
+
};
|
|
256
|
+
activeOperation?: {
|
|
257
|
+
operationId: string;
|
|
258
|
+
operationKind: "user" | "prepare";
|
|
259
|
+
startedAt: string;
|
|
260
|
+
};
|
|
261
|
+
lastCompletedOperation?: {
|
|
262
|
+
operationId: string;
|
|
263
|
+
operationKind: "user" | "prepare";
|
|
264
|
+
success: boolean;
|
|
265
|
+
error?: PedelecError;
|
|
266
|
+
completedAt: string;
|
|
267
|
+
};
|
|
268
|
+
pendingToolRequest?: {
|
|
269
|
+
requestId: string;
|
|
270
|
+
threadId: string;
|
|
271
|
+
operationId: string;
|
|
272
|
+
toolName: string;
|
|
273
|
+
args: unknown;
|
|
274
|
+
createdAt: string;
|
|
275
|
+
timeoutMs: number;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
219
278
|
type EventDispatchMeta = {
|
|
220
279
|
source: "core" | "sdk";
|
|
221
280
|
eventReceivedAt?: number;
|
|
222
281
|
};
|
|
223
282
|
type ChatHandler = (text: string, ctx: ChatEventContext) => void;
|
|
283
|
+
type ChatDeltaHandler = (text: string, ctx: ChatDeltaEventContext) => void;
|
|
224
284
|
type GenericToolHandler<TToolName extends string = string> = (tool: TToolName, args: unknown, ctx: ToolCallContext) => unknown | Promise<unknown>;
|
|
225
285
|
type ErrorHandler = (error: PedelecError, ctx: ErrorEventContext) => void;
|
|
226
286
|
type StatusHandler = (status: PedelecSessionStatus, ctx: StatusEventContext) => void;
|
|
@@ -235,10 +295,12 @@ export declare class Pedelec {
|
|
|
235
295
|
private readonly lastSeqBySession;
|
|
236
296
|
private nextRequestNumber;
|
|
237
297
|
private port;
|
|
298
|
+
private pageActivityListenersAttached;
|
|
299
|
+
private lastReportedPageActive;
|
|
238
300
|
constructor(options?: PedelecOptions);
|
|
239
301
|
createSession(): Promise<PedelecSession<string>>;
|
|
240
302
|
createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
241
|
-
|
|
303
|
+
workspaceFolderPicker(): Promise<WorkspaceFolderPickerResult | null>;
|
|
242
304
|
listProviders(): Promise<ProviderInfo[]>;
|
|
243
305
|
getSettings(): Promise<PedelecSettings>;
|
|
244
306
|
getApprovalStatus(): Promise<ApprovalStatus>;
|
|
@@ -246,6 +308,16 @@ export declare class Pedelec {
|
|
|
246
308
|
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
247
309
|
private requestWithOptions;
|
|
248
310
|
unregisterSession(sessionId: string): void;
|
|
311
|
+
/** @internal */
|
|
312
|
+
registerExistingSession(session: PedelecSession<any>): void;
|
|
313
|
+
/** @internal */
|
|
314
|
+
isSessionRegistered(sessionId: string, session: PedelecSession<any>): boolean;
|
|
315
|
+
private attachPageActivityListeners;
|
|
316
|
+
private isDocumentHidden;
|
|
317
|
+
private isDocumentFocused;
|
|
318
|
+
private isPageForegroundActive;
|
|
319
|
+
private postSdkHello;
|
|
320
|
+
private reportPageActivity;
|
|
249
321
|
private resolveCreateSessionInput;
|
|
250
322
|
checkAvailability(): Promise<PedelecAvailability>;
|
|
251
323
|
private resolveDefaultCreateSessionInput;
|
|
@@ -266,61 +338,79 @@ export declare class PedelecSession<TToolName extends string = string> {
|
|
|
266
338
|
readonly provider: string;
|
|
267
339
|
readonly effortLevel?: EffortLevel;
|
|
268
340
|
readonly sessionCreatedAt: number;
|
|
341
|
+
readonly usage: PedelecSessionUsage;
|
|
269
342
|
private status;
|
|
270
|
-
private
|
|
271
|
-
private pendingPrepare;
|
|
343
|
+
private pendingOperation;
|
|
272
344
|
private preparePromise;
|
|
273
345
|
private uploadPromise;
|
|
346
|
+
private reactivationPromise;
|
|
274
347
|
private ending;
|
|
275
348
|
private prepared;
|
|
349
|
+
private autoEndOnDisconnect;
|
|
276
350
|
private activeTurn;
|
|
277
351
|
private transportDetached;
|
|
278
352
|
private genericToolHandler;
|
|
279
353
|
private inlineToolHandlers;
|
|
280
354
|
private readonly namedToolHandlers;
|
|
281
355
|
private readonly chatHandlers;
|
|
356
|
+
private readonly chatDeltaHandlers;
|
|
282
357
|
private readonly errorHandlers;
|
|
283
358
|
private readonly statusHandlers;
|
|
284
359
|
private readonly endedHandlers;
|
|
285
|
-
|
|
360
|
+
private readonly recoveredToolCalls;
|
|
361
|
+
private readonly handledToolRequestIds;
|
|
362
|
+
private authoritativePendingToolRequest;
|
|
363
|
+
constructor(client: Pedelec, sessionId: string, provider: string, effortLevel?: EffortLevel, inlineToolHandlers?: Map<string, ToolSpecificHandler>, autoEndOnDisconnect?: boolean);
|
|
286
364
|
prepare(): Promise<void>;
|
|
287
365
|
sendText(text: string): Promise<void>;
|
|
288
366
|
onChat(handler: ChatHandler): () => void;
|
|
367
|
+
onChatDelta(handler: ChatDeltaHandler): () => void;
|
|
289
368
|
onTool(handler: GenericToolHandler<TToolName>): () => void;
|
|
290
369
|
onTool<TArgs = unknown, TResult = unknown>(toolName: TToolName, handler: ToolSpecificHandler<TArgs, TResult>): () => void;
|
|
291
|
-
uploadAsset(file: File): Promise<
|
|
292
|
-
uploadAsset(file: File, targetPath:
|
|
293
|
-
listAssets(): Promise<
|
|
294
|
-
readAsset(path:
|
|
295
|
-
readAsset<T = JsonValue>(path:
|
|
296
|
-
readAsset(path:
|
|
370
|
+
uploadAsset(file: File): Promise<AssetPath>;
|
|
371
|
+
uploadAsset(file: File, targetPath: AssetPath): Promise<AssetPath>;
|
|
372
|
+
listAssets(): Promise<Asset[]>;
|
|
373
|
+
readAsset(path: AssetPath, type: "text"): Promise<string>;
|
|
374
|
+
readAsset<T = JsonValue>(path: AssetPath, type: "json"): Promise<T>;
|
|
375
|
+
readAsset(path: AssetPath, type: "file"): Promise<File>;
|
|
297
376
|
onError(handler: ErrorHandler): () => void;
|
|
298
377
|
onStatus(handler: StatusHandler): () => void;
|
|
299
378
|
onEnded(handler: EndedHandler): () => void;
|
|
300
379
|
getStatus(): PedelecSessionStatus;
|
|
301
380
|
end(): Promise<void>;
|
|
381
|
+
resume(): Promise<void>;
|
|
382
|
+
/** @internal */
|
|
383
|
+
setAutoEndOnDisconnect(autoEndOnDisconnect: boolean): void;
|
|
302
384
|
handleEvent(event: SessionEvent, meta?: EventDispatchMeta): void;
|
|
385
|
+
/** @internal */
|
|
386
|
+
handleSnapshot(snapshot: SessionSnapshot, meta?: EventDispatchMeta): void;
|
|
303
387
|
replaceInlineToolHandlers(handlers: Map<string, ToolSpecificHandler>): void;
|
|
304
388
|
/** @internal */
|
|
305
389
|
isTransportDetached(): boolean;
|
|
306
390
|
/** @internal */
|
|
307
391
|
handleTransportDisconnect(error: PedelecError): void;
|
|
308
|
-
private
|
|
309
|
-
private resolvePendingSend;
|
|
310
|
-
private resolvePendingPrepare;
|
|
311
|
-
private resolveActivePending;
|
|
312
|
-
private rejectPendingSend;
|
|
313
|
-
private rejectPendingPrepare;
|
|
392
|
+
private handleToolCallOnce;
|
|
314
393
|
private rejectActivePending;
|
|
315
|
-
private clearPendingSend;
|
|
316
|
-
private clearPendingPrepare;
|
|
317
394
|
private markEnded;
|
|
318
395
|
private emitError;
|
|
319
396
|
private setStatus;
|
|
320
|
-
private
|
|
397
|
+
private requireOperationTurn;
|
|
398
|
+
private updateUsage;
|
|
399
|
+
private matchesActiveOperation;
|
|
400
|
+
private markOperationRequestSettled;
|
|
401
|
+
private failOperationFromRequest;
|
|
402
|
+
private completeOperation;
|
|
403
|
+
private maybeSettleOperation;
|
|
404
|
+
private rejectOperation;
|
|
405
|
+
private queueSnapshotTool;
|
|
406
|
+
private reconcileRecoveredToolCalls;
|
|
407
|
+
private isAuthoritativeToolRequestCurrent;
|
|
408
|
+
private hasToolHandler;
|
|
409
|
+
private drainRecoveredToolCalls;
|
|
321
410
|
private finishActiveTurn;
|
|
322
411
|
private createBaseContext;
|
|
323
412
|
private createChatContext;
|
|
413
|
+
private createChatDeltaContext;
|
|
324
414
|
private createToolCallContext;
|
|
325
415
|
private createStatusContext;
|
|
326
416
|
private createErrorContext;
|