跳到內容

PedelecWorkspace API

PedelecWorkspace 是 application 操作單一 Workspace filesystem 的 capability。它可由 pedelec.openWorkspace()、session.workspace 或 resumeSession().workspace 取得。

import type { PedelecWorkspace, WorkspaceRunResult } from "@kaoruisaac/pedelec";
readonly path: string | null

Explicit Workspace 會暴露通過 validation 的 custom path。Desktop-managed Workspace 刻意暴露 null;application 不應推測或顯示它的 physical managed directory。

const workspace = await pedelec.openWorkspace();
if (!workspace) return; // 使用者取消原生 picker
const session = await workspace.createSession({
model: "gpt-5.6-sol",
effort: "high",
});

傳入 absolute path 可跳過 picker:

const workspace = await pedelec.openWorkspace("C:\\workspace\\project-a");
const first = await workspace.createSession();
const second = await workspace.createSession();

一個 Workspace 可以擁有多個 Sessions。透過這個 handle 建立的 session 會保留同一個 Workspace capability。pedelec.createSession() 是 managed convenience flow,不接受 Workspace configuration;但回傳的 session.workspace 仍然可使用。

listFiles(path?: string): Promise<string[]>

遞迴列出 Workspace-relative path 下的一般檔案。省略 path 代表 Workspace root。結果使用 / separator、以 Workspace-relative path 回傳,並依 lexicographic 順序排序。.pedelec-runtime 會包含在結果中。Symlink 與 junction 不會被跟隨,也不會回傳。

結果會以單一 response 回傳,不會靜默截斷。結果過大時 reject WORKSPACE_LIST_TOO_LARGE;請改列較窄的 path。

listFolders(path?: string): Promise<string[]>

除了回傳 directories 外,root、relative path、separator、sorting、.pedelec-runtime、symlink/junction 與 response size 行為都與 listFiles() 相同。

uploadFile(file: File): Promise<string>
uploadFile(file: File, targetPath: string): Promise<string>

把一個 browser File 上傳到這個 Workspace。path 是 Workspace-relative,使用 /,而且沒有前導 /。

const workspace = await pedelec.openWorkspace("C:\\workspace\\project");
const file = new File(["hello"], "hello.txt", { type: "text/plain" });
const path = await workspace.uploadFile(file);
// Workspace root 下的 "hello.txt",沒有 generated prefix
await workspace.uploadFile(file, "references/photo.png");

沒有 targetPath 時,回傳 path 就是原始 file.name,檔案放在 Workspace root。指定 targetPath 時會原樣回傳,缺少的 parent directory 會建立。單檔上限 100 MiB。Upload 使用本機 transfer server 與一次性 ticket;多個 uploadFile() 可以同時進行。Pedelec 不會鎖住整個 Workspace,也不會替同一個 path 的並行寫入做衝突協調,這件事由 application 負責。成功的 upload 會取代既有 regular file。absolute path、..、symlink 與 junction 都會被拒絕。

readFile(path: string, type: "text"): Promise<string>
readFile<T = JsonValue>(path: string, type: "json"): Promise<T>
readFile(path: string, type: "file"): Promise<File>

讀取一個 Workspace-relative regular file,包含 .pedelec-runtime 底下的路徑。text 要求嚴格 UTF-8。json 會解析該文字。file 回傳 browser File:檔名是目標 basename,MIME type 依副檔名判斷,lastModified 使用 filesystem modification time。讀取上限同樣是 100 MiB。讀取過程中檔案若改變或被截斷,會失敗,不會回傳不完整內容。

uploadAsset()、readAsset() 與 listAssets() 仍是 Session-scoped convenience API,根目錄固定在 .pedelec-runtime/assets。一般 Workspace filesystem 存取請使用 uploadFile()、readFile() 與 listFiles()。

使用方式請先參閱 Workspace Runtime 執行;以下記錄精確 API 契約。

