@kaoruisaac/pedelec 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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,37 @@ 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
+ const validPath = isValidAssetPath(path) && path.slice(path.lastIndexOf("/") + 1) === name;
1795
+ const validSize = typeof sizeBytes === "number" && Number.isFinite(sizeBytes) &&
1796
+ Number.isInteger(sizeBytes) && sizeBytes >= 0;
1797
+ const validKind = kind === "image" || kind === "audio" || kind === "video" || kind === "file";
1798
+ const validSource = source === "image_generation" || source === "tool_result" || source === "provider_artifact";
1799
+ if (typeof id !== "string" || id.trim().length === 0 || !isProviderCode(provider) || !validPath ||
1800
+ !validName || typeof mimeType !== "string" || mimeType.trim().length === 0 || !validSize ||
1801
+ !validKind || !validSource || !optionalIdIsValid("providerArtifactId") || !optionalIdIsValid("toolCallId")) {
1802
+ throw makeError("SDK_PROTOCOL_ERROR", "provider_artifact event had an invalid artifact payload");
1803
+ }
1804
+ return {
1805
+ id,
1806
+ provider,
1807
+ path,
1808
+ name,
1809
+ mimeType,
1810
+ sizeBytes,
1811
+ kind,
1812
+ source,
1813
+ ...(value.providerArtifactId === undefined ? {} : { providerArtifactId: value.providerArtifactId }),
1814
+ ...(value.toolCallId === undefined ? {} : { toolCallId: value.toolCallId }),
1815
+ };
1816
+ }
1749
1817
  function normalizePedelecSettings(value) {
1750
1818
  if (!isPlainObject(value) || (value.defaultProvider !== null && !isProviderCode(value.defaultProvider))) {
1751
1819
  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.2";
1
+ export declare const SDK_VERSION = "0.4.4";
@@ -1,2 +1,2 @@
1
1
  // Generated from sdk/package.json. Do not edit manually.
2
- export const SDK_VERSION = "0.4.2";
2
+ export const SDK_VERSION = "0.4.4";
package/dist/vite.js CHANGED
@@ -12,6 +12,7 @@ const DECLARATION_LIKE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs
12
12
  const LEGACY_NODE_BUILTIN_MODULES = new Set(builtinModules.filter((specifier) => !specifier.startsWith("node:")));
13
13
  const NODE_BUILTIN_MODULES = new Set(builtinModules.map((specifier) => specifier.replace(/^node:/, "")));
14
14
  const nodeRequire = createRequire(import.meta.url);
15
+ const ssrResolvers = new WeakMap();
15
16
  /**
16
17
  * Vite build-time support for the static Deno module authoring API.
17
18
  *
@@ -85,7 +86,7 @@ export function pedelecVitePlugin() {
85
86
  };
86
87
  }
87
88
  async function prepareDeclaration(declaration, importer, resolveModule, config) {
88
- const entry = await resolveEntry(declaration, importer, resolveModule);
89
+ const entry = await resolveEntry(declaration, importer, resolveModule, config);
89
90
  const diagnosticPrefix = `Deno module "${declaration.name}" entry "${declaration.entry}" imported by "${importer}"`;
90
91
  let runtimeBundle;
91
92
  try {
@@ -119,13 +120,13 @@ async function prepareDeclaration(declaration, importer, resolveModule, config)
119
120
  dependencies: new Set([...dependencies].map(normalizeFile)),
120
121
  };
121
122
  }
122
- async function resolveEntry(declaration, importer, resolveModule) {
123
+ async function resolveEntry(declaration, importer, resolveModule, config) {
123
124
  if (isRemoteOrSchemeEntry(declaration.entry)) {
124
125
  throw new Error(`Deno module "${declaration.name}" entry "${declaration.entry}" is not a local or package specifier; remote and scheme URLs are not supported`);
125
126
  }
126
127
  let result;
127
128
  try {
128
- result = await resolveHostModule(declaration.entry, importer, resolveModule);
129
+ result = await resolveHostModule(declaration.entry, importer, resolveModule, config);
129
130
  }
130
131
  catch (error) {
131
132
  throw new Error(`could not resolve entry "${declaration.entry}" for Deno module "${declaration.name}" from "${importer}": ${formatError(error)}`);
@@ -145,7 +146,7 @@ async function bundleRuntime(entry, resolveModule, config) {
145
146
  return { id: builtin, external: true };
146
147
  if (!importer || source.startsWith("\0"))
147
148
  return null;
148
- const resolved = await resolveHostModule(source, importer, resolveModule);
149
+ const resolved = await resolveHostModule(source, importer, resolveModule, config);
149
150
  if (!resolved) {
150
151
  throw new Error(`unresolved runtime dependency "${source}" from "${importer}"`);
151
152
  }
@@ -232,7 +233,7 @@ async function bundleRuntime(entry, resolveModule, config) {
232
233
  }
233
234
  return { source: entryChunk.code, files };
234
235
  }
235
- async function resolveHostModule(source, importer, resolveModule) {
236
+ async function resolveHostModule(source, importer, resolveModule, config) {
236
237
  const resolved = await resolveModule(source, importer, { skipSelf: true });
237
238
  const resolvedFile = resolved ? stripQueryAndHash(resolved.id) : null;
238
239
  const needsPackageFallback = Boolean(isBareSpecifier(source) &&
@@ -240,6 +241,23 @@ async function resolveHostModule(source, importer, resolveModule) {
240
241
  (isOptimizedDependencyPath(resolved.id) || resolved.external || !ts.sys.fileExists(resolvedFile)));
241
242
  if (!needsPackageFallback)
242
243
  return resolved;
244
+ if (config) {
245
+ try {
246
+ let resolver = ssrResolvers.get(config);
247
+ if (!resolver) {
248
+ resolver = config.createResolver();
249
+ ssrResolvers.set(config, resolver);
250
+ }
251
+ const serverResolved = await resolver(source, importer, false, true);
252
+ if (serverResolved)
253
+ return { id: serverResolved };
254
+ }
255
+ catch {
256
+ // Fall through to the legacy Node resolver. Custom Vite plugins can still
257
+ // return a usable outer resolution even when Vite's internal SSR resolver
258
+ // cannot resolve the specifier.
259
+ }
260
+ }
243
261
  try {
244
262
  return { id: nodeRequire.resolve(source, { paths: [dirname(importer)] }) };
245
263
  }
@@ -298,7 +316,7 @@ async function bundleDeclarations(runtimeEntry, specifier, importer, diagnosticP
298
316
  continue;
299
317
  if (!canResolveDeclarationDependency(moduleSpecifier, packageBoundary))
300
318
  continue;
301
- const resolved = await resolveDeclarationDependency(moduleSpecifier, fileName, options, host, resolveModule);
319
+ const resolved = await resolveDeclarationDependency(moduleSpecifier, fileName, options, host, resolveModule, config);
302
320
  if (!resolved) {
303
321
  throw new Error(`${diagnosticPrefix}: public declaration still depends on unresolved module "${moduleSpecifier}"`);
304
322
  }
@@ -481,12 +499,12 @@ function resolveExportTarget(target, packageDirectory) {
481
499
  }
482
500
  return null;
483
501
  }
484
- async function resolveDeclarationDependency(specifier, importer, options, host, resolveModule) {
502
+ async function resolveDeclarationDependency(specifier, importer, options, host, resolveModule, config) {
485
503
  const fromTypeScript = ts.resolveModuleName(specifier, importer, options, host).resolvedModule?.resolvedFileName;
486
504
  if (fromTypeScript && !isTypeScriptLib(fromTypeScript) && isDeclarationLikePath(fromTypeScript)) {
487
505
  return normalizeFile(fromTypeScript);
488
506
  }
489
- const viteResolved = await resolveHostModule(specifier, importer, resolveModule);
507
+ const viteResolved = await resolveHostModule(specifier, importer, resolveModule, config);
490
508
  if (!viteResolved || viteResolved.external)
491
509
  return null;
492
510
  const resolvedId = stripQueryAndHash(viteResolved.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaoruisaac/pedelec",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",