@kaoruisaac/pedelec 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -233,6 +233,20 @@ const result = await session.readAsset("/model.glb", "file");
233
233
 
234
234
  Public asset paths use `/...` with `assets/` as their implicit root; nested paths such as `/results/model.glb` are supported up to 100 MiB.
235
235
 
236
+ ### Provider-generated assets
237
+
238
+ When a supported provider generates an image or file, Pedelec imports it under `.pedelec-runtime/assets/provider-artifacts/` before notifying the application. `onArtifact()` receives metadata only; binary bytes and provider-private absolute paths are not included in the event. The public `artifact.path` works immediately with `readAsset()` and the same file is included by `listAssets()`.
239
+
240
+ ```ts
241
+ const offArtifact = session.onArtifact(async (artifact) => {
242
+ if (artifact.kind !== "image") return;
243
+ const file = await session.readAsset(artifact.path, "file");
244
+ renderImage(file);
245
+ });
246
+ ```
247
+
248
+ Use `listAssets()` to discover files from earlier turns; registering an artifact handler does not replay history. Provider artifacts follow the existing per-file 100 MiB asset limit. Availability depends on the selected provider emitting a supported generated artifact, so not every provider or model can generate every artifact kind.
249
+
236
250
  ## Workspace
237
251
 
238
252
  Workspace is the filesystem root in which the Agent works. Pedelec stores its private runtime data under `<workspace>/.pedelec-runtime/`. There are two supported flows:
@@ -290,7 +304,7 @@ The default timeout is 60 seconds; `timeoutMs` must be a positive integer. The r
290
304
 
291
305
  `workspace.run()` has read and write access to the Workspace, but no network, host environment, subprocess execution, FFI, or `sys` permission. The existing no-remote, cached-only, and no-npm runtime restrictions still apply. This is a direct deterministic capability for an approved Web application to read and modify the opened Workspace; it is not unrestricted host code execution and is not mediated by an Agent.
292
306
 
293
- Several `workspace.run()` calls in one Workspace may execute concurrently. A Workspace run cannot start while any Session in that Workspace has an active Agent/provider operation. Conversely, a Session in that Workspace cannot begin `sendText()` or provider preparation while one or more Workspace runs are active. This rule is Workspace-wide, including operations started through another `PedelecSession` handle; conflicts reject with `WORKSPACE_BUSY`.
307
+ Multiple Sessions, Agent/provider operations, and `workspace.run()` executions may operate concurrently in the same Workspace. A second turn on the same Session is still rejected while that Session is busy. Pedelec does not provide filesystem-level locking, snapshots, transactions, or conflict resolution between concurrent Workspace actors. Applications are responsible for coordinating shared file access when required.
294
308
 
295
309
  Session `skills.denoModules` belong to the Session/Thread that declares them and are available only to Agent-side `pedelec-deno` execution for that Thread. Workspace runs accept the same Vite-prepared declarations explicitly:
296
310
 
package/dist/index.d.ts CHANGED
@@ -16,6 +16,21 @@ export type Asset = {
16
16
  sizeBytes: number;
17
17
  modifiedAt: number;
18
18
  };