run(
script: string,
options?: {
timeoutMs?: number;
runtimeModules?: readonly RuntimeModuleDefinition[];
},
): Promise<WorkspaceRunResult>

在此 Workspace 執行 application 提供的 TypeScript code。Default timeout 是 60 秒;override 必須是 positive integer。

const result = await workspace.run(
"console.log(JSON.stringify({ ok: true }))",
{ timeoutMs: 60_000 },
);
if (result.stdoutTruncated || result.stderrTruncated) {
throw new Error("Workspace script output was truncated");
}
const value = JSON.parse(result.stdout);

runtimeModules 會為這一次 run mount Vite-prepared modules。Workspace-only declaration 只需要 logical import name 與 build-time entry:

const sceneTools = defineRuntimeModule({
name: "scene-tools",
entry: "./deno/scene-tools.ts",
});
const result = await workspace.run(
`import { buildScene } from "scene-tools";\nawait buildScene();`,
{ runtimeModules: [sceneTools] },
);

每個 artifact 都必須先由 Vite plugin 準備好,才能呼叫 workspace.run()。entry 只由 Vite 解析,不會送到 Desktop/Core。每次 run 都只會收到該次列出的 module names 所組成的 import map,不會繼承 Session 或其他 Workspace run 的 modules。Prepared packages 會按 Workspace 與 authorized SDK origin 私下 cache,後續 run 可以重用 ready module 而不必再次 upload artifact。同一個 Core process lifetime 內,ready logical name 不可替換;要使用不同 implementation 請改用新的 name。

Result 是 raw Deno output:exitCode、stdout、stderr、stdoutTruncated 與 stderrTruncated。非零 exitCode 仍會正常 resolve;如何解讀由 application 決定,需要 function-like value 時再自行 parse stdout。Browser response 過大時 reject WORKSPACE_RUN_OUTPUT_TOO_LARGE。

workspace.run() 是 approved Web application 的直接 capability。TypeScript code 可以讀寫 Workspace,但沒有 network、host environment、subprocess execution、FFI 或 sys permission;既有 no-remote、cached-only 與 no-npm runtime restrictions 仍適用。預設模式不是 unrestricted host code execution,也不是 Agent-mediated action。

skills.runtimeModules 屬於宣告它的 Session/Thread,只提供給該 Thread 的 Agent-side pedelec-ts-runtime execution。Workspace-run modules 是分開且明確的 per-run capability:workspace.run() 不會繼承 Session import map,workspace.createSession() 也不會繼承先前由 workspace.run() 使用的 modules。Workspace-run modules 永遠不會出現在 Agent Host Context。Core restart 時會重設 private Workspace-run cache;project files、assets 與 Thread module snapshots 不會被清除。

同一個 Workspace 裡,多個 Session、Agent/provider operation 與 workspace.run() 可以同時進行。同一個 Session 在 busy 時,第二次 turn 仍會被拒絕。Pedelec 不提供 concurrent Workspace actors 之間的 filesystem locking、snapshots、transactions 或 conflict resolution。需要這些保證時,由 application 自行協調共用檔案存取。

Custom Workspace 是明確開啟的,可以由多個 Session 共用,也會在 Session 結束或 app cleanup 時由 Pedelec 保留、不刪除。Managed Workspace 由 Pedelec.createSession() 自動建立,cleanup 由 Desktop 負責;Thread 結束不代表會立即刪除。Browser capability 受 runtime scope 限制,Desktop/Core restart 後需要時請重新開啟 custom Workspace。

resumeSession(sessionId) 回傳的 Session 已經包含 session.workspace;不需要另外呼叫 openWorkspace()。同一 handle 的 session.resume() 會保留既有 Workspace handle,但仍受 transport、Core thread 與 recorded Workspace caveats 限制。

workspace.run() 預設為 Sandbox;使用 Full Instance、兩項 Extension grants 與 { runtimeFullAccess: true } 可為單次執行要求 Full Access,不需要 Session。同時指定 runtimeModules 與 legacy denoModules 會回 INVALID_INPUT。