@kaoruisaac/pedelec 0.1.2 → 0.1.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 +60 -25
- package/dist/index.d.ts +84 -22
- package/dist/index.js +219 -35
- package/dist/index.type-test.d.ts +1 -0
- package/dist/index.type-test.js +101 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -66,25 +66,26 @@ const session = await pedelec.createSession({
|
|
|
66
66
|
properties: {},
|
|
67
67
|
required: [],
|
|
68
68
|
},
|
|
69
|
-
handler: () => ({
|
|
69
|
+
handler: (_args, ctx) => ({
|
|
70
70
|
url: location.href,
|
|
71
71
|
title: document.title,
|
|
72
|
+
turnId: ctx.turnId,
|
|
72
73
|
}),
|
|
73
74
|
}),
|
|
74
75
|
],
|
|
75
76
|
},
|
|
76
77
|
});
|
|
77
78
|
|
|
78
|
-
session.onChat((text) => {
|
|
79
|
-
console.log(text);
|
|
79
|
+
session.onChat((text, ctx) => {
|
|
80
|
+
console.log(ctx.turnId, text);
|
|
80
81
|
});
|
|
81
82
|
|
|
82
|
-
session.onStatus((status) => {
|
|
83
|
-
console.log("status", status);
|
|
83
|
+
session.onStatus((status, ctx) => {
|
|
84
|
+
console.log("status", status, "previous", ctx.previousStatus);
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
session.onError((error) => {
|
|
87
|
-
console.error(error.code, error.message, error.details);
|
|
87
|
+
session.onError((error, ctx) => {
|
|
88
|
+
console.error(ctx.type, error.code, error.message, error.details);
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
await session.sendText("Analyze the current page state.");
|
|
@@ -277,7 +278,8 @@ type CreateSessionInput =
|
|
|
277
278
|
```ts
|
|
278
279
|
let assistantText = "";
|
|
279
280
|
|
|
280
|
-
session.onChat((text) => {
|
|
281
|
+
session.onChat((text, ctx) => {
|
|
282
|
+
console.debug("chat delta for turn", ctx.turnId, ctx.eventReceivedAt);
|
|
281
283
|
assistantText += text;
|
|
282
284
|
renderAssistantMessage(assistantText);
|
|
283
285
|
});
|
|
@@ -286,8 +288,8 @@ session.onChat((text) => {
|
|
|
286
288
|
The returned function unsubscribes the handler:
|
|
287
289
|
|
|
288
290
|
```ts
|
|
289
|
-
const unsubscribe = session.onChat((text) => {
|
|
290
|
-
console.log(text);
|
|
291
|
+
const unsubscribe = session.onChat((text, ctx) => {
|
|
292
|
+
console.log(ctx.turnId, text);
|
|
291
293
|
});
|
|
292
294
|
|
|
293
295
|
unsubscribe();
|
|
@@ -308,7 +310,8 @@ try {
|
|
|
308
310
|
## Session Status
|
|
309
311
|
|
|
310
312
|
```ts
|
|
311
|
-
session.onStatus((status) => {
|
|
313
|
+
session.onStatus((status, ctx) => {
|
|
314
|
+
console.debug("status changed from", ctx.previousStatus, "to", ctx.status);
|
|
312
315
|
switch (status) {
|
|
313
316
|
case "idle":
|
|
314
317
|
break;
|
|
@@ -363,10 +366,11 @@ const session = await pedelec.createSession({
|
|
|
363
366
|
properties: {},
|
|
364
367
|
required: [],
|
|
365
368
|
},
|
|
366
|
-
handler: () => ({
|
|
369
|
+
handler: (_args, ctx) => ({
|
|
367
370
|
url: location.href,
|
|
368
371
|
title: document.title,
|
|
369
372
|
selectedText: window.getSelection()?.toString() ?? "",
|
|
373
|
+
turnId: ctx.turnId,
|
|
370
374
|
}),
|
|
371
375
|
}),
|
|
372
376
|
],
|
|
@@ -400,8 +404,9 @@ const session = await pedelec.createSession({
|
|
|
400
404
|
},
|
|
401
405
|
});
|
|
402
406
|
|
|
403
|
-
session.onTool("update_counter", async (args) => {
|
|
407
|
+
session.onTool("update_counter", async (args, ctx) => {
|
|
404
408
|
const { delta } = args as { delta: number };
|
|
409
|
+
console.debug("tool call", ctx.toolRequestId, ctx.turnId);
|
|
405
410
|
counter.value += delta;
|
|
406
411
|
return {
|
|
407
412
|
counter: counter.value,
|
|
@@ -412,7 +417,37 @@ session.onTool("update_counter", async (args) => {
|
|
|
412
417
|
|
|
413
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.
|
|
414
419
|
|
|
415
|
-
The
|
|
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.
|
|
416
451
|
|
|
417
452
|
### Tool Args Schema
|
|
418
453
|
|
|
@@ -448,8 +483,8 @@ If you have saved a `sessionId`, resume it later:
|
|
|
448
483
|
```ts
|
|
449
484
|
const session = await pedelec.resumeSession("thread_abc123");
|
|
450
485
|
|
|
451
|
-
session.onChat((text) => {
|
|
452
|
-
console.log(text);
|
|
486
|
+
session.onChat((text, ctx) => {
|
|
487
|
+
console.log(ctx.turnId, text);
|
|
453
488
|
});
|
|
454
489
|
|
|
455
490
|
await session.sendText("Continue the previous task.");
|
|
@@ -468,8 +503,8 @@ After a session ends, future `sendText()` calls reject with `SESSION_ENDED`.
|
|
|
468
503
|
Listen for runtime-ended sessions:
|
|
469
504
|
|
|
470
505
|
```ts
|
|
471
|
-
session.onEnded(() => {
|
|
472
|
-
console.log("session ended");
|
|
506
|
+
session.onEnded((ctx) => {
|
|
507
|
+
console.log("session ended", ctx.source);
|
|
473
508
|
});
|
|
474
509
|
```
|
|
475
510
|
|
|
@@ -494,8 +529,8 @@ Low-level requests still use the SDK bridge timeout and reject with `PedelecErro
|
|
|
494
529
|
Register `onError()` for session-level errors and use `try/catch` around async SDK calls.
|
|
495
530
|
|
|
496
531
|
```ts
|
|
497
|
-
session.onError((error) => {
|
|
498
|
-
console.error("session error", error.code, error.message, error.details);
|
|
532
|
+
session.onError((error, ctx) => {
|
|
533
|
+
console.error("session error", ctx.type, error.code, error.message, error.details);
|
|
499
534
|
});
|
|
500
535
|
|
|
501
536
|
try {
|
|
@@ -555,11 +590,11 @@ class PedelecSession {
|
|
|
555
590
|
readonly model?: string;
|
|
556
591
|
|
|
557
592
|
sendText(text: string): Promise<void>;
|
|
558
|
-
onChat(handler: (text: string) => void): () => void;
|
|
559
|
-
onTool(handler: (tool: string, args: unknown) => unknown | Promise<unknown>): () => void;
|
|
560
|
-
onError(handler: (error: PedelecError) => void): () => void;
|
|
561
|
-
onStatus(handler: (status: PedelecSessionStatus) => void): () => void;
|
|
562
|
-
onEnded(handler: () => void): () => 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;
|
|
563
598
|
getStatus(): PedelecSessionStatus;
|
|
564
599
|
end(): Promise<void>;
|
|
565
600
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -51,18 +51,19 @@ export type ToolArgsOneOfSchema = ToolArgsSchemaMeta & {
|
|
|
51
51
|
};
|
|
52
52
|
export type ToolArgsSchemaNode = ToolArgsStringSchema | ToolArgsNumberSchema | ToolArgsIntegerSchema | ToolArgsBooleanSchema | ToolArgsArraySchema | ToolArgsObjectSchema | ToolArgsOneOfSchema;
|
|
53
53
|
export type ToolArgsSchema = ToolArgsObjectSchema;
|
|
54
|
-
export type ToolSpecificHandler<TArgs = unknown, TResult = unknown> = (args: TArgs) => TResult | Promise<TResult>;
|
|
55
|
-
export type ToolDefinition<TArgs = unknown, TResult = unknown> = {
|
|
56
|
-
name:
|
|
54
|
+
export type ToolSpecificHandler<TArgs = unknown, TResult = unknown> = (args: TArgs, ctx: ToolCallContext) => TResult | Promise<TResult>;
|
|
55
|
+
export type ToolDefinition<TArgs = unknown, TResult = unknown, TName extends string = string> = {
|
|
56
|
+
name: TName;
|
|
57
57
|
description: string;
|
|
58
58
|
argsSchema: ToolArgsSchema;
|
|
59
59
|
timeoutMs?: number;
|
|
60
60
|
handler?: ToolSpecificHandler<TArgs, TResult>;
|
|
61
61
|
};
|
|
62
|
-
export type SkillsInput = {
|
|
62
|
+
export type SkillsInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
63
63
|
guidance: string;
|
|
64
|
-
tools:
|
|
64
|
+
tools: TTools;
|
|
65
65
|
};
|
|
66
|
+
export type ToolNameOf<TTools extends readonly ToolDefinition[]> = Extract<TTools[number]["name"], string>;
|
|
66
67
|
export type SerializableToolManifest = {
|
|
67
68
|
name: string;
|
|
68
69
|
description: string;
|
|
@@ -73,19 +74,19 @@ export type SerializableSkillsManifest = {
|
|
|
73
74
|
guidance: string;
|
|
74
75
|
tools: SerializableToolManifest[];
|
|
75
76
|
};
|
|
76
|
-
type CreateSessionInputWithProvider = {
|
|
77
|
+
type CreateSessionInputWithProvider<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
77
78
|
provider: ProviderCode;
|
|
78
79
|
model?: string;
|
|
79
|
-
skills?: SkillsInput
|
|
80
|
+
skills?: SkillsInput<TTools>;
|
|
80
81
|
autoEndOnDisconnect?: boolean;
|
|
81
82
|
};
|
|
82
|
-
type CreateSessionInputWithDefaults = {
|
|
83
|
+
type CreateSessionInputWithDefaults<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = {
|
|
83
84
|
provider?: undefined;
|
|
84
85
|
model?: never;
|
|
85
|
-
skills?: SkillsInput
|
|
86
|
+
skills?: SkillsInput<TTools>;
|
|
86
87
|
autoEndOnDisconnect?: boolean;
|
|
87
88
|
};
|
|
88
|
-
export type CreateSessionInput = CreateSessionInputWithProvider | CreateSessionInputWithDefaults
|
|
89
|
+
export type CreateSessionInput<TTools extends readonly ToolDefinition[] = readonly ToolDefinition[]> = CreateSessionInputWithProvider<TTools> | CreateSessionInputWithDefaults<TTools>;
|
|
89
90
|
export type ProviderInfo = {
|
|
90
91
|
name: string;
|
|
91
92
|
code: ProviderCode;
|
|
@@ -108,6 +109,43 @@ export type PedelecError = {
|
|
|
108
109
|
details?: unknown;
|
|
109
110
|
};
|
|
110
111
|
export type PedelecSessionStatus = "idle" | "running" | "waiting_tool_result" | "ended" | "error";
|
|
112
|
+
export type PedelecEventContext = {
|
|
113
|
+
sessionId: string;
|
|
114
|
+
provider: string;
|
|
115
|
+
model?: string;
|
|
116
|
+
sessionCreatedAt: number;
|
|
117
|
+
eventReceivedAt?: number;
|
|
118
|
+
eventEmittedAt: number;
|
|
119
|
+
turnId?: string;
|
|
120
|
+
turnStartedAt?: number;
|
|
121
|
+
turnKind?: "user" | "prepare";
|
|
122
|
+
source: "core" | "sdk";
|
|
123
|
+
};
|
|
124
|
+
export type ChatEventContext = PedelecEventContext & {
|
|
125
|
+
type: "chat_delta";
|
|
126
|
+
turnId: string;
|
|
127
|
+
turnStartedAt: number;
|
|
128
|
+
eventReceivedAt: number;
|
|
129
|
+
};
|
|
130
|
+
export type ToolCallContext = PedelecEventContext & {
|
|
131
|
+
type: "tool_call";
|
|
132
|
+
toolRequestId: string;
|
|
133
|
+
tool: string;
|
|
134
|
+
turnId: string;
|
|
135
|
+
turnStartedAt: number;
|
|
136
|
+
eventReceivedAt: number;
|
|
137
|
+
};
|
|
138
|
+
export type StatusEventContext = PedelecEventContext & {
|
|
139
|
+
type: "status_changed" | "sdk_status_changed";
|
|
140
|
+
status: PedelecSessionStatus;
|
|
141
|
+
previousStatus: PedelecSessionStatus;
|
|
142
|
+
};
|
|
143
|
+
export type ErrorEventContext = PedelecEventContext & {
|
|
144
|
+
type: "error" | "sdk_error";
|
|
145
|
+
};
|
|
146
|
+
export type EndedEventContext = PedelecEventContext & {
|
|
147
|
+
type: "ended" | "sdk_ended";
|
|
148
|
+
};
|
|
111
149
|
type SessionEvent = {
|
|
112
150
|
type: "chat_delta";
|
|
113
151
|
channelId: string;
|
|
@@ -145,12 +183,16 @@ type SessionEvent = {
|
|
|
145
183
|
sessionId: string;
|
|
146
184
|
seq?: number;
|
|
147
185
|
};
|
|
148
|
-
type
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
type
|
|
153
|
-
|
|
186
|
+
type EventDispatchMeta = {
|
|
187
|
+
source: "core" | "sdk";
|
|
188
|
+
eventReceivedAt?: number;
|
|
189
|
+
};
|
|
190
|
+
type ChatHandler = (text: string, ctx: ChatEventContext) => void;
|
|
191
|
+
type GenericToolHandler<TToolName extends string = string> = (tool: TToolName, args: unknown, ctx: ToolCallContext) => unknown | Promise<unknown>;
|
|
192
|
+
type ErrorHandler = (error: PedelecError, ctx: ErrorEventContext) => void;
|
|
193
|
+
type StatusHandler = (status: PedelecSessionStatus, ctx: StatusEventContext) => void;
|
|
194
|
+
type EndedHandler = (ctx: EndedEventContext) => void;
|
|
195
|
+
export declare function defineTool<TArgs = unknown, TResult = unknown, const TName extends string = string>(tool: ToolDefinition<TArgs, TResult, TName>): ToolDefinition<TArgs, TResult, TName>;
|
|
154
196
|
export declare class Pedelec {
|
|
155
197
|
private readonly pageWindow;
|
|
156
198
|
private readonly channelId;
|
|
@@ -162,11 +204,12 @@ export declare class Pedelec {
|
|
|
162
204
|
private disconnectedError;
|
|
163
205
|
private port;
|
|
164
206
|
constructor(options?: PedelecOptions);
|
|
165
|
-
createSession(
|
|
207
|
+
createSession(): Promise<PedelecSession<string>>;
|
|
208
|
+
createSession<const TTools extends readonly ToolDefinition[]>(input: CreateSessionInput<TTools>): Promise<PedelecSession<ToolNameOf<TTools>>>;
|
|
166
209
|
listProviders(): Promise<ProviderInfo[]>;
|
|
167
210
|
getSettings(): Promise<PedelecSettings>;
|
|
168
211
|
getApprovalStatus(): Promise<ApprovalStatus>;
|
|
169
|
-
resumeSession(sessionId: string): Promise<PedelecSession
|
|
212
|
+
resumeSession(sessionId: string): Promise<PedelecSession<string>>;
|
|
170
213
|
request<T>(type: string, payload?: Record<string, unknown>): Promise<T>;
|
|
171
214
|
unregisterSession(sessionId: string): void;
|
|
172
215
|
private resolveCreateSessionInput;
|
|
@@ -181,13 +224,18 @@ export declare class Pedelec {
|
|
|
181
224
|
private handleDisconnect;
|
|
182
225
|
private broadcastError;
|
|
183
226
|
}
|
|
184
|
-
export declare class PedelecSession {
|
|
227
|
+
export declare class PedelecSession<TToolName extends string = string> {
|
|
185
228
|
private readonly client;
|
|
186
229
|
readonly sessionId: string;
|
|
187
230
|
readonly provider: string;
|
|
188
231
|
readonly model?: string;
|
|
232
|
+
readonly sessionCreatedAt: number;
|
|
189
233
|
private status;
|
|
190
234
|
private pendingSend;
|
|
235
|
+
private pendingPrepare;
|
|
236
|
+
private preparePromise;
|
|
237
|
+
private prepared;
|
|
238
|
+
private activeTurn;
|
|
191
239
|
private genericToolHandler;
|
|
192
240
|
private inlineToolHandlers;
|
|
193
241
|
private readonly namedToolHandlers;
|
|
@@ -196,23 +244,37 @@ export declare class PedelecSession {
|
|
|
196
244
|
private readonly statusHandlers;
|
|
197
245
|
private readonly endedHandlers;
|
|
198
246
|
constructor(client: Pedelec, sessionId: string, provider: string, model?: string, inlineToolHandlers?: Map<string, ToolSpecificHandler>);
|
|
247
|
+
prepare(): Promise<void>;
|
|
199
248
|
sendText(text: string): Promise<void>;
|
|
200
249
|
onChat(handler: ChatHandler): () => void;
|
|
201
|
-
onTool(handler: GenericToolHandler): () => void;
|
|
202
|
-
onTool<TArgs = unknown, TResult = unknown>(toolName:
|
|
250
|
+
onTool(handler: GenericToolHandler<TToolName>): () => void;
|
|
251
|
+
onTool<TArgs = unknown, TResult = unknown>(toolName: TToolName, handler: ToolSpecificHandler<TArgs, TResult>): () => void;
|
|
203
252
|
onError(handler: ErrorHandler): () => void;
|
|
204
253
|
onStatus(handler: StatusHandler): () => void;
|
|
205
254
|
onEnded(handler: EndedHandler): () => void;
|
|
206
255
|
getStatus(): PedelecSessionStatus;
|
|
207
256
|
end(): Promise<void>;
|
|
208
|
-
handleEvent(event: SessionEvent): void;
|
|
257
|
+
handleEvent(event: SessionEvent, meta?: EventDispatchMeta): void;
|
|
209
258
|
replaceInlineToolHandlers(handlers: Map<string, ToolSpecificHandler>): void;
|
|
210
259
|
private handleToolCall;
|
|
211
260
|
private resolvePendingSend;
|
|
261
|
+
private resolvePendingPrepare;
|
|
262
|
+
private resolveActivePending;
|
|
212
263
|
private rejectPendingSend;
|
|
264
|
+
private rejectPendingPrepare;
|
|
265
|
+
private rejectActivePending;
|
|
213
266
|
private clearPendingSend;
|
|
267
|
+
private clearPendingPrepare;
|
|
214
268
|
private markEnded;
|
|
215
269
|
private emitError;
|
|
216
270
|
private setStatus;
|
|
271
|
+
private requireActiveTurn;
|
|
272
|
+
private finishActiveTurn;
|
|
273
|
+
private createBaseContext;
|
|
274
|
+
private createChatContext;
|
|
275
|
+
private createToolCallContext;
|
|
276
|
+
private createStatusContext;
|
|
277
|
+
private createErrorContext;
|
|
278
|
+
private createEndedContext;
|
|
217
279
|
}
|
|
218
280
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -309,8 +309,12 @@ export class Pedelec {
|
|
|
309
309
|
return;
|
|
310
310
|
}
|
|
311
311
|
if (isSessionEvent(message) && this.isNewEvent(message)) {
|
|
312
|
+
const eventReceivedAt = Date.now();
|
|
312
313
|
if (message.sessionId) {
|
|
313
|
-
this.sessions.get(message.sessionId)?.handleEvent(message
|
|
314
|
+
this.sessions.get(message.sessionId)?.handleEvent(message, {
|
|
315
|
+
source: "core",
|
|
316
|
+
eventReceivedAt,
|
|
317
|
+
});
|
|
314
318
|
}
|
|
315
319
|
else if (message.type === "error") {
|
|
316
320
|
this.broadcastError(normalizeError(message.error, "SDK_TRANSPORT_ERROR", "Pedelec transport error"));
|
|
@@ -354,7 +358,7 @@ export class Pedelec {
|
|
|
354
358
|
}
|
|
355
359
|
broadcastError(error) {
|
|
356
360
|
for (const session of this.sessions.values()) {
|
|
357
|
-
session.handleEvent({ type: "error", sessionId: session.sessionId, error });
|
|
361
|
+
session.handleEvent({ type: "error", sessionId: session.sessionId, error }, { source: "sdk" });
|
|
358
362
|
}
|
|
359
363
|
}
|
|
360
364
|
}
|
|
@@ -363,8 +367,13 @@ export class PedelecSession {
|
|
|
363
367
|
sessionId;
|
|
364
368
|
provider;
|
|
365
369
|
model;
|
|
370
|
+
sessionCreatedAt = Date.now();
|
|
366
371
|
status = "idle";
|
|
367
372
|
pendingSend = null;
|
|
373
|
+
pendingPrepare = null;
|
|
374
|
+
preparePromise = null;
|
|
375
|
+
prepared = false;
|
|
376
|
+
activeTurn = null;
|
|
368
377
|
genericToolHandler = null;
|
|
369
378
|
inlineToolHandlers = new Map();
|
|
370
379
|
namedToolHandlers = new Map();
|
|
@@ -379,14 +388,63 @@ export class PedelecSession {
|
|
|
379
388
|
this.model = model;
|
|
380
389
|
this.inlineToolHandlers = new Map(inlineToolHandlers);
|
|
381
390
|
}
|
|
382
|
-
|
|
391
|
+
prepare() {
|
|
383
392
|
if (this.status === "ended") {
|
|
384
393
|
return Promise.reject(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
385
394
|
}
|
|
386
395
|
if (this.pendingSend) {
|
|
387
396
|
return Promise.reject(makeError("SESSION_BUSY", "session is already running", { sessionId: this.sessionId }));
|
|
388
397
|
}
|
|
389
|
-
this.
|
|
398
|
+
if (this.prepared) {
|
|
399
|
+
return Promise.resolve();
|
|
400
|
+
}
|
|
401
|
+
if (this.preparePromise) {
|
|
402
|
+
return this.preparePromise;
|
|
403
|
+
}
|
|
404
|
+
const turn = createTurn("prepare");
|
|
405
|
+
this.activeTurn = turn;
|
|
406
|
+
this.setStatus("running", { source: "sdk" });
|
|
407
|
+
const donePromise = new Promise((resolve, reject) => {
|
|
408
|
+
this.pendingPrepare = { resolve, reject };
|
|
409
|
+
});
|
|
410
|
+
const requestPromise = this.client
|
|
411
|
+
.request("prepare_session", {
|
|
412
|
+
sessionId: this.sessionId,
|
|
413
|
+
})
|
|
414
|
+
.catch((err) => {
|
|
415
|
+
const error = normalizeError(err, "PREPARE_FAILED", "prepare failed");
|
|
416
|
+
this.emitError(error, { source: "sdk" });
|
|
417
|
+
this.clearPendingPrepare();
|
|
418
|
+
this.finishActiveTurn(turn);
|
|
419
|
+
throw error;
|
|
420
|
+
});
|
|
421
|
+
this.preparePromise = Promise.all([requestPromise, donePromise])
|
|
422
|
+
.then(() => {
|
|
423
|
+
this.prepared = true;
|
|
424
|
+
})
|
|
425
|
+
.finally(() => {
|
|
426
|
+
this.preparePromise = null;
|
|
427
|
+
});
|
|
428
|
+
return this.preparePromise;
|
|
429
|
+
}
|
|
430
|
+
async sendText(text) {
|
|
431
|
+
if (this.preparePromise) {
|
|
432
|
+
try {
|
|
433
|
+
await this.preparePromise;
|
|
434
|
+
}
|
|
435
|
+
catch {
|
|
436
|
+
// prepare is an optimization; sendText falls back to the original first-run path.
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (this.status === "ended") {
|
|
440
|
+
return Promise.reject(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
441
|
+
}
|
|
442
|
+
if (this.pendingSend || this.pendingPrepare) {
|
|
443
|
+
return Promise.reject(makeError("SESSION_BUSY", "session is already running", { sessionId: this.sessionId }));
|
|
444
|
+
}
|
|
445
|
+
const turn = createTurn("user");
|
|
446
|
+
this.activeTurn = turn;
|
|
447
|
+
this.setStatus("running", { source: "sdk" });
|
|
390
448
|
const donePromise = new Promise((resolve, reject) => {
|
|
391
449
|
this.pendingSend = { resolve, reject };
|
|
392
450
|
});
|
|
@@ -397,8 +455,9 @@ export class PedelecSession {
|
|
|
397
455
|
})
|
|
398
456
|
.catch((err) => {
|
|
399
457
|
const error = normalizeError(err, "SEND_TEXT_FAILED", "sendText failed");
|
|
458
|
+
this.emitError(error, { source: "sdk" });
|
|
400
459
|
this.clearPendingSend();
|
|
401
|
-
this.
|
|
460
|
+
this.finishActiveTurn(turn);
|
|
402
461
|
throw error;
|
|
403
462
|
});
|
|
404
463
|
return Promise.all([requestPromise, donePromise]).then(() => undefined);
|
|
@@ -456,65 +515,73 @@ export class PedelecSession {
|
|
|
456
515
|
}
|
|
457
516
|
catch (err) {
|
|
458
517
|
const error = normalizeError(err, "END_SESSION_FAILED", "end failed");
|
|
459
|
-
this.emitError(error);
|
|
518
|
+
this.emitError(error, { source: "sdk" });
|
|
460
519
|
throw error;
|
|
461
520
|
}
|
|
462
|
-
this.markEnded();
|
|
521
|
+
this.markEnded({ source: "sdk" });
|
|
463
522
|
this.client.unregisterSession(this.sessionId);
|
|
464
523
|
}
|
|
465
|
-
handleEvent(event) {
|
|
524
|
+
handleEvent(event, meta = { source: "sdk" }) {
|
|
466
525
|
if (event.type === "chat_delta") {
|
|
526
|
+
const turn = this.requireActiveTurn("chat_delta", meta);
|
|
527
|
+
if (!turn)
|
|
528
|
+
return;
|
|
529
|
+
if (turn.kind === "prepare")
|
|
530
|
+
return;
|
|
467
531
|
for (const handler of this.chatHandlers) {
|
|
468
|
-
handler(event.text);
|
|
532
|
+
handler(event.text, this.createChatContext(meta, turn));
|
|
469
533
|
}
|
|
470
534
|
return;
|
|
471
535
|
}
|
|
472
536
|
if (event.type === "status_changed") {
|
|
473
|
-
this.setStatus(event.status);
|
|
537
|
+
this.setStatus(event.status, meta);
|
|
474
538
|
if (event.status === "idle") {
|
|
475
|
-
this.
|
|
539
|
+
this.resolveActivePending();
|
|
476
540
|
}
|
|
477
541
|
else if (event.status === "ended") {
|
|
478
|
-
this.markEnded();
|
|
542
|
+
this.markEnded(meta);
|
|
479
543
|
}
|
|
480
544
|
else if (event.status === "error") {
|
|
481
545
|
const error = makeError("SESSION_ERROR", "session entered error status", { sessionId: this.sessionId });
|
|
482
|
-
this.
|
|
483
|
-
this.
|
|
546
|
+
this.emitError(error, meta);
|
|
547
|
+
this.rejectActivePending(error);
|
|
484
548
|
}
|
|
485
549
|
return;
|
|
486
550
|
}
|
|
487
551
|
if (event.type === "tool_call") {
|
|
488
|
-
this.
|
|
489
|
-
|
|
552
|
+
const turn = this.requireActiveTurn("tool_call", meta);
|
|
553
|
+
if (!turn)
|
|
554
|
+
return;
|
|
555
|
+
this.setStatus("waiting_tool_result", meta);
|
|
556
|
+
this.handleToolCall(event, meta, turn);
|
|
490
557
|
return;
|
|
491
558
|
}
|
|
492
559
|
if (event.type === "done") {
|
|
493
|
-
this.setStatus("idle");
|
|
494
|
-
this.
|
|
560
|
+
this.setStatus("idle", meta);
|
|
561
|
+
this.resolveActivePending();
|
|
495
562
|
return;
|
|
496
563
|
}
|
|
497
564
|
if (event.type === "error") {
|
|
498
|
-
this.setStatus("error");
|
|
565
|
+
this.setStatus("error", meta);
|
|
499
566
|
const error = normalizeError(event.error, "SESSION_ERROR", "session error");
|
|
500
|
-
this.
|
|
501
|
-
this.
|
|
567
|
+
this.emitError(error, meta);
|
|
568
|
+
this.rejectActivePending(error);
|
|
502
569
|
return;
|
|
503
570
|
}
|
|
504
571
|
if (event.type === "ended") {
|
|
505
|
-
this.markEnded();
|
|
572
|
+
this.markEnded(meta);
|
|
506
573
|
}
|
|
507
574
|
}
|
|
508
575
|
replaceInlineToolHandlers(handlers) {
|
|
509
576
|
this.inlineToolHandlers = new Map(handlers);
|
|
510
577
|
}
|
|
511
|
-
async handleToolCall(event) {
|
|
578
|
+
async handleToolCall(event, meta, turn) {
|
|
512
579
|
let result;
|
|
513
580
|
const namedHandler = this.namedToolHandlers.get(event.tool);
|
|
514
581
|
const inlineHandler = this.inlineToolHandlers.get(event.tool);
|
|
515
582
|
if (namedHandler) {
|
|
516
583
|
try {
|
|
517
|
-
result = await namedHandler(event.args);
|
|
584
|
+
result = await namedHandler(event.args, this.createToolCallContext(event, meta, turn));
|
|
518
585
|
}
|
|
519
586
|
catch (err) {
|
|
520
587
|
result = {
|
|
@@ -524,7 +591,7 @@ export class PedelecSession {
|
|
|
524
591
|
}
|
|
525
592
|
else if (inlineHandler) {
|
|
526
593
|
try {
|
|
527
|
-
result = await inlineHandler(event.args);
|
|
594
|
+
result = await inlineHandler(event.args, this.createToolCallContext(event, meta, turn));
|
|
528
595
|
}
|
|
529
596
|
catch (err) {
|
|
530
597
|
result = {
|
|
@@ -534,7 +601,7 @@ export class PedelecSession {
|
|
|
534
601
|
}
|
|
535
602
|
else if (this.genericToolHandler) {
|
|
536
603
|
try {
|
|
537
|
-
result = await this.genericToolHandler(event.tool, event.args);
|
|
604
|
+
result = await this.genericToolHandler(event.tool, event.args, this.createToolCallContext(event, meta, turn));
|
|
538
605
|
}
|
|
539
606
|
catch (err) {
|
|
540
607
|
result = {
|
|
@@ -557,45 +624,152 @@ export class PedelecSession {
|
|
|
557
624
|
});
|
|
558
625
|
}
|
|
559
626
|
catch (err) {
|
|
560
|
-
this.emitError(normalizeError(err, "SUBMIT_TOOL_RESULT_FAILED", "submit_tool_result failed")
|
|
627
|
+
this.emitError(normalizeError(err, "SUBMIT_TOOL_RESULT_FAILED", "submit_tool_result failed"), {
|
|
628
|
+
source: "sdk",
|
|
629
|
+
});
|
|
561
630
|
}
|
|
562
631
|
}
|
|
563
632
|
resolvePendingSend() {
|
|
564
633
|
const pending = this.pendingSend;
|
|
565
634
|
this.pendingSend = null;
|
|
566
635
|
pending?.resolve();
|
|
636
|
+
this.finishActiveTurn();
|
|
637
|
+
}
|
|
638
|
+
resolvePendingPrepare() {
|
|
639
|
+
const pending = this.pendingPrepare;
|
|
640
|
+
this.pendingPrepare = null;
|
|
641
|
+
pending?.resolve();
|
|
642
|
+
this.finishActiveTurn();
|
|
643
|
+
}
|
|
644
|
+
resolveActivePending() {
|
|
645
|
+
if (this.activeTurn?.kind === "prepare") {
|
|
646
|
+
this.resolvePendingPrepare();
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
this.resolvePendingSend();
|
|
650
|
+
}
|
|
567
651
|
}
|
|
568
652
|
rejectPendingSend(error) {
|
|
569
653
|
const pending = this.pendingSend;
|
|
570
654
|
this.pendingSend = null;
|
|
571
655
|
pending?.reject(error);
|
|
656
|
+
this.finishActiveTurn();
|
|
657
|
+
}
|
|
658
|
+
rejectPendingPrepare(error) {
|
|
659
|
+
const pending = this.pendingPrepare;
|
|
660
|
+
this.pendingPrepare = null;
|
|
661
|
+
pending?.reject(error);
|
|
662
|
+
this.finishActiveTurn();
|
|
663
|
+
}
|
|
664
|
+
rejectActivePending(error) {
|
|
665
|
+
if (this.activeTurn?.kind === "prepare") {
|
|
666
|
+
this.rejectPendingPrepare(error);
|
|
667
|
+
}
|
|
668
|
+
else {
|
|
669
|
+
this.rejectPendingSend(error);
|
|
670
|
+
}
|
|
572
671
|
}
|
|
573
672
|
clearPendingSend() {
|
|
574
673
|
this.pendingSend = null;
|
|
575
674
|
}
|
|
576
|
-
|
|
675
|
+
clearPendingPrepare() {
|
|
676
|
+
this.pendingPrepare = null;
|
|
677
|
+
}
|
|
678
|
+
markEnded(meta = { source: "sdk" }) {
|
|
577
679
|
const wasEnded = this.status === "ended";
|
|
578
|
-
this.setStatus("ended");
|
|
579
|
-
this.rejectPendingSend(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
680
|
+
this.setStatus("ended", meta);
|
|
580
681
|
if (!wasEnded) {
|
|
581
682
|
for (const handler of this.endedHandlers) {
|
|
582
|
-
handler();
|
|
683
|
+
handler(this.createEndedContext(meta));
|
|
583
684
|
}
|
|
584
685
|
}
|
|
686
|
+
this.rejectPendingSend(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
687
|
+
this.rejectPendingPrepare(makeError("SESSION_ENDED", "session has ended", { sessionId: this.sessionId }));
|
|
688
|
+
this.finishActiveTurn();
|
|
585
689
|
}
|
|
586
|
-
emitError(error) {
|
|
690
|
+
emitError(error, meta = { source: "sdk" }) {
|
|
587
691
|
for (const handler of this.errorHandlers) {
|
|
588
|
-
handler(error);
|
|
692
|
+
handler(error, this.createErrorContext(meta));
|
|
589
693
|
}
|
|
590
694
|
}
|
|
591
|
-
setStatus(status) {
|
|
695
|
+
setStatus(status, meta = { source: "sdk" }) {
|
|
592
696
|
if (this.status === status)
|
|
593
697
|
return;
|
|
698
|
+
const previousStatus = this.status;
|
|
594
699
|
this.status = status;
|
|
595
700
|
for (const handler of this.statusHandlers) {
|
|
596
|
-
handler(status);
|
|
701
|
+
handler(status, this.createStatusContext(status, previousStatus, meta));
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
requireActiveTurn(type, meta) {
|
|
705
|
+
if (this.activeTurn)
|
|
706
|
+
return this.activeTurn;
|
|
707
|
+
this.emitError(makeError("SDK_PROTOCOL_ERROR", `${type} event was received without an active turn`, {
|
|
708
|
+
sessionId: this.sessionId,
|
|
709
|
+
}), { source: "sdk", eventReceivedAt: meta.eventReceivedAt });
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
finishActiveTurn(turn = this.activeTurn) {
|
|
713
|
+
if (!turn || !this.activeTurn)
|
|
714
|
+
return;
|
|
715
|
+
if (this.activeTurn.turnId === turn.turnId) {
|
|
716
|
+
this.activeTurn = null;
|
|
597
717
|
}
|
|
598
718
|
}
|
|
719
|
+
createBaseContext(meta, turn = this.activeTurn) {
|
|
720
|
+
return {
|
|
721
|
+
sessionId: this.sessionId,
|
|
722
|
+
provider: this.provider,
|
|
723
|
+
model: this.model,
|
|
724
|
+
sessionCreatedAt: this.sessionCreatedAt,
|
|
725
|
+
...(meta.eventReceivedAt === undefined ? {} : { eventReceivedAt: meta.eventReceivedAt }),
|
|
726
|
+
eventEmittedAt: Date.now(),
|
|
727
|
+
...(turn ? { turnId: turn.turnId, turnStartedAt: turn.turnStartedAt, turnKind: turn.kind } : {}),
|
|
728
|
+
source: meta.source,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
createChatContext(meta, turn) {
|
|
732
|
+
return {
|
|
733
|
+
...this.createBaseContext(meta, turn),
|
|
734
|
+
type: "chat_delta",
|
|
735
|
+
turnId: turn.turnId,
|
|
736
|
+
turnStartedAt: turn.turnStartedAt,
|
|
737
|
+
eventReceivedAt: meta.eventReceivedAt ?? Date.now(),
|
|
738
|
+
source: "core",
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
createToolCallContext(event, meta, turn) {
|
|
742
|
+
return {
|
|
743
|
+
...this.createBaseContext(meta, turn),
|
|
744
|
+
type: "tool_call",
|
|
745
|
+
toolRequestId: event.toolRequestId,
|
|
746
|
+
tool: event.tool,
|
|
747
|
+
turnId: turn.turnId,
|
|
748
|
+
turnStartedAt: turn.turnStartedAt,
|
|
749
|
+
eventReceivedAt: meta.eventReceivedAt ?? Date.now(),
|
|
750
|
+
source: "core",
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
createStatusContext(status, previousStatus, meta) {
|
|
754
|
+
return {
|
|
755
|
+
...this.createBaseContext(meta),
|
|
756
|
+
type: meta.source === "core" ? "status_changed" : "sdk_status_changed",
|
|
757
|
+
status,
|
|
758
|
+
previousStatus,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
createErrorContext(meta) {
|
|
762
|
+
return {
|
|
763
|
+
...this.createBaseContext(meta),
|
|
764
|
+
type: meta.source === "core" ? "error" : "sdk_error",
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
createEndedContext(meta) {
|
|
768
|
+
return {
|
|
769
|
+
...this.createBaseContext(meta),
|
|
770
|
+
type: meta.source === "core" ? "ended" : "sdk_ended",
|
|
771
|
+
};
|
|
772
|
+
}
|
|
599
773
|
}
|
|
600
774
|
function isSessionEvent(message) {
|
|
601
775
|
return (message.type === "chat_delta" ||
|
|
@@ -651,6 +825,16 @@ function createChannelId() {
|
|
|
651
825
|
: Math.random().toString(36).slice(2);
|
|
652
826
|
return `pedelec_${Date.now()}_${random}`;
|
|
653
827
|
}
|
|
828
|
+
function createTurn(kind = "user") {
|
|
829
|
+
const random = typeof crypto !== "undefined" && "randomUUID" in crypto
|
|
830
|
+
? crypto.randomUUID()
|
|
831
|
+
: Math.random().toString(36).slice(2);
|
|
832
|
+
return {
|
|
833
|
+
turnId: `turn_${Date.now()}_${random}`,
|
|
834
|
+
turnStartedAt: Date.now(),
|
|
835
|
+
kind,
|
|
836
|
+
};
|
|
837
|
+
}
|
|
654
838
|
function getCurrentOrigin(pageWindow) {
|
|
655
839
|
const origin = pageWindow?.location?.origin;
|
|
656
840
|
return typeof origin === "string" && origin ? origin : null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Pedelec, defineTool, } from "./index";
|
|
2
|
+
async function typedOnToolNameFromCreateSession() {
|
|
3
|
+
const pedelec = new Pedelec();
|
|
4
|
+
const session = await pedelec.createSession({
|
|
5
|
+
provider: "codex",
|
|
6
|
+
skills: {
|
|
7
|
+
guidance: "Use available tools.",
|
|
8
|
+
tools: [
|
|
9
|
+
defineTool({
|
|
10
|
+
name: "get_selection",
|
|
11
|
+
description: "Get selected text.",
|
|
12
|
+
argsSchema: { type: "object", properties: {}, required: [] },
|
|
13
|
+
}),
|
|
14
|
+
defineTool({
|
|
15
|
+
name: "replace_text",
|
|
16
|
+
description: "Replace selected text.",
|
|
17
|
+
argsSchema: { type: "object", properties: {}, required: [] },
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
session.onTool((name, args, ctx) => {
|
|
23
|
+
const allowed = name;
|
|
24
|
+
const stillUnknown = args;
|
|
25
|
+
const toolCtx = ctx;
|
|
26
|
+
return { allowed, stillUnknown, toolCtx };
|
|
27
|
+
});
|
|
28
|
+
session.onTool("get_selection", async (args, ctx) => {
|
|
29
|
+
const stillUnknown = args;
|
|
30
|
+
const toolName = ctx.tool;
|
|
31
|
+
const turnId = ctx.turnId;
|
|
32
|
+
return { ok: true, stillUnknown, toolName, turnId };
|
|
33
|
+
});
|
|
34
|
+
session.onTool("replace_text", async () => {
|
|
35
|
+
return { ok: true };
|
|
36
|
+
});
|
|
37
|
+
// @ts-expect-error tool name must come from skills.tools[].name
|
|
38
|
+
session.onTool("not_exists", async () => {
|
|
39
|
+
return { ok: false };
|
|
40
|
+
});
|
|
41
|
+
session.onTool((name, _args, ctx) => {
|
|
42
|
+
// @ts-expect-error generic handler name should not be arbitrary string
|
|
43
|
+
const invalid = name;
|
|
44
|
+
// @ts-expect-error user-facing context must not expose core seq
|
|
45
|
+
const noSeq = ctx.seq;
|
|
46
|
+
return noSeq ?? invalid;
|
|
47
|
+
});
|
|
48
|
+
session.onChat((_text, ctx) => {
|
|
49
|
+
const chatCtx = ctx;
|
|
50
|
+
const receivedAt = ctx.eventReceivedAt;
|
|
51
|
+
void receivedAt;
|
|
52
|
+
void chatCtx;
|
|
53
|
+
});
|
|
54
|
+
session.onStatus((_status, ctx) => {
|
|
55
|
+
const statusCtx = ctx;
|
|
56
|
+
const previous = ctx.previousStatus;
|
|
57
|
+
void previous;
|
|
58
|
+
void statusCtx;
|
|
59
|
+
});
|
|
60
|
+
session.onError((_error, ctx) => {
|
|
61
|
+
const errorCtx = ctx;
|
|
62
|
+
return errorCtx;
|
|
63
|
+
});
|
|
64
|
+
session.onEnded((ctx) => {
|
|
65
|
+
const endedCtx = ctx;
|
|
66
|
+
return endedCtx;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function resumedSessionFallsBackToString() {
|
|
70
|
+
const pedelec = new Pedelec();
|
|
71
|
+
const session = await pedelec.resumeSession("thread_1");
|
|
72
|
+
session.onTool((name) => {
|
|
73
|
+
const anyString = name;
|
|
74
|
+
return anyString;
|
|
75
|
+
});
|
|
76
|
+
session.onTool("runtime_tool_name", async () => {
|
|
77
|
+
return { ok: true };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async function noSkillsFallsBackToString() {
|
|
81
|
+
const pedelec = new Pedelec();
|
|
82
|
+
const session = await pedelec.createSession({ provider: "codex" });
|
|
83
|
+
session.onTool((name) => {
|
|
84
|
+
const anyString = name;
|
|
85
|
+
return anyString;
|
|
86
|
+
});
|
|
87
|
+
session.onTool("runtime_tool_name", async () => {
|
|
88
|
+
return { ok: true };
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
void typedOnToolNameFromCreateSession;
|
|
92
|
+
void resumedSessionFallsBackToString;
|
|
93
|
+
void noSkillsFallsBackToString;
|
|
94
|
+
const baseContext = {
|
|
95
|
+
sessionId: "thread_1",
|
|
96
|
+
provider: "codex",
|
|
97
|
+
sessionCreatedAt: Date.now(),
|
|
98
|
+
eventEmittedAt: Date.now(),
|
|
99
|
+
source: "sdk",
|
|
100
|
+
};
|
|
101
|
+
void baseContext;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaoruisaac/pedelec",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "tsc -p tsconfig.json",
|
|
28
28
|
"test": "vitest run",
|
|
29
|
-
"
|
|
29
|
+
"type-test": "tsc -p tsconfig.type-test.json",
|
|
30
|
+
"prepublishOnly": "npm run test && npm run type-test && npm run build"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"typescript": "^5.8.3",
|