19
+ export type ProviderArtifactKind = "image" | "audio" | "video" | "file";
20
+ export type ProviderArtifactSource = "image_generation" | "tool_result" | "provider_artifact";
21
+ /** Metadata for a provider-generated file already materialized in Workspace assets. */
22
+ export type ProviderArtifact = {
23
+ id: string;
24
+ provider: ProviderCode;
25
+ path: AssetPath;
26
+ name: string;
27
+ mimeType: string;
28
+ sizeBytes: number;
29
+ kind: ProviderArtifactKind;
30
+ source: ProviderArtifactSource;
31
+ providerArtifactId?: string;
32
+ toolCallId?: string;
33
+ };
19
34
  export type DenoModuleDefinition<TName extends string = string> = {
20
35
  name: TName;
21
36
  description?: string;
@@ -197,6 +212,14 @@ export type ChatDeltaEventContext = PedelecEventContext & {
197
212
  turnStartedAt: number;
198
213
  eventReceivedAt: number;
199
214
  };
215
+ export type ProviderArtifactEventContext = PedelecEventContext & {
216
+ type: "provider_artifact";
217
+ turnId: string;
218
+ turnStartedAt: number;
219
+ turnKind: "user";
220
+ eventReceivedAt: number;
221
+ source: "core";
222
+ };
200
223
  export type ToolCallContext = PedelecEventContext & {
201
224
  type: "tool_call";
202
225
  toolRequestId: string;
@@ -230,6 +253,13 @@ type SessionEvent = {
230
253
  seq?: number;
231
254
  operationId?: string;
232
255
  text: string;
256
+ } | {
257
+ type: "provider_artifact";
258
+ channelId: string;
259
+ sessionId: string;
260
+ seq?: number;
261
+ operationId: string;
262
+ artifact: unknown;
233
263
  } | {
234
264
  type: "status_changed";
235
265
  channelId: string;
@@ -309,6 +339,7 @@ type EventDispatchMeta = {
309
339
  };
310
340
  type ChatHandler = (text: string, ctx: ChatEventContext) => void;
311
341
  type ChatDeltaHandler = (text: string, ctx: ChatDeltaEventContext) => void;
342
+ export type ProviderArtifactHandler = (artifact: ProviderArtifact, ctx: ProviderArtifactEventContext) => void;
312
343
  type GenericToolHandler<TToolName extends string = string> = (tool: TToolName, args: unknown, ctx: ToolCallContext) => unknown | Promise<unknown>;
313
344
  type ErrorHandler = (error: PedelecError, ctx: ErrorEventContext) => void;
314
345
  type StatusHandler = (status: PedelecSessionStatus, ctx: StatusEventContext) => void;
@@ -418,6 +449,7 @@ export declare class PedelecSession<TToolName extends string = string> {
418
449
  private readonly namedToolHandlers;
419
450
  private readonly chatHandlers;
420
451
  private readonly chatDeltaHandlers;
452
+ private readonly artifactHandlers;
421
453
  private readonly errorHandlers;
422
454
  private readonly statusHandlers;
423
455
  private readonly endedHandlers;
@@ -429,6 +461,7 @@ export declare class PedelecSession<TToolName extends string = string> {
429
461
  sendText(text: string): Promise<void>;
430
462
  onChat(handler: ChatHandler): () => void;
431
463
  onChatDelta(handler: ChatDeltaHandler): () => void;
464
+ onArtifact(handler: ProviderArtifactHandler): () => void;
432
465
  onTool(handler: GenericToolHandler<TToolName>): () => void;
433
466
  onTool<TArgs = unknown, TResult = unknown>(toolName: TToolName, handler: ToolSpecificHandler<TArgs, TResult>): () => void;
434
467
  uploadAsset(file: File): Promise<AssetPath>;
@@ -475,6 +508,7 @@ export declare class PedelecSession<TToolName extends string = string> {
475
508
  private createBaseContext;
476
509
  private createChatContext;
477
510
  private createChatDeltaContext;
511
+ private createProviderArtifactContext;
478
512
  private createToolCallContext;
479
513
  private createStatusContext;
480
514
  private createErrorContext;
package/dist/index.js CHANGED
@@ -955,6 +955,7 @@ export class PedelecSession {
955
955
  namedToolHandlers = new Map();
956
956
  chatHandlers = new Set();
957
957
  chatDeltaHandlers = new Set();
958
+ artifactHandlers = new Set();
958
959
  errorHandlers = new Set();
959
960
  statusHandlers = new Set();
960
961
  endedHandlers = new Set();
@@ -1072,6 +1073,10 @@ export class PedelecSession {
1072
1073
  this.chatDeltaHandlers.add(handler);
1073
1074
  return () => this.chatDeltaHandlers.delete(handler);
1074
1075
  }
1076
+ onArtifact(handler) {
1077
+ this.artifactHandlers.add(handler);
1078
+ return () => this.artifactHandlers.delete(handler);
1079
+ }
1075
1080
  onTool(toolNameOrHandler, maybeHandler) {
1076
1081
  if (typeof toolNameOrHandler === "string") {
1077
1082
  const toolName = toolNameOrHandler;
@@ -1307,6 +1312,26 @@ export class PedelecSession {
1307
1312
  }
1308
1313
  return;
1309
1314
  }
1315
+ if (event.type === "provider_artifact") {
1316
+ let artifact;
1317
+ try {
1318
+ artifact = normalizeProviderArtifact(event.artifact);
1319
+ }
1320
+ catch (error) {
1321
+ this.emitError(normalizeError(error, "SDK_PROTOCOL_ERROR", "provider_artifact event had an invalid payload"), {
1322
+ source: "sdk",
1323
+ eventReceivedAt: meta.eventReceivedAt,
1324
+ });
1325
+ return;
1326
+ }
1327
+ const turn = this.requireOperationTurn(event.operationId, "provider_artifact", meta);
1328
+ if (!turn || turn.kind === "prepare")
1329
+ return;
1330
+ for (const handler of this.artifactHandlers) {
1331
+ handler(artifact, this.createProviderArtifactContext(meta, turn));
1332
+ }
1333
+ return;
1334
+ }
1310
1335
  if (event.type === "status_changed") {
1311
1336
  if (event.operationId && !this.matchesActiveOperation(event.operationId))
1312
1337
  return;
@@ -1690,6 +1715,17 @@ export class PedelecSession {
1690
1715
  source: "core",
1691
1716
  };
1692
1717
  }
1718
+ createProviderArtifactContext(meta, turn) {
1719
+ return {
1720
+ ...this.createBaseContext(meta, turn),
1721
+ type: "provider_artifact",
1722
+ turnId: turn.turnId,
1723
+ turnStartedAt: turn.turnStartedAt,
1724
+ turnKind: "user",
1725
+ eventReceivedAt: meta.eventReceivedAt ?? Date.now(),
1726
+ source: "core",
1727
+ };
1728
+ }
1693
1729
  createToolCallContext(event, meta, turn) {
1694
1730
  return {
1695
1731
  ...this.createBaseContext(meta, turn),
@@ -1726,6 +1762,7 @@ export class PedelecSession {
1726
1762
  function isSessionEvent(message) {
1727
1763
  return (message.type === "chat_delta" ||
1728
1764
  message.type === "chat_message" ||
1765
+ message.type === "provider_artifact" ||
1729
1766
  message.type === "status_changed" ||
1730
1767
  message.type === "tool_call" ||
1731
1768
  message.type === "operation_completed" ||
@@ -1746,6 +1783,39 @@ function isEffort(value) {
1746
1783
  function isPlainObject(value) {
1747
1784
  return !!value && typeof value === "object" && !Array.isArray(value);
1748
1785
  }
1786
+ function normalizeProviderArtifact(value) {
1787
+ if (!isPlainObject(value)) {
1788
+ throw makeError("SDK_PROTOCOL_ERROR", "provider_artifact event had an invalid artifact object");
1789
+ }
1790
+ const { id, provider, path, name, mimeType, sizeBytes, kind, source } = value;
1791
+ const optionalIdIsValid = (key) => !(key in value) || (typeof value[key] === "string" && value[key].trim().length > 0);
1792
+ const validName = typeof name === "string" && name.trim().length > 0 &&
1793
+ !name.includes("/") && !name.includes("\\") && name !== "." && name !== "..";
1794
+ // Core persists a collision-safe basename (`<artifact-id>-<name>`). Logical `name`
1795
+ // and public `path` are validated independently.
1796
+ const validPath = isValidAssetPath(path);
1797
+ const validSize = typeof sizeBytes === "number" && Number.isFinite(sizeBytes) &&
1798
+ Number.isInteger(sizeBytes) && sizeBytes >= 0;
1799
+ const validKind = kind === "image" || kind === "audio" || kind === "video" || kind === "file";
1800
+ const validSource = source === "image_generation" || source === "tool_result" || source === "provider_artifact";
1801
+ if (typeof id !== "string" || id.trim().length === 0 || !isProviderCode(provider) || !validPath ||
1802
+ !validName || typeof mimeType !== "string" || mimeType.trim().length === 0 || !validSize ||
1803
+ !validKind || !validSource || !optionalIdIsValid("providerArtifactId") || !optionalIdIsValid("toolCallId")) {
1804
+ throw makeError("SDK_PROTOCOL_ERROR", "provider_artifact event had an invalid artifact payload");
1805
+ }
1806
+ return {
1807
+ id,
1808
+ provider,
1809
+ path,
1810
+ name,
1811
+ mimeType,
1812
+ sizeBytes,
1813
+ kind,
1814
+ source,
1815
+ ...(value.providerArtifactId === undefined ? {} : { providerArtifactId: value.providerArtifactId }),
1816
+ ...(value.toolCallId === undefined ? {} : { toolCallId: value.toolCallId }),
1817
+ };
1818
+ }
1749
1819
  function normalizePedelecSettings(value) {
1750
1820
  if (!isPlainObject(value) || (value.defaultProvider !== null && !isProviderCode(value.defaultProvider))) {
1751
1821
  throw makeError("SDK_PROTOCOL_ERROR", "get_settings response had invalid shape");
@@ -92,6 +92,18 @@ async function typedOnToolNameFromCreateSession() {
92
92
  void deltaType;
93
93
  void chatDeltaCtx;
94
94
  });
95
+ session.onArtifact((artifact, ctx) => {
96
+ const publicArtifact = artifact;
97
+ const artifactContext = ctx;
98
+ const turnKind = ctx.turnKind;
99
+ const origin = ctx.source;
100
+ const file = session.readAsset(artifact.path, "file");
101
+ void publicArtifact;
102
+ void artifactContext;
103
+ void turnKind;
104
+ void origin;
105
+ void file;
106
+ });
95
107
  session.onStatus((_status, ctx) => {
96
108
  const statusCtx = ctx;
97
109
  const previous = ctx.previousStatus;
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "0.4.3";
1
+ export declare const SDK_VERSION = "0.4.5";
@@ -1,2 +1,2 @@
1
1
  // Generated from sdk/package.json. Do not edit manually.
2
- export const SDK_VERSION = "0.4.3";
2
+ export const SDK_VERSION = "0.4.5";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaoruisaac/pedelec",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",